Home | History | Annotate | Line # | Download | only in puffs
puffs_msgif.c revision 1.49
      1 /*	$NetBSD: puffs_msgif.c,v 1.49 2007/10/21 14:28:05 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.49 2007/10/21 14:28:05 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  *
    390  * This unlocks vnodes if they are supplied.  vp1 is the vnode
    391  * before in the locking order, i.e. the one which must be locked
    392  * before accessing vp2.  This is done here so that operations are
    393  * already ordered in the queue when vnodes are unlocked (I'm not
    394  * sure if that's really necessary, but it can't hurt).  Okok, maybe
    395  * there's a slight ugly-factor also, but let's not worry about that.
    396  */
    397 static int
    398 touser(struct puffs_mount *pmp, struct puffs_msgpark *park)
    399 {
    400 	struct lwp *l = curlwp;
    401 	struct mount *mp;
    402 	struct puffs_req *preq;
    403 	int rv = 0;
    404 
    405 	mp = PMPTOMP(pmp);
    406 	preq = park->park_preq;
    407 	preq->preq_buflen = park->park_maxlen;
    408 	KASSERT(preq->preq_id == 0);
    409 
    410 	if ((park->park_flags & PARKFLAG_WANTREPLY) == 0)
    411 		preq->preq_opclass |= PUFFSOPFLAG_FAF;
    412 	else
    413 		preq->preq_id = puffs_getmsgid(pmp);
    414 
    415 	/* fill in caller information */
    416 	preq->preq_pid = l->l_proc->p_pid;
    417 	preq->preq_lid = l->l_lid;
    418 
    419 	/*
    420 	 * To support PCATCH, yet another movie: check if there are signals
    421 	 * pending and we are issueing a non-FAF.  If so, return an error
    422 	 * directly UNLESS we are issueing INACTIVE.  In that case, convert
    423 	 * it to a FAF, fire off to the file server and return an error.
    424 	 * Yes, this is bordering disgusting.  Barfbags are on me.
    425 	 */
    426 	if ((park->park_flags & PARKFLAG_WANTREPLY)
    427 	   && (park->park_flags & PARKFLAG_CALL) == 0
    428 	   && (l->l_flag & LW_PENDSIG) != 0 && sigispending(l, 0)) {
    429 		if (PUFFSOP_OPCLASS(preq->preq_opclass) == PUFFSOP_VN
    430 		    && preq->preq_optype == PUFFS_VN_INACTIVE) {
    431 			park->park_preq->preq_opclass |= PUFFSOPFLAG_FAF;
    432 			park->park_flags &= ~PARKFLAG_WANTREPLY;
    433 			DPRINTF(("puffs touser: converted to FAF %p\n", park));
    434 			rv = EINTR;
    435 		} else {
    436 			return EINTR;
    437 		}
    438 	}
    439 
    440 	/*
    441 	 * test for suspension lock.
    442 	 *
    443 	 * Note that we *DO NOT* keep the lock, since that might block
    444 	 * lock acquiring PLUS it would give userlandia control over
    445 	 * the lock.  The operation queue enforces a strict ordering:
    446 	 * when the fs server gets in the op stream, it knows things
    447 	 * are in order.  The kernel locks can't guarantee that for
    448 	 * userspace, in any case.
    449 	 *
    450 	 * BUT: this presents a problem for ops which have a consistency
    451 	 * clause based on more than one operation.  Unfortunately such
    452 	 * operations (read, write) do not reliably work yet.
    453 	 *
    454 	 * Ya, Ya, it's wrong wong wrong, me be fixink this someday.
    455 	 *
    456 	 * XXX: and there is one more problem.  We sometimes need to
    457 	 * take a lazy lock in case the fs is suspending and we are
    458 	 * executing as the fs server context.  This might happen
    459 	 * e.g. in the case that the user server triggers a reclaim
    460 	 * in the kernel while the fs is suspending.  It's not a very
    461 	 * likely event, but it needs to be fixed some day.
    462 	 */
    463 
    464 	/*
    465 	 * MOREXXX: once PUFFS_WCACHEINFO is enabled, we can't take
    466 	 * the mutex here, since getpages() might be called locked.
    467 	 */
    468 	fstrans_start(mp, FSTRANS_NORMAL);
    469 	mutex_enter(&pmp->pmp_lock);
    470 	fstrans_done(mp);
    471 
    472 	if (pmp->pmp_status != PUFFSTAT_RUNNING) {
    473 		mutex_exit(&pmp->pmp_lock);
    474 		return ENXIO;
    475 	}
    476 
    477 #ifdef PUFFSDEBUG
    478 	parkqdump(&pmp->pmp_msg_touser, puffsdebug > 1);
    479 	parkqdump(&pmp->pmp_msg_replywait, puffsdebug > 1);
    480 #endif
    481 
    482 	mutex_enter(&park->park_mtx);
    483 	TAILQ_INSERT_TAIL(&pmp->pmp_msg_touser, park, park_entries);
    484 	park->park_flags |= PARKFLAG_ONQUEUE1;
    485 	puffs_mp_reference(pmp);
    486 	pmp->pmp_msg_touser_count++;
    487 	mutex_exit(&pmp->pmp_lock);
    488 
    489 	DPRINTF(("touser: req %" PRIu64 ", preq: %p, park: %p, "
    490 	    "c/t: 0x%x/0x%x, f: 0x%x\n", preq->preq_id, preq, park,
    491 	    preq->preq_opclass, preq->preq_optype, park->park_flags));
    492 
    493 	cv_broadcast(&pmp->pmp_msg_waiter_cv);
    494 	selnotify(pmp->pmp_sel, 0);
    495 
    496 	if ((park->park_flags & PARKFLAG_WANTREPLY)
    497 	    && (park->park_flags & PARKFLAG_CALL) == 0) {
    498 		int error;
    499 
    500 		error = cv_wait_sig(&park->park_cv, &park->park_mtx);
    501 		DPRINTF(("puffs_touser: waiter for %p woke up with %d\n",
    502 		    park, error));
    503 		if (error) {
    504 			park->park_flags |= PARKFLAG_WAITERGONE;
    505 			if (park->park_flags & PARKFLAG_DONE) {
    506 				rv = preq->preq_rv;
    507 			} else {
    508 				/*
    509 				 * ok, we marked it as going away, but
    510 				 * still need to do queue ops.  take locks
    511 				 * in correct order.
    512 				 *
    513 				 * We don't want to release our reference
    514 				 * if it's on replywait queue to avoid error
    515 				 * to file server.  putop() code will DTRT.
    516 				 */
    517 				mutex_exit(&park->park_mtx);
    518 				mutex_enter(&pmp->pmp_lock);
    519 				mutex_enter(&park->park_mtx);
    520 
    521 				/* remove from queue1 */
    522 				if (park->park_flags & PARKFLAG_ONQUEUE1) {
    523 					TAILQ_REMOVE(&pmp->pmp_msg_touser,
    524 					    park, park_entries);
    525 					pmp->pmp_msg_touser_count--;
    526 					park->park_flags &= ~PARKFLAG_ONQUEUE1;
    527 				}
    528 
    529 				/*
    530 				 * If it's waiting for a response already,
    531 				 * boost reference count.  Park will get
    532 				 * nuked once the response arrives from
    533 				 * the file server.
    534 				 */
    535 				if (park->park_flags & PARKFLAG_ONQUEUE2)
    536 					puffs_msgpark_reference(park);
    537 
    538 				mutex_exit(&pmp->pmp_lock);
    539 
    540 				rv = error;
    541 			}
    542 		} else {
    543 			rv = preq->preq_rv;
    544 		}
    545 
    546 		/*
    547 		 * retake the lock and release.  This makes sure (haha,
    548 		 * I'm humorous) that we don't process the same vnode in
    549 		 * multiple threads due to the locks hacks we have in
    550 		 * puffs_lock().  In reality this is well protected by
    551 		 * the biglock, but once that's gone, well, hopefully
    552 		 * this will be fixed for real.  (and when you read this
    553 		 * comment in 2017 and subsequently barf, my condolences ;).
    554 		 */
    555 		if (rv == 0 && !fstrans_is_owner(mp)) {
    556 			fstrans_start(mp, FSTRANS_NORMAL);
    557 			fstrans_done(mp);
    558 		}
    559 	} else {
    560 		/*
    561 		 * Take extra reference for FAF, i.e. don't free us
    562 		 * immediately upon return to the caller, but rather
    563 		 * only when the message has been transported.
    564 		 */
    565 		puffs_msgpark_reference(park);
    566 	}
    567 
    568 	mutex_exit(&park->park_mtx);
    569 
    570 	mutex_enter(&pmp->pmp_lock);
    571 	puffs_mp_release(pmp);
    572 	mutex_exit(&pmp->pmp_lock);
    573 
    574 	return rv;
    575 }
    576 
    577 /*
    578  * Get next request in the outgoing queue.  "maxsize" controls the
    579  * size the caller can accommodate and "nonblock" signals if this
    580  * should block while waiting for input.  Handles all locking internally.
    581  */
    582 int
    583 puffs_msgif_getout(void *this, size_t maxsize, int nonblock,
    584 	uint8_t **data, size_t *dlen, void **parkptr)
    585 {
    586 	struct puffs_mount *pmp = this;
    587 	struct puffs_msgpark *park;
    588 	struct puffs_req *preq;
    589 	int error;
    590 
    591 	error = 0;
    592 	mutex_enter(&pmp->pmp_lock);
    593 	for (;;) {
    594 		/* RIP? */
    595 		if (pmp->pmp_status != PUFFSTAT_RUNNING) {
    596 			error = ENXIO;
    597 			break;
    598 		}
    599 
    600 		/* need platinum yendorian express card? */
    601 		if (TAILQ_EMPTY(&pmp->pmp_msg_touser)) {
    602 			DPRINTF(("puffs_getout: no outgoing op, "));
    603 			if (nonblock) {
    604 				DPRINTF(("returning EWOULDBLOCK\n"));
    605 				error = EWOULDBLOCK;
    606 				break;
    607 			}
    608 			DPRINTF(("waiting ...\n"));
    609 
    610 			error = cv_wait_sig(&pmp->pmp_msg_waiter_cv,
    611 			    &pmp->pmp_lock);
    612 			if (error)
    613 				break;
    614 			else
    615 				continue;
    616 		}
    617 
    618 		park = TAILQ_FIRST(&pmp->pmp_msg_touser);
    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 	mutex_exit(&pmp->pmp_lock);
    661 
    662 	if (error == 0) {
    663 		*data = (uint8_t *)preq;
    664 		preq->preq_frhdr.pfr_len = park->park_copylen;
    665 		preq->preq_frhdr.pfr_alloclen = park->park_maxlen;
    666 		preq->preq_frhdr.pfr_type = preq->preq_opclass; /* yay! */
    667 		*dlen = preq->preq_frhdr.pfr_len;
    668 		*parkptr = park;
    669 	}
    670 
    671 	return error;
    672 }
    673 
    674 /*
    675  * Release outgoing structure.  Now, depending on the success of the
    676  * outgoing send, it is either going onto the result waiting queue
    677  * or the death chamber.
    678  */
    679 void
    680 puffs_msgif_releaseout(void *this, void *parkptr, int status)
    681 {
    682 	struct puffs_mount *pmp = this;
    683 	struct puffs_msgpark *park = parkptr;
    684 
    685 	DPRINTF(("puffs_releaseout: returning park %p, errno %d: " ,
    686 	    park, status));
    687 	mutex_enter(&pmp->pmp_lock);
    688 	mutex_enter(&park->park_mtx);
    689 	if (park->park_flags & PARKFLAG_WANTREPLY) {
    690 		if (status == 0) {
    691 			DPRINTF(("enqueue replywait\n"));
    692 			TAILQ_INSERT_TAIL(&pmp->pmp_msg_replywait, park,
    693 			    park_entries);
    694 			park->park_flags |= PARKFLAG_ONQUEUE2;
    695 		} else {
    696 			DPRINTF(("error path!\n"));
    697 			park->park_preq->preq_rv = status;
    698 			park->park_flags |= PARKFLAG_DONE;
    699 			cv_signal(&park->park_cv);
    700 		}
    701 		puffs_msgpark_release(park);
    702 	} else {
    703 		DPRINTF(("release\n"));
    704 		puffs_msgpark_release1(park, 2);
    705 	}
    706 	mutex_exit(&pmp->pmp_lock);
    707 }
    708 
    709 void
    710 puffs_msgif_incoming(void *this, void *buf)
    711 {
    712 	struct puffs_mount *pmp = this;
    713 	struct puffs_req *preq = buf;
    714 	struct puffs_frame *pfr = &preq->preq_frhdr;
    715 	struct puffs_msgpark *park;
    716 	int release, wgone;
    717 
    718 	/* XXX */
    719 	if (PUFFSOP_OPCLASS(preq->preq_opclass) != PUFFSOP_VN
    720 	    && PUFFSOP_OPCLASS(preq->preq_opclass) != PUFFSOP_VFS)
    721 		return;
    722 
    723 	mutex_enter(&pmp->pmp_lock);
    724 
    725 	/* Locate waiter */
    726 	TAILQ_FOREACH(park, &pmp->pmp_msg_replywait, park_entries) {
    727 		if (park->park_preq->preq_id == preq->preq_id)
    728 			break;
    729 	}
    730 	if (park == NULL) {
    731 		DPRINTF(("puffs_msgif_income: no request: %" PRIu64 "\n",
    732 		    preq->preq_id));
    733 		mutex_exit(&pmp->pmp_lock);
    734 		return; /* XXX send error */
    735 	}
    736 
    737 	mutex_enter(&park->park_mtx);
    738 	puffs_msgpark_reference(park);
    739 	if (pfr->pfr_len > park->park_maxlen) {
    740 		DPRINTF(("puffs_msgif_income: invalid buffer length: "
    741 		    "%zu (req %" PRIu64 ", \n", pfr->pfr_len, preq->preq_id));
    742 		park->park_preq->preq_rv = EPROTO;
    743 		cv_signal(&park->park_cv);
    744 		puffs_msgpark_release(park);
    745 		mutex_exit(&pmp->pmp_lock);
    746 		return; /* XXX: error */
    747 	}
    748 	wgone = park->park_flags & PARKFLAG_WAITERGONE;
    749 
    750 	KASSERT(park->park_flags & PARKFLAG_ONQUEUE2);
    751 	TAILQ_REMOVE(&pmp->pmp_msg_replywait, park, park_entries);
    752 	park->park_flags &= ~PARKFLAG_ONQUEUE2;
    753 	mutex_exit(&pmp->pmp_lock);
    754 
    755 	if (wgone) {
    756 		DPRINTF(("puffs_putop: bad service - waiter gone for "
    757 		    "park %p\n", park));
    758 		release = 2;
    759 	} else {
    760 		if (park->park_flags & PARKFLAG_CALL) {
    761 			DPRINTF(("puffs_msgif_income: call for %p, arg %p\n",
    762 			    park->park_preq, park->park_donearg));
    763 			park->park_done(pmp, buf, park->park_donearg);
    764 			release = 2;
    765 		} else {
    766 			/* XXX: yes, I know */
    767 			memcpy(park->park_preq, buf, pfr->pfr_len);
    768 			release = 1;
    769 		}
    770 	}
    771 
    772 	if (!wgone) {
    773 		DPRINTF(("puffs_putop: flagging done for "
    774 		    "park %p\n", park));
    775 		cv_signal(&park->park_cv);
    776 	}
    777 
    778 	park->park_flags |= PARKFLAG_DONE;
    779 	puffs_msgpark_release1(park, release);
    780 }
    781 
    782 /*
    783  * We're dead, kaput, RIP, slightly more than merely pining for the
    784  * fjords, belly-up, fallen, lifeless, finished, expired, gone to meet
    785  * our maker, ceased to be, etcetc.  YASD.  It's a dead FS!
    786  *
    787  * Caller must hold puffs mutex.
    788  */
    789 void
    790 puffs_userdead(struct puffs_mount *pmp)
    791 {
    792 	struct puffs_msgpark *park, *park_next;
    793 
    794 	/*
    795 	 * Mark filesystem status as dying so that operations don't
    796 	 * attempt to march to userspace any longer.
    797 	 */
    798 	pmp->pmp_status = PUFFSTAT_DYING;
    799 
    800 	/* signal waiters on REQUEST TO file server queue */
    801 	for (park = TAILQ_FIRST(&pmp->pmp_msg_touser); park; park = park_next) {
    802 		uint8_t opclass;
    803 
    804 		mutex_enter(&park->park_mtx);
    805 		puffs_msgpark_reference(park);
    806 		park_next = TAILQ_NEXT(park, park_entries);
    807 
    808 		KASSERT(park->park_flags & PARKFLAG_ONQUEUE1);
    809 		TAILQ_REMOVE(&pmp->pmp_msg_touser, park, park_entries);
    810 		park->park_flags &= ~PARKFLAG_ONQUEUE1;
    811 		pmp->pmp_msg_touser_count--;
    812 
    813 		/*
    814 		 * If the waiter is gone, we may *NOT* access preq anymore.
    815 		 */
    816 		if (park->park_flags & PARKFLAG_WAITERGONE) {
    817 			KASSERT((park->park_flags & PARKFLAG_CALL) == 0);
    818 			KASSERT(park->park_flags & PARKFLAG_WANTREPLY);
    819 			puffs_msgpark_release(park);
    820 		} else {
    821 			opclass = park->park_preq->preq_opclass;
    822 			park->park_preq->preq_rv = ENXIO;
    823 
    824 			if (park->park_flags & PARKFLAG_CALL) {
    825 				park->park_done(pmp, park->park_preq,
    826 				    park->park_donearg);
    827 				puffs_msgpark_release1(park, 2);
    828 			} else if ((park->park_flags & PARKFLAG_WANTREPLY)==0) {
    829 				puffs_msgpark_release1(park, 2);
    830 			} else {
    831 				park->park_preq->preq_rv = ENXIO;
    832 				cv_signal(&park->park_cv);
    833 				puffs_msgpark_release(park);
    834 			}
    835 		}
    836 	}
    837 
    838 	/* signal waiters on RESPONSE FROM file server queue */
    839 	for (park=TAILQ_FIRST(&pmp->pmp_msg_replywait); park; park=park_next) {
    840 		mutex_enter(&park->park_mtx);
    841 		puffs_msgpark_reference(park);
    842 		park_next = TAILQ_NEXT(park, park_entries);
    843 
    844 		KASSERT(park->park_flags & PARKFLAG_ONQUEUE2);
    845 		KASSERT(park->park_flags & PARKFLAG_WANTREPLY);
    846 
    847 		TAILQ_REMOVE(&pmp->pmp_msg_replywait, park, park_entries);
    848 		park->park_flags &= ~PARKFLAG_ONQUEUE2;
    849 
    850 		/*
    851 		 * If the waiter is gone, we may *NOT* access preq anymore.
    852 		 */
    853 		if (park->park_flags & PARKFLAG_WAITERGONE) {
    854 			KASSERT((park->park_flags & PARKFLAG_CALL) == 0);
    855 			puffs_msgpark_release(park);
    856 		} else {
    857 			park->park_preq->preq_rv = ENXIO;
    858 			if (park->park_flags & PARKFLAG_CALL) {
    859 				park->park_done(pmp, park->park_preq,
    860 				    park->park_donearg);
    861 				puffs_msgpark_release1(park, 2);
    862 			} else {
    863 				cv_signal(&park->park_cv);
    864 				puffs_msgpark_release(park);
    865 			}
    866 		}
    867 	}
    868 }
    869