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