diff options
Diffstat (limited to 'sys/kern/vfs_init.c')
-rw-r--r-- | sys/kern/vfs_init.c | 461 |
1 files changed, 461 insertions, 0 deletions
diff --git a/sys/kern/vfs_init.c b/sys/kern/vfs_init.c new file mode 100644 index 0000000..43589c74 --- /dev/null +++ b/sys/kern/vfs_init.c @@ -0,0 +1,461 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed + * to Berkeley by John Heidemann of the UCLA Ficus project. + * + * Source: * @(#)i405_init.c 2.10 92/04/27 UCLA Ficus project + * + * 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. + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. + * + * @(#)vfs_init.c 8.3 (Berkeley) 1/4/94 + * $Id: vfs_init.c,v 1.40 1998/11/15 15:18:30 bde Exp $ + */ + + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/mount.h> +#include <sys/sysctl.h> +#include <sys/vnode.h> +#include <sys/malloc.h> +#include <vm/vm_zone.h> + + +MALLOC_DEFINE(M_VNODE, "vnodes", "Dynamically allocated vnodes"); + +/* + * XXX this bloat just exands the sysctl__vfs linker set a little so that + * we can attach sysctls for VFS modules without expanding the linker set. + * Currently (1998/09/06), only one VFS uses sysctls, so 2 extra linker + * set slots are more than sufficient. + */ +extern struct linker_set sysctl__vfs; +static int mod_xx; +SYSCTL_INT(_vfs, OID_AUTO, mod0, CTLFLAG_RD, &mod_xx, 0, ""); +SYSCTL_INT(_vfs, OID_AUTO, mod1, CTLFLAG_RD, &mod_xx, 0, ""); + +/* + * Zone for namei + */ +struct vm_zone *namei_zone; + +/* + * vfs_init.c + * + * Allocate and fill in operations vectors. + * + * An undocumented feature of this approach to defining operations is that + * there can be multiple entries in vfs_opv_descs for the same operations + * vector. This allows third parties to extend the set of operations + * supported by another layer in a binary compatibile way. For example, + * assume that NFS needed to be modified to support Ficus. NFS has an entry + * (probably nfs_vnopdeop_decls) declaring all the operations NFS supports by + * default. Ficus could add another entry (ficus_nfs_vnodeop_decl_entensions) + * listing those new operations Ficus adds to NFS, all without modifying the + * NFS code. (Of couse, the OTW NFS protocol still needs to be munged, but + * that is a(whole)nother story.) This is a feature. + */ + +/* Table of known vnodeop vectors (list of VFS vnode vectors) */ +static struct vnodeopv_desc **vnodeopv_descs; +static int vnodeopv_num; + +/* Table of known descs (list of vnode op handlers "vop_access_desc") */ +static struct vnodeop_desc **vfs_op_descs; +static int *vfs_op_desc_refs; /* reference counts */ +static int num_op_descs; +static int vfs_opv_numops; + +static void +vfs_opv_recalc(void) +{ + int i, j; + vop_t ***opv_desc_vector_p; + vop_t **opv_desc_vector; + struct vnodeopv_entry_desc *opve_descp; + struct vnodeopv_desc *opv; + + if (vfs_op_descs == NULL) + panic("vfs_opv_recalc called with null vfs_op_descs"); + + /* + * Run through and make sure all known descs have an offset + * + * vop_default_desc is hardwired at offset 1, and offset 0 + * is a panic sanity check. + */ + vfs_opv_numops = 0; + for (i = 0; i < num_op_descs; i++) + if (vfs_opv_numops < (vfs_op_descs[i]->vdesc_offset + 1)) + vfs_opv_numops = vfs_op_descs[i]->vdesc_offset + 1; + for (i = 0; i < num_op_descs; i++) + if (vfs_op_descs[i]->vdesc_offset == 0) + vfs_op_descs[i]->vdesc_offset = vfs_opv_numops++; + /* + * Allocate and fill in the vectors + */ + for (i = 0; i < vnodeopv_num; i++) { + opv = vnodeopv_descs[i]; + opv_desc_vector_p = opv->opv_desc_vector_p; + if (*opv_desc_vector_p) + FREE(*opv_desc_vector_p, M_VNODE); + MALLOC(*opv_desc_vector_p, vop_t **, + vfs_opv_numops * sizeof(vop_t *), M_VNODE, M_WAITOK); + if (*opv_desc_vector_p == NULL) + panic("no memory for vop_t ** vector"); + bzero(*opv_desc_vector_p, vfs_opv_numops * sizeof(vop_t *)); + + /* Fill in, with slot 0 being panic */ + opv_desc_vector = *opv_desc_vector_p; + opv_desc_vector[0] = (vop_t *)vop_panic; + for (j = 0; opv->opv_desc_ops[j].opve_op; j++) { + opve_descp = &(opv->opv_desc_ops[j]); + opv_desc_vector[opve_descp->opve_op->vdesc_offset] = + opve_descp->opve_impl; + } + + /* Replace unfilled routines with their default (slot 1). */ + opv_desc_vector = *(opv->opv_desc_vector_p); + if (opv_desc_vector[1] == NULL) + panic("vfs_opv_recalc: vector without a default."); + for (j = 0; j < vfs_opv_numops; j++) + if (opv_desc_vector[j] == NULL) + opv_desc_vector[j] = opv_desc_vector[1]; + } +} + +void +vfs_add_vnodeops(void *data) +{ + struct vnodeopv_desc *opv; + struct vnodeopv_desc **newopv; + struct vnodeop_desc **newop; + int *newref; + vop_t **opv_desc_vector; + struct vnodeop_desc *desc; + int i, j; + + opv = (struct vnodeopv_desc *)data; + MALLOC(newopv, struct vnodeopv_desc **, + (vnodeopv_num + 1) * sizeof(*newopv), M_VNODE, M_WAITOK); + if (newopv == NULL) + panic("vfs_add_vnodeops: no memory"); + if (vnodeopv_descs) { + bcopy(vnodeopv_descs, newopv, vnodeopv_num * sizeof(*newopv)); + FREE(vnodeopv_descs, M_VNODE); + } + newopv[vnodeopv_num] = opv; + vnodeopv_descs = newopv; + vnodeopv_num++; + + /* See if we have turned up a new vnode op desc */ + opv_desc_vector = *(opv->opv_desc_vector_p); + for (i = 0; (desc = opv->opv_desc_ops[i].opve_op); i++) { + for (j = 0; j < num_op_descs; j++) { + if (desc == vfs_op_descs[j]) { + /* found it, increase reference count */ + vfs_op_desc_refs[j]++; + break; + } + } + if (j == num_op_descs) { + /* not found, new entry */ + MALLOC(newop, struct vnodeop_desc **, + (num_op_descs + 1) * sizeof(*newop), + M_VNODE, M_WAITOK); + if (newop == NULL) + panic("vfs_add_vnodeops: no memory for desc"); + /* new reference count (for unload) */ + MALLOC(newref, int *, + (num_op_descs + 1) * sizeof(*newref), + M_VNODE, M_WAITOK); + if (newref == NULL) + panic("vfs_add_vnodeops: no memory for refs"); + if (vfs_op_descs) { + bcopy(vfs_op_descs, newop, + num_op_descs * sizeof(*newop)); + FREE(vfs_op_descs, M_VNODE); + } + if (vfs_op_desc_refs) { + bcopy(vfs_op_desc_refs, newref, + num_op_descs * sizeof(*newref)); + FREE(vfs_op_desc_refs, M_VNODE); + } + newop[num_op_descs] = desc; + newref[num_op_descs] = 1; + vfs_op_descs = newop; + vfs_op_desc_refs = newref; + num_op_descs++; + } + } + vfs_opv_recalc(); +} + +void +vfs_rm_vnodeops(void *data) +{ + struct vnodeopv_desc *opv; + struct vnodeopv_desc **newopv; + struct vnodeop_desc **newop; + int *newref; + vop_t **opv_desc_vector; + struct vnodeop_desc *desc; + int i, j, k; + + opv = (struct vnodeopv_desc *)data; + /* Lower ref counts on descs in the table and release if zero */ + opv_desc_vector = *(opv->opv_desc_vector_p); + for (i = 0; (desc = opv->opv_desc_ops[i].opve_op); i++) { + for (j = 0; j < num_op_descs; j++) { + if (desc == vfs_op_descs[j]) { + /* found it, decrease reference count */ + vfs_op_desc_refs[j]--; + break; + } + } + for (j = 0; j < num_op_descs; j++) { + if (vfs_op_desc_refs[j] > 0) + continue; + if (vfs_op_desc_refs[j] < 0) + panic("vfs_remove_vnodeops: negative refcnt"); + MALLOC(newop, struct vnodeop_desc **, + (num_op_descs - 1) * sizeof(*newop), + M_VNODE, M_WAITOK); + if (newop == NULL) + panic("vfs_remove_vnodeops: no memory for desc"); + /* new reference count (for unload) */ + MALLOC(newref, int *, + (num_op_descs - 1) * sizeof(*newref), + M_VNODE, M_WAITOK); + if (newref == NULL) + panic("vfs_remove_vnodeops: no memory for refs"); + for (k = j; k < (num_op_descs - 1); k++) { + vfs_op_descs[k] = vfs_op_descs[k + 1]; + vfs_op_desc_refs[k] = vfs_op_desc_refs[k + 1]; + } + bcopy(vfs_op_descs, newop, + (num_op_descs - 1) * sizeof(*newop)); + bcopy(vfs_op_desc_refs, newref, + (num_op_descs - 1) * sizeof(*newref)); + FREE(vfs_op_descs, M_VNODE); + FREE(vfs_op_desc_refs, M_VNODE); + vfs_op_descs = newop; + vfs_op_desc_refs = newref; + num_op_descs--; + } + } + + for (i = 0; i < vnodeopv_num; i++) { + if (vnodeopv_descs[i] == opv) { + for (j = i; j < (vnodeopv_num - 1); j++) + vnodeopv_descs[j] = vnodeopv_descs[j + 1]; + break; + } + } + if (i == vnodeopv_num) + panic("vfs_remove_vnodeops: opv not found"); + MALLOC(newopv, struct vnodeopv_desc **, + (vnodeopv_num - 1) * sizeof(*newopv), M_VNODE, M_WAITOK); + if (newopv == NULL) + panic("vfs_remove_vnodeops: no memory"); + bcopy(vnodeopv_descs, newopv, (vnodeopv_num - 1) * sizeof(*newopv)); + FREE(vnodeopv_descs, M_VNODE); + vnodeopv_descs = newopv; + vnodeopv_num--; + + vfs_opv_recalc(); +} + +/* + * Routines having to do with the management of the vnode table. + */ +struct vattr va_null; + +/* + * Initialize the vnode structures and initialize each file system type. + */ +/* ARGSUSED*/ +static void +vfsinit(void *dummy) +{ + + namei_zone = zinit("NAMEI", MAXPATHLEN, 0, 0, 2); + + /* + * Initialize the vnode table + */ + vntblinit(); + /* + * Initialize the vnode name cache + */ + nchinit(); + /* + * Initialize each file system type. + * Vfs type numbers must be distinct from VFS_GENERIC (and VFS_VFSCONF). + */ + vattr_null(&va_null); + maxvfsconf = VFS_GENERIC + 1; +} +SYSINIT(vfs, SI_SUB_VFS, SI_ORDER_FIRST, vfsinit, NULL) + +int +vfs_register(struct vfsconf *vfc) +{ + struct linker_set *l; + struct sysctl_oid **oidpp; + struct vfsconf *vfsp; + int i, exists; + + vfsp = NULL; + l = &sysctl__vfs; + if (vfsconf) + for (vfsp = vfsconf; vfsp->vfc_next; vfsp = vfsp->vfc_next) + if (!strcmp(vfc->vfc_name, vfsp->vfc_name)) + return EEXIST; + + vfc->vfc_typenum = maxvfsconf++; + if (vfc->vfc_vfsops->vfs_oid != NULL) { + /* + * Attach the oid to the "vfs" node of the sysctl tree if + * it isn't already there (it will be there for statically + * configured vfs's). + */ + exists = 0; + for (i = l->ls_length, + oidpp = (struct sysctl_oid **)l->ls_items; + i-- != 0; oidpp++) + if (*oidpp == vfc->vfc_vfsops->vfs_oid) { + exists = 1; + break; + } + if (exists == 0) + for (i = l->ls_length, + oidpp = (struct sysctl_oid **)l->ls_items; + i-- != 0; oidpp++) { + if (*oidpp == NULL || + *oidpp == &sysctl___vfs_mod0 || + *oidpp == &sysctl___vfs_mod1) { + *oidpp = vfc->vfc_vfsops->vfs_oid; + break; + } + } + + vfc->vfc_vfsops->vfs_oid->oid_number = vfc->vfc_typenum; + sysctl_order_all(); + } + if (vfsp) + vfsp->vfc_next = vfc; + else + vfsconf = vfc; + vfc->vfc_next = NULL; + + /* + * Call init function for this VFS... + */ + (*(vfc->vfc_vfsops->vfs_init))(vfc); + + return 0; +} + + +int +vfs_unregister(struct vfsconf *vfc) +{ + struct linker_set *l; + struct sysctl_oid **oidpp; + struct vfsconf *vfsp, *prev_vfsp; + int error, i, maxtypenum; + + i = vfc->vfc_typenum; + + prev_vfsp = NULL; + for (vfsp = vfsconf; vfsp; + prev_vfsp = vfsp, vfsp = vfsp->vfc_next) { + if (!strcmp(vfc->vfc_name, vfsp->vfc_name)) + break; + } + if (vfsp == NULL) + return EINVAL; + if (vfsp->vfc_refcount) + return EBUSY; + if (vfc->vfc_vfsops->vfs_uninit != NULL) { + error = (*vfc->vfc_vfsops->vfs_uninit)(vfsp); + if (error) + return (error); + } + if (prev_vfsp) + prev_vfsp->vfc_next = vfsp->vfc_next; + else + vfsconf = vfsp->vfc_next; + if (vfsp->vfc_vfsops->vfs_oid != NULL) { + l = &sysctl__vfs; + for (i = l->ls_length, + oidpp = (struct sysctl_oid **)l->ls_items; + i--; oidpp++) { + if (*oidpp == vfsp->vfc_vfsops->vfs_oid) { + *oidpp = NULL; + sysctl_order_all(); + break; + } + } + } + maxtypenum = VFS_GENERIC; + for (vfsp = vfsconf; vfsp != NULL; vfsp = vfsp->vfc_next) + if (maxtypenum < vfsp->vfc_typenum) + maxtypenum = vfsp->vfc_typenum; + maxvfsconf = maxtypenum + 1; + return 0; +} + +int +vfs_modevent(module_t mod, int type, void *data) +{ + struct vfsconf *vfc; + int error = 0; + + vfc = (struct vfsconf *)data; + + switch (type) { + case MOD_LOAD: + if (vfc) + error = vfs_register(vfc); + break; + + case MOD_UNLOAD: + if (vfc) + error = vfs_unregister(vfc); + break; + default: /* including MOD_SHUTDOWN */ + break; + } + return (error); +} |