diff options
author | delphij <delphij@FreeBSD.org> | 2014-07-15 05:36:26 +0000 |
---|---|---|
committer | delphij <delphij@FreeBSD.org> | 2014-07-15 05:36:26 +0000 |
commit | 4d09e20b956fcbbcb5d1311c44c4e1cee4c3b0e4 (patch) | |
tree | 06c882f7c58191d7c93d7600afb79aaec614f3d8 /sys/cddl/contrib/opensolaris/uts/common/fs/zfs/metaslab.c | |
parent | f8b96a7c88fb1506692a5290c444d1494309d443 (diff) | |
download | FreeBSD-src-4d09e20b956fcbbcb5d1311c44c4e1cee4c3b0e4.zip FreeBSD-src-4d09e20b956fcbbcb5d1311c44c4e1cee4c3b0e4.tar.gz |
MFC r268086: MFV r267570:
4756 metaslab_group_preload() could deadlock
Diffstat (limited to 'sys/cddl/contrib/opensolaris/uts/common/fs/zfs/metaslab.c')
-rw-r--r-- | sys/cddl/contrib/opensolaris/uts/common/fs/zfs/metaslab.c | 25 |
1 files changed, 22 insertions, 3 deletions
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/metaslab.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/metaslab.c index 407c6ea..d834e83 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/metaslab.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/metaslab.c @@ -1299,6 +1299,8 @@ metaslab_preload(void *arg) metaslab_t *msp = arg; spa_t *spa = msp->ms_group->mg_vd->vdev_spa; + ASSERT(!MUTEX_HELD(&msp->ms_group->mg_lock)); + mutex_enter(&msp->ms_lock); metaslab_load_wait(msp); if (!msp->ms_loaded) @@ -1323,19 +1325,36 @@ metaslab_group_preload(metaslab_group_t *mg) taskq_wait(mg->mg_taskq); return; } - mutex_enter(&mg->mg_lock); + mutex_enter(&mg->mg_lock); /* - * Prefetch the next potential metaslabs + * Load the next potential metaslabs */ - for (msp = avl_first(t); msp != NULL; msp = AVL_NEXT(t, msp)) { + msp = avl_first(t); + while (msp != NULL) { + metaslab_t *msp_next = AVL_NEXT(t, msp); /* If we have reached our preload limit then we're done */ if (++m > metaslab_preload_limit) break; + /* + * We must drop the metaslab group lock here to preserve + * lock ordering with the ms_lock (when grabbing both + * the mg_lock and the ms_lock, the ms_lock must be taken + * first). As a result, it is possible that the ordering + * of the metaslabs within the avl tree may change before + * we reacquire the lock. The metaslab cannot be removed from + * the tree while we're in syncing context so it is safe to + * drop the mg_lock here. If the metaslabs are reordered + * nothing will break -- we just may end up loading a + * less than optimal one. + */ + mutex_exit(&mg->mg_lock); VERIFY(taskq_dispatch(mg->mg_taskq, metaslab_preload, msp, TQ_SLEEP) != 0); + mutex_enter(&mg->mg_lock); + msp = msp_next; } mutex_exit(&mg->mg_lock); } |