Add -p switch to "zpool get"
[zfs.git] / module / zfs / spa_history.c
index c997240..9fb75f3 100644 (file)
  */
 
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011 by Delphix. All rights reserved.
  */
 
-#pragma ident  "%Z%%M% %I%     %E% SMI"
-
 #include <sys/spa.h>
 #include <sys/spa_impl.h>
 #include <sys/zap.h>
@@ -35,6 +33,7 @@
 #include <sys/utsname.h>
 #include <sys/cmn_err.h>
 #include <sys/sunddi.h>
+#include "zfs_comutil.h"
 #ifdef _KERNEL
 #include <sys/zone.h>
 #endif
@@ -103,10 +102,11 @@ spa_history_create_obj(spa_t *spa, dmu_tx_t *tx)
 
        /*
         * Figure out maximum size of history log.  We set it at
-        * 1% of pool size, with a max of 32MB and min of 128KB.
+        * 0.1% of pool size, with a max of 1G and min of 128KB.
         */
-       shpp->sh_phys_max_off = spa_get_dspace(spa) / 100;
-       shpp->sh_phys_max_off = MIN(shpp->sh_phys_max_off, 32<<20);
+       shpp->sh_phys_max_off =
+           metaslab_class_get_dspace(spa_normal_class(spa)) / 1000;
+       shpp->sh_phys_max_off = MIN(shpp->sh_phys_max_off, 1<<30);
        shpp->sh_phys_max_off = MAX(shpp->sh_phys_max_off, 128<<10);
 
        dmu_buf_rele(dbp, FTAG);
@@ -127,12 +127,12 @@ spa_history_advance_bof(spa_t *spa, spa_history_phys_t *shpp)
        firstread = MIN(sizeof (reclen), shpp->sh_phys_max_off - phys_bof);
 
        if ((err = dmu_read(mos, spa->spa_history, phys_bof, firstread,
-           buf)) != 0)
+           buf, DMU_READ_PREFETCH)) != 0)
                return (err);
        if (firstread != sizeof (reclen)) {
                if ((err = dmu_read(mos, spa->spa_history,
                    shpp->sh_pool_create_len, sizeof (reclen) - firstread,
-                   buf + firstread)) != 0)
+                   buf + firstread, DMU_READ_PREFETCH)) != 0)
                        return (err);
        }
 
@@ -176,10 +176,14 @@ spa_history_write(spa_t *spa, void *buf, uint64_t len, spa_history_phys_t *shpp,
 }
 
 static char *
-spa_history_zone()
+spa_history_zone(void)
 {
 #ifdef _KERNEL
+#ifdef HAVE_SPL
+       return ("linux");
+#else
        return (curproc->p_zone->zone_name);
+#endif
 #else
        return ("global");
 #endif
@@ -188,8 +192,9 @@ spa_history_zone()
 /*
  * Write out a history event.
  */
+/*ARGSUSED*/
 static void
-spa_history_log_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
+spa_history_log_sync(void *arg1, void *arg2, dmu_tx_t *tx)
 {
        spa_t           *spa = arg1;
        history_arg_t   *hap = arg2;
@@ -229,12 +234,11 @@ spa_history_log_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
        }
 #endif
 
-       VERIFY(nvlist_alloc(&nvrecord, NV_UNIQUE_NAME, KM_SLEEP) == 0);
+       VERIFY(nvlist_alloc(&nvrecord, NV_UNIQUE_NAME, KM_PUSHPAGE) == 0);
        VERIFY(nvlist_add_uint64(nvrecord, ZPOOL_HIST_TIME,
            gethrestime_sec()) == 0);
-       VERIFY(nvlist_add_uint64(nvrecord, ZPOOL_HIST_WHO,
-           (uint64_t)crgetuid(cr)) == 0);
-       if (hap->ha_zone[0] != '\0')
+       VERIFY(nvlist_add_uint64(nvrecord, ZPOOL_HIST_WHO, hap->ha_uid) == 0);
+       if (hap->ha_zone != NULL)
                VERIFY(nvlist_add_string(nvrecord, ZPOOL_HIST_ZONE,
                    hap->ha_zone) == 0);
 #ifdef _KERNEL
@@ -245,6 +249,8 @@ spa_history_log_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
            hap->ha_log_type == LOG_CMD_NORMAL) {
                VERIFY(nvlist_add_string(nvrecord, ZPOOL_HIST_CMD,
                    history_str) == 0);
+
+               zfs_dbgmsg("command: %s", history_str);
        } else {
                VERIFY(nvlist_add_uint64(nvrecord, ZPOOL_HIST_INT_EVENT,
                    hap->ha_event) == 0);
@@ -252,13 +258,18 @@ spa_history_log_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
                    tx->tx_txg) == 0);
                VERIFY(nvlist_add_string(nvrecord, ZPOOL_HIST_INT_STR,
                    history_str) == 0);
