coda_vfsops.c revision 176139
1/*-
2 *             Coda: an Experimental Distributed File System
3 *                              Release 3.1
4 *
5 *           Copyright (c) 1987-1998 Carnegie Mellon University
6 *                          All Rights Reserved
7 *
8 * Permission  to  use, copy, modify and distribute this software and its
9 * documentation is hereby granted,  provided  that  both  the  copyright
10 * notice  and  this  permission  notice  appear  in  all  copies  of the
11 * software, derivative works or  modified  versions,  and  any  portions
12 * thereof, and that both notices appear in supporting documentation, and
13 * that credit is given to Carnegie Mellon University  in  all  documents
14 * and publicity pertaining to direct or indirect use of this code or its
15 * derivatives.
16 *
17 * CODA IS AN EXPERIMENTAL SOFTWARE SYSTEM AND IS  KNOWN  TO  HAVE  BUGS,
18 * SOME  OF  WHICH MAY HAVE SERIOUS CONSEQUENCES.  CARNEGIE MELLON ALLOWS
19 * FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION.   CARNEGIE  MELLON
20 * DISCLAIMS  ANY  LIABILITY  OF  ANY  KIND  FOR  ANY  DAMAGES WHATSOEVER
21 * RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE  OR  OF
22 * ANY DERIVATIVE WORK.
23 *
24 * Carnegie  Mellon  encourages  users  of  this  software  to return any
25 * improvements or extensions that  they  make,  and  to  grant  Carnegie
26 * Mellon the rights to redistribute these changes without encumbrance.
27 *
28 *  	@(#) src/sys/cfs/coda_vfsops.c,v 1.1.1.1 1998/08/29 21:14:52 rvb Exp $
29 */
30
31/*-
32 * Mach Operating System
33 * Copyright (c) 1989 Carnegie-Mellon University
34 * All rights reserved.  The CMU software License Agreement specifies
35 * the terms and conditions for use and redistribution.
36 */
37
38/*
39 * This code was written for the Coda filesystem at Carnegie Mellon
40 * University.  Contributers include David Steere, James Kistler, and
41 * M. Satyanarayanan.
42 */
43
44#include <sys/cdefs.h>
45__FBSDID("$FreeBSD: head/sys/fs/coda/coda_vfsops.c 176139 2008-02-10 11:18:12Z rwatson $");
46
47#include <sys/param.h>
48#include <sys/systm.h>
49#include <sys/conf.h>
50#include <sys/kernel.h>
51#include <sys/lock.h>
52#include <sys/malloc.h>
53#include <sys/mount.h>
54#include <sys/namei.h>
55#include <sys/proc.h>
56
57#include <fs/coda/coda.h>
58#include <fs/coda/cnode.h>
59#include <fs/coda/coda_vfsops.h>
60#include <fs/coda/coda_venus.h>
61#include <fs/coda/coda_subr.h>
62#include <fs/coda/coda_opstats.h>
63
64MALLOC_DEFINE(M_CODA, "coda", "Various Coda Structures");
65
66int codadebug = 0;
67int coda_vfsop_print_entry = 0;
68#define	ENTRY do {							\
69	if (coda_vfsop_print_entry)					\
70		myprintf(("Entered %s\n", __func__));			\
71} while (0)
72
73struct vnode *coda_ctlvp;
74
75/*
76 * Structure to keep statistics of internally generated/satisfied calls.
77 */
78static struct coda_op_stats coda_vfsopstats[CODA_VFSOPS_SIZE];
79
80#define	MARK_ENTRY(op)		(coda_vfsopstats[op].entries++)
81#define	MARK_INT_SAT(op)	(coda_vfsopstats[op].sat_intrn++)
82#define	MARK_INT_FAIL(op)	(coda_vfsopstats[op].unsat_intrn++)
83#define	MARK_INT_GEN(op)	(coda_vfsopstats[op].gen_intrn++)
84
85int
86coda_vfsopstats_init(void)
87{
88	int i;
89
90	for (i=0; i<CODA_VFSOPS_SIZE;i++) {
91		coda_vfsopstats[i].opcode = i;
92		coda_vfsopstats[i].entries = 0;
93		coda_vfsopstats[i].sat_intrn = 0;
94		coda_vfsopstats[i].unsat_intrn = 0;
95		coda_vfsopstats[i].gen_intrn = 0;
96	}
97	return (0);
98}
99
100static const char *coda_opts[] = { "from", NULL };
101/*
102 * cfs mount vfsop
103 *
104 * Set up mount info record and attach it to vfs struct.
105 */
106/*ARGSUSED*/
107int
108coda_mount(struct mount *vfsp, struct thread *td)
109{
110	struct vnode *dvp;
111	struct cnode *cp;
112	struct cdev *dev;
113	struct coda_mntinfo *mi;
114	struct vnode *rootvp;
115	CodaFid rootfid = INVAL_FID;
116	CodaFid ctlfid = CTL_FID;
117	int error;
118	struct nameidata ndp;
119	ENTRY;
120	char *from;
121
122	if (vfs_filteropt(vfsp->mnt_optnew, coda_opts))
123		return (EINVAL);
124	from = vfs_getopts(vfsp->mnt_optnew, "from", &error);
125	if (error)
126		return (error);
127	coda_vfsopstats_init();
128	coda_vnodeopstats_init();
129	MARK_ENTRY(CODA_MOUNT_STATS);
130	if (CODA_MOUNTED(vfsp)) {
131		MARK_INT_FAIL(CODA_MOUNT_STATS);
132		return (EBUSY);
133	}
134
135	/*
136	 * Validate mount device.  Similar to getmdev().
137	 */
138	NDINIT(&ndp, LOOKUP, FOLLOW, UIO_SYSSPACE, from, td);
139	error = namei(&ndp);
140	dvp = ndp.ni_vp;
141	if (error) {
142		MARK_INT_FAIL(CODA_MOUNT_STATS);
143		return (error);
144	}
145	if (dvp->v_type != VCHR) {
146		MARK_INT_FAIL(CODA_MOUNT_STATS);
147		vrele(dvp);
148		NDFREE(&ndp, NDF_ONLY_PNBUF);
149		return (ENXIO);
150	}
151	dev = dvp->v_rdev;
152	vrele(dvp);
153	NDFREE(&ndp, NDF_ONLY_PNBUF);
154
155	/*
156	 * Initialize the mount record and link it to the vfs struct.
157	 */
158	mi = dev2coda_mntinfo(dev);
159	if (!mi) {
160		MARK_INT_FAIL(CODA_MOUNT_STATS);
161		printf("Coda mount: %s is not a cfs device\n", from);
162		return (ENXIO);
163	}
164	if (!VC_OPEN(&mi->mi_vcomm)) {
165		MARK_INT_FAIL(CODA_MOUNT_STATS);
166		return (ENODEV);
167	}
168
169	/*
170	 * No initialization (here) of mi_vcomm!
171	 */
172	vfsp->mnt_data = mi;
173	vfs_getnewfsid (vfsp);
174	mi->mi_vfsp = vfsp;
175	mi->mi_started = 0;			/* XXX See coda_root() */
176
177	/*
178	 * Make a root vnode to placate the Vnode interface, but don't
179	 * actually make the CODA_ROOT call to venus until the first call to
180	 * coda_root in case a server is down while venus is starting.
181	 */
182	cp = make_coda_node(&rootfid, vfsp, VDIR);
183	rootvp = CTOV(cp);
184	rootvp->v_vflag |= VV_ROOT;
185	cp = make_coda_node(&ctlfid, vfsp, VREG);
186	coda_ctlvp = CTOV(cp);
187
188	/*
189	 * Add vfs and rootvp to chain of vfs hanging off mntinfo.
190	 */
191	mi->mi_vfsp = vfsp;
192	mi->mi_rootvp = rootvp;
193	vfs_mountedfrom(vfsp, from);
194
195	/*
196	 * Error is currently guaranteed to be zero, but in case some code
197	 * changes...
198	 */
199	CODADEBUG(1, myprintf(("coda_mount returned %d\n", error)););
200	if (error)
201		MARK_INT_FAIL(CODA_MOUNT_STATS);
202	else
203		MARK_INT_SAT(CODA_MOUNT_STATS);
204	return (error);
205}
206
207int
208coda_unmount(struct mount *vfsp, int mntflags, struct thread *td)
209{
210	struct coda_mntinfo *mi = vftomi(vfsp);
211	int active, error = 0;
212
213	ENTRY;
214	MARK_ENTRY(CODA_UMOUNT_STATS);
215	if (!CODA_MOUNTED(vfsp)) {
216		MARK_INT_FAIL(CODA_UMOUNT_STATS);
217		return (EINVAL);
218	}
219	if (mi->mi_vfsp == vfsp) {
220		/*
221		 * We found the victim.
222		 */
223		if (!IS_UNMOUNTING(VTOC(mi->mi_rootvp)))
224			return (EBUSY); 	/* Venus is still running */
225#ifdef DEBUG
226		printf("coda_unmount: ROOT: vp %p, cp %p\n", mi->mi_rootvp,
227		    VTOC(mi->mi_rootvp));
228#endif
229		vrele(mi->mi_rootvp);
230		mi->mi_rootvp = NULL;
231		vrele(coda_ctlvp);
232		coda_ctlvp = NULL;
233		active = coda_kill(vfsp, NOT_DOWNCALL);
234		error = vflush(mi->mi_vfsp, 0, FORCECLOSE, td);
235#ifdef CODA_VERBOSE
236		printf("coda_unmount: active = %d, vflush active %d\n",
237		    active, error);
238#endif
239		error = 0;
240		/*
241		 * I'm going to take this out to allow lookups to go through.
242		 * I'm not sure it's important anyway. -- DCS 2/2/94
243		 */
244		/* vfsp->VFS_DATA = NULL; */
245
246		/*
247		 * No more vfsp's to hold onto.
248		 */
249		mi->mi_vfsp = NULL;
250
251		if (error)
252			MARK_INT_FAIL(CODA_UMOUNT_STATS);
253		else
254			MARK_INT_SAT(CODA_UMOUNT_STATS);
255		return (error);
256	}
257	return (EINVAL);
258}
259
260/*
261 * Find root of cfs.
262 */
263int
264coda_root(struct mount *vfsp, int flags, struct vnode **vpp,
265     struct thread *td)
266{
267	struct coda_mntinfo *mi = vftomi(vfsp);
268	struct vnode **result;
269	int error;
270	struct proc *p = td->td_proc;
271	CodaFid VFid;
272	static const CodaFid invalfid = INVAL_FID;
273
274	ENTRY;
275	MARK_ENTRY(CODA_ROOT_STATS);
276	result = NULL;
277	if (vfsp == mi->mi_vfsp) {
278		/*
279		 * Cache the root across calls.  We only need to pass the
280		 * request on to Venus if the root vnode is the dummy we
281		 * installed in coda_mount() with all c_fid members zeroed.
282		 *
283		 * XXX In addition, we assume that the first call to
284		 * coda_root() is from vfs_mount() (before the call to
285		 * checkdirs()) and return the dummy root node to avoid a
286		 * deadlock.  This bug is fixed in the Coda CVS repository
287		 * but not in any released versions as of 6 Mar 2003.
288		 */
289		if (memcmp(&VTOC(mi->mi_rootvp)->c_fid, &invalfid,
290		    sizeof(CodaFid)) != 0 || mi->mi_started == 0) {
291			/*
292			 * Found valid root.
293			 */
294			*vpp = mi->mi_rootvp;
295			mi->mi_started = 1;
296
297			/*
298			 * On Mach, this is vref.  On FreeBSD, vref +
299			 * vn_lock.
300			 */
301			vref(*vpp);
302			vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY);
303			MARK_INT_SAT(CODA_ROOT_STATS);
304			return (0);
305		}
306	}
307
308	error = venus_root(vftomi(vfsp), td->td_ucred, p, &VFid);
309	if (!error) {
310		/*
311		 * Save the new rootfid in the cnode, and rehash the cnode
312		 * into the cnode hash with the new fid key.
313		 */
314		coda_unsave(VTOC(mi->mi_rootvp));
315		VTOC(mi->mi_rootvp)->c_fid = VFid;
316		coda_save(VTOC(mi->mi_rootvp));
317		*vpp = mi->mi_rootvp;
318		vref(*vpp);
319		vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY);
320		MARK_INT_SAT(CODA_ROOT_STATS);
321	} else if (error == ENODEV || error == EINTR) {
322		/*
323		 * Gross hack here!
324		 *
325		 * If Venus fails to respond to the CODA_ROOT call, coda_call
326		 * returns ENODEV. Return the uninitialized root vnode to
327		 * allow vfs operations such as unmount to continue.  Without
328		 * this hack, there is no way to do an unmount if Venus dies
329		 * before a successful CODA_ROOT call is done.  All vnode
330		 * operations will fail.
331		 */
332		*vpp = mi->mi_rootvp;
333		vref(*vpp);
334		vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY);
335		MARK_INT_FAIL(CODA_ROOT_STATS);
336		error = 0;
337	} else {
338		CODADEBUG(CODA_ROOT, myprintf(("error %d in CODA_ROOT\n",
339		    error)););
340		MARK_INT_FAIL(CODA_ROOT_STATS);
341	}
342	return (error);
343}
344
345/*
346 * Get filesystem statistics.
347 */
348int
349coda_statfs(struct mount *vfsp, struct statfs *sbp, struct thread *td)
350{
351
352	ENTRY;
353	MARK_ENTRY(CODA_STATFS_STATS);
354	if (!CODA_MOUNTED(vfsp)) {
355		MARK_INT_FAIL(CODA_STATFS_STATS);
356		return (EINVAL);
357	}
358
359	/*
360	 * XXX - what to do about f_flags, others? --bnoble
361	 *
362	 * We just make up free space counts that are sufficiently large.
363	 */
364	sbp->f_flags = 0;
365	sbp->f_bsize = 8192; /* XXX */
366	sbp->f_iosize = 8192; /* XXX */
367#define	CODA_SFS_SIZ	0x8AB75D
368	sbp->f_blocks = CODA_SFS_SIZ;
369	sbp->f_bfree = CODA_SFS_SIZ;
370	sbp->f_bavail = CODA_SFS_SIZ;
371	sbp->f_files = CODA_SFS_SIZ;
372	sbp->f_ffree = CODA_SFS_SIZ;
373	MARK_INT_SAT(CODA_STATFS_STATS);
374	return (0);
375}
376
377/*
378 * Flush any pending I/O.
379 */
380int
381coda_sync(struct mount *vfsp, int waitfor, struct thread *td)
382{
383
384	ENTRY;
385	MARK_ENTRY(CODA_SYNC_STATS);
386	MARK_INT_SAT(CODA_SYNC_STATS);
387	return (0);
388}
389
390/*
391 * fhtovp is now what vget used to be in 4.3-derived systems.  For some silly
392 * reason, vget is now keyed by a 32 bit ino_t, rather than a type-specific
393 * fid.
394 *
395 * XXX: coda_fhtovp is currently not hooked up, so no NFS export for Coda.
396 * We leave it here in the hopes that someone will find it someday and hook
397 * it up.  Among other things, it will need some reworking to match the
398 * vfs_fhtovp_t prototype.
399 */
400int
401coda_fhtovp(struct mount *vfsp, struct fid *fhp, struct mbuf *nam,
402    struct vnode **vpp, int *exflagsp, struct ucred **creadanonp)
403{
404	struct cfid *cfid = (struct cfid *)fhp;
405	struct cnode *cp = NULL;
406	int error;
407	struct thread *td = curthread; /* XXX -mach */
408	struct proc *p = td->td_proc;
409	CodaFid VFid;
410	int vtype;
411
412	ENTRY;
413	MARK_ENTRY(CODA_VGET_STATS);
414
415	/*
416	 * Check for vget of control object.
417	 */
418	if (IS_CTL_FID(&cfid->cfid_fid)) {
419		*vpp = coda_ctlvp;
420		vref(coda_ctlvp);
421		MARK_INT_SAT(CODA_VGET_STATS);
422		return (0);
423	}
424	error = venus_fhtovp(vftomi(vfsp), &cfid->cfid_fid, td->td_ucred, p,
425	    &VFid, &vtype);
426	if (error) {
427		CODADEBUG(CODA_VGET, myprintf(("vget error %d\n",error)););
428		*vpp = NULL;
429	} else {
430		CODADEBUG(CODA_VGET, myprintf(("vget: %s type %d result "
431		    "%d\n", coda_f2s(&VFid), vtype, error)););
432		cp = make_coda_node(&VFid, vfsp, vtype);
433		*vpp = CTOV(cp);
434	}
435	return (error);
436}
437
438struct vfsops coda_vfsops = {
439	.vfs_mount =		coda_mount,
440	.vfs_root = 		coda_root,
441	.vfs_statfs =		coda_statfs,
442	.vfs_sync = 		coda_sync,
443	.vfs_unmount =		coda_unmount,
444};
445VFS_SET(coda_vfsops, coda, VFCF_NETWORK);
446