diff --git a/module/zfs/zfs_vfsops.c b/module/zfs/zfs_vfsops.c index 41a1c4d8849d..64dedc2ad0f7 100644 --- a/module/zfs/zfs_vfsops.c +++ b/module/zfs/zfs_vfsops.c @@ -1130,15 +1130,34 @@ EXPORT_SYMBOL(zfs_sb_prune); int zfs_sb_teardown(zfs_sb_t *zsb, boolean_t unmounting) { - znode_t *zp; - /* * If someone has not already unmounted this file system, * 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) + if (zsb->z_os) { + uint64_t b = zsb->z_nr_znodes; + uint64_t a1; + uint64_t a2 = 0; + taskq_wait(dsl_pool_iput_taskq(dmu_objset_pool(zsb->z_os))); + a1 = zsb->z_nr_znodes; + /* + * Iputs run from the taskq may add the parents of dir-based + * xattrs to the taskq: 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 increment z_nr_znodes. + */ + if (zsb->z_nr_znodes > 0) { + taskq_wait(dsl_pool_iput_taskq(dmu_objset_pool( + zsb->z_os))); + a2 = zsb->z_nr_znodes; + } + printk(KERN_WARNING "zfs_sb_teardown: z_nr_znodes before=%llu, " + "after1=%llu, after2=%llu\n", b, a1, a2); + } rrw_enter(&zsb->z_teardown_lock, RW_WRITER, FTAG); @@ -1175,27 +1194,13 @@ zfs_sb_teardown(zfs_sb_t *zsb, boolean_t unmounting) return (SET_ERROR(EIO)); } - /* - * At this point there are no VFS ops active, and any new VFS ops - * will fail with EIO since we have z_teardown_lock for writer (only - * relevant for forced unmount). - * - * 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); - } - mutex_exit(&zsb->z_znodes_lock); - /* * If we are unmounting, set the unmounted flag and let new VFS ops * unblock. zfs_inactive will have the unmounted behavior, and all * other VFS ops will fail with EIO. */ if (unmounting) { + ASSERT(list_is_empty(&zsb->z_all_znodes)); zsb->z_unmounted = B_TRUE; rrw_exit(&zsb->z_teardown_lock, FTAG); rw_exit(&zsb->z_teardown_inactive_lock);