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