xref: /dragonfly/sys/kern/vfs_init.c (revision fcce2b94)
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.12 2006/06/01 06:10:50 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 <sys/objcache.h>
86 
87 static MALLOC_DEFINE(M_VNODEOP, "vnodeops", "vnode operations vectors");
88 static MALLOC_DEFINE(M_NAMEI, "nameibufs", "namei path buffers");
89 
90 /*
91  * Zone for namei
92  */
93 struct objcache *namei_oc;
94 
95 /*
96  * vfs_init() will set maxvfsconf
97  * to the highest defined type number.
98  */
99 int maxvfsconf;
100 struct vfsconf *vfsconf;
101 
102 static TAILQ_HEAD(, vnodeopv_node) vnodeopv_list;
103 static void vfs_recalc_vnodeops(void);
104 
105 /*
106  * Add a vnode operations (vnops) vector to the global list.
107  */
108 void
109 vfs_add_vnodeops_sysinit(const void *data)
110 {
111 	const struct vnodeopv_desc *vdesc = data;
112 
113 	vfs_add_vnodeops(NULL, vdesc->opv_desc_vector,
114 			 vdesc->opv_desc_ops, vdesc->opv_flags);
115 }
116 
117 /*
118  * Unlink previously added vnode operations vector.
119  */
120 void
121 vfs_rm_vnodeops_sysinit(const void *data)
122 {
123 	const struct vnodeopv_desc *vdesc = data;
124 
125 	vfs_rm_vnodeops(vdesc->opv_desc_vector);
126 }
127 
128 void
129 vfs_add_vnodeops(struct mount *mp, struct vop_ops **vops_pp,
130 		struct vnodeopv_entry_desc *descs, int flags)
131 {
132 	struct vnodeopv_node *node;
133 	struct vop_ops *ops;
134 
135 	node = malloc(sizeof(*node), M_VNODEOP, M_ZERO|M_WAITOK);
136 	KKASSERT(*vops_pp == NULL);
137 	if ((ops = *vops_pp) == NULL) {
138 		ops = malloc(sizeof(struct vop_ops),
139 				M_VNODEOP, M_ZERO|M_WAITOK);
140 		*vops_pp = ops;
141 	}
142 	node->ops = ops;
143 	node->descs = descs;
144 	ops->vv_mount = mp;
145 	ops->vv_flags |= flags;
146 
147 	/*
148 	 * Journal and coherency ops inherit normal ops flags
149 	 */
150 	if (vops_pp == &mp->mnt_vn_coherency_ops && mp->mnt_vn_norm_ops)
151 	    ops->vv_flags |= mp->mnt_vn_norm_ops->vv_flags;
152 	if (vops_pp == &mp->mnt_vn_journal_ops && mp->mnt_vn_norm_ops)
153 	    ops->vv_flags |= mp->mnt_vn_norm_ops->vv_flags;
154 
155 	++ops->vv_refs;
156 	TAILQ_INSERT_TAIL(&vnodeopv_list, node, entry);
157 
158 	vfs_recalc_vnodeops();
159 
160 	if (mp) {
161 		if (mp->mnt_vn_coherency_ops)
162 			mp->mnt_vn_use_ops = mp->mnt_vn_coherency_ops;
163 		else if (mp->mnt_vn_journal_ops)
164 			mp->mnt_vn_use_ops = mp->mnt_vn_journal_ops;
165 		else
166 			mp->mnt_vn_use_ops = mp->mnt_vn_norm_ops;
167 	}
168 }
169 
170 void
171 vfs_rm_vnodeops(struct vop_ops **vops_pp)
172 {
173 	struct vop_ops *ops = *vops_pp;
174 	struct vnodeopv_node *node;
175 	struct mount *mp;
176 
177 	if (ops == NULL)
178 		return;
179 
180 	TAILQ_FOREACH(node, &vnodeopv_list, entry) {
181 		if (node->ops == ops)
182 			break;
183 	}
184 	if (node == NULL) {
185 		printf("vfs_rm_vnodeops: unable to find ops: %p\n", ops);
186 		return;
187 	}
188 	TAILQ_REMOVE(&vnodeopv_list, node, entry);
189 	free(node, M_VNODEOP);
190 	KKASSERT(ops != NULL && ops->vv_refs > 0);
191 	if (--ops->vv_refs == 0) {
192 		*vops_pp = NULL;
193 		if ((mp = ops->vv_mount) != NULL) {
194 			if (mp->mnt_vn_coherency_ops)
195 				mp->mnt_vn_use_ops = mp->mnt_vn_coherency_ops;
196 			else if (mp->mnt_vn_journal_ops)
197 				mp->mnt_vn_use_ops = mp->mnt_vn_journal_ops;
198 			else
199 				mp->mnt_vn_use_ops = mp->mnt_vn_norm_ops;
200 		}
201 		free(ops, M_VNODEOP);
202 	}
203 	vfs_recalc_vnodeops();
204 }
205 
206 /*
207  * Recalculate VFS operations vectors
208  */
209 static void
210 vfs_recalc_vnodeops(void)
211 {
212 	struct vnodeopv_node *node;
213 	struct vnodeopv_entry_desc *desc;
214 	struct vop_ops *ops;
215 	struct vop_ops *vnew;
216 	int off;
217 
218 	/*
219 	 * Because vop_ops may be active we can't just blow them away, we
220 	 * have to generate new vop_ops and then copy them into the running
221 	 * vop_ops.  Any missing entries will be assigned to the default
222 	 * entry.  If the default entry itself is missing it will be assigned
223 	 * to vop_eopnotsupp.
224 	 */
225 	TAILQ_FOREACH(node, &vnodeopv_list, entry) {
226 		ops = node->ops;
227 		if ((vnew = ops->vv_new) == NULL) {
228 			vnew = malloc(sizeof(struct vop_ops),
229 					M_VNODEOP, M_ZERO|M_WAITOK);
230 			ops->vv_new = vnew;
231 			vnew->vop_default = vop_eopnotsupp;
232 		}
233 		for (desc = node->descs; desc->opve_op; ++desc) {
234 			off = desc->opve_op->vdesc_offset;
235 			*(void **)((char *)vnew + off) = desc->opve_func;
236 		}
237 		for (off = __offsetof(struct vop_ops, vop_ops_first_field);
238 		     off <= __offsetof(struct vop_ops, vop_ops_last_field);
239 		     off += sizeof(void **)
240 		) {
241 			if (*(void **)((char *)vnew + off) == NULL)
242 			    *(void **)((char *)vnew + off) = vnew->vop_default;
243 		}
244 	}
245 
246 	/*
247 	 * Copy the temporary ops into the running configuration and then
248 	 * delete them.
249 	 */
250 	TAILQ_FOREACH(node, &vnodeopv_list, entry) {
251 		ops = node->ops;
252 		if ((vnew = ops->vv_new) == NULL)
253 			continue;
254 		for (off = __offsetof(struct vop_ops, vop_ops_first_field);
255 		     off <= __offsetof(struct vop_ops, vop_ops_last_field);
256 		     off += sizeof(void **)
257 		) {
258 			*(void **)((char *)ops + off) =
259 				*(void **)((char *)vnew + off);
260 		}
261 		ops->vv_new = NULL;
262 		free(vnew, M_VNODEOP);
263 	}
264 }
265 
266 /*
267  * Routines having to do with the management of the vnode table.
268  */
269 struct vattr va_null;
270 
271 /*
272  * Initialize the vnode structures and initialize each file system type.
273  */
274 /* ARGSUSED*/
275 static void
276 vfsinit(void *dummy)
277 {
278 	TAILQ_INIT(&vnodeopv_list);
279 	namei_oc = objcache_create_simple(M_NAMEI, MAXPATHLEN);
280 
281 	/*
282 	 * Initialize the vnode table
283 	 */
284 	vfs_subr_init();
285 	vfs_mount_init();
286 	vfs_lock_init();
287 	vfs_sync_init();
288 	/*
289 	 * Initialize the vnode name cache
290 	 */
291 	nchinit();
292 	/*
293 	 * Initialize each file system type.
294 	 * Vfs type numbers must be distinct from VFS_GENERIC (and VFS_VFSCONF).
295 	 */
296 	vattr_null(&va_null);
297 	maxvfsconf = VFS_GENERIC + 1;
298 }
299 SYSINIT(vfs, SI_SUB_VFS, SI_ORDER_FIRST, vfsinit, NULL)
300 
301 /*
302  * Register a VFS.
303  *
304  * After doing general initialisation, this function will
305  * call the filesystem specific initialisation vector op,
306  * i.e. vfsops->vfs_init().
307  */
308 int
309 vfs_register(struct vfsconf *vfc)
310 {
311 	struct sysctl_oid *oidp;
312 	struct vfsconf *vfsp;
313 	struct vfsops *vfsops = NULL;
314 
315 	vfsp = NULL;
316 	if (vfsconf)
317 		for (vfsp = vfsconf; vfsp->vfc_next; vfsp = vfsp->vfc_next)
318 			if (strcmp(vfc->vfc_name, vfsp->vfc_name) == 0)
319 				return EEXIST;
320 
321 	vfc->vfc_typenum = maxvfsconf++;
322 	if (vfsp)
323 		vfsp->vfc_next = vfc;
324 	else
325 		vfsconf = vfc;
326 	vfc->vfc_next = NULL;
327 
328 	/*
329 	 * If this filesystem has a sysctl node under vfs
330 	 * (i.e. vfs.xxfs), then change the oid number of that node to
331 	 * match the filesystem's type number.  This allows user code
332 	 * which uses the type number to read sysctl variables defined
333 	 * by the filesystem to continue working. Since the oids are
334 	 * in a sorted list, we need to make sure the order is
335 	 * preserved by re-registering the oid after modifying its
336 	 * number.
337 	 */
338 	SLIST_FOREACH(oidp, &sysctl__vfs_children, oid_link)
339 		if (strcmp(oidp->oid_name, vfc->vfc_name) == 0) {
340 			sysctl_unregister_oid(oidp);
341 			oidp->oid_number = vfc->vfc_typenum;
342 			sysctl_register_oid(oidp);
343 		}
344 
345 	/*
346 	 * Initialise unused fields in the file system's vfsops vector.
347 	 *
348 	 * NOTE the file system should provide the mount and unmount ops
349 	 * at the least.  In order for unmount to succeed, we also need
350 	 * the file system to provide us with vfsops->vfs_root otherwise
351 	 * the unmount(2) operation will not succeed.
352 	 */
353 	vfsops = vfc->vfc_vfsops;
354 	KKASSERT(vfc->vfc_vfsops != NULL);
355 	KKASSERT(vfsops->vfs_mount != NULL);
356 	KKASSERT(vfsops->vfs_root != NULL);
357 	KKASSERT(vfsops->vfs_unmount != NULL);
358 
359 	if (vfsops->vfs_root == NULL) {
360 		/* return file system's root vnode */
361 		vfsops->vfs_root = vfs_stdroot;
362 	}
363 	if (vfsops->vfs_start == NULL) {
364 		/*
365 		 * Make file system operational before first use.  This
366 		 * routine is called at mount-time for initialising MFS,
367 		 * not used by other file systems.
368 		 */
369 		vfsops->vfs_start = vfs_stdstart;
370 	}
371 	if (vfsops->vfs_quotactl == NULL) {
372 		/* quota control */
373 		vfsops->vfs_quotactl = vfs_stdquotactl;
374 	}
375 	if (vfsops->vfs_statfs == NULL) {
376 		/* return file system's status */
377 		vfsops->vfs_statfs = vfs_stdstatfs;
378 	}
379 	if (vfsops->vfs_sync == NULL) {
380 		/*
381 		 * Flush dirty buffers.  File systems can use vfs_stdsync()
382 		 * by explicitly setting it in the vfsops->vfs_sync vector
383 		 * entry.
384 		 */
385 		vfsops->vfs_sync = vfs_stdnosync;
386 	}
387 	if (vfsops->vfs_vget == NULL) {
388 		/* convert an inode number to a vnode */
389 		vfsops->vfs_vget = vfs_stdvget;
390 	}
391 	if (vfsops->vfs_fhtovp == NULL) {
392 		/* turn an NFS file handle into a vnode */
393 		vfsops->vfs_fhtovp = vfs_stdfhtovp;
394 	}
395 	if (vfsops->vfs_checkexp == NULL) {
396 		/* check if file system is exported */
397 		vfsops->vfs_checkexp = vfs_stdcheckexp;
398 	}
399 	if (vfsops->vfs_vptofh == NULL) {
400 		/* turn a vnode into an NFS file handle */
401 		vfsops->vfs_vptofh = vfs_stdvptofh;
402 	}
403 	if (vfsops->vfs_init == NULL) {
404 		/* file system specific initialisation */
405 		vfsops->vfs_init = vfs_stdinit;
406 	}
407 	if (vfsops->vfs_uninit == NULL) {
408 		/* file system specific uninitialisation */
409 		vfsops->vfs_uninit = vfs_stduninit;
410 	}
411 	if (vfsops->vfs_extattrctl == NULL) {
412 		/* extended attribute control */
413 		vfsops->vfs_extattrctl = vfs_stdextattrctl;
414 	}
415 
416 	/*
417 	 * Call init function for this VFS...
418 	 */
419 	(*(vfc->vfc_vfsops->vfs_init))(vfc);
420 
421 	return 0;
422 }
423 
424 
425 /*
426  * Remove previously registered VFS.
427  *
428  * After doing general de-registration like removing sysctl
429  * nodes etc, it will call the filesystem specific vector
430  * op, i.e. vfsops->vfs_uninit().
431  *
432  */
433 int
434 vfs_unregister(struct vfsconf *vfc)
435 {
436 	struct vfsconf *vfsp, *prev_vfsp;
437 	int error, i, maxtypenum;
438 
439 	i = vfc->vfc_typenum;
440 
441 	prev_vfsp = NULL;
442 	for (vfsp = vfsconf; vfsp;
443 			prev_vfsp = vfsp, vfsp = vfsp->vfc_next) {
444 		if (!strcmp(vfc->vfc_name, vfsp->vfc_name))
445 			break;
446 	}
447 	if (vfsp == NULL)
448 		return EINVAL;
449 	if (vfsp->vfc_refcount)
450 		return EBUSY;
451 	if (vfc->vfc_vfsops->vfs_uninit != NULL) {
452 		error = (*vfc->vfc_vfsops->vfs_uninit)(vfsp);
453 		if (error)
454 			return (error);
455 	}
456 	if (prev_vfsp)
457 		prev_vfsp->vfc_next = vfsp->vfc_next;
458 	else
459 		vfsconf = vfsp->vfc_next;
460 	maxtypenum = VFS_GENERIC;
461 	for (vfsp = vfsconf; vfsp != NULL; vfsp = vfsp->vfc_next)
462 		if (maxtypenum < vfsp->vfc_typenum)
463 			maxtypenum = vfsp->vfc_typenum;
464 	maxvfsconf = maxtypenum + 1;
465 	return 0;
466 }
467 
468 int
469 vfs_modevent(module_t mod, int type, void *data)
470 {
471 	struct vfsconf *vfc;
472 	int error = 0;
473 
474 	vfc = (struct vfsconf *)data;
475 
476 	switch (type) {
477 	case MOD_LOAD:
478 		if (vfc)
479 			error = vfs_register(vfc);
480 		break;
481 
482 	case MOD_UNLOAD:
483 		if (vfc)
484 			error = vfs_unregister(vfc);
485 		break;
486 	default:	/* including MOD_SHUTDOWN */
487 		break;
488 	}
489 	return (error);
490 }
491