2007-05-11 20:22:38 +04:00
|
|
|
/* $NetBSD: framebuf.c,v 1.5 2007/05/11 16:22:38 pooka Exp $ */
|
2007-05-05 19:48:18 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2007 Antti Kantee. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Development of this software was supported by the
|
|
|
|
* Finnish Cultural Foundation.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
|
|
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
|
|
|
#if !defined(lint)
|
2007-05-11 20:22:38 +04:00
|
|
|
__RCSID("$NetBSD: framebuf.c,v 1.5 2007/05/11 16:22:38 pooka Exp $");
|
2007-05-05 19:48:18 +04:00
|
|
|
#endif /* !lint */
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/queue.h>
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <poll.h>
|
|
|
|
#include <puffs.h>
|
|
|
|
#include <stdlib.h>
|
2007-05-11 20:22:38 +04:00
|
|
|
#include <unistd.h>
|
2007-05-05 19:48:18 +04:00
|
|
|
|
|
|
|
#include "puffs_priv.h"
|
|
|
|
|
|
|
|
struct puffs_framebuf {
|
|
|
|
struct puffs_cc *pcc; /* pcc to continue with */
|
|
|
|
/* OR */
|
|
|
|
puffs_framebuf_cb fcb; /* non-blocking callback */
|
|
|
|
void *fcb_arg; /* argument for previous */
|
|
|
|
|
|
|
|
uint8_t *buf; /* buffer base */
|
|
|
|
size_t len; /* total length */
|
|
|
|
|
|
|
|
size_t offset; /* cursor, telloff() */
|
|
|
|
size_t maxoff; /* maximum offset for data, tellsize() */
|
|
|
|
|
|
|
|
volatile int rv; /* errno value for pcc framebufs */
|
|
|
|
|
|
|
|
int istat;
|
|
|
|
|
|
|
|
TAILQ_ENTRY(puffs_framebuf) pfb_entries;
|
|
|
|
};
|
|
|
|
#define ISTAT_NODESTROY 0x01 /* indestructible by framebuf_destroy() */
|
|
|
|
#define ISTAT_INTERNAL 0x02 /* never leaves library */
|
|
|
|
#define ISTAT_NOREPLY 0x04 /* nuke after sending */
|
|
|
|
|
2007-05-11 20:22:38 +04:00
|
|
|
struct puffs_fctrl_io {
|
2007-05-05 19:48:18 +04:00
|
|
|
int io_fd;
|
2007-05-11 20:22:38 +04:00
|
|
|
int wrstat;
|
2007-05-05 19:48:18 +04:00
|
|
|
|
|
|
|
struct puffs_framebuf *cur_in;
|
|
|
|
|
|
|
|
TAILQ_HEAD(, puffs_framebuf) snd_qing; /* queueing to be sent */
|
|
|
|
TAILQ_HEAD(, puffs_framebuf) res_qing; /* q'ing for rescue */
|
2007-05-11 20:22:38 +04:00
|
|
|
|
|
|
|
LIST_ENTRY(puffs_fctrl_io) fio_entries;
|
|
|
|
};
|
|
|
|
#define EN_WRITE(fio) (fio->wrstat == 0 && !TAILQ_EMPTY(&fio->snd_qing))
|
|
|
|
#define RM_WRITE(fio) (fio->wrstat == 1 && TAILQ_EMPTY(&fio->snd_qing))
|
|
|
|
|
|
|
|
struct puffs_framectrl {
|
|
|
|
puffs_framebuf_readframe_fn rfb;
|
|
|
|
puffs_framebuf_writeframe_fn wfb;
|
|
|
|
puffs_framebuf_respcmp_fn cmpfb;
|
|
|
|
|
|
|
|
struct kevent *evs;
|
|
|
|
struct kevent *ch_evs;
|
|
|
|
size_t nevs;
|
|
|
|
int kq;
|
|
|
|
|
|
|
|
LIST_HEAD(, puffs_fctrl_io) fb_ios;
|
2007-05-05 19:48:18 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
#define PUFBUF_INCRALLOC 65536 /* 64k ought to be enough for anyone */
|
|
|
|
#define PUFBUF_REMAIN(p) (p->len - p->offset)
|
|
|
|
|
2007-05-11 20:22:38 +04:00
|
|
|
static struct puffs_fctrl_io *
|
|
|
|
getfiobyfd(struct puffs_usermount *pu, int fd)
|
|
|
|
{
|
|
|
|
struct puffs_fctrl_io *fio;
|
|
|
|
|
|
|
|
LIST_FOREACH(fio, &pu->pu_framectrl->fb_ios, fio_entries)
|
|
|
|
if (fio->io_fd == fd)
|
|
|
|
return fio;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2007-05-05 19:48:18 +04:00
|
|
|
struct puffs_framebuf *
|
|
|
|
puffs_framebuf_make()
|
|
|
|
{
|
|
|
|
struct puffs_framebuf *pufbuf;
|
|
|
|
|
|
|
|
pufbuf = malloc(sizeof(struct puffs_framebuf));
|
|
|
|
if (pufbuf == NULL)
|
|
|
|
return NULL;
|
|
|
|
memset(pufbuf, 0, sizeof(struct puffs_framebuf));
|
|
|
|
|
|
|
|
pufbuf->buf = malloc(PUFBUF_INCRALLOC);
|
|
|
|
pufbuf->len = PUFBUF_INCRALLOC;
|
|
|
|
if (pufbuf->buf == NULL) {
|
|
|
|
free(pufbuf);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
puffs_framebuf_recycle(pufbuf);
|
|
|
|
return pufbuf;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
puffs_framebuf_destroy(struct puffs_framebuf *pufbuf)
|
|
|
|
{
|
|
|
|
|
|
|
|
assert((pufbuf->istat & ISTAT_NODESTROY) == 0);
|
|
|
|
|
|
|
|
free(pufbuf->buf);
|
|
|
|
free(pufbuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
puffs_framebuf_recycle(struct puffs_framebuf *pufbuf)
|
|
|
|
{
|
|
|
|
|
|
|
|
assert((pufbuf->istat & ISTAT_NODESTROY) == 0);
|
|
|
|
|
|
|
|
pufbuf->offset = 0;
|
|
|
|
pufbuf->maxoff = 0;
|
|
|
|
pufbuf->istat = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
reservespace(struct puffs_framebuf *pufbuf, size_t off, size_t wantsize)
|
|
|
|
{
|
|
|
|
size_t incr;
|
|
|
|
void *nd;
|
|
|
|
|
|
|
|
if (off <= pufbuf->len && pufbuf->len - off >= wantsize)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (incr = PUFBUF_INCRALLOC;
|
|
|
|
pufbuf->len + incr < off + wantsize;
|
|
|
|
incr += PUFBUF_INCRALLOC)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
nd = realloc(pufbuf->buf, pufbuf->offset + incr);
|
|
|
|
if (nd == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
pufbuf->buf = nd;
|
|
|
|
pufbuf->len += incr;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
puffs_framebuf_reserve_space(struct puffs_framebuf *pufbuf, size_t wantsize)
|
|
|
|
{
|
|
|
|
|
|
|
|
return reservespace(pufbuf, pufbuf->offset, wantsize);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
puffs_framebuf_putdata(struct puffs_framebuf *pufbuf,
|
|
|
|
const void *data, size_t dlen)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (PUFBUF_REMAIN(pufbuf) < dlen)
|
|
|
|
if (puffs_framebuf_reserve_space(pufbuf, dlen) == -1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
memcpy(pufbuf->buf + pufbuf->offset, data, dlen);
|
|
|
|
pufbuf->offset += dlen;
|
|
|
|
|
|
|
|
if (pufbuf->offset > pufbuf->maxoff)
|
|
|
|
pufbuf->maxoff = pufbuf->offset;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
puffs_framebuf_putdata_atoff(struct puffs_framebuf *pufbuf, size_t offset,
|
|
|
|
const void *data, size_t dlen)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (reservespace(pufbuf, offset, dlen) == -1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
memcpy(pufbuf->buf + offset, data, dlen);
|
|
|
|
|
|
|
|
if (offset + dlen > pufbuf->maxoff)
|
|
|
|
pufbuf->maxoff = offset + dlen;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
puffs_framebuf_getdata(struct puffs_framebuf *pufbuf, void *data, size_t dlen)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (pufbuf->maxoff < pufbuf->offset + dlen) {
|
|
|
|
errno = ENOBUFS;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(data, pufbuf->buf + pufbuf->offset, dlen);
|
|
|
|
pufbuf->offset += dlen;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
puffs_framebuf_getdata_atoff(struct puffs_framebuf *pufbuf, size_t offset,
|
|
|
|
void *data, size_t dlen)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (pufbuf->maxoff < offset + dlen) {
|
|
|
|
errno = ENOBUFS;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(data, pufbuf->buf + offset, dlen);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t
|
|
|
|
puffs_framebuf_telloff(struct puffs_framebuf *pufbuf)
|
|
|
|
{
|
|
|
|
|
|
|
|
return pufbuf->offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t
|
|
|
|
puffs_framebuf_tellsize(struct puffs_framebuf *pufbuf)
|
|
|
|
{
|
|
|
|
|
|
|
|
return pufbuf->maxoff;
|
|
|
|
}
|
|
|
|
|
2007-05-06 14:54:41 +04:00
|
|
|
size_t
|
|
|
|
puffs_framebuf_remaining(struct puffs_framebuf *pufbuf)
|
|
|
|
{
|
|
|
|
|
|
|
|
return puffs_framebuf_tellsize(pufbuf) - puffs_framebuf_telloff(pufbuf);
|
|
|
|
}
|
|
|
|
|
2007-05-05 19:48:18 +04:00
|
|
|
int
|
|
|
|
puffs_framebuf_seekset(struct puffs_framebuf *pufbuf, size_t newoff)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (reservespace(pufbuf, newoff, 0) == -1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
pufbuf->offset = newoff;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
puffs_framebuf_getwindow(struct puffs_framebuf *pufbuf, size_t winoff,
|
|
|
|
void **data, size_t *dlen)
|
|
|
|
{
|
|
|
|
size_t winlen;
|
|
|
|
|
|
|
|
#ifdef WINTESTING
|
|
|
|
winlen = MIN(*dlen, 32);
|
|
|
|
#else
|
|
|
|
winlen = *dlen;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (reservespace(pufbuf, winoff, winlen) == -1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
*data = pufbuf->buf + winoff;
|
|
|
|
if (pufbuf->maxoff < winoff + winlen)
|
|
|
|
pufbuf->maxoff = winoff + winlen;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-05-11 20:22:38 +04:00
|
|
|
puffs_framebuf_enqueue_cc(struct puffs_cc *pcc, int fd,
|
|
|
|
struct puffs_framebuf *pufbuf)
|
2007-05-05 19:48:18 +04:00
|
|
|
{
|
|
|
|
struct puffs_usermount *pu = puffs_cc_getusermount(pcc);
|
2007-05-11 20:22:38 +04:00
|
|
|
struct puffs_fctrl_io *fio;
|
|
|
|
|
|
|
|
fio = getfiobyfd(pu, fd);
|
|
|
|
if (fio == NULL) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
2007-05-05 19:48:18 +04:00
|
|
|
|
|
|
|
pufbuf->pcc = pcc;
|
|
|
|
pufbuf->fcb = NULL;
|
|
|
|
pufbuf->fcb_arg = NULL;
|
|
|
|
|
|
|
|
pufbuf->offset = 0;
|
|
|
|
pufbuf->istat |= ISTAT_NODESTROY;
|
|
|
|
|
2007-05-11 20:22:38 +04:00
|
|
|
TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
|
2007-05-05 19:48:18 +04:00
|
|
|
|
|
|
|
puffs_cc_yield(pcc);
|
|
|
|
if (pufbuf->rv) {
|
|
|
|
errno = pufbuf->rv;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-05-11 20:22:38 +04:00
|
|
|
int
|
|
|
|
puffs_framebuf_enqueue_cb(struct puffs_usermount *pu, int fd,
|
2007-05-05 19:48:18 +04:00
|
|
|
struct puffs_framebuf *pufbuf, puffs_framebuf_cb fcb, void *arg)
|
|
|
|
{
|
2007-05-11 20:22:38 +04:00
|
|
|
struct puffs_fctrl_io *fio;
|
|
|
|
|
|
|
|
fio = getfiobyfd(pu, fd);
|
|
|
|
if (fio == NULL) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
2007-05-05 19:48:18 +04:00
|
|
|
|
|
|
|
pufbuf->pcc = NULL;
|
|
|
|
pufbuf->fcb = fcb;
|
|
|
|
pufbuf->fcb_arg = arg;
|
|
|
|
|
|
|
|
pufbuf->offset = 0;
|
|
|
|
pufbuf->istat |= ISTAT_NODESTROY;
|
|
|
|
|
2007-05-11 20:22:38 +04:00
|
|
|
TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
|
|
|
|
|
|
|
|
return 0;
|
2007-05-05 19:48:18 +04:00
|
|
|
}
|
|
|
|
|
2007-05-11 20:22:38 +04:00
|
|
|
int
|
|
|
|
puffs_framebuf_enqueue_justsend(struct puffs_usermount *pu, int fd,
|
2007-05-05 19:48:18 +04:00
|
|
|
struct puffs_framebuf *pufbuf, int reply)
|
|
|
|
{
|
2007-05-11 20:22:38 +04:00
|
|
|
struct puffs_fctrl_io *fio;
|
|
|
|
|
|
|
|
fio = getfiobyfd(pu, fd);
|
|
|
|
if (fio == NULL) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
2007-05-05 19:48:18 +04:00
|
|
|
|
|
|
|
pufbuf->pcc = NULL;
|
|
|
|
pufbuf->fcb = NULL;
|
|
|
|
pufbuf->fcb_arg = NULL;
|
|
|
|
|
|
|
|
pufbuf->offset = 0;
|
|
|
|
pufbuf->istat |= ISTAT_NODESTROY;
|
|
|
|
if (!reply)
|
|
|
|
pufbuf->istat |= ISTAT_NOREPLY;
|
|
|
|
|
2007-05-11 20:22:38 +04:00
|
|
|
TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
|
|
|
|
|
|
|
|
return 0;
|
2007-05-05 19:48:18 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct puffs_framebuf *
|
|
|
|
findbuf(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
|
2007-05-11 20:22:38 +04:00
|
|
|
struct puffs_fctrl_io *fio, struct puffs_framebuf *findme)
|
2007-05-05 19:48:18 +04:00
|
|
|
{
|
|
|
|
struct puffs_framebuf *cand;
|
|
|
|
|
2007-05-11 20:22:38 +04:00
|
|
|
TAILQ_FOREACH(cand, &fio->res_qing, pfb_entries)
|
2007-05-05 19:48:18 +04:00
|
|
|
if (fctrl->cmpfb(pu, findme, cand))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (cand == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2007-05-11 20:22:38 +04:00
|
|
|
TAILQ_REMOVE(&fio->res_qing, cand, pfb_entries);
|
2007-05-05 19:48:18 +04:00
|
|
|
return cand;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
moveinfo(struct puffs_framebuf *from, struct puffs_framebuf *to)
|
|
|
|
{
|
|
|
|
|
|
|
|
assert(from->istat & ISTAT_INTERNAL);
|
|
|
|
|
|
|
|
/* migrate buffer */
|
|
|
|
free(to->buf);
|
|
|
|
to->buf = from->buf;
|
|
|
|
from->buf = NULL;
|
|
|
|
|
|
|
|
/* migrate buffer info */
|
|
|
|
to->len = from->len;
|
|
|
|
to->offset = from->offset;
|
|
|
|
to->maxoff = from->maxoff;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
handle_input(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
|
2007-05-11 20:22:38 +04:00
|
|
|
struct puffs_fctrl_io *fio, struct puffs_putreq *ppr)
|
2007-05-05 19:48:18 +04:00
|
|
|
{
|
|
|
|
struct puffs_framebuf *pufbuf, *appbuf;
|
|
|
|
int rv, complete;
|
|
|
|
|
|
|
|
for (;;) {
|
2007-05-11 20:22:38 +04:00
|
|
|
if ((pufbuf = fio->cur_in) == NULL) {
|
2007-05-05 19:48:18 +04:00
|
|
|
pufbuf = puffs_framebuf_make();
|
|
|
|
if (pufbuf == NULL)
|
|
|
|
return -1;
|
|
|
|
pufbuf->istat |= ISTAT_INTERNAL;
|
2007-05-11 20:22:38 +04:00
|
|
|
fio->cur_in = pufbuf;
|
2007-05-05 19:48:18 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
complete = 0;
|
2007-05-11 20:22:38 +04:00
|
|
|
rv = fctrl->rfb(pu, pufbuf, fio->io_fd, &complete);
|
2007-05-05 19:48:18 +04:00
|
|
|
|
|
|
|
/* error */
|
|
|
|
if (rv) {
|
|
|
|
errno = rv;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* partial read, come back to fight another day */
|
|
|
|
if (complete == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* else: full read, process */
|
|
|
|
|
2007-05-11 20:22:38 +04:00
|
|
|
appbuf = findbuf(pu, fctrl, fio, pufbuf);
|
2007-05-05 19:48:18 +04:00
|
|
|
if (appbuf == NULL) {
|
|
|
|
errno = ENOMSG;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
appbuf->istat &= ~ISTAT_NODESTROY;
|
|
|
|
moveinfo(pufbuf, appbuf);
|
|
|
|
if (appbuf->pcc) {
|
|
|
|
puffs_docc(appbuf->pcc, ppr);
|
|
|
|
} else if (appbuf->fcb) {
|
|
|
|
appbuf->fcb(pu, appbuf, appbuf->fcb_arg);
|
|
|
|
} else {
|
|
|
|
puffs_framebuf_destroy(appbuf);
|
|
|
|
}
|
|
|
|
puffs_framebuf_destroy(pufbuf);
|
|
|
|
|
|
|
|
/* hopeless romantics, here we go again */
|
2007-05-11 20:22:38 +04:00
|
|
|
fio->cur_in = NULL;
|
2007-05-05 19:48:18 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2007-05-11 20:22:38 +04:00
|
|
|
handle_output(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
|
|
|
|
struct puffs_fctrl_io *fio)
|
2007-05-05 19:48:18 +04:00
|
|
|
{
|
|
|
|
struct puffs_framebuf *pufbuf, *pufbuf_next;
|
|
|
|
int rv, complete;
|
|
|
|
|
2007-05-11 20:22:38 +04:00
|
|
|
for (pufbuf = TAILQ_FIRST(&fio->snd_qing);
|
2007-05-05 19:48:18 +04:00
|
|
|
pufbuf;
|
|
|
|
pufbuf = pufbuf_next) {
|
|
|
|
complete = 0;
|
|
|
|
pufbuf_next = TAILQ_NEXT(pufbuf, pfb_entries);
|
2007-05-11 20:22:38 +04:00
|
|
|
rv = fctrl->wfb(pu, pufbuf, fio->io_fd, &complete);
|
2007-05-05 19:48:18 +04:00
|
|
|
|
|
|
|
if (rv) {
|
2007-05-11 20:22:38 +04:00
|
|
|
TAILQ_REMOVE(&fio->snd_qing, pufbuf, pfb_entries);
|
2007-05-05 19:48:18 +04:00
|
|
|
pufbuf->rv = rv;
|
|
|
|
errno = rv;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* partial write */
|
|
|
|
if (complete == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* else, complete write */
|
2007-05-11 20:22:38 +04:00
|
|
|
TAILQ_REMOVE(&fio->snd_qing, pufbuf, pfb_entries);
|
2007-05-05 19:48:18 +04:00
|
|
|
|
|
|
|
if ((pufbuf->istat & ISTAT_NOREPLY) == 0) {
|
2007-05-11 20:22:38 +04:00
|
|
|
TAILQ_INSERT_TAIL(&fio->res_qing, pufbuf,
|
2007-05-05 19:48:18 +04:00
|
|
|
pfb_entries);
|
|
|
|
} else {
|
|
|
|
pufbuf->istat &= ~ISTAT_NODESTROY;
|
|
|
|
puffs_framebuf_destroy(pufbuf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2007-05-11 20:22:38 +04:00
|
|
|
puffs_framebuf_addfd(struct puffs_usermount *pu, int fd)
|
|
|
|
{
|
|
|
|
struct puffs_framectrl *pfctrl = pu->pu_framectrl;
|
|
|
|
struct puffs_fctrl_io *fio;
|
|
|
|
struct kevent kev[2];
|
|
|
|
struct kevent *newevs;
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
newevs = realloc(pfctrl->evs, (2*pfctrl->nevs+1)*sizeof(struct kevent));
|
|
|
|
if (newevs == NULL)
|
|
|
|
return -1;
|
|
|
|
pfctrl->evs = newevs;
|
|
|
|
|
|
|
|
newevs = realloc(pfctrl->ch_evs, pfctrl->nevs * sizeof(struct kevent));
|
|
|
|
if (newevs == NULL)
|
|
|
|
return -1;
|
|
|
|
pfctrl->ch_evs = newevs;
|
|
|
|
|
|
|
|
fio = malloc(sizeof(struct puffs_fctrl_io));
|
|
|
|
if (fio == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
EV_SET(&kev[0], fd, EVFILT_READ, EV_ADD, 0, 0, (intptr_t)fio);
|
|
|
|
EV_SET(&kev[1], fd, EVFILT_WRITE, EV_ADD|EV_DISABLE, 0,0,(intptr_t)fio);
|
|
|
|
rv = kevent(pfctrl->kq, kev, 2, NULL, 0, NULL);
|
|
|
|
if (rv == -1) {
|
|
|
|
free(fio);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
fio->io_fd = fd;
|
|
|
|
fio->cur_in = NULL;
|
|
|
|
fio->wrstat = 0;
|
|
|
|
TAILQ_INIT(&fio->snd_qing);
|
|
|
|
TAILQ_INIT(&fio->res_qing);
|
|
|
|
|
|
|
|
LIST_INSERT_HEAD(&pfctrl->fb_ios, fio, fio_entries);
|
|
|
|
pfctrl->nevs++;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
removefio(struct puffs_usermount *pu, struct puffs_fctrl_io *fio)
|
|
|
|
{
|
|
|
|
struct puffs_framectrl *pfctrl = pu->pu_framectrl;
|
|
|
|
struct puffs_framebuf *pufbuf;
|
|
|
|
struct kevent kev[2];
|
|
|
|
|
|
|
|
/* found, now remove */
|
|
|
|
EV_SET(&kev[0], fio->io_fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
|
|
|
|
EV_SET(&kev[1], fio->io_fd, EVFILT_WRITE, EV_DELETE, 0, 0, 0);
|
|
|
|
(void) kevent(pfctrl->kq, kev, 2, NULL, 0, NULL);
|
|
|
|
|
|
|
|
LIST_REMOVE(fio, fio_entries);
|
|
|
|
|
|
|
|
/* free buffers */
|
|
|
|
while ((pufbuf = TAILQ_FIRST(&fio->snd_qing)) != NULL) {
|
|
|
|
TAILQ_REMOVE(&fio->snd_qing, pufbuf, pfb_entries);
|
|
|
|
puffs_framebuf_destroy(pufbuf);
|
|
|
|
}
|
|
|
|
while ((pufbuf = TAILQ_FIRST(&fio->res_qing)) != NULL) {
|
|
|
|
TAILQ_REMOVE(&fio->res_qing, pufbuf, pfb_entries);
|
|
|
|
puffs_framebuf_destroy(pufbuf);
|
|
|
|
}
|
|
|
|
if (fio->cur_in)
|
|
|
|
puffs_framebuf_destroy(fio->cur_in);
|
|
|
|
free(fio);
|
|
|
|
|
|
|
|
/* don't bother with realloc */
|
|
|
|
pfctrl->nevs--;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
puffs_framebuf_removefd(struct puffs_usermount *pu, int fd)
|
|
|
|
{
|
|
|
|
struct puffs_fctrl_io *fio;
|
|
|
|
|
|
|
|
fio = getfiobyfd(pu, fd);
|
|
|
|
if (fio == NULL) {
|
|
|
|
errno = ENXIO;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return removefio(pu, fio);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
puffs_framebuf_eventloop(struct puffs_usermount *pu, int *io_fds, size_t nfds,
|
2007-05-05 19:48:18 +04:00
|
|
|
puffs_framebuf_readframe_fn rfb, puffs_framebuf_writeframe_fn wfb,
|
|
|
|
puffs_framebuf_respcmp_fn cmpfb,
|
2007-05-06 14:21:45 +04:00
|
|
|
puffs_framebuf_loop_fn lfb)
|
2007-05-05 19:48:18 +04:00
|
|
|
{
|
|
|
|
struct puffs_getreq *pgr = NULL;
|
|
|
|
struct puffs_putreq *ppr = NULL;
|
|
|
|
struct puffs_framectrl *pfctrl = NULL;
|
2007-05-11 20:22:38 +04:00
|
|
|
struct puffs_fctrl_io *fio;
|
|
|
|
struct kevent kev;
|
|
|
|
struct kevent *curev;
|
|
|
|
size_t nchanges;
|
|
|
|
int puffsfd, sverrno;
|
|
|
|
int ndone;
|
2007-05-05 19:48:18 +04:00
|
|
|
|
|
|
|
assert(puffs_getstate(pu) >= PUFFS_STATE_RUNNING);
|
|
|
|
|
|
|
|
pgr = puffs_req_makeget(pu, puffs_getmaxreqlen(pu), 0);
|
|
|
|
if (pgr == NULL)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
ppr = puffs_req_makeput(pu);
|
|
|
|
if (ppr == NULL)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
pfctrl = malloc(sizeof(struct puffs_framectrl));
|
|
|
|
if (pfctrl == NULL)
|
|
|
|
goto out;
|
|
|
|
pfctrl->rfb = rfb;
|
|
|
|
pfctrl->wfb = wfb;
|
|
|
|
pfctrl->cmpfb = cmpfb;
|
2007-05-11 20:22:38 +04:00
|
|
|
pfctrl->evs = malloc(sizeof(struct kevent));
|
|
|
|
if (pfctrl->evs == NULL)
|
|
|
|
goto out;
|
|
|
|
pfctrl->ch_evs = NULL;
|
|
|
|
pfctrl->nevs = 1;
|
|
|
|
pfctrl->kq = kqueue();
|
|
|
|
if (pfctrl->kq == -1)
|
|
|
|
goto out;
|
|
|
|
LIST_INIT(&pfctrl->fb_ios);
|
2007-05-05 19:48:18 +04:00
|
|
|
pu->pu_framectrl = pfctrl;
|
|
|
|
|
2007-05-11 20:22:38 +04:00
|
|
|
for (; nfds--; io_fds++)
|
|
|
|
if (puffs_framebuf_addfd(pu, *io_fds) == -1)
|
|
|
|
goto out;
|
|
|
|
|
2007-05-05 19:48:18 +04:00
|
|
|
puffsfd = puffs_getselectable(pu);
|
2007-05-11 20:22:38 +04:00
|
|
|
EV_SET(&kev, puffsfd, EVFILT_READ, EV_ADD, 0, 0, 0);
|
|
|
|
if (kevent(pfctrl->kq, &kev, 1, NULL, 0, NULL) == -1)
|
|
|
|
goto out;
|
|
|
|
|
2007-05-05 19:48:18 +04:00
|
|
|
while (puffs_getstate(pu) != PUFFS_STATE_UNMOUNTED) {
|
|
|
|
if (lfb)
|
2007-05-06 14:21:45 +04:00
|
|
|
lfb(pu);
|
2007-05-05 19:48:18 +04:00
|
|
|
|
2007-05-11 20:22:38 +04:00
|
|
|
/*
|
|
|
|
* Build list of which to enable/disable in writecheck.
|
|
|
|
* Don't bother worrying about O(n) for now.
|
|
|
|
*/
|
|
|
|
nchanges = 0;
|
|
|
|
LIST_FOREACH(fio, &pfctrl->fb_ios, fio_entries) {
|
|
|
|
/*
|
|
|
|
* Try to write out everything to avoid the
|
|
|
|
* need for enabling EVFILT_WRITE. The likely
|
|
|
|
* case is that we can fit everything into the
|
|
|
|
* socket buffer.
|
|
|
|
*/
|
|
|
|
if (handle_output(pu, pfctrl, fio) == -1)
|
2007-05-05 19:48:18 +04:00
|
|
|
goto out;
|
|
|
|
|
2007-05-11 20:22:38 +04:00
|
|
|
assert((EN_WRITE(fio) && RM_WRITE(fio)) == 0);
|
|
|
|
if (EN_WRITE(fio)) {
|
|
|
|
EV_SET(&pfctrl->ch_evs[nchanges], fio->io_fd,
|
|
|
|
EVFILT_WRITE, EV_ENABLE, 0, 0,
|
|
|
|
(uintptr_t)fio);
|
|
|
|
fio->wrstat = 1; /* XXX: not before call */
|
|
|
|
nchanges++;
|
|
|
|
}
|
|
|
|
if (RM_WRITE(fio)) {
|
|
|
|
EV_SET(&pfctrl->ch_evs[nchanges], fio->io_fd,
|
|
|
|
EVFILT_WRITE, EV_DISABLE, 0, 0,
|
|
|
|
(uintptr_t)fio);
|
|
|
|
fio->wrstat = 0; /* XXX: not before call */
|
|
|
|
nchanges++;
|
|
|
|
}
|
|
|
|
}
|
2007-05-05 19:48:18 +04:00
|
|
|
|
2007-05-11 20:22:38 +04:00
|
|
|
ndone = kevent(pfctrl->kq, pfctrl->ch_evs, nchanges,
|
|
|
|
pfctrl->evs, pfctrl->nevs, NULL);
|
|
|
|
if (ndone == -1)
|
2007-05-05 19:48:18 +04:00
|
|
|
goto out;
|
|
|
|
|
2007-05-11 20:22:38 +04:00
|
|
|
/* XXX: handle errors */
|
|
|
|
|
|
|
|
/* iterate over the results */
|
|
|
|
for (curev = pfctrl->evs; ndone--; curev++) {
|
|
|
|
/* get & possibly dispatch events from kernel */
|
|
|
|
if (curev->ident == puffsfd) {
|
|
|
|
if (puffs_req_handle(pgr, ppr, 0) == -1)
|
|
|
|
goto out;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (curev->filter == EVFILT_READ) {
|
|
|
|
if (handle_input(pu, pfctrl,
|
|
|
|
(void *)curev->udata, ppr) == -1)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (curev->filter == EVFILT_WRITE) {
|
|
|
|
if (handle_output(pu, pfctrl,
|
|
|
|
(void *)curev->udata) == -1)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-05-05 19:48:18 +04:00
|
|
|
/* stuff all replies from both of the above into kernel */
|
|
|
|
if (puffs_req_putput(ppr) == -1)
|
|
|
|
goto out;
|
|
|
|
puffs_req_resetput(ppr);
|
|
|
|
}
|
|
|
|
errno = 0;
|
|
|
|
|
|
|
|
out:
|
2007-05-11 20:22:38 +04:00
|
|
|
/* store the real error for a while */
|
|
|
|
sverrno = errno;
|
|
|
|
|
2007-05-05 19:48:18 +04:00
|
|
|
if (pfctrl) {
|
2007-05-11 20:22:38 +04:00
|
|
|
while ((fio = LIST_FIRST(&pfctrl->fb_ios)) != NULL)
|
|
|
|
removefio(pu, fio);
|
2007-05-05 19:48:18 +04:00
|
|
|
pu->pu_framectrl = NULL;
|
2007-05-11 20:22:38 +04:00
|
|
|
free(pfctrl->evs);
|
|
|
|
close(pfctrl->kq); /* takes care of puffsfd */
|
2007-05-05 19:48:18 +04:00
|
|
|
free(pfctrl);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ppr)
|
|
|
|
puffs_req_destroyput(ppr);
|
|
|
|
if (pgr)
|
|
|
|
puffs_req_destroyget(pgr);
|
|
|
|
|
2007-05-11 20:22:38 +04:00
|
|
|
errno = sverrno;
|
|
|
|
if (errno)
|
|
|
|
return -1;
|
|
|
|
else
|
|
|
|
return 0;
|
2007-05-05 19:48:18 +04:00
|
|
|
}
|