Optionally skip zil_close during zvol_create_minor_impl

If there were no zil entries to replay, skip zil_close.  zil_close waits
for a transaction to sync.  That can take several seconds, for example
during pool import of a resilvering pool.  Skipping zil_close can cut
the time for "zpool import" from 2 hours to 45 seconds on a resilvering
pool with a thousand zvols.

Reviewed-by: Richard Yao <richard.yao@alumni.stonybrook.edu>
Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Reviewed-by: Ryan Moeller <ryan@iXsystems.com>
Sponsored-by: Axcient
Closes #13999 
Closes #14015
This commit is contained in:
Alan Somers 2022-11-08 13:38:08 -07:00 committed by GitHub
parent f224eddf92
commit e197bb24f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 22 additions and 13 deletions

View File

@ -539,10 +539,10 @@ extern zilog_t *zil_open(objset_t *os, zil_get_data_t *get_data,
zil_sums_t *zil_sums); zil_sums_t *zil_sums);
extern void zil_close(zilog_t *zilog); extern void zil_close(zilog_t *zilog);
extern void zil_replay(objset_t *os, void *arg, extern boolean_t zil_replay(objset_t *os, void *arg,
zil_replay_func_t *const replay_func[TX_MAX_TYPE]); zil_replay_func_t *const replay_func[TX_MAX_TYPE]);
extern boolean_t zil_replaying(zilog_t *zilog, dmu_tx_t *tx); extern boolean_t zil_replaying(zilog_t *zilog, dmu_tx_t *tx);
extern void zil_destroy(zilog_t *zilog, boolean_t keep_first); extern boolean_t zil_destroy(zilog_t *zilog, boolean_t keep_first);
extern void zil_destroy_sync(zilog_t *zilog, dmu_tx_t *tx); extern void zil_destroy_sync(zilog_t *zilog, dmu_tx_t *tx);
extern itx_t *zil_itx_create(uint64_t txtype, size_t lrsize); extern itx_t *zil_itx_create(uint64_t txtype, size_t lrsize);

View File

@ -1386,6 +1386,7 @@ zvol_os_create_minor(const char *name)
uint64_t volsize; uint64_t volsize;
uint64_t volmode, hash; uint64_t volmode, hash;
int error; int error;
bool replayed_zil = B_FALSE;
ZFS_LOG(1, "Creating ZVOL %s...", name); ZFS_LOG(1, "Creating ZVOL %s...", name);
hash = zvol_name_hash(name); hash = zvol_name_hash(name);
@ -1490,10 +1491,11 @@ zvol_os_create_minor(const char *name)
zv->zv_zilog = zil_open(os, zvol_get_data, &zv->zv_kstat.dk_zil_sums); zv->zv_zilog = zil_open(os, zvol_get_data, &zv->zv_kstat.dk_zil_sums);
if (spa_writeable(dmu_objset_spa(os))) { if (spa_writeable(dmu_objset_spa(os))) {
if (zil_replay_disable) if (zil_replay_disable)
zil_destroy(zv->zv_zilog, B_FALSE); replayed_zil = zil_destroy(zv->zv_zilog, B_FALSE);
else else
zil_replay(os, zv, zvol_replay_vector); replayed_zil = zil_replay(os, zv, zvol_replay_vector);
} }
if (replayed_zil)
zil_close(zv->zv_zilog); zil_close(zv->zv_zilog);
zv->zv_zilog = NULL; zv->zv_zilog = NULL;

View File

