Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_vfsops.h =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_vfsops.h +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_vfsops.h @@ -85,6 +85,7 @@ sa_attr_type_t *z_attr_table; /* SA attr mapping->id */ #define ZFS_OBJ_MTX_SZ 64 kmutex_t z_hold_mtx[ZFS_OBJ_MTX_SZ]; /* znode hold locks */ + struct task z_unlinked_drain_task; }; /* Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_dir.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_dir.c +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_dir.c @@ -393,7 +393,6 @@ { zfsvfs_t *zfsvfs = zp->z_zfsvfs; objset_t *os = zfsvfs->z_os; - znode_t *xzp = NULL; dmu_tx_t *tx; uint64_t acl_obj; uint64_t xattr_obj; @@ -443,11 +442,8 @@ */ error = sa_lookup(zp->z_sa_hdl, SA_ZPL_XATTR(zfsvfs), &xattr_obj, sizeof (xattr_obj)); - if (error == 0 && xattr_obj) { - error = zfs_zget(zfsvfs, xattr_obj, &xzp); - ASSERT3S(error, ==, 0); - vn_lock(ZTOV(xzp), LK_EXCLUSIVE | LK_RETRY); - } + if (error) + xattr_obj = 0; acl_obj = zfs_external_acl(zp); @@ -457,10 +453,8 @@ tx = dmu_tx_create(os); dmu_tx_hold_free(tx, zp->z_id, 0, DMU_OBJECT_END); dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, FALSE, NULL); - if (xzp) { + if (xattr_obj) dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, TRUE, NULL); - dmu_tx_hold_sa(tx, xzp->z_sa_hdl, B_FALSE); - } if (acl_obj) dmu_tx_hold_free(tx, acl_obj, 0, DMU_OBJECT_END); @@ -475,16 +469,13 @@ dmu_tx_abort(tx); zfs_znode_dmu_fini(zp); zfs_znode_free(zp); - goto out; + return; } - if (xzp) { - ASSERT(error == 0); - xzp->z_unlinked = B_TRUE; /* mark xzp for deletion */ - xzp->z_links = 0; /* no more links to it */ - VERIFY(0 == sa_update(xzp->z_sa_hdl, SA_ZPL_LINKS(zfsvfs), - &xzp->z_links, sizeof (xzp->z_links), tx)); - zfs_unlinked_add(xzp, tx); + if (xattr_obj) { + /* Add extended attribute directory to the unlinked set. */ + VERIFY3U(0, ==, + zap_add_int(os, zfsvfs->z_unlinkedobj, xattr_obj, tx)); } /* Remove this znode from the unlinked set */ @@ -494,9 +485,16 @@ zfs_znode_delete(zp, tx); dmu_tx_commit(tx); -out: - if (xzp) - vput(ZTOV(xzp)); + + if (xattr_obj) { + /* + * We're using the FreeBSD taskqueue API here instead of + * the Solaris taskq API since the FreeBSD API allows for a + * task to be enqueued multiple times but executed once. + */ + taskqueue_enqueue(system_taskq->tq_queue, + &zfsvfs->z_unlinked_drain_task); + } } static uint64_t Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c @@ -102,6 +102,8 @@ static void zfs_objset_close(zfsvfs_t *zfsvfs); static void zfs_freevfs(vfs_t *vfsp); +static task_fn_t zfsvfs_task_unlinked_drain; + struct vfsops zfs_vfsops = { .vfs_mount = zfs_mount, .vfs_unmount = zfs_umount, @@ -1023,6 +1025,8 @@ mutex_init(&zfsvfs->z_lock, NULL, MUTEX_DEFAULT, NULL); list_create(&zfsvfs->z_all_znodes, sizeof (znode_t), offsetof(znode_t, z_link_node)); + TASK_INIT(&zfsvfs->z_unlinked_drain_task, 0, + zfsvfs_task_unlinked_drain, zfsvfs); #ifdef DIAGNOSTIC rrm_init(&zfsvfs->z_teardown_lock, B_TRUE); #else @@ -2015,6 +2019,11 @@ } #endif + while (taskqueue_cancel(system_taskq->tq_queue, + &zfsvfs->z_unlinked_drain_task, NULL) != 0) + taskqueue_drain(system_taskq->tq_queue, + &zfsvfs->z_unlinked_drain_task); + VERIFY(zfsvfs_teardown(zfsvfs, B_TRUE) == 0); os = zfsvfs->z_os; @@ -2559,3 +2568,10 @@ mtx_unlock(&mountlist_mtx); } #endif + +static void +zfsvfs_task_unlinked_drain(void *context, int pending __unused) +{ + + zfs_unlinked_drain((zfsvfs_t *)context); +}