+
+               zfs_dbgmsg("internal %s pool:%s txg:%llu %s",
+                   zfs_history_event_names[hap->ha_event], spa_name(spa),
+                   (longlong_t)tx->tx_txg, history_str);
+
        }
 
        VERIFY(nvlist_size(nvrecord, &reclen, NV_ENCODE_XDR) == 0);
-       record_packed = kmem_alloc(reclen, KM_SLEEP);
+       record_packed = kmem_alloc(reclen, KM_PUSHPAGE);
 
        VERIFY(nvlist_pack(nvrecord, &record_packed, &reclen,
-           NV_ENCODE_XDR, KM_SLEEP) == 0);
+           NV_ENCODE_XDR, KM_PUSHPAGE) == 0);
 
        mutex_enter(&spa->spa_history_lock);
        if (hap->ha_log_type == LOG_CMD_POOL_CREATE)
@@ -280,10 +291,10 @@ spa_history_log_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
        kmem_free(record_packed, reclen);
        dmu_buf_rele(dbp, FTAG);
 
-       if (hap->ha_log_type == LOG_INTERNAL) {
-               kmem_free((void*)hap->ha_history_str, HIS_MAX_RECORD_LEN);
-               kmem_free(hap, sizeof (history_arg_t));
-       }
+       strfree(hap->ha_history_str);
+       if (hap->ha_zone != NULL)
+               strfree(hap->ha_zone);
+       kmem_free(hap, sizeof (history_arg_t));
 }
 
 /*
@@ -292,15 +303,32 @@ spa_history_log_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
 int
 spa_history_log(spa_t *spa, const char *history_str, history_log_type_t what)
 {
-       history_arg_t ha;
+       history_arg_t *ha;
+       int err = 0;
+       dmu_tx_t *tx;
 
        ASSERT(what != LOG_INTERNAL);
 
-       ha.ha_history_str = history_str;
-       ha.ha_log_type = what;
-       (void) strlcpy(ha.ha_zone, spa_history_zone(), sizeof (ha.ha_zone));
-       return (dsl_sync_task_do(spa_get_dsl(spa), NULL, spa_history_log_sync,
-           spa, &ha, 0));
+       tx = dmu_tx_create_dd(spa_get_dsl(spa)->dp_mos_dir);
+       err = dmu_tx_assign(tx, TXG_WAIT);
+       if (err) {
+               dmu_tx_abort(tx);
+               return (err);
+       }
+
+       ha = kmem_alloc(sizeof (history_arg_t), KM_PUSHPAGE);
+       ha->ha_history_str = strdup(history_str);
+       ha->ha_zone = strdup(spa_history_zone());
+       ha->ha_log_type = what;
+       ha->ha_uid = crgetuid(CRED());
+
+       /* Kick this off asynchronously; errors are ignored. */
+       dsl_sync_task_do_nowait(spa_get_dsl(spa), NULL,
+           spa_history_log_sync, spa, ha, 0, tx);
+       dmu_tx_commit(tx);
+
+       /* spa_history_log_sync will free ha and strings */
+       return (err);
 }
 
 /*
@@ -323,6 +351,14 @@ spa_history_get(spa_t *spa, uint64_t *offp, uint64_t *len, char *buf)
        if (!spa->spa_history)
                return (ENOENT);
 
+       /*
+        * The history is logged asynchronously, so when they request
+        * the first chunk of history, make sure everything has been
+        * synced to disk so that we get it.
+        */
+       if (*offp == 0 && spa_writeable(spa))
+               txg_wait_synced(spa_get_dsl(spa), 0);
+
        if ((err = dmu_bonus_hold(mos, spa->spa_history, FTAG, &dbp)) != 0)
                return (err);
        shpp = dbp->db_data;
@@ -380,10 +416,11 @@ spa_history_get(spa_t *spa, uint64_t *offp, uint64_t *len, char *buf)
                return (0);
        }
 
-       err = dmu_read(mos, spa->spa_history, phys_read_off, read_len, buf);
+       err = dmu_read(mos, spa->spa_history, phys_read_off, read_len, buf,
+           DMU_READ_PREFETCH);
        if (leftover && err == 0) {
                err = dmu_read(mos, spa->spa_history, shpp->sh_pool_create_len,
-                   leftover, buf + read_len);
+                   leftover, buf + read_len, DMU_READ_PREFETCH);
        }
        mutex_exit(&spa->spa_history_lock);
 
