1 /* $NetBSD: chfs_vfsops.c,v 1.9 2013/10/20 17:18:38 christos Exp $ */
4 * Copyright (c) 2010 Department of Software Engineering,
5 * University of Szeged, Hungary
6 * Copyright (C) 2010 Tamas Toth <ttoth@inf.u-szeged.hu>
7 * Copyright (C) 2010 Adam Hoka <ahoka@NetBSD.org>
10 * This code is derived from software contributed to The NetBSD Foundation
11 * by the Department of Software Engineering, University of Szeged, Hungary
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR 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
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 #include <sys/cdefs.h>
37 #include <sys/param.h>
38 #include <sys/types.h>
40 #include <sys/mount.h>
42 #include <sys/systm.h>
44 #include <sys/module.h>
45 #include <sys/namei.h>
46 #include <sys/malloc.h>
47 #include <sys/fcntl.h>
50 //XXX needed just for debugging
51 #include <sys/fstrans.h>
52 #include <sys/sleepq.h>
53 #include <sys/lockdebug.h>
54 #include <sys/ktrace.h>
57 #include <uvm/uvm_pager.h>
58 #include <ufs/ufs/dir.h>
59 #include <ufs/ufs/ufs_extern.h>
60 #include <miscfs/genfs/genfs.h>
61 #include <miscfs/genfs/genfs_node.h>
62 #include <miscfs/specfs/specdev.h>
64 #include "chfs_args.h"
66 MODULE(MODULE_CLASS_VFS
, chfs
, "flash");
68 /* --------------------------------------------------------------------- */
71 static int chfs_mount(struct mount
*, const char *, void *, size_t *);
72 static int chfs_unmount(struct mount
*, int);
73 static int chfs_root(struct mount
*, struct vnode
**);
74 static int chfs_vget(struct mount
*, ino_t
, struct vnode
**);
75 static int chfs_fhtovp(struct mount
*, struct fid
*, struct vnode
**);
76 static int chfs_vptofh(struct vnode
*, struct fid
*, size_t *);
77 static int chfs_start(struct mount
*, int);
78 static int chfs_statvfs(struct mount
*, struct statvfs
*);
79 static int chfs_sync(struct mount
*, int, kauth_cred_t
);
80 static void chfs_init(void);
81 static void chfs_reinit(void);
82 static void chfs_done(void);
83 static int chfs_snapshot(struct mount
*, struct vnode
*,
86 /* --------------------------------------------------------------------- */
90 chfs_gop_alloc(struct vnode
*vp
, off_t off
, off_t len
, int flags
,
96 const struct genfs_ops chfs_genfsops
= {
97 .gop_size
= genfs_size
,
98 .gop_alloc
= chfs_gop_alloc
,
99 .gop_write
= genfs_gop_write
,
100 .gop_markupdate
= ufs_gop_markupdate
,
103 struct pool chfs_inode_pool
;
105 /* for looking up the major for flash */
106 extern const struct cdevsw flash_cdevsw
;
108 /* --------------------------------------------------------------------- */
111 chfs_mount(struct mount
*mp
,
112 const char *path
, void *data
, size_t *data_len
)
114 struct lwp
*l
= curlwp
;
117 struct vnode
*devvp
= NULL
;
118 struct ufs_args
*args
= data
;
119 struct ufsmount
*ump
= NULL
;
120 struct chfs_mount
*chmp
;
126 if (*data_len
< sizeof *args
)
129 if (mp
->mnt_flag
& MNT_GETARGS
) {
133 memset(args
, 0, sizeof *args
);
135 *data_len
= sizeof *args
;
139 if (mp
->mnt_flag
& MNT_UPDATE
) {
140 /* XXX: There is no support yet to update file system
141 * settings. Should be added. */
146 if (args
->fspec
!= NULL
) {
147 err
= pathbuf_copyin(args
->fspec
, &pb
);
151 /* Look up the name and verify that it's sane. */
152 NDINIT(&nd
, LOOKUP
, FOLLOW
, pb
);
153 if ((err
= namei(&nd
)) != 0 )
157 /* Be sure this is a valid block device */
158 if (devvp
->v_type
!= VBLK
)
160 else if (bdevsw_lookup(devvp
->v_rdev
) == NULL
)
169 if (mp
->mnt_flag
& MNT_RDONLY
)
172 xflags
= FREAD
|FWRITE
;
174 err
= VOP_OPEN(devvp
, xflags
, FSCRED
);
178 /* call CHFS mount function */
179 err
= chfs_mountfs(devvp
, mp
);
181 vn_lock(devvp
, LK_EXCLUSIVE
| LK_RETRY
);
182 (void)VOP_CLOSE(devvp
, xflags
, NOCRED
);
193 return set_statvfs_info(path
,
194 UIO_USERSPACE
, args
->fspec
,
195 UIO_USERSPACE
, mp
->mnt_op
->vfs_name
, mp
, l
);
202 /* chfs_mountfs - init CHFS */
204 chfs_mountfs(struct vnode
*devvp
, struct mount
*mp
)
206 struct lwp
*l
= curlwp
;
208 devmajor_t flash_major
;
210 struct ufsmount
* ump
= NULL
;
211 struct chfs_mount
* chmp
;
218 cred
= l
? l
->l_cred
: NOCRED
;
220 /* Flush out any old buffers remaining from a previous use. */
221 vn_lock(devvp
, LK_EXCLUSIVE
| LK_RETRY
);
222 err
= vinvalbuf(devvp
, V_SAVE
, cred
, l
, 0, 0);
228 flash_major
= cdevsw_lookup_major(&flash_cdevsw
);
230 if (devvp
->v_type
!= VBLK
)
232 else if (bdevsw_lookup(dev
) == NULL
)
234 else if (major(dev
) != flash_major
) {
235 dbg("major(dev): %d, flash_major: %d\n",
236 major(dev
), flash_major
);
244 /* Connect CHFS to UFS. */
245 ump
= kmem_zalloc(sizeof(struct ufsmount
), KM_SLEEP
);
247 ump
->um_fstype
= UFS1
;
248 ump
->um_chfs
= kmem_zalloc(sizeof(struct chfs_mount
), KM_SLEEP
);
249 mutex_init(&ump
->um_lock
, MUTEX_DEFAULT
, IPL_NONE
);
253 /* Initialize erase block handler. */
254 chmp
->chm_ebh
= kmem_alloc(sizeof(struct chfs_ebh
), KM_SLEEP
);
256 dbg("[]opening flash: %u\n", (unsigned int)devvp
->v_rdev
);
257 err
= ebh_open(chmp
->chm_ebh
, devvp
->v_rdev
);
259 dbg("error while opening flash\n");
263 //TODO check flash sizes
265 /* Initialize vnode cache's hashtable and eraseblock array. */
266 chmp
->chm_gbl_version
= 0;
267 chmp
->chm_vnocache_hash
= chfs_vnocache_hash_init();
269 chmp
->chm_blocks
= kmem_zalloc(chmp
->chm_ebh
->peb_nr
*
270 sizeof(struct chfs_eraseblock
), KM_SLEEP
);
272 /* Initialize mutexes. */
273 mutex_init(&chmp
->chm_lock_mountfields
, MUTEX_DEFAULT
, IPL_NONE
);
274 mutex_init(&chmp
->chm_lock_sizes
, MUTEX_DEFAULT
, IPL_NONE
);
275 mutex_init(&chmp
->chm_lock_vnocache
, MUTEX_DEFAULT
, IPL_NONE
);
277 /* Initialize read/write contants. (from UFS) */
278 chmp
->chm_fs_bmask
= -4096;
279 chmp
->chm_fs_bsize
= 4096;
280 chmp
->chm_fs_qbmask
= 4095;
281 chmp
->chm_fs_bshift
= 12;
282 chmp
->chm_fs_fmask
= -2048;
283 chmp
->chm_fs_qfmask
= 2047;
285 /* Initialize writebuffer. */
286 chmp
->chm_wbuf_pagesize
= chmp
->chm_ebh
->flash_if
->page_size
;
287 dbg("wbuf size: %zu\n", chmp
->chm_wbuf_pagesize
);
288 chmp
->chm_wbuf
= kmem_alloc(chmp
->chm_wbuf_pagesize
, KM_SLEEP
);
289 rw_init(&chmp
->chm_lock_wbuf
);
291 /* Initialize queues. */
292 TAILQ_INIT(&chmp
->chm_free_queue
);
293 TAILQ_INIT(&chmp
->chm_clean_queue
);
294 TAILQ_INIT(&chmp
->chm_dirty_queue
);
295 TAILQ_INIT(&chmp
->chm_very_dirty_queue
);
296 TAILQ_INIT(&chmp
->chm_erasable_pending_wbuf_queue
);
297 TAILQ_INIT(&chmp
->chm_erase_pending_queue
);
299 /* Initialize flash-specific constants. */
300 chfs_calc_trigger_levels(chmp
);
302 /* Initialize sizes. */
303 chmp
->chm_nr_free_blocks
= 0;
304 chmp
->chm_nr_erasable_blocks
= 0;
305 chmp
->chm_max_vno
= 2;
306 chmp
->chm_checked_vno
= 2;
307 chmp
->chm_unchecked_size
= 0;
308 chmp
->chm_used_size
= 0;
309 chmp
->chm_dirty_size
= 0;
310 chmp
->chm_wasted_size
= 0;
311 chmp
->chm_free_size
= chmp
->chm_ebh
->eb_size
* chmp
->chm_ebh
->peb_nr
;
313 /* Build filesystem. */
314 err
= chfs_build_filesystem(chmp
);
317 /* Armageddon and return. */
318 chfs_vnocache_hash_destroy(chmp
->chm_vnocache_hash
);
319 ebh_close(chmp
->chm_ebh
);
324 /* Initialize UFS. */
326 mp
->mnt_stat
.f_fsidx
.__fsid_val
[0] = (long)dev
;
327 mp
->mnt_stat
.f_fsidx
.__fsid_val
[1] = makefstype(MOUNT_CHFS
);
328 mp
->mnt_stat
.f_fsid
= mp
->mnt_stat
.f_fsidx
.__fsid_val
[0];
329 mp
->mnt_stat
.f_namemax
= MAXNAMLEN
;
330 mp
->mnt_flag
|= MNT_LOCAL
;
331 mp
->mnt_fs_bshift
= PAGE_SHIFT
;
332 mp
->mnt_dev_bshift
= DEV_BSHIFT
;
333 mp
->mnt_iflag
|= IMNT_MPSAFE
;
337 ump
->um_devvp
= devvp
;
338 ump
->um_maxfilesize
= 1048512 * 1024;
340 /* Allocate the root vnode. */
341 err
= VFS_VGET(mp
, CHFS_ROOTINO
, &vp
);
343 dbg("error: %d while allocating root node\n", err
);
349 chfs_gc_thread_start(chmp
);
350 mutex_enter(&chmp
->chm_lock_mountfields
);
351 chfs_gc_trigger(chmp
);
352 mutex_exit(&chmp
->chm_lock_mountfields
);
354 spec_node_setmountedfs(devvp
, mp
);
358 kmem_free(chmp
->chm_ebh
, sizeof(struct chfs_ebh
));
359 kmem_free(chmp
, sizeof(struct chfs_mount
));
360 kmem_free(ump
, sizeof(struct ufsmount
));
364 /* --------------------------------------------------------------------- */
367 chfs_unmount(struct mount
*mp
, int mntflags
)
369 int flags
= 0, i
= 0;
370 struct ufsmount
*ump
;
371 struct chfs_mount
*chmp
;
373 if (mntflags
& MNT_FORCE
)
382 chfs_gc_thread_stop(chmp
);
384 /* Flush everyt buffer. */
385 (void)vflush(mp
, NULLVP
, flags
);
387 if (chmp
->chm_wbuf_len
) {
388 mutex_enter(&chmp
->chm_lock_mountfields
);
389 chfs_flush_pending_wbuf(chmp
);
390 mutex_exit(&chmp
->chm_lock_mountfields
);
393 /* Free node references. */
394 for (i
= 0; i
< chmp
->chm_ebh
->peb_nr
; i
++) {
395 chfs_free_node_refs(&chmp
->chm_blocks
[i
]);
398 /* Destroy vnode cache hashtable. */
399 chfs_vnocache_hash_destroy(chmp
->chm_vnocache_hash
);
401 /* Close eraseblock handler. */
402 ebh_close(chmp
->chm_ebh
);
404 /* Destroy mutexes. */
405 rw_destroy(&chmp
->chm_lock_wbuf
);
406 mutex_destroy(&chmp
->chm_lock_vnocache
);
407 mutex_destroy(&chmp
->chm_lock_sizes
);
408 mutex_destroy(&chmp
->chm_lock_mountfields
);
411 if (ump
->um_devvp
->v_type
!= VBAD
) {
412 spec_node_setmountedfs(ump
->um_devvp
, NULL
);
414 vn_lock(ump
->um_devvp
, LK_EXCLUSIVE
| LK_RETRY
);
415 (void)VOP_CLOSE(ump
->um_devvp
, FREAD
|FWRITE
, NOCRED
);
418 mutex_destroy(&ump
->um_lock
);
420 /* Everything done. */
421 kmem_free(ump
, sizeof(struct ufsmount
));
423 mp
->mnt_flag
&= ~MNT_LOCAL
;
428 /* --------------------------------------------------------------------- */
431 chfs_root(struct mount
*mp
, struct vnode
**vpp
)
436 if ((error
= VFS_VGET(mp
, (ino_t
)UFS_ROOTINO
, &vp
)) != 0)
442 /* --------------------------------------------------------------------- */
444 extern rb_tree_ops_t frag_rbtree_ops
;
447 chfs_vget(struct mount
*mp
, ino_t ino
, struct vnode
**vpp
)
449 struct chfs_mount
*chmp
;
450 struct chfs_inode
*ip
;
451 struct ufsmount
*ump
;
455 struct chfs_vnode_cache
* chvc
= NULL
;
456 struct chfs_node_ref
* nref
= NULL
;
459 dbg("vget() | ino: %llu\n", (unsigned long long)ino
);
465 vpp
= kmem_alloc(sizeof(struct vnode
*), KM_SLEEP
);
468 /* Get node from inode hash. */
469 if ((*vpp
= chfs_ihashget(dev
, ino
, LK_EXCLUSIVE
)) != NULL
) {
473 /* Allocate a new vnode/inode. */
474 if ((error
= getnewvnode(VT_CHFS
,
475 mp
, chfs_vnodeop_p
, NULL
, &vp
)) != 0) {
479 ip
= pool_get(&chfs_inode_pool
, PR_WAITOK
);
481 mutex_enter(&chfs_hashlock
);
482 if ((*vpp
= chfs_ihashget(dev
, ino
, LK_EXCLUSIVE
)) != NULL
) {
483 mutex_exit(&chfs_hashlock
);
485 pool_put(&chfs_inode_pool
, ip
);
489 vp
->v_vflag
|= VV_LOCKSWORK
;
491 /* Initialize vnode/inode. */
492 memset(ip
, 0, sizeof(*ip
));
495 ip
->ch_type
= VTTOCHT(vp
->v_type
);
497 ip
->chmp
= chmp
= ump
->um_chfs
;
501 genfs_node_init(vp
, &chfs_genfsops
);
503 rb_tree_init(&ip
->fragtree
, &frag_rbtree_ops
);
506 mutex_exit(&chfs_hashlock
);
508 /* Set root inode. */
509 if (ino
== CHFS_ROOTINO
) {
511 vp
->v_vflag
|= VV_ROOT
;
513 ip
->ch_type
= CHT_DIR
;
514 ip
->mode
= IFMT
| IEXEC
| IWRITE
| IREAD
;
515 ip
->iflag
|= (IN_ACCESS
| IN_CHANGE
| IN_UPDATE
);
516 chfs_update(vp
, NULL
, NULL
, UPDATE_WAIT
);
517 TAILQ_INIT(&ip
->dents
);
518 chfs_set_vnode_size(vp
, 512);
521 mutex_enter(&chmp
->chm_lock_vnocache
);
522 chvc
= chfs_vnode_cache_get(chmp
, ino
);
523 mutex_exit(&chmp
->chm_lock_vnocache
);
526 /* Initialize the corresponding vnode cache. */
527 /* XXX, we cant alloc under a lock, refactor this! */
528 chvc
= chfs_vnode_cache_alloc(ino
);
529 mutex_enter(&chmp
->chm_lock_vnocache
);
530 if (ino
== CHFS_ROOTINO
) {
532 chvc
->pvno
= CHFS_ROOTINO
;
533 chvc
->state
= VNO_STATE_CHECKEDABSENT
;
535 chfs_vnode_cache_add(chmp
, chvc
);
536 mutex_exit(&chmp
->chm_lock_vnocache
);
539 TAILQ_INIT(&ip
->dents
);
543 /* We had a vnode cache, the node is already on flash, so read it */
544 if (ino
== CHFS_ROOTINO
) {
545 chvc
->pvno
= CHFS_ROOTINO
;
546 TAILQ_INIT(&chvc
->scan_dirents
);
548 chfs_readvnode(mp
, ino
, &vp
);
551 mutex_enter(&chmp
->chm_lock_mountfields
);
552 /* Initialize type specific things. */
553 switch (ip
->ch_type
) {
555 /* Read every dirent. */
556 nref
= chvc
->dirents
;
558 (struct chfs_vnode_cache
*)nref
!= chvc
) {
559 chfs_readdirent(mp
, nref
, ip
);
560 nref
= nref
->nref_next
;
562 chfs_set_vnode_size(vp
, 512);
568 dbg("read_inode_internal | ino: %llu\n",
569 (unsigned long long)ip
->ino
);
570 error
= chfs_read_inode(chmp
, ip
);
574 mutex_exit(&chmp
->chm_lock_mountfields
);
580 dbg("read_inode_internal | ino: %llu\n",
581 (unsigned long long)ip
->ino
);
582 error
= chfs_read_inode_internal(chmp
, ip
);
586 mutex_exit(&chmp
->chm_lock_mountfields
);
591 dbg("size: %llu\n", (unsigned long long)ip
->size
);
592 bp
= getiobuf(vp
, true);
594 bp
->b_bufsize
= bp
->b_resid
=
595 bp
->b_bcount
= ip
->size
;
596 bp
->b_data
= kmem_alloc(ip
->size
, KM_SLEEP
);
597 chfs_read_data(chmp
, vp
, bp
);
599 ip
->target
= kmem_alloc(ip
->size
,
601 memcpy(ip
->target
, bp
->b_data
, ip
->size
);
602 kmem_free(bp
->b_data
, ip
->size
);
612 dbg("read_inode_internal | ino: %llu\n",
613 (unsigned long long)ip
->ino
);
614 error
= chfs_read_inode_internal(chmp
, ip
);
618 mutex_exit(&chmp
->chm_lock_mountfields
);
623 bp
= getiobuf(vp
, true);
625 bp
->b_bufsize
= bp
->b_resid
=
626 bp
->b_bcount
= sizeof(dev_t
);
627 bp
->b_data
= kmem_alloc(sizeof(dev_t
), KM_SLEEP
);
628 chfs_read_data(chmp
, vp
, bp
);
630 bp
->b_data
, sizeof(dev_t
));
631 kmem_free(bp
->b_data
, sizeof(dev_t
));
633 /* Set specific operations. */
634 if (ip
->ch_type
== CHT_FIFO
) {
635 vp
->v_op
= chfs_fifoop_p
;
637 vp
->v_op
= chfs_specop_p
;
638 spec_node_init(vp
, ip
->rdev
);
647 mutex_exit(&chmp
->chm_lock_mountfields
);
651 /* Finish inode initalization. */
652 ip
->devvp
= ump
->um_devvp
;
655 uvm_vnp_setsize(vp
, ip
->size
);
661 /* --------------------------------------------------------------------- */
664 chfs_fhtovp(struct mount
*mp
, struct fid
*fhp
, struct vnode
**vpp
)
669 /* --------------------------------------------------------------------- */
672 chfs_vptofh(struct vnode
*vp
, struct fid
*fhp
, size_t *fh_size
)
677 /* --------------------------------------------------------------------- */
680 chfs_start(struct mount
*mp
, int flags
)
685 /* --------------------------------------------------------------------- */
688 chfs_statvfs(struct mount
*mp
, struct statvfs
*sbp
)
690 struct chfs_mount
*chmp
;
691 struct ufsmount
*ump
;
697 sbp
->f_flag
= mp
->mnt_flag
;
698 sbp
->f_bsize
= chmp
->chm_ebh
->eb_size
;
699 sbp
->f_frsize
= chmp
->chm_ebh
->eb_size
;
700 sbp
->f_iosize
= chmp
->chm_ebh
->eb_size
;
702 sbp
->f_blocks
= chmp
->chm_ebh
->peb_nr
;
704 sbp
->f_bavail
= chmp
->chm_nr_free_blocks
- chmp
->chm_resv_blocks_write
;
706 sbp
->f_bfree
= chmp
->chm_nr_free_blocks
;
707 sbp
->f_bresvd
= chmp
->chm_resv_blocks_write
;
714 copy_statvfs_info(sbp
, mp
);
719 /* --------------------------------------------------------------------- */
722 chfs_sync(struct mount
*mp
, int waitfor
,
728 /* --------------------------------------------------------------------- */
733 /* Initialize pools and inode hash. */
734 chfs_alloc_pool_caches();
736 pool_init(&chfs_inode_pool
, sizeof(struct chfs_inode
), 0, 0, 0,
737 "chfsinopl", &pool_allocator_nointr
, IPL_NONE
);
741 /* --------------------------------------------------------------------- */
750 /* --------------------------------------------------------------------- */
757 pool_destroy(&chfs_inode_pool
);
758 chfs_destroy_pool_caches();
761 /* --------------------------------------------------------------------- */
764 chfs_snapshot(struct mount
*mp
, struct vnode
*vp
,
765 struct timespec
*ctime
)
770 /* --------------------------------------------------------------------- */
773 * chfs vfs operations.
776 extern const struct vnodeopv_desc chfs_fifoop_opv_desc
;
777 extern const struct vnodeopv_desc chfs_specop_opv_desc
;
778 extern const struct vnodeopv_desc chfs_vnodeop_opv_desc
;
780 const struct vnodeopv_desc
* const chfs_vnodeopv_descs
[] = {
781 &chfs_fifoop_opv_desc
,
782 &chfs_specop_opv_desc
,
783 &chfs_vnodeop_opv_desc
,
787 struct vfsops chfs_vfsops
= {
788 MOUNT_CHFS
, /* vfs_name */
789 sizeof (struct chfs_args
),
790 chfs_mount
, /* vfs_mount */
791 chfs_start
, /* vfs_start */
792 chfs_unmount
, /* vfs_unmount */
793 chfs_root
, /* vfs_root */
794 ufs_quotactl
, /* vfs_quotactl */
795 chfs_statvfs
, /* vfs_statvfs */
796 chfs_sync
, /* vfs_sync */
797 chfs_vget
, /* vfs_vget */
798 chfs_fhtovp
, /* vfs_fhtovp */
799 chfs_vptofh
, /* vfs_vptofh */
800 chfs_init
, /* vfs_init */
801 chfs_reinit
, /* vfs_reinit */
802 chfs_done
, /* vfs_done */
803 NULL
, /* vfs_mountroot */
804 chfs_snapshot
, /* vfs_snapshot */
805 vfs_stdextattrctl
, /* vfs_extattrctl */
806 (void *)eopnotsupp
, /* vfs_suspendctl */
807 genfs_renamelock_enter
,
808 genfs_renamelock_exit
,
811 0, /* vfs_refcount */
815 /* For using CHFS as a module. */
817 chfs_modcmd(modcmd_t cmd
, void *arg
)
820 case MODULE_CMD_INIT
:
821 return vfs_attach(&chfs_vfsops
);
822 case MODULE_CMD_FINI
:
823 return vfs_detach(&chfs_vfsops
);