framebuf.c revision 1.7 1 /* $NetBSD: framebuf.c,v 1.7 2007/05/12 07:44:58 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.7 2007/05/12 07:44:58 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 #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 int
276 puffs_framebuf_enqueue_cc(struct puffs_cc *pcc, int fd,
277 struct puffs_framebuf *pufbuf)
278 {
279 struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
280 struct puffs_fctrl_io *fio;
281
282 fio = getfiobyfd(pu, fd);
283 if (fio == NULL) {
284 errno = EINVAL;
285 return -1;
286 }
287
288 pufbuf->pcc = pcc;
289 pufbuf->fcb = NULL;
290 pufbuf->fcb_arg = NULL;
291
292 pufbuf->offset = 0;
293 pufbuf->istat |= ISTAT_NODESTROY;
294
295 TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
296
297 puffs_cc_yield(pcc);
298 if (pufbuf->rv) {
299 errno = pufbuf->rv;
300 return -1;
301 }
302
303 return 0;
304 }
305
306 int
307 puffs_framebuf_enqueue_cb(struct puffs_usermount *pu, int fd,
308 struct puffs_framebuf *pufbuf, puffs_framebuf_cb fcb, void *arg)
309 {
310 struct puffs_fctrl_io *fio;
311
312 fio = getfiobyfd(pu, fd);
313 if (fio == NULL) {
314 errno = EINVAL;
315 return -1;
316 }
317
318 pufbuf->pcc = NULL;
319 pufbuf->fcb = fcb;
320 pufbuf->fcb_arg = arg;
321
322 pufbuf->offset = 0;
323 pufbuf->istat |= ISTAT_NODESTROY;
324
325 TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
326
327 return 0;
328 }
329
330 int
331 puffs_framebuf_enqueue_justsend(struct puffs_usermount *pu, int fd,
332 struct puffs_framebuf *pufbuf, int reply)
333 {
334 struct puffs_fctrl_io *fio;
335
336 fio = getfiobyfd(pu, fd);
337 if (fio == NULL) {
338 errno = EINVAL;
339 return -1;
340 }
341
342 pufbuf->pcc = NULL;
343 pufbuf->fcb = NULL;
344 pufbuf->fcb_arg = NULL;
345
346 pufbuf->offset = 0;
347 pufbuf->istat |= ISTAT_NODESTROY;
348 if (!reply)
349 pufbuf->istat |= ISTAT_NOREPLY;
350
351 TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
352
353 return 0;
354 }
355
356 static struct puffs_framebuf *
357 findbuf(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
358 struct puffs_fctrl_io *fio, struct puffs_framebuf *findme)
359 {
360 struct puffs_framebuf *cand;
361
362 TAILQ_FOREACH(cand, &fio->res_qing, pfb_entries)
363 if (fctrl->cmpfb(pu, findme, cand))
364 break;
365
366 if (cand == NULL)
367 return NULL;
368
369 TAILQ_REMOVE(&fio->res_qing, cand, pfb_entries);
370 return cand;
371 }
372
373 static void
374 moveinfo(struct puffs_framebuf *from, struct puffs_framebuf *to)
375 {
376
377 assert(from->istat & ISTAT_INTERNAL);
378
379 /* migrate buffer */
380 free(to->buf);
381 to->buf = from->buf;
382 from->buf = NULL;
383
384 /* migrate buffer info */
385 to->len = from->len;
386 to->offset = from->offset;
387 to->maxoff = from->maxoff;
388 }
389
390 int
391 puffs_framebuf_input(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
392 struct puffs_fctrl_io *fio, struct puffs_putreq *ppr)
393 {
394 struct puffs_framebuf *pufbuf, *appbuf;
395 int rv, complete;
396
397 for (;;) {
398 if ((pufbuf = fio->cur_in) == NULL) {
399 pufbuf = puffs_framebuf_make();
400 if (pufbuf == NULL)
401 return -1;
402 pufbuf->istat |= ISTAT_INTERNAL;
403 fio->cur_in = pufbuf;
404 }
405
406 complete = 0;
407 rv = fctrl->rfb(pu, pufbuf, fio->io_fd, &complete);
408
409 /* error */
410 if (rv) {
411 errno = rv;
412 return -1;
413 }
414
415 /* partial read, come back to fight another day */
416 if (complete == 0)
417 break;
418
419 /* else: full read, process */
420
421 appbuf = findbuf(pu, fctrl, fio, pufbuf);
422 if (appbuf == NULL) {
423 errno = ENOMSG;
424 return -1;
425 }
426
427 appbuf->istat &= ~ISTAT_NODESTROY;
428 moveinfo(pufbuf, appbuf);
429 if (appbuf->pcc) {
430 puffs_docc(appbuf->pcc, ppr);
431 } else if (appbuf->fcb) {
432 appbuf->fcb(pu, appbuf, appbuf->fcb_arg);
433 } else {
434 puffs_framebuf_destroy(appbuf);
435 }
436 puffs_framebuf_destroy(pufbuf);
437
438 /* hopeless romantics, here we go again */
439 fio->cur_in = NULL;
440 }
441
442 return rv;
443 }
444
445 int
446 puffs_framebuf_output(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
447 struct puffs_fctrl_io *fio)
448 {
449 struct puffs_framebuf *pufbuf, *pufbuf_next;
450 int rv, complete;
451
452 for (pufbuf = TAILQ_FIRST(&fio->snd_qing);
453 pufbuf;
454 pufbuf = pufbuf_next) {
455 complete = 0;
456 pufbuf_next = TAILQ_NEXT(pufbuf, pfb_entries);
457 rv = fctrl->wfb(pu, pufbuf, fio->io_fd, &complete);
458
459 if (rv) {
460 TAILQ_REMOVE(&fio->snd_qing, pufbuf, pfb_entries);
461 pufbuf->rv = rv;
462 errno = rv;
463 return -1;
464 }
465
466 /* partial write */
467 if (complete == 0)
468 return 0;
469
470 /* else, complete write */
471 TAILQ_REMOVE(&fio->snd_qing, pufbuf, pfb_entries);
472
473 if ((pufbuf->istat & ISTAT_NOREPLY) == 0) {
474 TAILQ_INSERT_TAIL(&fio->res_qing, pufbuf,
475 pfb_entries);
476 } else {
477 pufbuf->istat &= ~ISTAT_NODESTROY;
478 puffs_framebuf_destroy(pufbuf);
479 }
480 }
481
482 return 0;
483 }
484
485 int
486 puffs_framebuf_addfd(struct puffs_usermount *pu, int fd)
487 {
488 struct puffs_framectrl *pfctrl = &pu->pu_framectrl;
489 struct puffs_fctrl_io *fio;
490 struct kevent kev[2];
491 struct kevent *newevs;
492 size_t nfds;
493 int rv;
494
495 nfds = pfctrl->nfds+1;
496 newevs = realloc(pfctrl->evs, (2*nfds+1) * sizeof(struct kevent));
497 if (newevs == NULL)
498 return -1;
499 pfctrl->evs = newevs;
500
501 newevs = realloc(pfctrl->ch_evs, nfds * sizeof(struct kevent));
502 if (newevs == NULL)
503 return -1;
504 pfctrl->ch_evs = newevs;
505
506 fio = malloc(sizeof(struct puffs_fctrl_io));
507 if (fio == NULL)
508 return -1;
509
510 if (pu->pu_state & PU_INLOOP) {
511 EV_SET(&kev[0], fd, EVFILT_READ, EV_ADD, 0, 0, (intptr_t)fio);
512 EV_SET(&kev[1], fd, EVFILT_WRITE, EV_ADD|EV_DISABLE,
513 0, 0, (intptr_t)fio);
514 rv = kevent(pu->pu_kq, kev, 2, NULL, 0, NULL);
515 if (rv == -1) {
516 free(fio);
517 return -1;
518 }
519 }
520
521 fio->io_fd = fd;
522 fio->cur_in = NULL;
523 fio->wrstat = 0;
524 TAILQ_INIT(&fio->snd_qing);
525 TAILQ_INIT(&fio->res_qing);
526
527 LIST_INSERT_HEAD(&pfctrl->fb_ios, fio, fio_entries);
528 pfctrl->nfds = nfds;
529
530 return 0;
531 }
532
533 static int
534 removefio(struct puffs_usermount *pu, struct puffs_fctrl_io *fio)
535 {
536 struct puffs_framectrl *pfctrl = &pu->pu_framectrl;
537 struct puffs_framebuf *pufbuf;
538 struct kevent kev[2];
539
540 /* found, now remove */
541 if (pu->pu_state & PU_INLOOP) {
542 EV_SET(&kev[0], fio->io_fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
543 EV_SET(&kev[1], fio->io_fd, EVFILT_WRITE, EV_DELETE, 0, 0, 0);
544 (void) kevent(pu->pu_kq, kev, 2, NULL, 0, NULL);
545 }
546
547 LIST_REMOVE(fio, fio_entries);
548
549 /* free buffers */
550 while ((pufbuf = TAILQ_FIRST(&fio->snd_qing)) != NULL) {
551 TAILQ_REMOVE(&fio->snd_qing, pufbuf, pfb_entries);
552 puffs_framebuf_destroy(pufbuf);
553 }
554 while ((pufbuf = TAILQ_FIRST(&fio->res_qing)) != NULL) {
555 TAILQ_REMOVE(&fio->res_qing, pufbuf, pfb_entries);
556 puffs_framebuf_destroy(pufbuf);
557 }
558 if (fio->cur_in)
559 puffs_framebuf_destroy(fio->cur_in);
560 free(fio);
561
562 /* don't bother with realloc */
563 pfctrl->nfds--;
564
565 return 0;
566
567 }
568
569 int
570 puffs_framebuf_removefd(struct puffs_usermount *pu, int fd)
571 {
572 struct puffs_fctrl_io *fio;
573
574 fio = getfiobyfd(pu, fd);
575 if (fio == NULL) {
576 errno = ENXIO;
577 return -1;
578 }
579
580 return removefio(pu, fio);
581 }
582
583 int
584 puffs_framebuf_init(struct puffs_usermount *pu,
585 puffs_framebuf_readframe_fn rfb, puffs_framebuf_writeframe_fn wfb,
586 puffs_framebuf_respcmp_fn cmpfb,
587 puffs_framebuf_loop_fn lfb)
588 {
589 struct puffs_framectrl *pfctrl;
590
591 pfctrl = &pu->pu_framectrl;
592 pfctrl->rfb = rfb;
593 pfctrl->wfb = wfb;
594 pfctrl->cmpfb = cmpfb;
595 pfctrl->lfb = lfb;
596 pfctrl->evs = NULL;
597 pfctrl->ch_evs = NULL;
598 pfctrl->nfds = 0;
599
600 return 0;
601 }
602
603 void
604 puffs_framebuf_exit(struct puffs_usermount *pu)
605 {
606 struct puffs_framectrl *pfctrl = &pu->pu_framectrl;
607 struct puffs_fctrl_io *fio;
608
609 while ((fio = LIST_FIRST(&pfctrl->fb_ios)) != NULL)
610 removefio(pu, fio);
611 free(pfctrl->evs);
612
613 /* closing pu->pu_kq takes care of puffsfd */
614 }
615