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