puffs_msgif.c revision 1.19.2.7 1 /* $NetBSD: puffs_msgif.c,v 1.19.2.7 2007/10/09 13:44:18 ad 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 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 *
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_msgif.c,v 1.19.2.7 2007/10/09 13:44:18 ad Exp $");
34
35 #include <sys/param.h>
36 #include <sys/fstrans.h>
37 #include <sys/malloc.h>
38 #include <sys/mount.h>
39 #include <sys/vnode.h>
40 #include <sys/lock.h>
41 #include <sys/proc.h>
42
43 #include <fs/puffs/puffs_msgif.h>
44 #include <fs/puffs/puffs_sys.h>
45
46 /*
47 * waitq data structures
48 */
49
50 /*
51 * While a request is going to userspace, park the caller within the
52 * kernel. This is the kernel counterpart of "struct puffs_req".
53 */
54 struct puffs_park {
55 struct puffs_req *park_preq; /* req followed by buf */
56 uint64_t park_id; /* duplicate of preq_id */
57
58 size_t park_copylen; /* userspace copylength */
59 size_t park_maxlen; /* max size in comeback */
60
61 parkdone_fn park_done;
62 void *park_donearg;
63
64 int park_flags;
65 int park_refcount;
66
67 kcondvar_t park_cv;
68 kmutex_t park_mtx;
69
70 TAILQ_ENTRY(puffs_park) park_entries;
71 };
72 #define PARKFLAG_WAITERGONE 0x01
73 #define PARKFLAG_DONE 0x02
74 #define PARKFLAG_ONQUEUE1 0x04
75 #define PARKFLAG_ONQUEUE2 0x08
76 #define PARKFLAG_CALL 0x10
77 #define PARKFLAG_WANTREPLY 0x20
78
79 static pool_cache_t parkpc;
80
81 static int
82 makepark(void *arg, void *obj, int flags)
83 {
84 struct puffs_park *park = obj;
85
86 mutex_init(&park->park_mtx, MUTEX_DEFAULT, IPL_NONE);
87 cv_init(&park->park_cv, "puffsrpl");
88
89 return 0;
90 }
91
92 static void
93 nukepark(void *arg, void *obj)
94 {
95 struct puffs_park *park = obj;
96
97 cv_destroy(&park->park_cv);
98 mutex_destroy(&park->park_mtx);
99 }
100
101 void
102 puffs_msgif_init()
103 {
104
105 parkpc = pool_cache_init(sizeof(struct puffs_park), 0, 0, 0,
106 "puffprkl", NULL, IPL_NONE, makepark, nukepark, NULL);
107 KASSERT(parkpc != NULL); /* XXX */
108 }
109
110 void
111 puffs_msgif_destroy()
112 {
113
114 pool_cache_destroy(parkpc);
115 }
116
117 void *
118 puffs_park_alloc(int waitok)
119 {
120 struct puffs_park *park;
121
122 park = pool_cache_get(parkpc, waitok ? PR_WAITOK : PR_NOWAIT);
123 if (park) {
124 park->park_refcount = 1;
125 mutex_enter(&park->park_mtx);
126 }
127
128 return park;
129 }
130
131 static void
132 puffs_park_reference(struct puffs_park *park)
133 {
134
135 mutex_enter(&park->park_mtx);
136 park->park_refcount++;
137 }
138
139 void
140 puffs_park_release(void *arg, int fullnuke)
141 {
142 struct puffs_park *park = arg;
143
144 KASSERT(mutex_owned(&park->park_mtx));
145 --park->park_refcount;
146
147 mutex_exit(&park->park_mtx);
148 if (park->park_refcount == 0 || fullnuke)
149 pool_cache_put(parkpc, park);
150 }
151
152 #ifdef PUFFSDEBUG
153 static void
154 parkdump(struct puffs_park *park)
155 {
156
157 DPRINTF(("park %p, preq %p, id %" PRIu64 "\n"
158 "\tcopy %zu, max %zu - done: %p/%p\n"
159 "\tflags 0x%08x, refcount %d, cv/mtx: %p/%p\n",
160 park, park->park_preq, park->park_id,
161 park->park_copylen, park->park_maxlen,
162 park->park_done, park->park_donearg,
163 park->park_flags, park->park_refcount,
164 &park->park_cv, &park->park_mtx));
165 }
166
167 static void
168 parkqdump(struct puffs_wq *q, int dumpall)
169 {
170 struct puffs_park *park;
171 int total = 0;
172
173 TAILQ_FOREACH(park, q, park_entries) {
174 if (dumpall)
175 parkdump(park);
176 total++;
177 }
178 DPRINTF(("puffs waitqueue at %p dumped, %d total\n", q, total));
179
180 }
181 #endif /* PUFFSDEBUG */
182
183 /*
184 * Converts a non-FAF op to a FAF. This simply involves making copies
185 * of the park and request structures and tagging the request as a FAF.
186 * It is safe to block here, since the original op is not a FAF.
187 */
188 static void
189 puffs_reqtofaf(struct puffs_park *park)
190 {
191 struct puffs_req *newpreq;
192
193 KASSERT((park->park_preq->preq_opclass & PUFFSOPFLAG_FAF) == 0);
194
195 MALLOC(newpreq, struct puffs_req *, park->park_copylen,
196 M_PUFFS, M_ZERO | M_WAITOK);
197
198 memcpy(newpreq, park->park_preq, park->park_copylen);
199
200 park->park_preq = newpreq;
201 park->park_preq->preq_opclass |= PUFFSOPFLAG_FAF;
202 park->park_flags &= ~PARKFLAG_WANTREPLY;
203 }
204
205
206 /*
207 * kernel-user-kernel waitqueues
208 */
209
210 static int touser(struct puffs_mount *, struct puffs_park *, uint64_t);
211
212 uint64_t
213 puffs_getreqid(struct puffs_mount *pmp)
214 {
215 uint64_t rv;
216
217 mutex_enter(&pmp->pmp_lock);
218 rv = pmp->pmp_nextreq++;
219 mutex_exit(&pmp->pmp_lock);
220
221 return rv;
222 }
223
224 /* vfs request */
225 int
226 puffs_vfstouser(struct puffs_mount *pmp, int optype, void *kbuf, size_t buflen)
227 {
228 struct puffs_park *park;
229
230 park = puffs_park_alloc(1);
231 park->park_preq = kbuf;
232
233 park->park_preq->preq_opclass = PUFFSOP_VFS;
234 park->park_preq->preq_optype = optype;
235
236 park->park_maxlen = park->park_copylen = buflen;
237 park->park_flags = 0;
238
239 return touser(pmp, park, puffs_getreqid(pmp));
240 }
241
242 void
243 puffs_suspendtouser(struct puffs_mount *pmp, int status)
244 {
245 struct puffs_vfsreq_suspend *pvfsr_susp;
246 struct puffs_park *park;
247
248 pvfsr_susp = malloc(sizeof(struct puffs_vfsreq_suspend),
249 M_PUFFS, M_WAITOK | M_ZERO);
250 park = puffs_park_alloc(1);
251
252 pvfsr_susp->pvfsr_status = status;
253 park->park_preq = (struct puffs_req *)pvfsr_susp;
254
255 park->park_preq->preq_opclass = PUFFSOP_VFS | PUFFSOPFLAG_FAF;
256 park->park_preq->preq_optype = PUFFS_VFS_SUSPEND;
257
258 park->park_maxlen = park->park_copylen
259 = sizeof(struct puffs_vfsreq_suspend);
260 park->park_flags = 0;
261
262 (void)touser(pmp, park, 0);
263 }
264
265 /*
266 * vnode level request
267 */
268 int
269 puffs_vntouser(struct puffs_mount *pmp, int optype,
270 void *kbuf, size_t buflen, size_t maxdelta,
271 struct vnode *vp_opc, struct vnode *vp_aux)
272 {
273 struct puffs_park *park;
274 struct puffs_req *preq;
275 void *cookie = VPTOPNC(vp_opc);
276 struct puffs_node *pnode;
277 int rv;
278
279 park = puffs_park_alloc(1);
280 park->park_preq = kbuf;
281
282 park->park_preq->preq_opclass = PUFFSOP_VN;
283 park->park_preq->preq_optype = optype;
284 park->park_preq->preq_cookie = cookie;
285
286 park->park_copylen = buflen;
287 park->park_maxlen = buflen + maxdelta;
288 park->park_flags = 0;
289
290 rv = touser(pmp, park, puffs_getreqid(pmp));
291
292 /*
293 * Check if the user server requests that inactive be called
294 * when the time is right.
295 */
296 preq = park->park_preq;
297 if (preq->preq_setbacks & PUFFS_SETBACK_INACT_N1) {
298 pnode = vp_opc->v_data;
299 pnode->pn_stat |= PNODE_DOINACT;
300 }
301 if (preq->preq_setbacks & PUFFS_SETBACK_INACT_N2) {
302 /* if no vp_aux, just ignore */
303 if (vp_aux) {
304 pnode = vp_aux->v_data;
305 pnode->pn_stat |= PNODE_DOINACT;
306 }
307 }
308 if (preq->preq_setbacks & PUFFS_SETBACK_NOREF_N1) {
309 pnode = vp_opc->v_data;
310 pnode->pn_stat |= PNODE_NOREFS;
311 }
312 if (preq->preq_setbacks & PUFFS_SETBACK_NOREF_N2) {
313 /* if no vp_aux, just ignore */
314 if (vp_aux) {
315 pnode = vp_aux->v_data;
316 pnode->pn_stat |= PNODE_NOREFS;
317 }
318 }
319
320 return rv;
321 }
322
323 int
324 puffs_cookietouser(struct puffs_mount *pmp, int optype,
325 void *kbuf, size_t buflen, void *cookie, int faf)
326 {
327 struct puffs_park *park;
328
329 park = puffs_park_alloc(1);
330 park->park_preq = kbuf;
331
332 park->park_preq->preq_opclass = PUFFSOP_VN |(faf ? PUFFSOPFLAG_FAF : 0);
333 park->park_preq->preq_optype = optype;
334 park->park_preq->preq_cookie = cookie;
335
336 park->park_copylen = park->park_maxlen = buflen;
337 park->park_flags = 0;
338
339 return touser(pmp, park, puffs_getreqid(pmp));
340 }
341
342 /*
343 * vnode level request, caller-controller req id
344 */
345 int
346 puffs_vntouser_req(struct puffs_mount *pmp, int optype,
347 void *kbuf, size_t buflen, size_t maxdelta,
348 uint64_t reqid, struct vnode *vp_opc, struct vnode *vp_aux)
349 {
350 struct puffs_park *park;
351 void *cookie = VPTOPNC(vp_opc);
352
353 park = puffs_park_alloc(1);
354 park->park_preq = kbuf;
355
356 park->park_preq->preq_opclass = PUFFSOP_VN;
357 park->park_preq->preq_optype = optype;
358 park->park_preq->preq_cookie = cookie;
359
360 park->park_copylen = buflen;
361 park->park_maxlen = buflen + maxdelta;
362 park->park_flags = 0;
363
364 return touser(pmp, park, reqid);
365 }
366
367 void
368 puffs_vntouser_call(struct puffs_mount *pmp, int optype,
369 void *kbuf, size_t buflen, size_t maxdelta,
370 parkdone_fn donefn, void *donearg,
371 struct vnode *vp_opc, struct vnode *vp_aux)
372 {
373 struct puffs_park *park;
374 void *cookie = VPTOPNC(vp_opc);
375
376 park = puffs_park_alloc(1);
377 park->park_preq = kbuf;
378
379 park->park_preq->preq_opclass = PUFFSOP_VN;
380 park->park_preq->preq_optype = optype;
381 park->park_preq->preq_cookie = cookie;
382
383 park->park_copylen = buflen;
384 park->park_maxlen = buflen + maxdelta;
385 park->park_done = donefn;
386 park->park_donearg = donearg;
387 park->park_flags = PARKFLAG_CALL;
388
389 (void) touser(pmp, park, puffs_getreqid(pmp));
390 }
391
392 /*
393 * Notice: kbuf will be free'd later. I must be allocated from the
394 * kernel heap and it's ownership is shifted to this function from
395 * now on, i.e. the caller is not allowed to use it anymore!
396 */
397 void
398 puffs_vntouser_faf(struct puffs_mount *pmp, int optype,
399 void *kbuf, size_t buflen, struct vnode *vp_opc)
400 {
401 struct puffs_park *park;
402 void *cookie = VPTOPNC(vp_opc);
403
404 /* XXX: is it allowable to sleep here? */
405 park = puffs_park_alloc(0);
406 if (park == NULL)
407 return; /* 2bad */
408
409 park->park_preq = kbuf;
410
411 park->park_preq->preq_opclass = PUFFSOP_VN | PUFFSOPFLAG_FAF;
412 park->park_preq->preq_optype = optype;
413 park->park_preq->preq_cookie = cookie;
414
415 park->park_maxlen = park->park_copylen = buflen;
416 park->park_flags = 0;
417
418 (void)touser(pmp, park, 0);
419 }
420
421 void
422 puffs_cacheop(struct puffs_mount *pmp, struct puffs_park *park,
423 struct puffs_cacheinfo *pcinfo, size_t pcilen, void *cookie)
424 {
425
426 park->park_preq = (struct puffs_req *)pcinfo;
427 park->park_preq->preq_opclass = PUFFSOP_CACHE | PUFFSOPFLAG_FAF;
428 park->park_preq->preq_optype = PCACHE_TYPE_WRITE; /* XXX */
429 park->park_preq->preq_cookie = cookie;
430
431 park->park_maxlen = park->park_copylen = pcilen;
432 park->park_flags = 0;
433
434 (void)touser(pmp, park, 0);
435 }
436
437 void
438 puffs_errnotify(struct puffs_mount *pmp, uint8_t type, int error,
439 const char *str, void *cookie)
440 {
441 struct puffs_park *park;
442 struct puffs_error *perr;
443
444 park = puffs_park_alloc(1);
445 MALLOC(perr, struct puffs_error *, sizeof(struct puffs_error),
446 M_PUFFS, M_ZERO | M_WAITOK);
447
448 perr->perr_error = error;
449 strlcpy(perr->perr_str, str, sizeof(perr->perr_str));
450
451 park->park_preq = (struct puffs_req *)perr;
452 park->park_preq->preq_opclass = PUFFSOP_ERROR | PUFFSOPFLAG_FAF;
453 park->park_preq->preq_optype = type;
454 park->park_preq->preq_cookie = cookie;
455
456 park->park_maxlen = park->park_copylen = sizeof(struct puffs_error);
457 park->park_flags = 0;
458
459 (void)touser(pmp, park, 0);
460 }
461
462 /*
463 * Wait for the userspace ping-pong game in calling process context.
464 *
465 * This unlocks vnodes if they are supplied. vp1 is the vnode
466 * before in the locking order, i.e. the one which must be locked
467 * before accessing vp2. This is done here so that operations are
468 * already ordered in the queue when vnodes are unlocked (I'm not
469 * sure if that's really necessary, but it can't hurt). Okok, maybe
470 * there's a slight ugly-factor also, but let's not worry about that.
471 */
472 static int
473 touser(struct puffs_mount *pmp, struct puffs_park *park, uint64_t reqid)
474 {
475 struct lwp *l = curlwp;
476 struct mount *mp;
477 struct puffs_req *preq;
478 int rv = 0;
479
480 mp = PMPTOMP(pmp);
481 preq = park->park_preq;
482 preq->preq_id = park->park_id = reqid;
483 preq->preq_buflen = ALIGN(park->park_maxlen);
484
485 if (PUFFSOP_WANTREPLY(preq->preq_opclass))
486 park->park_flags |= PARKFLAG_WANTREPLY;
487
488 /*
489 * To support PCATCH, yet another movie: check if there are signals
490 * pending and we are issueing a non-FAF. If so, return an error
491 * directly UNLESS we are issueing INACTIVE. In that case, convert
492 * it to a FAF, fire off to the file server and return an error.
493 * Yes, this is bordering disgusting. Barfbags are on me.
494 */
495 if ((park->park_flags & PARKFLAG_WANTREPLY)
496 && (park->park_flags & PARKFLAG_CALL) == 0
497 && (l->l_flag & LW_PENDSIG) != 0 && sigispending(l, 0)) {
498 if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN
499 && preq->preq_optype == PUFFS_VN_INACTIVE) {
500 puffs_reqtofaf(park);
501 DPRINTF(("puffs touser: converted to FAF %p\n", park));
502 rv = EINTR;
503 } else {
504 puffs_park_release(park, 0);
505 return EINTR;
506 }
507 }
508
509 /*
510 * test for suspension lock.
511 *
512 * Note that we *DO NOT* keep the lock, since that might block
513 * lock acquiring PLUS it would give userlandia control over
514 * the lock. The operation queue enforces a strict ordering:
515 * when the fs server gets in the op stream, it knows things
516 * are in order. The kernel locks can't guarantee that for
517 * userspace, in any case.
518 *
519 * BUT: this presents a problem for ops which have a consistency
520 * clause based on more than one operation. Unfortunately such
521 * operations (read, write) do not reliably work yet.
522 *
523 * Ya, Ya, it's wrong wong wrong, me be fixink this someday.
524 *
525 * XXX: and there is one more problem. We sometimes need to
526 * take a lazy lock in case the fs is suspending and we are
527 * executing as the fs server context. This might happen
528 * e.g. in the case that the user server triggers a reclaim
529 * in the kernel while the fs is suspending. It's not a very
530 * likely event, but it needs to be fixed some day.
531 */
532
533 /*
534 * MOREXXX: once PUFFS_WCACHEINFO is enabled, we can't take
535 * the mutex here, since getpages() might be called locked.
536 */
537 fstrans_start(mp, FSTRANS_NORMAL);
538 mutex_enter(&pmp->pmp_lock);
539 fstrans_done(mp);
540
541 if (pmp->pmp_status != PUFFSTAT_RUNNING) {
542 mutex_exit(&pmp->pmp_lock);
543 puffs_park_release(park, 0);
544 return ENXIO;
545 }
546
547 #ifdef PUFFSDEBUG
548 parkqdump(&pmp->pmp_req_touser, puffsdebug > 1);
549 parkqdump(&pmp->pmp_req_replywait, puffsdebug > 1);
550 #endif
551
552 TAILQ_INSERT_TAIL(&pmp->pmp_req_touser, park, park_entries);
553 park->park_flags |= PARKFLAG_ONQUEUE1;
554 puffs_mp_reference(pmp);
555 pmp->pmp_req_touser_count++;
556 mutex_exit(&pmp->pmp_lock);
557
558 DPRINTF(("touser: req %" PRIu64 ", preq: %p, park: %p, "
559 "c/t: 0x%x/0x%x, f: 0x%x\n", preq->preq_id, preq, park,
560 preq->preq_opclass, preq->preq_optype, park->park_flags));
561
562 cv_broadcast(&pmp->pmp_req_waiter_cv);
563 selnotify(pmp->pmp_sel, 0);
564
565 if ((park->park_flags & PARKFLAG_WANTREPLY)
566 && (park->park_flags & PARKFLAG_CALL) == 0) {
567 int error;
568
569 error = cv_wait_sig(&park->park_cv, &park->park_mtx);
570 if (error) {
571 park->park_flags |= PARKFLAG_WAITERGONE;
572 if (park->park_flags & PARKFLAG_DONE) {
573 rv = preq->preq_rv;
574 puffs_park_release(park, 0);
575 } else {
576 /*
577 * ok, we marked it as going away, but
578 * still need to do queue ops. take locks
579 * in correct order.
580 *
581 * We don't want to release our reference
582 * if it's on replywait queue to avoid error
583 * to file server. putop() code will DTRT.
584 */
585 KASSERT(park->park_flags &
586 (PARKFLAG_ONQUEUE1 | PARKFLAG_ONQUEUE2));
587 mutex_exit(&park->park_mtx);
588
589 mutex_enter(&pmp->pmp_lock);
590 mutex_enter(&park->park_mtx);
591 if (park->park_flags & PARKFLAG_ONQUEUE1) {
592 TAILQ_REMOVE(&pmp->pmp_req_touser,
593 park, park_entries);
594 pmp->pmp_req_touser_count--;
595 park->park_flags &= ~PARKFLAG_ONQUEUE1;
596 }
597 if ((park->park_flags & PARKFLAG_ONQUEUE2) == 0)
598 puffs_park_release(park, 0);
599 else
600 mutex_exit(&park->park_mtx);
601 mutex_exit(&pmp->pmp_lock);
602
603 rv = error;
604 }
605 } else {
606 rv = preq->preq_rv;
607 puffs_park_release(park, 0);
608 }
609
610 /*
611 * retake the lock and release. This makes sure (haha,
612 * I'm humorous) that we don't process the same vnode in
613 * multiple threads due to the locks hacks we have in
614 * puffs_lock(). In reality this is well protected by
615 * the biglock, but once that's gone, well, hopefully
616 * this will be fixed for real. (and when you read this
617 * comment in 2017 and subsequently barf, my condolences ;).
618 */
619 if (rv == 0 && !fstrans_is_owner(mp)) {
620 fstrans_start(mp, FSTRANS_NORMAL);
621 fstrans_done(mp);
622 }
623 } else {
624 mutex_exit(&park->park_mtx);
625 }
626
627 mutex_enter(&pmp->pmp_lock);
628 puffs_mp_release(pmp);
629 mutex_exit(&pmp->pmp_lock);
630
631 return rv;
632 }
633
634
635 /*
636 * getop: scan through queued requests until:
637 * 1) max number of requests satisfied
638 * OR
639 * 2) buffer runs out of space
640 * OR
641 * 3) nonblocking is set AND there are no operations available
642 * OR
643 * 4) at least one operation was transferred AND there are no more waiting
644 */
645 int
646 puffs_getop(struct puffs_mount *pmp, struct puffs_reqh_get *phg, int nonblock)
647 {
648 struct puffs_park *park;
649 struct puffs_req *preq;
650 uint8_t *bufpos;
651 int error, donesome;
652
653 donesome = error = 0;
654 bufpos = phg->phg_buf;
655
656 mutex_enter(&pmp->pmp_lock);
657 while (phg->phg_nops == 0 || donesome != phg->phg_nops) {
658 again:
659 if (pmp->pmp_status != PUFFSTAT_RUNNING) {
660 /* if we got some, they don't really matter anymore */
661 error = ENXIO;
662 goto out;
663 }
664 if (TAILQ_EMPTY(&pmp->pmp_req_touser)) {
665 if (donesome)
666 goto out;
667
668 if (nonblock) {
669 error = EWOULDBLOCK;
670 goto out;
671 }
672
673 error = cv_wait_sig(&pmp->pmp_req_waiter_cv,
674 &pmp->pmp_lock);
675 if (error)
676 goto out;
677 else
678 goto again;
679 }
680
681 park = TAILQ_FIRST(&pmp->pmp_req_touser);
682 puffs_park_reference(park);
683
684 /* If it's a goner, don't process any furher */
685 if (park->park_flags & PARKFLAG_WAITERGONE) {
686 puffs_park_release(park, 0);
687 continue;
688 }
689
690 preq = park->park_preq;
691 if (phg->phg_buflen < preq->preq_buflen) {
692 if (!donesome)
693 error = E2BIG;
694 puffs_park_release(park, 0);
695 goto out;
696 }
697
698 TAILQ_REMOVE(&pmp->pmp_req_touser, park, park_entries);
699 KASSERT(park->park_flags & PARKFLAG_ONQUEUE1);
700 park->park_flags &= ~PARKFLAG_ONQUEUE1;
701 pmp->pmp_req_touser_count--;
702 KASSERT(pmp->pmp_req_touser_count >= 0);
703 mutex_exit(&pmp->pmp_lock);
704
705 DPRINTF(("puffsgetop: get op %" PRIu64 " (%d.), from %p "
706 "len %zu (buflen %zu), target %p\n", preq->preq_id,
707 donesome, preq, park->park_copylen, preq->preq_buflen,
708 bufpos));
709
710 if ((error = copyout(preq, bufpos, park->park_copylen)) != 0) {
711 DPRINTF(("puffs_getop: copyout failed: %d\n", error));
712 /*
713 * ok, user server is probably trying to cheat.
714 * stuff op back & return error to user. We need
715 * to take locks in the correct order.
716 */
717 mutex_exit(&park->park_mtx);
718
719 /*
720 * XXX: ONQUEUE1 | ONQUEUE2 invariant doesn't
721 * hold here
722 */
723
724 mutex_enter(&pmp->pmp_lock);
725 mutex_enter(&park->park_mtx);
726 if ((park->park_flags & PARKFLAG_WAITERGONE) == 0) {
727 TAILQ_INSERT_HEAD(&pmp->pmp_req_touser, park,
728 park_entries);
729 park->park_flags |= PARKFLAG_ONQUEUE1;
730 pmp->pmp_req_touser_count++;
731 }
732
733 if (donesome)
734 error = 0;
735 puffs_park_release(park, 0);
736 goto out;
737 }
738 bufpos += preq->preq_buflen;
739 phg->phg_buflen -= preq->preq_buflen;
740 donesome++;
741
742 /* XXXfixme: taking this lock in the wrong order */
743 mutex_enter(&pmp->pmp_lock);
744
745 if (park->park_flags & PARKFLAG_WANTREPLY) {
746 TAILQ_INSERT_TAIL(&pmp->pmp_req_replywait, park,
747 park_entries);
748 park->park_flags |= PARKFLAG_ONQUEUE2;
749 puffs_park_release(park, 0);
750 } else {
751 free(preq, M_PUFFS);
752 puffs_park_release(park, 1);
753 }
754 }
755
756 out:
757 phg->phg_more = pmp->pmp_req_touser_count;
758 mutex_exit(&pmp->pmp_lock);
759
760 phg->phg_nops = donesome;
761
762 return error;
763 }
764
765 int
766 puffs_putop(struct puffs_mount *pmp, struct puffs_reqh_put *php)
767 {
768 struct puffs_park *park;
769 struct puffs_req tmpreq;
770 struct puffs_req *nextpreq;
771 void *userbuf;
772 uint64_t id;
773 size_t reqlen;
774 int donesome, error, wgone, release;
775
776 donesome = error = wgone = 0;
777
778 id = php->php_id;
779 userbuf = php->php_buf;
780 reqlen = php->php_buflen;
781
782 mutex_enter(&pmp->pmp_lock);
783 while (donesome != php->php_nops) {
784 release = 0;
785 #ifdef PUFFSDEBUG
786 DPRINTF(("puffsputop: searching for %" PRIu64 ", ubuf: %p, "
787 "len %zu\n", id, userbuf, reqlen));
788 #endif
789 TAILQ_FOREACH(park, &pmp->pmp_req_replywait, park_entries) {
790 if (park->park_id == id)
791 break;
792 }
793
794 if (park == NULL) {
795 DPRINTF(("puffsputop: no request: %" PRIu64 "\n", id));
796 error = EINVAL;
797 break;
798 }
799
800 puffs_park_reference(park);
801 if (reqlen == 0 || reqlen > park->park_maxlen) {
802 DPRINTF(("puffsputop: invalid buffer length: "
803 "%zu\n", reqlen));
804 error = E2BIG;
805 puffs_park_release(park, 0);
806 break;
807 }
808 wgone = park->park_flags & PARKFLAG_WAITERGONE;
809
810 /* check if it's still on the queue after acquiring lock */
811 if (park->park_flags & PARKFLAG_ONQUEUE2) {
812 TAILQ_REMOVE(&pmp->pmp_req_replywait, park,
813 park_entries);
814 park->park_flags &= ~PARKFLAG_ONQUEUE2;
815 }
816
817 mutex_exit(&pmp->pmp_lock);
818
819 /*
820 * If the caller has gone south, go to next, collect
821 * $200 and free the structure there instead of wakeup.
822 * We also need to copyin the header info. Flag structure
823 * release to mode total and utter destruction.
824 */
825 if (wgone) {
826 DPRINTF(("puffs_putop: bad service - waiter gone for "
827 "park %p\n", park));
828 error = copyin(userbuf, &tmpreq,
829 sizeof(struct puffs_req));
830 release = 1;
831 if (error)
832 goto loopout;
833 nextpreq = &tmpreq;
834 goto next;
835 }
836
837 DPRINTF(("puffsputpop: copyin from %p to %p, len %zu\n",
838 userbuf, park->park_preq, reqlen));
839 error = copyin(userbuf, park->park_preq, reqlen);
840 if (error)
841 goto loopout;
842 nextpreq = park->park_preq;
843
844 next:
845 /* all's well, prepare for next op */
846 id = nextpreq->preq_id;
847 reqlen = nextpreq->preq_buflen;
848 userbuf = nextpreq->preq_nextbuf;
849 donesome++;
850
851 loopout:
852 if (error && !wgone)
853 park->park_preq->preq_rv = error;
854
855 if (park->park_flags & PARKFLAG_CALL) {
856 DPRINTF(("puffsputopt: call for %p, arg %p\n",
857 park->park_preq, park->park_donearg));
858 park->park_done(pmp,park->park_preq,park->park_donearg);
859 release = 1;
860 }
861
862 if (!wgone) {
863 DPRINTF(("puffs_putop: flagging done for "
864 "park %p\n", park));
865
866 cv_signal(&park->park_cv);
867 }
868 park->park_flags |= PARKFLAG_DONE;
869 puffs_park_release(park, release);
870
871 mutex_enter(&pmp->pmp_lock);
872 if (error)
873 break;
874 wgone = 0;
875 }
876
877 mutex_exit(&pmp->pmp_lock);
878 php->php_nops -= donesome;
879
880 return error;
881 }
882
883 /*
884 * We're dead, kaput, RIP, slightly more than merely pining for the
885 * fjords, belly-up, fallen, lifeless, finished, expired, gone to meet
886 * our maker, ceased to be, etcetc. YASD. It's a dead FS!
887 *
888 * Caller must hold puffs mutex.
889 */
890 void
891 puffs_userdead(struct puffs_mount *pmp)
892 {
893 struct puffs_park *park, *park_next;
894
895 /*
896 * Mark filesystem status as dying so that operations don't
897 * attempt to march to userspace any longer.
898 */
899 pmp->pmp_status = PUFFSTAT_DYING;
900
901 /* signal waiters on REQUEST TO file server queue */
902 for (park = TAILQ_FIRST(&pmp->pmp_req_touser); park; park = park_next) {
903 uint8_t opclass;
904
905 puffs_park_reference(park);
906 park_next = TAILQ_NEXT(park, park_entries);
907
908 KASSERT(park->park_flags & PARKFLAG_ONQUEUE1);
909 TAILQ_REMOVE(&pmp->pmp_req_touser, park, park_entries);
910 park->park_flags &= ~PARKFLAG_ONQUEUE1;
911 pmp->pmp_req_touser_count--;
912
913 /*
914 * If the waiter is gone, we may *NOT* access preq anymore.
915 */
916 if (park->park_flags & PARKFLAG_WAITERGONE) {
917 KASSERT((park->park_flags & PARKFLAG_CALL) == 0);
918 KASSERT(park->park_flags & PARKFLAG_WANTREPLY);
919 puffs_park_release(park, 0);
920 } else {
921 opclass = park->park_preq->preq_opclass;
922 park->park_preq->preq_rv = ENXIO;
923
924 if (park->park_flags & PARKFLAG_CALL) {
925 park->park_done(pmp, park->park_preq,
926 park->park_donearg);
927 puffs_park_release(park, 1);
928 } else if ((park->park_flags & PARKFLAG_WANTREPLY)==0) {
929 free(park->park_preq, M_PUFFS);
930 puffs_park_release(park, 1);
931 } else {
932 park->park_preq->preq_rv = ENXIO;
933 cv_signal(&park->park_cv);
934 puffs_park_release(park, 0);
935 }
936 }
937 }
938
939 /* signal waiters on RESPONSE FROM file server queue */
940 for (park=TAILQ_FIRST(&pmp->pmp_req_replywait); park; park=park_next) {
941 puffs_park_reference(park);
942 park_next = TAILQ_NEXT(park, park_entries);
943
944 KASSERT(park->park_flags & PARKFLAG_ONQUEUE2);
945 KASSERT(park->park_flags & PARKFLAG_WANTREPLY);
946
947 TAILQ_REMOVE(&pmp->pmp_req_replywait, park, park_entries);
948 park->park_flags &= ~PARKFLAG_ONQUEUE2;
949
950 /*
951 * If the waiter is gone, we may *NOT* access preq anymore.
952 */
953 if (park->park_flags & PARKFLAG_WAITERGONE) {
954 KASSERT((park->park_flags & PARKFLAG_CALL) == 0);
955 puffs_park_release(park, 0);
956 } else {
957 park->park_preq->preq_rv = ENXIO;
958 if (park->park_flags & PARKFLAG_CALL) {
959 park->park_done(pmp, park->park_preq,
960 park->park_donearg);
961 puffs_park_release(park, 1);
962 } else {
963 cv_signal(&park->park_cv);
964 puffs_park_release(park, 0);
965 }
966 }
967 }
968 }
969