@ -1279,6 +1279,7 @@ zvol_os_create_minor(const char *name)
int error = 0; int error = 0;
int idx; int idx;
uint64_t hash = zvol_name_hash(name); uint64_t hash = zvol_name_hash(name);
bool replayed_zil = B_FALSE;
if (zvol_inhibit_dev) if (zvol_inhibit_dev)
return (0); return (0);
@ -1420,10 +1421,11 @@ zvol_os_create_minor(const char *name)
zv->zv_zilog = zil_open(os, zvol_get_data, &zv->zv_kstat.dk_zil_sums); zv->zv_zilog = zil_open(os, zvol_get_data, &zv->zv_kstat.dk_zil_sums);
if (spa_writeable(dmu_objset_spa(os))) { if (spa_writeable(dmu_objset_spa(os))) {
if (zil_replay_disable) if (zil_replay_disable)
zil_destroy(zv->zv_zilog, B_FALSE); replayed_zil = zil_destroy(zv->zv_zilog, B_FALSE);
else else
zil_replay(os, zv, zvol_replay_vector); replayed_zil = zil_replay(os, zv, zvol_replay_vector);
} }
if (replayed_zil)
zil_close(zv->zv_zilog); zil_close(zv->zv_zilog);
zv->zv_zilog = NULL; zv->zv_zilog = NULL;

View File

@ -887,8 +887,9 @@ zil_create(zilog_t *zilog)
* txg_wait_synced() here either when keep_first is set, because both * txg_wait_synced() here either when keep_first is set, because both
* zil_create() and zil_destroy() will wait for any in-progress destroys * zil_create() and zil_destroy() will wait for any in-progress destroys
* to complete. * to complete.
* Return B_TRUE if there were any entries to replay.
*/ */
void boolean_t
zil_destroy(zilog_t *zilog, boolean_t keep_first) zil_destroy(zilog_t *zilog, boolean_t keep_first)
{ {
const zil_header_t *zh = zilog->zl_header; const zil_header_t *zh = zilog->zl_header;
@ -904,7 +905,7 @@ zil_destroy(zilog_t *zilog, boolean_t keep_first)
zilog->zl_old_header = *zh; /* debugging aid */ zilog->zl_old_header = *zh; /* debugging aid */
if (BP_IS_HOLE(&zh->zh_log)) if (BP_IS_HOLE(&zh->zh_log))
return; return (B_FALSE);
tx = dmu_tx_create(zilog->zl_os); tx = dmu_tx_create(zilog->zl_os);
VERIFY0(dmu_tx_assign(tx, TXG_WAIT)); VERIFY0(dmu_tx_assign(tx, TXG_WAIT));
@ -937,6 +938,8 @@ zil_destroy(zilog_t *zilog, boolean_t keep_first)
mutex_exit(&zilog->zl_lock); mutex_exit(&zilog->zl_lock);
dmu_tx_commit(tx); dmu_tx_commit(tx);
return (B_TRUE);
} }
void void
@ -3849,8 +3852,9 @@ zil_incr_blks(zilog_t *zilog, const blkptr_t *bp, void *arg, uint64_t claim_txg)
/* /*
* If this dataset has a non-empty intent log, replay it and destroy it. * If this dataset has a non-empty intent log, replay it and destroy it.
* Return B_TRUE if there were any entries to replay.
*/ */
void boolean_t
zil_replay(objset_t *os, void *arg, zil_replay(objset_t *os, void *arg,
zil_replay_func_t *const replay_func[TX_MAX_TYPE]) zil_replay_func_t *const replay_func[TX_MAX_TYPE])
{ {
@ -3859,8 +3863,7 @@ zil_replay(objset_t *os, void *arg,
zil_replay_arg_t zr; zil_replay_arg_t zr;
if ((zh->zh_flags & ZIL_REPLAY_NEEDED) == 0) { if ((zh->zh_flags & ZIL_REPLAY_NEEDED) == 0) {
zil_destroy(zilog, B_TRUE); return (zil_destroy(zilog, B_TRUE));
return;
} }
zr.zr_replay = replay_func; zr.zr_replay = replay_func;
@ -3883,6 +3886,8 @@ zil_replay(objset_t *os, void *arg,
zil_destroy(zilog, B_FALSE); zil_destroy(zilog, B_FALSE);
txg_wait_synced(zilog->zl_dmu_pool, zilog->zl_destroy_txg); txg_wait_synced(zilog->zl_dmu_pool, zilog->zl_destroy_txg);
zilog->zl_replay = B_FALSE; zilog->zl_replay = B_FALSE;
return (B_TRUE);
} }
boolean_t boolean_t