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
This commit is contained in:
Andriy Gapon 2016-05-16 15:28:39 +00:00
parent 705e6b8170
commit 0ab1aa90fa

View File

@ -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);