puffs_subr.c revision 1.24 1 /* $NetBSD: puffs_subr.c,v 1.24 2007/03/14 12:13:58 pooka Exp $ */
2
3 /*
4 * Copyright (c) 2005, 2006 Antti Kantee. All Rights Reserved.
5 *
6 * Development of this software was supported by the
7 * Google Summer of Code program and the Ulla Tuominen Foundation.
8 * The Google SoC project was mentored by Bill Studenmund.
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 * 3. The name of the company nor the name of the author may be used to
19 * endorse or promote products derived from this software without specific
20 * prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
23 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, 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 __KERNEL_RCSID(0, "$NetBSD: puffs_subr.c,v 1.24 2007/03/14 12:13:58 pooka Exp $");
37
38 #include <sys/param.h>
39 #include <sys/conf.h>
40 #include <sys/hash.h>
41 #include <sys/malloc.h>
42 #include <sys/mount.h>
43 #include <sys/socketvar.h>
44 #include <sys/vnode.h>
45 #include <sys/kauth.h>
46 #include <sys/namei.h>
47
48 #include <fs/puffs/puffs_msgif.h>
49 #include <fs/puffs/puffs_sys.h>
50
51 #include <miscfs/genfs/genfs_node.h>
52 #include <miscfs/specfs/specdev.h>
53
54 POOL_INIT(puffs_pnpool, sizeof(struct puffs_node), 0, 0, 0, "puffspnpl",
55 &pool_allocator_nointr, IPL_NONE);
56
57 #ifdef PUFFSDEBUG
58 int puffsdebug;
59 #endif
60
61 static __inline struct puffs_node_hashlist
62 *puffs_cookie2hashlist(struct puffs_mount *, void *);
63 static struct puffs_node *puffs_cookie2pnode(struct puffs_mount *, void *);
64
65 static void puffs_gop_size(struct vnode *, off_t, off_t *, int);
66 static void puffs_gop_markupdate(struct vnode *, int);
67
68 static const struct genfs_ops puffs_genfsops = {
69 .gop_size = puffs_gop_size,
70 .gop_write = genfs_gop_write,
71 .gop_markupdate = puffs_gop_markupdate,
72 #if 0
73 .gop_alloc, should ask userspace
74 #endif
75 };
76
77 /*
78 * Grab a vnode, intialize all the puffs-dependant stuff.
79 */
80 int
81 puffs_getvnode(struct mount *mp, void *cookie, enum vtype type,
82 voff_t vsize, dev_t rdev, struct vnode **vpp)
83 {
84 struct puffs_mount *pmp;
85 struct vnode *vp, *nvp;
86 struct puffs_node *pnode;
87 struct puffs_node_hashlist *plist;
88 int error;
89
90 pmp = MPTOPUFFSMP(mp);
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 return error;
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 if (mp->mnt_iflag & IMNT_UNMOUNT) {
121 DPRINTF(("puffs_getvnode: mp %p unmount, unable to create "
122 "vnode for cookie %p\n", mp, cookie));
123 ungetnewvnode(vp);
124 return ENXIO;
125 }
126
127 /* So it's not dead yet.. good.. inform new vnode of its master */
128 simple_lock(&mntvnode_slock);
129 TAILQ_INSERT_TAIL(&mp->mnt_vnodelist, vp, v_mntvnodes);
130 simple_unlock(&mntvnode_slock);
131 vp->v_mount = mp;
132
133 /*
134 * clerical tasks & footwork
135 */
136
137 /* dances based on vnode type. almost ufs_vinit(), but not quite */
138 switch (type) {
139 case VCHR:
140 case VBLK:
141 /*
142 * replace vnode operation vector with the specops vector.
143 * our user server has very little control over the node
144 * if it decides its a character or block special file
145 */
146 vp->v_op = puffs_specop_p;
147
148 /* do the standard checkalias-dance */
149 if ((nvp = checkalias(vp, rdev, mp)) != NULL) {
150 /*
151 * found: release & unallocate aliased
152 * old (well, actually, new) node
153 */
154 vp->v_op = spec_vnodeop_p;
155 vp->v_flag &= ~VLOCKSWORK;
156 vrele(vp);
157 vgone(vp); /* cya */
158
159 /* init "new" vnode */
160 vp = nvp;
161 vp->v_vnlock = NULL;
162 vp->v_mount = mp;
163 }
164 break;
165
166 case VFIFO:
167 vp->v_op = puffs_fifoop_p;
168 break;
169
170 case VREG:
171 uvm_vnp_setsize(vp, vsize);
172 break;
173
174 case VDIR:
175 case VLNK:
176 case VSOCK:
177 break;
178 default:
179 #ifdef DIAGNOSTIC
180 panic("puffs_getvnode: invalid vtype %d", type);
181 #endif
182 break;
183 }
184
185 pnode = pool_get(&puffs_pnpool, PR_WAITOK);
186 pnode->pn_cookie = cookie;
187 pnode->pn_stat = 0;
188 plist = puffs_cookie2hashlist(pmp, cookie);
189 LIST_INSERT_HEAD(plist, pnode, pn_hashent);
190 vp->v_data = pnode;
191 vp->v_type = type;
192 pnode->pn_vp = vp;
193
194 genfs_node_init(vp, &puffs_genfsops);
195 *vpp = vp;
196
197 DPRINTF(("new vnode at %p, pnode %p, cookie %p\n", vp,
198 pnode, pnode->pn_cookie));
199
200 return 0;
201 }
202
203 /* new node creating for creative vop ops (create, symlink, mkdir, mknod) */
204 int
205 puffs_newnode(struct mount *mp, struct vnode *dvp, struct vnode **vpp,
206 void *cookie, struct componentname *cnp, enum vtype type, dev_t rdev)
207 {
208 struct puffs_mount *pmp = MPTOPUFFSMP(mp);
209 struct vnode *vp;
210 int error;
211
212 /* userspace probably has this as a NULL op */
213 if (cookie == NULL) {
214 error = EOPNOTSUPP;
215 return error;
216 }
217
218 /*
219 * Check for previous node with the same designation.
220 * Explicitly check the root node cookie, since it might be
221 * reclaimed from the kernel when this check is made.
222 *
223 * XXX: technically this error check should punish the fs,
224 * not the caller.
225 */
226 simple_lock(&pmp->pmp_lock);
227 if (cookie == pmp->pmp_rootcookie
228 || puffs_cookie2pnode(pmp, cookie) != NULL) {
229 simple_unlock(&pmp->pmp_lock);
230 error = EEXIST;
231 return error;
232 }
233 simple_unlock(&pmp->pmp_lock);
234
235 error = puffs_getvnode(dvp->v_mount, cookie, type, 0, rdev, &vp);
236 if (error)
237 return error;
238
239 vp->v_type = type;
240 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
241 *vpp = vp;
242
243 if ((cnp->cn_flags & MAKEENTRY) && PUFFS_DOCACHE(pmp))
244 cache_enter(dvp, vp, cnp);
245
246 return 0;
247 }
248
249 void
250 puffs_putvnode(struct vnode *vp)
251 {
252 struct puffs_mount *pmp;
253 struct puffs_node *pnode;
254
255 pmp = VPTOPUFFSMP(vp);
256 pnode = VPTOPP(vp);
257
258 #ifdef DIAGNOSTIC
259 if (vp->v_tag != VT_PUFFS)
260 panic("puffs_putvnode: %p not a puffs vnode", vp);
261 #endif
262
263 LIST_REMOVE(pnode, pn_hashent);
264 genfs_node_destroy(vp);
265 pool_put(&puffs_pnpool, vp->v_data);
266 vp->v_data = NULL;
267
268 return;
269 }
270
271 static __inline struct puffs_node_hashlist *
272 puffs_cookie2hashlist(struct puffs_mount *pmp, void *cookie)
273 {
274 uint32_t hash;
275
276 hash = hash32_buf(&cookie, sizeof(void *), HASH32_BUF_INIT);
277 return &pmp->pmp_pnodehash[hash % pmp->pmp_npnodehash];
278 }
279
280 /*
281 * Translate cookie to puffs_node. Caller must hold mountpoint
282 * lock and it will be held upon return.
283 */
284 static struct puffs_node *
285 puffs_cookie2pnode(struct puffs_mount *pmp, void *cookie)
286 {
287 struct puffs_node_hashlist *plist;
288 struct puffs_node *pnode;
289
290 plist = puffs_cookie2hashlist(pmp, cookie);
291 LIST_FOREACH(pnode, plist, pn_hashent) {
292 if (pnode->pn_cookie == cookie)
293 break;
294 }
295
296 return pnode;
297 }
298
299 /*
300 * Locate the in-kernel vnode based on the cookie received given
301 * from userspace. Returns a vnode, if found, NULL otherwise.
302 * The parameter "lock" control whether to lock the possible or
303 * not. Locking always might cause us to lock against ourselves
304 * in situations where we want the vnode but don't care for the
305 * vnode lock, e.g. file server issued putpages.
306 */
307 struct vnode *
308 puffs_pnode2vnode(struct puffs_mount *pmp, void *cookie, int lock)
309 {
310 struct puffs_node *pnode;
311 struct vnode *vp;
312 int vgetflags;
313
314 /*
315 * If we're trying to get the root vnode, return it through
316 * puffs_root() to get all the right things set. Lock must
317 * be set, since VFS_ROOT() always locks the returned vnode.
318 */
319 if (cookie == pmp->pmp_rootcookie) {
320 if (!lock)
321 return NULL;
322 if (VFS_ROOT(pmp->pmp_mp, &vp))
323 return NULL;
324
325 return vp;
326 }
327
328 vgetflags = LK_INTERLOCK;
329 if (lock)
330 vgetflags |= LK_EXCLUSIVE | LK_RETRY;
331
332 simple_lock(&pmp->pmp_lock);
333 pnode = puffs_cookie2pnode(pmp, cookie);
334
335 if (pnode == NULL) {
336 simple_unlock(&pmp->pmp_lock);
337 return NULL;
338 }
339 vp = pnode->pn_vp;
340
341 simple_lock(&vp->v_interlock);
342 simple_unlock(&pmp->pmp_lock);
343
344 if (vget(vp, vgetflags))
345 return NULL;
346
347 return vp;
348 }
349
350 void
351 puffs_makecn(struct puffs_kcn *pkcn, const struct componentname *cn)
352 {
353
354 pkcn->pkcn_nameiop = cn->cn_nameiop;
355 pkcn->pkcn_flags = cn->cn_flags;
356 pkcn->pkcn_pid = cn->cn_lwp->l_proc->p_pid;
357 puffs_credcvt(&pkcn->pkcn_cred, cn->cn_cred);
358
359 (void)memcpy(&pkcn->pkcn_name, cn->cn_nameptr, cn->cn_namelen);
360 pkcn->pkcn_name[cn->cn_namelen] = '\0';
361 pkcn->pkcn_namelen = cn->cn_namelen;
362 }
363
364 /*
365 * Convert given credentials to struct puffs_cred for userspace.
366 */
367 void
368 puffs_credcvt(struct puffs_cred *pcr, const kauth_cred_t cred)
369 {
370
371 memset(pcr, 0, sizeof(struct puffs_cred));
372
373 if (cred == NOCRED || cred == FSCRED) {
374 pcr->pcr_type = PUFFCRED_TYPE_INTERNAL;
375 if (cred == NOCRED)
376 pcr->pcr_internal = PUFFCRED_CRED_NOCRED;
377 if (cred == FSCRED)
378 pcr->pcr_internal = PUFFCRED_CRED_FSCRED;
379 } else {
380 pcr->pcr_type = PUFFCRED_TYPE_UUC;
381 kauth_cred_to_uucred(&pcr->pcr_uuc, cred);
382 }
383 }
384
385 /*
386 * Return pid. In case the operation is coming from within the
387 * kernel without any process context, borrow the swapper's pid.
388 */
389 pid_t
390 puffs_lwp2pid(struct lwp *l)
391 {
392
393 return l ? l->l_proc->p_pid : 0;
394 }
395
396
397 static void
398 puffs_gop_size(struct vnode *vp, off_t size, off_t *eobp,
399 int flags)
400 {
401
402 *eobp = size;
403 }
404
405 static void
406 puffs_gop_markupdate(struct vnode *vp, int flags)
407 {
408 int uflags = 0;
409
410 if (flags & GOP_UPDATE_ACCESSED)
411 uflags |= PUFFS_UPDATEATIME;
412 if (flags & GOP_UPDATE_MODIFIED)
413 uflags |= PUFFS_UPDATEMTIME;
414
415 puffs_updatenode(vp, uflags);
416 }
417
418 void
419 puffs_updatenode(struct vnode *vp, int flags)
420 {
421 struct timespec ts;
422 struct puffs_vnreq_setattr *setattr_arg;
423
424 if (flags == 0)
425 return;
426
427 setattr_arg = malloc(sizeof(struct puffs_vnreq_setattr), M_PUFFS,
428 M_NOWAIT | M_ZERO);
429 if (setattr_arg == NULL)
430 return; /* 2bad */
431
432 nanotime(&ts);
433
434 VATTR_NULL(&setattr_arg->pvnr_va);
435 if (flags & PUFFS_UPDATEATIME)
436 setattr_arg->pvnr_va.va_atime = ts;
437 if (flags & PUFFS_UPDATECTIME)
438 setattr_arg->pvnr_va.va_ctime = ts;
439 if (flags & PUFFS_UPDATEMTIME)
440 setattr_arg->pvnr_va.va_mtime = ts;
441 if (flags & PUFFS_UPDATESIZE)
442 setattr_arg->pvnr_va.va_size = vp->v_size;
443
444 setattr_arg->pvnr_pid = 0;
445 puffs_credcvt(&setattr_arg->pvnr_cred, NOCRED);
446
447 /* setattr_arg ownership shifted to callee */
448 puffs_vntouser_faf(MPTOPUFFSMP(vp->v_mount), PUFFS_VN_SETATTR,
449 setattr_arg, sizeof(struct puffs_vnreq_setattr), VPTOPNC(vp));
450 }
451
452 void
453 puffs_updatevpsize(struct vnode *vp)
454 {
455 struct vattr va;
456
457 if (VOP_GETATTR(vp, &va, FSCRED, NULL))
458 return;
459
460 if (va.va_size != VNOVAL)
461 vp->v_size = va.va_size;
462 }
463
464 /*
465 * We're dead, kaput, RIP, slightly more than merely pining for the
466 * fjords, belly-up, fallen, lifeless, finished, expired, gone to meet
467 * our maker, ceased to be, etcetc. YASD. It's a dead FS!
468 *
469 * Caller must hold puffs spinlock.
470 */
471 void
472 puffs_userdead(struct puffs_mount *pmp)
473 {
474 struct puffs_park *park;
475 struct buf *bp;
476
477 /*
478 * Mark filesystem status as dying so that operations don't
479 * attempt to march to userspace any longer.
480 */
481 pmp->pmp_status = PUFFSTAT_DYING;
482
483 /* and wakeup processes waiting for a reply from userspace */
484 TAILQ_FOREACH(park, &pmp->pmp_req_replywait, park_entries) {
485 if (park->park_preq)
486 park->park_preq->preq_rv = ENXIO;
487 TAILQ_REMOVE(&pmp->pmp_req_replywait, park, park_entries);
488 if (park->park_flags & PUFFS_PARKFLAG_ASYNCBIOREAD) {
489 bp = park->park_bp;
490 bp->b_error = ENXIO;
491 bp->b_flags |= B_ERROR;
492 biodone(bp);
493 } else {
494 wakeup(park);
495 }
496 }
497
498 /* wakeup waiters for completion of vfs/vnode requests */
499 TAILQ_FOREACH(park, &pmp->pmp_req_touser, park_entries) {
500 if (park->park_preq)
501 park->park_preq->preq_rv = ENXIO;
502 TAILQ_REMOVE(&pmp->pmp_req_touser, park, park_entries);
503 if (park->park_flags & PUFFS_PARKFLAG_ASYNCBIOREAD) {
504 bp = park->park_bp;
505 bp->b_error = ENXIO;
506 bp->b_flags |= B_ERROR;
507 biodone(bp);
508 } else {
509 wakeup(park);
510 }
511 }
512 }
513
514 /*
515 * Converts a non-FAF op to a FAF. This simply involves making copies
516 * of the park and request structures and tagging the request as a FAF.
517 * It is safe to block here, since the original op is not a FAF.
518 */
519 struct puffs_park *
520 puffs_reqtofaf(struct puffs_park *ppark)
521 {
522 struct puffs_park *newpark;
523 struct puffs_req *newpreq;
524
525 KASSERT((ppark->park_preq->preq_opclass & PUFFSOPFLAG_FAF) == 0);
526
527 MALLOC(newpark, struct puffs_park *, sizeof(struct puffs_park),
528 M_PUFFS, M_ZERO | M_WAITOK);
529 MALLOC(newpreq, struct puffs_req *, sizeof(struct puffs_req),
530 M_PUFFS, M_ZERO | M_WAITOK);
531
532 memcpy(newpark, ppark, sizeof(struct puffs_park));
533 memcpy(newpreq, ppark->park_preq, sizeof(struct puffs_req));
534
535 newpark->park_preq = newpreq;
536 newpark->park_preq->preq_opclass |= PUFFSOPFLAG_FAF;
537
538 return newpark;
539 }
540