diff --git a/module/zfs/zfs_vfsops.c b/module/zfs/zfs_vfsops.c index 41a1c4d88..59f73776c 100644 --- a/module/zfs/zfs_vfsops.c +++ b/module/zfs/zfs_vfsops.c @@ -1137,8 +1137,28 @@ zfs_sb_teardown(zfs_sb_t *zsb, boolean_t unmounting) * drain the iput_taskq to ensure all active references to the * zfs_sb_t have been handled only then can it be safely destroyed. */ - if (zsb->z_os) - taskq_wait(dsl_pool_iput_taskq(dmu_objset_pool(zsb->z_os))); + if (zsb->z_os) { + /* + * If we're unmounting we have to wait for the list to + * drain completely. + * + * If we're not unmounting there's no guarantee the list + * will drain completely, but iputs run from the taskq + * may add the parents of dir-based xattrs to the taskq + * so we want to wait for these. + * + * We can safely read z_nr_znodes without locking because the + * VFS has already blocked operations which add to the + * z_all_znodes list and thus increment z_nr_znodes. + */ + int round = 0; + while (zsb->z_nr_znodes > 0) { + taskq_wait(dsl_pool_iput_taskq(dmu_objset_pool( + zsb->z_os))); + if (++round > 1 && !unmounting) + break; + } + } rrw_enter(&zsb->z_teardown_lock, RW_WRITER, FTAG); @@ -1182,13 +1202,15 @@ zfs_sb_teardown(zfs_sb_t *zsb, boolean_t unmounting) * * Release all holds on dbufs. */ - mutex_enter(&zsb->z_znodes_lock); - for (zp = list_head(&zsb->z_all_znodes); zp != NULL; - zp = list_next(&zsb->z_all_znodes, zp)) { - if (zp->z_sa_hdl) - zfs_znode_dmu_fini(zp); + if (!unmounting) { + mutex_enter(&zsb->z_znodes_lock); + for (zp = list_head(&zsb->z_all_znodes); zp != NULL; + zp = list_next(&zsb->z_all_znodes, zp)) { + if (zp->z_sa_hdl) + zfs_znode_dmu_fini(zp); + } + mutex_exit(&zsb->z_znodes_lock); } - mutex_exit(&zsb->z_znodes_lock); /* * If we are unmounting, set the unmounted flag and let new VFS ops