Home | History | Annotate | Line # | Download | only in libpuffs
framebuf.c revision 1.3
      1 /*	$NetBSD: framebuf.c,v 1.3 2007/05/06 10:54:41 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.3 2007/05/06 10:54:41 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 
     45 #include "puffs_priv.h"
     46 
     47 struct puffs_framebuf {
     48 	struct puffs_cc *pcc;	/* pcc to continue with */
     49 	/* OR */
     50 	puffs_framebuf_cb fcb;	/* non-blocking callback */
     51 	void *fcb_arg;		/* argument for previous */
     52 
     53 	uint8_t *buf;		/* buffer base */
     54 	size_t len;		/* total length */
     55 
     56 	size_t offset;		/* cursor, telloff() */
     57 	size_t maxoff;		/* maximum offset for data, tellsize() */
     58 
     59 	volatile int rv;	/* errno value for pcc framebufs */
     60 
     61 	int	istat;
     62 
     63 	TAILQ_ENTRY(puffs_framebuf) pfb_entries;
     64 };
     65 #define ISTAT_NODESTROY	0x01	/* indestructible by framebuf_destroy() */
     66 #define ISTAT_INTERNAL	0x02	/* never leaves library			*/
     67 #define ISTAT_NOREPLY	0x04	/* nuke after sending 			*/
     68 
     69 struct puffs_framectrl {
     70 	puffs_framebuf_readframe_fn rfb;
     71 	puffs_framebuf_writeframe_fn wfb;
     72 	puffs_framebuf_respcmp_fn cmpfb;
     73 	int io_fd;
     74 
     75 	struct puffs_framebuf *cur_in;
     76 
     77 	TAILQ_HEAD(, puffs_framebuf) snd_qing;	/* queueing to be sent */
     78 	TAILQ_HEAD(, puffs_framebuf) res_qing;	/* q'ing for rescue */
     79 };
     80 
     81 #define PUFBUF_INCRALLOC 65536	/* 64k ought to be enough for anyone */
     82 #define PUFBUF_REMAIN(p) (p->len - p->offset)
     83 
     84 struct puffs_framebuf *
     85 puffs_framebuf_make()
     86 {
     87 	struct puffs_framebuf *pufbuf;
     88 
     89 	pufbuf = malloc(sizeof(struct puffs_framebuf));
     90 	if (pufbuf == NULL)
     91 		return NULL;
     92 	memset(pufbuf, 0, sizeof(struct puffs_framebuf));
     93 
     94 	pufbuf->buf = malloc(PUFBUF_INCRALLOC);
     95 	pufbuf->len = PUFBUF_INCRALLOC;
     96 	if (pufbuf->buf == NULL) {
     97 		free(pufbuf);
     98 		return NULL;
     99 	}
    100 
    101 	puffs_framebuf_recycle(pufbuf);
    102 	return pufbuf;
    103 }
    104 
    105 void
    106 puffs_framebuf_destroy(struct puffs_framebuf *pufbuf)
    107 {
    108 
    109 	assert((pufbuf->istat & ISTAT_NODESTROY) == 0);
    110 
    111 	free(pufbuf->buf);
    112 	free(pufbuf);
    113 }
    114 
    115 void
    116 puffs_framebuf_recycle(struct puffs_framebuf *pufbuf)
    117 {
    118 
    119 	assert((pufbuf->istat & ISTAT_NODESTROY) == 0);
    120 
    121 	pufbuf->offset = 0;
    122 	pufbuf->maxoff = 0;
    123 	pufbuf->istat = 0;
    124 }
    125 
    126 static int
    127 reservespace(struct puffs_framebuf *pufbuf, size_t off, size_t wantsize)
    128 {
    129 	size_t incr;
    130 	void *nd;
    131 
    132 	if (off <= pufbuf->len && pufbuf->len - off >= wantsize)
    133 		return 0;
    134 
    135 	for (incr = PUFBUF_INCRALLOC;
    136 	    pufbuf->len + incr < off + wantsize;
    137 	    incr += PUFBUF_INCRALLOC)
    138 		continue;
    139 
    140 	nd = realloc(pufbuf->buf, pufbuf->offset + incr);
    141 	if (nd == NULL)
    142 		return -1;
    143 
    144 	pufbuf->buf = nd;
    145 	pufbuf->len += incr;
    146 
    147 	return 0;
    148 }
    149 
    150 int
    151 puffs_framebuf_reserve_space(struct puffs_framebuf *pufbuf, size_t wantsize)
    152 {
    153 
    154 	return reservespace(pufbuf, pufbuf->offset, wantsize);
    155 }
    156 
    157 int
    158 puffs_framebuf_putdata(struct puffs_framebuf *pufbuf,
    159 	const void *data, size_t dlen)
    160 {
    161 
    162 	if (PUFBUF_REMAIN(pufbuf) < dlen)
    163 		if (puffs_framebuf_reserve_space(pufbuf, dlen) == -1)
    164 			return -1;
    165 
    166 	memcpy(pufbuf->buf + pufbuf->offset, data, dlen);
    167 	pufbuf->offset += dlen;
    168 
    169 	if (pufbuf->offset > pufbuf->maxoff)
    170 		pufbuf->maxoff = pufbuf->offset;
    171 
    172 	return 0;
    173 }
    174 
    175 int
    176 puffs_framebuf_putdata_atoff(struct puffs_framebuf *pufbuf, size_t offset,
    177 	const void *data, size_t dlen)
    178 {
    179 
    180 	if (reservespace(pufbuf, offset, dlen) == -1)
    181 		return -1;
    182 
    183 	memcpy(pufbuf->buf + offset, data, dlen);
    184 
    185 	if (offset + dlen > pufbuf->maxoff)
    186 		pufbuf->maxoff = offset + dlen;
    187 
    188 	return 0;
    189 }
    190 
    191 int
    192 puffs_framebuf_getdata(struct puffs_framebuf *pufbuf, void *data, size_t dlen)
    193 {
    194 
    195 	if (pufbuf->maxoff < pufbuf->offset + dlen) {
    196 		errno = ENOBUFS;
    197 		return -1;
    198 	}
    199 
    200 	memcpy(data, pufbuf->buf + pufbuf->offset, dlen);
    201 	pufbuf->offset += dlen;
    202 
    203 	return 0;
    204 }
    205 
    206 int
    207 puffs_framebuf_getdata_atoff(struct puffs_framebuf *pufbuf, size_t offset,
    208 	void *data, size_t dlen)
    209 {
    210 
    211 	if (pufbuf->maxoff < offset + dlen) {
    212 		errno = ENOBUFS;
    213 		return -1;
    214 	}
    215 
    216 	memcpy(data, pufbuf->buf + offset, dlen);
    217 	return 0;
    218 }
    219 
    220 size_t
    221 puffs_framebuf_telloff(struct puffs_framebuf *pufbuf)
    222 {
    223 
    224 	return pufbuf->offset;
    225 }
    226 
    227 size_t
    228 puffs_framebuf_tellsize(struct puffs_framebuf *pufbuf)
    229 {
    230 
    231 	return pufbuf->maxoff;
    232 }
    233 
    234 size_t
    235 puffs_framebuf_remaining(struct puffs_framebuf *pufbuf)
    236 {
    237 
    238 	return puffs_framebuf_tellsize(pufbuf) - puffs_framebuf_telloff(pufbuf);
    239 }
    240 
    241 int
    242 puffs_framebuf_seekset(struct puffs_framebuf *pufbuf, size_t newoff)
    243 {
    244 
    245 	if (reservespace(pufbuf, newoff, 0) == -1)
    246 		return -1;
    247 
    248 	pufbuf->offset = newoff;
    249 	return 0;
    250 }
    251 
    252 int
    253 puffs_framebuf_getwindow(struct puffs_framebuf *pufbuf, size_t winoff,
    254 	void **data, size_t *dlen)
    255 {
    256 	size_t winlen;
    257 
    258 #ifdef WINTESTING
    259 	winlen = MIN(*dlen, 32);
    260 #else
    261 	winlen = *dlen;
    262 #endif
    263 
    264 	if (reservespace(pufbuf, winoff, winlen) == -1)
    265 		return -1;
    266 
    267 	*data = pufbuf->buf + winoff;
    268 	if (pufbuf->maxoff < winoff + winlen)
    269 		pufbuf->maxoff = winoff + winlen;
    270 
    271 	return 0;
    272 }
    273 
    274 int
    275 puffs_framebuf_enqueue_cc(struct puffs_cc *pcc, struct puffs_framebuf *pufbuf)
    276 {
    277 	struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
    278 	struct puffs_framectrl *fctrl = pu->pu_framectrl;
    279 
    280 	pufbuf->pcc = pcc;
    281 	pufbuf->fcb = NULL;
    282 	pufbuf->fcb_arg = NULL;
    283 
    284 	pufbuf->offset = 0;
    285 	pufbuf->istat |= ISTAT_NODESTROY;
    286 
    287 	TAILQ_INSERT_TAIL(&fctrl->snd_qing, pufbuf, pfb_entries);
    288 
    289 	puffs_cc_yield(pcc);
    290 	if (pufbuf->rv) {
    291 		errno = pufbuf->rv;
    292 		return -1;
    293 	}
    294 
    295 	return 0;
    296 }
    297 
    298 void
    299 puffs_framebuf_enqueue_cb(struct puffs_usermount *pu,
    300 	struct puffs_framebuf *pufbuf, puffs_framebuf_cb fcb, void *arg)
    301 {
    302 	struct puffs_framectrl *fctrl = pu->pu_framectrl;
    303 
    304 	pufbuf->pcc = NULL;
    305 	pufbuf->fcb = fcb;
    306 	pufbuf->fcb_arg = arg;
    307 
    308 	pufbuf->offset = 0;
    309 	pufbuf->istat |= ISTAT_NODESTROY;
    310 
    311 	TAILQ_INSERT_TAIL(&fctrl->snd_qing, pufbuf, pfb_entries);
    312 }
    313 
    314 void
    315 puffs_framebuf_enqueue_justsend(struct puffs_usermount *pu,
    316 	struct puffs_framebuf *pufbuf, int reply)
    317 {
    318 	struct puffs_framectrl *fctrl = pu->pu_framectrl;
    319 
    320 	pufbuf->pcc = NULL;
    321 	pufbuf->fcb = NULL;
    322 	pufbuf->fcb_arg = NULL;
    323 
    324 	pufbuf->offset = 0;
    325 	pufbuf->istat |= ISTAT_NODESTROY;
    326 	if (!reply)
    327 		pufbuf->istat |= ISTAT_NOREPLY;
    328 
    329 	TAILQ_INSERT_TAIL(&fctrl->snd_qing, pufbuf, pfb_entries);
    330 }
    331 
    332 static struct puffs_framebuf *
    333 findbuf(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
    334 	struct puffs_framebuf *findme)
    335 {
    336 	struct puffs_framebuf *cand;
    337 
    338 	TAILQ_FOREACH(cand, &fctrl->res_qing, pfb_entries)
    339 		if (fctrl->cmpfb(pu, findme, cand))
    340 			break;
    341 
    342 	if (cand == NULL)
    343 		return NULL;
    344 
    345 	TAILQ_REMOVE(&fctrl->res_qing, cand, pfb_entries);
    346 	return cand;
    347 }
    348 
    349 static void
    350 moveinfo(struct puffs_framebuf *from, struct puffs_framebuf *to)
    351 {
    352 
    353 	assert(from->istat & ISTAT_INTERNAL);
    354 
    355 	/* migrate buffer */
    356 	free(to->buf);
    357 	to->buf = from->buf;
    358 	from->buf = NULL;
    359 
    360 	/* migrate buffer info */
    361 	to->len = from->len;
    362 	to->offset = from->offset;
    363 	to->maxoff = from->maxoff;
    364 }
    365 
    366 static int
    367 handle_input(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
    368 	struct puffs_putreq *ppr)
    369 {
    370 	struct puffs_framebuf *pufbuf, *appbuf;
    371 	int rv, complete;
    372 
    373 	for (;;) {
    374 		if ((pufbuf = fctrl->cur_in) == NULL) {
    375 			pufbuf = puffs_framebuf_make();
    376 			if (pufbuf == NULL)
    377 				return -1;
    378 			pufbuf->istat |= ISTAT_INTERNAL;
    379 			fctrl->cur_in = pufbuf;
    380 		}
    381 
    382 		complete = 0;
    383 		rv = fctrl->rfb(pu, pufbuf, fctrl->io_fd, &complete);
    384 
    385 		/* error */
    386 		if (rv) {
    387 			errno = rv;
    388 			return -1;
    389 		}
    390 
    391 		/* partial read, come back to fight another day */
    392 		if (complete == 0)
    393 			break;
    394 
    395 		/* else: full read, process */
    396 
    397 		appbuf = findbuf(pu, fctrl, pufbuf);
    398 		if (appbuf == NULL) {
    399 			errno = ENOMSG;
    400 			return -1;
    401 		}
    402 
    403 		appbuf->istat &= ~ISTAT_NODESTROY;
    404 		moveinfo(pufbuf, appbuf);
    405 		if (appbuf->pcc) {
    406 			puffs_docc(appbuf->pcc, ppr);
    407 		} else if (appbuf->fcb) {
    408 			appbuf->fcb(pu, appbuf, appbuf->fcb_arg);
    409 		} else {
    410 			puffs_framebuf_destroy(appbuf);
    411 		}
    412 		puffs_framebuf_destroy(pufbuf);
    413 
    414 		/* hopeless romantics, here we go again */
    415 		fctrl->cur_in = NULL;
    416 	}
    417 
    418 	return rv;
    419 }
    420 
    421 static int
    422 handle_output(struct puffs_usermount *pu, struct puffs_framectrl *fctrl)
    423 {
    424 	struct puffs_framebuf *pufbuf, *pufbuf_next;
    425 	int rv, complete;
    426 
    427 	for (pufbuf = TAILQ_FIRST(&fctrl->snd_qing);
    428 	    pufbuf;
    429 	    pufbuf = pufbuf_next) {
    430 		complete = 0;
    431 		pufbuf_next = TAILQ_NEXT(pufbuf, pfb_entries);
    432 		rv = fctrl->wfb(pu, pufbuf, fctrl->io_fd, &complete);
    433 
    434 		if (rv) {
    435 			TAILQ_REMOVE(&fctrl->snd_qing, pufbuf, pfb_entries);
    436 			pufbuf->rv = rv;
    437 			errno = rv;
    438 			return -1;
    439 		}
    440 
    441 		/* partial write */
    442 		if (complete == 0)
    443 			return 0;
    444 
    445 		/* else, complete write */
    446 		TAILQ_REMOVE(&fctrl->snd_qing, pufbuf, pfb_entries);
    447 
    448 		if ((pufbuf->istat & ISTAT_NOREPLY) == 0) {
    449 			TAILQ_INSERT_TAIL(&fctrl->res_qing, pufbuf,
    450 			    pfb_entries);
    451 		} else {
    452 			pufbuf->istat &= ~ISTAT_NODESTROY;
    453 			puffs_framebuf_destroy(pufbuf);
    454 		}
    455 	}
    456 
    457 	return 0;
    458 }
    459 
    460 #define FRAMEFD 0
    461 #define PUFFSFD 1
    462 int
    463 puffs_framebuf_eventloop(struct puffs_usermount *pu, int io_fd,
    464 	puffs_framebuf_readframe_fn rfb, puffs_framebuf_writeframe_fn wfb,
    465 	puffs_framebuf_respcmp_fn cmpfb,
    466 	puffs_framebuf_loop_fn lfb)
    467 {
    468 	struct puffs_getreq *pgr = NULL;
    469 	struct puffs_putreq *ppr = NULL;
    470 	struct puffs_framectrl *pfctrl = NULL;
    471 	struct pollfd pfds[2];
    472 	int puffsfd, rv;
    473 
    474 	assert(puffs_getstate(pu) >= PUFFS_STATE_RUNNING);
    475 
    476 	pgr = puffs_req_makeget(pu, puffs_getmaxreqlen(pu), 0);
    477 	if (pgr == NULL)
    478 		goto out;
    479 
    480 	ppr = puffs_req_makeput(pu);
    481 	if (ppr == NULL)
    482 		goto out;
    483 
    484 	pfctrl = malloc(sizeof(struct puffs_framectrl));
    485 	if (pfctrl == NULL)
    486 		goto out;
    487 	pfctrl->rfb = rfb;
    488 	pfctrl->wfb = wfb;
    489 	pfctrl->cmpfb = cmpfb;
    490 	pfctrl->io_fd = io_fd;
    491 	TAILQ_INIT(&pfctrl->snd_qing);
    492 	TAILQ_INIT(&pfctrl->res_qing);
    493 	pu->pu_framectrl = pfctrl;
    494 
    495 	puffsfd = puffs_getselectable(pu);
    496 	while (puffs_getstate(pu) != PUFFS_STATE_UNMOUNTED) {
    497 		if (lfb)
    498 			lfb(pu);
    499 
    500 		memset(pfds, 0, sizeof(pfds));
    501 		pfds[FRAMEFD].events = POLLIN;
    502 		if (!TAILQ_EMPTY(&pfctrl->snd_qing))
    503 			pfds[FRAMEFD].events |= POLLOUT;
    504 		pfds[FRAMEFD].fd = io_fd;
    505 		pfds[PUFFSFD].events = POLLIN;
    506 		pfds[PUFFSFD].fd = puffsfd;
    507 
    508 		if (poll(pfds, 2, INFTIM) == -1)
    509 			goto out;
    510 
    511 		/* if there is room in the sucket for output ... */
    512 		if (pfds[FRAMEFD].revents & POLLOUT)
    513 			if (handle_output(pu, pfctrl) == -1)
    514 				goto out;
    515 
    516 		/* get & possibly dispatch events from kernel */
    517 		if (pfds[PUFFSFD].revents & POLLIN)
    518 			if (puffs_req_handle(pu, pgr, ppr, 0) == -1)
    519 				goto out;
    520 
    521 		/* get input from framefd, possibly build more responses */
    522 		if (pfds[FRAMEFD].revents & POLLIN)
    523 			if (handle_input(pu, pfctrl, ppr) == -1)
    524 				goto out;
    525 
    526 		/* it's likely we got outputtables, poke the ice with a stick */
    527 		if (handle_output(pu, pfctrl) == -1)
    528 			goto out;
    529 
    530 		/* stuff all replies from both of the above into kernel */
    531 		if (puffs_req_putput(ppr) == -1)
    532 			goto out;
    533 		puffs_req_resetput(ppr);
    534 	}
    535 	errno = 0;
    536 
    537  out:
    538 	rv = errno;
    539 	if (pfctrl) {
    540 		pu->pu_framectrl = NULL;
    541 		free(pfctrl);
    542 	}
    543 
    544 	if (ppr)
    545 		puffs_req_destroyput(ppr);
    546 	if (pgr)
    547 		puffs_req_destroyget(pgr);
    548 
    549 	return rv;
    550 }
    551