1074 lines
23 KiB
C
1074 lines
23 KiB
C
/* $NetBSD: framebuf.c,v 1.36 2021/10/30 10:34:18 nia Exp $ */
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* The event portion of this code is a twisty maze of pointers,
|
|
* flags, yields and continues. Sincere aplogies.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
#if !defined(lint)
|
|
__RCSID("$NetBSD: framebuf.c,v 1.36 2021/10/30 10:34:18 nia Exp $");
|
|
#endif /* !lint */
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/queue.h>
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <poll.h>
|
|
#include <puffs.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include "puffs_priv.h"
|
|
|
|
struct puffs_framebuf {
|
|
struct puffs_cc *pcc; /* pcc to continue with */
|
|
/* OR */
|
|
puffs_framev_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 */
|
|
|
|
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 */
|
|
#define ISTAT_DIRECT 0x08 /* receive directly, no moveinfo */
|
|
|
|
#define ISTAT_ONQUEUE ISTAT_NODESTROY /* alias */
|
|
|
|
#define PUFBUF_INCRALLOC 4096
|
|
#define PUFBUF_REMAIN(p) (p->len - p->offset)
|
|
|
|
/* for poll/kqueue */
|
|
struct puffs_fbevent {
|
|
struct puffs_cc *pcc;
|
|
int what;
|
|
volatile int rv;
|
|
|
|
LIST_ENTRY(puffs_fbevent) pfe_entries;
|
|
};
|
|
|
|
static struct puffs_fctrl_io *
|
|
getfiobyfd(struct puffs_usermount *pu, int fd)
|
|
{
|
|
struct puffs_fctrl_io *fio;
|
|
|
|
LIST_FOREACH(fio, &pu->pu_ios, fio_entries)
|
|
if (fio->io_fd == fd)
|
|
return fio;
|
|
return NULL;
|
|
}
|
|
|
|
struct puffs_framebuf *
|
|
puffs_framebuf_make(void)
|
|
{
|
|
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);
|
|
if (pufbuf->buf == NULL) {
|
|
free(pufbuf);
|
|
return NULL;
|
|
}
|
|
pufbuf->len = PUFBUF_INCRALLOC;
|
|
|
|
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->len + incr);
|
|
if (nd == NULL)
|
|
return -1;
|
|
|
|
pufbuf->buf = nd;
|
|
pufbuf->len += incr;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
puffs_framebuf_dup(struct puffs_framebuf *pb, struct puffs_framebuf **pbp)
|
|
{
|
|
struct puffs_framebuf *newpb;
|
|
|
|
newpb = puffs_framebuf_make();
|
|
if (newpb == NULL) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
memcpy(newpb, pb, sizeof(struct puffs_framebuf));
|
|
|
|
newpb->buf = NULL;
|
|
newpb->len = 0;
|
|
if (reservespace(newpb, 0, pb->maxoff) == -1) {
|
|
puffs_framebuf_destroy(newpb);
|
|
return -1;
|
|
}
|
|
|
|
memcpy(newpb->buf, pb->buf, pb->maxoff);
|
|
newpb->istat = 0;
|
|
*pbp = newpb;
|
|
|
|
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;
|
|
}
|
|
|
|
size_t
|
|
puffs_framebuf_remaining(struct puffs_framebuf *pufbuf)
|
|
{
|
|
|
|
return puffs_framebuf_tellsize(pufbuf) - puffs_framebuf_telloff(pufbuf);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
void *
|
|
puffs__framebuf_getdataptr(struct puffs_framebuf *pufbuf)
|
|
{
|
|
|
|
return pufbuf->buf;
|
|
}
|
|
|
|
static void
|
|
errnotify(struct puffs_usermount *pu, struct puffs_framebuf *pufbuf, int error)
|
|
{
|
|
|
|
pufbuf->rv = error;
|
|
if (pufbuf->pcc) {
|
|
puffs__goto(pufbuf->pcc);
|
|
} else if (pufbuf->fcb) {
|
|
pufbuf->istat &= ~ISTAT_NODESTROY;
|
|
pufbuf->fcb(pu, pufbuf, pufbuf->fcb_arg, error);
|
|
} else {
|
|
pufbuf->istat &= ~ISTAT_NODESTROY;
|
|
puffs_framebuf_destroy(pufbuf);
|
|
}
|
|
}
|
|
|
|
#define GETFIO(fd) \
|
|
do { \
|
|
fio = getfiobyfd(pu, fd); \
|
|
if (fio == NULL) { \
|
|
errno = EINVAL; \
|
|
return -1; \
|
|
} \
|
|
if (fio->stat & FIO_WRGONE) { \
|
|
errno = ESHUTDOWN; \
|
|
return -1; \
|
|
} \
|
|
} while (/*CONSTCOND*/0)
|
|
|
|
int
|
|
puffs_framev_enqueue_cc(struct puffs_cc *pcc, int fd,
|
|
struct puffs_framebuf *pufbuf, int flags)
|
|
{
|
|
struct puffs_usermount *pu = pcc->pcc_pu;
|
|
struct puffs_fctrl_io *fio;
|
|
|
|
/*
|
|
* Technically we shouldn't allow this if RDGONE, but it's
|
|
* difficult to trap write close without allowing writes.
|
|
* And besides, there's probably a disconnect sequence in
|
|
* the protocol, so unexpectedly getting a closed fd is
|
|
* most likely an error condition.
|
|
*/
|
|
GETFIO(fd);
|
|
|
|
pufbuf->pcc = pcc;
|
|
pufbuf->fcb = NULL;
|
|
pufbuf->fcb_arg = NULL;
|
|
|
|
pufbuf->offset = 0;
|
|
pufbuf->istat |= ISTAT_NODESTROY;
|
|
|
|
if (flags & PUFFS_FBQUEUE_URGENT)
|
|
TAILQ_INSERT_HEAD(&fio->snd_qing, pufbuf, pfb_entries);
|
|
else
|
|
TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
|
|
|
|
puffs_cc_yield(pcc);
|
|
if (pufbuf->rv) {
|
|
pufbuf->istat &= ~ISTAT_NODESTROY;
|
|
errno = pufbuf->rv;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
puffs_framev_enqueue_cb(struct puffs_usermount *pu, int fd,
|
|
struct puffs_framebuf *pufbuf, puffs_framev_cb fcb, void *arg,
|
|
int flags)
|
|
{
|
|
struct puffs_fctrl_io *fio;
|
|
|
|
/* see enqueue_cc */
|
|
GETFIO(fd);
|
|
|
|
pufbuf->pcc = NULL;
|
|
pufbuf->fcb = fcb;
|
|
pufbuf->fcb_arg = arg;
|
|
|
|
pufbuf->offset = 0;
|
|
pufbuf->istat |= ISTAT_NODESTROY;
|
|
|
|
if (flags & PUFFS_FBQUEUE_URGENT)
|
|
TAILQ_INSERT_HEAD(&fio->snd_qing, pufbuf, pfb_entries);
|
|
else
|
|
TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
puffs_framev_enqueue_justsend(struct puffs_usermount *pu, int fd,
|
|
struct puffs_framebuf *pufbuf, int reply, int flags)
|
|
{
|
|
struct puffs_fctrl_io *fio;
|
|
|
|
assert((pufbuf->istat & ISTAT_INTERNAL) == 0);
|
|
|
|
GETFIO(fd);
|
|
|
|
pufbuf->pcc = NULL;
|
|
pufbuf->fcb = NULL;
|
|
pufbuf->fcb_arg = NULL;
|
|
|
|
pufbuf->offset = 0;
|
|
pufbuf->istat |= ISTAT_NODESTROY;
|
|
if (!reply)
|
|
pufbuf->istat |= ISTAT_NOREPLY;
|
|
|
|
if (flags & PUFFS_FBQUEUE_URGENT)
|
|
TAILQ_INSERT_HEAD(&fio->snd_qing, pufbuf, pfb_entries);
|
|
else
|
|
TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ARGSUSED */
|
|
int
|
|
puffs_framev_enqueue_directreceive(struct puffs_cc *pcc, int fd,
|
|
struct puffs_framebuf *pufbuf, int flags /* used in the future */)
|
|
{
|
|
struct puffs_usermount *pu = pcc->pcc_pu;
|
|
struct puffs_fctrl_io *fio;
|
|
|
|
assert((pufbuf->istat & ISTAT_INTERNAL) == 0);
|
|
|
|
fio = getfiobyfd(pu, fd);
|
|
if (fio == NULL) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
/* XXX: should have cur_in queue */
|
|
assert(fio->cur_in == NULL);
|
|
fio->cur_in = pufbuf;
|
|
|
|
pufbuf->pcc = pcc;
|
|
pufbuf->fcb = NULL;
|
|
pufbuf->fcb_arg = NULL;
|
|
|
|
pufbuf->offset = 0;
|
|
pufbuf->istat |= ISTAT_NODESTROY | ISTAT_DIRECT;
|
|
|
|
puffs_cc_yield(pcc);
|
|
pufbuf->istat &= ~ISTAT_NODESTROY; /* XXX: not the right place */
|
|
if (pufbuf->rv) {
|
|
errno = pufbuf->rv;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
puffs_framev_enqueue_directsend(struct puffs_cc *pcc, int fd,
|
|
struct puffs_framebuf *pufbuf, int flags)
|
|
{
|
|
struct puffs_usermount *pu = pcc->pcc_pu;
|
|
struct puffs_fctrl_io *fio;
|
|
|
|
assert((pufbuf->istat & ISTAT_INTERNAL) == 0);
|
|
|
|
if (flags & PUFFS_FBQUEUE_URGENT)
|
|
abort(); /* EOPNOTSUPP for now */
|
|
|
|
GETFIO(fd);
|
|
|
|
pufbuf->pcc = pcc;
|
|
pufbuf->fcb = NULL;
|
|
pufbuf->fcb_arg = NULL;
|
|
|
|
pufbuf->offset = 0;
|
|
pufbuf->istat |= ISTAT_NODESTROY | ISTAT_DIRECT;
|
|
|
|
TAILQ_INSERT_TAIL(&fio->snd_qing, pufbuf, pfb_entries);
|
|
|
|
puffs_cc_yield(pcc);
|
|
if (pufbuf->rv) {
|
|
pufbuf->istat &= ~ISTAT_NODESTROY;
|
|
errno = pufbuf->rv;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
puffs_framev_framebuf_ccpromote(struct puffs_framebuf *pufbuf,
|
|
struct puffs_cc *pcc)
|
|
{
|
|
|
|
if ((pufbuf->istat & ISTAT_ONQUEUE) == 0) {
|
|
errno = EBUSY;
|
|
return -1;
|
|
}
|
|
|
|
pufbuf->pcc = pcc;
|
|
pufbuf->fcb = NULL;
|
|
pufbuf->fcb_arg = NULL;
|
|
pufbuf->istat &= ~ISTAT_NOREPLY;
|
|
|
|
puffs_cc_yield(pcc);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
puffs_framev_enqueue_waitevent(struct puffs_cc *pcc, int fd, int *what)
|
|
{
|
|
struct puffs_usermount *pu = pcc->pcc_pu;
|
|
struct puffs_fctrl_io *fio;
|
|
struct puffs_fbevent feb;
|
|
struct kevent kev;
|
|
int rv, svwhat;
|
|
|
|
svwhat = *what;
|
|
|
|
if (*what == 0) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
fio = getfiobyfd(pu, fd);
|
|
if (fio == NULL) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
feb.pcc = pcc;
|
|
feb.what = *what & (PUFFS_FBIO_READ|PUFFS_FBIO_WRITE|PUFFS_FBIO_ERROR);
|
|
|
|
if (*what & PUFFS_FBIO_READ)
|
|
if ((fio->stat & FIO_ENABLE_R) == 0)
|
|
EV_SET(&kev, fd, EVFILT_READ, EV_ENABLE, 0, 0, fio);
|
|
|
|
if (kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL) == -1)
|
|
return -1;
|
|
|
|
if (*what & PUFFS_FBIO_READ)
|
|
fio->rwait++;
|
|
if (*what & PUFFS_FBIO_WRITE)
|
|
fio->wwait++;
|
|
|
|
LIST_INSERT_HEAD(&fio->ev_qing, &feb, pfe_entries);
|
|
puffs_cc_yield(pcc);
|
|
|
|
assert(svwhat == *what);
|
|
|
|
if (*what & PUFFS_FBIO_READ) {
|
|
fio->rwait--;
|
|
if (fio->rwait == 0 && (fio->stat & FIO_ENABLE_R) == 0) {
|
|
EV_SET(&kev, fd, EVFILT_READ, EV_DISABLE, 0, 0, fio);
|
|
rv = kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
|
|
#if 0
|
|
if (rv != 0)
|
|
/* XXXXX oh dear */;
|
|
#endif
|
|
}
|
|
}
|
|
if (*what & PUFFS_FBIO_WRITE)
|
|
fio->wwait--;
|
|
|
|
if (feb.rv == 0) {
|
|
*what = feb.what;
|
|
rv = 0;
|
|
} else {
|
|
*what = PUFFS_FBIO_ERROR;
|
|
errno = feb.rv;
|
|
rv = -1;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
void
|
|
puffs__framev_notify(struct puffs_fctrl_io *fio, int what)
|
|
{
|
|
struct puffs_fbevent *fbevp;
|
|
|
|
restart:
|
|
LIST_FOREACH(fbevp, &fio->ev_qing, pfe_entries) {
|
|
if (fbevp->what & what) {
|
|
fbevp->what = what;
|
|
fbevp->rv = 0;
|
|
LIST_REMOVE(fbevp, pfe_entries);
|
|
puffs_cc_continue(fbevp->pcc);
|
|
goto restart;
|
|
}
|
|
}
|
|
}
|
|
|
|
static struct puffs_framebuf *
|
|
findbuf(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
|
|
struct puffs_fctrl_io *fio, struct puffs_framebuf *findme)
|
|
{
|
|
struct puffs_framebuf *cand;
|
|
int notresp = 0;
|
|
|
|
TAILQ_FOREACH(cand, &fio->res_qing, pfb_entries)
|
|
if (fctrl->cmpfb(pu, findme, cand, ¬resp) == 0 || notresp)
|
|
break;
|
|
|
|
assert(!(notresp && cand == NULL));
|
|
if (notresp || cand == NULL)
|
|
return NULL;
|
|
|
|
TAILQ_REMOVE(&fio->res_qing, cand, pfb_entries);
|
|
return cand;
|
|
}
|
|
|
|
void
|
|
puffs__framebuf_moveinfo(struct puffs_framebuf *from, struct puffs_framebuf *to)
|
|
{
|
|
|
|
assert(from->istat & ISTAT_INTERNAL);
|
|
|
|
/* migrate buffer */
|
|
free(to->buf);
|
|
to->buf = from->buf;
|
|
|
|
/* migrate buffer info */
|
|
to->len = from->len;
|
|
to->offset = from->offset;
|
|
to->maxoff = from->maxoff;
|
|
|
|
from->buf = NULL;
|
|
from->len = 0;
|
|
}
|
|
|
|
void
|
|
puffs__framev_input(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
|
|
struct puffs_fctrl_io *fio)
|
|
{
|
|
struct puffs_framebuf *pufbuf, *appbuf;
|
|
int rv, complete;
|
|
|
|
while ((fio->stat & FIO_DEAD) == 0 && (fio->stat & FIO_ENABLE_R)) {
|
|
if ((pufbuf = fio->cur_in) == NULL) {
|
|
pufbuf = puffs_framebuf_make();
|
|
if (pufbuf == NULL)
|
|
return;
|
|
pufbuf->istat |= ISTAT_INTERNAL;
|
|
fio->cur_in = pufbuf;
|
|
}
|
|
|
|
complete = 0;
|
|
rv = fctrl->rfb(pu, pufbuf, fio->io_fd, &complete);
|
|
|
|
/* error */
|
|
if (rv) {
|
|
puffs__framev_readclose(pu, fio, rv);
|
|
fio->cur_in = NULL;
|
|
return;
|
|
}
|
|
|
|
/* partial read, come back to fight another day */
|
|
if (complete == 0)
|
|
break;
|
|
|
|
/* else: full read, process */
|
|
fio->cur_in = NULL;
|
|
if ((pufbuf->istat & ISTAT_DIRECT) == 0) {
|
|
appbuf = findbuf(pu, fctrl, fio, pufbuf);
|
|
|
|
/*
|
|
* No request for this frame? If fs implements
|
|
* gotfb, give frame to that. Otherwise drop it.
|
|
*/
|
|
if (appbuf == NULL) {
|
|
if (fctrl->gotfb) {
|
|
pufbuf->istat &= ~ISTAT_INTERNAL;
|
|
fctrl->gotfb(pu, pufbuf);
|
|
} else {
|
|
puffs_framebuf_destroy(pufbuf);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
puffs__framebuf_moveinfo(pufbuf, appbuf);
|
|
puffs_framebuf_destroy(pufbuf);
|
|
} else {
|
|
appbuf = pufbuf;
|
|
}
|
|
appbuf->istat &= ~ISTAT_NODESTROY;
|
|
|
|
if (appbuf->pcc) {
|
|
puffs__cc_cont(appbuf->pcc);
|
|
} else if (appbuf->fcb) {
|
|
appbuf->fcb(pu, appbuf, appbuf->fcb_arg, 0);
|
|
} else {
|
|
puffs_framebuf_destroy(appbuf);
|
|
}
|
|
|
|
/* hopeless romantics, here we go again */
|
|
}
|
|
}
|
|
|
|
int
|
|
puffs__framev_output(struct puffs_usermount *pu, struct puffs_framectrl *fctrl,
|
|
struct puffs_fctrl_io *fio)
|
|
{
|
|
struct puffs_framebuf *pufbuf;
|
|
int rv, complete, done;
|
|
|
|
if (fio->stat & FIO_DEAD)
|
|
return 0;
|
|
|
|
for (pufbuf = TAILQ_FIRST(&fio->snd_qing), done = 0;
|
|
pufbuf && (fio->stat & FIO_DEAD) == 0 && fio->stat & FIO_ENABLE_W;
|
|
pufbuf = TAILQ_FIRST(&fio->snd_qing)) {
|
|
complete = 0;
|
|
rv = fctrl->wfb(pu, pufbuf, fio->io_fd, &complete);
|
|
|
|
if (rv) {
|
|
puffs__framev_writeclose(pu, fio, rv);
|
|
done = 1;
|
|
break;
|
|
}
|
|
|
|
/* partial write */
|
|
if (complete == 0)
|
|
return done;
|
|
|
|
/* else, complete write */
|
|
TAILQ_REMOVE(&fio->snd_qing, pufbuf, pfb_entries);
|
|
|
|
/* can't wait for result if we can't read */
|
|
if (fio->stat & FIO_RDGONE) {
|
|
errnotify(pu, pufbuf, ENXIO);
|
|
done = 1;
|
|
} else if ((pufbuf->istat & ISTAT_DIRECT)) {
|
|
pufbuf->istat &= ~ISTAT_NODESTROY;
|
|
done = 1;
|
|
puffs__cc_cont(pufbuf->pcc);
|
|
} else if ((pufbuf->istat & ISTAT_NOREPLY) == 0) {
|
|
TAILQ_INSERT_TAIL(&fio->res_qing, pufbuf,
|
|
pfb_entries);
|
|
} else {
|
|
pufbuf->istat &= ~ISTAT_NODESTROY;
|
|
puffs_framebuf_destroy(pufbuf);
|
|
}
|
|
|
|
/* omstart! */
|
|
}
|
|
|
|
return done;
|
|
}
|
|
|
|
int
|
|
puffs__framev_addfd_ctrl(struct puffs_usermount *pu, int fd, int what,
|
|
struct puffs_framectrl *pfctrl)
|
|
{
|
|
struct puffs_fctrl_io *fio;
|
|
struct kevent kev[2];
|
|
size_t nevs;
|
|
int rv, readenable;
|
|
|
|
nevs = pu->pu_nevs+2;
|
|
if (reallocarr(&pu->pu_evs, nevs, sizeof(struct kevent)) != 0) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
fio = malloc(sizeof(struct puffs_fctrl_io));
|
|
if (fio == NULL)
|
|
return -1;
|
|
memset(fio, 0, sizeof(struct puffs_fctrl_io));
|
|
fio->io_fd = fd;
|
|
fio->cur_in = NULL;
|
|
fio->fctrl = pfctrl;
|
|
TAILQ_INIT(&fio->snd_qing);
|
|
TAILQ_INIT(&fio->res_qing);
|
|
LIST_INIT(&fio->ev_qing);
|
|
|
|
readenable = 0;
|
|
if ((what & PUFFS_FBIO_READ) == 0)
|
|
readenable = EV_DISABLE;
|
|
|
|
if (pu->pu_state & PU_INLOOP) {
|
|
struct stat st;
|
|
size_t nf = 0;
|
|
|
|
if (fstat(fd, &st) == -1)
|
|
goto out;
|
|
EV_SET(&kev[nf], fd, EVFILT_READ, EV_ADD|readenable, 0, 0, fio);
|
|
nf++;
|
|
if (S_ISSOCK(st.st_mode)) {
|
|
EV_SET(&kev[nf], fd, EVFILT_WRITE,
|
|
EV_ADD|EV_DISABLE, 0, 0, fio);
|
|
nf++;
|
|
}
|
|
rv = kevent(pu->pu_kq, kev, nf, NULL, 0, NULL);
|
|
if (rv == -1)
|
|
goto out;
|
|
}
|
|
if (what & PUFFS_FBIO_READ)
|
|
fio->stat |= FIO_ENABLE_R;
|
|
if (what & PUFFS_FBIO_WRITE)
|
|
fio->stat |= FIO_ENABLE_W;
|
|
|
|
LIST_INSERT_HEAD(&pu->pu_ios, fio, fio_entries);
|
|
pu->pu_nevs = nevs;
|
|
|
|
return 0;
|
|
out:
|
|
free(fio);
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
puffs_framev_addfd(struct puffs_usermount *pu, int fd, int what)
|
|
{
|
|
|
|
return puffs__framev_addfd_ctrl(pu, fd, what,
|
|
&pu->pu_framectrl[PU_FRAMECTRL_USER]);
|
|
}
|
|
|
|
/*
|
|
* XXX: the following en/disable should be coalesced and executed
|
|
* only during the actual kevent call. So feel free to fix if
|
|
* threatened by mindblowing boredom.
|
|
*/
|
|
|
|
int
|
|
puffs_framev_enablefd(struct puffs_usermount *pu, int fd, int what)
|
|
{
|
|
struct kevent kev;
|
|
struct puffs_fctrl_io *fio;
|
|
int rv = 0;
|
|
|
|
assert((what & (PUFFS_FBIO_READ | PUFFS_FBIO_WRITE)) != 0);
|
|
|
|
fio = getfiobyfd(pu, fd);
|
|
if (fio == NULL) {
|
|
errno = ENXIO;
|
|
return -1;
|
|
}
|
|
|
|
/* write is enabled in the event loop if there is output */
|
|
if (what & PUFFS_FBIO_READ && fio->rwait == 0) {
|
|
EV_SET(&kev, fd, EVFILT_READ, EV_ENABLE, 0, 0, fio);
|
|
rv = kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
|
|
}
|
|
|
|
if (rv == 0) {
|
|
if (what & PUFFS_FBIO_READ)
|
|
fio->stat |= FIO_ENABLE_R;
|
|
if (what & PUFFS_FBIO_WRITE)
|
|
fio->stat |= FIO_ENABLE_W;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
int
|
|
puffs_framev_disablefd(struct puffs_usermount *pu, int fd, int what)
|
|
{
|
|
struct kevent kev[2];
|
|
struct puffs_fctrl_io *fio;
|
|
size_t i;
|
|
int rv;
|
|
|
|
assert((what & (PUFFS_FBIO_READ | PUFFS_FBIO_WRITE)) != 0);
|
|
|
|
fio = getfiobyfd(pu, fd);
|
|
if (fio == NULL) {
|
|
errno = ENXIO;
|
|
return -1;
|
|
}
|
|
|
|
i = 0;
|
|
if (what & PUFFS_FBIO_READ && fio->rwait == 0) {
|
|
EV_SET(&kev[0], fd, EVFILT_READ, EV_DISABLE, 0, 0, fio);
|
|
i++;
|
|
}
|
|
if (what & PUFFS_FBIO_WRITE && fio->stat & FIO_WR && fio->wwait == 0) {
|
|
EV_SET(&kev[1], fd, EVFILT_WRITE, EV_DISABLE, 0, 0, fio);
|
|
i++;
|
|
}
|
|
if (i)
|
|
rv = kevent(pu->pu_kq, kev, i, NULL, 0, NULL);
|
|
else
|
|
rv = 0;
|
|
|
|
if (rv == 0) {
|
|
if (what & PUFFS_FBIO_READ)
|
|
fio->stat &= ~FIO_ENABLE_R;
|
|
if (what & PUFFS_FBIO_WRITE)
|
|
fio->stat &= ~FIO_ENABLE_W;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
void
|
|
puffs__framev_readclose(struct puffs_usermount *pu,
|
|
struct puffs_fctrl_io *fio, int error)
|
|
{
|
|
struct puffs_framebuf *pufbuf;
|
|
struct kevent kev;
|
|
int notflag;
|
|
|
|
if (fio->stat & FIO_RDGONE || fio->stat & FIO_DEAD)
|
|
return;
|
|
fio->stat |= FIO_RDGONE;
|
|
|
|
if (fio->cur_in) {
|
|
if ((fio->cur_in->istat & ISTAT_DIRECT) == 0) {
|
|
puffs_framebuf_destroy(fio->cur_in);
|
|
fio->cur_in = NULL;
|
|
} else {
|
|
errnotify(pu, fio->cur_in, error);
|
|
}
|
|
}
|
|
|
|
while ((pufbuf = TAILQ_FIRST(&fio->res_qing)) != NULL) {
|
|
TAILQ_REMOVE(&fio->res_qing, pufbuf, pfb_entries);
|
|
errnotify(pu, pufbuf, error);
|
|
}
|
|
|
|
EV_SET(&kev, fio->io_fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
|
|
(void) kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
|
|
|
|
notflag = PUFFS_FBIO_READ;
|
|
if (fio->stat & FIO_WRGONE)
|
|
notflag |= PUFFS_FBIO_WRITE;
|
|
|
|
if (fio->fctrl->fdnotfn)
|
|
fio->fctrl->fdnotfn(pu, fio->io_fd, notflag);
|
|
}
|
|
|
|
void
|
|
puffs__framev_writeclose(struct puffs_usermount *pu,
|
|
struct puffs_fctrl_io *fio, int error)
|
|
{
|
|
struct puffs_framebuf *pufbuf;
|
|
struct kevent kev;
|
|
int notflag;
|
|
|
|
if (fio->stat & FIO_WRGONE || fio->stat & FIO_DEAD)
|
|
return;
|
|
fio->stat |= FIO_WRGONE;
|
|
|
|
while ((pufbuf = TAILQ_FIRST(&fio->snd_qing)) != NULL) {
|
|
TAILQ_REMOVE(&fio->snd_qing, pufbuf, pfb_entries);
|
|
errnotify(pu, pufbuf, error);
|
|
}
|
|
|
|
EV_SET(&kev, fio->io_fd, EVFILT_WRITE, EV_DELETE, 0, 0, 0);
|
|
(void) kevent(pu->pu_kq, &kev, 1, NULL, 0, NULL);
|
|
|
|
notflag = PUFFS_FBIO_WRITE;
|
|
if (fio->stat & FIO_RDGONE)
|
|
notflag |= PUFFS_FBIO_READ;
|
|
|
|
if (fio->fctrl->fdnotfn)
|
|
fio->fctrl->fdnotfn(pu, fio->io_fd, notflag);
|
|
}
|
|
|
|
static int
|
|
removefio(struct puffs_usermount *pu, struct puffs_fctrl_io *fio, int error)
|
|
{
|
|
struct puffs_fbevent *fbevp;
|
|
|
|
LIST_REMOVE(fio, fio_entries);
|
|
if (pu->pu_state & PU_INLOOP) {
|
|
puffs__framev_readclose(pu, fio, error);
|
|
puffs__framev_writeclose(pu, fio, error);
|
|
}
|
|
|
|
while ((fbevp = LIST_FIRST(&fio->ev_qing)) != NULL) {
|
|
fbevp->rv = error;
|
|
LIST_REMOVE(fbevp, pfe_entries);
|
|
puffs__goto(fbevp->pcc);
|
|
}
|
|
|
|
/* don't bother with realloc */
|
|
pu->pu_nevs -= 2;
|
|
|
|
/* don't free us yet, might have some references in event arrays */
|
|
fio->stat |= FIO_DEAD;
|
|
LIST_INSERT_HEAD(&pu->pu_ios_rmlist, fio, fio_entries);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int
|
|
puffs_framev_removefd(struct puffs_usermount *pu, int fd, int error)
|
|
{
|
|
struct puffs_fctrl_io *fio;
|
|
|
|
fio = getfiobyfd(pu, fd);
|
|
if (fio == NULL) {
|
|
errno = ENXIO;
|
|
return -1;
|
|
}
|
|
|
|
return removefio(pu, fio, error ? error : ECONNRESET);
|
|
}
|
|
|
|
void
|
|
puffs_framev_removeonclose(struct puffs_usermount *pu, int fd, int what)
|
|
{
|
|
|
|
if (what == (PUFFS_FBIO_READ | PUFFS_FBIO_WRITE))
|
|
(void) puffs_framev_removefd(pu, fd, ECONNRESET);
|
|
}
|
|
|
|
void
|
|
puffs_framev_unmountonclose(struct puffs_usermount *pu, int fd, int what)
|
|
{
|
|
|
|
/* XXX & X: unmount is non-sensible */
|
|
puffs_framev_removeonclose(pu, fd, what);
|
|
if (what == (PUFFS_FBIO_READ | PUFFS_FBIO_WRITE))
|
|
PU_SETSTATE(pu, PUFFS_STATE_UNMOUNTED);
|
|
}
|
|
|
|
void
|
|
puffs_framev_init(struct puffs_usermount *pu,
|
|
puffs_framev_readframe_fn rfb, puffs_framev_writeframe_fn wfb,
|
|
puffs_framev_cmpframe_fn cmpfb, puffs_framev_gotframe_fn gotfb,
|
|
puffs_framev_fdnotify_fn fdnotfn)
|
|
{
|
|
struct puffs_framectrl *pfctrl;
|
|
|
|
pfctrl = &pu->pu_framectrl[PU_FRAMECTRL_USER];
|
|
pfctrl->rfb = rfb;
|
|
pfctrl->wfb = wfb;
|
|
pfctrl->cmpfb = cmpfb;
|
|
pfctrl->gotfb = gotfb;
|
|
pfctrl->fdnotfn = fdnotfn;
|
|
}
|
|
|
|
void
|
|
puffs__framev_exit(struct puffs_usermount *pu)
|
|
{
|
|
struct puffs_fctrl_io *fio;
|
|
|
|
while ((fio = LIST_FIRST(&pu->pu_ios)) != NULL)
|
|
removefio(pu, fio, ENXIO);
|
|
free(pu->pu_evs);
|
|
|
|
/* closing pu->pu_kq takes care of puffsfd */
|
|
}
|