diff --git a/sys/fs/pseudofs/pseudofs.h b/sys/fs/pseudofs/pseudofs.h --- a/sys/fs/pseudofs/pseudofs.h +++ b/sys/fs/pseudofs/pseudofs.h @@ -31,6 +31,7 @@ #ifndef _PSEUDOFS_H_INCLUDED #define _PSEUDOFS_H_INCLUDED +#include #include /* @@ -188,9 +189,11 @@ /* * pfs_info: describes a pseudofs instance * - * The pi_mutex is only used to avoid using the global subr_unit lock - * for unrhdr. The rest of struct pfs_info is only modified during - * vfs_init() and vfs_uninit() of the consumer filesystem. + * The pi_mutex is used to avoid using the global subr_unit lock for unrhdr, and + * the pi_mountlock is used to coordinate initialization of the consumer + * filesystem on first mount. The rest of struct pfs_info is only modified + * during pi_init() and pi_uninit() of the consumer filesystem, which are fully + * serialized. */ struct pfs_info { char pi_name[PFS_FSNAMELEN]; @@ -198,9 +201,11 @@ pfs_init_t pi_uninit; /* members below this line are initialized at run time */ + struct sx pi_mountlock; struct pfs_node *pi_root; struct mtx pi_mutex; struct unrhdr *pi_unrhdr; + u_int pi_mounts; }; /* @@ -249,8 +254,8 @@ int pfs_root (struct mount *mp, int flags, struct vnode **vpp); int pfs_statfs (struct mount *mp, struct statfs *sbp); -int pfs_init (struct pfs_info *pi, struct vfsconf *vfc); -int pfs_uninit (struct pfs_info *pi, struct vfsconf *vfc); +int pfs_vfsinit (struct pfs_info *pi, struct vfsconf *vfc); +int pfs_vfsuninit (struct pfs_info *pi, struct vfsconf *vfc); /* * Directory structure construction and manipulation @@ -276,9 +281,9 @@ #define PSEUDOFS(name, version, flags) \ \ static struct pfs_info name##_info = { \ - #name, \ - name##_init, \ - name##_uninit, \ + .pi_name = #name, \ + .pi_init = name##_init, \ + .pi_uninit = name##_uninit, \ }; \ \ static int \ @@ -287,22 +292,22 @@ } \ \ static int \ -_##name##_init(struct vfsconf *vfc) { \ - return (pfs_init(&name##_info, vfc)); \ +_##name##_vfsinit(struct vfsconf *vfc) { \ + return (pfs_vfsinit(&name##_info, vfc)); \ } \ \ static int \ -_##name##_uninit(struct vfsconf *vfc) { \ - return (pfs_uninit(&name##_info, vfc)); \ +_##name##_vfsuninit(struct vfsconf *vfc) { \ + return (pfs_vfsuninit(&name##_info, vfc)); \ } \ \ static struct vfsops name##_vfsops = { \ .vfs_cmount = pfs_cmount, \ - .vfs_init = _##name##_init, \ + .vfs_init = _##name##_vfsinit, \ .vfs_mount = _##name##_mount, \ .vfs_root = pfs_root, \ .vfs_statfs = pfs_statfs, \ - .vfs_uninit = _##name##_uninit, \ + .vfs_uninit = _##name##_vfsuninit, \ .vfs_unmount = pfs_unmount, \ }; \ VFS_SET(name##_vfsops, name, VFCF_SYNTHETIC | flags); \ diff --git a/sys/fs/pseudofs/pseudofs.c b/sys/fs/pseudofs/pseudofs.c --- a/sys/fs/pseudofs/pseudofs.c +++ b/sys/fs/pseudofs/pseudofs.c @@ -40,13 +40,18 @@ #include #include #include +#include #include +#include #include #include #include #include +static int pfs_setup(struct pfs_info *pi, struct vfsconf *vfc); +static int pfs_teardown(struct pfs_info *pi, struct vfsconf *vfc); + static MALLOC_DEFINE(M_PFSNODES, "pfs_nodes", "pseudofs nodes"); SYSCTL_NODE(_vfs, OID_AUTO, pfs, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, @@ -382,10 +387,20 @@ pfs_mount(struct pfs_info *pi, struct mount *mp) { struct statfs *sbp; + int error = 0; if (mp->mnt_flag & MNT_UPDATE) return (EOPNOTSUPP); + sx_xlock(&pi->pi_mountlock); + if (pi->pi_root == NULL) + error = pfs_setup(pi, mp->mnt_vfc); + if (error == 0) + refcount_acquire(&pi->pi_mounts); + sx_xunlock(&pi->pi_mountlock); + if (error != 0) + return (error); + MNT_ILOCK(mp); mp->mnt_flag |= MNT_LOCAL; mp->mnt_kern_flag |= MNTK_NOMSYNC; @@ -424,10 +439,23 @@ int pfs_unmount(struct mount *mp, int mntflags) { + struct pfs_info *pi; int error; error = vflush(mp, 0, (mntflags & MNT_FORCE) ? FORCECLOSE : 0, curthread); + if (error != 0) + return (error); + + pi = (struct pfs_info *)mp->mnt_data; + sx_xlock(&pi->pi_mountlock); + if (!refcount_release_if_not_last(&pi->pi_mounts)) { + error = pfs_teardown(pi, mp->mnt_vfc); + if (error == 0) + refcount_release(&pi->pi_mounts); + } + sx_xunlock(&pi->pi_mountlock); + return (error); } @@ -454,10 +482,35 @@ } /* - * Initialize a pseudofs instance + * Initialize pseudofs synchronization bits. These will generally be needed + * in order to avoid problems with parallel mounting of pseudofs consumers. */ int -pfs_init(struct pfs_info *pi, struct vfsconf *vfc) +pfs_vfsinit(struct pfs_info *pi, struct vfsconf *vfc) +{ + + sx_init(&pi->pi_mountlock, "pfs mountlock"); + refcount_init(&pi->pi_mounts, 0); + return (0); +} + +int +pfs_vfsuninit(struct pfs_info *pi, struct vfsconf *vfc) +{ + + MPASS(pi->pi_root == NULL); + sx_destroy(&pi->pi_mountlock); + + if (bootverbose) + printf("%s unregistered\n", pi->pi_name); + return (0); +} + +/* + * Initialize a pseudofs instance + */ +static int +pfs_setup(struct pfs_info *pi, struct vfsconf *vfc) { struct pfs_node *root; int error; @@ -487,18 +540,20 @@ /* * Destroy a pseudofs instance */ -int -pfs_uninit(struct pfs_info *pi, struct vfsconf *vfc) +static int +pfs_teardown(struct pfs_info *pi, struct vfsconf *vfc) { int error; + MPASS(pi->pi_root != NULL); + error = (pi->pi_uninit)(pi, vfc); + if (error != 0) + return (error); + pfs_destroy(pi->pi_root); pi->pi_root = NULL; pfs_fileno_uninit(pi); - if (bootverbose) - printf("%s unregistered\n", pi->pi_name); - error = (pi->pi_uninit)(pi, vfc); - return (error); + return (0); } /* diff --git a/sys/modules/pseudofs/Makefile b/sys/modules/pseudofs/Makefile --- a/sys/modules/pseudofs/Makefile +++ b/sys/modules/pseudofs/Makefile @@ -13,8 +13,8 @@ pfs_unmount \ pfs_root \ pfs_statfs \ - pfs_init \ - pfs_uninit \ + pfs_vfsinit \ + pfs_vfsuninit \ pfs_create_dir \ pfs_create_file \ pfs_create_link \