xref: /netbsd-src/sys/fs/puffs/puffs_node.c (revision 7fa608457b817eca6e0977b37f758ae064f3c99c)
1 /*	$NetBSD: puffs_node.c,v 1.6 2007/10/11 19:41:13 pooka Exp $	*/
2 
3 /*
4  * Copyright (c) 2005, 2006, 2007  Antti Kantee.  All Rights Reserved.
5  *
6  * Development of this software was supported by the
7  * Google Summer of Code program, the Ulla Tuominen Foundation
8  * and the Finnish Cultural Foundation.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
20  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: puffs_node.c,v 1.6 2007/10/11 19:41:13 pooka Exp $");
34 
35 #include <sys/param.h>
36 #include <sys/hash.h>
37 #include <sys/kmem.h>
38 #include <sys/malloc.h>
39 #include <sys/mount.h>
40 #include <sys/namei.h>
41 #include <sys/vnode.h>
42 
43 #include <fs/puffs/puffs_msgif.h>
44 #include <fs/puffs/puffs_sys.h>
45 
46 #include <miscfs/genfs/genfs_node.h>
47 #include <miscfs/specfs/specdev.h>
48 
49 static const struct genfs_ops puffs_genfsops = {
50 	.gop_size = puffs_gop_size,
51 	.gop_write = genfs_gop_write,
52 	.gop_markupdate = puffs_gop_markupdate,
53 #if 0
54 	.gop_alloc, should ask userspace
55 #endif
56 };
57 
58 static __inline struct puffs_node_hashlist
59 	*puffs_cookie2hashlist(struct puffs_mount *, void *);
60 static struct puffs_node *puffs_cookie2pnode(struct puffs_mount *, void *);
61 
62 struct pool puffs_pnpool;
63 
64 /*
65  * Grab a vnode, intialize all the puffs-dependant stuff.
66  */
67 int
68 puffs_getvnode(struct mount *mp, void *cookie, enum vtype type,
69 	voff_t vsize, dev_t rdev, struct vnode **vpp)
70 {
71 	struct puffs_mount *pmp;
72 	struct puffs_newcookie *pnc;
73 	struct vnode *vp, *nvp;
74 	struct puffs_node *pnode;
75 	struct puffs_node_hashlist *plist;
76 	int error;
77 
78 	pmp = MPTOPUFFSMP(mp);
79 
80 	error = EPROTO;
81 	if (type <= VNON || type >= VBAD) {
82 		puffs_msg_errnotify(pmp, PUFFS_ERR_MAKENODE, EINVAL,
83 		    "bad node type", cookie);
84 		goto bad;
85 	}
86 	if (vsize == VSIZENOTSET) {
87 		puffs_msg_errnotify(pmp, PUFFS_ERR_MAKENODE, EINVAL,
88 		    "VSIZENOTSET is not a valid size", cookie);
89 		goto bad;
90 	}
91 
92 	/*
93 	 * XXX: there is a deadlock condition between vfs_busy() and
94 	 * vnode locks.  For an unmounting file system the mountpoint
95 	 * is frozen, but in unmount(FORCE) vflush() wants to access all
96 	 * of the vnodes.  If we are here waiting for the mountpoint
97 	 * lock while holding on to a vnode lock, well, we ain't
98 	 * just pining for the fjords anymore.  If we release the
99 	 * vnode lock, we will be in the situation "mount point
100 	 * is dying" and panic() will ensue in insmntque.  So as a
101 	 * temporary workaround, get a vnode without putting it on
102 	 * the mount point list, check if mount point is still alive
103 	 * and kicking and only then add the vnode to the list.
104 	 */
105 	error = getnewvnode(VT_PUFFS, NULL, puffs_vnodeop_p, &vp);
106 	if (error)
107 		goto bad;
108 	vp->v_vnlock = NULL;
109 	vp->v_type = type;
110 
111 	/*
112 	 * Check what mount point isn't going away.  This will work
113 	 * until we decide to remove biglock or make the kernel
114 	 * preemptive.  But hopefully the real problem will be fixed
115 	 * by then.
116 	 *
117 	 * XXX: yes, should call vfs_busy(), but thar be rabbits with
118 	 * vicious streaks a mile wide ...
119 	 *
120 	 * XXX: there is a transient failure here: if someone is unmounting
121 	 * the file system but doesn't succeed (due to it being busy),
122 	 * we incorrectly fail new vnode allocation.  This is *very*
123 	 * hard to fix with the current structure of file system unmounting.
124 	 */
125 	if (mp->mnt_iflag & IMNT_UNMOUNT) {
126 		DPRINTF(("puffs_getvnode: mp %p unmount, unable to create "
127 		    "vnode for cookie %p\n", mp, cookie));
128 		ungetnewvnode(vp);
129 		error = ENXIO;
130 		goto bad;
131 	}
132 
133 	/*
134 	 * Creation should not fail after this point.  Or if it does,
135 	 * care must be taken so that VOP_INACTIVE() isn't called.
136 	 */
137 
138 	/* So mp is not dead yet.. good.. inform new vnode of its master */
139 	simple_lock(&mntvnode_slock);
140 	TAILQ_INSERT_TAIL(&mp->mnt_vnodelist, vp, v_mntvnodes);
141 	simple_unlock(&mntvnode_slock);
142 	vp->v_mount = mp;
143 
144 	/*
145 	 * clerical tasks & footwork
146 	 */
147 
148 	/* default size */
149 	uvm_vnp_setsize(vp, 0);
150 
151 	/* dances based on vnode type. almost ufs_vinit(), but not quite */
152 	switch (type) {
153 	case VCHR:
154 	case VBLK:
155 		/*
156 		 * replace vnode operation vector with the specops vector.
157 		 * our user server has very little control over the node
158 		 * if it decides its a character or block special file
159 		 */
160 		vp->v_op = puffs_specop_p;
161 
162 		/* do the standard checkalias-dance */
163 		if ((nvp = checkalias(vp, rdev, mp)) != NULL) {
164 			/*
165 			 * found: release & unallocate aliased
166 			 * old (well, actually, new) node
167 			 */
168 			vp->v_op = spec_vnodeop_p;
169 			vp->v_vflag &= ~VV_LOCKSWORK;
170 			vrele(vp);
171 			vgone(vp); /* cya */
172 
173 			/* init "new" vnode */
174 			vp = nvp;
175 			vp->v_vnlock = NULL;
176 			vp->v_mount = mp;
177 		}
178 		break;
179 
180 	case VFIFO:
181 		vp->v_op = puffs_fifoop_p;
182 		break;
183 
184 	case VREG:
185 		uvm_vnp_setsize(vp, vsize);
186 		break;
187 
188 	case VDIR:
189 	case VLNK:
190 	case VSOCK:
191 		break;
192 	default:
193 		panic("puffs_getvnode: invalid vtype %d", type);
194 	}
195 
196 	pnode = pool_get(&puffs_pnpool, PR_WAITOK);
197 	memset(pnode, 0, sizeof(struct puffs_node));
198 
199 	pnode->pn_cookie = cookie;
200 	pnode->pn_refcount = 1;
201 
202 	/* insert cookie on list, take off of interlock list */
203 	mutex_init(&pnode->pn_mtx, MUTEX_DEFAULT, IPL_NONE);
204 	SLIST_INIT(&pnode->pn_sel.sel_klist);
205 	plist = puffs_cookie2hashlist(pmp, cookie);
206 	mutex_enter(&pmp->pmp_lock);
207 	LIST_INSERT_HEAD(plist, pnode, pn_hashent);
208 	if (cookie != pmp->pmp_root_cookie) {
209 		LIST_FOREACH(pnc, &pmp->pmp_newcookie, pnc_entries) {
210 			if (pnc->pnc_cookie == cookie) {
211 				LIST_REMOVE(pnc, pnc_entries);
212 				kmem_free(pnc, sizeof(struct puffs_newcookie));
213 				break;
214 			}
215 		}
216 		KASSERT(pnc != NULL);
217 	}
218 	mutex_exit(&pmp->pmp_lock);
219 
220 	vp->v_data = pnode;
221 	vp->v_type = type;
222 	pnode->pn_vp = vp;
223 	pnode->pn_serversize = vsize;
224 
225 	genfs_node_init(vp, &puffs_genfsops);
226 	*vpp = vp;
227 
228 	DPRINTF(("new vnode at %p, pnode %p, cookie %p\n", vp,
229 	    pnode, pnode->pn_cookie));
230 
231 	return 0;
232 
233  bad:
234 	/* remove staging cookie from list */
235 	if (cookie != pmp->pmp_root_cookie) {
236 		mutex_enter(&pmp->pmp_lock);
237 		LIST_FOREACH(pnc, &pmp->pmp_newcookie, pnc_entries) {
238 			if (pnc->pnc_cookie == cookie) {
239 				LIST_REMOVE(pnc, pnc_entries);
240 				kmem_free(pnc, sizeof(struct puffs_newcookie));
241 				break;
242 			}
243 		}
244 		KASSERT(pnc != NULL);
245 		mutex_exit(&pmp->pmp_lock);
246 	}
247 
248 	return error;
249 }
250 
251 /* new node creating for creative vop ops (create, symlink, mkdir, mknod) */
252 int
253 puffs_newnode(struct mount *mp, struct vnode *dvp, struct vnode **vpp,
254 	void *cookie, struct componentname *cnp, enum vtype type, dev_t rdev)
255 {
256 	struct puffs_mount *pmp = MPTOPUFFSMP(mp);
257 	struct puffs_newcookie *pnc;
258 	struct vnode *vp;
259 	int error;
260 
261 	/* userspace probably has this as a NULL op */
262 	if (cookie == NULL) {
263 		error = EOPNOTSUPP;
264 		return error;
265 	}
266 
267 	/*
268 	 * Check for previous node with the same designation.
269 	 * Explicitly check the root node cookie, since it might be
270 	 * reclaimed from the kernel when this check is made.
271 	 */
272 	mutex_enter(&pmp->pmp_lock);
273 	if (cookie == pmp->pmp_root_cookie
274 	    || puffs_cookie2pnode(pmp, cookie) != NULL) {
275 		mutex_exit(&pmp->pmp_lock);
276 		puffs_msg_errnotify(pmp, PUFFS_ERR_MAKENODE, EEXIST,
277 		    "cookie exists", cookie);
278 		return EPROTO;
279 	}
280 
281 	LIST_FOREACH(pnc, &pmp->pmp_newcookie, pnc_entries) {
282 		if (pnc->pnc_cookie == cookie) {
283 			mutex_exit(&pmp->pmp_lock);
284 			puffs_msg_errnotify(pmp, PUFFS_ERR_MAKENODE, EEXIST,
285 			    "cookie exists", cookie);
286 			return EPROTO;
287 		}
288 	}
289 	pnc = kmem_alloc(sizeof(struct puffs_newcookie), KM_SLEEP);
290 	pnc->pnc_cookie = cookie;
291 	LIST_INSERT_HEAD(&pmp->pmp_newcookie, pnc, pnc_entries);
292 	mutex_exit(&pmp->pmp_lock);
293 
294 	error = puffs_getvnode(dvp->v_mount, cookie, type, 0, rdev, &vp);
295 	if (error)
296 		return error;
297 
298 	vp->v_type = type;
299 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
300 	*vpp = vp;
301 
302 	if ((cnp->cn_flags & MAKEENTRY) && PUFFS_USE_NAMECACHE(pmp))
303 		cache_enter(dvp, vp, cnp);
304 
305 	return 0;
306 }
307 
308 void
309 puffs_putvnode(struct vnode *vp)
310 {
311 	struct puffs_mount *pmp;
312 	struct puffs_node *pnode;
313 
314 	pmp = VPTOPUFFSMP(vp);
315 	pnode = VPTOPP(vp);
316 
317 #ifdef DIAGNOSTIC
318 	if (vp->v_tag != VT_PUFFS)
319 		panic("puffs_putvnode: %p not a puffs vnode", vp);
320 #endif
321 
322 	LIST_REMOVE(pnode, pn_hashent);
323 	genfs_node_destroy(vp);
324 	puffs_releasenode(pnode);
325 	vp->v_data = NULL;
326 
327 	return;
328 }
329 
330 static __inline struct puffs_node_hashlist *
331 puffs_cookie2hashlist(struct puffs_mount *pmp, void *cookie)
332 {
333 	uint32_t hash;
334 
335 	hash = hash32_buf(&cookie, sizeof(void *), HASH32_BUF_INIT);
336 	return &pmp->pmp_pnodehash[hash % pmp->pmp_npnodehash];
337 }
338 
339 /*
340  * Translate cookie to puffs_node.  Caller must hold pmp_lock
341  * and it will be held upon return.
342  */
343 static struct puffs_node *
344 puffs_cookie2pnode(struct puffs_mount *pmp, void *cookie)
345 {
346 	struct puffs_node_hashlist *plist;
347 	struct puffs_node *pnode;
348 
349 	plist = puffs_cookie2hashlist(pmp, cookie);
350 	LIST_FOREACH(pnode, plist, pn_hashent) {
351 		if (pnode->pn_cookie == cookie)
352 			break;
353 	}
354 
355 	return pnode;
356 }
357 
358 /*
359  * Make sure root vnode exists and reference it.  Does NOT lock.
360  */
361 static int
362 puffs_makeroot(struct puffs_mount *pmp)
363 {
364 	struct vnode *vp;
365 	int rv;
366 
367 	/*
368 	 * pmp_lock must be held if vref()'ing or vrele()'ing the
369 	 * root vnode.  the latter is controlled by puffs_inactive().
370 	 *
371 	 * pmp_root is set here and cleared in puffs_reclaim().
372 	 */
373  retry:
374 	mutex_enter(&pmp->pmp_lock);
375 	vp = pmp->pmp_root;
376 	if (vp) {
377 		simple_lock(&vp->v_interlock);
378 		mutex_exit(&pmp->pmp_lock);
379 		if (vget(vp, LK_INTERLOCK) == 0)
380 			return 0;
381 	} else
382 		mutex_exit(&pmp->pmp_lock);
383 
384 	/*
385 	 * So, didn't have the magic root vnode available.
386 	 * No matter, grab another and stuff it with the cookie.
387 	 */
388 	if ((rv = puffs_getvnode(pmp->pmp_mp, pmp->pmp_root_cookie,
389 	    pmp->pmp_root_vtype, pmp->pmp_root_vsize, pmp->pmp_root_rdev, &vp)))
390 		return rv;
391 
392 	/*
393 	 * Someone magically managed to race us into puffs_getvnode?
394 	 * Put our previous new vnode back and retry.
395 	 */
396 	mutex_enter(&pmp->pmp_lock);
397 	if (pmp->pmp_root) {
398 		mutex_exit(&pmp->pmp_lock);
399 		puffs_putvnode(vp);
400 		goto retry;
401 	}
402 
403 	/* store cache */
404 	vp->v_vflag |= VV_ROOT;
405 	pmp->pmp_root = vp;
406 	mutex_exit(&pmp->pmp_lock);
407 
408 	return 0;
409 }
410 
411 /*
412  * Locate the in-kernel vnode based on the cookie received given
413  * from userspace.  Returns a vnode, if found, NULL otherwise.
414  * The parameter "lock" control whether to lock the possible or
415  * not.  Locking always might cause us to lock against ourselves
416  * in situations where we want the vnode but don't care for the
417  * vnode lock, e.g. file server issued putpages.
418  */
419 int
420 puffs_cookie2vnode(struct puffs_mount *pmp, void *cookie, int lock,
421 	int willcreate, struct vnode **vpp)
422 {
423 	struct puffs_node *pnode;
424 	struct puffs_newcookie *pnc;
425 	struct vnode *vp;
426 	int vgetflags, rv;
427 
428 	/*
429 	 * Handle root in a special manner, since we want to make sure
430 	 * pmp_root is properly set.
431 	 */
432 	if (cookie == pmp->pmp_root_cookie) {
433 		if ((rv = puffs_makeroot(pmp)))
434 			return rv;
435 		if (lock)
436 			vn_lock(pmp->pmp_root, LK_EXCLUSIVE | LK_RETRY);
437 
438 		*vpp = pmp->pmp_root;
439 		return 0;
440 	}
441 
442 	mutex_enter(&pmp->pmp_lock);
443 	pnode = puffs_cookie2pnode(pmp, cookie);
444 	if (pnode == NULL) {
445 		if (willcreate) {
446 			pnc = kmem_alloc(sizeof(struct puffs_newcookie),
447 			    KM_SLEEP);
448 			pnc->pnc_cookie = cookie;
449 			LIST_INSERT_HEAD(&pmp->pmp_newcookie, pnc, pnc_entries);
450 		}
451 		mutex_exit(&pmp->pmp_lock);
452 		return PUFFS_NOSUCHCOOKIE;
453 	}
454 	vp = pnode->pn_vp;
455 	simple_lock(&vp->v_interlock);
456 	mutex_exit(&pmp->pmp_lock);
457 
458 	vgetflags = LK_INTERLOCK;
459 	if (lock)
460 		vgetflags |= LK_EXCLUSIVE | LK_RETRY;
461 	if ((rv = vget(vp, vgetflags)))
462 		return rv;
463 
464 	*vpp = vp;
465 	return 0;
466 }
467 
468 void
469 puffs_updatenode(struct vnode *vp, int flags)
470 {
471 	struct puffs_node *pn;
472 	struct timespec ts;
473 
474 	if (flags == 0)
475 		return;
476 
477 	pn = VPTOPP(vp);
478 	nanotime(&ts);
479 
480 	if (flags & PUFFS_UPDATEATIME) {
481 		pn->pn_mc_atime = ts;
482 		pn->pn_stat |= PNODE_METACACHE_ATIME;
483 	}
484 	if (flags & PUFFS_UPDATECTIME) {
485 		pn->pn_mc_ctime = ts;
486 		pn->pn_stat |= PNODE_METACACHE_CTIME;
487 	}
488 	if (flags & PUFFS_UPDATEMTIME) {
489 		pn->pn_mc_mtime = ts;
490 		pn->pn_stat |= PNODE_METACACHE_MTIME;
491 	}
492 	if (flags & PUFFS_UPDATESIZE) {
493 		pn->pn_mc_size = vp->v_size;
494 		pn->pn_stat |= PNODE_METACACHE_SIZE;
495 	}
496 }
497 
498 /*
499  * Add reference to node.
500  *  mutex held on entry and return
501  */
502 void
503 puffs_referencenode(struct puffs_node *pn)
504 {
505 
506 	KASSERT(mutex_owned(&pn->pn_mtx));
507 	pn->pn_refcount++;
508 }
509 
510 /*
511  * Release pnode structure which dealing with references to the
512  * puffs_node instead of the vnode.  Can't use vref()/vrele() on
513  * the vnode there, since that causes the lovely VOP_INACTIVE(),
514  * which in turn causes the lovely deadlock when called by the one
515  * who is supposed to handle it.
516  */
517 void
518 puffs_releasenode(struct puffs_node *pn)
519 {
520 
521 	mutex_enter(&pn->pn_mtx);
522 	if (--pn->pn_refcount == 0) {
523 		mutex_exit(&pn->pn_mtx);
524 		mutex_destroy(&pn->pn_mtx);
525 		pool_put(&puffs_pnpool, pn);
526 	} else {
527 		mutex_exit(&pn->pn_mtx);
528 	}
529 }
530