Index: stable/6/sys/fs/pseudofs/pseudofs.c =================================================================== --- stable/6/sys/fs/pseudofs/pseudofs.c (revision 169948) +++ stable/6/sys/fs/pseudofs/pseudofs.c (revision 169949) @@ -1,412 +1,420 @@ /*- * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_pseudofs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_PFSNODES, "pfs_nodes", "pseudofs nodes"); SYSCTL_NODE(_vfs, OID_AUTO, pfs, CTLFLAG_RW, 0, "pseudofs"); #if PFS_FSNAMELEN != MFSNAMELEN #error "PFS_FSNAMELEN is not equal to MFSNAMELEN" #endif /* * Add a node to a directory */ static int _pfs_add_node(struct pfs_node *parent, struct pfs_node *node) { KASSERT(parent != NULL, ("%s(): parent is NULL", __func__)); KASSERT(parent->pn_info != NULL, ("%s(): parent has no pn_info", __func__)); KASSERT(parent->pn_type == pfstype_dir || parent->pn_type == pfstype_procdir || parent->pn_type == pfstype_root, ("%s(): parent is not a directory", __func__)); /* XXX should check for duplicate names etc. */ mtx_lock(&parent->pn_info->pi_mutex); node->pn_info = parent->pn_info; node->pn_parent = parent; node->pn_next = parent->pn_nodes; parent->pn_nodes = node; /* Propagate flag to all child nodes (and thus their vnodes) */ if ((parent->pn_flags & PFS_PROCDEP) != 0) node->pn_flags |= PFS_PROCDEP; mtx_unlock(&parent->pn_info->pi_mutex); return (0); } /* * Add . and .. to a directory */ static int _pfs_fixup_dir(struct pfs_node *parent) { struct pfs_node *dir; MALLOC(dir, struct pfs_node *, sizeof *dir, M_PFSNODES, M_WAITOK|M_ZERO); dir->pn_name[0] = '.'; dir->pn_type = pfstype_this; if (_pfs_add_node(parent, dir) != 0) { FREE(dir, M_PFSNODES); return (-1); } MALLOC(dir, struct pfs_node *, sizeof *dir, M_PFSNODES, M_WAITOK|M_ZERO); dir->pn_name[0] = dir->pn_name[1] = '.'; dir->pn_type = pfstype_parent; if (_pfs_add_node(parent, dir) != 0) { FREE(dir, M_PFSNODES); return (-1); } return (0); } /* * Create a directory */ struct pfs_node * pfs_create_dir(struct pfs_node *parent, const char *name, pfs_attr_t attr, pfs_vis_t vis, int flags) { struct pfs_node *dir; KASSERT(strlen(name) < PFS_NAMELEN, ("%s(): node name is too long", __func__)); MALLOC(dir, struct pfs_node *, sizeof *dir, M_PFSNODES, M_WAITOK|M_ZERO); strcpy(dir->pn_name, name); dir->pn_type = (flags & PFS_PROCDEP) ? pfstype_procdir : pfstype_dir; dir->pn_attr = attr; dir->pn_vis = vis; dir->pn_flags = flags; if (_pfs_add_node(parent, dir) != 0) { FREE(dir, M_PFSNODES); return (NULL); } if (_pfs_fixup_dir(dir) != 0) { pfs_destroy(dir); return (NULL); } return (dir); } /* * Create a file */ struct pfs_node * pfs_create_file(struct pfs_node *parent, const char *name, pfs_fill_t fill, pfs_attr_t attr, pfs_vis_t vis, int flags) { struct pfs_node *node; KASSERT(strlen(name) < PFS_NAMELEN, ("%s(): node name is too long", __func__)); MALLOC(node, struct pfs_node *, sizeof *node, M_PFSNODES, M_WAITOK|M_ZERO); strcpy(node->pn_name, name); node->pn_type = pfstype_file; node->pn_func = fill; node->pn_attr = attr; node->pn_vis = vis; node->pn_flags = flags; if (_pfs_add_node(parent, node) != 0) { FREE(node, M_PFSNODES); return (NULL); } return (node); } /* * Create a symlink */ struct pfs_node * pfs_create_link(struct pfs_node *parent, const char *name, pfs_fill_t fill, pfs_attr_t attr, pfs_vis_t vis, int flags) { struct pfs_node *node; node = pfs_create_file(parent, name, fill, attr, vis, flags); if (node == NULL) return (NULL); node->pn_type = pfstype_symlink; return (node); } /* * Locate a node by name */ struct pfs_node * pfs_find_node(struct pfs_node *parent, const char *name) { struct pfs_node *node; for (node = parent->pn_nodes; node != NULL; node = node->pn_next) if (strcmp(node->pn_name, name) == 0) return (node); return (NULL); } /* * Destroy a node or a tree of nodes */ int pfs_destroy(struct pfs_node *node) { struct pfs_node *parent, *rover; KASSERT(node != NULL, ("%s(): node is NULL", __func__)); KASSERT(node->pn_info != NULL, ("%s(): node has no pn_info", __func__)); /* destroy children */ if (node->pn_type == pfstype_dir || node->pn_type == pfstype_procdir || node->pn_type == pfstype_root) while (node->pn_nodes != NULL) pfs_destroy(node->pn_nodes); /* unlink from parent */ if ((parent = node->pn_parent) != NULL) { KASSERT(parent->pn_info == node->pn_info, ("%s(): parent has different pn_info", __func__)); mtx_lock(&node->pn_info->pi_mutex); if (parent->pn_nodes == node) { parent->pn_nodes = node->pn_next; } else { rover = parent->pn_nodes; while (rover->pn_next != NULL) { if (rover->pn_next == node) { rover->pn_next = node->pn_next; break; } rover = rover->pn_next; } } mtx_unlock(&node->pn_info->pi_mutex); } - /* revoke vnodes and release memory */ - pfs_disable(node); + /* revoke fileno and vnodes and release memory */ + if (node->pn_fileno) + pfs_fileno_free(node->pn_info, node); + pfs_purge(node); FREE(node, M_PFSNODES); return (0); } /* * Mount a pseudofs instance */ int pfs_mount(struct pfs_info *pi, struct mount *mp, struct thread *td) { struct statfs *sbp; if (mp->mnt_flag & MNT_UPDATE) return (EOPNOTSUPP); + MNT_ILOCK(mp); mp->mnt_flag |= MNT_LOCAL; +#if 0 + /* not quite ready for this yet */ + mp->mnt_kern_flag |= MNTK_MPSAFE; +#endif + MNT_IUNLOCK(mp); mp->mnt_data = (qaddr_t)pi; vfs_getnewfsid(mp); sbp = &mp->mnt_stat; vfs_mountedfrom(mp, pi->pi_name); sbp->f_bsize = PAGE_SIZE; sbp->f_iosize = PAGE_SIZE; sbp->f_blocks = 1; sbp->f_bfree = 0; sbp->f_bavail = 0; sbp->f_files = 1; sbp->f_ffree = 0; return (0); } /* * Unmount a pseudofs instance */ int pfs_unmount(struct mount *mp, int mntflags, struct thread *td) { struct pfs_info *pi; int error; pi = (struct pfs_info *)mp->mnt_data; /* XXX do stuff with pi... */ error = vflush(mp, 0, (mntflags & MNT_FORCE) ? FORCECLOSE : 0, td); return (error); } /* * Return a root vnode */ int pfs_root(struct mount *mp, int flags, struct vnode **vpp, struct thread *td) { struct pfs_info *pi; pi = (struct pfs_info *)mp->mnt_data; return pfs_vncache_alloc(mp, vpp, pi->pi_root, NO_PID); } /* * Return filesystem stats */ int pfs_statfs(struct mount *mp, struct statfs *sbp, struct thread *td) { /* no-op: always called with mp->mnt_stat */ return (0); } /* * Initialize a pseudofs instance */ int pfs_init(struct pfs_info *pi, struct vfsconf *vfc) { struct pfs_node *root; int error; mtx_init(&pi->pi_mutex, "pseudofs", NULL, MTX_DEF); /* set up the root diretory */ MALLOC(root, struct pfs_node *, sizeof *root, M_PFSNODES, M_WAITOK|M_ZERO); root->pn_type = pfstype_root; root->pn_name[0] = '/'; root->pn_info = pi; if (_pfs_fixup_dir(root) != 0) { FREE(root, M_PFSNODES); return (ENODEV); /* XXX not really the right errno */ } pi->pi_root = root; /* construct file hierarchy */ error = (pi->pi_init)(pi, vfc); if (error) { pfs_destroy(root); pi->pi_root = NULL; mtx_destroy(&pi->pi_mutex); return (error); } pfs_fileno_init(pi); if (bootverbose) printf("%s registered\n", pi->pi_name); return (0); } /* * Destroy a pseudofs instance */ int pfs_uninit(struct pfs_info *pi, struct vfsconf *vfc) { int error; - pfs_fileno_uninit(pi); pfs_destroy(pi->pi_root); pi->pi_root = NULL; + pfs_fileno_uninit(pi); mtx_destroy(&pi->pi_mutex); if (bootverbose) printf("%s unregistered\n", pi->pi_name); error = (pi->pi_uninit)(pi, vfc); return (error); } /* * Handle load / unload events */ static int pfs_modevent(module_t mod, int evt, void *arg) { switch (evt) { case MOD_LOAD: pfs_vncache_load(); break; case MOD_UNLOAD: case MOD_SHUTDOWN: pfs_vncache_unload(); break; default: return EOPNOTSUPP; break; } return 0; } /* * Module declaration */ static moduledata_t pseudofs_data = { "pseudofs", pfs_modevent, NULL }; DECLARE_MODULE(pseudofs, pseudofs_data, SI_SUB_EXEC, SI_ORDER_FIRST); MODULE_VERSION(pseudofs, 1); Index: stable/6/sys/fs/pseudofs/pseudofs.h =================================================================== --- stable/6/sys/fs/pseudofs/pseudofs.h (revision 169948) +++ stable/6/sys/fs/pseudofs/pseudofs.h (revision 169949) @@ -1,265 +1,266 @@ /*- * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _PSEUDOFS_H_INCLUDED #define _PSEUDOFS_H_INCLUDED /* * Opaque structures */ struct mount; struct nameidata; struct proc; struct sbuf; struct statfs; struct thread; struct uio; struct vfsconf; struct vnode; /* * Limits and constants */ #define PFS_NAMELEN 24 #define PFS_FSNAMELEN 16 /* equal to MFSNAMELEN */ #define PFS_DELEN (8 + PFS_NAMELEN) typedef enum { pfstype_none = 0, pfstype_root, pfstype_dir, pfstype_this, pfstype_parent, pfstype_file, pfstype_symlink, pfstype_procdir } pfs_type_t; /* * Flags */ #define PFS_RD 0x0001 /* readable */ #define PFS_WR 0x0002 /* writeable */ #define PFS_RDWR (PFS_RD|PFS_WR) #define PFS_RAWRD 0x0004 /* raw reader */ #define PFS_RAWWR 0x0008 /* raw writer */ #define PFS_RAW (PFS_RAWRD|PFS_RAWWR) #define PFS_PROCDEP 0x0010 /* process-dependent */ #define PFS_DISABLED 0x8000 /* node is disabled */ /* * Data structures */ struct pfs_info; struct pfs_node; struct pfs_bitmap; /* * Init / uninit callback */ #define PFS_INIT_ARGS \ struct pfs_info *pi, struct vfsconf *vfc #define PFS_INIT_PROTO(name) \ int name(PFS_INIT_ARGS); typedef int (*pfs_init_t)(PFS_INIT_ARGS); /* * Filler callback */ #define PFS_FILL_ARGS \ struct thread *td, struct proc *p, struct pfs_node *pn, \ struct sbuf *sb, struct uio *uio #define PFS_FILL_PROTO(name) \ int name(PFS_FILL_ARGS); typedef int (*pfs_fill_t)(PFS_FILL_ARGS); /* * Attribute callback */ struct vattr; #define PFS_ATTR_ARGS \ struct thread *td, struct proc *p, struct pfs_node *pn, \ struct vattr *vap #define PFS_ATTR_PROTO(name) \ int name(PFS_ATTR_ARGS); typedef int (*pfs_attr_t)(PFS_ATTR_ARGS); struct pfs_bitmap; /* opaque */ /* * Visibility callback */ #define PFS_VIS_ARGS \ struct thread *td, struct proc *p, struct pfs_node *pn #define PFS_VIS_PROTO(name) \ int name(PFS_VIS_ARGS); typedef int (*pfs_vis_t)(PFS_VIS_ARGS); /* * Ioctl callback */ #define PFS_IOCTL_ARGS \ struct thread *td, struct proc *p, struct pfs_node *pn, \ unsigned long cmd, void *data #define PFS_IOCTL_PROTO(name) \ int name(PFS_IOCTL_ARGS); typedef int (*pfs_ioctl_t)(PFS_IOCTL_ARGS); /* * Getextattr callback */ #define PFS_GETEXTATTR_ARGS \ struct thread *td, struct proc *p, struct pfs_node *pn, \ int attrnamespace, const char *name, struct uio *uio, \ size_t *size, struct ucred *cred #define PFS_GETEXTATTR_PROTO(name) \ int name(PFS_GETEXTATTR_ARGS); struct ucred; typedef int (*pfs_getextattr_t)(PFS_GETEXTATTR_ARGS); /* * Last-close callback */ #define PFS_CLOSE_ARGS \ struct thread *td, struct proc *p, struct pfs_node *pn #define PFS_CLOSE_PROTO(name) \ int name(PFS_CLOSE_ARGS); typedef int (*pfs_close_t)(PFS_CLOSE_ARGS); /* * pfs_info: describes a pseudofs instance */ struct pfs_info { char pi_name[PFS_FSNAMELEN]; pfs_init_t pi_init; pfs_init_t pi_uninit; /* members below this line aren't initialized */ struct pfs_node *pi_root; /* currently, the mutex is only used to protect the bitmap */ struct mtx pi_mutex; struct unrhdr *pi_unrhdr; }; /* * pfs_node: describes a node (file or directory) within a pseudofs */ struct pfs_node { char pn_name[PFS_NAMELEN]; pfs_type_t pn_type; union { void *_pn_dummy; pfs_fill_t _pn_func; struct pfs_node *_pn_nodes; } u1; #define pn_func u1._pn_func #define pn_nodes u1._pn_nodes pfs_ioctl_t pn_ioctl; pfs_close_t pn_close; pfs_attr_t pn_attr; pfs_vis_t pn_vis; pfs_getextattr_t pn_getextattr; void *pn_data; int pn_flags; struct pfs_info *pn_info; struct pfs_node *pn_parent; struct pfs_node *pn_next; u_int32_t pn_fileno; }; /* * VFS interface */ int pfs_mount (struct pfs_info *pi, struct mount *mp, struct thread *td); int pfs_unmount (struct mount *mp, int mntflags, struct thread *td); int pfs_root (struct mount *mp, int flags, struct vnode **vpp, struct thread *td); int pfs_statfs (struct mount *mp, struct statfs *sbp, struct thread *td); int pfs_init (struct pfs_info *pi, struct vfsconf *vfc); int pfs_uninit (struct pfs_info *pi, struct vfsconf *vfc); /* * Directory structure construction and manipulation */ struct pfs_node *pfs_create_dir (struct pfs_node *parent, const char *name, pfs_attr_t attr, pfs_vis_t vis, int flags); struct pfs_node *pfs_create_file(struct pfs_node *parent, const char *name, pfs_fill_t fill, pfs_attr_t attr, pfs_vis_t vis, int flags); struct pfs_node *pfs_create_link(struct pfs_node *parent, const char *name, pfs_fill_t fill, pfs_attr_t attr, pfs_vis_t vis, int flags); struct pfs_node *pfs_find_node (struct pfs_node *parent, const char *name); +void pfs_purge (struct pfs_node *pn); int pfs_disable (struct pfs_node *pn); int pfs_enable (struct pfs_node *pn); int pfs_destroy (struct pfs_node *pn); /* * Now for some initialization magic... */ #define PSEUDOFS(name, version) \ \ static struct pfs_info name##_info = { \ #name, \ name##_init, \ name##_uninit, \ }; \ \ static int \ _##name##_mount(struct mount *mp, struct thread *td) { \ return pfs_mount(&name##_info, mp, td); \ } \ \ static int \ _##name##_init(struct vfsconf *vfc) { \ return pfs_init(&name##_info, vfc); \ } \ \ static int \ _##name##_uninit(struct vfsconf *vfc) { \ return pfs_uninit(&name##_info, vfc); \ } \ \ static struct vfsops name##_vfsops = { \ .vfs_init = _##name##_init, \ .vfs_mount = _##name##_mount, \ .vfs_root = pfs_root, \ .vfs_statfs = pfs_statfs, \ .vfs_uninit = _##name##_uninit, \ .vfs_unmount = pfs_unmount, \ }; \ VFS_SET(name##_vfsops, name, VFCF_SYNTHETIC); \ MODULE_VERSION(name, version); \ MODULE_DEPEND(name, pseudofs, 1, 1, 1); #endif Index: stable/6/sys/fs/pseudofs/pseudofs_fileno.c =================================================================== --- stable/6/sys/fs/pseudofs/pseudofs_fileno.c (revision 169948) +++ stable/6/sys/fs/pseudofs/pseudofs_fileno.c (revision 169949) @@ -1,154 +1,165 @@ /*- * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_pseudofs.h" #include #include #include #include #include #include #include #include +#include #include #include /* * Initialize fileno bitmap */ void pfs_fileno_init(struct pfs_info *pi) { struct unrhdr *up; up = new_unrhdr(3, INT_MAX, &pi->pi_mutex); mtx_lock(&pi->pi_mutex); pi->pi_unrhdr = up; - pi->pi_root->pn_fileno = 2; mtx_unlock(&pi->pi_mutex); } /* * Tear down fileno bitmap */ void pfs_fileno_uninit(struct pfs_info *pi) { struct unrhdr *up; mtx_lock(&pi->pi_mutex); up = pi->pi_unrhdr; pi->pi_unrhdr = NULL; mtx_unlock(&pi->pi_mutex); delete_unrhdr(up); } /* * Allocate a file number */ void pfs_fileno_alloc(struct pfs_info *pi, struct pfs_node *pn) { + /* pi is not really necessary as it can be derived */ + KASSERT(pi == pn->pn_info, ("pn / pi mismatch")); + /* make sure our parent has a file number */ if (pn->pn_parent && !pn->pn_parent->pn_fileno) pfs_fileno_alloc(pi, pn->pn_parent); switch (pn->pn_type) { case pfstype_root: + /* root must always be 2 */ + pn->pn_fileno = 2; + break; case pfstype_dir: case pfstype_file: case pfstype_symlink: case pfstype_procdir: pn->pn_fileno = alloc_unr(pi->pi_unrhdr); break; case pfstype_this: KASSERT(pn->pn_parent != NULL, ("pfstype_this node has no parent")); pn->pn_fileno = pn->pn_parent->pn_fileno; break; case pfstype_parent: KASSERT(pn->pn_parent != NULL, ("pfstype_parent node has no parent")); if (pn->pn_parent == pi->pi_root) { pn->pn_fileno = pn->pn_parent->pn_fileno; break; } KASSERT(pn->pn_parent->pn_parent != NULL, ("pfstype_parent node has no grandparent")); pn->pn_fileno = pn->pn_parent->pn_parent->pn_fileno; break; case pfstype_none: KASSERT(0, ("pfs_fileno_alloc() called for pfstype_none node")); break; } #if 0 printf("pfs_fileno_alloc(): %s: ", pi->pi_name); if (pn->pn_parent) { if (pn->pn_parent->pn_parent) { printf("%s/", pn->pn_parent->pn_parent->pn_name); } printf("%s/", pn->pn_parent->pn_name); } printf("%s -> %d\n", pn->pn_name, pn->pn_fileno); #endif } /* * Release a file number */ void pfs_fileno_free(struct pfs_info *pi, struct pfs_node *pn) { + /* pi is not really necessary as it can be derived */ + KASSERT(pi == pn->pn_info, ("pn / pi mismatch")); + switch (pn->pn_type) { case pfstype_root: + /* not allocated from unrhdr */ + return; case pfstype_dir: case pfstype_file: case pfstype_symlink: case pfstype_procdir: free_unr(pi->pi_unrhdr, pn->pn_fileno); break; case pfstype_this: case pfstype_parent: /* ignore these, as they don't "own" their file number */ break; case pfstype_none: KASSERT(0, ("pfs_fileno_free() called for pfstype_none node")); break; } } Index: stable/6/sys/fs/pseudofs/pseudofs_internal.h =================================================================== --- stable/6/sys/fs/pseudofs/pseudofs_internal.h (revision 169948) +++ stable/6/sys/fs/pseudofs/pseudofs_internal.h (revision 169949) @@ -1,66 +1,67 @@ /*- * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _PSEUDOFS_INTERNAL_H_INCLUDED #define _PSEUDOFS_INTERNAL_H_INCLUDED /* * Sysctl subtree */ SYSCTL_DECL(_vfs_pfs); /* * Vnode data */ struct pfs_vdata { struct pfs_node *pvd_pn; pid_t pvd_pid; struct vnode *pvd_vnode; struct pfs_vdata*pvd_prev, *pvd_next; + int pvd_dead:1; }; /* * Vnode cache */ void pfs_vncache_load (void); void pfs_vncache_unload (void); int pfs_vncache_alloc (struct mount *, struct vnode **, struct pfs_node *, pid_t pid); int pfs_vncache_free (struct vnode *); /* * File number bitmap */ void pfs_fileno_init (struct pfs_info *); void pfs_fileno_uninit (struct pfs_info *); void pfs_fileno_alloc (struct pfs_info *, struct pfs_node *); void pfs_fileno_free (struct pfs_info *, struct pfs_node *); #endif Index: stable/6/sys/fs/pseudofs/pseudofs_vncache.c =================================================================== --- stable/6/sys/fs/pseudofs/pseudofs_vncache.c (revision 169948) +++ stable/6/sys/fs/pseudofs/pseudofs_vncache.c (revision 169949) @@ -1,312 +1,314 @@ /*- * Copyright (c) 2001 Dag-Erling Coïdan Smørgrav * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_pseudofs.h" #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_PFSVNCACHE, "pfs_vncache", "pseudofs vnode cache"); static struct mtx pfs_vncache_mutex; static struct pfs_vdata *pfs_vncache; static eventhandler_tag pfs_exit_tag; static void pfs_exit(void *arg, struct proc *p); SYSCTL_NODE(_vfs_pfs, OID_AUTO, vncache, CTLFLAG_RW, 0, "pseudofs vnode cache"); static int pfs_vncache_entries; SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, entries, CTLFLAG_RD, &pfs_vncache_entries, 0, "number of entries in the vnode cache"); static int pfs_vncache_maxentries; SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, maxentries, CTLFLAG_RD, &pfs_vncache_maxentries, 0, "highest number of entries in the vnode cache"); static int pfs_vncache_hits; SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, hits, CTLFLAG_RD, &pfs_vncache_hits, 0, "number of cache hits since initialization"); static int pfs_vncache_misses; SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, misses, CTLFLAG_RD, &pfs_vncache_misses, 0, "number of cache misses since initialization"); extern struct vop_vector pfs_vnodeops; /* XXX -> .h file */ /* * Initialize vnode cache */ void pfs_vncache_load(void) { mtx_init(&pfs_vncache_mutex, "pseudofs_vncache", NULL, MTX_DEF); pfs_exit_tag = EVENTHANDLER_REGISTER(process_exit, pfs_exit, NULL, EVENTHANDLER_PRI_ANY); } /* * Tear down vnode cache */ void pfs_vncache_unload(void) { EVENTHANDLER_DEREGISTER(process_exit, pfs_exit_tag); if (pfs_vncache_entries != 0) printf("pfs_vncache_unload(): %d entries remaining\n", pfs_vncache_entries); mtx_destroy(&pfs_vncache_mutex); } /* * Allocate a vnode */ int pfs_vncache_alloc(struct mount *mp, struct vnode **vpp, struct pfs_node *pn, pid_t pid) { struct pfs_vdata *pvd; struct vnode *vp; int error; /* * See if the vnode is in the cache. * XXX linear search is not very efficient. */ retry: mtx_lock(&pfs_vncache_mutex); for (pvd = pfs_vncache; pvd; pvd = pvd->pvd_next) { if (pvd->pvd_pn == pn && pvd->pvd_pid == pid && pvd->pvd_vnode->v_mount == mp) { vp = pvd->pvd_vnode; VI_LOCK(vp); mtx_unlock(&pfs_vncache_mutex); if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, curthread) == 0) { ++pfs_vncache_hits; *vpp = vp; - /* XXX see comment at top of pfs_lookup() */ - cache_purge(vp); + /* + * Some callers cache_enter(vp) later, so + * we have to make sure it's not in the + * VFS cache so it doesn't get entered + * twice. A better solution would be to + * make pfs_vncache_alloc() responsible + * for entering the vnode in the VFS + * cache. + */ + cache_purge(vp); return (0); } goto retry; } } mtx_unlock(&pfs_vncache_mutex); ++pfs_vncache_misses; /* nope, get a new one */ MALLOC(pvd, struct pfs_vdata *, sizeof *pvd, M_PFSVNCACHE, M_WAITOK); if (++pfs_vncache_entries > pfs_vncache_maxentries) pfs_vncache_maxentries = pfs_vncache_entries; error = getnewvnode("pseudofs", mp, &pfs_vnodeops, vpp); if (error) { FREE(pvd, M_PFSVNCACHE); return (error); } pvd->pvd_pn = pn; pvd->pvd_pid = pid; (*vpp)->v_data = pvd; switch (pn->pn_type) { case pfstype_root: (*vpp)->v_vflag = VV_ROOT; #if 0 printf("root vnode allocated\n"); #endif /* fall through */ case pfstype_dir: case pfstype_this: case pfstype_parent: case pfstype_procdir: (*vpp)->v_type = VDIR; break; case pfstype_file: (*vpp)->v_type = VREG; break; case pfstype_symlink: (*vpp)->v_type = VLNK; break; case pfstype_none: KASSERT(0, ("pfs_vncache_alloc called for null node\n")); default: panic("%s has unexpected type: %d", pn->pn_name, pn->pn_type); } /* * Propagate flag through to vnode so users know it can change * if the process changes (i.e. execve) */ if ((pn->pn_flags & PFS_PROCDEP) != 0) (*vpp)->v_vflag |= VV_PROCDEP; pvd->pvd_vnode = *vpp; mtx_lock(&pfs_vncache_mutex); pvd->pvd_prev = NULL; pvd->pvd_next = pfs_vncache; if (pvd->pvd_next) pvd->pvd_next->pvd_prev = pvd; pfs_vncache = pvd; mtx_unlock(&pfs_vncache_mutex); (*vpp)->v_vnlock->lk_flags |= LK_CANRECURSE; vn_lock(*vpp, LK_RETRY | LK_EXCLUSIVE, curthread); return (0); } /* * Free a vnode */ int pfs_vncache_free(struct vnode *vp) { struct pfs_vdata *pvd; mtx_lock(&pfs_vncache_mutex); pvd = (struct pfs_vdata *)vp->v_data; KASSERT(pvd != NULL, ("pfs_vncache_free(): no vnode data\n")); if (pvd->pvd_next) pvd->pvd_next->pvd_prev = pvd->pvd_prev; if (pvd->pvd_prev) pvd->pvd_prev->pvd_next = pvd->pvd_next; else pfs_vncache = pvd->pvd_next; mtx_unlock(&pfs_vncache_mutex); --pfs_vncache_entries; FREE(pvd, M_PFSVNCACHE); vp->v_data = NULL; return (0); } /* - * Free all vnodes associated with a defunct process + * Purge the cache of dead / disabled entries * - * XXXRW: It is unfortunate that pfs_exit() always acquires and releases two - * mutexes (one of which is Giant) for every process exit, even if procfs - * isn't mounted. + * This is extremely inefficient due to the fact that vgone() not only + * indirectly modifies the vnode cache, but may also sleep. We can + * neither hold pfs_vncache_mutex across a vgone() call, nor make any + * assumptions about the state of the cache after vgone() returns. In + * consequence, we must start over after every vgone() call, and keep + * trying until we manage to traverse the entire cache. + * + * The only way to improve this situation is to change the data structure + * used to implement the cache. */ -static void -pfs_exit(void *arg, struct proc *p) +void +pfs_purge(struct pfs_node *pn) { struct pfs_vdata *pvd; struct vnode *vnp; - if (pfs_vncache == NULL) - return; - mtx_lock(&Giant); - /* - * This is extremely inefficient due to the fact that vgone() not - * only indirectly modifies the vnode cache, but may also sleep. - * We can neither hold pfs_vncache_mutex across a vgone() call, - * nor make any assumptions about the state of the cache after - * vgone() returns. In consequence, we must start over after - * every vgone() call, and keep trying until we manage to traverse - * the entire cache. - * - * The only way to improve this situation is to change the data - * structure used to implement the cache. An obvious choice in - * this particular case would be a BST sorted by PID. - */ mtx_lock(&pfs_vncache_mutex); pvd = pfs_vncache; while (pvd != NULL) { - if (pvd->pvd_pid == p->p_pid) { + if (pvd->pvd_dead || (pn != NULL && pvd->pvd_pn == pn)) { vnp = pvd->pvd_vnode; vhold(vnp); mtx_unlock(&pfs_vncache_mutex); VOP_LOCK(vnp, LK_EXCLUSIVE, curthread); vgone(vnp); VOP_UNLOCK(vnp, 0, curthread); vdrop(vnp); mtx_lock(&pfs_vncache_mutex); pvd = pfs_vncache; } else { pvd = pvd->pvd_next; } } mtx_unlock(&pfs_vncache_mutex); +} + +/* + * Free all vnodes associated with a defunct process + * + * XXXRW: It is unfortunate that pfs_exit() always acquires and releases two + * mutexes (one of which is Giant) for every process exit, even if procfs + * isn't mounted. + */ +static void +pfs_exit(void *arg, struct proc *p) +{ + struct pfs_vdata *pvd; + int dead; + + if (pfs_vncache == NULL) + return; + mtx_lock(&Giant); + mtx_lock(&pfs_vncache_mutex); + for (pvd = pfs_vncache, dead = 0; pvd != NULL; pvd = pvd->pvd_next) + if (pvd->pvd_pid == p->p_pid) + dead = pvd->pvd_dead = 1; + mtx_unlock(&pfs_vncache_mutex); + if (dead) + pfs_purge(NULL); mtx_unlock(&Giant); } /* * Disable a pseudofs node, and free all vnodes associated with it */ int pfs_disable(struct pfs_node *pn) { - struct pfs_vdata *pvd; - struct vnode *vnp; - if (pn->pn_flags & PFS_DISABLED) return (0); pn->pn_flags |= PFS_DISABLED; - /* XXX see comment above nearly identical code in pfs_exit() */ - mtx_lock(&pfs_vncache_mutex); - pvd = pfs_vncache; - while (pvd != NULL) { - if (pvd->pvd_pn == pn) { - vnp = pvd->pvd_vnode; - vhold(vnp); - mtx_unlock(&pfs_vncache_mutex); - VOP_LOCK(vnp, LK_EXCLUSIVE, curthread); - vgone(vnp); - VOP_UNLOCK(vnp, 0, curthread); - vdrop(vnp); - mtx_lock(&pfs_vncache_mutex); - pvd = pfs_vncache; - } else { - pvd = pvd->pvd_next; - } - } - mtx_unlock(&pfs_vncache_mutex); + pfs_purge(pn); return (0); } /* * Re-enable a disabled pseudofs node */ int pfs_enable(struct pfs_node *pn) { pn->pn_flags &= ~PFS_DISABLED; return (0); }