Index: sys/fs/fdescfs/fdesc.h =================================================================== RCS file: /srv/ncvs/src/sys/fs/fdescfs/fdesc.h,v retrieving revision 1.20 diff -u -r1.20 fdesc.h --- sys/fs/fdescfs/fdesc.h 10 Feb 2005 12:09:15 -0000 1.20 +++ sys/fs/fdescfs/fdesc.h 7 Nov 2007 08:49:17 -0000 @@ -31,7 +31,7 @@ * * @(#)fdesc.h 8.5 (Berkeley) 1/21/94 * - * $FreeBSD: src/sys/fs/fdescfs/fdesc.h,v 1.20 2005/02/10 12:09:15 phk Exp $ + * $FreeBSD$ */ #ifdef _KERNEL @@ -59,6 +59,7 @@ #define VTOFDESC(vp) ((struct fdescnode *)(vp)->v_data) extern vfs_init_t fdesc_init; +extern vfs_uninit_t fdesc_uninit; extern int fdesc_allocvp(fdntype, int, struct mount *, struct vnode **, struct thread *); #endif /* _KERNEL */ Index: sys/fs/fdescfs/fdesc_vfsops.c =================================================================== RCS file: /srv/ncvs/src/sys/fs/fdescfs/fdesc_vfsops.c,v retrieving revision 1.57 diff -u -r1.57 fdesc_vfsops.c --- sys/fs/fdescfs/fdesc_vfsops.c 16 Oct 2007 10:54:52 -0000 1.57 +++ sys/fs/fdescfs/fdesc_vfsops.c 7 Nov 2007 08:49:17 -0000 @@ -31,7 +31,7 @@ * * @(#)fdesc_vfsops.c 8.4 (Berkeley) 1/21/94 * - * $FreeBSD: src/sys/fs/fdescfs/fdesc_vfsops.c,v 1.57 2007/10/16 10:54:52 alfred Exp $ + * $FreeBSD$ */ /* @@ -85,18 +85,23 @@ if (mp->mnt_flag & (MNT_UPDATE | MNT_ROOTFS)) return (EOPNOTSUPP); - error = fdesc_allocvp(Froot, FD_ROOT, mp, &rvp, td); - if (error) - return (error); - MALLOC(fmp, struct fdescmount *, sizeof(struct fdescmount), M_FDESCMNT, M_WAITOK); /* XXX */ + error = fdesc_allocvp(Froot, FD_ROOT, mp, &rvp, td); + if (error) { + free(fmp, M_FDESCMNT); + return (error); + } rvp->v_type = VDIR; rvp->v_vflag |= VV_ROOT; fmp->f_root = rvp; + VOP_UNLOCK(rvp, 0, td); /* XXX -- don't mark as local to work around fts() problems */ /*mp->mnt_flag |= MNT_LOCAL;*/ - mp->mnt_data = fmp; + MNT_ILOCK(mp); + mp->mnt_kern_flag |= MNTK_MPSAFE; + MNT_IUNLOCK(mp); + mp->mnt_data = (qaddr_t) fmp; vfs_getnewfsid(mp); vfs_mountedfrom(mp, "fdescfs"); @@ -209,6 +214,7 @@ .vfs_root = fdesc_root, .vfs_statfs = fdesc_statfs, .vfs_unmount = fdesc_unmount, + .vfs_uninit = fdesc_uninit, }; VFS_SET(fdesc_vfsops, fdescfs, VFCF_SYNTHETIC); Index: sys/fs/fdescfs/fdesc_vnops.c =================================================================== RCS file: /srv/ncvs/src/sys/fs/fdescfs/fdesc_vnops.c,v retrieving revision 1.104 diff -u -r1.104 fdesc_vnops.c --- sys/fs/fdescfs/fdesc_vnops.c 4 Apr 2007 09:11:32 -0000 1.104 +++ sys/fs/fdescfs/fdesc_vnops.c 7 Nov 2007 08:49:17 -0000 @@ -31,7 +31,7 @@ * * @(#)fdesc_vnops.c 8.9 (Berkeley) 1/21/94 * - * $FreeBSD: src/sys/fs/fdescfs/fdesc_vnops.c,v 1.104 2007/04/04 09:11:32 rwatson Exp $ + * $FreeBSD$ */ /* @@ -58,7 +58,7 @@ #define FDL_WANT 0x01 #define FDL_LOCKED 0x02 -static int fdcache_lock; +static struct mtx fdesc_hashmtx; #define NFDCACHE 4 #define FD_NHASH(ix) \ @@ -97,9 +97,33 @@ { fdhashtbl = hashinit(NFDCACHE, M_CACHE, &fdhash); + mtx_init(&fdesc_hashmtx, "fdescfs", NULL, MTX_DEF); return (0); } +/* Cleanup. */ +int +fdesc_uninit(vfsp) + struct vfsconf *vfsp; +{ + + mtx_destroy(&fdesc_hashmtx); + hashdestroy(fdhashtbl, M_CACHE, fdhash); + return (0); +} + +static void +fdesc_insmntque_dtr(struct vnode *vp, void *arg) +{ + struct fdescnode *fd; + + fd = (struct fdescnode *)arg; + free(fd, M_TEMP); + vp->v_data = NULL; + vgone(vp); + vput(vp); +} + int fdesc_allocvp(ftype, ix, mp, vpp, td) fdntype ftype; @@ -109,30 +133,24 @@ struct thread *td; { struct fdhashhead *fc; - struct fdescnode *fd; + struct fdescnode *fd, *fd2; + struct vnode *vp; int error = 0; fc = FD_NHASH(ix); loop: + mtx_lock(&fdesc_hashmtx); LIST_FOREACH(fd, fc, fd_hash) { if (fd->fd_ix == ix && fd->fd_vnode->v_mount == mp) { - if (vget(fd->fd_vnode, 0, td)) + VI_LOCK(fd->fd_vnode); + mtx_unlock(&fdesc_hashmtx); + if (vget(fd->fd_vnode, LK_EXCLUSIVE | LK_INTERLOCK, td)) goto loop; *vpp = fd->fd_vnode; return (error); } } - - /* - * otherwise lock the array while we call getnewvnode - * since that can block. - */ - if (fdcache_lock & FDL_LOCKED) { - fdcache_lock |= FDL_WANT; - (void) tsleep( &fdcache_lock, PINOD, "fdalvp", 0); - goto loop; - } - fdcache_lock |= FDL_LOCKED; + mtx_unlock(&fdesc_hashmtx); /* * Do the MALLOC before the getnewvnode since doing so afterward @@ -141,37 +159,54 @@ */ MALLOC(fd, struct fdescnode *, sizeof(struct fdescnode), M_TEMP, M_WAITOK); - error = getnewvnode("fdesc", mp, &fdesc_vnodeops, vpp); + error = getnewvnode("fdesc", mp, &fdesc_vnodeops, &vp); if (error) { FREE(fd, M_TEMP); goto out; } - (*vpp)->v_data = fd; - fd->fd_vnode = *vpp; + vp->v_data = fd; + fd->fd_vnode = vp; fd->fd_type = ftype; fd->fd_fd = -1; fd->fd_ix = ix; - /* XXX: vnode should be locked here */ - error = insmntque(*vpp, mp); /* XXX: Too early for mpsafe fs */ + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); + error = insmntque1(vp, mp, fdesc_insmntque_dtr, fd); if (error != 0) { - free(fd, M_TEMP); *vpp = NULLVP; goto out; } - LIST_INSERT_HEAD(fc, fd, fd_hash); - -out: - fdcache_lock &= ~FDL_LOCKED; - if (fdcache_lock & FDL_WANT) { - fdcache_lock &= ~FDL_WANT; - wakeup( &fdcache_lock); + /* Make sure that someone didn't beat us when inserting. */ + mtx_lock(&fdesc_hashmtx); + LIST_FOREACH(fd2, fc, fd_hash) { + if (fd2->fd_ix == ix && fd2->fd_vnode->v_mount == mp) { + VI_LOCK(fd2->fd_vnode); + mtx_unlock(&fdesc_hashmtx); + error = vget(fd2->fd_vnode, LK_EXCLUSIVE | LK_INTERLOCK, + td); + if (error) + panic("fdesc_allocvp: vget error %d", error); + /* Someone beat us, free data, dec use_count */ + FREE(fd, M_TEMP); + vp->v_data = NULL; + vgone(vp); + vput(vp); + *vpp = fd2->fd_vnode; + return (error); + } } - + /* If we came here, we can insert it safely. */ + LIST_INSERT_HEAD(fc, fd, fd_hash); + *vpp = vp; + mtx_unlock(&fdesc_hashmtx); +out: return (error); } /* + * Insert node into hash table, unless it's already there. + */ +/* * vp is the current namei directory * ndp is the name to locate in that directory... */ @@ -228,13 +263,20 @@ if ((error = fget(td, fd, &fp)) != 0) goto bad; - error = fdesc_allocvp(Fdesc, FD_DESC+fd, dvp->v_mount, &fvp, td); + /* + * XXX: Not sure if this is necessary, but this is used to avoid a + * deadlock if fdesc_allocvp is looking up dvp and trying to lock it + * when we have the lock. + */ + if (VTOFDESC(dvp)->fd_ix == FD_DESC + fd) + fvp = dvp; + else + error = fdesc_allocvp(Fdesc, FD_DESC+fd, dvp->v_mount, &fvp, td); fdrop(fp, td); if (error) goto bad; VTOFDESC(fvp)->fd_fd = fd; - if (fvp != dvp) - vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, td); + *vpp = fvp; return (0); @@ -528,7 +570,10 @@ struct vnode *vp = ap->a_vp; struct fdescnode *fd = VTOFDESC(vp); + mtx_lock(&fdesc_hashmtx); LIST_REMOVE(fd, fd_hash); + mtx_unlock(&fdesc_hashmtx); + FREE(vp->v_data, M_TEMP); vp->v_data = 0;