Author: jhb
Date: Wed Mar 11 22:13:12 2009
New Revision: 189709
URL: http://svn.freebsd.org/changeset/base/189709

Log:
  MFC: Make cd9660 MPSAFE and able to use shared vnode locks for pathname
  lookups.  Also, disable operations on character device nodes.

Modified:
  stable/7/sys/   (props changed)
  stable/7/sys/contrib/pf/   (props changed)
  stable/7/sys/dev/ath/ath_hal/   (props changed)
  stable/7/sys/dev/cxgb/   (props changed)
  stable/7/sys/fs/cd9660/cd9660_lookup.c
  stable/7/sys/fs/cd9660/cd9660_node.c
  stable/7/sys/fs/cd9660/cd9660_node.h
  stable/7/sys/fs/cd9660/cd9660_vfsops.c
  stable/7/sys/fs/cd9660/cd9660_vnops.c

Modified: stable/7/sys/fs/cd9660/cd9660_lookup.c
==============================================================================
--- stable/7/sys/fs/cd9660/cd9660_lookup.c      Wed Mar 11 22:00:03 2009        
(r189708)
+++ stable/7/sys/fs/cd9660/cd9660_lookup.c      Wed Mar 11 22:13:12 2009        
(r189709)
@@ -94,29 +94,34 @@ cd9660_lookup(ap)
        struct iso_node *dp;            /* inode for directory being searched */
        struct iso_mnt *imp;            /* filesystem that directory is in */
        struct buf *bp;                 /* a buffer of directory entries */
-       struct iso_directory_record *ep = 0;/* the current directory entry */
+       struct iso_directory_record *ep;/* the current directory entry */
+       struct iso_directory_record *ep2;/* copy of current directory entry */
        int entryoffsetinblock;         /* offset of ep in bp's buffer */
        int saveoffset = 0;             /* offset of last directory entry in 
dir */
+       doff_t i_diroff;                /* cached i_diroff value. */
+       doff_t i_offset;                /* cached i_offset value. */
        int numdirpasses;               /* strategy for directory search */
        doff_t endsearch;               /* offset to end directory search */
        struct vnode *pdp;              /* saved dp during symlink work */
        struct vnode *tdp;              /* returned by cd9660_vget_internal */
        u_long bmask;                   /* block offset mask */
        int error;
-       ino_t ino = 0, saved_ino;
-       int reclen;
+       ino_t ino, i_ino;
+       int ltype, reclen;
        u_short namelen;
        int isoflags;
        char altname[NAME_MAX];
        int res;
        int assoc, len;
        char *name;
+       struct mount *mp;
        struct vnode **vpp = ap->a_vpp;
        struct componentname *cnp = ap->a_cnp;
        int flags = cnp->cn_flags;
        int nameiop = cnp->cn_nameiop;
        struct thread *td = cnp->cn_thread;
 
+       ep2 = ep = NULL;
        bp = NULL;
        *vpp = NULL;
        vdp = ap->a_dvp;
@@ -126,9 +131,11 @@ cd9660_lookup(ap)
        /*
         * We now have a segment name to search for, and a directory to search.
         */
-
+       ino = reclen = 0;
+       i_diroff = dp->i_diroff;
        len = cnp->cn_namelen;
        name = cnp->cn_nameptr;
+
        /*
         * A leading `=' means, we are looking for an associated file
         */
@@ -150,15 +157,14 @@ cd9660_lookup(ap)
         * of simplicity.
         */
        bmask = imp->im_bmask;
