xref: /dragonfly/sys/kern/vfs_init.c (revision 113f6df6)
1 /*
2  * Copyright (c) 2003,2004 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  *
35  * Copyright (c) 1989, 1993
36  *	The Regents of the University of California.  All rights reserved.
37  *
38  * This code is derived from software contributed
39  * to Berkeley by John Heidemann of the UCLA Ficus project.
40  *
41  * Source: * @(#)i405_init.c 2.10 92/04/27 UCLA Ficus project
42  *
43  * Redistribution and use in source and binary forms, with or without
44  * modification, are permitted provided that the following conditions
45  * are met:
46  * 1. Redistributions of source code must retain the above copyright
47  *    notice, this list of conditions and the following disclaimer.
48  * 2. Redistributions in binary form must reproduce the above copyright
49  *    notice, this list of conditions and the following disclaimer in the
50  *    documentation and/or other materials provided with the distribution.
51  * 3. All advertising materials mentioning features or use of this software
52  *    must display the following acknowledgement:
53  *	This product includes software developed by the University of
54  *	California, Berkeley and its contributors.
55  * 4. Neither the name of the University nor the names of its contributors
56  *    may be used to endorse or promote products derived from this software
57  *    without specific prior written permission.
58  *
59  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69  * SUCH DAMAGE.
70  *
71  *	@(#)vfs_init.c	8.3 (Berkeley) 1/4/94
72  * $FreeBSD: src/sys/kern/vfs_init.c,v 1.59 2002/04/30 18:44:32 dillon Exp $
73  * $DragonFly: src/sys/kern/vfs_init.c,v 1.7 2004/10/12 19:20:46 dillon Exp $
74  */
75 /*
76  * Manage vnode VOP operations vectors
77  */
78 #include <sys/param.h>
79 #include <sys/systm.h>
80 #include <sys/kernel.h>
81 #include <sys/mount.h>
82 #include <sys/sysctl.h>
83 #include <sys/vnode.h>
84 #include <sys/malloc.h>
85 #include <vm/vm_zone.h>
86 
87 static MALLOC_DEFINE(M_VNODEOP, "vnodeops", "vnode operations vectors");
88 
89 /*
90  * Zone for namei
91  */
92 struct vm_zone *namei_zone;
93 
94 /*
95  * vfs_init() will set maxvfsconf
96  * to the highest defined type number.
97  */
98 int maxvfsconf;
99 struct vfsconf *vfsconf;
100 
101 static TAILQ_HEAD(, vnodeopv_node) vnodeopv_list;
102 static void vfs_recalc_vnodeops(void);
103 
104 /*
105  * Add a vnode operations (vnops) vector to the global list.
106  */
107 void
108 vfs_add_vnodeops_sysinit(const void *data)
109 {
110 	const struct vnodeopv_desc *vdesc = data;
111 
112 	vfs_add_vnodeops(vdesc->opv_desc_vector, vdesc->opv_desc_ops);
113 }
114 
115 /*
116  * Unlink previously added vnode operations vector.
117  */
118 void
119 vfs_rm_vnodeops_sysinit(const void *data)
120 {
121 	const struct vnodeopv_desc *vdesc = data;
122 
123 	vfs_rm_vnodeops(vdesc->opv_desc_vector);
124 }
125 
126 void
127 vfs_add_vnodeops(struct vop_ops **vops_pp, struct vnodeopv_entry_desc *descs)
128 {
129 	struct vnodeopv_node *node;
130 	struct vop_ops *ops;
131 
132 	node = malloc(sizeof(*node), M_VNODEOP, M_ZERO|M_WAITOK);
133 	if ((ops = *vops_pp) == NULL) {
134 		ops = malloc(sizeof(struct vop_ops),
135 				M_VNODEOP, M_ZERO|M_WAITOK);
136 		*vops_pp = ops;
137 	}
138 	node->ops = ops;
139 	node->descs = descs;
140 	++ops->vv_refs;
141 	TAILQ_INSERT_TAIL(&vnodeopv_list, node, entry);
142 	vfs_recalc_vnodeops();
143 }
144 
145 void
146 vfs_rm_vnodeops(struct vop_ops **vops_pp)
147 {
148 	struct vop_ops *ops = *vops_pp;
149 	struct vnodeopv_node *node;
150 
151 	if (ops == NULL)
152 		return;
153 
154 	TAILQ_FOREACH(node, &vnodeopv_list, entry) {
155 		if (node->ops == ops)
156 			break;
157 	}
158 	if (node == NULL) {
159 		printf("vfs_rm_vnodeops: unable to find ops: %p\n", ops);
160 		return;
161 	}
162 	TAILQ_REMOVE(&vnodeopv_list, node, entry);
163 	free(node, M_VNODEOP);
164 	KKASSERT(ops != NULL && ops->vv_refs > 0);
165 	if (--ops->vv_refs == 0) {
166 		*vops_pp = NULL;
167 		free(ops, M_VNODEOP);
168 	}
169 	vfs_recalc_vnodeops();
170 }
171 
172 /*
173  * Recalculate VFS operations vectors
174  */
175 static void
176 vfs_recalc_vnodeops(void)
177 {
178 	struct vnodeopv_node *node;
179 	struct vnodeopv_entry_desc *desc;
180 	struct vop_ops *ops;
181 	struct vop_ops *vnew;
182 	int off;
183 
184 	/*
185 	 * Because vop_ops may be active we can't just blow them away, we
186 	 * have to generate new vop_ops and then copy them into the running
187 	 * vop_ops.  Any missing entries will be assigned to the default
188 	 * entry.  If the default entry itself is missing it will be assigned
189 	 * to vop_eopnotsupp.
190 	 */
191 	TAILQ_FOREACH(node, &vnodeopv_list, entry) {
192 		ops = node->ops;
193 		if ((vnew = ops->vv_new) == NULL) {
194 			vnew = malloc(sizeof(struct vop_ops),
195 					M_VNODEOP, M_ZERO|M_WAITOK);
196 			ops->vv_new = vnew;
197 			vnew->vop_default = vop_eopnotsupp;
198 		}
199 		for (desc = node->descs; desc->opve_op; ++desc) {
200 			off = desc->opve_op->vdesc_offset;
201 			*(void **)((char *)vnew + off) = desc->opve_func;
202 		}
203 		for (off = __offsetof(struct vop_ops, vop_ops_first_field);
204 		     off <= __offsetof(struct vop_ops, vop_ops_last_field);
205 		     off += sizeof(void **)
206 		) {
207 			if (*(void **)((char *)vnew + off) == NULL)
208 			    *(void **)((char *)vnew + off) = vnew->vop_default;
209 		}
210 	}
211 
212 	/*
213 	 * Copy the temporary ops into the running configuration and then
214 	 * delete them.
215 	 */
216 	TAILQ_FOREACH(node, &vnodeopv_list, entry) {
217 		ops = node->ops;
218 		if ((vnew = ops->vv_new) == NULL)
219 			continue;
220 		for (off = __offsetof(struct vop_ops, vop_ops_first_field);
221 		     off <= __offsetof(struct vop_ops, vop_ops_last_field);
222 		     off += sizeof(void **)
223 		) {
224 			*(void **)((char *)ops + off) =
225 				*(void **)((char *)vnew + off);
226 		}
227 		ops->vv_new = NULL;
228 		free(vnew, M_VNODEOP);
229 	}
230 }
231 
232 /*
233  * Routines having to do with the management of the vnode table.
234  */
235 struct vattr va_null;
236 
237 /*
238  * Initialize the vnode structures and initialize each file system type.
239  */
240 /* ARGSUSED*/
241 static void
242 vfsinit(void *dummy)
243 {
244 	TAILQ_INIT(&vnodeopv_list);
245 	namei_zone = zinit("NAMEI", MAXPATHLEN, 0, 0, 2);
246 
247 	/*
248 	 * Initialize the vnode table
249 	 */
250 	vfs_subr_init();
251 	vfs_mount_init();
252 	vfs_lock_init();
253 	vfs_sync_init();
254 	/*
255 	 * Initialize the vnode name cache
256 	 */
257 	nchinit();
258 	/*
259 	 * Initialize each file system type.
260 	 * Vfs type numbers must be distinct from VFS_GENERIC (and VFS_VFSCONF).
261 	 */
262 	vattr_null(&va_null);
263 	maxvfsconf = VFS_GENERIC + 1;
264 }
265 SYSINIT(vfs, SI_SUB_VFS, SI_ORDER_FIRST, vfsinit, NULL)
266 
267 /*
268  * Register a VFS.
269  *
270  * After doing general initialisation, this function will
271  * call the filesystem specific initialisation vector op,
272  * i.e. vfsops->vfs_init().
273  */
274 int
275 vfs_register(struct vfsconf *vfc)
276 {
277 	struct sysctl_oid *oidp;
278 	struct vfsconf *vfsp;
279 
280 	vfsp = NULL;
281 	if (vfsconf)
282 		for (vfsp = vfsconf; vfsp->vfc_next; vfsp = vfsp->vfc_next)
283 			if (strcmp(vfc->vfc_name, vfsp->vfc_name) == 0)
284 				return EEXIST;
285 
286 	vfc->vfc_typenum = maxvfsconf++;
287 	if (vfsp)
288 		vfsp->vfc_next = vfc;
289 	else
290 		vfsconf = vfc;
291 	vfc->vfc_next = NULL;
292 
293 	/*
294 	 * If this filesystem has a sysctl node under vfs
295 	 * (i.e. vfs.xxfs), then change the oid number of that node to
296 	 * match the filesystem's type number.  This allows user code
297 	 * which uses the type number to read sysctl variables defined
298 	 * by the filesystem to continue working. Since the oids are
299 	 * in a sorted list, we need to make sure the order is
300 	 * preserved by re-registering the oid after modifying its
301 	 * number.
302 	 */
303 	SLIST_FOREACH(oidp, &sysctl__vfs_children, oid_link)
304 		if (strcmp(oidp->oid_name, vfc->vfc_name) == 0) {
305 			sysctl_unregister_oid(oidp);
306 			oidp->oid_number = vfc->vfc_typenum;
307 			sysctl_register_oid(oidp);
308 		}
309 
310 	/*
311 	 * Call init function for this VFS...
312 	 */
313 	(*(vfc->vfc_vfsops->vfs_init))(vfc);
314 
315 	return 0;
316 }
317 
318 
319 /*
320  * Remove previously registered VFS.
321  *
322  * After doing general de-registration like removing sysctl
323  * nodes etc, it will call the filesystem specific vector
324  * op, i.e. vfsops->vfs_uninit().
325  *
326  */
327 int
328 vfs_unregister(struct vfsconf *vfc)
329 {
330 	struct vfsconf *vfsp, *prev_vfsp;
331 	int error, i, maxtypenum;
332 
333 	i = vfc->vfc_typenum;
334 
335 	prev_vfsp = NULL;
336 	for (vfsp = vfsconf; vfsp;
337 			prev_vfsp = vfsp, vfsp = vfsp->vfc_next) {
338 		if (!strcmp(vfc->vfc_name, vfsp->vfc_name))
339 			break;
340 	}
341 	if (vfsp == NULL)
342 		return EINVAL;
343 	if (vfsp->vfc_refcount)
344 		return EBUSY;
345 	if (vfc->vfc_vfsops->vfs_uninit != NULL) {
346 		error = (*vfc->vfc_vfsops->vfs_uninit)(vfsp);
347 		if (error)
348 			return (error);
349 	}
350 	if (prev_vfsp)
351 		prev_vfsp->vfc_next = vfsp->vfc_next;
352 	else
353 		vfsconf = vfsp->vfc_next;
354 	maxtypenum = VFS_GENERIC;
355 	for (vfsp = vfsconf; vfsp != NULL; vfsp = vfsp->vfc_next)
356 		if (maxtypenum < vfsp->vfc_typenum)
357 			maxtypenum = vfsp->vfc_typenum;
358 	maxvfsconf = maxtypenum + 1;
359 	return 0;
360 }
361 
362 int
363 vfs_modevent(module_t mod, int type, void *data)
364 {
365 	struct vfsconf *vfc;
366 	int error = 0;
367 
368 	vfc = (struct vfsconf *)data;
369 
370 	switch (type) {
371 	case MOD_LOAD:
372 		if (vfc)
373 			error = vfs_register(vfc);
374 		break;
375 
376 	case MOD_UNLOAD:
377 		if (vfc)
378 			error = vfs_unregister(vfc);
379 		break;
380 	default:	/* including MOD_SHUTDOWN */
381 		break;
382 	}
383 	return (error);
384 }
385