diff options
author | avg <avg@FreeBSD.org> | 2016-05-16 15:28:39 +0000 |
---|---|---|
committer | avg <avg@FreeBSD.org> | 2016-05-16 15:28:39 +0000 |
commit | 77ee692358b84d49d81b84c705673d8fb2939f74 (patch) | |
tree | 7f493cd017d27b533f72ce53dc4bf7f5e1aa09e5 /sys/cddl | |
parent | fd1f834a75d4b8bf74209551230c614be7c9f20b (diff) | |
download | FreeBSD-src-77ee692358b84d49d81b84c705673d8fb2939f74.zip FreeBSD-src-77ee692358b84d49d81b84c705673d8fb2939f74.tar.gz |
fix locking in zfsctl_root_lookup
Dropping the root vnode's lock after VFS_ROOT() didn't really help the
fact that we acquired the lock while holding its child's, .zfs, lock
while performing the operaiton.
So, directly use zfs_zget() to get the root vnode.
While there simplify the code in zfsctl_freebsd_root_lookup.
We know that .zfs is always exclusively locked.
We know that there is already a reference on *vpp, so no need for an
extra one.
Account for the fact that .. lookup may ask for a different lock type,
not necessarily LK_EXCLUSIVE. And handle a possible failure to acquire
the lock given the lock flags.
MFC after: 5 weeks
Diffstat (limited to 'sys/cddl')
-rw-r--r-- | sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ctldir.c | 30 |
1 files changed, 20 insertions, 10 deletions
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ctldir.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ctldir.c index ec4518d..743e1f8 100644 --- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ctldir.c +++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ctldir.c @@ -537,9 +537,20 @@ zfsctl_root_lookup(vnode_t *dvp, char *nm, vnode_t **vpp, pathname_t *pnp, ZFS_ENTER(zfsvfs); if (strcmp(nm, "..") == 0) { +#ifdef illumos err = VFS_ROOT(dvp->v_vfsp, LK_EXCLUSIVE, vpp); +#else + /* + * NB: can not use VFS_ROOT here as it would acquire + * the vnode lock of the parent (root) vnode while + * holding the child's (.zfs) lock. + */ + znode_t *rootzp; + + err = zfs_zget(zfsvfs, zfsvfs->z_root, &rootzp); if (err == 0) - VOP_UNLOCK(*vpp, 0); + *vpp = ZTOV(rootzp); +#endif } else { err = gfs_vop_lookup(dvp, nm, vpp, pnp, flags, rdir, cr, ct, direntflags, realpnp); @@ -601,10 +612,10 @@ zfsctl_freebsd_root_lookup(ap) vnode_t **vpp = ap->a_vpp; cred_t *cr = ap->a_cnp->cn_cred; int flags = ap->a_cnp->cn_flags; + int lkflags = ap->a_cnp->cn_lkflags; int nameiop = ap->a_cnp->cn_nameiop; char nm[NAME_MAX + 1]; int err; - int ltype; if ((flags & ISLASTCN) && (nameiop == RENAME || nameiop == CREATE)) return (EOPNOTSUPP); @@ -613,16 +624,15 @@ zfsctl_freebsd_root_lookup(ap) strlcpy(nm, ap->a_cnp->cn_nameptr, ap->a_cnp->cn_namelen + 1); err = zfsctl_root_lookup(dvp, nm, vpp, NULL, 0, NULL, cr, NULL, NULL, NULL); if (err == 0 && (nm[0] != '.' || nm[1] != '\0')) { - ltype = VOP_ISLOCKED(dvp); - if (flags & ISDOTDOT) { - VN_HOLD(*vpp); + if (flags & ISDOTDOT) VOP_UNLOCK(dvp, 0); + err = vn_lock(*vpp, lkflags); + if (err != 0) { + vrele(*vpp); + *vpp = NULL; } - vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY); - if (flags & ISDOTDOT) { - VN_RELE(*vpp); - vn_lock(dvp, ltype| LK_RETRY); - } + if (flags & ISDOTDOT) + vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY); } return (err); |