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