Home | History | Annotate | Line # | Download | only in libpuffs
framebuf.c revision 1.13
      1 /*	$NetBSD: framebuf.c,v 1.13 2007/05/20 19:56:56 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.13 2007/05/20 19:56:56 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_framev_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 */
     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 #define ISTAT_ONQUEUE	ISTAT_NODESTROY	/* alias */
     71 
     72 #define PUFBUF_INCRALLOC 4096
     73 #define PUFBUF_REMAIN(p) (p->len - p->offset)
     74 
     75 static struct puffs_fctrl_io *
     76 getfiobyfd(struct puffs_usermount *pu, int fd)
     77 {
     78 	struct puffs_framectrl *pfctrl = &pu->pu_framectrl;
     79 	struct puffs_fctrl_io *fio;
     80 
     81 	LIST_FOREACH(fio, &pfctrl->fb_ios, fio_entries)
     82 		if (fio->io_fd == fd)
     83 			return fio;
     84 	return NULL;
     85 }
     86 
     87 struct puffs_framebuf *
     88 puffs_framebuf_make()
     89 {
     90 	struct puffs_framebuf *pufbuf;
     91 
     92 	pufbuf = malloc(sizeof(struct puffs_framebuf));
     93 	if (pufbuf == NULL)
     94 		return NULL;
     95 	memset(pufbuf, 0, sizeof(struct puffs_framebuf));
     96 
     97 	pufbuf->buf = malloc(PUFBUF_INCRALLOC);
     98 	if (pufbuf->buf == NULL) {
     99 		free(pufbuf);
    100 		return NULL;
    101 	}
    102 	pufbuf->len = PUFBUF_INCRALLOC;
    103 
    104 	puffs_framebuf_recycle(pufbuf);
    105 	return pufbuf;
    106 }
    107 
    108 void
    109 puffs_framebuf_destroy(struct puffs_framebuf *pufbuf)
    110 {
    111 
    112 	assert((pufbuf->istat & ISTAT_NODESTROY) == 0);
    113 
    114 	free(pufbuf->buf);
    115 	free(pufbuf);
    116 }
    117 
    118 void
    119 puffs_framebuf_recycle(struct puffs_framebuf *pufbuf)
    120 {
    121 
    122 	assert((pufbuf->istat & ISTAT_NODESTROY) == 0);
    123 
    124 	pufbuf->offset = 0;
    125 	pufbuf->maxoff = 0;
    126 	pufbuf->istat = 0;
    127 }
    128 
    129 static int
    130 reservespace(struct puffs_framebuf *pufbuf, size_t off, size_t wantsize)
    131 {
    132 	size_t incr;
    133 	void *nd;
    134 
    135 	if (off <= pufbuf->len && pufbuf->len - off >= wantsize)
    136 		return 0;
    137 
    138 	for (incr = PUFBUF_INCRALLOC;
    139 	    pufbuf->len + incr < off + wantsize;
    140 	    incr += PUFBUF_INCRALLOC)
    141 		continue;
    142 
    143 	nd = realloc(pufbuf->buf, pufbuf->offset + incr);
    144 	if (nd == NULL)
    145 		return -1;
    146 
    147 	pufbuf->buf = nd;
    148 	pufbuf->len += incr;
    149 
    150 	return 0;
    151 }
    152 
    153 int
    154 puffs_framebuf_reserve_space(struct puffs_framebuf *pufbuf, size_t wantsize)
    155 {
    156 
    157 	return reservespace(pufbuf, pufbuf->offset, wantsize);
    158 }
    159 
    160 int
    161 puffs_framebuf_putdata(struct puffs_framebuf *pufbuf,
    162 	const void *data, size_t dlen)
    163 {
    164 
    165 	if (PUFBUF_REMAIN(pufbuf) < dlen)
    166 		if (puffs_framebuf_reserve_space(pufbuf, dlen) == -1)
    167 			return -1;
    168 
    169 	memcpy(pufbuf->buf + pufbuf->offset, data, dlen);
    170 	pufbuf->offset += dlen;
    171 
    172 	if (pufbuf->offset > pufbuf->maxoff)
    173 		pufbuf->maxoff = pufbuf->offset;
    174 
    175 	return 0;
    176 }
    177 
    178 int
    179 puffs_framebuf_putdata_atoff(struct puffs_framebuf *pufbuf, size_t offset,
    180 	const void *data, size_t dlen)
    181 {
    182 
    183 	if (reservespace(pufbuf, offset, dlen) == -1)
    184 		return -1;
    185 
    186 	memcpy(pufbuf->buf + offset, data, dlen);
    187 
    188 	if (offset + dlen > pufbuf->maxoff)
    189 		pufbuf->maxoff = offset + dlen;
    190 
    191 	return 0;
    192 }
    193 
    194 int
    195 puffs_framebuf_getdata(struct puffs_framebuf *pufbuf, void *data, size_t dlen)
    196 {
    197 
    198 	if (pufbuf->maxoff < pufbuf->offset + dlen) {
    199 		errno = ENOBUFS;
    200 		return -1;
    201 	}
    202 
    203 	memcpy(data, pufbuf->buf + pufbuf->offset, dlen);
    204 	pufbuf->offset += dlen;
    205 
    206 	return 0;
    207 }
    208 
    209 int
    210 puffs_framebuf_getdata_atoff(struct puffs_framebuf *pufbuf, size_t offset,
    211 	void *data, size_t dlen)
    212 {
    213 
    214 	if (pufbuf->maxoff < offset + dlen) {
    215 		errno = ENOBUFS;
    216 		return -1;
    217 	}
    218 
    219 	memcpy(data, pufbuf->buf + offset, dlen);
    220 	return 0;
    221 }
    222 
    223 size_t
    224 puffs_framebuf_telloff(struct puffs_framebuf *pufbuf)
    225 {
    226 
    227 	return pufbuf->offset;
    228 }
    229 
    230 size_t
    231 puffs_framebuf_tellsize(struct puffs_framebuf *pufbuf)
    232 {
    233 
    234 	return pufbuf->maxoff;
    235 }
    236 
    237 size_t
    238 puffs_framebuf_remaining(struct puffs_framebuf *pufbuf)
    239 {
    240 
    241 	return puffs_framebuf_tellsize(pufbuf) - puffs_framebuf_telloff(pufbuf);
    242 }
    243 
    244 int
    245 puffs_framebuf_seekset(struct puffs_framebuf *pufbuf, size_t newoff)
    246 {
    247 
    248 	if (reservespace(pufbuf, newoff, 0) == -1)
    249 		return -1;
    250 
    251 	pufbuf->offset = newoff;
    252 	return 0;
    253 }
    254 
    255 int
    256 puffs_framebuf_getwindow(struct puffs_framebuf *pufbuf, size_t winoff,
    257 	void **data, size_t *dlen)
    258 {
    259 	size_t winlen;
    260 
    261 #ifdef WINTESTING
    262 	winlen = MIN(*dlen, 32);
    263 #else
    264 	winlen = *dlen;
    265 #endif
    266 
    267 	if (reservespace(pufbuf, winoff, winlen) == -1)
    268 		return -1;
    269 
    270 	*data = pufbuf->buf + winoff;
    271 	if (pufbuf->maxoff < winoff + winlen)
    272 		pufbuf->maxoff = winoff + winlen;
    273 
    274 	return 0;
    275 }
    276 
    277 static void
    278 errnotify(struct puffs_framebuf *pufbuf, int error)
    279 {
    280 
    281 	pufbuf->rv = error;
    282 	if (pufbuf->pcc) {
    283 		puffs_goto(pufbuf->pcc);
    284 	} else if (pufbuf->fcb) {
    285 		pufbuf->istat &= ~ISTAT_NODESTROY;
    286 		pufbuf->fcb(puffs_cc_getusermount(pufbuf->pcc),
    287 		    pufbuf, pufbuf->fcb_arg, error);
    288 	} else {
    289 		pufbuf->istat &= ~ISTAT_NODESTROY;
    290 		puffs_framebuf_destroy(pufbuf);
    291 	}
    292 }
    293 
    294 #define GETFIO(fd)							\
    295 do {									\
    296 	fio = getfiobyfd(pu, fd);					\
    297 	if (fio == NULL) {						\
    298 		errno = EINVAL;						\
    299 		return -1;						\
    300 	}								\
    301 	if (fio->stat & FIO_WRGONE) {					\
    302 		errno = ESHUTDOWN;					\
    303 		return -1;						\
    304 	}								\
    305 } while (/*CONSTCOND*/0)
    306 
    307 int
    308 puffs_framev_enqueue_cc(struct puffs_cc *pcc, int fd,
    309 	struct puffs_framebuf *pufbuf)
    310 {
    311 	struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
    312 	struct puffs_fctrl_io *fio;
    313 
    314 	/*
    315 	 * Technically we shouldn't allow this is RDGONE, but it's
    316 	 * difficult to trap write close without allowing writes.
    317 	 * And besides, there's probably a disconnect sequence in
    318 	 * the protocol, so unexpectedly getting a closed fd is
    319 	 * most likely an error condition.
    320 	 */
    321 	GETFIO(fd);
    322 
    323 	pufbuf->pcc = pcc;
    324 	pufbuf->fcb = NULL;
    325 	pufbuf->fcb_arg = NULL;
    326 
    327 	pufbuf->offset = 0;
    328 	pufbuf->istat |= ISTAT_NODESTROY;
    329 
    330 	TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
    331 
    332 	puffs_cc_yield(pcc);
    333 	if (pufbuf->rv) {
    334 		pufbuf->istat &= ~ISTAT_NODESTROY;
    335 		errno = pufbuf->rv;
    336 		return -1;
    337 	}
    338 
    339 	return 0;
    340 }
    341 
    342 int
    343 puffs_framev_enqueue_cb(struct puffs_usermount *pu, int fd,
    344 	struct puffs_framebuf *pufbuf, puffs_framev_cb fcb, void *arg)
    345 {
    346 	struct puffs_fctrl_io *fio;
    347 
    348 	/* see enqueue_cc */
    349 	GETFIO(fd);
    350 
    351 	pufbuf->pcc = NULL;
    352 	pufbuf->fcb = fcb;
    353 	pufbuf->fcb_arg = arg;
    354 
    355 	pufbuf->offset = 0;
    356 	pufbuf->istat |= ISTAT_NODESTROY;
    357 
    358 	TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
    359 
    360 	return 0;
    361 }
    362 
    363 int
    364 puffs_framev_enqueue_justsend(struct puffs_usermount *pu, int fd,
    365 	struct puffs_framebuf *pufbuf, int reply)
    366 {
    367 	struct puffs_fctrl_io *fio;
    368 
    369 	GETFIO(fd);
    370 
    371 	pufbuf->pcc = NULL;
    372 	pufbuf->fcb = NULL;
    373 	pufbuf->fcb_arg = NULL;
    374 
    375 	pufbuf->offset = 0;
    376 	pufbuf->istat |= ISTAT_NODESTROY;
    377 	if (!reply)
    378 		pufbuf->istat |= ISTAT_NOREPLY;
    379 
    380 	TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
    381 
    382 	return 0;
    383 }
    384 
    385 /*
    386  * this beauty shall remain undocumented for now
    387  */
    388 int
    389 puffs_framev_framebuf_ccpromote(struct puffs_framebuf *pufbuf,
    390 	struct puffs_cc *pcc)
    391 {
    392 
    393 	if ((pufbuf->istat & ISTAT_ONQUEUE) == 0) {
    394 		errno = EBUSY;
    395 		return -1;
    396 	}
    397 
    398 	pufbuf->pcc = pcc;
    399 	pufbuf->fcb = NULL;
    400 	pufbuf->fcb_arg = NULL;
    401 	pufbuf->istat &= ~ISTAT_NOREPLY;
    402 
    403 	puffs_cc_yield(pcc);
    404 
    405 	return 0;
    406 }
    407 
    408 static struct puffs_framebuf *
    409 findbuf(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
    410 	struct puffs_fctrl_io *fio, struct puffs_framebuf *findme)
    411 {
    412 	struct puffs_framebuf *cand;
    413 
    414 	TAILQ_FOREACH(cand, &fio->res_qing, pfb_entries)
    415 		if (fctrl->cmpfb(pu, findme, cand) == 0)
    416 			break;
    417 
    418 	if (cand == NULL)
    419 		return NULL;
    420 
    421 	TAILQ_REMOVE(&fio->res_qing, cand, pfb_entries);
    422 	return cand;
    423 }
    424 
    425 static void
    426 moveinfo(struct puffs_framebuf *from, struct puffs_framebuf *to)
    427 {
    428 
    429 	assert(from->istat & ISTAT_INTERNAL);
    430 
    431 	/* migrate buffer */
    432 	free(to->buf);
    433 	to->buf = from->buf;
    434 	from->buf = NULL;
    435 
    436 	/* migrate buffer info */
    437 	to->len = from->len;
    438 	to->offset = from->offset;
    439 	to->maxoff = from->maxoff;
    440 }
    441 
    442 void
    443 puffs_framev_input(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
    444 	struct puffs_fctrl_io *fio, struct puffs_putreq *ppr)
    445 {
    446 	struct puffs_framebuf *pufbuf, *appbuf;
    447 	int rv, complete;
    448 
    449 	if (fio->stat & FIO_DEAD)
    450 		return;
    451 
    452 	for (;;) {
    453 		if ((pufbuf = fio->cur_in) == NULL) {
    454 			pufbuf = puffs_framebuf_make();
    455 			if (pufbuf == NULL)
    456 				return;
    457 			pufbuf->istat |= ISTAT_INTERNAL;
    458 			fio->cur_in = pufbuf;
    459 		}
    460 
    461 		complete = 0;
    462 		rv = fctrl->rfb(pu, pufbuf, fio->io_fd, &complete);
    463 
    464 		/* error */
    465 		if (rv) {
    466 			puffs_framev_readclose(pu, fio, rv);
    467 			return;
    468 		}
    469 
    470 		/* partial read, come back to fight another day */
    471 		if (complete == 0)
    472 			break;
    473 
    474 		/* else: full read, process */
    475 
    476 		appbuf = findbuf(pu, fctrl, fio, pufbuf);
    477 
    478 		/* XXX: error delivery? */
    479 		if (appbuf == NULL) {
    480 			/* errno = ENOMSG; */
    481 			return;
    482 		}
    483 
    484 		appbuf->istat &= ~ISTAT_NODESTROY;
    485 		moveinfo(pufbuf, appbuf);
    486 		if (appbuf->pcc) {
    487 			puffs_docc(appbuf->pcc, ppr);
    488 		} else if (appbuf->fcb) {
    489 			appbuf->fcb(pu, appbuf, appbuf->fcb_arg, 0);
    490 		} else {
    491 			puffs_framebuf_destroy(appbuf);
    492 		}
    493 		puffs_framebuf_destroy(pufbuf);
    494 
    495 		/* hopeless romantics, here we go again */
    496 		fio->cur_in = NULL;
    497 	}
    498 }
    499 
    500 int
    501 puffs_framev_output(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
    502 	struct puffs_fctrl_io *fio)
    503 {
    504 	struct puffs_framebuf *pufbuf, *pufbuf_next;
    505 	int rv, complete, error;
    506 
    507 	if (fio->stat & FIO_DEAD)
    508 		return 0;
    509 
    510 	for (pufbuf = TAILQ_FIRST(&fio->snd_qing), error = 0;
    511 	    pufbuf;
    512 	    pufbuf = pufbuf_next) {
    513 		complete = 0;
    514 		pufbuf_next = TAILQ_NEXT(pufbuf, pfb_entries);
    515 		rv = fctrl->wfb(pu, pufbuf, fio->io_fd, &complete);
    516 
    517 		if (rv) {
    518 			puffs_framev_writeclose(pu, fio, rv);
    519 			error = 1;
    520 			break;
    521 		}
    522 
    523 		/* partial write */
    524 		if (complete == 0)
    525 			return error;
    526 
    527 		/* else, complete write */
    528 		TAILQ_REMOVE(&fio->snd_qing, pufbuf, pfb_entries);
    529 
    530 		/* can't wait for result if we can't read */
    531 		if (fio->stat & FIO_RDGONE) {
    532 			errnotify(pufbuf, ENXIO);
    533 			error = 1;
    534 
    535 		} else  if ((pufbuf->istat & ISTAT_NOREPLY) == 0) {
    536 			TAILQ_INSERT_TAIL(&fio->res_qing, pufbuf,
    537 			    pfb_entries);
    538 		} else {
    539 			pufbuf->istat &= ~ISTAT_NODESTROY;
    540 			puffs_framebuf_destroy(pufbuf);
    541 		}
    542 
    543 		/* omstart! */
    544 	}
    545 
    546 	return error;
    547 }
    548 
    549 int
    550 puffs_framev_addfd(struct puffs_usermount *pu, int fd)
    551 {
    552 	struct puffs_framectrl *pfctrl = &pu->pu_framectrl;
    553 	struct puffs_fctrl_io *fio;
    554 	struct kevent kev[2];
    555 	struct kevent *newevs;
    556 	size_t nfds;
    557 	int rv;
    558 
    559 	nfds = pfctrl->nfds+1;
    560 	newevs = realloc(pfctrl->evs, (2*nfds+1) * sizeof(struct kevent));
    561 	if (newevs == NULL)
    562 		return -1;
    563 	pfctrl->evs = newevs;
    564 
    565 	fio = malloc(sizeof(struct puffs_fctrl_io));
    566 	if (fio == NULL)
    567 		return -1;
    568 
    569 	if (pu->pu_state & PU_INLOOP) {
    570 		EV_SET(&kev[0], fd, EVFILT_READ, EV_ADD, 0, 0, (intptr_t)fio);
    571 		EV_SET(&kev[1], fd, EVFILT_WRITE, EV_ADD|EV_DISABLE,
    572 		    0, 0, (intptr_t)fio);
    573 		rv = kevent(pu->pu_kq, kev, 2, NULL, 0, NULL);
    574 		if (rv == -1) {
    575 			free(fio);
    576 			return -1;
    577 		}
    578 	}
    579 
    580 	fio->io_fd = fd;
    581 	fio->cur_in = NULL;
    582 	fio->stat = 0;
    583 	TAILQ_INIT(&fio->snd_qing);
    584 	TAILQ_INIT(&fio->res_qing);
    585 
    586 	LIST_INSERT_HEAD(&pfctrl->fb_ios, fio, fio_entries);
    587 	pfctrl->nfds = nfds;
    588 
    589 	return 0;
    590 }
    591 
    592 void
    593 puffs_framev_readclose(struct puffs_usermount *pu,
    594 	struct puffs_fctrl_io *fio, int error)
    595 {
    596 	struct puffs_framebuf *pufbuf;
    597 	struct kevent kev;
    598 	int notflag;
    599 
    600 	if (fio->stat & FIO_RDGONE || fio->stat & FIO_DEAD)
    601 		return;
    602 	fio->stat |= FIO_RDGONE;
    603 
    604 	if (fio->cur_in) {
    605 		puffs_framebuf_destroy(fio->cur_in);
    606 		fio->cur_in = NULL;
    607 	}
    608 
    609 	while ((pufbuf = TAILQ_FIRST(&fio->res_qing)) != NULL) {
    610 		TAILQ_REMOVE(&fio->res_qing, pufbuf, pfb_entries);
    611 		errnotify(pufbuf, error);
    612 	}
    613 
    614 	EV_SET(&kev, fio->io_fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
    615 	(void) kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
    616 
    617 	notflag = PUFFS_FBGONE_READ;
    618 	if (fio->stat & FIO_WRGONE)
    619 		notflag |= PUFFS_FBGONE_WRITE;
    620 
    621 	pu->pu_framectrl.fdnotfn(pu, fio->io_fd, notflag);
    622 }
    623 
    624 void
    625 puffs_framev_writeclose(struct puffs_usermount *pu,
    626 	struct puffs_fctrl_io *fio, int error)
    627 {
    628 	struct puffs_framebuf *pufbuf;
    629 	struct kevent kev;
    630 	int notflag;
    631 
    632 	if (fio->stat & FIO_WRGONE || fio->stat & FIO_DEAD)
    633 		return;
    634 	fio->stat |= FIO_WRGONE;
    635 
    636 	while ((pufbuf = TAILQ_FIRST(&fio->snd_qing)) != NULL) {
    637 		TAILQ_REMOVE(&fio->snd_qing, pufbuf, pfb_entries);
    638 		errnotify(pufbuf, error);
    639 	}
    640 
    641 	EV_SET(&kev, fio->io_fd, EVFILT_WRITE, EV_DELETE, 0, 0, 0);
    642 	(void) kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
    643 
    644 	notflag = PUFFS_FBGONE_WRITE;
    645 	if (fio->stat & FIO_RDGONE)
    646 		notflag |= PUFFS_FBGONE_READ;
    647 
    648 	pu->pu_framectrl.fdnotfn(pu, fio->io_fd, notflag);
    649 }
    650 
    651 static int
    652 removefio(struct puffs_usermount *pu, struct puffs_fctrl_io *fio, int error)
    653 {
    654 	struct puffs_framectrl *pfctrl = &pu->pu_framectrl;
    655 
    656 	LIST_REMOVE(fio, fio_entries);
    657 	if (pu->pu_state & PU_INLOOP) {
    658 		puffs_framev_readclose(pu, fio, error);
    659 		puffs_framev_writeclose(pu, fio, error);
    660 	}
    661 
    662 	/* don't bother with realloc */
    663 	pfctrl->nfds--;
    664 
    665 	/* don't free us yet, might have some references in event arrays */
    666 	fio->stat |= FIO_DEAD;
    667 	LIST_INSERT_HEAD(&pfctrl->fb_ios_rmlist, fio, fio_entries);
    668 
    669 	return 0;
    670 
    671 }
    672 
    673 int
    674 puffs_framev_removefd(struct puffs_usermount *pu, int fd, int error)
    675 {
    676 	struct puffs_fctrl_io *fio;
    677 
    678 	fio = getfiobyfd(pu, fd);
    679 	if (fio == NULL) {
    680 		errno = ENXIO;
    681 		return -1;
    682 	}
    683 
    684 	return removefio(pu, fio, error ? error : ECONNRESET);
    685 }
    686 
    687 static void
    688 defaultnot(struct puffs_usermount *pu, int fd, int what)
    689 {
    690 
    691 	if (PUFFS_FBGONE_BOTH(what))
    692 		(void) puffs_framev_removefd(pu, fd, ECONNRESET);
    693 }
    694 
    695 void
    696 puffs_framev_unmountonclose(struct puffs_usermount *pu, int fd, int what)
    697 {
    698 
    699 	/* XXX & X: unmount is non-sensible */
    700 	defaultnot(pu, fd, what);
    701 	if (PUFFS_FBGONE_BOTH(what))
    702 		PU_SETSTATE(pu, PUFFS_STATE_UNMOUNTED);
    703 }
    704 
    705 void
    706 puffs_framev_init(struct puffs_usermount *pu,
    707 	puffs_framev_readframe_fn rfb, puffs_framev_writeframe_fn wfb,
    708 	puffs_framev_cmpframe_fn cmpfb, puffs_framev_fdnotify_fn fdnotfn)
    709 {
    710 	struct puffs_framectrl *pfctrl;
    711 
    712 	pfctrl = &pu->pu_framectrl;
    713 	pfctrl->rfb = rfb;
    714 	pfctrl->wfb = wfb;
    715 	pfctrl->cmpfb = cmpfb;
    716 	if (fdnotfn)
    717 		pfctrl->fdnotfn = fdnotfn;
    718 	else
    719 		pfctrl->fdnotfn = defaultnot;
    720 }
    721 
    722 void
    723 puffs_framev_exit(struct puffs_usermount *pu)
    724 {
    725 	struct puffs_framectrl *pfctrl = &pu->pu_framectrl;
    726 	struct puffs_fctrl_io *fio;
    727 
    728 	while ((fio = LIST_FIRST(&pfctrl->fb_ios)) != NULL)
    729 		removefio(pu, fio, ENXIO);
    730 	free(pfctrl->evs);
    731 
    732 	/* closing pu->pu_kq takes care of puffsfd */
    733 }
    734