From 48ea2cb2ca9b5c828fc829924277900c2a269471 Mon Sep 17 00:00:00 2001 From: jhb Date: Mon, 9 Feb 2009 20:50:23 +0000 Subject: Mark udf(4) MPSAFE and add support for shared vnode locks during pathname lookups: - Honor the caller's locking flags in udf_root() and udf_vget(). - Set VV_ROOT for the root vnode in udf_vget() instead of only doing it in udf_root(). - Honor the requested locking flags during pathname lookups in udf_lookup(). - Release the buffer holding the directory data before looking up the vnode for a given file to avoid a LOR between the "udf" vnode locks and "bufwait". - Use vn_vget_ino() to handle ".." lookups. - Special case "." lookups instead of calling udf_vget(). We have to do extra checking for the vnode lock for "." lookups. --- sys/fs/udf/udf_vfsops.c | 35 +++++++++++++++++++++++++---------- sys/fs/udf/udf_vnops.c | 44 ++++++++++++++++++++++++++++++-------------- 2 files changed, 55 insertions(+), 24 deletions(-) diff --git a/sys/fs/udf/udf_vfsops.c b/sys/fs/udf/udf_vfsops.c index faab9df..3d3b86d 100644 --- a/sys/fs/udf/udf_vfsops.c +++ b/sys/fs/udf/udf_vfsops.c @@ -344,6 +344,7 @@ udf_mountfs(struct vnode *devvp, struct mount *mp) { mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum; MNT_ILOCK(mp); mp->mnt_flag |= MNT_LOCAL; + mp->mnt_kern_flag |= MNTK_MPSAFE | MNTK_LOOKUP_SHARED; MNT_IUNLOCK(mp); udfmp->im_mountp = mp; udfmp->im_dev = devvp->v_rdev; @@ -546,22 +547,13 @@ static int udf_root(struct mount *mp, int flags, struct vnode **vpp, struct thread *td) { struct udf_mnt *udfmp; - struct vnode *vp; ino_t id; - int error; udfmp = VFSTOUDFFS(mp); id = udf_getid(&udfmp->root_icb); - error = udf_vget(mp, id, LK_EXCLUSIVE, vpp); - if (error) - return error; - - vp = *vpp; - vp->v_vflag |= VV_ROOT; - - return (0); + return (udf_vget(mp, id, flags, vpp)); } static int @@ -597,6 +589,22 @@ udf_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp) if (error || *vpp != NULL) return (error); + /* + * We must promote to an exclusive lock for vnode creation. This + * can happen if lookup is passed LOCKSHARED. + */ + if ((flags & LK_TYPE_MASK) == LK_SHARED) { + flags &= ~LK_TYPE_MASK; + flags |= LK_EXCLUSIVE; + } + + /* + * We do not lock vnode creation as it is believed to be too + * expensive for such rare case as simultaneous creation of vnode + * for same ino by different processes. We just allow them to race + * and check later to decide who wins. Let the race begin! + */ + td = curthread; udfmp = VFSTOUDFFS(mp); @@ -689,6 +697,13 @@ udf_vget(struct mount *mp, ino_t ino, int flags, struct vnode **vpp) vp->v_type = VLNK; break; } + + if (vp->v_type != VFIFO) + VN_LOCK_ASHARE(vp); + + if (ino == udf_getid(&udfmp->root_icb)) + vp->v_vflag |= VV_ROOT; + *vpp = vp; return (0); diff --git a/sys/fs/udf/udf_vnops.c b/sys/fs/udf/udf_vnops.c index a80c3ce..05481bc 100644 --- a/sys/fs/udf/udf_vnops.c +++ b/sys/fs/udf/udf_vnops.c @@ -1068,13 +1068,14 @@ udf_lookup(struct vop_cachedlookup_args *a) long namelen; ino_t id = 0; int offset, error = 0; - int numdirpasses, fsize; + int fsize, lkflags, ltype, numdirpasses; dvp = a->a_dvp; node = VTON(dvp); udfmp = node->udfmp; nameiop = a->a_cnp->cn_nameiop; flags = a->a_cnp->cn_flags; + lkflags = a->a_cnp->cn_lkflags; nameptr = a->a_cnp->cn_nameptr; namelen = a->a_cnp->cn_namelen; fsize = le64toh(node->fentry->inf_len); @@ -1135,20 +1136,35 @@ lookloop: /* Did we have a match? */ if (id) { - if (flags & ISDOTDOT) - VOP_UNLOCK(dvp, 0); - error = udf_vget(udfmp->im_mountp, id, LK_EXCLUSIVE, &tdp); - if (flags & ISDOTDOT) - vn_lock(dvp, LK_EXCLUSIVE|LK_RETRY); - if (!error) { + /* + * Remember where this entry was if it's the final + * component. + */ + if ((flags & ISLASTCN) && nameiop == LOOKUP) + node->diroff = ds->offset + ds->off; + if (numdirpasses == 2) + nchstats.ncs_pass2++; + udf_closedir(ds); + + if (flags & ISDOTDOT) { + error = vn_vget_ino(dvp, id, lkflags, &tdp); + } else if (node->hash_id == id) { + VREF(dvp); /* we want ourself, ie "." */ /* - * Remember where this entry was if it's the final - * component. + * When we lookup "." we still can be asked to lock it + * differently. */ - if ((flags & ISLASTCN) && nameiop == LOOKUP) - node->diroff = ds->offset + ds->off; - if (numdirpasses == 2) - nchstats.ncs_pass2++; + ltype = lkflags & LK_TYPE_MASK; + if (ltype != VOP_ISLOCKED(dvp)) { + if (ltype == LK_EXCLUSIVE) + vn_lock(dvp, LK_UPGRADE | LK_RETRY); + else /* if (ltype == LK_SHARED) */ + vn_lock(dvp, LK_DOWNGRADE | LK_RETRY); + } + tdp = dvp; + } else + error = udf_vget(udfmp->im_mountp, id, lkflags, &tdp); + if (!error) { *vpp = tdp; /* Put this entry in the cache */ if (flags & MAKEENTRY) @@ -1162,6 +1178,7 @@ lookloop: udf_closedir(ds); goto lookloop; } + udf_closedir(ds); /* Enter name into cache as non-existant */ if (flags & MAKEENTRY) @@ -1175,7 +1192,6 @@ lookloop: } } - udf_closedir(ds); return (error); } -- cgit v1.1