Home | History | Annotate | Line # | Download | only in libpuffs
framebuf.c revision 1.5
      1 /*	$NetBSD: framebuf.c,v 1.5 2007/05/11 16:22:38 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 #include <sys/cdefs.h>
     32 #if !defined(lint)
     33 __RCSID("$NetBSD: framebuf.c,v 1.5 2007/05/11 16:22:38 pooka Exp $");
     34 #endif /* !lint */
     35 
     36 #include <sys/types.h>
     37 #include <sys/queue.h>
     38 
     39 #include <assert.h>
     40 #include <errno.h>
     41 #include <poll.h>
     42 #include <puffs.h>
     43 #include <stdlib.h>
     44 #include <unistd.h>
     45 
     46 #include "puffs_priv.h"
     47 
     48 struct puffs_framebuf {
     49 	struct puffs_cc *pcc;	/* pcc to continue with */
     50 	/* OR */
     51 	puffs_framebuf_cb fcb;	/* non-blocking callback */
     52 	void *fcb_arg;		/* argument for previous */
     53 
     54 	uint8_t *buf;		/* buffer base */
     55 	size_t len;		/* total length */
     56 
     57 	size_t offset;		/* cursor, telloff() */
     58 	size_t maxoff;		/* maximum offset for data, tellsize() */
     59 
     60 	volatile int rv;	/* errno value for pcc framebufs */
     61 
     62 	int	istat;
     63 
     64 	TAILQ_ENTRY(puffs_framebuf) pfb_entries;
     65 };
     66 #define ISTAT_NODESTROY	0x01	/* indestructible by framebuf_destroy() */
     67 #define ISTAT_INTERNAL	0x02	/* never leaves library			*/
     68 #define ISTAT_NOREPLY	0x04	/* nuke after sending 			*/
     69 
     70 struct puffs_fctrl_io {
     71 	int io_fd;
     72 	int wrstat;
     73 
     74 	struct puffs_framebuf *cur_in;
     75 
     76 	TAILQ_HEAD(, puffs_framebuf) snd_qing;	/* queueing to be sent */
     77 	TAILQ_HEAD(, puffs_framebuf) res_qing;	/* q'ing for rescue */
     78 
     79 	LIST_ENTRY(puffs_fctrl_io) fio_entries;
     80 };
     81 #define EN_WRITE(fio) (fio->wrstat == 0 && !TAILQ_EMPTY(&fio->snd_qing))
     82 #define RM_WRITE(fio) (fio->wrstat == 1 && TAILQ_EMPTY(&fio->snd_qing))
     83 
     84 struct puffs_framectrl {
     85 	puffs_framebuf_readframe_fn rfb;
     86 	puffs_framebuf_writeframe_fn wfb;
     87 	puffs_framebuf_respcmp_fn cmpfb;
     88 
     89 	struct kevent *evs;
     90 	struct kevent *ch_evs;
     91 	size_t nevs;
     92 	int kq;
     93 
     94 	LIST_HEAD(, puffs_fctrl_io) fb_ios;
     95 };
     96 
     97 #define PUFBUF_INCRALLOC 65536	/* 64k ought to be enough for anyone */
     98 #define PUFBUF_REMAIN(p) (p->len - p->offset)
     99 
    100 static struct puffs_fctrl_io *
    101 getfiobyfd(struct puffs_usermount *pu, int fd)
    102 {
    103 	struct puffs_fctrl_io *fio;
    104 
    105 	LIST_FOREACH(fio, &pu->pu_framectrl->fb_ios, fio_entries)
    106 		if (fio->io_fd == fd)
    107 			return fio;
    108 	return NULL;
    109 }
    110 
    111 struct puffs_framebuf *
    112 puffs_framebuf_make()
    113 {
    114 	struct puffs_framebuf *pufbuf;
    115 
    116 	pufbuf = malloc(sizeof(struct puffs_framebuf));
    117 	if (pufbuf == NULL)
    118 		return NULL;
    119 	memset(pufbuf, 0, sizeof(struct puffs_framebuf));
    120 
    121 	pufbuf->buf = malloc(PUFBUF_INCRALLOC);
    122 	pufbuf->len = PUFBUF_INCRALLOC;
    123 	if (pufbuf->buf == NULL) {
    124 		free(pufbuf);
    125 		return NULL;
    126 	}
    127 
    128 	puffs_framebuf_recycle(pufbuf);
    129 	return pufbuf;
    130 }
    131 
    132 void
    133 puffs_framebuf_destroy(struct puffs_framebuf *pufbuf)
    134 {
    135 
    136 	assert((pufbuf->istat & ISTAT_NODESTROY) == 0);
    137 
    138 	free(pufbuf->buf);
    139 	free(pufbuf);
    140 }
    141 
    142 void
    143 puffs_framebuf_recycle(struct puffs_framebuf *pufbuf)
    144 {
    145 
    146 	assert((pufbuf->istat & ISTAT_NODESTROY) == 0);
    147 
    148 	pufbuf->offset = 0;
    149 	pufbuf->maxoff = 0;
    150 	pufbuf->istat = 0;
    151 }
    152 
    153 static int
    154 reservespace(struct puffs_framebuf *pufbuf, size_t off, size_t wantsize)
    155 {
    156 	size_t incr;
    157 	void *nd;
    158 
    159 	if (off <= pufbuf->len && pufbuf->len - off >= wantsize)
    160 		return 0;
    161 
    162 	for (incr = PUFBUF_INCRALLOC;
    163 	    pufbuf->len + incr < off + wantsize;
    164 	    incr += PUFBUF_INCRALLOC)
    165 		continue;
    166 
    167 	nd = realloc(pufbuf->buf, pufbuf->offset + incr);
    168 	if (nd == NULL)
    169 		return -1;
    170 
    171 	pufbuf->buf = nd;
    172 	pufbuf->len += incr;
    173 
    174 	return 0;
    175 }
    176 
    177 int
    178 puffs_framebuf_reserve_space(struct puffs_framebuf *pufbuf, size_t wantsize)
    179 {
    180 
    181 	return reservespace(pufbuf, pufbuf->offset, wantsize);
    182 }
    183 
    184 int
    185 puffs_framebuf_putdata(struct puffs_framebuf *pufbuf,
    186 	const void *data, size_t dlen)
    187 {
    188 
    189 	if (PUFBUF_REMAIN(pufbuf) < dlen)
    190 		if (puffs_framebuf_reserve_space(pufbuf, dlen) == -1)
    191 			return -1;
    192 
    193 	memcpy(pufbuf->buf + pufbuf->offset, data, dlen);
    194 	pufbuf->offset += dlen;
    195 
    196 	if (pufbuf->offset > pufbuf->maxoff)
    197 		pufbuf->maxoff = pufbuf->offset;
    198 
    199 	return 0;
    200 }
    201 
    202 int
    203 puffs_framebuf_putdata_atoff(struct puffs_framebuf *pufbuf, size_t offset,
    204 	const void *data, size_t dlen)
    205 {
    206 
    207 	if (reservespace(pufbuf, offset, dlen) == -1)
    208 		return -1;
    209 
    210 	memcpy(pufbuf->buf + offset, data, dlen);
    211 
    212 	if (offset + dlen > pufbuf->maxoff)
    213 		pufbuf->maxoff = offset + dlen;
    214 
    215 	return 0;
    216 }
    217 
    218 int
    219 puffs_framebuf_getdata(struct puffs_framebuf *pufbuf, void *data, size_t dlen)
    220 {
    221 
    222 	if (pufbuf->maxoff < pufbuf->offset + dlen) {
    223 		errno = ENOBUFS;
    224 		return -1;
    225 	}
    226 
    227 	memcpy(data, pufbuf->buf + pufbuf->offset, dlen);
    228 	pufbuf->offset += dlen;
    229 
    230 	return 0;
    231 }
    232 
    233 int
    234 puffs_framebuf_getdata_atoff(struct puffs_framebuf *pufbuf, size_t offset,
    235 	void *data, size_t dlen)
    236 {
    237 
    238 	if (pufbuf->maxoff < offset + dlen) {
    239 		errno = ENOBUFS;
    240 		return -1;
    241 	}
    242 
    243 	memcpy(data, pufbuf->buf + offset, dlen);
    244 	return 0;
    245 }
    246 
    247 size_t
    248 puffs_framebuf_telloff(struct puffs_framebuf *pufbuf)
    249 {
    250 
    251 	return pufbuf->offset;
    252 }
    253 
    254 size_t
    255 puffs_framebuf_tellsize(struct puffs_framebuf *pufbuf)
    256 {
    257 
    258 	return pufbuf->maxoff;
    259 }
    260 
    261 size_t
    262 puffs_framebuf_remaining(struct puffs_framebuf *pufbuf)
    263 {
    264 
    265 	return puffs_framebuf_tellsize(pufbuf) - puffs_framebuf_telloff(pufbuf);
    266 }
    267 
    268 int
    269 puffs_framebuf_seekset(struct puffs_framebuf *pufbuf, size_t newoff)
    270 {
    271 
    272 	if (reservespace(pufbuf, newoff, 0) == -1)
    273 		return -1;
    274 
    275 	pufbuf->offset = newoff;
    276 	return 0;
    277 }
    278 
    279 int
    280 puffs_framebuf_getwindow(struct puffs_framebuf *pufbuf, size_t winoff,
    281 	void **data, size_t *dlen)
    282 {
    283 	size_t winlen;
    284 
    285 #ifdef WINTESTING
    286 	winlen = MIN(*dlen, 32);
    287 #else
    288 	winlen = *dlen;
    289 #endif
    290 
    291 	if (reservespace(pufbuf, winoff, winlen) == -1)
    292 		return -1;
    293 
    294 	*data = pufbuf->buf + winoff;
    295 	if (pufbuf->maxoff < winoff + winlen)
    296 		pufbuf->maxoff = winoff + winlen;
    297 
    298 	return 0;
    299 }
    300 
    301 int
    302 puffs_framebuf_enqueue_cc(struct puffs_cc *pcc, int fd,
    303 	struct puffs_framebuf *pufbuf)
    304 {
    305 	struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
    306 	struct puffs_fctrl_io *fio;
    307 
    308 	fio = getfiobyfd(pu, fd);
    309 	if (fio == NULL) {
    310 		errno = EINVAL;
    311 		return -1;
    312 	}
    313 
    314 	pufbuf->pcc = pcc;
    315 	pufbuf->fcb = NULL;
    316 	pufbuf->fcb_arg = NULL;
    317 
    318 	pufbuf->offset = 0;
    319 	pufbuf->istat |= ISTAT_NODESTROY;
    320 
    321 	TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
    322 
    323 	puffs_cc_yield(pcc);
    324 	if (pufbuf->rv) {
    325 		errno = pufbuf->rv;
    326 		return -1;
    327 	}
    328 
    329 	return 0;
    330 }
    331 
    332 int
    333 puffs_framebuf_enqueue_cb(struct puffs_usermount *pu, int fd,
    334 	struct puffs_framebuf *pufbuf, puffs_framebuf_cb fcb, void *arg)
    335 {
    336 	struct puffs_fctrl_io *fio;
    337 
    338 	fio = getfiobyfd(pu, fd);
    339 	if (fio == NULL) {
    340 		errno = EINVAL;
    341 		return -1;
    342 	}
    343 
    344 	pufbuf->pcc = NULL;
    345 	pufbuf->fcb = fcb;
    346 	pufbuf->fcb_arg = arg;
    347 
    348 	pufbuf->offset = 0;
    349 	pufbuf->istat |= ISTAT_NODESTROY;
    350 
    351 	TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
    352 
    353 	return 0;
    354 }
    355 
    356 int
    357 puffs_framebuf_enqueue_justsend(struct puffs_usermount *pu, int fd,
    358 	struct puffs_framebuf *pufbuf, int reply)
    359 {
    360 	struct puffs_fctrl_io *fio;
    361 
    362 	fio = getfiobyfd(pu, fd);
    363 	if (fio == NULL) {
    364 		errno = EINVAL;
    365 		return -1;
    366 	}
    367 
    368 	pufbuf->pcc = NULL;
    369 	pufbuf->fcb = NULL;
    370 	pufbuf->fcb_arg = NULL;
    371 
    372 	pufbuf->offset = 0;
    373 	pufbuf->istat |= ISTAT_NODESTROY;
    374 	if (!reply)
    375 		pufbuf->istat |= ISTAT_NOREPLY;
    376 
    377 	TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
    378 
    379 	return 0;
    380 }
    381 
    382 static struct puffs_framebuf *
    383 findbuf(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
    384 	struct puffs_fctrl_io *fio, struct puffs_framebuf *findme)
    385 {
    386 	struct puffs_framebuf *cand;
    387 
    388 	TAILQ_FOREACH(cand, &fio->res_qing, pfb_entries)
    389 		if (fctrl->cmpfb(pu, findme, cand))
    390 			break;
    391 
    392 	if (cand == NULL)
    393 		return NULL;
    394 
    395 	TAILQ_REMOVE(&fio->res_qing, cand, pfb_entries);
    396 	return cand;
    397 }
    398 
    399 static void
    400 moveinfo(struct puffs_framebuf *from, struct puffs_framebuf *to)
    401 {
    402 
    403 	assert(from->istat & ISTAT_INTERNAL);
    404 
    405 	/* migrate buffer */
    406 	free(to->buf);
    407 	to->buf = from->buf;
    408 	from->buf = NULL;
    409 
    410 	/* migrate buffer info */
    411 	to->len = from->len;
    412 	to->offset = from->offset;
    413 	to->maxoff = from->maxoff;
    414 }
    415 
    416 static int
    417 handle_input(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
    418 	struct puffs_fctrl_io *fio, struct puffs_putreq *ppr)
    419 {
    420 	struct puffs_framebuf *pufbuf, *appbuf;
    421 	int rv, complete;
    422 
    423 	for (;;) {
    424 		if ((pufbuf = fio->cur_in) == NULL) {
    425 			pufbuf = puffs_framebuf_make();
    426 			if (pufbuf == NULL)
    427 				return -1;
    428 			pufbuf->istat |= ISTAT_INTERNAL;
    429 			fio->cur_in = pufbuf;
    430 		}
    431 
    432 		complete = 0;
    433 		rv = fctrl->rfb(pu, pufbuf, fio->io_fd, &complete);
    434 
    435 		/* error */
    436 		if (rv) {
    437 			errno = rv;
    438 			return -1;
    439 		}
    440 
    441 		/* partial read, come back to fight another day */
    442 		if (complete == 0)
    443 			break;
    444 
    445 		/* else: full read, process */
    446 
    447 		appbuf = findbuf(pu, fctrl, fio, pufbuf);
    448 		if (appbuf == NULL) {
    449 			errno = ENOMSG;
    450 			return -1;
    451 		}
    452 
    453 		appbuf->istat &= ~ISTAT_NODESTROY;
    454 		moveinfo(pufbuf, appbuf);
    455 		if (appbuf->pcc) {
    456 			puffs_docc(appbuf->pcc, ppr);
    457 		} else if (appbuf->fcb) {
    458 			appbuf->fcb(pu, appbuf, appbuf->fcb_arg);
    459 		} else {
    460 			puffs_framebuf_destroy(appbuf);
    461 		}
    462 		puffs_framebuf_destroy(pufbuf);
    463 
    464 		/* hopeless romantics, here we go again */
    465 		fio->cur_in = NULL;
    466 	}
    467 
    468 	return rv;
    469 }
    470 
    471 static int
    472 handle_output(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
    473 	struct puffs_fctrl_io *fio)
    474 {
    475 	struct puffs_framebuf *pufbuf, *pufbuf_next;
    476 	int rv, complete;
    477 
    478 	for (pufbuf = TAILQ_FIRST(&fio->snd_qing);
    479 	    pufbuf;
    480 	    pufbuf = pufbuf_next) {
    481 		complete = 0;
    482 		pufbuf_next = TAILQ_NEXT(pufbuf, pfb_entries);
    483 		rv = fctrl->wfb(pu, pufbuf, fio->io_fd, &complete);
    484 
    485 		if (rv) {
    486 			TAILQ_REMOVE(&fio->snd_qing, pufbuf, pfb_entries);
    487 			pufbuf->rv = rv;
    488 			errno = rv;
    489 			return -1;
    490 		}
    491 
    492 		/* partial write */
    493 		if (complete == 0)
    494 			return 0;
    495 
    496 		/* else, complete write */
    497 		TAILQ_REMOVE(&fio->snd_qing, pufbuf, pfb_entries);
    498 
    499 		if ((pufbuf->istat & ISTAT_NOREPLY) == 0) {
    500 			TAILQ_INSERT_TAIL(&fio->res_qing, pufbuf,
    501 			    pfb_entries);
    502 		} else {
    503 			pufbuf->istat &= ~ISTAT_NODESTROY;
    504 			puffs_framebuf_destroy(pufbuf);
    505 		}
    506 	}
    507 
    508 	return 0;
    509 }
    510 
    511 int
    512 puffs_framebuf_addfd(struct puffs_usermount *pu, int fd)
    513 {
    514 	struct puffs_framectrl *pfctrl = pu->pu_framectrl;
    515 	struct puffs_fctrl_io *fio;
    516 	struct kevent kev[2];
    517 	struct kevent *newevs;
    518 	int rv;
    519 
    520 	newevs = realloc(pfctrl->evs, (2*pfctrl->nevs+1)*sizeof(struct kevent));
    521 	if (newevs == NULL)
    522 		return -1;
    523 	pfctrl->evs = newevs;
    524 
    525 	newevs = realloc(pfctrl->ch_evs, pfctrl->nevs * sizeof(struct kevent));
    526 	if (newevs == NULL)
    527 		return -1;
    528 	pfctrl->ch_evs = newevs;
    529 
    530 	fio = malloc(sizeof(struct puffs_fctrl_io));
    531 	if (fio == NULL)
    532 		return -1;
    533 
    534 	EV_SET(&kev[0], fd, EVFILT_READ, EV_ADD, 0, 0, (intptr_t)fio);
    535 	EV_SET(&kev[1], fd, EVFILT_WRITE, EV_ADD|EV_DISABLE, 0,0,(intptr_t)fio);
    536 	rv = kevent(pfctrl->kq, kev, 2, NULL, 0, NULL);
    537 	if (rv == -1) {
    538 		free(fio);
    539 		return -1;
    540 	}
    541 
    542 	fio->io_fd = fd;
    543 	fio->cur_in = NULL;
    544 	fio->wrstat = 0;
    545 	TAILQ_INIT(&fio->snd_qing);
    546 	TAILQ_INIT(&fio->res_qing);
    547 
    548 	LIST_INSERT_HEAD(&pfctrl->fb_ios, fio, fio_entries);
    549 	pfctrl->nevs++;
    550 
    551 	return 0;
    552 }
    553 
    554 static int
    555 removefio(struct puffs_usermount *pu, struct puffs_fctrl_io *fio)
    556 {
    557 	struct puffs_framectrl *pfctrl = pu->pu_framectrl;
    558 	struct puffs_framebuf *pufbuf;
    559 	struct kevent kev[2];
    560 
    561 	/* found, now remove */
    562 	EV_SET(&kev[0], fio->io_fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
    563 	EV_SET(&kev[1], fio->io_fd, EVFILT_WRITE, EV_DELETE, 0, 0, 0);
    564 	(void) kevent(pfctrl->kq, kev, 2, NULL, 0, NULL);
    565 
    566 	LIST_REMOVE(fio, fio_entries);
    567 
    568 	/* free buffers */
    569 	while ((pufbuf = TAILQ_FIRST(&fio->snd_qing)) != NULL) {
    570 		TAILQ_REMOVE(&fio->snd_qing, pufbuf, pfb_entries);
    571 		puffs_framebuf_destroy(pufbuf);
    572 	}
    573 	while ((pufbuf = TAILQ_FIRST(&fio->res_qing)) != NULL) {
    574 		TAILQ_REMOVE(&fio->res_qing, pufbuf, pfb_entries);
    575 		puffs_framebuf_destroy(pufbuf);
    576 	}
    577 	if (fio->cur_in)
    578 		puffs_framebuf_destroy(fio->cur_in);
    579 	free(fio);
    580 
    581 	/* don't bother with realloc */
    582 	pfctrl->nevs--;
    583 
    584 	return 0;
    585 
    586 }
    587 
    588 int
    589 puffs_framebuf_removefd(struct puffs_usermount *pu, int fd)
    590 {
    591 	struct puffs_fctrl_io *fio;
    592 
    593 	fio = getfiobyfd(pu, fd);
    594 	if (fio == NULL) {
    595 		errno = ENXIO;
    596 		return -1;
    597 	}
    598 
    599 	return removefio(pu, fio);
    600 }
    601 
    602 int
    603 puffs_framebuf_eventloop(struct puffs_usermount *pu, int *io_fds, size_t nfds,
    604 	puffs_framebuf_readframe_fn rfb, puffs_framebuf_writeframe_fn wfb,
    605 	puffs_framebuf_respcmp_fn cmpfb,
    606 	puffs_framebuf_loop_fn lfb)
    607 {
    608 	struct puffs_getreq *pgr = NULL;
    609 	struct puffs_putreq *ppr = NULL;
    610 	struct puffs_framectrl *pfctrl = NULL;
    611 	struct puffs_fctrl_io *fio;
    612 	struct kevent kev;
    613 	struct kevent *curev;
    614 	size_t nchanges;
    615 	int puffsfd, sverrno;
    616 	int ndone;
    617 
    618 	assert(puffs_getstate(pu) >= PUFFS_STATE_RUNNING);
    619 
    620 	pgr = puffs_req_makeget(pu, puffs_getmaxreqlen(pu), 0);
    621 	if (pgr == NULL)
    622 		goto out;
    623 
    624 	ppr = puffs_req_makeput(pu);
    625 	if (ppr == NULL)
    626 		goto out;
    627 
    628 	pfctrl = malloc(sizeof(struct puffs_framectrl));
    629 	if (pfctrl == NULL)
    630 		goto out;
    631 	pfctrl->rfb = rfb;
    632 	pfctrl->wfb = wfb;
    633 	pfctrl->cmpfb = cmpfb;
    634 	pfctrl->evs = malloc(sizeof(struct kevent));
    635 	if (pfctrl->evs == NULL)
    636 		goto out;
    637 	pfctrl->ch_evs = NULL;
    638 	pfctrl->nevs = 1;
    639 	pfctrl->kq = kqueue();
    640 	if (pfctrl->kq == -1)
    641 		goto out;
    642 	LIST_INIT(&pfctrl->fb_ios);
    643 	pu->pu_framectrl = pfctrl;
    644 
    645 	for (; nfds--; io_fds++)
    646 		if (puffs_framebuf_addfd(pu, *io_fds) == -1)
    647 			goto out;
    648 
    649 	puffsfd = puffs_getselectable(pu);
    650 	EV_SET(&kev, puffsfd, EVFILT_READ, EV_ADD, 0, 0, 0);
    651 	if (kevent(pfctrl->kq, &kev, 1, NULL, 0, NULL) == -1)
    652 		goto out;
    653 
    654 	while (puffs_getstate(pu) != PUFFS_STATE_UNMOUNTED) {
    655 		if (lfb)
    656 			lfb(pu);
    657 
    658 		/*
    659 		 * Build list of which to enable/disable in writecheck.
    660 		 * Don't bother worrying about O(n) for now.
    661 		 */
    662 		nchanges = 0;
    663 		LIST_FOREACH(fio, &pfctrl->fb_ios, fio_entries) {
    664 			/*
    665 			 * Try to write out everything to avoid the
    666 			 * need for enabling EVFILT_WRITE.  The likely
    667 			 * case is that we can fit everything into the
    668 			 * socket buffer.
    669 			 */
    670 			if (handle_output(pu, pfctrl, fio) == -1)
    671 				goto out;
    672 
    673 			assert((EN_WRITE(fio) && RM_WRITE(fio)) == 0);
    674 			if (EN_WRITE(fio)) {
    675 				EV_SET(&pfctrl->ch_evs[nchanges], fio->io_fd,
    676 				    EVFILT_WRITE, EV_ENABLE, 0, 0,
    677 				    (uintptr_t)fio);
    678 				fio->wrstat = 1; /* XXX: not before call */
    679 				nchanges++;
    680 			}
    681 			if (RM_WRITE(fio)) {
    682 				EV_SET(&pfctrl->ch_evs[nchanges], fio->io_fd,
    683 				    EVFILT_WRITE, EV_DISABLE, 0, 0,
    684 				    (uintptr_t)fio);
    685 				fio->wrstat = 0; /* XXX: not before call */
    686 				nchanges++;
    687 			}
    688 		}
    689 
    690 		ndone = kevent(pfctrl->kq, pfctrl->ch_evs, nchanges,
    691 		    pfctrl->evs, pfctrl->nevs, NULL);
    692 		if (ndone == -1)
    693 			goto out;
    694 
    695 		/* XXX: handle errors */
    696 
    697 		/* iterate over the results */
    698 		for (curev = pfctrl->evs; ndone--; curev++) {
    699 			/* get & possibly dispatch events from kernel */
    700 			if (curev->ident == puffsfd) {
    701 				if (puffs_req_handle(pgr, ppr, 0) == -1)
    702 					goto out;
    703 				continue;
    704 			}
    705 
    706 			if (curev->filter == EVFILT_READ) {
    707 				if (handle_input(pu, pfctrl,
    708 				    (void *)curev->udata, ppr) == -1)
    709 					goto out;
    710 			}
    711 
    712 			if (curev->filter == EVFILT_WRITE) {
    713 				if (handle_output(pu, pfctrl,
    714 				    (void *)curev->udata) == -1)
    715 					goto out;
    716 			}
    717 		}
    718 
    719 		/* stuff all replies from both of the above into kernel */
    720 		if (puffs_req_putput(ppr) == -1)
    721 			goto out;
    722 		puffs_req_resetput(ppr);
    723 	}
    724 	errno = 0;
    725 
    726  out:
    727 	/* store the real error for a while */
    728 	sverrno = errno;
    729 
    730 	if (pfctrl) {
    731 		while ((fio = LIST_FIRST(&pfctrl->fb_ios)) != NULL)
    732 			removefio(pu, fio);
    733 		pu->pu_framectrl = NULL;
    734 		free(pfctrl->evs);
    735 		close(pfctrl->kq); /* takes care of puffsfd */
    736 		free(pfctrl);
    737 	}
    738 
    739 	if (ppr)
    740 		puffs_req_destroyput(ppr);
    741 	if (pgr)
    742 		puffs_req_destroyget(pgr);
    743 
    744 	errno = sverrno;
    745 	if (errno)
    746 		return -1;
    747 	else
    748 		return 0;
    749 }
    750