*/
/*
- * 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.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/spa.h>
#include <sys/spa_impl.h>
#include <sys/zap.h>
#include <sys/utsname.h>
#include <sys/cmn_err.h>
#include <sys/sunddi.h>
+#include "zfs_comutil.h"
#ifdef _KERNEL
#include <sys/zone.h>
#endif
* Figure out maximum size of history log. We set it at
* 1% of pool size, with a max of 32MB and min of 128KB.
*/
- shpp->sh_phys_max_off = spa_get_dspace(spa) / 100;
+ shpp->sh_phys_max_off =
+ metaslab_class_get_dspace(spa_normal_class(spa)) / 100;
shpp->sh_phys_max_off = MIN(shpp->sh_phys_max_off, 32<<20);
shpp->sh_phys_max_off = MAX(shpp->sh_phys_max_off, 128<<10);
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);
}
}
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
/*
* 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;
VERIFY(nvlist_alloc(&nvrecord, NV_UNIQUE_NAME, KM_SLEEP) == 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
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);
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);
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));
}
/*
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_SLEEP);
+ 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);
}
/*
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;
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);
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
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_SLEEP);
+ 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