--- //depot/projects/smpng/sys/fs/cd9660/cd9660_lookup.c 2008/01/21 18:58:30 +++ //depot/user/jhb/lock/fs/cd9660/cd9660_lookup.c 2008/11/21 19:19:32 @@ -94,17 +94,20 @@ 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]; @@ -116,6 +119,7 @@ int flags = cnp->cn_flags; int nameiop = cnp->cn_nameiop; + ep2 = ep = NULL; bp = NULL; *vpp = NULL; vdp = ap->a_dvp; @@ -125,9 +129,11 @@ /* * We now have a segment name to search for, and a directory to search. */ - + ino = 0; + i_diroff = dp->i_diroff; len = cnp->cn_namelen; name = cnp->cn_nameptr; + /* * A leading `=' means, we are looking for an associated file */ @@ -149,15 +155,14 @@ * 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++; @@ -165,17 +170,17 @@ 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; } @@ -188,8 +193,8 @@ 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; } @@ -224,7 +229,7 @@ * 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 @@ -241,7 +246,7 @@ 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 */ @@ -257,22 +262,22 @@ 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); @@ -283,7 +288,7 @@ entryoffsetinblock = saveoffset & bmask; ep = (struct iso_directory_record *) ((char *)bp->b_data + entryoffsetinblock); - dp->i_offset = saveoffset; + i_offset = saveoffset; } goto found; } @@ -294,8 +299,8 @@ */ if (numdirpasses == 2) { numdirpasses--; - dp->i_offset = 0; - endsearch = dp->i_diroff; + i_offset = 0; + endsearch = i_diroff; goto searchloop; } if (bp != NULL) @@ -320,7 +325,7 @@ * 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 @@ -342,30 +347,54 @@ * 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(sizeof(*ep2), M_TEMP, M_WAITOK); + *ep2 = *ep; + ep = ep2; + } + brelse(bp); + + /* + * If ino is different from i_ino, * it's a relocated directory. */ if (flags & ISDOTDOT) { - saved_ino = dp->i_ino; + ltype = VOP_ISLOCKED(pdp); VOP_UNLOCK(pdp, 0); /* 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); + error = cd9660_vget_internal(vdp->v_mount, i_ino, + cnp->cn_lkflags, &tdp, + i_ino != ino, ep); + free(ep2, M_TEMP); + vn_lock(pdp, ltype | LK_RETRY); 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)) { + if (ltype == LK_EXCLUSIVE) + vn_lock(vdp, LK_UPGRADE | LK_RETRY); + else /* if (ltype == LK_SHARED) */ + vn_lock(vdp, LK_DOWNGRADE | LK_RETRY); + } *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; --- //depot/projects/smpng/sys/fs/cd9660/cd9660_node.c 2008/11/18 23:25:45 +++ //depot/user/jhb/lock/fs/cd9660/cd9660_node.c 2008/11/18 23:59:14 @@ -92,7 +92,6 @@ } */ *ap; { struct vnode *vp = ap->a_vp; - struct iso_node *ip = VTOI(vp); if (prtactive && vrefcnt(vp) != 0) vprint("cd9660_reclaim: pushing active", vp); @@ -108,8 +107,6 @@ /* * 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); --- //depot/projects/smpng/sys/fs/cd9660/cd9660_node.h 2008/11/18 23:25:45 +++ //depot/user/jhb/lock/fs/cd9660/cd9660_node.h 2008/11/18 23:59:14 @@ -64,8 +64,6 @@ 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; --- //depot/projects/smpng/sys/fs/cd9660/cd9660_vfsops.c 2008/11/18 23:25:45 +++ //depot/user/jhb/lock/fs/cd9660/cd9660_vfsops.c 2008/11/18 23:59:14 @@ -375,6 +376,7 @@ 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; @@ -545,7 +547,7 @@ * 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)); } @@ -659,6 +661,22 @@ 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; @@ -669,6 +687,7 @@ } ip = malloc(sizeof(struct iso_node), M_ISOFSNODE, M_WAITOK | M_ZERO); + VN_LOCK_AREC(vp); vp->v_data = ip; ip->i_vnode = vp; ip->i_number = ino; @@ -739,7 +758,6 @@ bp = 0; ip->i_mnt = imp; - VREF(imp->im_devvp); if (relocated) { /* @@ -797,6 +815,7 @@ vp->v_op = &cd9660_fifoops; break; default: + VN_LOCK_ASHARE(vp); break; } --- //depot/projects/smpng/sys/fs/cd9660/cd9660_vnops.c 2008/11/18 23:25:45 +++ //depot/user/jhb/lock/fs/cd9660/cd9660_vnops.c 2008/11/18 23:59:14 @@ -168,10 +168,14 @@ int a_fdidx; } */ *ap; { - struct iso_node *ip = VTOI(ap->a_vp); + struct vnode *vp = ap->a_vp; + struct iso_node *ip = VTOI(vp); + + if (vp->v_type == VCHR || vp->v_type == VBLK) + return (EOPNOTSUPP); - vnode_create_vobject(ap->a_vp, ip->i_size, ap->a_td); - return 0; + vnode_create_vobject(vp, ip->i_size, ap->a_td); + return (0); } --- //depot/projects/smpng/sys/libkern/iconv.c 2005/10/31 21:29:45 +++ //depot/user/jhb/lock/libkern/iconv.c 2008/11/17 06:04:07 @@ -39,6 +39,7 @@ #include #include #include +#include #include #include "iconv_converter_if.h" @@ -52,6 +53,8 @@ MODULE_VERSION(libiconv, 2); +static struct sx iconv_lock; + #ifdef notnow /* * iconv converter instance @@ -86,11 +89,16 @@ { struct iconv_cspair *csp; + sx_xlock(&iconv_lock); while ((csp = TAILQ_FIRST(&iconv_cslist)) != NULL) { if (csp->cp_refcount) return EBUSY; + } + + while ((csp = TAILQ_FIRST(&iconv_cslist)) != NULL) iconv_unregister_cspair(csp); - } + sx_xunlock(&iconv_lock); + sx_destroy(&iconv_lock); return 0; } @@ -102,6 +110,7 @@ switch (type) { case MOD_LOAD: error = 0; + sx_init(&iconv_lock, "iconv"); break; case MOD_UNLOAD: error = iconv_mod_unload(); @@ -311,7 +320,7 @@ int error; error = 0; - + sx_slock(&iconv_lock); TAILQ_FOREACH(dcp, &iconv_converters, cc_link) { name = ICONV_CONVERTER_NAME(dcp); if (name == NULL) @@ -320,6 +329,7 @@ if (error) break; } + sx_sunlock(&iconv_lock); if (error) return error; spc = 0; @@ -343,7 +353,7 @@ error = 0; bzero(&csi, sizeof(csi)); csi.cs_version = ICONV_CSPAIR_INFO_VER; - + sx_slock(&iconv_lock); TAILQ_FOREACH(csp, &iconv_cslist, cp_link) { csi.cs_id = csp->cp_id; csi.cs_refcount = csp->cp_refcount; @@ -354,6 +364,7 @@ if (error) break; } + sx_sunlock(&iconv_lock); return error; } @@ -387,9 +398,12 @@ return EINVAL; if (iconv_lookupconv(din.ia_converter, &dcp) != 0) return EINVAL; + sx_xlock(&iconv_lock); error = iconv_register_cspair(din.ia_to, din.ia_from, dcp, NULL, &csp); - if (error) + if (error) { + sx_xunlock(&iconv_lock); return error; + } if (din.ia_datalen) { csp->cp_data = malloc(din.ia_datalen, M_ICONVDATA, M_WAITOK); error = copyin(din.ia_data, csp->cp_data, din.ia_datalen); @@ -400,10 +414,12 @@ error = SYSCTL_OUT(req, &dout, sizeof(dout)); if (error) goto bad; + sx_xunlock(&iconv_lock); ICDEBUG("%s => %s, %d bytes\n",din.ia_from, din.ia_to, din.ia_datalen); return 0; bad: iconv_unregister_cspair(csp); + sx_xunlock(&iconv_lock); return error; } @@ -433,16 +449,22 @@ switch (type) { case MOD_LOAD: + sx_xlock(&iconv_lock); error = iconv_register_converter(dcp); - if (error) + if (error) { + sx_xunlock(&iconv_lock); break; + } error = ICONV_CONVERTER_INIT(dcp); if (error) iconv_unregister_converter(dcp); + sx_xunlock(&iconv_lock); break; case MOD_UNLOAD: + sx_xlock(&iconv_lock); ICONV_CONVERTER_DONE(dcp); error = iconv_unregister_converter(dcp); + sx_xunlock(&iconv_lock); break; default: error = EINVAL;