xref: /netbsd-src/lib/libpuffs/framebuf.c (revision 0df165c04d0a9ca1adde9ed2b890344c937954a6)
1 /*	$NetBSD: framebuf.c,v 1.23 2007/09/06 18:01:34 pooka Exp $	*/
2 
3 /*
4  * Copyright (c) 2007  Antti Kantee.  All Rights Reserved.
5  *
6  * Development of this software was supported by the
7  * Finnish Cultural Foundation.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
19  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 /*
32  * The event portion of this code is a twisty maze of pointers,
33  * flags, yields and continues.  Sincere aplogies.
34  */
35 
36 #include <sys/cdefs.h>
37 #if !defined(lint)
38 __RCSID("$NetBSD: framebuf.c,v 1.23 2007/09/06 18:01:34 pooka Exp $");
39 #endif /* !lint */
40 
41 #include <sys/types.h>
42 #include <sys/queue.h>
43 
44 #include <assert.h>
45 #include <errno.h>
46 #include <poll.h>
47 #include <puffs.h>
48 #include <stdlib.h>
49 #include <unistd.h>
50 
51 #include "puffs_priv.h"
52 
53 struct puffs_framebuf {
54 	struct puffs_cc *pcc;	/* pcc to continue with */
55 	/* OR */
56 	puffs_framev_cb fcb;	/* non-blocking callback */
57 	void *fcb_arg;		/* argument for previous */
58 
59 	uint8_t *buf;		/* buffer base */
60 	size_t len;		/* total length */
61 
62 	size_t offset;		/* cursor, telloff() */
63 	size_t maxoff;		/* maximum offset for data, tellsize() */
64 
65 	volatile int rv;	/* errno value */
66 
67 	int	istat;
68 
69 	TAILQ_ENTRY(puffs_framebuf) pfb_entries;
70 };
71 #define ISTAT_NODESTROY	0x01	/* indestructible by framebuf_destroy() */
72 #define ISTAT_INTERNAL	0x02	/* never leaves library			*/
73 #define ISTAT_NOREPLY	0x04	/* nuke after sending 			*/
74 #define ISTAT_DIRECT	0x08	/* receive directly, no moveinfo	*/
75 
76 #define ISTAT_ONQUEUE	ISTAT_NODESTROY	/* alias */
77 
78 #define PUFBUF_INCRALLOC 4096
79 #define PUFBUF_REMAIN(p) (p->len - p->offset)
80 
81 /* for poll/kqueue */
82 struct puffs_fbevent {
83 	struct puffs_cc	*pcc;
84 	int what;
85 	volatile int rv;
86 
87 	LIST_ENTRY(puffs_fbevent) pfe_entries;
88 };
89 
90 static struct puffs_fctrl_io *
91 getfiobyfd(struct puffs_usermount *pu, int fd)
92 {
93 	struct puffs_framectrl *pfctrl = &pu->pu_framectrl;
94 	struct puffs_fctrl_io *fio;
95 
96 	LIST_FOREACH(fio, &pfctrl->fb_ios, fio_entries)
97 		if (fio->io_fd == fd)
98 			return fio;
99 	return NULL;
100 }
101 
102 struct puffs_framebuf *
103 puffs_framebuf_make()
104 {
105 	struct puffs_framebuf *pufbuf;
106 
107 	pufbuf = malloc(sizeof(struct puffs_framebuf));
108 	if (pufbuf == NULL)
109 		return NULL;
110 	memset(pufbuf, 0, sizeof(struct puffs_framebuf));
111 
112 	pufbuf->buf = malloc(PUFBUF_INCRALLOC);
113 	if (pufbuf->buf == NULL) {
114 		free(pufbuf);
115 		return NULL;
116 	}
117 	pufbuf->len = PUFBUF_INCRALLOC;
118 
119 	puffs_framebuf_recycle(pufbuf);
120 	return pufbuf;
121 }
122 
123 void
124 puffs_framebuf_destroy(struct puffs_framebuf *pufbuf)
125 {
126 
127 	assert((pufbuf->istat & ISTAT_NODESTROY) == 0);
128 
129 	free(pufbuf->buf);
130 	free(pufbuf);
131 }
132 
133 void
134 puffs_framebuf_recycle(struct puffs_framebuf *pufbuf)
135 {
136 
137 	assert((pufbuf->istat & ISTAT_NODESTROY) == 0);
138 
139 	pufbuf->offset = 0;
140 	pufbuf->maxoff = 0;
141 	pufbuf->istat = 0;
142 }
143 
144 static int
145 reservespace(struct puffs_framebuf *pufbuf, size_t off, size_t wantsize)
146 {
147 	size_t incr;
148 	void *nd;
149 
150 	if (off <= pufbuf->len && pufbuf->len - off >= wantsize)
151 		return 0;
152 
153 	for (incr = PUFBUF_INCRALLOC;
154 	    pufbuf->len + incr < off + wantsize;
155 	    incr += PUFBUF_INCRALLOC)
156 		continue;
157 
158 	nd = realloc(pufbuf->buf, pufbuf->offset + incr);
159 	if (nd == NULL)
160 		return -1;
161 
162 	pufbuf->buf = nd;
163 	pufbuf->len += incr;
164 
165 	return 0;
166 }
167 
168 int
169 puffs_framebuf_reserve_space(struct puffs_framebuf *pufbuf, size_t wantsize)
170 {
171 
172 	return reservespace(pufbuf, pufbuf->offset, wantsize);
173 }
174 
175 int
176 puffs_framebuf_putdata(struct puffs_framebuf *pufbuf,
177 	const void *data, size_t dlen)
178 {
179 
180 	if (PUFBUF_REMAIN(pufbuf) < dlen)
181 		if (puffs_framebuf_reserve_space(pufbuf, dlen) == -1)
182 			return -1;
183 
184 	memcpy(pufbuf->buf + pufbuf->offset, data, dlen);
185 	pufbuf->offset += dlen;
186 
187 	if (pufbuf->offset > pufbuf->maxoff)
188 		pufbuf->maxoff = pufbuf->offset;
189 
190 	return 0;
191 }
192 
193 int
194 puffs_framebuf_putdata_atoff(struct puffs_framebuf *pufbuf, size_t offset,
195 	const void *data, size_t dlen)
196 {
197 
198 	if (reservespace(pufbuf, offset, dlen) == -1)
199 		return -1;
200 
201 	memcpy(pufbuf->buf + offset, data, dlen);
202 
203 	if (offset + dlen > pufbuf->maxoff)
204 		pufbuf->maxoff = offset + dlen;
205 
206 	return 0;
207 }
208 
209 int
210 puffs_framebuf_getdata(struct puffs_framebuf *pufbuf, void *data, size_t dlen)
211 {
212 
213 	if (pufbuf->maxoff < pufbuf->offset + dlen) {
214 		errno = ENOBUFS;
215 		return -1;
216 	}
217 
218 	memcpy(data, pufbuf->buf + pufbuf->offset, dlen);
219 	pufbuf->offset += dlen;
220 
221 	return 0;
222 }
223 
224 int
225 puffs_framebuf_getdata_atoff(struct puffs_framebuf *pufbuf, size_t offset,
226 	void *data, size_t dlen)
227 {
228 
229 	if (pufbuf->maxoff < offset + dlen) {
230 		errno = ENOBUFS;
231 		return -1;
232 	}
233 
234 	memcpy(data, pufbuf->buf + offset, dlen);
235 	return 0;
236 }
237 
238 size_t
239 puffs_framebuf_telloff(struct puffs_framebuf *pufbuf)
240 {
241 
242 	return pufbuf->offset;
243 }
244 
245 size_t
246 puffs_framebuf_tellsize(struct puffs_framebuf *pufbuf)
247 {
248 
249 	return pufbuf->maxoff;
250 }
251 
252 size_t
253 puffs_framebuf_remaining(struct puffs_framebuf *pufbuf)
254 {
255 
256 	return puffs_framebuf_tellsize(pufbuf) - puffs_framebuf_telloff(pufbuf);
257 }
258 
259 int
260 puffs_framebuf_seekset(struct puffs_framebuf *pufbuf, size_t newoff)
261 {
262 
263 	if (reservespace(pufbuf, newoff, 0) == -1)
264 		return -1;
265 
266 	pufbuf->offset = newoff;
267 	return 0;
268 }
269 
270 int
271 puffs_framebuf_getwindow(struct puffs_framebuf *pufbuf, size_t winoff,
272 	void **data, size_t *dlen)
273 {
274 	size_t winlen;
275 
276 #ifdef WINTESTING
277 	winlen = MIN(*dlen, 32);
278 #else
279 	winlen = *dlen;
280 #endif
281 
282 	if (reservespace(pufbuf, winoff, winlen) == -1)
283 		return -1;
284 
285 	*data = pufbuf->buf + winoff;
286 	if (pufbuf->maxoff < winoff + winlen)
287 		pufbuf->maxoff = winoff + winlen;
288 
289 	return 0;
290 }
291 
292 static void
293 errnotify(struct puffs_usermount *pu, struct puffs_framebuf *pufbuf, int error)
294 {
295 
296 	pufbuf->rv = error;
297 	if (pufbuf->pcc) {
298 		puffs_goto(pufbuf->pcc);
299 	} else if (pufbuf->fcb) {
300 		pufbuf->istat &= ~ISTAT_NODESTROY;
301 		pufbuf->fcb(pu, pufbuf, pufbuf->fcb_arg, error);
302 	} else {
303 		pufbuf->istat &= ~ISTAT_NODESTROY;
304 		puffs_framebuf_destroy(pufbuf);
305 	}
306 }
307 
308 #define GETFIO(fd)							\
309 do {									\
310 	fio = getfiobyfd(pu, fd);					\
311 	if (fio == NULL) {						\
312 		errno = EINVAL;						\
313 		return -1;						\
314 	}								\
315 	if (fio->stat & FIO_WRGONE) {					\
316 		errno = ESHUTDOWN;					\
317 		return -1;						\
318 	}								\
319 } while (/*CONSTCOND*/0)
320 
321 int
322 puffs_framev_enqueue_cc(struct puffs_cc *pcc, int fd,
323 	struct puffs_framebuf *pufbuf, int flags)
324 {
325 	struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
326 	struct puffs_fctrl_io *fio;
327 
328 	/*
329 	 * Technically we shouldn't allow this is RDGONE, but it's
330 	 * difficult to trap write close without allowing writes.
331 	 * And besides, there's probably a disconnect sequence in
332 	 * the protocol, so unexpectedly getting a closed fd is
333 	 * most likely an error condition.
334 	 */
335 	GETFIO(fd);
336 
337 	pufbuf->pcc = pcc;
338 	pufbuf->fcb = NULL;
339 	pufbuf->fcb_arg = NULL;
340 
341 	pufbuf->offset = 0;
342 	pufbuf->istat |= ISTAT_NODESTROY;
343 
344 	if (flags & PUFFS_FBQUEUE_URGENT)
345 		TAILQ_INSERT_HEAD(&fio->snd_qing, pufbuf, pfb_entries);
346 	else
347 		TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
348 
349 	puffs_cc_yield(pcc);
350 	if (pufbuf->rv) {
351 		pufbuf->istat &= ~ISTAT_NODESTROY;
352 		errno = pufbuf->rv;
353 		return -1;
354 	}
355 
356 	return 0;
357 }
358 
359 int
360 puffs_framev_enqueue_cb(struct puffs_usermount *pu, int fd,
361 	struct puffs_framebuf *pufbuf, puffs_framev_cb fcb, void *arg,
362 	int flags)
363 {
364 	struct puffs_fctrl_io *fio;
365 
366 	/* see enqueue_cc */
367 	GETFIO(fd);
368 
369 	pufbuf->pcc = NULL;
370 	pufbuf->fcb = fcb;
371 	pufbuf->fcb_arg = arg;
372 
373 	pufbuf->offset = 0;
374 	pufbuf->istat |= ISTAT_NODESTROY;
375 
376 	if (flags & PUFFS_FBQUEUE_URGENT)
377 		TAILQ_INSERT_HEAD(&fio->snd_qing, pufbuf, pfb_entries);
378 	else
379 		TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
380 
381 	return 0;
382 }
383 
384 int
385 puffs_framev_enqueue_justsend(struct puffs_usermount *pu, int fd,
386 	struct puffs_framebuf *pufbuf, int reply, int flags)
387 {
388 	struct puffs_fctrl_io *fio;
389 
390 	GETFIO(fd);
391 
392 	pufbuf->pcc = NULL;
393 	pufbuf->fcb = NULL;
394 	pufbuf->fcb_arg = NULL;
395 
396 	pufbuf->offset = 0;
397 	pufbuf->istat |= ISTAT_NODESTROY;
398 	if (!reply)
399 		pufbuf->istat |= ISTAT_NOREPLY;
400 
401 	if (flags & PUFFS_FBQUEUE_URGENT)
402 		TAILQ_INSERT_HEAD(&fio->snd_qing, pufbuf, pfb_entries);
403 	else
404 		TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
405 
406 	return 0;
407 }
408 
409 /* ARGSUSED */
410 int
411 puffs_framev_enqueue_directreceive(struct puffs_cc *pcc, int fd,
412 	struct puffs_framebuf *pufbuf, int flags /* used in the future */)
413 {
414 	struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
415 	struct puffs_fctrl_io *fio;
416 
417 	fio = getfiobyfd(pu, fd);
418 	if (fio == NULL) {
419 		errno = EINVAL;
420 		return -1;
421 	}
422 
423 	/* XXX: should have cur_in queue */
424 	assert(fio->cur_in == NULL);
425 	fio->cur_in = pufbuf;
426 
427 	pufbuf->pcc = pcc;
428 	pufbuf->fcb = NULL;
429 	pufbuf->fcb_arg = NULL;
430 
431 	pufbuf->offset = 0;
432 	pufbuf->istat |= ISTAT_NODESTROY | ISTAT_DIRECT;
433 
434 	puffs_cc_yield(pcc);
435 	pufbuf->istat &= ~ISTAT_NODESTROY; /* XXX: not the right place */
436 	if (pufbuf->rv) {
437 		errno = pufbuf->rv;
438 		return -1;
439 	}
440 
441 	return 0;
442 }
443 
444 int
445 puffs_framev_enqueue_directsend(struct puffs_cc *pcc, int fd,
446 	struct puffs_framebuf *pufbuf, int flags)
447 {
448 	struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
449 	struct puffs_fctrl_io *fio;
450 
451 	if (flags & PUFFS_FBQUEUE_URGENT)
452 		abort(); /* EOPNOTSUPP for now */
453 
454 	GETFIO(fd);
455 
456 	pufbuf->pcc = pcc;
457 	pufbuf->fcb = NULL;
458 	pufbuf->fcb_arg = NULL;
459 
460 	pufbuf->offset = 0;
461 	pufbuf->istat |= ISTAT_NODESTROY | ISTAT_DIRECT;
462 
463 	TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
464 
465 	puffs_cc_yield(pcc);
466 	if (pufbuf->rv) {
467 		pufbuf->istat &= ~ISTAT_NODESTROY;
468 		errno = pufbuf->rv;
469 		return -1;
470 	}
471 
472 	return 0;
473 }
474 
475 int
476 puffs_framev_framebuf_ccpromote(struct puffs_framebuf *pufbuf,
477 	struct puffs_cc *pcc)
478 {
479 
480 	if ((pufbuf->istat & ISTAT_ONQUEUE) == 0) {
481 		errno = EBUSY;
482 		return -1;
483 	}
484 
485 	pufbuf->pcc = pcc;
486 	pufbuf->fcb = NULL;
487 	pufbuf->fcb_arg = NULL;
488 	pufbuf->istat &= ~ISTAT_NOREPLY;
489 
490 	puffs_cc_yield(pcc);
491 
492 	return 0;
493 }
494 
495 int
496 puffs_framev_enqueue_waitevent(struct puffs_cc *pcc, int fd, int *what)
497 {
498 	struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
499 	struct puffs_fctrl_io *fio;
500 	struct puffs_fbevent feb;
501 	struct kevent kev;
502 	int rv, svwhat;
503 
504 	svwhat = *what;
505 
506 	if (*what == 0) {
507 		errno = EINVAL;
508 		return -1;
509 	}
510 
511 	fio = getfiobyfd(pu, fd);
512 	if (fio == NULL) {
513 		errno = EINVAL;
514 		return -1;
515 	}
516 
517 	feb.pcc = pcc;
518 	feb.what = *what & (PUFFS_FBIO_READ|PUFFS_FBIO_WRITE|PUFFS_FBIO_ERROR);
519 
520 	if (*what & PUFFS_FBIO_READ)
521 		if ((fio->stat & FIO_ENABLE_R) == 0)
522 			EV_SET(&kev, fd, EVFILT_READ, EV_ENABLE,
523 			    0, 0, (uintptr_t)fio);
524 
525 	rv = kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
526 	if (rv != 0)
527 		return errno;
528 
529 	if (*what & PUFFS_FBIO_READ)
530 		fio->rwait++;
531 	if (*what & PUFFS_FBIO_WRITE)
532 		fio->wwait++;
533 
534 	LIST_INSERT_HEAD(&fio->ev_qing, &feb, pfe_entries);
535 	puffs_cc_yield(pcc);
536 
537 	assert(svwhat == *what);
538 
539 	if (*what & PUFFS_FBIO_READ) {
540 		fio->rwait--;
541 		if (fio->rwait == 0 && (fio->stat & FIO_ENABLE_R) == 0) {
542 			EV_SET(&kev, fd, EVFILT_READ, EV_DISABLE,
543 			    0, 0, (uintptr_t)fio);
544 			rv = kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
545 #if 0
546 			if (rv != 0)
547 				/* XXXXX oh dear */;
548 #endif
549 		}
550 	}
551 	if (*what & PUFFS_FBIO_WRITE)
552 		fio->wwait--;
553 
554 	if (feb.rv == 0) {
555 		*what = feb.what;
556 		rv = 0;
557 	} else {
558 		*what = PUFFS_FBIO_ERROR;
559 		errno = feb.rv;
560 		rv = -1;
561 	}
562 
563 	return rv;
564 }
565 
566 void
567 puffs_framev_notify(struct puffs_fctrl_io *fio, int what)
568 {
569 	struct puffs_fbevent *fbevp;
570 
571  restart:
572 	LIST_FOREACH(fbevp, &fio->ev_qing, pfe_entries) {
573 		if (fbevp->what & what) {
574 			fbevp->what = what;
575 			fbevp->rv = 0;
576 			LIST_REMOVE(fbevp, pfe_entries);
577 			puffs_cc_continue(fbevp->pcc);
578 			goto restart;
579 		}
580 	}
581 }
582 
583 static struct puffs_framebuf *
584 findbuf(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
585 	struct puffs_fctrl_io *fio, struct puffs_framebuf *findme)
586 {
587 	struct puffs_framebuf *cand;
588 	int notresp = 0;
589 
590 	TAILQ_FOREACH(cand, &fio->res_qing, pfb_entries)
591 		if (fctrl->cmpfb(pu, findme, cand, &notresp) == 0 || notresp)
592 			break;
593 
594 	assert(!(notresp && cand == NULL));
595 	if (notresp || cand == NULL)
596 		return NULL;
597 
598 	TAILQ_REMOVE(&fio->res_qing, cand, pfb_entries);
599 	return cand;
600 }
601 
602 static void
603 moveinfo(struct puffs_framebuf *from, struct puffs_framebuf *to)
604 {
605 
606 	assert(from->istat & ISTAT_INTERNAL);
607 
608 	/* migrate buffer */
609 	free(to->buf);
610 	to->buf = from->buf;
611 	from->buf = NULL;
612 
613 	/* migrate buffer info */
614 	to->len = from->len;
615 	to->offset = from->offset;
616 	to->maxoff = from->maxoff;
617 }
618 
619 void
620 puffs_framev_input(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
621 	struct puffs_fctrl_io *fio, struct puffs_putreq *ppr)
622 {
623 	struct puffs_framebuf *pufbuf, *appbuf;
624 	int rv, complete;
625 
626 	while ((fio->stat & FIO_DEAD) == 0 && (fio->stat & FIO_ENABLE_R)) {
627 		if ((pufbuf = fio->cur_in) == NULL) {
628 			pufbuf = puffs_framebuf_make();
629 			if (pufbuf == NULL)
630 				return;
631 			pufbuf->istat |= ISTAT_INTERNAL;
632 			fio->cur_in = pufbuf;
633 		}
634 
635 		complete = 0;
636 		rv = fctrl->rfb(pu, pufbuf, fio->io_fd, &complete);
637 
638 		/* error */
639 		if (rv) {
640 			puffs_framev_readclose(pu, fio, rv);
641 			fio->cur_in = NULL;
642 			if ((pufbuf->istat & ISTAT_DIRECT) == 0) {
643 				assert((pufbuf->istat & ISTAT_NODESTROY) == 0);
644 				puffs_framebuf_destroy(pufbuf);
645 			}
646 			return;
647 		}
648 
649 		/* partial read, come back to fight another day */
650 		if (complete == 0)
651 			break;
652 
653 		/* else: full read, process */
654 		if ((pufbuf->istat & ISTAT_DIRECT) == 0) {
655 			appbuf = findbuf(pu, fctrl, fio, pufbuf);
656 
657 			/*
658 			 * No request for this frame?  If fs implements
659 			 * gotfb, give frame to that.  Otherwise drop it.
660 			 */
661 			if (appbuf == NULL) {
662 				if (fctrl->gotfb)
663 					fctrl->gotfb(pu, pufbuf);
664 
665 				/* XXX: ugly */
666 				pufbuf->istat &= ~ISTAT_NODESTROY;
667 				fio->cur_in = NULL;
668 				puffs_framebuf_destroy(pufbuf);
669 				continue;
670 			}
671 
672 			moveinfo(pufbuf, appbuf);
673 			puffs_framebuf_destroy(pufbuf);
674 		} else {
675 			appbuf = pufbuf;
676 		}
677 		appbuf->istat &= ~ISTAT_NODESTROY;
678 		fio->cur_in = NULL;
679 
680 		if (appbuf->pcc) {
681 			puffs_docc(appbuf->pcc, ppr);
682 		} else if (appbuf->fcb) {
683 			appbuf->fcb(pu, appbuf, appbuf->fcb_arg, 0);
684 		} else {
685 			puffs_framebuf_destroy(appbuf);
686 		}
687 
688 		/* hopeless romantics, here we go again */
689 	}
690 }
691 
692 int
693 puffs_framev_output(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
694 	struct puffs_fctrl_io *fio, struct puffs_putreq *ppr)
695 {
696 	struct puffs_framebuf *pufbuf;
697 	int rv, complete, done;
698 
699 	if (fio->stat & FIO_DEAD)
700 		return 0;
701 
702 	for (pufbuf = TAILQ_FIRST(&fio->snd_qing), done = 0;
703 	    pufbuf && (fio->stat & FIO_DEAD) == 0 && fio->stat & FIO_ENABLE_W;
704 	    pufbuf = TAILQ_FIRST(&fio->snd_qing)) {
705 		complete = 0;
706 		rv = fctrl->wfb(pu, pufbuf, fio->io_fd, &complete);
707 
708 		if (rv) {
709 			puffs_framev_writeclose(pu, fio, rv);
710 			done = 1;
711 			break;
712 		}
713 
714 		/* partial write */
715 		if (complete == 0)
716 			return done;
717 
718 		/* else, complete write */
719 		TAILQ_REMOVE(&fio->snd_qing, pufbuf, pfb_entries);
720 
721 		/* can't wait for result if we can't read */
722 		if (fio->stat & FIO_RDGONE) {
723 			errnotify(pu, pufbuf, ENXIO);
724 			done = 1;
725 		} else if ((pufbuf->istat & ISTAT_DIRECT)) {
726 			pufbuf->istat &= ~ISTAT_NODESTROY;
727 			puffs_docc(pufbuf->pcc, ppr);
728 			done = 1;
729 		} else if ((pufbuf->istat & ISTAT_NOREPLY) == 0) {
730 			TAILQ_INSERT_TAIL(&fio->res_qing, pufbuf,
731 			    pfb_entries);
732 		} else {
733 			pufbuf->istat &= ~ISTAT_NODESTROY;
734 			puffs_framebuf_destroy(pufbuf);
735 		}
736 
737 		/* omstart! */
738 	}
739 
740 	return done;
741 }
742 
743 int
744 puffs_framev_addfd(struct puffs_usermount *pu, int fd, int what)
745 {
746 	struct puffs_framectrl *pfctrl = &pu->pu_framectrl;
747 	struct puffs_fctrl_io *fio;
748 	struct kevent *newevs;
749 	struct kevent kev[2];
750 	size_t nfds;
751 	int rv, readenable;
752 
753 	nfds = pfctrl->nfds+1;
754 	newevs = realloc(pfctrl->evs, (2*nfds+1) * sizeof(struct kevent));
755 	if (newevs == NULL)
756 		return -1;
757 	pfctrl->evs = newevs;
758 
759 	fio = malloc(sizeof(struct puffs_fctrl_io));
760 	if (fio == NULL)
761 		return -1;
762 	memset(fio, 0, sizeof(struct puffs_fctrl_io));
763 	fio->io_fd = fd;
764 	fio->cur_in = NULL;
765 	TAILQ_INIT(&fio->snd_qing);
766 	TAILQ_INIT(&fio->res_qing);
767 	LIST_INIT(&fio->ev_qing);
768 
769 	readenable = 0;
770 	if ((what & PUFFS_FBIO_READ) == 0)
771 		readenable = EV_DISABLE;
772 
773 	if (pu->pu_state & PU_INLOOP) {
774 		EV_SET(&kev[0], fd, EVFILT_READ,
775 		    EV_ADD|readenable, 0, 0, (intptr_t)fio);
776 		EV_SET(&kev[1], fd, EVFILT_WRITE,
777 		    EV_ADD|EV_DISABLE, 0, 0, (intptr_t)fio);
778 		rv = kevent(pu->pu_kq, kev, 2, NULL, 0, NULL);
779 		if (rv == -1) {
780 			free(fio);
781 			return -1;
782 		}
783 	}
784 	if (what & PUFFS_FBIO_READ)
785 		fio->stat |= FIO_ENABLE_R;
786 	if (what & PUFFS_FBIO_WRITE)
787 		fio->stat |= FIO_ENABLE_W;
788 
789 	LIST_INSERT_HEAD(&pfctrl->fb_ios, fio, fio_entries);
790 	pfctrl->nfds = nfds;
791 
792 	return 0;
793 }
794 
795 /*
796  * XXX: the following en/disable should be coalesced and executed
797  * only during the actual kevent call.  So feel free to fix if
798  * threatened by mindblowing boredom.
799  */
800 
801 int
802 puffs_framev_enablefd(struct puffs_usermount *pu, int fd, int what)
803 {
804 	struct kevent kev;
805 	struct puffs_fctrl_io *fio;
806 	int rv = 0;
807 
808 	assert((what & (PUFFS_FBIO_READ | PUFFS_FBIO_WRITE)) != 0);
809 
810 	fio = getfiobyfd(pu, fd);
811 	if (fio == NULL) {
812 		errno = ENXIO;
813 		return -1;
814 	}
815 
816 	/* write is enabled in the event loop if there is output */
817 	if (what & PUFFS_FBIO_READ && fio->rwait == 0) {
818 		EV_SET(&kev, fd, EVFILT_READ, EV_ENABLE, 0, 0, (uintptr_t)fio);
819 		rv = kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
820 	}
821 
822 	if (rv == 0) {
823 		if (what & PUFFS_FBIO_READ)
824 			fio->stat |= FIO_ENABLE_R;
825 		if (what & PUFFS_FBIO_WRITE)
826 			fio->stat |= FIO_ENABLE_W;
827 	}
828 
829 	return rv;
830 }
831 
832 int
833 puffs_framev_disablefd(struct puffs_usermount *pu, int fd, int what)
834 {
835 	struct kevent kev[2];
836 	struct puffs_fctrl_io *fio;
837 	size_t i;
838 	int rv;
839 
840 	assert((what & (PUFFS_FBIO_READ | PUFFS_FBIO_WRITE)) != 0);
841 
842 	fio = getfiobyfd(pu, fd);
843 	if (fio == NULL) {
844 		errno = ENXIO;
845 		return -1;
846 	}
847 
848 	i = 0;
849 	if (what & PUFFS_FBIO_READ && fio->rwait == 0) {
850 		EV_SET(&kev[0], fd,
851 		    EVFILT_READ, EV_DISABLE, 0, 0, (uintptr_t)fio);
852 		i++;
853 	}
854 	if (what & PUFFS_FBIO_WRITE && fio->stat & FIO_WR && fio->wwait == 0) {
855 		EV_SET(&kev[1], fd,
856 		    EVFILT_WRITE, EV_DISABLE, 0, 0, (uintptr_t)fio);
857 		i++;
858 	}
859 	if (i)
860 		rv = kevent(pu->pu_kq, kev, i, NULL, 0, NULL);
861 	else
862 		rv = 0;
863 
864 	if (rv == 0) {
865 		if (what & PUFFS_FBIO_READ)
866 			fio->stat &= ~FIO_ENABLE_R;
867 		if (what & PUFFS_FBIO_WRITE)
868 			fio->stat &= ~FIO_ENABLE_W;
869 	}
870 
871 	return rv;
872 }
873 
874 void
875 puffs_framev_readclose(struct puffs_usermount *pu,
876 	struct puffs_fctrl_io *fio, int error)
877 {
878 	struct puffs_framebuf *pufbuf;
879 	struct kevent kev;
880 	int notflag;
881 
882 	if (fio->stat & FIO_RDGONE || fio->stat & FIO_DEAD)
883 		return;
884 	fio->stat |= FIO_RDGONE;
885 
886 	if (fio->cur_in) {
887 		if ((fio->cur_in->istat & ISTAT_DIRECT) == 0) {
888 			puffs_framebuf_destroy(fio->cur_in);
889 			fio->cur_in = NULL;
890 		} else {
891 			errnotify(pu, fio->cur_in, error);
892 		}
893 	}
894 
895 	while ((pufbuf = TAILQ_FIRST(&fio->res_qing)) != NULL) {
896 		TAILQ_REMOVE(&fio->res_qing, pufbuf, pfb_entries);
897 		errnotify(pu, pufbuf, error);
898 	}
899 
900 	EV_SET(&kev, fio->io_fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
901 	(void) kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
902 
903 	notflag = PUFFS_FBIO_READ;
904 	if (fio->stat & FIO_WRGONE)
905 		notflag |= PUFFS_FBIO_WRITE;
906 
907 	if (pu->pu_framectrl.fdnotfn)
908 		pu->pu_framectrl.fdnotfn(pu, fio->io_fd, notflag);
909 }
910 
911 void
912 puffs_framev_writeclose(struct puffs_usermount *pu,
913 	struct puffs_fctrl_io *fio, int error)
914 {
915 	struct puffs_framebuf *pufbuf;
916 	struct kevent kev;
917 	int notflag;
918 
919 	if (fio->stat & FIO_WRGONE || fio->stat & FIO_DEAD)
920 		return;
921 	fio->stat |= FIO_WRGONE;
922 
923 	while ((pufbuf = TAILQ_FIRST(&fio->snd_qing)) != NULL) {
924 		TAILQ_REMOVE(&fio->snd_qing, pufbuf, pfb_entries);
925 		errnotify(pu, pufbuf, error);
926 	}
927 
928 	EV_SET(&kev, fio->io_fd, EVFILT_WRITE, EV_DELETE, 0, 0, 0);
929 	(void) kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
930 
931 	notflag = PUFFS_FBIO_WRITE;
932 	if (fio->stat & FIO_RDGONE)
933 		notflag |= PUFFS_FBIO_READ;
934 
935 	if (pu->pu_framectrl.fdnotfn)
936 		pu->pu_framectrl.fdnotfn(pu, fio->io_fd, notflag);
937 }
938 
939 static int
940 removefio(struct puffs_usermount *pu, struct puffs_fctrl_io *fio, int error)
941 {
942 	struct puffs_framectrl *pfctrl = &pu->pu_framectrl;
943 	struct puffs_fbevent *fbevp;
944 
945 	LIST_REMOVE(fio, fio_entries);
946 	if (pu->pu_state & PU_INLOOP) {
947 		puffs_framev_readclose(pu, fio, error);
948 		puffs_framev_writeclose(pu, fio, error);
949 	}
950 
951 	while ((fbevp = LIST_FIRST(&fio->ev_qing)) != NULL) {
952 		fbevp->rv = error;
953 		LIST_REMOVE(fbevp, pfe_entries);
954 		puffs_goto(fbevp->pcc);
955 	}
956 
957 	/* don't bother with realloc */
958 	pfctrl->nfds--;
959 
960 	/* don't free us yet, might have some references in event arrays */
961 	fio->stat |= FIO_DEAD;
962 	LIST_INSERT_HEAD(&pfctrl->fb_ios_rmlist, fio, fio_entries);
963 
964 	return 0;
965 
966 }
967 
968 int
969 puffs_framev_removefd(struct puffs_usermount *pu, int fd, int error)
970 {
971 	struct puffs_fctrl_io *fio;
972 
973 	fio = getfiobyfd(pu, fd);
974 	if (fio == NULL) {
975 		errno = ENXIO;
976 		return -1;
977 	}
978 
979 	return removefio(pu, fio, error ? error : ECONNRESET);
980 }
981 
982 void
983 puffs_framev_removeonclose(struct puffs_usermount *pu, int fd, int what)
984 {
985 
986 	if (what == (PUFFS_FBIO_READ | PUFFS_FBIO_WRITE))
987 		(void) puffs_framev_removefd(pu, fd, ECONNRESET);
988 }
989 
990 void
991 puffs_framev_unmountonclose(struct puffs_usermount *pu, int fd, int what)
992 {
993 
994 	/* XXX & X: unmount is non-sensible */
995 	puffs_framev_removeonclose(pu, fd, what);
996 	if (what == (PUFFS_FBIO_READ | PUFFS_FBIO_WRITE))
997 		PU_SETSTATE(pu, PUFFS_STATE_UNMOUNTED);
998 }
999 
1000 void
1001 puffs_framev_init(struct puffs_usermount *pu,
1002 	puffs_framev_readframe_fn rfb, puffs_framev_writeframe_fn wfb,
1003 	puffs_framev_cmpframe_fn cmpfb, puffs_framev_gotframe_fn gotfb,
1004 	puffs_framev_fdnotify_fn fdnotfn)
1005 {
1006 	struct puffs_framectrl *pfctrl;
1007 
1008 	pfctrl = &pu->pu_framectrl;
1009 	pfctrl->rfb = rfb;
1010 	pfctrl->wfb = wfb;
1011 	pfctrl->cmpfb = cmpfb;
1012 	pfctrl->gotfb = gotfb;
1013 	pfctrl->fdnotfn = fdnotfn;
1014 }
1015 
1016 void
1017 puffs_framev_exit(struct puffs_usermount *pu)
1018 {
1019 	struct puffs_framectrl *pfctrl = &pu->pu_framectrl;
1020 	struct puffs_fctrl_io *fio;
1021 
1022 	while ((fio = LIST_FIRST(&pfctrl->fb_ios)) != NULL)
1023 		removefio(pu, fio, ENXIO);
1024 	free(pfctrl->evs);
1025 
1026 	/* closing pu->pu_kq takes care of puffsfd */
1027 }
1028