Home | History | Annotate | Line # | Download | only in puffs
puffs_msgif.c revision 1.51
      1 /*	$NetBSD: puffs_msgif.c,v 1.51 2007/11/04 17:32:34 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 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.51 2007/11/04 17:32:34 pooka Exp $");
     34 
     35 #include <sys/param.h>
     36 #include <sys/fstrans.h>
     37 #include <sys/kmem.h>
     38 #include <sys/malloc.h>
     39 #include <sys/mount.h>
     40 #include <sys/vnode.h>
     41 #include <sys/lock.h>
     42 #include <sys/proc.h>
     43 
     44 #include <fs/puffs/puffs_msgif.h>
     45 #include <fs/puffs/puffs_sys.h>
     46 
     47 /*
     48  * waitq data structures
     49  */
     50 
     51 /*
     52  * While a request is going to userspace, park the caller within the
     53  * kernel.  This is the kernel counterpart of "struct puffs_req".
     54  */
     55 struct puffs_msgpark {
     56 	struct puffs_req	*park_preq;	/* req followed by buf	*/
     57 
     58 	size_t			park_copylen;	/* userspace copylength	*/
     59 	size_t			park_maxlen;	/* max size in comeback */
     60 
     61 	parkdone_fn		park_done;	/* "biodone" a'la puffs	*/
     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_msgpark) 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 struct pool_cache parkpc;
     80 static struct pool parkpool;
     81 
     82 static int
     83 makepark(void *arg, void *obj, int flags)
     84 {
     85 	struct puffs_msgpark *park = obj;
     86 
     87 	mutex_init(&park->park_mtx, MUTEX_DEFAULT, IPL_NONE);
     88 	cv_init(&park->park_cv, "puffsrpl");
     89 
     90 	return 0;
     91 }
     92 
     93 static void
     94 nukepark(void *arg, void *obj)
     95 {
     96 	struct puffs_msgpark *park = obj;
     97 
     98 	cv_destroy(&park->park_cv);
     99 	mutex_destroy(&park->park_mtx);
    100 }
    101 
    102 void
    103 puffs_msgif_init()
    104 {
    105 
    106 	pool_init(&parkpool, sizeof(struct puffs_msgpark), 0, 0, 0,
    107 	    "puffprkl", &pool_allocator_nointr, IPL_NONE);
    108 	pool_cache_init(&parkpc, &parkpool, makepark, nukepark, NULL);
    109 }
    110 
    111 void
    112 puffs_msgif_destroy()
    113 {
    114 
    115 	pool_cache_destroy(&parkpc);
    116 	pool_destroy(&parkpool);
    117 }
    118 
    119 static int alloced;
    120 
    121 static struct puffs_msgpark *
    122 puffs_msgpark_alloc(int waitok)
    123 {
    124 	struct puffs_msgpark *park;
    125 
    126 	park = pool_cache_get(&parkpc, waitok ? PR_WAITOK : PR_NOWAIT);
    127 	if (park == NULL)
    128 		return park;
    129 
    130 	park->park_refcount = 1;
    131 	park->park_preq = NULL;
    132 	park->park_flags = PARKFLAG_WANTREPLY;
    133 
    134 	return park;
    135 }
    136 
    137 static void
    138 puffs_msgpark_reference(struct puffs_msgpark *park)
    139 {
    140 
    141 	KASSERT(mutex_owned(&park->park_mtx));
    142 	park->park_refcount++;
    143 }
    144 
    145 /*
    146  * Release reference to park structure.
    147  */
    148 static void
    149 puffs_msgpark_release1(struct puffs_msgpark *park, int howmany)
    150 {
    151 	struct puffs_req *preq = park->park_preq;
    152 	int refcnt;
    153 
    154 	KASSERT(mutex_owned(&park->park_mtx));
    155 	refcnt = park->park_refcount -= howmany;
    156 	mutex_exit(&park->park_mtx);
    157 
    158 	KASSERT(refcnt >= 0);
    159 
    160 	if (refcnt == 0) {
    161 		alloced--;
    162 		if (preq)
    163 			kmem_free(preq, park->park_maxlen);
    164 		pool_cache_put(&parkpc, park);
    165 	}
    166 }
    167 #define puffs_msgpark_release(a) puffs_msgpark_release1(a, 1)
    168 
    169 #ifdef PUFFSDEBUG
    170 static void
    171 parkdump(struct puffs_msgpark *park)
    172 {
    173 
    174 	DPRINTF(("park %p, preq %p, id %" PRIu64 "\n"
    175 	    "\tcopy %zu, max %zu - done: %p/%p\n"
    176 	    "\tflags 0x%08x, refcount %d, cv/mtx: %p/%p\n",
    177 	    park, park->park_preq, park->park_preq->preq_id,
    178 	    park->park_copylen, park->park_maxlen,
    179 	    park->park_done, park->park_donearg,
    180 	    park->park_flags, park->park_refcount,
    181 	    &park->park_cv, &park->park_mtx));
    182 }
    183 
    184 static void
    185 parkqdump(struct puffs_wq *q, int dumpall)
    186 {
    187 	struct puffs_msgpark *park;
    188 	int total = 0;
    189 
    190 	TAILQ_FOREACH(park, q, park_entries) {
    191 		if (dumpall)
    192 			parkdump(park);
    193 		total++;
    194 	}
    195 	DPRINTF(("puffs waitqueue at %p dumped, %d total\n", q, total));
    196 
    197 }
    198 #endif /* PUFFSDEBUG */
    199 
    200 /*
    201  * A word about locking in the park structures: the lock protects the
    202  * fields of the *park* structure (not preq) and acts as an interlock
    203  * in cv operations.  The lock is always internal to this module and
    204  * callers do not need to worry about it.
    205  */
    206 
    207 int
    208 puffs_msgmem_alloc(size_t len, struct puffs_msgpark **ppark, void **mem,
    209 	int cansleep)
    210 {
    211 	struct puffs_msgpark *park;
    212 	void *m;
    213 
    214 	m = kmem_zalloc(len, cansleep ? KM_SLEEP : KM_NOSLEEP);
    215 	if (m == NULL) {
    216 		KASSERT(cansleep == 0);
    217 		return ENOMEM;
    218 	}
    219 
    220 	park = puffs_msgpark_alloc(cansleep);
    221 	if (park == NULL) {
    222 		KASSERT(cansleep == 0);
    223 		kmem_free(m, len);
    224 		return ENOMEM;
    225 	}
    226 
    227 	park->park_preq = m;
    228 	park->park_maxlen = len;
    229 
    230 	*ppark = park;
    231 	*mem = m;
    232 
    233 	return 0;
    234 }
    235 
    236 void
    237 puffs_msgmem_release(struct puffs_msgpark *park)
    238 {
    239 
    240 	if (park == NULL)
    241 		return;
    242 
    243 	mutex_enter(&park->park_mtx);
    244 	puffs_msgpark_release(park);
    245 }
    246 
    247 void
    248 puffs_msg_setfaf(struct puffs_msgpark *park)
    249 {
    250 
    251 	park->park_flags &= ~PARKFLAG_WANTREPLY;
    252 }
    253 
    254 /*
    255  * kernel-user-kernel waitqueues
    256  */
    257 
    258 static int touser(struct puffs_mount *, struct puffs_msgpark *);
    259 
    260 static uint64_t
    261 puffs_getmsgid(struct puffs_mount *pmp)
    262 {
    263 	uint64_t rv;
    264 
    265 	mutex_enter(&pmp->pmp_lock);
    266 	rv = pmp->pmp_nextmsgid++;
    267 	mutex_exit(&pmp->pmp_lock);
    268 
    269 	return rv;
    270 }
    271 
    272 /* vfs request */
    273 int
    274 puffs_msg_vfs(struct puffs_mount *pmp, struct puffs_msgpark *park, int optype)
    275 {
    276 
    277 	park->park_preq->preq_opclass = PUFFSOP_VFS;
    278 	park->park_preq->preq_optype = optype;
    279 
    280 	park->park_copylen = park->park_maxlen;
    281 
    282 	return touser(pmp, park);
    283 }
    284 
    285 /*
    286  * vnode level request
    287  */
    288 int
    289 puffs_msg_vn(struct puffs_mount *pmp, struct puffs_msgpark *park,
    290 	int optype, size_t delta, struct vnode *vp_opc, struct vnode *vp_aux)
    291 {
    292 	struct puffs_req *preq;
    293 	void *cookie = VPTOPNC(vp_opc);
    294 	struct puffs_node *pnode;
    295 	int rv;
    296 
    297 	park->park_preq->preq_opclass = PUFFSOP_VN;
    298 	park->park_preq->preq_optype = optype;
    299 	park->park_preq->preq_cookie = cookie;
    300 
    301 	KASSERT(delta < park->park_maxlen); /* "<=" wouldn't make sense */
    302 	park->park_copylen = park->park_maxlen - delta;
    303 
    304 	rv = touser(pmp, park);
    305 
    306 	/*
    307 	 * Check if the user server requests that inactive be called
    308 	 * when the time is right.
    309 	 */
    310 	preq = park->park_preq;
    311 	if (preq->preq_setbacks & PUFFS_SETBACK_INACT_N1) {
    312 		pnode = vp_opc->v_data;
    313 		pnode->pn_stat |= PNODE_DOINACT;
    314 	}
    315 	if (preq->preq_setbacks & PUFFS_SETBACK_INACT_N2) {
    316 		/* if no vp_aux, just ignore */
    317 		if (vp_aux) {
    318 			pnode = vp_aux->v_data;
    319 			pnode->pn_stat |= PNODE_DOINACT;
    320 		}
    321 	}
    322 	if (preq->preq_setbacks & PUFFS_SETBACK_NOREF_N1) {
    323 		pnode = vp_opc->v_data;
    324 		pnode->pn_stat |= PNODE_NOREFS;
    325 	}
    326 	if (preq->preq_setbacks & PUFFS_SETBACK_NOREF_N2) {
    327 		/* if no vp_aux, just ignore */
    328 		if (vp_aux) {
    329 			pnode = vp_aux->v_data;
    330 			pnode->pn_stat |= PNODE_NOREFS;
    331 		}
    332 	}
    333 
    334 	return rv;
    335 }
    336 
    337 void
    338 puffs_msg_vncall(struct puffs_mount *pmp, struct puffs_msgpark *park,
    339 	int optype, size_t delta, parkdone_fn donefn, void *donearg,
    340 	struct vnode *vp_opc)
    341 {
    342 	void *cookie = VPTOPNC(vp_opc);
    343 
    344 	park->park_preq->preq_opclass = PUFFSOP_VN;
    345 	park->park_preq->preq_optype = optype;
    346 	park->park_preq->preq_cookie = cookie;
    347 
    348 	KASSERT(delta < park->park_maxlen);
    349 	park->park_copylen = park->park_maxlen - delta;
    350 	park->park_done = donefn;
    351 	park->park_donearg = donearg;
    352 	park->park_flags |= PARKFLAG_CALL;
    353 
    354 	(void) touser(pmp, park);
    355 }
    356 
    357 int
    358 puffs_msg_raw(struct puffs_mount *pmp, struct puffs_msgpark *park)
    359 {
    360 
    361 	park->park_copylen = park->park_maxlen;
    362 
    363 	return touser(pmp, park);
    364 }
    365 
    366 void
    367 puffs_msg_errnotify(struct puffs_mount *pmp, uint8_t type, int error,
    368 	const char *str, void *cookie)
    369 {
    370 	struct puffs_msgpark *park;
    371 	struct puffs_error *perr;
    372 
    373 	puffs_msgmem_alloc(sizeof(struct puffs_error), &park, (void **)&perr,1);
    374 
    375 	perr->perr_error = error;
    376 	strlcpy(perr->perr_str, str, sizeof(perr->perr_str));
    377 
    378 	park->park_preq->preq_opclass |= PUFFSOP_ERROR | PUFFSOPFLAG_FAF;
    379 	park->park_preq->preq_optype = type;
    380 	park->park_preq->preq_cookie = cookie;
    381 
    382 	park->park_copylen = park->park_maxlen;
    383 
    384 	(void)touser(pmp, park);
    385 }
    386 
    387 /*
    388  * Wait for the userspace ping-pong game in calling process context,
    389  * unless a FAF / async call, in which case just enqueues the request
    390  * and return immediately.
    391  */
    392 static int
    393 touser(struct puffs_mount *pmp, struct puffs_msgpark *park)
    394 {
    395 	struct lwp *l = curlwp;
    396 	struct mount *mp;
    397 	struct puffs_req *preq;
    398 	int rv = 0;
    399 
    400 	mp = PMPTOMP(pmp);
    401 	preq = park->park_preq;
    402 	preq->preq_buflen = park->park_maxlen;
    403 	KASSERT(preq->preq_id == 0);
    404 
    405 	if ((park->park_flags & PARKFLAG_WANTREPLY) == 0)
    406 		preq->preq_opclass |= PUFFSOPFLAG_FAF;
    407 	else
    408 		preq->preq_id = puffs_getmsgid(pmp);
    409 
    410 	/* fill in caller information */
    411 	preq->preq_pid = l->l_proc->p_pid;
    412 	preq->preq_lid = l->l_lid;
    413 
    414 	/*
    415 	 * To support cv_sig, yet another movie: check if there are signals
    416 	 * pending and we are issueing a non-FAF.  If so, return an error
    417 	 * directly UNLESS we are issueing INACTIVE.  In that case, convert
    418 	 * it to a FAF, fire off to the file server and return an error.
    419 	 * Yes, this is bordering disgusting.  Barfbags are on me.
    420 	 */
    421 	if ((park->park_flags & PARKFLAG_WANTREPLY)
    422 	   && (park->park_flags & PARKFLAG_CALL) == 0
    423 	   && (l->l_flag & LW_PENDSIG) != 0 && sigispending(l, 0)) {
    424 		if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN
    425 		    && preq->preq_optype == PUFFS_VN_INACTIVE) {
    426 			park->park_preq->preq_opclass |= PUFFSOPFLAG_FAF;
    427 			park->park_flags &= ~PARKFLAG_WANTREPLY;
    428 			DPRINTF(("puffs touser: converted to FAF %p\n", park));
    429 			rv = EINTR;
    430 		} else {
    431 			return EINTR;
    432 		}
    433 	}
    434 
    435 	/*
    436 	 * test for suspension lock.
    437 	 *
    438 	 * Note that we *DO NOT* keep the lock, since that might block
    439 	 * lock acquiring PLUS it would give userlandia control over
    440 	 * the lock.  The operation queue enforces a strict ordering:
    441 	 * when the fs server gets in the op stream, it knows things
    442 	 * are in order.  The kernel locks can't guarantee that for
    443 	 * userspace, in any case.
    444 	 *
    445 	 * BUT: this presents a problem for ops which have a consistency
    446 	 * clause based on more than one operation.  Unfortunately such
    447 	 * operations (read, write) do not reliably work yet.
    448 	 *
    449 	 * Ya, Ya, it's wrong wong wrong, me be fixink this someday.
    450 	 *
    451 	 * XXX: and there is one more problem.  We sometimes need to
    452 	 * take a lazy lock in case the fs is suspending and we are
    453 	 * executing as the fs server context.  This might happen
    454 	 * e.g. in the case that the user server triggers a reclaim
    455 	 * in the kernel while the fs is suspending.  It's not a very
    456 	 * likely event, but it needs to be fixed some day.
    457 	 */
    458 
    459 	/*
    460 	 * MOREXXX: once PUFFS_WCACHEINFO is enabled, we can't take
    461 	 * the mutex here, since getpages() might be called locked.
    462 	 */
    463 	fstrans_start(mp, FSTRANS_NORMAL);
    464 	mutex_enter(&pmp->pmp_lock);
    465 	fstrans_done(mp);
    466 
    467 	if (pmp->pmp_status != PUFFSTAT_RUNNING) {
    468 		mutex_exit(&pmp->pmp_lock);
    469 		return ENXIO;
    470 	}
    471 
    472 #ifdef PUFFSDEBUG
    473 	parkqdump(&pmp->pmp_msg_touser, puffsdebug > 1);
    474 	parkqdump(&pmp->pmp_msg_replywait, puffsdebug > 1);
    475 #endif
    476 
    477 	mutex_enter(&park->park_mtx);
    478 	TAILQ_INSERT_TAIL(&pmp->pmp_msg_touser, park, park_entries);
    479 	park->park_flags |= PARKFLAG_ONQUEUE1;
    480 	puffs_mp_reference(pmp);
    481 	pmp->pmp_msg_touser_count++;
    482 	mutex_exit(&pmp->pmp_lock);
    483 
    484 	DPRINTF(("touser: req %" PRIu64 ", preq: %p, park: %p, "
    485 	    "c/t: 0x%x/0x%x, f: 0x%x\n", preq->preq_id, preq, park,
    486 	    preq->preq_opclass, preq->preq_optype, park->park_flags));
    487 
    488 	cv_broadcast(&pmp->pmp_msg_waiter_cv);
    489 	selnotify(pmp->pmp_sel, 0);
    490 
    491 	if ((park->park_flags & PARKFLAG_WANTREPLY)
    492 	    && (park->park_flags & PARKFLAG_CALL) == 0) {
    493 		int error;
    494 
    495 		error = cv_wait_sig(&park->park_cv, &park->park_mtx);
    496 		DPRINTF(("puffs_touser: waiter for %p woke up with %d\n",
    497 		    park, error));
    498 		if (error) {
    499 			park->park_flags |= PARKFLAG_WAITERGONE;
    500 			if (park->park_flags & PARKFLAG_DONE) {
    501 				rv = preq->preq_rv;
    502 			} else {
    503 				/*
    504 				 * ok, we marked it as going away, but
    505 				 * still need to do queue ops.  take locks
    506 				 * in correct order.
    507 				 *
    508 				 * We don't want to release our reference
    509 				 * if it's on replywait queue to avoid error
    510 				 * to file server.  putop() code will DTRT.
    511 				 */
    512 				mutex_exit(&park->park_mtx);
    513 				mutex_enter(&pmp->pmp_lock);
    514 				mutex_enter(&park->park_mtx);
    515 
    516 				/* remove from queue1 */
    517 				if (park->park_flags & PARKFLAG_ONQUEUE1) {
    518 					TAILQ_REMOVE(&pmp->pmp_msg_touser,
    519 					    park, park_entries);
    520 					pmp->pmp_msg_touser_count--;
    521 					park->park_flags &= ~PARKFLAG_ONQUEUE1;
    522 				}
    523 
    524 				/*
    525 				 * If it's waiting for a response already,
    526 				 * boost reference count.  Park will get
    527 				 * nuked once the response arrives from
    528 				 * the file server.
    529 				 */
    530 				if (park->park_flags & PARKFLAG_ONQUEUE2)
    531 					puffs_msgpark_reference(park);
    532 
    533 				mutex_exit(&pmp->pmp_lock);
    534 
    535 				rv = error;
    536 			}
    537 		} else {
    538 			rv = preq->preq_rv;
    539 		}
    540 
    541 		/*
    542 		 * retake the lock and release.  This makes sure (haha,
    543 		 * I'm humorous) that we don't process the same vnode in
    544 		 * multiple threads due to the locks hacks we have in
    545 		 * puffs_lock().  In reality this is well protected by
    546 		 * the biglock, but once that's gone, well, hopefully
    547 		 * this will be fixed for real.  (and when you read this
    548 		 * comment in 2017 and subsequently barf, my condolences ;).
    549 		 */
    550 		if (rv == 0 && !fstrans_is_owner(mp)) {
    551 			fstrans_start(mp, FSTRANS_NORMAL);
    552 			fstrans_done(mp);
    553 		}
    554 
    555 	} else {
    556 		/*
    557 		 * Take extra reference for FAF, i.e. don't free us
    558 		 * immediately upon return to the caller, but rather
    559 		 * only when the message has been transported.
    560 		 */
    561 		puffs_msgpark_reference(park);
    562 	}
    563 
    564 	mutex_exit(&park->park_mtx);
    565 
    566 	mutex_enter(&pmp->pmp_lock);
    567 	puffs_mp_release(pmp);
    568 	mutex_exit(&pmp->pmp_lock);
    569 
    570 	return rv;
    571 }
    572 
    573 /*
    574  * Get next request in the outgoing queue.  "maxsize" controls the
    575  * size the caller can accommodate and "nonblock" signals if this
    576  * should block while waiting for input.  Handles all locking internally.
    577  */
    578 int
    579 puffs_msgif_getout(void *this, size_t maxsize, int nonblock,
    580 	uint8_t **data, size_t *dlen, void **parkptr)
    581 {
    582 	struct puffs_mount *pmp = this;
    583 	struct puffs_msgpark *park;
    584 	struct puffs_req *preq;
    585 	int error;
    586 
    587 	error = 0;
    588 	mutex_enter(&pmp->pmp_lock);
    589 	puffs_mp_reference(pmp);
    590 	for (;;) {
    591 		/* RIP? */
    592 		if (pmp->pmp_status != PUFFSTAT_RUNNING) {
    593 			error = ENXIO;
    594 			break;
    595 		}
    596 
    597 		/* need platinum yendorian express card? */
    598 		if (TAILQ_EMPTY(&pmp->pmp_msg_touser)) {
    599 			DPRINTF(("puffs_getout: no outgoing op, "));
    600 			if (nonblock) {
    601 				DPRINTF(("returning EWOULDBLOCK\n"));
    602 				error = EWOULDBLOCK;
    603 				break;
    604 			}
    605 			DPRINTF(("waiting ...\n"));
    606 
    607 			error = cv_wait_sig(&pmp->pmp_msg_waiter_cv,
    608 			    &pmp->pmp_lock);
    609 			if (error)
    610 				break;
    611 			else
    612 				continue;
    613 		}
    614 
    615 		park = TAILQ_FIRST(&pmp->pmp_msg_touser);
    616 		if (park == NULL)
    617 			continue;
    618 
    619 		mutex_enter(&park->park_mtx);
    620 		puffs_msgpark_reference(park);
    621 
    622 		DPRINTF(("puffs_getout: found park at %p, ", park));
    623 
    624 		/* If it's a goner, don't process any furher */
    625 		if (park->park_flags & PARKFLAG_WAITERGONE) {
    626 			DPRINTF(("waitergone!\n"));
    627 			puffs_msgpark_release(park);
    628 			continue;
    629 		}
    630 
    631 		/* check size */
    632 		preq = park->park_preq;
    633 		if (maxsize < preq->preq_frhdr.pfr_len) {
    634 			DPRINTF(("buffer too small\n"));
    635 			puffs_msgpark_release(park);
    636 			error = E2BIG;
    637 			break;
    638 		}
    639 
    640 		DPRINTF(("returning\n"));
    641 
    642 		/*
    643 		 * Ok, we found what we came for.  Release it from the
    644 		 * outgoing queue but do not unlock.  We will unlock
    645 		 * only after we "releaseout" it to avoid complications:
    646 		 * otherwise it is (theoretically) possible for userland
    647 		 * to race us into "put" before we have a change to put
    648 		 * this baby on the receiving queue.
    649 		 */
    650 		TAILQ_REMOVE(&pmp->pmp_msg_touser, park, park_entries);
    651 		KASSERT(park->park_flags & PARKFLAG_ONQUEUE1);
    652 		park->park_flags &= ~PARKFLAG_ONQUEUE1;
    653 		mutex_exit(&park->park_mtx);
    654 
    655 		pmp->pmp_msg_touser_count--;
    656 		KASSERT(pmp->pmp_msg_touser_count >= 0);
    657 
    658 		break;
    659 	}
    660 	puffs_mp_release(pmp);
    661 	mutex_exit(&pmp->pmp_lock);
    662 
    663 	if (error == 0) {
    664 		*data = (uint8_t *)preq;
    665 		preq->preq_frhdr.pfr_len = park->park_copylen;
    666 		preq->preq_frhdr.pfr_alloclen = park->park_maxlen;
    667 		preq->preq_frhdr.pfr_type = preq->preq_opclass; /* yay! */
    668 		*dlen = preq->preq_frhdr.pfr_len;
    669 		*parkptr = park;
    670 	}
    671 
    672 	return error;
    673 }
    674 
    675 /*
    676  * Release outgoing structure.  Now, depending on the success of the
    677  * outgoing send, it is either going onto the result waiting queue
    678  * or the death chamber.
    679  */
    680 void
    681 puffs_msgif_releaseout(void *this, void *parkptr, int status)
    682 {
    683 	struct puffs_mount *pmp = this;
    684 	struct puffs_msgpark *park = parkptr;
    685 
    686 	DPRINTF(("puffs_releaseout: returning park %p, errno %d: " ,
    687 	    park, status));
    688 	mutex_enter(&pmp->pmp_lock);
    689 	mutex_enter(&park->park_mtx);
    690 	if (park->park_flags & PARKFLAG_WANTREPLY) {
    691 		if (status == 0) {
    692 			DPRINTF(("enqueue replywait\n"));
    693 			TAILQ_INSERT_TAIL(&pmp->pmp_msg_replywait, park,
    694 			    park_entries);
    695 			park->park_flags |= PARKFLAG_ONQUEUE2;
    696 		} else {
    697 			DPRINTF(("error path!\n"));
    698 			park->park_preq->preq_rv = status;
    699 			park->park_flags |= PARKFLAG_DONE;
    700 			cv_signal(&park->park_cv);
    701 		}
    702 		puffs_msgpark_release(park);
    703 	} else {
    704 		DPRINTF(("release\n"));
    705 		puffs_msgpark_release1(park, 2);
    706 	}
    707 	mutex_exit(&pmp->pmp_lock);
    708 }
    709 
    710 /*
    711  * XXX: locking with this one?
    712  */
    713 void
    714 puffs_msgif_incoming(void *this, void *buf)
    715 {
    716 	struct puffs_mount *pmp = this;
    717 	struct puffs_req *preq = buf;
    718 	struct puffs_frame *pfr = &preq->preq_frhdr;
    719 	struct puffs_msgpark *park;
    720 	int release, wgone;
    721 
    722 	/* XXX */
    723 	if (PUFFSOP_OPCLASS(preq->preq_opclass) != PUFFSOP_VN
    724 	    && PUFFSOP_OPCLASS(preq->preq_opclass) != PUFFSOP_VFS)
    725 		return;
    726 
    727 	mutex_enter(&pmp->pmp_lock);
    728 
    729 	/* Locate waiter */
    730 	TAILQ_FOREACH(park, &pmp->pmp_msg_replywait, park_entries) {
    731 		if (park->park_preq->preq_id == preq->preq_id)
    732 			break;
    733 	}
    734 	if (park == NULL) {
    735 		DPRINTF(("puffs_msgif_income: no request: %" PRIu64 "\n",
    736 		    preq->preq_id));
    737 		mutex_exit(&pmp->pmp_lock);
    738 		return; /* XXX send error */
    739 	}
    740 
    741 	mutex_enter(&park->park_mtx);
    742 	puffs_msgpark_reference(park);
    743 	if (pfr->pfr_len > park->park_maxlen) {
    744 		DPRINTF(("puffs_msgif_income: invalid buffer length: "
    745 		    "%zu (req %" PRIu64 ", \n", pfr->pfr_len, preq->preq_id));
    746 		park->park_preq->preq_rv = EPROTO;
    747 		cv_signal(&park->park_cv);
    748 		puffs_msgpark_release(park);
    749 		mutex_exit(&pmp->pmp_lock);
    750 		return; /* XXX: error */
    751 	}
    752 	wgone = park->park_flags & PARKFLAG_WAITERGONE;
    753 
    754 	KASSERT(park->park_flags & PARKFLAG_ONQUEUE2);
    755 	TAILQ_REMOVE(&pmp->pmp_msg_replywait, park, park_entries);
    756 	park->park_flags &= ~PARKFLAG_ONQUEUE2;
    757 	mutex_exit(&pmp->pmp_lock);
    758 
    759 	if (wgone) {
    760 		DPRINTF(("puffs_putop: bad service - waiter gone for "
    761 		    "park %p\n", park));
    762 		release = 2;
    763 	} else {
    764 		if (park->park_flags & PARKFLAG_CALL) {
    765 			DPRINTF(("puffs_msgif_income: call for %p, arg %p\n",
    766 			    park->park_preq, park->park_donearg));
    767 			park->park_done(pmp, buf, park->park_donearg);
    768 			release = 2;
    769 		} else {
    770 			/* XXX: yes, I know */
    771 			memcpy(park->park_preq, buf, pfr->pfr_len);
    772 			release = 1;
    773 		}
    774 	}
    775 
    776 	if (!wgone) {
    777 		DPRINTF(("puffs_putop: flagging done for "
    778 		    "park %p\n", park));
    779 		cv_signal(&park->park_cv);
    780 	}
    781 
    782 	park->park_flags |= PARKFLAG_DONE;
    783 	puffs_msgpark_release1(park, release);
    784 }
    785 
    786 /*
    787  * We're dead, kaput, RIP, slightly more than merely pining for the
    788  * fjords, belly-up, fallen, lifeless, finished, expired, gone to meet
    789  * our maker, ceased to be, etcetc.  YASD.  It's a dead FS!
    790  *
    791  * Caller must hold puffs mutex.
    792  */
    793 void
    794 puffs_userdead(struct puffs_mount *pmp)
    795 {
    796 	struct puffs_msgpark *park, *park_next;
    797 
    798 	/*
    799 	 * Mark filesystem status as dying so that operations don't
    800 	 * attempt to march to userspace any longer.
    801 	 */
    802 	pmp->pmp_status = PUFFSTAT_DYING;
    803 
    804 	/* signal waiters on REQUEST TO file server queue */
    805 	for (park = TAILQ_FIRST(&pmp->pmp_msg_touser); park; park = park_next) {
    806 		uint8_t opclass;
    807 
    808 		mutex_enter(&park->park_mtx);
    809 		puffs_msgpark_reference(park);
    810 		park_next = TAILQ_NEXT(park, park_entries);
    811 
    812 		KASSERT(park->park_flags & PARKFLAG_ONQUEUE1);
    813 		TAILQ_REMOVE(&pmp->pmp_msg_touser, park, park_entries);
    814 		park->park_flags &= ~PARKFLAG_ONQUEUE1;
    815 		pmp->pmp_msg_touser_count--;
    816 
    817 		/*
    818 		 * Even though waiters on QUEUE1 are removed in touser()
    819 		 * in case of WAITERGONE, it is still possible for us to
    820 		 * get raced here due to having to retake locks in said
    821 		 * touser().  In the race case simply "ignore" the item
    822 		 * on the queue and move on to the next one.
    823 		 */
    824 		if (park->park_flags & PARKFLAG_WAITERGONE) {
    825 			KASSERT((park->park_flags & PARKFLAG_CALL) == 0);
    826 			KASSERT(park->park_flags & PARKFLAG_WANTREPLY);
    827 			puffs_msgpark_release(park);
    828 
    829 		} else {
    830 			opclass = park->park_preq->preq_opclass;
    831 			park->park_preq->preq_rv = ENXIO;
    832 
    833 			if (park->park_flags & PARKFLAG_CALL) {
    834 				park->park_done(pmp, park->park_preq,
    835 				    park->park_donearg);
    836 				puffs_msgpark_release1(park, 2);
    837 			} else if ((park->park_flags & PARKFLAG_WANTREPLY)==0) {
    838 				puffs_msgpark_release1(park, 2);
    839 			} else {
    840 				park->park_preq->preq_rv = ENXIO;
    841 				cv_signal(&park->park_cv);
    842 				puffs_msgpark_release(park);
    843 			}
    844 		}
    845 	}
    846 
    847 	/* signal waiters on RESPONSE FROM file server queue */
    848 	for (park=TAILQ_FIRST(&pmp->pmp_msg_replywait); park; park=park_next) {
    849 		mutex_enter(&park->park_mtx);
    850 		puffs_msgpark_reference(park);
    851 		park_next = TAILQ_NEXT(park, park_entries);
    852 
    853 		KASSERT(park->park_flags & PARKFLAG_ONQUEUE2);
    854 		KASSERT(park->park_flags & PARKFLAG_WANTREPLY);
    855 
    856 		TAILQ_REMOVE(&pmp->pmp_msg_replywait, park, park_entries);
    857 		park->park_flags &= ~PARKFLAG_ONQUEUE2;
    858 
    859 		if (park->park_flags & PARKFLAG_WAITERGONE) {
    860 			KASSERT((park->park_flags & PARKFLAG_CALL) == 0);
    861 			puffs_msgpark_release(park);
    862 		} else {
    863 			park->park_preq->preq_rv = ENXIO;
    864 			if (park->park_flags & PARKFLAG_CALL) {
    865 				park->park_done(pmp, park->park_preq,
    866 				    park->park_donearg);
    867 				puffs_msgpark_release1(park, 2);
    868 			} else {
    869 				cv_signal(&park->park_cv);
    870 				puffs_msgpark_release(park);
    871 			}
    872 		}
    873 	}
    874 
    875 	cv_broadcast(&pmp->pmp_msg_waiter_cv);
    876 }
    877