diff options
author | avg <avg@FreeBSD.org> | 2014-01-16 12:22:46 +0000 |
---|---|---|
committer | avg <avg@FreeBSD.org> | 2014-01-16 12:22:46 +0000 |
commit | 97986ccb0b599062a18e0153ae8cba713fde7df5 (patch) | |
tree | 04715115a71000f63de3ed5641fbd5db2741a863 /sys/cddl | |
parent | f9bc6dc89c596d9c742b7f12fbbaad8656b640fe (diff) | |
download | FreeBSD-src-97986ccb0b599062a18e0153ae8cba713fde7df5.zip FreeBSD-src-97986ccb0b599062a18e0153ae8cba713fde7df5.tar.gz |
zfs: getnewvnode_reserve must be called outside of a zfs transaction
Otherwise we could run into the following deadlock.
A thread has a transaction open and assigned to a transaction group.
That would prevent the transaction group from be quiesced and synced.
The thread is blocked in getnewvnode_reserve waiting for a vnode to
a be reclaimed. vnlru thread is blocked trying to enter ZFS VOP because
a filesystem is suspended by an ongoing rollback or receive operation.
In its turn the operation is waiting for the current transaction group
to be synced.
zfs_zget is always used outside of active transactions, but zfs_mknode
is always used in a transaction context. Thus, we hoist
getnewvnode_reserve from zfs_mknode to its callers.
While there, assert that ZFS always calls getnewvnode while having
a vnode reserved.
Reported by: adrian
Tested by: adrian
MFC after: 17 days
Sponsored by: HybridCluster
Diffstat (limited to 'sys/cddl')
3 files changed, 30 insertions, 2 deletions
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_dir.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_dir.c index 1f7fee2..be96da9 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_dir.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_dir.c @@ -951,6 +951,8 @@ zfs_make_xattrdir(znode_t *zp, vattr_t *vap, vnode_t **xvpp, cred_t *cr) return (SET_ERROR(EDQUOT)); } + getnewvnode_reserve(1); + tx = dmu_tx_create(zfsvfs->z_os); dmu_tx_hold_sa_create(tx, acl_ids.z_aclp->z_acl_bytes + ZFS_SA_BASE_ATTR_SIZE); @@ -985,6 +987,8 @@ zfs_make_xattrdir(znode_t *zp, vattr_t *vap, vnode_t **xvpp, cred_t *cr) zfs_acl_ids_free(&acl_ids); dmu_tx_commit(tx); + getnewvnode_drop_reserve(); + *xvpp = ZTOV(xzp); return (0); diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c index 0339801..8699110 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vnops.c @@ -1625,6 +1625,9 @@ zfs_create(vnode_t *dvp, char *name, vattr_t *vap, int excl, int mode, return (error); } } + + getnewvnode_reserve(1); + top: *vpp = NULL; @@ -1653,6 +1656,7 @@ top: zfs_acl_ids_free(&acl_ids); if (strcmp(name, "..") == 0) error = SET_ERROR(EISDIR); + getnewvnode_drop_reserve(); ZFS_EXIT(zfsvfs); return (error); } @@ -1721,6 +1725,7 @@ top: } zfs_acl_ids_free(&acl_ids); dmu_tx_abort(tx); + getnewvnode_drop_reserve(); ZFS_EXIT(zfsvfs); return (error); } @@ -1787,6 +1792,7 @@ top: } } out: + getnewvnode_drop_reserve(); if (dl) zfs_dirent_unlock(dl); @@ -2130,6 +2136,9 @@ zfs_mkdir(vnode_t *dvp, char *dirname, vattr_t *vap, vnode_t **vpp, cred_t *cr, ZFS_EXIT(zfsvfs); return (error); } + + getnewvnode_reserve(1); + /* * First make sure the new directory doesn't exist. * @@ -2143,6 +2152,7 @@ top: if (error = zfs_dirent_lock(&dl, dzp, dirname, &zp, zf, NULL, NULL)) { zfs_acl_ids_free(&acl_ids); + getnewvnode_drop_reserve(); ZFS_EXIT(zfsvfs); return (error); } @@ -2150,6 +2160,7 @@ top: if (error = zfs_zaccess(dzp, ACE_ADD_SUBDIRECTORY, 0, B_FALSE, cr)) { zfs_acl_ids_free(&acl_ids); zfs_dirent_unlock(dl); + getnewvnode_drop_reserve(); ZFS_EXIT(zfsvfs); return (error); } @@ -2157,6 +2168,7 @@ top: if (zfs_acl_ids_overquota(zfsvfs, &acl_ids)) { zfs_acl_ids_free(&acl_ids); zfs_dirent_unlock(dl); + getnewvnode_drop_reserve(); ZFS_EXIT(zfsvfs); return (SET_ERROR(EDQUOT)); } @@ -2189,6 +2201,7 @@ top: } zfs_acl_ids_free(&acl_ids); dmu_tx_abort(tx); + getnewvnode_drop_reserve(); ZFS_EXIT(zfsvfs); return (error); } @@ -2218,6 +2231,8 @@ top: dmu_tx_commit(tx); + getnewvnode_drop_reserve(); + zfs_dirent_unlock(dl); if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) @@ -4109,6 +4124,9 @@ zfs_symlink(vnode_t *dvp, vnode_t **vpp, char *name, vattr_t *vap, char *link, ZFS_EXIT(zfsvfs); return (error); } + + getnewvnode_reserve(1); + top: /* * Attempt to lock directory; fail if entry already exists. @@ -4116,6 +4134,7 @@ top: error = zfs_dirent_lock(&dl, dzp, name, &zp, zflg, NULL, NULL); if (error) { zfs_acl_ids_free(&acl_ids); + getnewvnode_drop_reserve(); ZFS_EXIT(zfsvfs); return (error); } @@ -4123,6 +4142,7 @@ top: if (error = zfs_zaccess(dzp, ACE_ADD_FILE, 0, B_FALSE, cr)) { zfs_acl_ids_free(&acl_ids); zfs_dirent_unlock(dl); + getnewvnode_drop_reserve(); ZFS_EXIT(zfsvfs); return (error); } @@ -4130,6 +4150,7 @@ top: if (zfs_acl_ids_overquota(zfsvfs, &acl_ids)) { zfs_acl_ids_free(&acl_ids); zfs_dirent_unlock(dl); + getnewvnode_drop_reserve(); ZFS_EXIT(zfsvfs); return (SET_ERROR(EDQUOT)); } @@ -4157,6 +4178,7 @@ top: } zfs_acl_ids_free(&acl_ids); dmu_tx_abort(tx); + getnewvnode_drop_reserve(); ZFS_EXIT(zfsvfs); return (error); } @@ -4195,6 +4217,8 @@ top: dmu_tx_commit(tx); + getnewvnode_drop_reserve(); + zfs_dirent_unlock(dl); if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS) diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_znode.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_znode.c index 5c18671..8db16d5 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_znode.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_znode.c @@ -624,6 +624,8 @@ zfs_znode_alloc(zfsvfs_t *zfsvfs, dmu_buf_t *db, int blksz, zp = kmem_cache_alloc(znode_cache, KM_SLEEP); + KASSERT(td->td_vp_reserv > 0, + ("zfs_znode_alloc: getnewvnode without any vnodes reserved")); error = getnewvnode("zfs", zfsvfs->z_parent->z_vfs, &zfs_vnodeops, &vp); if (error != 0) { kmem_cache_free(znode_cache, zp); @@ -830,7 +832,6 @@ zfs_mknode(znode_t *dzp, vattr_t *vap, dmu_tx_t *tx, cred_t *cr, } } - getnewvnode_reserve(1); ZFS_OBJ_HOLD_ENTER(zfsvfs, obj); VERIFY(0 == sa_buf_hold(zfsvfs->z_os, obj, NULL, &db)); @@ -1016,7 +1017,6 @@ zfs_mknode(znode_t *dzp, vattr_t *vap, dmu_tx_t *tx, cred_t *cr, KASSERT(err == 0, ("insmntque() failed: error %d", err)); } ZFS_OBJ_HOLD_EXIT(zfsvfs, obj); - getnewvnode_drop_reserve(); } /* |