chfs_vfsops.c revision 1.15
1/*	$NetBSD: chfs_vfsops.c,v 1.15 2015/01/11 17:29:57 hannken Exp $	*/
2
3/*-
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>
8 * All rights reserved.
9 *
10 * This code is derived from software contributed to The NetBSD Foundation
11 * by the Department of Software Engineering, University of Szeged, Hungary
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
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.
21 *
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
32 * SUCH DAMAGE.
33 */
34
35#include <sys/cdefs.h>
36
37#include <sys/param.h>
38#include <sys/types.h>
39#include <sys/kmem.h>
40#include <sys/mount.h>
41#include <sys/stat.h>
42#include <sys/systm.h>
43#include <sys/proc.h>
44#include <sys/module.h>
45#include <sys/namei.h>
46#include <sys/fcntl.h>
47#include <sys/conf.h>
48#include <sys/buf.h>
49//XXX needed just for debugging
50#include <sys/fstrans.h>
51#include <sys/sleepq.h>
52#include <sys/lockdebug.h>
53#include <sys/ktrace.h>
54
55#include <uvm/uvm.h>
56#include <uvm/uvm_pager.h>
57#include <ufs/ufs/dir.h>
58#include <ufs/ufs/ufs_extern.h>
59#include <miscfs/genfs/genfs.h>
60#include <miscfs/genfs/genfs_node.h>
61#include <miscfs/specfs/specdev.h>
62#include "chfs.h"
63#include "chfs_args.h"
64
65MODULE(MODULE_CLASS_VFS, chfs, "flash");
66
67/* --------------------------------------------------------------------- */
68/* functions */
69
70static int chfs_mount(struct mount *, const char *, void *, size_t *);
71static int chfs_unmount(struct mount *, int);
72static int chfs_root(struct mount *, struct vnode **);
73static int chfs_loadvnode(struct mount *, struct vnode *,
74    const void *, size_t, const void **);
75static int chfs_vget(struct mount *, ino_t, struct vnode **);
76static int chfs_fhtovp(struct mount *, struct fid *, struct vnode **);
77static int chfs_vptofh(struct vnode *, struct fid *, size_t *);
78static int chfs_start(struct mount *, int);
79static int chfs_statvfs(struct mount *, struct statvfs *);
80static int chfs_sync(struct mount *, int, kauth_cred_t);
81static void chfs_init(void);
82static void chfs_reinit(void);
83static void chfs_done(void);
84static int chfs_snapshot(struct mount *, struct vnode *,
85    struct timespec *);
86
87/* --------------------------------------------------------------------- */
88/* structures */
89
90int
91chfs_gop_alloc(struct vnode *vp, off_t off, off_t len,  int flags,
92    kauth_cred_t cred)
93{
94	return (0);
95}
96
97const struct genfs_ops chfs_genfsops = {
98	.gop_size = genfs_size,
99	.gop_alloc = chfs_gop_alloc,
100	.gop_write = genfs_gop_write,
101	.gop_markupdate = ufs_gop_markupdate,
102};
103
104struct pool chfs_inode_pool;
105
106/* for looking up the major for flash */
107extern const struct cdevsw flash_cdevsw;
108
109/* --------------------------------------------------------------------- */
110
111static int
112chfs_mount(struct mount *mp,
113    const char *path, void *data, size_t *data_len)
114{
115	struct lwp *l = curlwp;
116	struct nameidata nd;
117	struct pathbuf *pb;
118	struct vnode *devvp = NULL;
119	struct ufs_args *args = data;
120	struct ufsmount *ump = NULL;
121	struct chfs_mount *chmp;
122	int err = 0;
123	int xflags;
124
125	dbg("mount()\n");
126
127	if (args == NULL)
128		return EINVAL;
129	if (*data_len < sizeof *args)
130		return EINVAL;
131
132	if (mp->mnt_flag & MNT_GETARGS) {
133		ump = VFSTOUFS(mp);
134		if (ump == NULL)
135			return EIO;
136		memset(args, 0, sizeof *args);
137		args->fspec = NULL;
138		*data_len = sizeof *args;
139		return 0;
140	}
141
142	if (mp->mnt_flag & MNT_UPDATE) {
143		/* XXX: There is no support yet to update file system
144		 * settings.  Should be added. */
145
146		return ENODEV;
147	}
148
149	if (args->fspec != NULL) {
150		err = pathbuf_copyin(args->fspec, &pb);
151		if (err) {
152			return err;
153		}
154		/* Look up the name and verify that it's sane. */
155		NDINIT(&nd, LOOKUP, FOLLOW, pb);
156		err = namei(&nd);
157		pathbuf_destroy(pb);
158		if (err)
159			return err;
160		devvp = nd.ni_vp;
161
162		/* Be sure this is a valid block device */
163		if (devvp->v_type != VBLK)
164			err = ENOTBLK;
165		else if (bdevsw_lookup(devvp->v_rdev) == NULL)
166			err = ENXIO;
167	}
168
169	if (err) {
170		vrele(devvp);
171		return (err);
172	}
173
174	if (mp->mnt_flag & MNT_RDONLY)
175		xflags = FREAD;
176	else
177		xflags = FREAD|FWRITE;
178
179	err = VOP_OPEN(devvp, xflags, FSCRED);
180	if (err)
181		goto fail;
182
183	/* call CHFS mount function */
184	err = chfs_mountfs(devvp, mp);
185	if (err) {
186		vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
187		(void)VOP_CLOSE(devvp, xflags, NOCRED);
188		VOP_UNLOCK(devvp);
189		goto fail;
190	}
191
192	ump = VFSTOUFS(mp);
193	chmp = ump->um_chfs;
194
195	vfs_getnewfsid(mp);
196	chmp->chm_fsmp = mp;
197
198	return set_statvfs_info(path,
199	    UIO_USERSPACE, args->fspec,
200	    UIO_USERSPACE, mp->mnt_op->vfs_name, mp, l);
201
202fail:
203	vrele(devvp);
204	return (err);
205}
206
207/* chfs_mountfs - init CHFS */
208int
209chfs_mountfs(struct vnode *devvp, struct mount *mp)
210{
211	struct lwp *l = curlwp;
212	kauth_cred_t cred;
213	devmajor_t flash_major;
214	dev_t dev;
215	struct ufsmount* ump = NULL;
216	struct chfs_mount* chmp;
217	struct vnode *vp;
218	int err = 0;
219
220	dbg("mountfs()\n");
221
222	dev = devvp->v_rdev;
223	cred = l ? l->l_cred : NOCRED;
224
225	/* Flush out any old buffers remaining from a previous use. */
226	vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
227	err = vinvalbuf(devvp, V_SAVE, cred, l, 0, 0);
228	VOP_UNLOCK(devvp);
229	if (err)
230		return (err);
231
232	/* Setup device. */
233	flash_major = cdevsw_lookup_major(&flash_cdevsw);
234
235	if (devvp->v_type != VBLK)
236		err = ENOTBLK;
237	else if (bdevsw_lookup(dev) == NULL)
238		err = ENXIO;
239	else if (major(dev) != flash_major) {
240		dbg("major(dev): %d, flash_major: %d\n",
241		    major(dev), flash_major);
242		err = ENODEV;
243	}
244	if (err) {
245		vrele(devvp);
246		return (err);
247	}
248
249	/* Connect CHFS to UFS. */
250	ump = kmem_zalloc(sizeof(struct ufsmount), KM_SLEEP);
251
252	ump->um_fstype = UFS1;
253	ump->um_chfs = kmem_zalloc(sizeof(struct chfs_mount), KM_SLEEP);
254	mutex_init(&ump->um_lock, MUTEX_DEFAULT, IPL_NONE);
255
256	chmp = ump->um_chfs;
257
258	/* Initialize erase block handler. */
259	chmp->chm_ebh = kmem_alloc(sizeof(struct chfs_ebh), KM_SLEEP);
260
261	dbg("[]opening flash: %u\n", (unsigned int)devvp->v_rdev);
262	err = ebh_open(chmp->chm_ebh, devvp->v_rdev);
263	if (err) {
264		dbg("error while opening flash\n");
265		goto fail;
266	}
267
268	//TODO check flash sizes
269
270	/* Initialize vnode cache's hashtable and eraseblock array. */
271	chmp->chm_gbl_version = 0;
272	chmp->chm_vnocache_hash = chfs_vnocache_hash_init();
273
274	chmp->chm_blocks = kmem_zalloc(chmp->chm_ebh->peb_nr *
275	    sizeof(struct chfs_eraseblock), KM_SLEEP);
276
277	/* Initialize mutexes. */
278	mutex_init(&chmp->chm_lock_mountfields, MUTEX_DEFAULT, IPL_NONE);
279	mutex_init(&chmp->chm_lock_sizes, MUTEX_DEFAULT, IPL_NONE);
280	mutex_init(&chmp->chm_lock_vnocache, MUTEX_DEFAULT, IPL_NONE);
281
282	/* Initialize read/write contants. (from UFS) */
283	chmp->chm_fs_bmask = -4096;
284	chmp->chm_fs_bsize = 4096;
285	chmp->chm_fs_qbmask = 4095;
286	chmp->chm_fs_bshift = 12;
287	chmp->chm_fs_fmask = -2048;
288	chmp->chm_fs_qfmask = 2047;
289
290	/* Initialize writebuffer. */
291	chmp->chm_wbuf_pagesize = chmp->chm_ebh->flash_if->page_size;
292	dbg("wbuf size: %zu\n", chmp->chm_wbuf_pagesize);
293	chmp->chm_wbuf = kmem_alloc(chmp->chm_wbuf_pagesize, KM_SLEEP);
294	rw_init(&chmp->chm_lock_wbuf);
295
296	/* Initialize queues. */
297	TAILQ_INIT(&chmp->chm_free_queue);
298	TAILQ_INIT(&chmp->chm_clean_queue);
299	TAILQ_INIT(&chmp->chm_dirty_queue);
300	TAILQ_INIT(&chmp->chm_very_dirty_queue);
301	TAILQ_INIT(&chmp->chm_erasable_pending_wbuf_queue);
302	TAILQ_INIT(&chmp->chm_erase_pending_queue);
303
304	/* Initialize flash-specific constants. */
305	chfs_calc_trigger_levels(chmp);
306
307	/* Initialize sizes. */
308	chmp->chm_nr_free_blocks = 0;
309	chmp->chm_nr_erasable_blocks = 0;
310	chmp->chm_max_vno = 2;
311	chmp->chm_checked_vno = 2;
312	chmp->chm_unchecked_size = 0;
313	chmp->chm_used_size = 0;
314	chmp->chm_dirty_size = 0;
315	chmp->chm_wasted_size = 0;
316	chmp->chm_free_size = chmp->chm_ebh->eb_size * chmp->chm_ebh->peb_nr;
317
318	/* Build filesystem. */
319	err = chfs_build_filesystem(chmp);
320
321	if (err) {
322		/* Armageddon and return. */
323		chfs_vnocache_hash_destroy(chmp->chm_vnocache_hash);
324		ebh_close(chmp->chm_ebh);
325		err = EIO;
326		goto fail;
327	}
328
329	/* Initialize UFS. */
330	mp->mnt_data = ump;
331	mp->mnt_stat.f_fsidx.__fsid_val[0] = (long)dev;
332	mp->mnt_stat.f_fsidx.__fsid_val[1] = makefstype(MOUNT_CHFS);
333	mp->mnt_stat.f_fsid = mp->mnt_stat.f_fsidx.__fsid_val[0];
334	mp->mnt_stat.f_namemax = MAXNAMLEN;
335	mp->mnt_flag |= MNT_LOCAL;
336	mp->mnt_fs_bshift = PAGE_SHIFT;
337	mp->mnt_dev_bshift = DEV_BSHIFT;
338	mp->mnt_iflag |= IMNT_MPSAFE;
339	ump->um_flags = 0;
340	ump->um_mountp = mp;
341	ump->um_dev = dev;
342	ump->um_devvp = devvp;
343	ump->um_maxfilesize = 1048512 * 1024;
344
345	/* Allocate the root vnode. */
346	err = VFS_VGET(mp, CHFS_ROOTINO, &vp);
347	if (err) {
348		dbg("error: %d while allocating root node\n", err);
349		return err;
350	}
351	vput(vp);
352
353	/* Start GC. */
354	chfs_gc_thread_start(chmp);
355	mutex_enter(&chmp->chm_lock_mountfields);
356	chfs_gc_trigger(chmp);
357	mutex_exit(&chmp->chm_lock_mountfields);
358
359	spec_node_setmountedfs(devvp, mp);
360	return 0;
361
362fail:
363	kmem_free(chmp->chm_ebh, sizeof(struct chfs_ebh));
364	kmem_free(chmp, sizeof(struct chfs_mount));
365	kmem_free(ump, sizeof(struct ufsmount));
366	return err;
367}
368
369/* --------------------------------------------------------------------- */
370
371static int
372chfs_unmount(struct mount *mp, int mntflags)
373{
374	int flags = 0, i = 0;
375	struct ufsmount *ump;
376	struct chfs_mount *chmp;
377
378	if (mntflags & MNT_FORCE)
379		flags |= FORCECLOSE;
380
381	dbg("[START]\n");
382
383	ump = VFSTOUFS(mp);
384	chmp = ump->um_chfs;
385
386	/* Stop GC. */
387	chfs_gc_thread_stop(chmp);
388
389	/* Flush everyt buffer. */
390	(void)vflush(mp, NULLVP, flags);
391
392	if (chmp->chm_wbuf_len) {
393		mutex_enter(&chmp->chm_lock_mountfields);
394		chfs_flush_pending_wbuf(chmp);
395		mutex_exit(&chmp->chm_lock_mountfields);
396	}
397
398	/* Free node references. */
399	for (i = 0; i < chmp->chm_ebh->peb_nr; i++) {
400		chfs_free_node_refs(&chmp->chm_blocks[i]);
401	}
402
403	/* Destroy vnode cache hashtable. */
404	chfs_vnocache_hash_destroy(chmp->chm_vnocache_hash);
405
406	/* Close eraseblock handler. */
407	ebh_close(chmp->chm_ebh);
408
409	/* Destroy mutexes. */
410	rw_destroy(&chmp->chm_lock_wbuf);
411	mutex_destroy(&chmp->chm_lock_vnocache);
412	mutex_destroy(&chmp->chm_lock_sizes);
413	mutex_destroy(&chmp->chm_lock_mountfields);
414
415	/* Unmount UFS. */
416	if (ump->um_devvp->v_type != VBAD) {
417		spec_node_setmountedfs(ump->um_devvp, NULL);
418	}
419	vn_lock(ump->um_devvp, LK_EXCLUSIVE | LK_RETRY);
420	(void)VOP_CLOSE(ump->um_devvp, FREAD|FWRITE, NOCRED);
421	vput(ump->um_devvp);
422
423	mutex_destroy(&ump->um_lock);
424
425	/* Everything done. */
426	kmem_free(ump, sizeof(struct ufsmount));
427	mp->mnt_data = NULL;
428	mp->mnt_flag &= ~MNT_LOCAL;
429	dbg("[END]\n");
430	return (0);
431}
432
433/* --------------------------------------------------------------------- */
434
435static int
436chfs_root(struct mount *mp, struct vnode **vpp)
437{
438	struct vnode *vp;
439	int error;
440
441	if ((error = VFS_VGET(mp, (ino_t)UFS_ROOTINO, &vp)) != 0)
442		return error;
443	*vpp = vp;
444	return 0;
445}
446
447/* --------------------------------------------------------------------- */
448
449extern rb_tree_ops_t frag_rbtree_ops;
450
451static int
452chfs_loadvnode(struct mount *mp, struct vnode *vp,
453    const void *key, size_t key_len, const void **new_key)
454{
455	struct chfs_mount *chmp;
456	struct chfs_inode *ip;
457	struct ufsmount *ump;
458	dev_t dev;
459	int error;
460	struct chfs_vnode_cache* chvc = NULL;
461	struct chfs_node_ref* nref = NULL;
462	struct buf *bp;
463	ino_t ino;
464
465	KASSERT(key_len == sizeof(ino));
466	memcpy(&ino, key, key_len);
467
468	dbg("vget() | ino: %llu\n", (unsigned long long)ino);
469
470	ump = VFSTOUFS(mp);
471	dev = ump->um_dev;
472
473	ip = pool_get(&chfs_inode_pool, PR_WAITOK);
474
475	/* Initialize vnode/inode. */
476	memset(ip, 0, sizeof(*ip));
477	ip->vp = vp;
478	ip->ump = ump;
479	ip->chmp = chmp = ump->um_chfs;
480	ip->dev = dev;
481	ip->ino = ino;
482
483	rb_tree_init(&ip->fragtree, &frag_rbtree_ops);
484
485	vp->v_tag = VT_CHFS;
486	vp->v_op = chfs_vnodeop_p;
487	vp->v_vflag |= VV_LOCKSWORK;
488	if (ino == CHFS_ROOTINO)
489		vp->v_vflag |= VV_ROOT;
490	vp->v_data = ip;
491
492	/* Set root inode. */
493	if (ino == CHFS_ROOTINO) {
494		dbg("SETROOT\n");
495		vp->v_type = VDIR;
496		ip->ch_type = CHT_DIR;
497		ip->mode = IFMT | IEXEC | IWRITE | IREAD;
498		ip->iflag |= (IN_ACCESS | IN_CHANGE | IN_UPDATE);
499		chfs_update(vp, NULL, NULL, UPDATE_WAIT);
500		TAILQ_INIT(&ip->dents);
501		chfs_set_vnode_size(vp, 512);
502	}
503
504	mutex_enter(&chmp->chm_lock_vnocache);
505	chvc = chfs_vnode_cache_get(chmp, ino);
506	mutex_exit(&chmp->chm_lock_vnocache);
507	if (!chvc) {
508		dbg("!chvc\n");
509		/* Initialize the corresponding vnode cache. */
510		/* XXX, we cant alloc under a lock, refactor this! */
511		chvc = chfs_vnode_cache_alloc(ino);
512		mutex_enter(&chmp->chm_lock_vnocache);
513		if (ino == CHFS_ROOTINO) {
514			chvc->nlink = 2;
515			chvc->pvno = CHFS_ROOTINO;
516			chvc->state = VNO_STATE_CHECKEDABSENT;
517		}
518		chfs_vnode_cache_add(chmp, chvc);
519		mutex_exit(&chmp->chm_lock_vnocache);
520
521		ip->chvc = chvc;
522		TAILQ_INIT(&ip->dents);
523	} else {
524		dbg("chvc\n");
525		ip->chvc = chvc;
526		/* We had a vnode cache, the node is already on flash, so read it */
527		if (ino == CHFS_ROOTINO) {
528			chvc->pvno = CHFS_ROOTINO;
529			TAILQ_INIT(&chvc->scan_dirents);
530		} else {
531			chfs_readvnode(mp, ino, &vp);
532		}
533
534		mutex_enter(&chmp->chm_lock_mountfields);
535		/* Initialize type specific things. */
536		error = 0;
537		switch (ip->ch_type) {
538		case CHT_DIR:
539			/* Read every dirent. */
540			nref = chvc->dirents;
541			while (nref &&
542			    (struct chfs_vnode_cache *)nref != chvc) {
543				chfs_readdirent(mp, nref, ip);
544				nref = nref->nref_next;
545			}
546			chfs_set_vnode_size(vp, 512);
547			break;
548		case CHT_REG:
549			/* FALLTHROUGH */
550		case CHT_SOCK:
551			/* Collect data. */
552			dbg("read_inode_internal | ino: %llu\n",
553				(unsigned long long)ip->ino);
554			error = chfs_read_inode(chmp, ip);
555			break;
556		case CHT_LNK:
557			/* Collect data. */
558			dbg("read_inode_internal | ino: %llu\n",
559				(unsigned long long)ip->ino);
560			error = chfs_read_inode_internal(chmp, ip);
561			if (error)
562				break;
563
564			/* Set link. */
565			dbg("size: %llu\n", (unsigned long long)ip->size);
566			bp = getiobuf(vp, true);
567			bp->b_blkno = 0;
568			bp->b_bufsize = bp->b_resid =
569			    bp->b_bcount = ip->size;
570			bp->b_data = kmem_alloc(ip->size, KM_SLEEP);
571			chfs_read_data(chmp, vp, bp);
572			if (!ip->target)
573				ip->target = kmem_alloc(ip->size,
574				    KM_SLEEP);
575			memcpy(ip->target, bp->b_data, ip->size);
576			kmem_free(bp->b_data, ip->size);
577			putiobuf(bp);
578
579			break;
580		case CHT_CHR:
581			/* FALLTHROUGH */
582		case CHT_BLK:
583			/* FALLTHROUGH */
584		case CHT_FIFO:
585			/* Collect data. */
586			dbg("read_inode_internal | ino: %llu\n",
587				(unsigned long long)ip->ino);
588			error = chfs_read_inode_internal(chmp, ip);
589			if (error)
590				break;
591
592			/* Set device. */
593			bp = getiobuf(vp, true);
594			bp->b_blkno = 0;
595			bp->b_bufsize = bp->b_resid =
596			    bp->b_bcount = sizeof(dev_t);
597			bp->b_data = kmem_alloc(sizeof(dev_t), KM_SLEEP);
598			chfs_read_data(chmp, vp, bp);
599			memcpy(&ip->rdev,
600			    bp->b_data, sizeof(dev_t));
601			kmem_free(bp->b_data, sizeof(dev_t));
602			putiobuf(bp);
603			/* Set specific operations. */
604			if (ip->ch_type == CHT_FIFO) {
605				vp->v_op = chfs_fifoop_p;
606			} else {
607				vp->v_op = chfs_specop_p;
608				spec_node_init(vp, ip->rdev);
609			}
610
611		    break;
612		case CHT_BLANK:
613			/* FALLTHROUGH */
614		case CHT_BAD:
615			break;
616		}
617		mutex_exit(&chmp->chm_lock_mountfields);
618		if (error) {
619			vp->v_data = NULL;
620			KASSERT(TAILQ_FIRST(&ip->dents) == NULL);
621			pool_put(&chfs_inode_pool, ip);
622			return error;
623		}
624
625	}
626
627	/* Finish inode initalization. */
628	ip->ch_type = VTTOCHT(vp->v_type);
629	ip->devvp = ump->um_devvp;
630	vref(ip->devvp);
631
632	genfs_node_init(vp, &chfs_genfsops);
633	uvm_vnp_setsize(vp, ip->size);
634
635	*new_key = &ip->ino;
636
637	return 0;
638}
639
640/* --------------------------------------------------------------------- */
641
642static int
643chfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
644{
645	int error;
646
647	error = vcache_get(mp, &ino, sizeof(ino), vpp);
648	if (error)
649		return error;
650
651	error = vn_lock(*vpp, LK_EXCLUSIVE);
652	if (error) {
653		vrele(*vpp);
654		*vpp = NULL;
655		return error;
656	}
657
658	return 0;
659}
660
661/* --------------------------------------------------------------------- */
662
663
664static int
665chfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
666{
667	return ENODEV;
668}
669
670/* --------------------------------------------------------------------- */
671
672static int
673chfs_vptofh(struct vnode *vp, struct fid *fhp, size_t *fh_size)
674{
675	return ENODEV;
676}
677
678/* --------------------------------------------------------------------- */
679
680static int
681chfs_start(struct mount *mp, int flags)
682{
683	return 0;
684}
685
686/* --------------------------------------------------------------------- */
687
688static int
689chfs_statvfs(struct mount *mp, struct statvfs *sbp)
690{
691 	struct chfs_mount *chmp;
692	struct ufsmount *ump;
693	dbg("statvfs\n");
694
695	ump = VFSTOUFS(mp);
696	chmp = ump->um_chfs;
697
698	sbp->f_flag   = mp->mnt_flag;
699	sbp->f_bsize  = chmp->chm_ebh->eb_size;
700	sbp->f_frsize = chmp->chm_ebh->eb_size;
701	sbp->f_iosize = chmp->chm_ebh->eb_size;
702
703	sbp->f_blocks = chmp->chm_ebh->peb_nr;
704	sbp->f_files  = 0;
705	sbp->f_bavail = chmp->chm_nr_free_blocks - chmp->chm_resv_blocks_write;
706
707	sbp->f_bfree = chmp->chm_nr_free_blocks;
708	sbp->f_bresvd = chmp->chm_resv_blocks_write;
709
710	/* FFS specific */
711	sbp->f_ffree  = 0;
712	sbp->f_favail = 0;
713	sbp->f_fresvd = 0;
714
715	copy_statvfs_info(sbp, mp);
716
717	return 0;
718}
719
720/* --------------------------------------------------------------------- */
721
722static int
723chfs_sync(struct mount *mp, int waitfor,
724    kauth_cred_t uc)
725{
726	return 0;
727}
728
729/* --------------------------------------------------------------------- */
730
731static void
732chfs_init(void)
733{
734	/* Initialize pools and inode hash. */
735	chfs_alloc_pool_caches();
736	pool_init(&chfs_inode_pool, sizeof(struct chfs_inode), 0, 0, 0,
737	    "chfsinopl", &pool_allocator_nointr, IPL_NONE);
738	ufs_init();
739}
740
741/* --------------------------------------------------------------------- */
742
743static void
744chfs_reinit(void)
745{
746	ufs_reinit();
747}
748
749/* --------------------------------------------------------------------- */
750
751static void
752chfs_done(void)
753{
754	ufs_done();
755	pool_destroy(&chfs_inode_pool);
756	chfs_destroy_pool_caches();
757}
758
759/* --------------------------------------------------------------------- */
760
761static int
762chfs_snapshot(struct mount *mp, struct vnode *vp,
763    struct timespec *ctime)
764{
765	return ENODEV;
766}
767
768/* --------------------------------------------------------------------- */
769
770/*
771 * chfs vfs operations.
772 */
773
774extern const struct vnodeopv_desc chfs_fifoop_opv_desc;
775extern const struct vnodeopv_desc chfs_specop_opv_desc;
776extern const struct vnodeopv_desc chfs_vnodeop_opv_desc;
777
778const struct vnodeopv_desc * const chfs_vnodeopv_descs[] = {
779	&chfs_fifoop_opv_desc,
780	&chfs_specop_opv_desc,
781	&chfs_vnodeop_opv_desc,
782	NULL,
783};
784
785struct vfsops chfs_vfsops = {
786	.vfs_name = MOUNT_CHFS,
787	.vfs_min_mount_data = sizeof (struct chfs_args),
788	.vfs_mount = chfs_mount,
789	.vfs_start = chfs_start,
790	.vfs_unmount = chfs_unmount,
791	.vfs_root = chfs_root,
792	.vfs_quotactl = ufs_quotactl,
793	.vfs_statvfs = chfs_statvfs,
794	.vfs_sync = chfs_sync,
795	.vfs_vget = chfs_vget,
796	.vfs_loadvnode = chfs_loadvnode,
797	.vfs_fhtovp = chfs_fhtovp,
798	.vfs_vptofh = chfs_vptofh,
799	.vfs_init = chfs_init,
800	.vfs_reinit = chfs_reinit,
801	.vfs_done = chfs_done,
802	.vfs_snapshot = chfs_snapshot,
803	.vfs_extattrctl = vfs_stdextattrctl,
804	.vfs_suspendctl = (void *)eopnotsupp,
805	.vfs_renamelock_enter = genfs_renamelock_enter,
806	.vfs_renamelock_exit = genfs_renamelock_exit,
807	.vfs_fsync = (void *)eopnotsupp,
808	.vfs_opv_descs = chfs_vnodeopv_descs
809};
810
811/* For using CHFS as a module. */
812static int
813chfs_modcmd(modcmd_t cmd, void *arg)
814{
815	switch (cmd) {
816	case MODULE_CMD_INIT:
817		return vfs_attach(&chfs_vfsops);
818	case MODULE_CMD_FINI:
819		return vfs_detach(&chfs_vfsops);
820	default:
821		return ENOTTY;
822	}
823}
824