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 27 Apr 2008 19:39:37 -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 int fdesc_allocvp(fdntype, int, struct mount *, struct vnode **, +extern vfs_uninit_t fdesc_uninit; +extern int fdesc_allocvp(fdntype, unsigned, 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.58 diff -u -r1.58 fdesc_vfsops.c --- sys/fs/fdescfs/fdesc_vfsops.c 10 Jan 2008 01:10:45 -0000 1.58 +++ sys/fs/fdescfs/fdesc_vfsops.c 27 Apr 2008 19:39:37 -0000 @@ -85,18 +85,24 @@ 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, -1, 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); /* 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"); @@ -143,13 +149,16 @@ struct thread *td; { struct vnode *vp; + int error; /* * Return locked reference to root. */ vp = VFSTOFDESC(mp)->f_root; - VREF(vp); - vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + error = vget(vp, LK_EXCLUSIVE | LK_RETRY, td); + if (error) + panic("fdesc-root: vget error %d", error); + *vpp = vp; return (0); } @@ -208,6 +217,7 @@ .vfs_mount = fdesc_mount, .vfs_root = fdesc_root, .vfs_statfs = fdesc_statfs, + .vfs_uninit = fdesc_uninit, .vfs_unmount = fdesc_unmount, }; Index: sys/fs/fdescfs/fdesc_vnops.c =================================================================== RCS file: /srv/ncvs/src/sys/fs/fdescfs/fdesc_vnops.c,v retrieving revision 1.108 diff -u -r1.108 fdesc_vnops.c --- sys/fs/fdescfs/fdesc_vnops.c 4 Apr 2008 09:37:57 -0000 1.108 +++ sys/fs/fdescfs/fdesc_vnops.c 27 Apr 2008 19:39:37 -0000 @@ -31,7 +31,7 @@ * * @(#)fdesc_vnops.c 8.9 (Berkeley) 1/21/94 * - * $FreeBSD: src/sys/fs/fdescfs/fdesc_vnops.c,v 1.108 2008/04/04 09:37:57 kib Exp $ + * $FreeBSD: src/sys/fs/fdescfs/fdesc_vnops.c,v 1.107 2008/02/26 10:10:55 kib Exp $ */ /* @@ -56,9 +56,7 @@ #include -#define FDL_WANT 0x01 -#define FDL_LOCKED 0x02 -static int fdcache_lock; +static struct mtx fdesc_hashmtx; #define NFDCACHE 4 #define FD_NHASH(ix) \ @@ -88,6 +86,9 @@ .vop_setattr = fdesc_setattr, }; +static void fdesc_insmntque_dtr(struct vnode *, void *); +static void fdesc_remove_entry(struct fdescnode *); + /* * Initialise cache headers */ @@ -96,81 +97,141 @@ struct vfsconf *vfsp; { + mtx_init(&fdesc_hashmtx, "fdescfs_hash", NULL, MTX_DEF); fdhashtbl = hashinit(NFDCACHE, M_CACHE, &fdhash); return (0); } +/* + * Uninit ready for unload. + */ +int +fdesc_uninit(vfsp) + struct vfsconf *vfsp; +{ + + hashdestroy(fdhashtbl, M_CACHE, fdhash); + mtx_destroy(&fdesc_hashmtx); + return (0); +} + +/* + * If allocating vnode fails, call this. + */ +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; + vp->v_type = VNON; + vgone(vp); + vput(vp); +} + +/* + * Remove an entry from the hash if it exists. + */ +static void +fdesc_remove_entry(struct fdescnode *fd) +{ + struct fdhashhead *fc; + struct fdescnode *fd2; + + fc = FD_NHASH(fd->fd_ix); + mtx_lock(&fdesc_hashmtx); + LIST_FOREACH(fd2, fc, fd_hash) { + if (fd == fd2) { + LIST_REMOVE(fd, fd_hash); + mtx_unlock(&fdesc_hashmtx); + return; + } + } + mtx_unlock(&fdesc_hashmtx); +} + int -fdesc_allocvp(ftype, ix, mp, vpp, td) +fdesc_allocvp(ftype, fd_fd, ix, mp, vpp, td) fdntype ftype; + unsigned fd_fd; int ix; struct mount *mp; struct vnode **vpp; struct thread *td; { struct fdhashhead *fc; - struct fdescnode *fd; + struct fdescnode *fd, *fd2; + struct vnode *vp, *vp2; 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, LK_EXCLUSIVE | LK_CANRECURSE, + /* Get reference to vnode in case it's being free'd */ + vp = fd->fd_vnode; + VI_LOCK(vp); + mtx_unlock(&fdesc_hashmtx); + if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) goto loop; - *vpp = fd->fd_vnode; - VOP_UNLOCK(*vpp, 0); + *vpp = vp; return (error); } } + mtx_unlock(&fdesc_hashmtx); - /* - * 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; - - /* - * Do the MALLOC before the getnewvnode since doing so afterward - * might cause a bogus v_data pointer to get dereferenced - * elsewhere if MALLOC should block. - */ MALLOC(fd, struct fdescnode *, sizeof(struct fdescnode), M_TEMP, M_WAITOK); - error = getnewvnode("fdescfs", mp, &fdesc_vnodeops, vpp); + error = getnewvnode("fdescfs", mp, &fdesc_vnodeops, &vp); if (error) { FREE(fd, M_TEMP); - goto out; + return (error); } - (*vpp)->v_data = fd; - fd->fd_vnode = *vpp; + vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); + vp->v_data = fd; + fd->fd_vnode = vp; fd->fd_type = ftype; - fd->fd_fd = -1; + fd->fd_fd = fd_fd; fd->fd_ix = ix; - /* XXX: vnode should be locked here */ - error = insmntque(*vpp, mp); /* XXX: Too early for mpsafe fs */ + error = insmntque1(vp, mp, fdesc_insmntque_dtr, fd); if (error != 0) { - free(fd, M_TEMP); *vpp = NULLVP; - goto out; + return (error); } - 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 the vnode. */ + mtx_lock(&fdesc_hashmtx); + LIST_FOREACH(fd2, fc, fd_hash) { + if (fd2->fd_ix == ix && fd2->fd_vnode->v_mount == mp) { + /* Get reference to vnode in case it's being free'd */ + vp2 = fd2->fd_vnode; + VI_LOCK(vp2); + mtx_unlock(&fdesc_hashmtx); + error = vget(vp2, LK_EXCLUSIVE | LK_INTERLOCK, + td); + /* Someone beat us, dec use count and wait for reclaim */ + vp->v_type = VNON; + vgone(vp); + vput(vp); + /* If we didn't get it, return no vnode. */ + if (error) { + *vpp = NULLVP; + return (error); + } + *vpp = vp2; + return (error); + } } - return (error); + /* If we came here, we can insert it safely. */ + LIST_INSERT_HEAD(fc, fd, fd_hash); + *vpp = vp; + mtx_unlock(&fdesc_hashmtx); + return (0); } /* @@ -230,13 +291,33 @@ if ((error = fget(td, fd, &fp)) != 0) goto bad; - error = fdesc_allocvp(Fdesc, FD_DESC+fd, dvp->v_mount, &fvp, td); - fdrop(fp, td); + /* Check if we're looking up ourselves. */ + if (VTOFDESC(dvp)->fd_ix == FD_DESC + fd) { + fdrop(fp, td); + fvp = dvp; + } else { + /* + * Unlock our root node (dvp) when doing this, since we might + * deadlock since the vnode might be locked by another thread + * and the root vnode lock will be obtained afterwards, which + * will be the opposite lock order. Vref the root vnode first + * (so we don't loose it), but is this really necessary when + * we're dealing with the root vnode? + */ + vref(dvp); + VOP_UNLOCK(dvp, 0); + error = fdesc_allocvp(Fdesc, fd, FD_DESC + fd, dvp->v_mount, + &fvp, td); + fdrop(fp, td); + /* + * The root vnode must be locked last to prevent deadlock condition. + */ + vn_lock(dvp, LK_RETRY | LK_EXCLUSIVE); + vrele(dvp); + } + if (error) goto bad; - VTOFDESC(fvp)->fd_fd = fd; - if (fvp != dvp) - vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY); *vpp = fvp; return (0); @@ -527,12 +608,13 @@ struct vnode *a_vp; } */ *ap; { - struct vnode *vp = ap->a_vp; - struct fdescnode *fd = VTOFDESC(vp); + struct vnode *vp; + struct fdescnode *fd; - LIST_REMOVE(fd, fd_hash); + vp = ap->a_vp; + fd = VTOFDESC(vp); + fdesc_remove_entry(fd); FREE(vp->v_data, M_TEMP); - vp->v_data = 0; - + vp->v_data = NULL; return (0); }