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