The branch main has been updated by kevans: URL: https://cgit.FreeBSD.org/src/commit/?id=65059dd2b6f94e570acc645be82b8ea056316459
commit 65059dd2b6f94e570acc645be82b8ea056316459 Author: Kyle Evans <kev...@freebsd.org> AuthorDate: 2025-09-04 02:08:52 +0000 Commit: Kyle Evans <kev...@freebsd.org> CommitDate: 2025-09-04 02:08:52 +0000 pseudofs: defer initialization until first mount Currently, pseudofs all get fully constructed when the module is loaded and vfs registered, but this is pretty unnecessary. Just loading the fs doesn't mean that it will be used so we're adding overhead and risk[0] by fully initializing these at the start, along with committing resources that may not be used. Deferring pfs_init() allows us to reduce the risk of simply loading the module causing problems that are harder to avoid, and existing pseudo filesystems don't really care: configuration that is context-sensitive is generally deferred to access-time with PFS_PROCDEP. To preserve symmetry, we'll also teardown our pseudofs on last unmount, which leaves us with a vfs_uninit() implementation that simply destroys our lock and prints a message. [0] Example of such being recent bugs in linsysfs, which caused a panic as soon as the module was loaded because we're eager to set it up. Reviewed by: des (previous version), kib Differential Revision: https://reviews.freebsd.org/D52156 --- sys/fs/pseudofs/pseudofs.c | 71 ++++++++++++++++++++++++++++++++++++++----- sys/fs/pseudofs/pseudofs.h | 33 +++++++++++--------- sys/modules/pseudofs/Makefile | 4 +-- 3 files changed, 84 insertions(+), 24 deletions(-) diff --git a/sys/fs/pseudofs/pseudofs.c b/sys/fs/pseudofs/pseudofs.c index efeeb57e6448..20cb87c6c6d5 100644 --- a/sys/fs/pseudofs/pseudofs.c +++ b/sys/fs/pseudofs/pseudofs.c @@ -40,13 +40,18 @@ #include <sys/mount.h> #include <sys/mutex.h> #include <sys/proc.h> +#include <sys/refcount.h> #include <sys/sbuf.h> +#include <sys/sx.h> #include <sys/sysctl.h> #include <sys/vnode.h> #include <fs/pseudofs/pseudofs.h> #include <fs/pseudofs/pseudofs_internal.h> +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 @@ int 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 @@ pfs_cmount(struct mntarg *ma, void *data, uint64_t flags) 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 @@ pfs_statfs(struct mount *mp, struct statfs *sbp) } /* - * 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 @@ pfs_init(struct pfs_info *pi, struct vfsconf *vfc) /* * 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/fs/pseudofs/pseudofs.h b/sys/fs/pseudofs/pseudofs.h index c60dd7b339d1..461a1d2402e4 100644 --- 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 <sys/_sx.h> #include <sys/jail.h> /* @@ -188,9 +189,11 @@ typedef int (*pfs_destroy_t)(PFS_DESTROY_ARGS); /* * 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 @@ struct pfs_info { 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_unmount (struct mount *mp, int mntflags); 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 @@ int pfs_destroy (struct pfs_node *pn); #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 @@ _##name##_mount(struct mount *mp) { \ } \ \ 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/modules/pseudofs/Makefile b/sys/modules/pseudofs/Makefile index cb92b8912c86..3928c950ab70 100644 --- a/sys/modules/pseudofs/Makefile +++ b/sys/modules/pseudofs/Makefile @@ -13,8 +13,8 @@ EXPORT_SYMS= pfs_cmount \ pfs_unmount \ pfs_root \ pfs_statfs \ - pfs_init \ - pfs_uninit \ + pfs_vfsinit \ + pfs_vfsuninit \ pfs_create_dir \ pfs_create_file \ pfs_create_link \