ASSERT(MUTEX_HELD(&spa_namespace_lock));
+ spa_import_progress_remove(spa_guid(spa));
spa_load_note(spa, "UNLOADING");
/*
int error;
spa->spa_load_state = state;
+ (void) spa_import_progress_set_state(spa_guid(spa),
+ spa_load_state(spa));
gethrestime(&spa->spa_loaded_ts);
error = spa_load_impl(spa, type, &ereport);
spa->spa_load_state = error ? SPA_LOAD_ERROR : SPA_LOAD_NONE;
spa->spa_ena = 0;
+ (void) spa_import_progress_set_state(spa_guid(spa),
+ spa_load_state(spa));
+
return (error);
}
*/
if (ub->ub_mmp_magic == MMP_MAGIC && ub->ub_mmp_delay == 0)
return (B_FALSE);
+
/*
* If the tryconfig_ values are nonzero, they are the results of an
* earlier tryimport. If they all match the uberblock we just found,
import_delay = spa_activity_check_duration(spa, ub);
/* Add a small random factor in case of simultaneous imports (0-25%) */
- import_expire = gethrtime() + import_delay +
- (import_delay * spa_get_random(250) / 1000);
+ import_delay += import_delay * spa_get_random(250) / 1000;
+
+ import_expire = gethrtime() + import_delay;
while (gethrtime() < import_expire) {
+ (void) spa_import_progress_set_mmp_check(spa_guid(spa),
+ NSEC2SEC(import_expire - gethrtime()));
+
vdev_uberblock_load(rvd, ub, &mmp_label);
if (txg != ub->ub_txg || timestamp != ub->ub_timestamp ||
return (spa_vdev_err(rvd, VDEV_AUX_CORRUPT_DATA, ENXIO));
}
+ if (spa->spa_load_max_txg != UINT64_MAX) {
+ (void) spa_import_progress_set_max_txg(spa_guid(spa),
+ (u_longlong_t)spa->spa_load_max_txg);
+ }
spa_load_note(spa, "using uberblock with txg=%llu",
(u_longlong_t)ub->ub_txg);
if (error != 0)
return (error);
+ spa_import_progress_add(spa);
+
/*
* Now that we have the vdev tree, try to open each vdev. This involves
* opening the underlying physical device, retrieving its geometry and
spa_config_exit(spa, SCL_CONFIG, FTAG);
}
+ spa_import_progress_remove(spa_guid(spa));
spa_load_note(spa, "LOADED");
return (0);
* from previous txgs when spa_load fails.
*/
ASSERT(spa->spa_import_flags & ZFS_IMPORT_CHECKPOINT);
+ spa_import_progress_remove(spa_guid(spa));
return (load_error);
}
if (rewind_flags & ZPOOL_NEVER_REWIND) {
nvlist_free(config);
+ spa_import_progress_remove(spa_guid(spa));
return (load_error);
}
if (state == SPA_LOAD_RECOVER) {
ASSERT3P(loadinfo, ==, NULL);
+ spa_import_progress_remove(spa_guid(spa));
return (rewind_error);
} else {
/* Store the rewind info as part of the initial load info */
fnvlist_free(spa->spa_load_info);
spa->spa_load_info = loadinfo;
+ spa_import_progress_remove(spa_guid(spa));
return (load_error);
}
}
return (spa->spa_dsl_pool->dp_dirty_total);
}
+/*
+ * ==========================================================================
+ * SPA Import Progress Routines
+ * ==========================================================================
+ */
+
+typedef struct spa_import_progress {
+ uint64_t pool_guid; /* unique id for updates */
+ char *pool_name;
+ spa_load_state_t spa_load_state;
+ uint64_t mmp_sec_remaining; /* MMP activity check */
+ uint64_t spa_load_max_txg; /* rewind txg */
+ procfs_list_node_t smh_node;
+} spa_import_progress_t;
+
+spa_history_list_t *spa_import_progress_list = NULL;
+
+static int
+spa_import_progress_show_header(struct seq_file *f)
+{
+ seq_printf(f, "%-20s %-14s %-14s %-12s %s\n", "pool_guid",
+ "load_state", "multihost_secs", "max_txg",
+ "pool_name");
+ return (0);
+}
+
+static int
+spa_import_progress_show(struct seq_file *f, void *data)
+{
+ spa_import_progress_t *sip = (spa_import_progress_t *)data;
+
+ seq_printf(f, "%-20llu %-14llu %-14llu %-12llu %s\n",
+ (u_longlong_t)sip->pool_guid, (u_longlong_t)sip->spa_load_state,
+ (u_longlong_t)sip->mmp_sec_remaining,
+ (u_longlong_t)sip->spa_load_max_txg,
+ (sip->pool_name ? sip->pool_name : "-"));
+
+ return (0);
+}
+
+/* Remove oldest elements from list until there are no more than 'size' left */
+static void
+spa_import_progress_truncate(spa_history_list_t *shl, unsigned int size)
+{
+ spa_import_progress_t *sip;
+ while (shl->size > size) {
+ sip = list_remove_head(&shl->procfs_list.pl_list);
+ if (sip->pool_name)
+ spa_strfree(sip->pool_name);
+ kmem_free(sip, sizeof (spa_import_progress_t));
+ shl->size--;
+ }
+
+ IMPLY(size == 0, list_is_empty(&shl->procfs_list.pl_list));
+}
+
+static void
+spa_import_progress_init(void)
+{
+ spa_import_progress_list = kmem_zalloc(sizeof (spa_history_list_t),
+ KM_SLEEP);
+
+ spa_import_progress_list->size = 0;
+
+ spa_import_progress_list->procfs_list.pl_private =
+ spa_import_progress_list;
+
+ procfs_list_install("zfs",
+ "import_progress",
+ 0644,
+ &spa_import_progress_list->procfs_list,
+ spa_import_progress_show,
+ spa_import_progress_show_header,
+ NULL,
+ offsetof(spa_import_progress_t, smh_node));
+}
+
+static void
+spa_import_progress_destroy(void)
+{
+ spa_history_list_t *shl = spa_import_progress_list;
+ procfs_list_uninstall(&shl->procfs_list);
+ spa_import_progress_truncate(shl, 0);
+ kmem_free(shl, sizeof (spa_history_list_t));
+ procfs_list_destroy(&shl->procfs_list);
+}
+
+int
+spa_import_progress_set_state(uint64_t pool_guid,
+ spa_load_state_t load_state)
+{
+ spa_history_list_t *shl = spa_import_progress_list;
+ spa_import_progress_t *sip;
+ int error = ENOENT;
+
+ if (shl->size == 0)
+ return (0);
+
+ mutex_enter(&shl->procfs_list.pl_lock);
+ for (sip = list_tail(&shl->procfs_list.pl_list); sip != NULL;
+ sip = list_prev(&shl->procfs_list.pl_list, sip)) {
+ if (sip->pool_guid == pool_guid) {
+ sip->spa_load_state = load_state;
+ error = 0;
+ break;
+ }
+ }
+ mutex_exit(&shl->procfs_list.pl_lock);
+
+ return (error);
+}
+
+int
+spa_import_progress_set_max_txg(uint64_t pool_guid, uint64_t load_max_txg)
+{
+ spa_history_list_t *shl = spa_import_progress_list;
+ spa_import_progress_t *sip;
+ int error = ENOENT;
+
+ if (shl->size == 0)
+ return (0);
+
+ mutex_enter(&shl->procfs_list.pl_lock);
+ for (sip = list_tail(&shl->procfs_list.pl_list); sip != NULL;
+ sip = list_prev(&shl->procfs_list.pl_list, sip)) {
+ if (sip->pool_guid == pool_guid) {
+ sip->spa_load_max_txg = load_max_txg;
+ error = 0;
+ break;
+ }
+ }
+ mutex_exit(&shl->procfs_list.pl_lock);
+
+ return (error);
+}
+
+int
+spa_import_progress_set_mmp_check(uint64_t pool_guid,
+ uint64_t mmp_sec_remaining)
+{
+ spa_history_list_t *shl = spa_import_progress_list;
+ spa_import_progress_t *sip;
+ int error = ENOENT;
+
+ if (shl->size == 0)
+ return (0);
+
+ mutex_enter(&shl->procfs_list.pl_lock);
+ for (sip = list_tail(&shl->procfs_list.pl_list); sip != NULL;
+ sip = list_prev(&shl->procfs_list.pl_list, sip)) {
+ if (sip->pool_guid == pool_guid) {
+ sip->mmp_sec_remaining = mmp_sec_remaining;
+ error = 0;
+ break;
+ }
+ }
+ mutex_exit(&shl->procfs_list.pl_lock);
+
+ return (error);
+}
+
+/*
+ * A new import is in progress, add an entry.
+ */
+void
+spa_import_progress_add(spa_t *spa)
+{
+ spa_history_list_t *shl = spa_import_progress_list;
+ spa_import_progress_t *sip;
+ char *poolname = NULL;
+
+ sip = kmem_zalloc(sizeof (spa_import_progress_t), KM_SLEEP);
+ sip->pool_guid = spa_guid(spa);
+
+ (void) nvlist_lookup_string(spa->spa_config, ZPOOL_CONFIG_POOL_NAME,
+ &poolname);
+ if (poolname == NULL)
+ poolname = spa_name(spa);
+ sip->pool_name = spa_strdup(poolname);
+ sip->spa_load_state = spa_load_state(spa);
+
+ mutex_enter(&shl->procfs_list.pl_lock);
+ procfs_list_add(&shl->procfs_list, sip);
+ shl->size++;
+ mutex_exit(&shl->procfs_list.pl_lock);
+}
+
+void
+spa_import_progress_remove(uint64_t pool_guid)
+{
+ spa_history_list_t *shl = spa_import_progress_list;
+ spa_import_progress_t *sip;
+
+ mutex_enter(&shl->procfs_list.pl_lock);
+ for (sip = list_tail(&shl->procfs_list.pl_list); sip != NULL;
+ sip = list_prev(&shl->procfs_list.pl_list, sip)) {
+ if (sip->pool_guid == pool_guid) {
+ if (sip->pool_name)
+ spa_strfree(sip->pool_name);
+ list_remove(&shl->procfs_list.pl_list, sip);
+ shl->size--;
+ kmem_free(sip, sizeof (spa_import_progress_t));
+ break;
+ }
+ }
+ mutex_exit(&shl->procfs_list.pl_lock);
+}
+
/*
* ==========================================================================
* Initialization and Termination
l2arc_start();
scan_init();
qat_init();
+ spa_import_progress_init();
}
void
fm_fini();
scan_fini();
qat_fini();
+ spa_import_progress_destroy();
avl_destroy(&spa_namespace_avl);
avl_destroy(&spa_spare_avl);