@@ -391,13 +428,12 @@ spa_history_get(spa_t *spa, uint64_t *offp, uint64_t *len, char *buf)
        return (err);
 }
 
-void
-spa_history_internal_log(history_internal_events_t event, spa_t *spa,
-    dmu_tx_t *tx, cred_t *cr, const char *fmt, ...)
+static void
+log_internal(history_internal_events_t event, spa_t *spa,
+    dmu_tx_t *tx, const char *fmt, va_list adx)
 {
-       history_arg_t *hap;
-       char *str;
-       va_list adx;
+       history_arg_t *ha;
+       va_list adx_copy;
 
        /*
         * If this is part of creating a pool, not everything is
@@ -406,23 +442,73 @@ spa_history_internal_log(history_internal_events_t event, spa_t *spa,
        if (tx->tx_txg == TXG_INITIAL)
                return;
 
-       hap = kmem_alloc(sizeof (history_arg_t), KM_SLEEP);
-       str = kmem_alloc(HIS_MAX_RECORD_LEN, KM_SLEEP);
+       ha = kmem_alloc(sizeof (history_arg_t), KM_PUSHPAGE);
+       va_copy(adx_copy, adx);
+       ha->ha_history_str = kmem_vasprintf(fmt, adx_copy);
+       va_end(adx_copy);
+       ha->ha_log_type = LOG_INTERNAL;
+       ha->ha_event = event;
+       ha->ha_zone = NULL;
+       ha->ha_uid = 0;
+
+       if (dmu_tx_is_syncing(tx)) {
+               spa_history_log_sync(spa, ha, tx);
+       } else {
+               dsl_sync_task_do_nowait(spa_get_dsl(spa), NULL,
+                   spa_history_log_sync, spa, ha, 0, tx);
+       }
+       /* spa_history_log_sync() will free ha and strings */
+}
+
+void
+spa_history_log_internal(history_internal_events_t event, spa_t *spa,
+    dmu_tx_t *tx, const char *fmt, ...)
+{
+       dmu_tx_t *htx = tx;
+       va_list adx;
+
+       /* create a tx if we didn't get one */
+       if (tx == NULL) {
+               htx = dmu_tx_create_dd(spa_get_dsl(spa)->dp_mos_dir);
+               if (dmu_tx_assign(htx, TXG_WAIT) != 0) {
+                       dmu_tx_abort(htx);
+                       return;
+               }
+       }
 
        va_start(adx, fmt);
-       (void) vsnprintf(str, HIS_MAX_RECORD_LEN, fmt, adx);
+       log_internal(event, spa, htx, fmt, adx);
        va_end(adx);
 
-       hap->ha_log_type = LOG_INTERNAL;
-       hap->ha_history_str = str;
-       hap->ha_event = event;
-       hap->ha_zone[0] = '\0';
+       /* if we didn't get a tx from the caller, commit the one we made */
+       if (tx == NULL)
+               dmu_tx_commit(htx);
+}
 
-       if (dmu_tx_is_syncing(tx)) {
-               spa_history_log_sync(spa, hap, cr, tx);
-       } else {
-               dsl_sync_task_do_nowait(spa_get_dsl(spa), NULL,
-                   spa_history_log_sync, spa, hap, 0, tx);
+void
+spa_history_log_version(spa_t *spa, history_internal_events_t event)
+{
+#ifdef _KERNEL
+       uint64_t current_vers = spa_version(spa);
+
+       if (current_vers >= SPA_VERSION_ZPOOL_HISTORY) {
+               spa_history_log_internal(event, spa, NULL,
+                   "pool spa %llu; zfs spa %llu; zpl %d; uts %s %s %s %s",
+                   (u_longlong_t)current_vers, SPA_VERSION, ZPL_VERSION,
+                   utsname.nodename, utsname.release, utsname.version,
+                   utsname.machine);
        }
-       /* spa_history_log_sync() will free hap and str */
+       cmn_err(CE_CONT, "!%s version %llu pool %s using %llu",
+           event == LOG_POOL_IMPORT ? "imported" :
+           event == LOG_POOL_CREATE ? "created" : "accessed",
+           (u_longlong_t)current_vers, spa_name(spa), SPA_VERSION);
+#endif
 }
+
+#if defined(_KERNEL) && defined(HAVE_SPL)
+EXPORT_SYMBOL(spa_history_create_obj);
+EXPORT_SYMBOL(spa_history_get);
+EXPORT_SYMBOL(spa_history_log);
+EXPORT_SYMBOL(spa_history_log_internal);
+EXPORT_SYMBOL(spa_history_log_version);
+#endif