Index: sys/fs/nullfs/null.h =================================================================== --- sys/fs/nullfs/null.h +++ sys/fs/nullfs/null.h @@ -42,7 +42,6 @@ #define NULLM_CACHE 0x0001 struct null_mount { - struct mount *nullm_vfs; struct vnode *nullm_lowerrootvp; /* Ref to lower root vnode */ uint64_t nullm_flags; }; @@ -79,6 +78,9 @@ #define NULLVPTOLOWERVP(vp) (VTONULL(vp)->null_lowervp) #endif +struct mount *nullfs_mount_trybusy(struct vnode *vp); +void nullfs_mount_unbusy(struct mount *mp); + extern struct vop_vector null_vnodeops; #ifdef MALLOC_DECLARE Index: sys/fs/nullfs/null_subr.c =================================================================== --- sys/fs/nullfs/null_subr.c +++ sys/fs/nullfs/null_subr.c @@ -46,6 +46,8 @@ #include #include +#include + #include /* @@ -343,3 +345,31 @@ return (a->null_lowervp); } #endif + +/* + * Attempt to mark the underlying mount for a vnode busy so that nullfs + * can safely access it or issue VFS_* operations against it. + * Returns NULL on failure. + */ +struct mount * +nullfs_mount_trybusy(struct vnode *vp) +{ + struct mount *mp; + int error; + + mp = atomic_load_ptr(&vp->v_mount); + if (mp == NULL) + return (NULL); + error = vfs_busy(mp, MBF_NOWAIT); + if (error != 0) + return (NULL); + + return (mp); +} + +void +nullfs_mount_unbusy(struct mount *mp) +{ + if (mp != NULL) + vfs_unbusy(mp); +} Index: sys/fs/nullfs/null_vfsops.c =================================================================== --- sys/fs/nullfs/null_vfsops.c +++ sys/fs/nullfs/null_vfsops.c @@ -74,6 +74,7 @@ static int nullfs_mount(struct mount *mp) { + struct mount *lowermp; struct vnode *lowerrootvp; struct vnode *nullm_rootvp; struct null_mount *xmp; @@ -160,19 +161,22 @@ M_NULLFSMNT, M_WAITOK | M_ZERO); /* - * Save pointer to underlying FS and the reference to the - * lower root vnode. + * Save the reference to the lower root vnode. */ - xmp->nullm_vfs = lowerrootvp->v_mount; vref(lowerrootvp); + lowermp = nullfs_mount_trybusy(lowerrootvp); xmp->nullm_lowerrootvp = lowerrootvp; mp->mnt_data = xmp; /* * Make sure the node alias worked. */ - error = null_nodeget(mp, lowerrootvp, &nullm_rootvp); + if (lowermp == NULL) + error = ENOENT; + else + error = null_nodeget(mp, lowerrootvp, &nullm_rootvp); if (error != 0) { + nullfs_mount_unbusy(lowermp); vrele(lowerrootvp); free(xmp, M_NULLFSMNT); return (error); @@ -186,7 +190,7 @@ xmp->nullm_flags |= NULLM_CACHE; if (vfs_getopt(mp->mnt_optnew, "nocache", NULL, NULL) == 0 || - (xmp->nullm_vfs->mnt_kern_flag & MNTK_NULL_NOCACHE) != 0) + (lowermp->mnt_kern_flag & MNTK_NULL_NOCACHE) != 0) xmp->nullm_flags &= ~NULLM_CACHE; MNT_ILOCK(mp); @@ -201,12 +205,13 @@ MNT_IUNLOCK(mp); vfs_getnewfsid(mp); if ((xmp->nullm_flags & NULLM_CACHE) != 0) { - MNT_ILOCK(xmp->nullm_vfs); - TAILQ_INSERT_TAIL(&xmp->nullm_vfs->mnt_uppers, mp, + MNT_ILOCK(lowermp); + TAILQ_INSERT_TAIL(&lowermp->mnt_uppers, mp, mnt_upper_link); - MNT_IUNLOCK(xmp->nullm_vfs); + MNT_IUNLOCK(lowermp); } + nullfs_mount_unbusy(lowermp); vfs_mountedfrom(mp, target); vput(nullm_rootvp); @@ -253,8 +258,13 @@ * Finally, throw away the null_mount structure */ mntdata = mp->mnt_data; - ump = mntdata->nullm_vfs; if ((mntdata->nullm_flags & NULLM_CACHE) != 0) { + ump = mntdata->nullm_lowerrootvp->v_mount; + /* + * Registration as upper should prevent even forced + * unmount of lower FS. + */ + KASSERT(ump != NULL, ("nullfs: lower mount gone")); MNT_ILOCK(ump); while ((ump->mnt_kern_flag & MNTK_VGONE_UPPER) != 0) { ump->mnt_kern_flag |= MNTK_VGONE_WAITER; @@ -294,13 +304,34 @@ } static int -nullfs_quotactl(mp, cmd, uid, arg) +nullfs_quotactl(mp, cmd, uid, arg, mp_busy) struct mount *mp; int cmd; uid_t uid; void *arg; + bool *mp_busy; { - return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, arg); + struct mount *lowermp; + struct null_mount *mntdata; + int error; + bool unbusy; + + unbusy = true; + + mntdata = MOUNTTONULLMOUNT(mp); + NULLFSDEBUG("nullfs_quotactl(mp = %p, vp = %p)\n", mp, + mntdata->nullm_lowerrootvp); + lowermp = nullfs_mount_trybusy(mntdata->nullm_lowerrootvp); + + if (lowermp == NULL) + return (ENOENT); + + error = VFS_QUOTACTL(lowermp, cmd, uid, arg, &unbusy); + + if (unbusy) + nullfs_mount_unbusy(lowermp); + + return (error); } static int @@ -308,16 +339,24 @@ struct mount *mp; struct statfs *sbp; { + struct mount *lowermp; + struct null_mount *mntdata; int error; struct statfs *mstat; - NULLFSDEBUG("nullfs_statfs(mp = %p, vp = %p->%p)\n", (void *)mp, - (void *)MOUNTTONULLMOUNT(mp)->nullm_rootvp, - (void *)NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp)); + mntdata = MOUNTTONULLMOUNT(mp); + NULLFSDEBUG("nullfs_statfs(mp = %p, vp = %p->%p)\n", mp, + mntdata->nullm_rootvp, NULLVPTOLOWERVP(mntdata->nullm_rootvp)); mstat = malloc(sizeof(struct statfs), M_STATFS, M_WAITOK | M_ZERO); - error = VFS_STATFS(MOUNTTONULLMOUNT(mp)->nullm_vfs, mstat); + lowermp = nullfs_mount_trybusy(mntdata->nullm_lowerrootvp); + if (lowermp == NULL) + error = ENOENT; + else + error = VFS_STATFS(lowermp, mstat); + + nullfs_mount_unbusy(lowermp); if (error) { free(mstat, M_STATFS); return (error); @@ -358,12 +397,21 @@ int flags; struct vnode **vpp; { + struct mount *lowermp; + struct null_mount *mntdata; int error; + mntdata = MOUNTTONULLMOUNT(mp); + KASSERT((flags & LK_TYPE_MASK) != 0, ("nullfs_vget: no lock requested")); - error = VFS_VGET(MOUNTTONULLMOUNT(mp)->nullm_vfs, ino, flags, vpp); + lowermp = nullfs_mount_trybusy(mntdata->nullm_lowerrootvp); + if (lowermp == NULL) + return (ENOENT); + + error = VFS_VGET(lowermp, ino, flags, vpp); + nullfs_mount_unbusy(lowermp); if (error != 0) return (error); return (null_nodeget(mp, *vpp, vpp)); @@ -376,10 +424,19 @@ int flags; struct vnode **vpp; { + struct mount *lowermp; + struct null_mount *mntdata; int error; - error = VFS_FHTOVP(MOUNTTONULLMOUNT(mp)->nullm_vfs, fidp, flags, + mntdata = MOUNTTONULLMOUNT(mp); + + lowermp = nullfs_mount_trybusy(mntdata->nullm_lowerrootvp); + if (lowermp == NULL) + return (ENOENT); + + error = VFS_FHTOVP(lowermp, fidp, flags, vpp); + nullfs_mount_unbusy(lowermp); if (error != 0) return (error); return (null_nodeget(mp, *vpp, vpp)); @@ -393,9 +450,21 @@ int namespace; const char *attrname; { + struct mount *lowermp; + struct null_mount *mntdata; + int error; + + mntdata = MOUNTTONULLMOUNT(mp); + + lowermp = nullfs_mount_trybusy(mntdata->nullm_lowerrootvp); + if (lowermp == NULL) + return (ENOENT); - return (VFS_EXTATTRCTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, - filename_vp, namespace, attrname)); + error = VFS_EXTATTRCTL(lowermp, cmd, + filename_vp, namespace, attrname); + + nullfs_mount_unbusy(lowermp); + return (error); } static void