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