-       if (nameiop != LOOKUP || dp->i_diroff == 0 ||
-           dp->i_diroff > dp->i_size) {
+       if (nameiop != LOOKUP || i_diroff == 0 || i_diroff > dp->i_size) {
                entryoffsetinblock = 0;
-               dp->i_offset = 0;
+               i_offset = 0;
                numdirpasses = 1;
        } else {
-               dp->i_offset = dp->i_diroff;
-               if ((entryoffsetinblock = dp->i_offset & bmask) &&
-                   (error = cd9660_blkatoff(vdp, (off_t)dp->i_offset, NULL, 
&bp)))
+               i_offset = i_diroff;
+               if ((entryoffsetinblock = i_offset & bmask) &&
+                   (error = cd9660_blkatoff(vdp, (off_t)i_offset, NULL, &bp)))
                                return (error);
                numdirpasses = 2;
                nchstats.ncs_2passes++;
@@ -166,17 +172,17 @@ cd9660_lookup(ap)
        endsearch = dp->i_size;
 
 searchloop:
-       while (dp->i_offset < endsearch) {
+       while (i_offset < endsearch) {
                /*
                 * If offset is on a block boundary,
                 * read the next directory block.
                 * Release previous if it exists.
                 */
-               if ((dp->i_offset & bmask) == 0) {
+               if ((i_offset & bmask) == 0) {
                        if (bp != NULL)
                                brelse(bp);
                        if ((error =
-                           cd9660_blkatoff(vdp, (off_t)dp->i_offset, NULL, 
&bp)) != 0)
+                           cd9660_blkatoff(vdp, (off_t)i_offset, NULL, &bp)) 
!= 0)
                                return (error);
                        entryoffsetinblock = 0;
                }
@@ -189,8 +195,8 @@ searchloop:
                reclen = isonum_711(ep->length);
                if (reclen == 0) {
                        /* skip to next block, if any */
-                       dp->i_offset =
-                           (dp->i_offset & ~bmask) + imp->logical_block_size;
+                       i_offset =
+                           (i_offset & ~bmask) + imp->logical_block_size;
                        continue;
                }
 
@@ -225,7 +231,7 @@ searchloop:
                                                 * Save directory entry's inode 
number and
                                                 * release directory buffer.
                                                 */
-                                               dp->i_ino = isodirino(ep, imp);
+                                               i_ino = isodirino(ep, imp);
                                                goto found;
                                        }
                                        if (namelen != 1
@@ -242,7 +248,7 @@ searchloop:
                                        else
                                                ino = dbtob(bp->b_blkno)
                                                        + entryoffsetinblock;
-                                       saveoffset = dp->i_offset;
+                                       saveoffset = i_offset;
                                } else if (ino)
                                        goto foundino;
 #ifdef NOSORTBUG       /* On some CDs directory entries are not sorted 
correctly */
@@ -258,22 +264,22 @@ searchloop:
                                ino = isodirino(ep, imp);
                        else
                                ino = dbtob(bp->b_blkno) + entryoffsetinblock;
-                       dp->i_ino = ino;
-                       cd9660_rrip_getname(ep,altname,&namelen,&dp->i_ino,imp);
+                       i_ino = ino;
+                       cd9660_rrip_getname(ep, altname, &namelen, &i_ino, imp);
                        if (namelen == cnp->cn_namelen
                            && !bcmp(name,altname,namelen))
                                goto found;
                        ino = 0;
                        break;
                }
-               dp->i_offset += reclen;
+               i_offset += reclen;
                entryoffsetinblock += reclen;
        }
        if (ino) {
 foundino:
-               dp->i_ino = ino;
-               if (saveoffset != dp->i_offset) {
-                       if (lblkno(imp, dp->i_offset) !=
+               i_ino = ino;
+               if (saveoffset != i_offset) {
+                       if (lblkno(imp, i_offset) !=
                            lblkno(imp, saveoffset)) {
                                if (bp != NULL)
                                        brelse(bp);
@@ -284,7 +290,8 @@ foundino:
                        entryoffsetinblock = saveoffset & bmask;
                        ep = (struct iso_directory_record *)
                                ((char *)bp->b_data + entryoffsetinblock);
-                       dp->i_offset = saveoffset;
+                       reclen = isonum_711(ep->length);
+                       i_offset = saveoffset;
                }
                goto found;
        }
@@ -295,8 +302,8 @@ notfound:
         */
        if (numdirpasses == 2) {
                numdirpasses--;
-               dp->i_offset = 0;
-               endsearch = dp->i_diroff;
+               i_offset = 0;
+               endsearch = i_diroff;
                goto searchloop;
        }
        if (bp != NULL)
@@ -321,10 +328,10 @@ found:
         * in the cache as to where the entry was found.
         */
        if ((flags & ISLASTCN) && nameiop == LOOKUP)
-               dp->i_diroff = dp->i_offset;
+               dp->i_diroff = i_offset;
 
        /*
-        * Step through the translation in the name.  We do not `iput' the
+        * Step through the translation in the name.  We do not `vput' the
         * directory because we may need it again if a symbolic link
         * is relative to the current directory.  Instead we save it
         * unlocked as "pdp".  We must get the target inode before unlocking
@@ -334,7 +341,7 @@ found:
         * when following backward pointers ".." we must unlock the
         * parent directory before getting the requested directory.
         * There is a potential race condition here if both the current
-        * and parent directories are removed before the `iget' for the
+        * and parent directories are removed before the `vget' for the
         * inode associated with ".." returns.  We hope that this occurs
         * infrequently since we cannot avoid this race condition without
         * implementing a sophisticated deadlock detection algorithm.
@@ -343,30 +350,75 @@ found:
         * that point backwards in the directory structure.
         */
        pdp = vdp;
+
        /*
-        * If ino is different from dp->i_ino,
+        * Make a copy of the directory entry for non "." lookups so
+        * we can drop the buffer before calling vget() to avoid a
+        * lock order reversal between the vnode lock and the buffer
+        * lock.
+        */
+       if (dp->i_number != i_ino) {
+               ep2 = malloc(reclen, M_TEMP, M_WAITOK);
+               bcopy(ep, ep2, reclen);
+               ep = ep2;
+       }
+       brelse(bp);
+
+       /*
+        * If ino is different from i_ino,
         * it's a relocated directory.
         */
        if (flags & ISDOTDOT) {
-               saved_ino = dp->i_ino;
-               VOP_UNLOCK(pdp, 0, td); /* race to get the inode */
-               error = cd9660_vget_internal(vdp->v_mount, saved_ino,
-                                            LK_EXCLUSIVE, &tdp,
-                                            saved_ino != ino, ep);
-               brelse(bp);
-               vn_lock(pdp, LK_EXCLUSIVE | LK_RETRY, td);
+               /*
+                * Expanded copy of vn_vget_ino() so that we can use
+                * cd9660_vget_internal().
+                */
+               mp = pdp->v_mount;
+               ltype = VOP_ISLOCKED(pdp, td);
+               for (;;) {
+                       error = vfs_busy(mp, LK_NOWAIT, NULL, curthread);
+                       if (error == 0)
+                               break;
+                       VOP_UNLOCK(pdp, 0, td);
+                       pause("vn_vget", 1);
+                       vn_lock(pdp, ltype | LK_RETRY, td);
+                       if (pdp->v_iflag & VI_DOOMED)
+                               return (ENOENT);
+               }
+               VOP_UNLOCK(pdp, 0, td);
+               error = cd9660_vget_internal(vdp->v_mount, i_ino,
+                                            cnp->cn_lkflags, &tdp,
+                                            i_ino != ino, ep);
+               free(ep2, M_TEMP);
+               vfs_unbusy(mp, td);
+               vn_lock(pdp, ltype | LK_RETRY, td);
+               if (pdp->v_iflag & VI_DOOMED) {
+                       if (error == 0)
+                               vput(tdp);
+                       error = ENOENT;
+               }
                if (error)
                        return (error);
                *vpp = tdp;
-       } else if (dp->i_number == dp->i_ino) {
-               brelse(bp);
+       } else if (dp->i_number == i_ino) {
                VREF(vdp);      /* we want ourself, ie "." */
+               /*
+                * When we lookup "." we still can be asked to lock it
+                * differently.
+                */
+               ltype = cnp->cn_lkflags & LK_TYPE_MASK;
+               if (ltype != VOP_ISLOCKED(vdp, td)) {
+                       if (ltype == LK_EXCLUSIVE)
+                               vn_lock(vdp, LK_UPGRADE | LK_RETRY, td);
+                       else /* if (ltype == LK_SHARED) */
+                               vn_lock(vdp, LK_DOWNGRADE | LK_RETRY, td);
+               }
                *vpp = vdp;
        } else {
-               error = cd9660_vget_internal(vdp->v_mount, dp->i_ino,
-                                            LK_EXCLUSIVE, &tdp,
-                                            dp->i_ino != ino, ep);
-               brelse(bp);
+               error = cd9660_vget_internal(vdp->v_mount, i_ino,
+                                            cnp->cn_lkflags, &tdp,
+                                            i_ino != ino, ep);
+               free(ep2, M_TEMP);
                if (error)
                        return (error);
                *vpp = tdp;

Modified: stable/7/sys/fs/cd9660/cd9660_node.c
==============================================================================
--- stable/7/sys/fs/cd9660/cd9660_node.c        Wed Mar 11 22:00:03 2009        
(r189708)
+++ stable/7/sys/fs/cd9660/cd9660_node.c        Wed Mar 11 22:13:12 2009        
(r189709)
@@ -93,7 +93,6 @@ cd9660_reclaim(ap)
        } */ *ap;
 {
        struct vnode *vp = ap->a_vp;
-       struct iso_node *ip = VTOI(vp);
 
        if (prtactive && vrefcnt(vp) != 0)
                vprint("cd9660_reclaim: pushing active", vp);
@@ -109,8 +108,6 @@ cd9660_reclaim(ap)
        /*
         * Purge old data structures associated with the inode.
         */
-       if (ip->i_mnt->im_devvp)
-               vrele(ip->i_mnt->im_devvp);
        FREE(vp->v_data, M_ISOFSNODE);
        vp->v_data = NULL;
        return (0);

Modified: stable/7/sys/fs/cd9660/cd9660_node.h
==============================================================================
--- stable/7/sys/fs/cd9660/cd9660_node.h        Wed Mar 11 22:00:03 2009        
(r189708)
+++ stable/7/sys/fs/cd9660/cd9660_node.h        Wed Mar 11 22:13:12 2009        
(r189709)
@@ -65,8 +65,6 @@ struct iso_node {
        struct  lockf *i_lockf; /* head of byte-level lock list */
        doff_t  i_endoff;       /* end of useful stuff in directory */
        doff_t  i_diroff;       /* offset in dir, where we found last entry */
-       doff_t  i_offset;       /* offset of free space in directory */
-       ino_t   i_ino;          /* inode number of found directory */
 
        long iso_extent;        /* extent of file */
        unsigned long i_size;

Modified: stable/7/sys/fs/cd9660/cd9660_vfsops.c
==============================================================================
--- stable/7/sys/fs/cd9660/cd9660_vfsops.c      Wed Mar 11 22:00:03 2009        
(r189708)
+++ stable/7/sys/fs/cd9660/cd9660_vfsops.c      Wed Mar 11 22:13:12 2009        
(r189709)
@@ -381,6 +381,7 @@ iso_mountfs(devvp, mp, td)
        mp->mnt_maxsymlinklen = 0;
        MNT_ILOCK(mp);
        mp->mnt_flag |= MNT_LOCAL;
+       mp->mnt_kern_flag |= MNTK_MPSAFE | MNTK_LOOKUP_SHARED;
        MNT_IUNLOCK(mp);
        isomp->im_mountp = mp;
        isomp->im_dev = dev;
@@ -556,7 +557,7 @@ cd9660_root(mp, flags, vpp, td)
         * With RRIP we must use the `.' entry of the root directory.
         * Simply tell vget, that it's a relocated directory.
         */
-       return (cd9660_vget_internal(mp, ino, LK_EXCLUSIVE, vpp,
+       return (cd9660_vget_internal(mp, ino, flags, vpp,
            imp->iso_ftype == ISO_FTYPE_RRIP, dp));
 }
 
@@ -670,6 +671,22 @@ cd9660_vget_internal(mp, ino, flags, 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!
+        */
+
        imp = VFSTOISOFS(mp);
        dev = imp->im_dev;
 
@@ -750,7 +767,6 @@ cd9660_vget_internal(mp, ino, flags, vpp
                bp = 0;
 
        ip->i_mnt = imp;
-       VREF(imp->im_devvp);
 
        if (relocated) {
                /*
@@ -808,6 +824,7 @@ cd9660_vget_internal(mp, ino, flags, vpp
                vp->v_op = &cd9660_fifoops;
                break;
        default:
+               vp->v_vnlock->lk_flags &= ~LK_NOSHARE;
                break;
        }
 

Modified: stable/7/sys/fs/cd9660/cd9660_vnops.c
==============================================================================
--- stable/7/sys/fs/cd9660/cd9660_vnops.c       Wed Mar 11 22:00:03 2009        
(r189708)
+++ stable/7/sys/fs/cd9660/cd9660_vnops.c       Wed Mar 11 22:13:12 2009        
(r189709)
@@ -169,10 +169,14 @@ cd9660_open(ap)
                int a_fdidx;
        } */ *ap;
 {
-       struct iso_node *ip = VTOI(ap->a_vp);
+       struct vnode *vp = ap->a_vp;
+       struct iso_node *ip = VTOI(vp);
 
-       vnode_create_vobject(ap->a_vp, ip->i_size, ap->a_td);
-       return 0;
+       if (vp->v_type == VCHR || vp->v_type == VBLK)
+               return (EOPNOTSUPP);
+
+       vnode_create_vobject(vp, ip->i_size, ap->a_td);
+       return (0);
 }
 
 
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to