Add support for parallel pool exports

Changed spa_export_common() such that it no longer holds the
spa_namespace_lock for the entire duration and instead sets
spa_export_thread to indicate an import is in progress on the
spa.  This allows for an export to a diffent pool to proceed
in parallel while an export is still processing potentially
long operations like spa_unload_log_sm_flush_all().

Calls like spa_lookup() and spa_vdev_enter() that rely on
the spa_namespace_lock to serialize them against a concurrent
export, now wait for any in-progress export thread to complete
before proceeding.

The 'zpool import -a' sub-command also provides multi-threaded
support, using a thread pool to submit the exports in parallel.

Sponsored-By: Klara Inc.
Sponsored-by: Wasabi Technology, Inc.
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: George Wilson <gwilson@delphix.com>
Signed-off-by: Don Brady <don.brady@klarasystems.com>
Closes #16153
This commit is contained in:
Don Brady
2024-05-02 19:28:10 +00:00
committed by Brian Behlendorf
parent abec7dcd30
commit 975a13259b
12 changed files with 373 additions and 33 deletions
+82 -6
View File
@@ -2030,10 +2030,19 @@ zpool_do_destroy(int argc, char **argv)
}
typedef struct export_cbdata {
tpool_t *tpool;
pthread_mutex_t mnttab_lock;
boolean_t force;
boolean_t hardforce;
int retval;
} export_cbdata_t;
typedef struct {
char *aea_poolname;
export_cbdata_t *aea_cbdata;
} async_export_args_t;
/*
* Export one pool
*/
@@ -2042,11 +2051,20 @@ zpool_export_one(zpool_handle_t *zhp, void *data)
{
export_cbdata_t *cb = data;
if (zpool_disable_datasets(zhp, cb->force) != 0)
return (1);
/*
* zpool_disable_datasets() is not thread-safe for mnttab access.
* So we serialize access here for 'zpool export -a' parallel case.
*/
if (cb->tpool != NULL)
pthread_mutex_lock(&cb->mnttab_lock);
/* The history must be logged as part of the export */
log_history = B_FALSE;
int retval = zpool_disable_datasets(zhp, cb->force);
if (cb->tpool != NULL)
pthread_mutex_unlock(&cb->mnttab_lock);
if (retval)
return (1);
if (cb->hardforce) {
if (zpool_export_force(zhp, history_str) != 0)
@@ -2058,6 +2076,48 @@ zpool_export_one(zpool_handle_t *zhp, void *data)
return (0);
}
/*
* Asynchronous export request
*/
static void
zpool_export_task(void *arg)
{
async_export_args_t *aea = arg;
zpool_handle_t *zhp = zpool_open(g_zfs, aea->aea_poolname);
if (zhp != NULL) {
int ret = zpool_export_one(zhp, aea->aea_cbdata);
if (ret != 0)
aea->aea_cbdata->retval = ret;
zpool_close(zhp);
} else {
aea->aea_cbdata->retval = 1;
}
free(aea->aea_poolname);
free(aea);
}
/*
* Process an export request in parallel
*/
static int
zpool_export_one_async(zpool_handle_t *zhp, void *data)
{
tpool_t *tpool = ((export_cbdata_t *)data)->tpool;
async_export_args_t *aea = safe_malloc(sizeof (async_export_args_t));
/* save pool name since zhp will go out of scope */
aea->aea_poolname = strdup(zpool_get_name(zhp));
aea->aea_cbdata = data;
/* ship off actual export to another thread */
if (tpool_dispatch(tpool, zpool_export_task, (void *)aea) != 0)
return (errno); /* unlikely */
else
return (0);
}
/*
* zpool export [-f] <pool> ...
*
@@ -2098,17 +2158,33 @@ zpool_do_export(int argc, char **argv)
cb.force = force;
cb.hardforce = hardforce;
cb.tpool = NULL;
cb.retval = 0;
argc -= optind;
argv += optind;
/* The history will be logged as part of the export itself */
log_history = B_FALSE;
if (do_all) {
if (argc != 0) {
(void) fprintf(stderr, gettext("too many arguments\n"));
usage(B_FALSE);
}
return (for_each_pool(argc, argv, B_TRUE, NULL,
ZFS_TYPE_POOL, B_FALSE, zpool_export_one, &cb));
cb.tpool = tpool_create(1, 5 * sysconf(_SC_NPROCESSORS_ONLN),
0, NULL);
pthread_mutex_init(&cb.mnttab_lock, NULL);
/* Asynchronously call zpool_export_one using thread pool */
ret = for_each_pool(argc, argv, B_TRUE, NULL, ZFS_TYPE_POOL,
B_FALSE, zpool_export_one_async, &cb);
tpool_wait(cb.tpool);
tpool_destroy(cb.tpool);
(void) pthread_mutex_destroy(&cb.mnttab_lock);
return (ret | cb.retval);
}
/* check arguments */