Merge branch 'vendor/TNFTP'
[dragonfly.git] / lib / libpuffs / framebuf.c
1 /*      $NetBSD: framebuf.c,v 1.30 2010/01/12 18:42:38 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 /*
32  * The event portion of this code is a twisty maze of pointers,
33  * flags, yields and continues.  Sincere aplogies.
34  */
35
36 #include <sys/types.h>
37 #include <sys/event.h>
38 #include <sys/queue.h>
39
40 #include <assert.h>
41 #include <errno.h>
42 #include <poll.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46
47 #include "puffs.h"
48 #include "puffs_priv.h"
49
50 struct puffs_framebuf {
51         struct puffs_cc *pcc;   /* pcc to continue with */
52         /* OR */
53         puffs_framev_cb fcb;    /* non-blocking callback */
54         void *fcb_arg;          /* argument for previous */
55
56         uint8_t *buf;           /* buffer base */
57         size_t len;             /* total length */
58
59         size_t offset;          /* cursor, telloff() */
60         size_t maxoff;          /* maximum offset for data, tellsize() */
61
62         volatile int rv;        /* errno value */
63
64         int     istat;
65
66         TAILQ_ENTRY(puffs_framebuf) pfb_entries;
67 };
68 #define ISTAT_NODESTROY 0x01    /* indestructible by framebuf_destroy() */
69 #define ISTAT_INTERNAL  0x02    /* never leaves library                 */
70 #define ISTAT_NOREPLY   0x04    /* nuke after sending                   */
71 #define ISTAT_DIRECT    0x08    /* receive directly, no moveinfo        */
72
73 #define ISTAT_ONQUEUE   ISTAT_NODESTROY /* alias */
74
75 #define PUFBUF_INCRALLOC 4096
76 #define PUFBUF_REMAIN(p) (p->len - p->offset)
77
78 /* for poll/kqueue */
79 struct puffs_fbevent {
80         struct puffs_cc *pcc;
81         int what;
82         volatile int rv;
83
84         LIST_ENTRY(puffs_fbevent) pfe_entries;
85 };
86
87 static struct puffs_fctrl_io *
88 getfiobyfd(struct puffs_usermount *pu, int fd)
89 {
90         struct puffs_fctrl_io *fio;
91
92         LIST_FOREACH(fio, &pu->pu_ios, fio_entries)
93                 if (fio->io_fd == fd)
94                         return fio;
95         return NULL;
96 }
97
98 struct puffs_framebuf *
99 puffs_framebuf_make()
100 {
101         struct puffs_framebuf *pufbuf;
102
103         pufbuf = malloc(sizeof(struct puffs_framebuf));
104         if (pufbuf == NULL)
105                 return NULL;
106         memset(pufbuf, 0, sizeof(struct puffs_framebuf));
107
108         pufbuf->buf = malloc(PUFBUF_INCRALLOC);
109         if (pufbuf->buf == NULL) {
110                 free(pufbuf);
111                 return NULL;
112         }
113         pufbuf->len = PUFBUF_INCRALLOC;
114
115         puffs_framebuf_recycle(pufbuf);
116         return pufbuf;
117 }
118
119 void
120 puffs_framebuf_destroy(struct puffs_framebuf *pufbuf)
121 {
122
123         assert((pufbuf->istat & ISTAT_NODESTROY) == 0);
124
125         free(pufbuf->buf);
126         free(pufbuf);
127 }
128
129 void
130 puffs_framebuf_recycle(struct puffs_framebuf *pufbuf)
131 {
132
133         assert((pufbuf->istat & ISTAT_NODESTROY) == 0);
134
135         pufbuf->offset = 0;
136         pufbuf->maxoff = 0;
137         pufbuf->istat = 0;
138 }
139
140 static int
141 reservespace(struct puffs_framebuf *pufbuf, size_t off, size_t wantsize)
142 {
143         size_t incr;
144         void *nd;
145
146         if (off <= pufbuf->len && pufbuf->len - off >= wantsize)
147                 return 0;
148
149         for (incr = PUFBUF_INCRALLOC;
150             pufbuf->len + incr < off + wantsize;
151             incr += PUFBUF_INCRALLOC)
152                 continue;
153
154         nd = realloc(pufbuf->buf, pufbuf->len + incr);
155         if (nd == NULL)
156                 return -1;
157
158         pufbuf->buf = nd;
159         pufbuf->len += incr;
160
161         return 0;
162 }
163
164 int
165 puffs_framebuf_dup(struct puffs_framebuf *pb, struct puffs_framebuf **pbp)
166 {
167         struct puffs_framebuf *newpb;
168
169         newpb = puffs_framebuf_make();
170         if (newpb == NULL) {
171                 errno = ENOMEM;
172                 return -1;
173         }
174         memcpy(newpb, pb, sizeof(struct puffs_framebuf));
175
176         newpb->buf = NULL;
177         newpb->len = 0;
178         if (reservespace(newpb, 0, pb->maxoff) == -1) {
179                 puffs_framebuf_destroy(newpb);
180                 return -1;
181         }
182
183         memcpy(newpb->buf, pb->buf, pb->maxoff);
184         newpb->istat = 0;
185         *pbp = newpb;
186
187         return 0;
188 }
189
190 int
191 puffs_framebuf_reserve_space(struct puffs_framebuf *pufbuf, size_t wantsize)
192 {
193
194         return reservespace(pufbuf, pufbuf->offset, wantsize);
195 }
196
197 int
198 puffs_framebuf_putdata(struct puffs_framebuf *pufbuf,
199         const void *data, size_t dlen)
200 {
201
202         if (PUFBUF_REMAIN(pufbuf) < dlen)
203                 if (puffs_framebuf_reserve_space(pufbuf, dlen) == -1)
204                         return -1;
205
206         memcpy(pufbuf->buf + pufbuf->offset, data, dlen);
207         pufbuf->offset += dlen;
208
209         if (pufbuf->offset > pufbuf->maxoff)
210                 pufbuf->maxoff = pufbuf->offset;
211
212         return 0;
213 }
214
215 int
216 puffs_framebuf_putdata_atoff(struct puffs_framebuf *pufbuf, size_t offset,
217         const void *data, size_t dlen)
218 {
219
220         if (reservespace(pufbuf, offset, dlen) == -1)
221                 return -1;
222
223         memcpy(pufbuf->buf + offset, data, dlen);
224
225         if (offset + dlen > pufbuf->maxoff)
226                 pufbuf->maxoff = offset + dlen;
227
228         return 0;
229 }
230
231 int
232 puffs_framebuf_getdata(struct puffs_framebuf *pufbuf, void *data, size_t dlen)
233 {
234
235         if (pufbuf->maxoff < pufbuf->offset + dlen) {
236                 errno = ENOBUFS;
237                 return -1;
238         }
239
240         memcpy(data, pufbuf->buf + pufbuf->offset, dlen);
241         pufbuf->offset += dlen;
242
243         return 0;
244 }
245
246 int
247 puffs_framebuf_getdata_atoff(struct puffs_framebuf *pufbuf, size_t offset,
248         void *data, size_t dlen)
249 {
250
251         if (pufbuf->maxoff < offset + dlen) {
252                 errno = ENOBUFS;
253                 return -1;
254         }
255
256         memcpy(data, pufbuf->buf + offset, dlen);
257         return 0;
258 }
259
260 size_t
261 puffs_framebuf_telloff(struct puffs_framebuf *pufbuf)
262 {
263
264         return pufbuf->offset;
265 }
266
267 size_t
268 puffs_framebuf_tellsize(struct puffs_framebuf *pufbuf)
269 {
270
271         return pufbuf->maxoff;
272 }
273
274 size_t
275 puffs_framebuf_remaining(struct puffs_framebuf *pufbuf)
276 {
277
278         return puffs_framebuf_tellsize(pufbuf) - puffs_framebuf_telloff(pufbuf);
279 }
280
281 int
282 puffs_framebuf_seekset(struct puffs_framebuf *pufbuf, size_t newoff)
283 {
284
285         if (reservespace(pufbuf, newoff, 0) == -1)
286                 return -1;
287
288         pufbuf->offset = newoff;
289         return 0;
290 }
291
292 int
293 puffs_framebuf_getwindow(struct puffs_framebuf *pufbuf, size_t winoff,
294         void **data, size_t *dlen)
295 {
296         size_t winlen;
297
298 #ifdef WINTESTING
299         winlen = MIN(*dlen, 32);
300 #else
301         winlen = *dlen;
302 #endif
303
304         if (reservespace(pufbuf, winoff, winlen) == -1)
305                 return -1;
306
307         *data = pufbuf->buf + winoff;
308         if (pufbuf->maxoff < winoff + winlen)
309                 pufbuf->maxoff = winoff + winlen;
310
311         return 0;
312 }
313
314 void *
315 puffs__framebuf_getdataptr(struct puffs_framebuf *pufbuf)
316 {
317
318         return pufbuf->buf;
319 }
320
321 static void
322 errnotify(struct puffs_usermount *pu, struct puffs_framebuf *pufbuf, int error)
323 {
324
325         pufbuf->rv = error;
326         if (pufbuf->pcc) {
327                 puffs__goto(pufbuf->pcc);
328         } else if (pufbuf->fcb) {
329                 pufbuf->istat &= ~ISTAT_NODESTROY;
330                 pufbuf->fcb(pu, pufbuf, pufbuf->fcb_arg, error);
331         } else {
332                 pufbuf->istat &= ~ISTAT_NODESTROY;
333                 puffs_framebuf_destroy(pufbuf);
334         }
335 }
336
337 #define GETFIO(fd)                                                      \
338 do {                                                                    \
339         fio = getfiobyfd(pu, fd);                                       \
340         if (fio == NULL) {                                              \
341                 errno = EINVAL;                                         \
342                 return -1;                                              \
343         }                                                               \
344         if (fio->stat & FIO_WRGONE) {                                   \
345                 errno = ESHUTDOWN;                                      \
346                 return -1;                                              \
347         }                                                               \
348 } while (/*CONSTCOND*/0)
349
350 int
351 puffs_framev_enqueue_cc(struct puffs_cc *pcc, int fd,
352         struct puffs_framebuf *pufbuf, int flags)
353 {
354         struct puffs_usermount *pu = pcc->pcc_pu;
355         struct puffs_fctrl_io *fio;
356
357         /*
358          * Technically we shouldn't allow this if RDGONE, but it's
359          * difficult to trap write close without allowing writes.
360          * And besides, there's probably a disconnect sequence in
361          * the protocol, so unexpectedly getting a closed fd is
362          * most likely an error condition.
363          */
364         GETFIO(fd);
365
366         pufbuf->pcc = pcc;
367         pufbuf->fcb = NULL;
368         pufbuf->fcb_arg = NULL;
369
370         pufbuf->offset = 0;
371         pufbuf->istat |= ISTAT_NODESTROY;
372
373         if (flags & PUFFS_FBQUEUE_URGENT)
374                 TAILQ_INSERT_HEAD(&fio->snd_qing, pufbuf, pfb_entries);
375         else
376                 TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
377
378         puffs_cc_yield(pcc);
379         if (pufbuf->rv) {
380                 pufbuf->istat &= ~ISTAT_NODESTROY;
381                 errno = pufbuf->rv;
382                 return -1;
383         }
384
385         return 0;
386 }
387
388 int
389 puffs_framev_enqueue_cb(struct puffs_usermount *pu, int fd,
390         struct puffs_framebuf *pufbuf, puffs_framev_cb fcb, void *arg,
391         int flags)
392 {
393         struct puffs_fctrl_io *fio;
394
395         /* see enqueue_cc */
396         GETFIO(fd);
397
398         pufbuf->pcc = NULL;
399         pufbuf->fcb = fcb;
400         pufbuf->fcb_arg = arg;
401
402         pufbuf->offset = 0;
403         pufbuf->istat |= ISTAT_NODESTROY;
404
405         if (flags & PUFFS_FBQUEUE_URGENT)
406                 TAILQ_INSERT_HEAD(&fio->snd_qing, pufbuf, pfb_entries);
407         else
408                 TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
409
410         return 0;
411 }
412
413 int
414 puffs_framev_enqueue_justsend(struct puffs_usermount *pu, int fd,
415         struct puffs_framebuf *pufbuf, int reply, int flags)
416 {
417         struct puffs_fctrl_io *fio;
418
419         assert((pufbuf->istat & ISTAT_INTERNAL) == 0);
420
421         GETFIO(fd);
422
423         pufbuf->pcc = NULL;
424         pufbuf->fcb = NULL;
425         pufbuf->fcb_arg = NULL;
426
427         pufbuf->offset = 0;
428         pufbuf->istat |= ISTAT_NODESTROY;
429         if (!reply)
430                 pufbuf->istat |= ISTAT_NOREPLY;
431
432         if (flags & PUFFS_FBQUEUE_URGENT)
433                 TAILQ_INSERT_HEAD(&fio->snd_qing, pufbuf, pfb_entries);
434         else
435                 TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
436
437         return 0;
438 }
439
440 /* ARGSUSED */
441 int
442 puffs_framev_enqueue_directreceive(struct puffs_cc *pcc, int fd,
443         struct puffs_framebuf *pufbuf, int flags /* used in the future */)
444 {
445         struct puffs_usermount *pu = pcc->pcc_pu;
446         struct puffs_fctrl_io *fio;
447
448         assert((pufbuf->istat & ISTAT_INTERNAL) == 0);
449
450         fio = getfiobyfd(pu, fd);
451         if (fio == NULL) {
452                 errno = EINVAL;
453                 return -1;
454         }
455
456         /* XXX: should have cur_in queue */
457         assert(fio->cur_in == NULL);
458         fio->cur_in = pufbuf;
459
460         pufbuf->pcc = pcc;
461         pufbuf->fcb = NULL;
462         pufbuf->fcb_arg = NULL;
463
464         pufbuf->offset = 0;
465         pufbuf->istat |= ISTAT_NODESTROY | ISTAT_DIRECT;
466
467         puffs_cc_yield(pcc);
468         pufbuf->istat &= ~ISTAT_NODESTROY; /* XXX: not the right place */
469         if (pufbuf->rv) {
470                 errno = pufbuf->rv;
471                 return -1;
472         }
473
474         return 0;
475 }
476
477 int
478 puffs_framev_enqueue_directsend(struct puffs_cc *pcc, int fd,
479         struct puffs_framebuf *pufbuf, int flags)
480 {
481         struct puffs_usermount *pu = pcc->pcc_pu;
482         struct puffs_fctrl_io *fio;
483
484         assert((pufbuf->istat & ISTAT_INTERNAL) == 0);
485
486         if (flags & PUFFS_FBQUEUE_URGENT)
487                 abort(); /* EOPNOTSUPP for now */
488
489         GETFIO(fd);
490
491         pufbuf->pcc = pcc;
492         pufbuf->fcb = NULL;
493         pufbuf->fcb_arg = NULL;
494
495         pufbuf->offset = 0;
496         pufbuf->istat |= ISTAT_NODESTROY | ISTAT_DIRECT;
497
498         TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
499
500         puffs_cc_yield(pcc);
501         if (pufbuf->rv) {
502                 pufbuf->istat &= ~ISTAT_NODESTROY;
503                 errno = pufbuf->rv;
504                 return -1;
505         }
506
507         return 0;
508 }
509
510 int
511 puffs_framev_framebuf_ccpromote(struct puffs_framebuf *pufbuf,
512         struct puffs_cc *pcc)
513 {
514
515         if ((pufbuf->istat & ISTAT_ONQUEUE) == 0) {
516                 errno = EBUSY;
517                 return -1;
518         }
519
520         pufbuf->pcc = pcc;
521         pufbuf->fcb = NULL;
522         pufbuf->fcb_arg = NULL;
523         pufbuf->istat &= ~ISTAT_NOREPLY;
524
525         puffs_cc_yield(pcc);
526
527         return 0;
528 }
529
530 int
531 puffs_framev_enqueue_waitevent(struct puffs_cc *pcc, int fd, int *what)
532 {
533         struct puffs_usermount *pu = pcc->pcc_pu;
534         struct puffs_fctrl_io *fio;
535         struct puffs_fbevent feb;
536         struct kevent kev;
537         int rv, svwhat;
538
539         svwhat = *what;
540
541         if (*what == 0) {
542                 errno = EINVAL;
543                 return -1;
544         }
545
546         fio = getfiobyfd(pu, fd);
547         if (fio == NULL) {
548                 errno = EINVAL;
549                 return -1;
550         }
551
552         feb.pcc = pcc;
553         feb.what = *what & (PUFFS_FBIO_READ|PUFFS_FBIO_WRITE|PUFFS_FBIO_ERROR);
554
555         if (*what & PUFFS_FBIO_READ)
556                 if ((fio->stat & FIO_ENABLE_R) == 0)
557                         EV_SET(&kev, fd, EVFILT_READ, EV_ENABLE,
558                             0, 0, fio);
559
560         rv = kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
561         if (rv != 0)
562                 return errno;
563
564         if (*what & PUFFS_FBIO_READ)
565                 fio->rwait++;
566         if (*what & PUFFS_FBIO_WRITE)
567                 fio->wwait++;
568
569         LIST_INSERT_HEAD(&fio->ev_qing, &feb, pfe_entries);
570         puffs_cc_yield(pcc);
571
572         assert(svwhat == *what);
573
574         if (*what & PUFFS_FBIO_READ) {
575                 fio->rwait--;
576                 if (fio->rwait == 0 && (fio->stat & FIO_ENABLE_R) == 0) {
577                         EV_SET(&kev, fd, EVFILT_READ, EV_DISABLE,
578                             0, 0, fio);
579                         rv = kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
580 #if 0
581                         if (rv != 0)
582                                 /* XXXXX oh dear */;
583 #endif
584                 }
585         }
586         if (*what & PUFFS_FBIO_WRITE)
587                 fio->wwait--;
588
589         if (feb.rv == 0) {
590                 *what = feb.what;
591                 rv = 0;
592         } else {
593                 *what = PUFFS_FBIO_ERROR;
594                 errno = feb.rv;
595                 rv = -1;
596         }
597
598         return rv;
599 }
600
601 void
602 puffs__framev_notify(struct puffs_fctrl_io *fio, int what)
603 {
604         struct puffs_fbevent *fbevp;
605
606  restart:
607         LIST_FOREACH(fbevp, &fio->ev_qing, pfe_entries) {
608                 if (fbevp->what & what) {
609                         fbevp->what = what;
610                         fbevp->rv = 0;
611                         LIST_REMOVE(fbevp, pfe_entries);
612                         puffs_cc_continue(fbevp->pcc);
613                         goto restart;
614                 }
615         }
616 }
617
618 static struct puffs_framebuf *
619 findbuf(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
620         struct puffs_fctrl_io *fio, struct puffs_framebuf *findme)
621 {
622         struct puffs_framebuf *cand;
623         int notresp = 0;
624
625         TAILQ_FOREACH(cand, &fio->res_qing, pfb_entries)
626                 if (fctrl->cmpfb(pu, findme, cand, &notresp) == 0 || notresp)
627                         break;
628
629         assert(!(notresp && cand == NULL));
630         if (notresp || cand == NULL)
631                 return NULL;
632
633         TAILQ_REMOVE(&fio->res_qing, cand, pfb_entries);
634         return cand;
635 }
636
637 void
638 puffs__framebuf_moveinfo(struct puffs_framebuf *from, struct puffs_framebuf *to)
639 {
640
641         assert(from->istat & ISTAT_INTERNAL);
642
643         /* migrate buffer */
644         free(to->buf);
645         to->buf = from->buf;
646
647         /* migrate buffer info */
648         to->len = from->len;
649         to->offset = from->offset;
650         to->maxoff = from->maxoff;
651
652         from->buf = NULL;
653         from->len = 0;
654 }
655
656 void
657 puffs__framev_input(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
658         struct puffs_fctrl_io *fio)
659 {
660         struct puffs_framebuf *pufbuf, *appbuf;
661         int rv, complete;
662
663         while ((fio->stat & FIO_DEAD) == 0 && (fio->stat & FIO_ENABLE_R)) {
664                 if ((pufbuf = fio->cur_in) == NULL) {
665                         pufbuf = puffs_framebuf_make();
666                         if (pufbuf == NULL)
667                                 return;
668                         pufbuf->istat |= ISTAT_INTERNAL;
669                         fio->cur_in = pufbuf;
670                 }
671
672                 complete = 0;
673                 rv = fctrl->rfb(pu, pufbuf, fio->io_fd, &complete);
674
675                 /* error */
676                 if (rv) {
677                         puffs__framev_readclose(pu, fio, rv);
678                         fio->cur_in = NULL;
679                         return;
680                 }
681
682                 /* partial read, come back to fight another day */
683                 if (complete == 0)
684                         break;
685
686                 /* else: full read, process */
687                 fio->cur_in = NULL;
688                 if ((pufbuf->istat & ISTAT_DIRECT) == 0) {
689                         appbuf = findbuf(pu, fctrl, fio, pufbuf);
690
691                         /*
692                          * No request for this frame?  If fs implements
693                          * gotfb, give frame to that.  Otherwise drop it.
694                          */
695                         if (appbuf == NULL) {
696                                 if (fctrl->gotfb) {
697                                         pufbuf->istat &= ~ISTAT_INTERNAL;
698                                         fctrl->gotfb(pu, pufbuf);
699                                 } else {
700                                         puffs_framebuf_destroy(pufbuf);
701                                 }
702                                 continue;
703                         }
704
705                         puffs__framebuf_moveinfo(pufbuf, appbuf);
706                         puffs_framebuf_destroy(pufbuf);
707                 } else {
708                         appbuf = pufbuf;
709                 }
710                 appbuf->istat &= ~ISTAT_NODESTROY;
711
712                 if (appbuf->pcc) {
713                         puffs__cc_cont(appbuf->pcc);
714                 } else if (appbuf->fcb) {
715                         appbuf->fcb(pu, appbuf, appbuf->fcb_arg, 0);
716                 } else {
717                         puffs_framebuf_destroy(appbuf);
718                 }
719
720                 /* hopeless romantics, here we go again */
721         }
722 }
723
724 int
725 puffs__framev_output(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
726         struct puffs_fctrl_io *fio)
727 {
728         struct puffs_framebuf *pufbuf;
729         int rv, complete, done;
730
731         if (fio->stat & FIO_DEAD)
732                 return 0;
733
734         for (pufbuf = TAILQ_FIRST(&fio->snd_qing), done = 0;
735             pufbuf && (fio->stat & FIO_DEAD) == 0 && fio->stat & FIO_ENABLE_W;
736             pufbuf = TAILQ_FIRST(&fio->snd_qing)) {
737                 complete = 0;
738                 rv = fctrl->wfb(pu, pufbuf, fio->io_fd, &complete);
739
740                 if (rv) {
741                         puffs__framev_writeclose(pu, fio, rv);
742                         done = 1;
743                         break;
744                 }
745
746                 /* partial write */
747                 if (complete == 0)
748                         return done;
749
750                 /* else, complete write */
751                 TAILQ_REMOVE(&fio->snd_qing, pufbuf, pfb_entries);
752
753                 /* can't wait for result if we can't read */
754                 if (fio->stat & FIO_RDGONE) {
755                         errnotify(pu, pufbuf, ENXIO);
756                         done = 1;
757                 } else if ((pufbuf->istat & ISTAT_DIRECT)) {
758                         pufbuf->istat &= ~ISTAT_NODESTROY;
759                         done = 1;
760                         puffs__cc_cont(pufbuf->pcc);
761                 } else if ((pufbuf->istat & ISTAT_NOREPLY) == 0) {
762                         TAILQ_INSERT_TAIL(&fio->res_qing, pufbuf,
763                             pfb_entries);
764                 } else {
765                         pufbuf->istat &= ~ISTAT_NODESTROY;
766                         puffs_framebuf_destroy(pufbuf);
767                 }
768
769                 /* omstart! */
770         }
771
772         return done;
773 }
774
775 int
776 puffs__framev_addfd_ctrl(struct puffs_usermount *pu, int fd, int what,
777         struct puffs_framectrl *pfctrl)
778 {
779         struct puffs_fctrl_io *fio;
780         struct kevent *newevs;
781         struct kevent kev[2];
782         size_t nevs;
783         int rv, readenable;
784
785         nevs = pu->pu_nevs+2;
786         newevs = realloc(pu->pu_evs, nevs*sizeof(struct kevent));
787         if (newevs == NULL)
788                 return -1;
789         pu->pu_evs = newevs;
790
791         fio = malloc(sizeof(struct puffs_fctrl_io));
792         if (fio == NULL)
793                 return -1;
794         memset(fio, 0, sizeof(struct puffs_fctrl_io));
795         fio->io_fd = fd;
796         fio->cur_in = NULL;
797         fio->fctrl = pfctrl;
798         TAILQ_INIT(&fio->snd_qing);
799         TAILQ_INIT(&fio->res_qing);
800         LIST_INIT(&fio->ev_qing);
801
802         readenable = 0;
803         if ((what & PUFFS_FBIO_READ) == 0)
804                 readenable = EV_DISABLE;
805
806         if (pu->pu_state & PU_INLOOP) {
807                 EV_SET(&kev[0], fd, EVFILT_READ,
808                     EV_ADD|readenable, 0, 0, fio);
809                 EV_SET(&kev[1], fd, EVFILT_WRITE,
810                     EV_ADD|EV_DISABLE, 0, 0, fio);
811                 rv = kevent(pu->pu_kq, kev, 2, NULL, 0, NULL);
812                 if (rv == -1) {
813                         free(fio);
814                         return -1;
815                 }
816         }
817         if (what & PUFFS_FBIO_READ)
818                 fio->stat |= FIO_ENABLE_R;
819         if (what & PUFFS_FBIO_WRITE)
820                 fio->stat |= FIO_ENABLE_W;
821
822         LIST_INSERT_HEAD(&pu->pu_ios, fio, fio_entries);
823         pu->pu_nevs = nevs;
824
825         return 0;
826 }
827
828 int
829 puffs_framev_addfd(struct puffs_usermount *pu, int fd, int what)
830 {
831
832         return puffs__framev_addfd_ctrl(pu, fd, what,
833             &pu->pu_framectrl[PU_FRAMECTRL_USER]);
834 }
835
836 /*
837  * XXX: the following en/disable should be coalesced and executed
838  * only during the actual kevent call.  So feel free to fix if
839  * threatened by mindblowing boredom.
840  */
841
842 int
843 puffs_framev_enablefd(struct puffs_usermount *pu, int fd, int what)
844 {
845         struct kevent kev;
846         struct puffs_fctrl_io *fio;
847         int rv = 0;
848
849         assert((what & (PUFFS_FBIO_READ | PUFFS_FBIO_WRITE)) != 0);
850
851         fio = getfiobyfd(pu, fd);
852         if (fio == NULL) {
853                 errno = ENXIO;
854                 return -1;
855         }
856
857         /* write is enabled in the event loop if there is output */
858         if (what & PUFFS_FBIO_READ && fio->rwait == 0) {
859                 EV_SET(&kev, fd, EVFILT_READ, EV_ENABLE, 0, 0, fio);
860                 rv = kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
861         }
862
863         if (rv == 0) {
864                 if (what & PUFFS_FBIO_READ)
865                         fio->stat |= FIO_ENABLE_R;
866                 if (what & PUFFS_FBIO_WRITE)
867                         fio->stat |= FIO_ENABLE_W;
868         }
869
870         return rv;
871 }
872
873 int
874 puffs_framev_disablefd(struct puffs_usermount *pu, int fd, int what)
875 {
876         struct kevent kev[2];
877         struct puffs_fctrl_io *fio;
878         size_t i;
879         int rv;
880
881         assert((what & (PUFFS_FBIO_READ | PUFFS_FBIO_WRITE)) != 0);
882
883         fio = getfiobyfd(pu, fd);
884         if (fio == NULL) {
885                 errno = ENXIO;
886                 return -1;
887         }
888
889         i = 0;
890         if (what & PUFFS_FBIO_READ && fio->rwait == 0) {
891                 EV_SET(&kev[0], fd,
892                     EVFILT_READ, EV_DISABLE, 0, 0, fio);
893                 i++;
894         }
895         if (what & PUFFS_FBIO_WRITE && fio->stat & FIO_WR && fio->wwait == 0) {
896                 EV_SET(&kev[1], fd,
897                     EVFILT_WRITE, EV_DISABLE, 0, 0, fio);
898                 i++;
899         }
900         if (i)
901                 rv = kevent(pu->pu_kq, kev, i, NULL, 0, NULL);
902         else
903                 rv = 0;
904
905         if (rv == 0) {
906                 if (what & PUFFS_FBIO_READ)
907                         fio->stat &= ~FIO_ENABLE_R;
908                 if (what & PUFFS_FBIO_WRITE)
909                         fio->stat &= ~FIO_ENABLE_W;
910         }
911
912         return rv;
913 }
914
915 void
916 puffs__framev_readclose(struct puffs_usermount *pu,
917         struct puffs_fctrl_io *fio, int error)
918 {
919         struct puffs_framebuf *pufbuf;
920         struct kevent kev;
921         int notflag;
922
923         if (fio->stat & FIO_RDGONE || fio->stat & FIO_DEAD)
924                 return;
925         fio->stat |= FIO_RDGONE;
926
927         if (fio->cur_in) {
928                 if ((fio->cur_in->istat & ISTAT_DIRECT) == 0) {
929                         puffs_framebuf_destroy(fio->cur_in);
930                         fio->cur_in = NULL;
931                 } else {
932                         errnotify(pu, fio->cur_in, error);
933                 }
934         }
935
936         while ((pufbuf = TAILQ_FIRST(&fio->res_qing)) != NULL) {
937                 TAILQ_REMOVE(&fio->res_qing, pufbuf, pfb_entries);
938                 errnotify(pu, pufbuf, error);
939         }
940
941         EV_SET(&kev, fio->io_fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
942         (void) kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
943
944         notflag = PUFFS_FBIO_READ;
945         if (fio->stat & FIO_WRGONE)
946                 notflag |= PUFFS_FBIO_WRITE;
947
948         if (fio->fctrl->fdnotfn)
949                 fio->fctrl->fdnotfn(pu, fio->io_fd, notflag);
950 }
951
952 void
953 puffs__framev_writeclose(struct puffs_usermount *pu,
954         struct puffs_fctrl_io *fio, int error)
955 {
956         struct puffs_framebuf *pufbuf;
957         struct kevent kev;
958         int notflag;
959
960         if (fio->stat & FIO_WRGONE || fio->stat & FIO_DEAD)
961                 return;
962         fio->stat |= FIO_WRGONE;
963
964         while ((pufbuf = TAILQ_FIRST(&fio->snd_qing)) != NULL) {
965                 TAILQ_REMOVE(&fio->snd_qing, pufbuf, pfb_entries);
966                 errnotify(pu, pufbuf, error);
967         }
968
969         EV_SET(&kev, fio->io_fd, EVFILT_WRITE, EV_DELETE, 0, 0, 0);
970         (void) kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
971
972         notflag = PUFFS_FBIO_WRITE;
973         if (fio->stat & FIO_RDGONE)
974                 notflag |= PUFFS_FBIO_READ;
975
976         if (fio->fctrl->fdnotfn)
977                 fio->fctrl->fdnotfn(pu, fio->io_fd, notflag);
978 }
979
980 static int
981 removefio(struct puffs_usermount *pu, struct puffs_fctrl_io *fio, int error)
982 {
983         struct puffs_fbevent *fbevp;
984
985         LIST_REMOVE(fio, fio_entries);
986         if (pu->pu_state & PU_INLOOP) {
987                 puffs__framev_readclose(pu, fio, error);
988                 puffs__framev_writeclose(pu, fio, error);
989         }
990
991         while ((fbevp = LIST_FIRST(&fio->ev_qing)) != NULL) {
992                 fbevp->rv = error;
993                 LIST_REMOVE(fbevp, pfe_entries);
994                 puffs__goto(fbevp->pcc);
995         }
996
997         /* don't bother with realloc */
998         pu->pu_nevs -= 2;
999
1000         /* don't free us yet, might have some references in event arrays */
1001         fio->stat |= FIO_DEAD;
1002         LIST_INSERT_HEAD(&pu->pu_ios_rmlist, fio, fio_entries);
1003
1004         return 0;
1005
1006 }
1007
1008 int
1009 puffs_framev_removefd(struct puffs_usermount *pu, int fd, int error)
1010 {
1011         struct puffs_fctrl_io *fio;
1012
1013         fio = getfiobyfd(pu, fd);
1014         if (fio == NULL) {
1015                 errno = ENXIO;
1016                 return -1;
1017         }
1018
1019         return removefio(pu, fio, error ? error : ECONNRESET);
1020 }
1021
1022 void
1023 puffs_framev_removeonclose(struct puffs_usermount *pu, int fd, int what)
1024 {
1025
1026         if (what == (PUFFS_FBIO_READ | PUFFS_FBIO_WRITE))
1027                 (void) puffs_framev_removefd(pu, fd, ECONNRESET);
1028 }
1029
1030 void
1031 puffs_framev_unmountonclose(struct puffs_usermount *pu, int fd, int what)
1032 {
1033
1034         /* XXX & X: unmount is non-sensible */
1035         puffs_framev_removeonclose(pu, fd, what);
1036         if (what == (PUFFS_FBIO_READ | PUFFS_FBIO_WRITE))
1037                 PU_SETSTATE(pu, PUFFS_STATE_UNMOUNTED);
1038 }
1039
1040 void
1041 puffs_framev_init(struct puffs_usermount *pu,
1042         puffs_framev_readframe_fn rfb, puffs_framev_writeframe_fn wfb,
1043         puffs_framev_cmpframe_fn cmpfb, puffs_framev_gotframe_fn gotfb,
1044         puffs_framev_fdnotify_fn fdnotfn)
1045 {
1046         struct puffs_framectrl *pfctrl;
1047
1048         pfctrl = &pu->pu_framectrl[PU_FRAMECTRL_USER];
1049         pfctrl->rfb = rfb;
1050         pfctrl->wfb = wfb;
1051         pfctrl->cmpfb = cmpfb;
1052         pfctrl->gotfb = gotfb;
1053         pfctrl->fdnotfn = fdnotfn;
1054 }
1055
1056 void
1057 puffs__framev_exit(struct puffs_usermount *pu)
1058 {
1059         struct puffs_fctrl_io *fio;
1060
1061         while ((fio = LIST_FIRST(&pu->pu_ios)) != NULL)
1062                 removefio(pu, fio, ENXIO);
1063         free(pu->pu_evs);
1064
1065         /* closing pu->pu_kq takes care of puffsfd */
1066 }