#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/filedesc.h>
#include <sys/filio.h>
#include <sys/ttycom.h>
#include <sys/stat.h>
#include <sys/signalvar.h>
#include <sys/sysmsg.h>
#include <sys/pipe.h>
#include <sys/vnode.h>
#include <sys/uio.h>
#include <sys/event.h>
#include <sys/globaldata.h>
#include <sys/module.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
#include <sys/socket.h>
#include <sys/kern_syscall.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/vm_object.h>
#include <vm/vm_kern.h>
#include <vm/vm_extern.h>
#include <vm/pmap.h>
#include <vm/vm_map.h>
#include <vm/vm_page.h>
#include <vm/vm_zone.h>
#include <sys/file2.h>
#include <sys/signal2.h>
#include <sys/mutex2.h>
#include <machine/cpufunc.h>
struct pipegdlock {
struct mtx mtx;
} __cachealign;
static int pipe_read (struct file *fp, struct uio *uio,
struct ucred *cred, int flags);
static int pipe_write (struct file *fp, struct uio *uio,
struct ucred *cred, int flags);
static int pipe_close (struct file *fp);
static int pipe_shutdown (struct file *fp, int how);
static int pipe_kqfilter (struct file *fp, struct knote *kn);
static int pipe_stat (struct file *fp, struct stat *sb, struct ucred *cred);
static int pipe_ioctl (struct file *fp, u_long cmd, caddr_t data,
struct ucred *cred, struct sysmsg *msg);
__read_mostly static struct fileops pipeops = {
.fo_read = pipe_read,
.fo_write = pipe_write,
.fo_ioctl = pipe_ioctl,
.fo_kqfilter = pipe_kqfilter,
.fo_stat = pipe_stat,
.fo_close = pipe_close,
.fo_shutdown = pipe_shutdown,
.fo_seek = badfo_seek
};
static void filt_pipedetach(struct knote *kn);
static int filt_piperead(struct knote *kn, long hint);
static int filt_pipewrite(struct knote *kn, long hint);
__read_mostly static struct filterops pipe_rfiltops =
{ FILTEROP_ISFD|FILTEROP_MPSAFE, NULL, filt_pipedetach, filt_piperead };
__read_mostly static struct filterops pipe_wfiltops =
{ FILTEROP_ISFD|FILTEROP_MPSAFE, NULL, filt_pipedetach, filt_pipewrite };
MALLOC_DEFINE(M_PIPE, "pipe", "pipe structures");
#define PIPEQ_MAX_CACHE 16
__read_mostly static int pipe_maxcache = PIPEQ_MAX_CACHE;
__read_mostly static struct pipegdlock *pipe_gdlocks;
SYSCTL_NODE(_kern, OID_AUTO, pipe, CTLFLAG_RW, 0, "Pipe operation");
SYSCTL_INT(_kern_pipe, OID_AUTO, maxcache,
CTLFLAG_RW, &pipe_maxcache, 0, "max pipes cached per-cpu");
__read_mostly static int pipe_size = 32768;
SYSCTL_INT(_kern_pipe, OID_AUTO, size,
CTLFLAG_RW, &pipe_size, 0, "Pipe buffer size (16384 minimum)");
#ifdef _RDTSC_SUPPORTED_
__read_mostly static int pipe_delay = 4000;
SYSCTL_INT(_kern_pipe, OID_AUTO, delay,
CTLFLAG_RW, &pipe_delay, 0, "SMP delay optimization in ns");
#endif
static
void
pipeinit(void *dummy)
{
size_t mbytes = kmem_lim_size();
int n;
if (pipe_maxcache == PIPEQ_MAX_CACHE) {
if (mbytes >= 7 * 1024)
pipe_maxcache *= 2;
if (mbytes >= 15 * 1024)
pipe_maxcache *= 2;
}
if (ncpus > 64) {
pipe_maxcache = pipe_maxcache * 64 / ncpus;
if (pipe_maxcache < PIPEQ_MAX_CACHE)
pipe_maxcache = PIPEQ_MAX_CACHE;
}
pipe_gdlocks = kmalloc(sizeof(*pipe_gdlocks) * ncpus,
M_PIPE, M_WAITOK | M_ZERO);
for (n = 0; n < ncpus; ++n)
mtx_init(&pipe_gdlocks[n].mtx, "pipekm");
}
SYSINIT(kmem, SI_BOOT2_MACHDEP, SI_ORDER_ANY, pipeinit, NULL);
static void pipeclose (struct pipe *pipe,
struct pipebuf *pbr, struct pipebuf *pbw);
static void pipe_free_kmem (struct pipebuf *buf);
static int pipe_create (struct pipe **pipep);
static __inline void
pipesignal(struct pipebuf *pb, uint32_t flags)
{
uint32_t oflags;
uint32_t nflags;
for (;;) {
oflags = pb->state;
cpu_ccfence();
nflags = oflags & ~flags;
if (atomic_cmpset_int(&pb->state, oflags, nflags))
break;
}
if (oflags & flags)
wakeup(pb);
}
static __inline void
pipewakeup(struct pipebuf *pb, int dosigio)
{
if (dosigio && (pb->state & PIPE_ASYNC) && pb->sigio) {
lwkt_gettoken(&sigio_token);
pgsigio(pb->sigio, SIGIO, 0);
lwkt_reltoken(&sigio_token);
}
KNOTE(&pb->kq.ki_note, 0);
}
static __inline int
pipe_start_uio(int *ipp)
{
int error;
while (*ipp) {
*ipp = -1;
error = tsleep(ipp, PCATCH, "pipexx", 0);
if (error)
return (error);
}
*ipp = 1;
return (0);
}
static __inline void
pipe_end_uio(int *ipp)
{
if (*ipp < 0) {
*ipp = 0;
wakeup(ipp);
} else {
KKASSERT(*ipp > 0);
*ipp = 0;
}
}
int
sys_pipe(struct sysmsg *sysmsg, const struct pipe_args *uap)
{
return kern_pipe(sysmsg->sysmsg_fds, 0);
}
int
sys_pipe2(struct sysmsg *sysmsg, const struct pipe2_args *uap)
{
if ((uap->flags & ~(O_CLOEXEC | O_CLOFORK | O_NONBLOCK)) != 0)
return (EINVAL);
return kern_pipe(sysmsg->sysmsg_fds, uap->flags);
}
int
kern_pipe(long *fds, int flags)
{
struct thread *td = curthread;
struct filedesc *fdp = td->td_proc->p_fd;
struct file *rf, *wf;
struct pipe *pipe;
int fd1, fd2, error;
pipe = NULL;
if (pipe_create(&pipe)) {
pipeclose(pipe, &pipe->bufferA, &pipe->bufferB);
pipeclose(pipe, &pipe->bufferB, &pipe->bufferA);
return (ENFILE);
}
error = falloc(td->td_lwp, &rf, &fd1);
if (error) {
pipeclose(pipe, &pipe->bufferA, &pipe->bufferB);
pipeclose(pipe, &pipe->bufferB, &pipe->bufferA);
return (error);
}
fds[0] = fd1;
rf->f_type = DTYPE_PIPE;
rf->f_flag = FREAD | FWRITE;
rf->f_ops = &pipeops;
rf->f_data = (void *)((intptr_t)pipe | 0);
if (flags & O_NONBLOCK)
rf->f_flag |= O_NONBLOCK;
if (flags & O_CLOEXEC)
fdp->fd_files[fd1].fileflags |= UF_EXCLOSE;
if (flags & O_CLOFORK)
fdp->fd_files[fd1].fileflags |= UF_FOCLOSE;
error = falloc(td->td_lwp, &wf, &fd2);
if (error) {
fsetfd(fdp, NULL, fd1);
fdrop(rf);
pipeclose(pipe, &pipe->bufferB, &pipe->bufferA);
return (error);
}
wf->f_type = DTYPE_PIPE;
wf->f_flag = FREAD | FWRITE;
wf->f_ops = &pipeops;
wf->f_data = (void *)((intptr_t)pipe | 1);
if (flags & O_NONBLOCK)
wf->f_flag |= O_NONBLOCK;
if (flags & O_CLOEXEC)
fdp->fd_files[fd2].fileflags |= UF_EXCLOSE;
if (flags & O_CLOFORK)
fdp->fd_files[fd2].fileflags |= UF_FOCLOSE;
fds[1] = fd2;
fsetfd(fdp, rf, fd1);
fsetfd(fdp, wf, fd2);
fdrop(rf);
fdrop(wf);
return (0);
}
static int
pipespace(struct pipe *pipe, struct pipebuf *pb, size_t size)
{
struct vm_object *object;
caddr_t buffer;
vm_pindex_t npages;
int error;
size = (size + PAGE_MASK) & ~(size_t)PAGE_MASK;
if (size < 16384)
size = 16384;
if (size > 1024*1024)
size = 1024*1024;
npages = round_page(size) / PAGE_SIZE;
object = pb->object;
if (object == NULL || object->size != npages) {
object = vm_object_allocate(OBJT_DEFAULT, npages);
buffer = (caddr_t)vm_map_min(kernel_map);
error = vm_map_find(kernel_map, object, NULL,
0, (vm_offset_t *)&buffer, size,
PAGE_SIZE, TRUE,
VM_MAPTYPE_NORMAL, VM_SUBSYS_PIPE,
VM_PROT_ALL, VM_PROT_ALL, 0);
if (error != KERN_SUCCESS) {
vm_object_deallocate(object);
return (ENOMEM);
}
pipe_free_kmem(pb);
pb->object = object;
pb->buffer = buffer;
pb->size = size;
}
pb->rindex = 0;
pb->windex = 0;
return (0);
}
static int
pipe_create(struct pipe **pipep)
{
globaldata_t gd = mycpu;
struct pipe *pipe;
int error;
if ((pipe = gd->gd_pipeq) != NULL) {
gd->gd_pipeq = pipe->next;
--gd->gd_pipeqcount;
pipe->next = NULL;
} else {
pipe = kmalloc(sizeof(*pipe), M_PIPE, M_WAITOK | M_ZERO);
pipe->inum = gd->gd_anoninum++ * ncpus + gd->gd_cpuid + 2;
lwkt_token_init(&pipe->bufferA.rlock, "piper");
lwkt_token_init(&pipe->bufferA.wlock, "pipew");
lwkt_token_init(&pipe->bufferB.rlock, "piper");
lwkt_token_init(&pipe->bufferB.wlock, "pipew");
}
*pipep = pipe;
if ((error = pipespace(pipe, &pipe->bufferA, pipe_size)) != 0) {
return (error);
}
if ((error = pipespace(pipe, &pipe->bufferB, pipe_size)) != 0) {
return (error);
}
vfs_timestamp(&pipe->ctime);
pipe->bufferA.atime = pipe->ctime;
pipe->bufferA.mtime = pipe->ctime;
pipe->bufferB.atime = pipe->ctime;
pipe->bufferB.mtime = pipe->ctime;
pipe->open_count = 2;
return (0);
}
static int
pipe_read(struct file *fp, struct uio *uio, struct ucred *cred, int fflags)
{
struct pipebuf *rpb;
struct pipebuf *wpb;
struct pipe *pipe;
size_t nread = 0;
size_t size;
size_t nsize;
size_t rindex;
int notify_writer;
int bigread;
int bigcount;
int error;
int nbio;
pipe = (struct pipe *)((intptr_t)fp->f_data & ~(intptr_t)1);
if ((intptr_t)fp->f_data & 1) {
rpb = &pipe->bufferB;
wpb = &pipe->bufferA;
} else {
rpb = &pipe->bufferA;
wpb = &pipe->bufferB;
}
atomic_set_int(&curthread->td_mpflags, TDF_MP_BATCH_DEMARC);
if (uio->uio_resid == 0)
return(0);
if (fflags & O_FBLOCKING)
nbio = 0;
else if (fflags & O_FNONBLOCKING)
nbio = 1;
else if (fp->f_flag & O_NONBLOCK)
nbio = 1;
else
nbio = 0;
if (nbio && rpb->rindex == rpb->windex &&
(rpb->state & PIPE_REOF) == 0) {
return EAGAIN;
}
lwkt_gettoken(&rpb->rlock);
error = pipe_start_uio(&rpb->rip);
if (error) {
lwkt_reltoken(&rpb->rlock);
return (error);
}
notify_writer = 0;
bigread = (uio->uio_resid > 10 * 1024 * 1024);
bigcount = 10;
while (uio->uio_resid) {
if (bigread && --bigcount == 0) {
lwkt_user_yield();
bigcount = 10;
if (CURSIG(curthread->td_lwp)) {
error = EINTR;
break;
}
}
size = rpb->windex - rpb->rindex;
cpu_lfence();
if (size) {
rindex = rpb->rindex & (rpb->size - 1);
nsize = size;
if (nsize > rpb->size - rindex)
nsize = rpb->size - rindex;
nsize = szmin(nsize, uio->uio_resid);
if (nsize > (rpb->size >> 1))
nsize = rpb->size >> 1;
error = uiomove(&rpb->buffer[rindex], nsize, uio);
if (error)
break;
rpb->rindex += nsize;
nread += nsize;
if (size - nsize > (rpb->size >> 1)) {
notify_writer = 0;
} else {
notify_writer = 1;
pipesignal(rpb, PIPE_WANTW);
}
continue;
}
pipesignal(rpb, PIPE_WANTW);
if (rpb->windex != rpb->rindex)
continue;
#ifdef _RDTSC_SUPPORTED_
if (pipe_delay) {
int64_t tsc_target;
int good = 0;
tsc_target = tsc_get_target(pipe_delay);
while (tsc_test_target(tsc_target) == 0) {
cpu_lfence();
if (rpb->windex != rpb->rindex) {
good = 1;
break;
}
cpu_pause();
}
if (good)
continue;
}
#endif
if (rpb->state & PIPE_REOF)
break;
if (nread > 0)
break;
if (nbio) {
error = EAGAIN;
break;
}
tsleep_interlock(rpb, PCATCH);
atomic_set_int(&rpb->state, PIPE_WANTR);
size = rpb->windex - rpb->rindex;
if (size)
continue;
if (rpb->state & PIPE_REOF)
break;
error = tsleep(rpb, PCATCH | PINTERLOCKED, "piperd", 0);
if (error)
break;
}
pipe_end_uio(&rpb->rip);
if (error == 0 && nread && rpb->lticks != ticks) {
vfs_timestamp(&rpb->atime);
rpb->lticks = ticks;
}
if (notify_writer) {
pipesignal(rpb, PIPE_WANTW);
pipewakeup(wpb, 0);
}
lwkt_reltoken(&rpb->rlock);
return (error);
}
static int
pipe_write(struct file *fp, struct uio *uio, struct ucred *cred, int fflags)
{
struct pipebuf *rpb;
struct pipebuf *wpb;
struct pipe *pipe;
size_t windex;
size_t space;
size_t wcount;
size_t orig_resid;
int bigwrite;
int bigcount;
int error;
int nbio;
pipe = (struct pipe *)((intptr_t)fp->f_data & ~(intptr_t)1);
if ((intptr_t)fp->f_data & 1) {
rpb = &pipe->bufferB;
wpb = &pipe->bufferA;
} else {
rpb = &pipe->bufferA;
wpb = &pipe->bufferB;
}
if (fflags & O_FBLOCKING)
nbio = 0;
else if (fflags & O_FNONBLOCKING)
nbio = 1;
else if (fp->f_flag & O_NONBLOCK)
nbio = 1;
else
nbio = 0;
if (nbio && wpb->size == (wpb->windex - wpb->rindex) &&
uio->uio_resid && (wpb->state & PIPE_WEOF) == 0) {
return EAGAIN;
}
lwkt_gettoken(&wpb->wlock);
if (wpb->state & PIPE_WEOF) {
lwkt_reltoken(&wpb->wlock);
return (EPIPE);
}
if (uio->uio_resid == 0) {
lwkt_reltoken(&wpb->wlock);
return(0);
}
error = pipe_start_uio(&wpb->wip);
if (error) {
lwkt_reltoken(&wpb->wlock);
return (error);
}
orig_resid = uio->uio_resid;
wcount = 0;
bigwrite = (uio->uio_resid > 10 * 1024 * 1024);
bigcount = 10;
while (uio->uio_resid) {
if (wpb->state & PIPE_WEOF) {
error = EPIPE;
break;
}
if (bigwrite && --bigcount == 0) {
lwkt_user_yield();
bigcount = 10;
if (CURSIG(curthread->td_lwp)) {
error = EINTR;
break;
}
}
windex = wpb->windex & (wpb->size - 1);
space = wpb->size - (wpb->windex - wpb->rindex);
if ((space < uio->uio_resid) && (orig_resid <= PIPE_BUF))
space = 0;
if (space > 0) {
size_t segsize;
space = szmin(space, uio->uio_resid);
if (space > (wpb->size >> 1))
space = (wpb->size >> 1);
segsize = wpb->size - windex;
if (segsize > space)
segsize = space;
if (wcount == 0)
pipesignal(wpb, PIPE_WANTR);
error = uiomove(&wpb->buffer[windex], segsize, uio);
if (error == 0 && segsize < space) {
segsize = space - segsize;
error = uiomove(&wpb->buffer[0], segsize, uio);
}
if (error)
break;
cpu_sfence();
wpb->windex += space;
if (wcount != 0)
pipesignal(wpb, PIPE_WANTR);
wcount += space;
continue;
}
pipesignal(wpb, PIPE_WANTR);
if (nbio) {
error = EAGAIN;
break;
}
#ifdef _RDTSC_SUPPORTED_
if (pipe_delay) {
int64_t tsc_target;
int good = 0;
tsc_target = tsc_get_target(pipe_delay);
while (tsc_test_target(tsc_target) == 0) {
cpu_lfence();
space = wpb->size - (wpb->windex - wpb->rindex);
if ((space < uio->uio_resid) &&
(orig_resid <= PIPE_BUF)) {
space = 0;
}
if (space) {
good = 1;
break;
}
cpu_pause();
}
if (good)
continue;
}
#endif
tsleep_interlock(wpb, PCATCH);
atomic_set_int(&wpb->state, PIPE_WANTW);
space = wpb->size - (wpb->windex - wpb->rindex);
if ((space < uio->uio_resid) && (orig_resid <= PIPE_BUF))
space = 0;
if (wpb->state & PIPE_WEOF) {
error = EPIPE;
break;
}
if (space == 0) {
pipewakeup(wpb, 1);
error = tsleep(wpb, PCATCH | PINTERLOCKED, "pipewr", 0);
}
if (error)
break;
if (wpb->state & PIPE_WEOF) {
error = EPIPE;
break;
}
}
pipe_end_uio(&wpb->wip);
if (wpb->windex != wpb->rindex) {
pipesignal(wpb, PIPE_WANTR);
pipewakeup(wpb, 1);
}
if ((wpb->rindex == wpb->windex) &&
(uio->uio_resid == 0) &&
(error == EPIPE)) {
error = 0;
}
if (error == 0 && wpb->lticks != ticks) {
vfs_timestamp(&wpb->mtime);
wpb->lticks = ticks;
}
lwkt_reltoken(&wpb->wlock);
return (error);
}
static int
pipe_ioctl(struct file *fp, u_long cmd, caddr_t data,
struct ucred *cred, struct sysmsg *msg)
{
struct pipebuf *rpb;
struct pipe *pipe;
int error;
pipe = (struct pipe *)((intptr_t)fp->f_data & ~(intptr_t)1);
if ((intptr_t)fp->f_data & 1) {
rpb = &pipe->bufferB;
} else {
rpb = &pipe->bufferA;
}
lwkt_gettoken(&rpb->rlock);
lwkt_gettoken(&rpb->wlock);
switch (cmd) {
case FIOASYNC:
if (*(int *)data) {
atomic_set_int(&rpb->state, PIPE_ASYNC);
} else {
atomic_clear_int(&rpb->state, PIPE_ASYNC);
}
error = 0;
break;
case FIONREAD:
*(int *)data = (int)(rpb->windex - rpb->rindex);
error = 0;
break;
case FIOSETOWN:
error = fsetown(*(int *)data, &rpb->sigio);
break;
case FIOGETOWN:
*(int *)data = fgetown(&rpb->sigio);
error = 0;
break;
case TIOCSPGRP:
error = fsetown(-(*(int *)data), &rpb->sigio);
break;
case TIOCGPGRP:
*(int *)data = -fgetown(&rpb->sigio);
error = 0;
break;
default:
error = ENOTTY;
break;
}
lwkt_reltoken(&rpb->wlock);
lwkt_reltoken(&rpb->rlock);
return (error);
}
static int
pipe_stat(struct file *fp, struct stat *ub, struct ucred *cred)
{
struct pipebuf *rpb;
struct pipe *pipe;
pipe = (struct pipe *)((intptr_t)fp->f_data & ~(intptr_t)1);
if ((intptr_t)fp->f_data & 1) {
rpb = &pipe->bufferB;
} else {
rpb = &pipe->bufferA;
}
bzero((caddr_t)ub, sizeof(*ub));
ub->st_mode = S_IFIFO;
ub->st_blksize = rpb->size;
ub->st_size = rpb->windex - rpb->rindex;
ub->st_blocks = howmany(ub->st_size, ub->st_blksize);
ub->st_atimespec = rpb->atime;
ub->st_mtimespec = rpb->mtime;
ub->st_ctimespec = pipe->ctime;
ub->st_uid = fp->f_cred->cr_uid;
ub->st_gid = fp->f_cred->cr_gid;
ub->st_ino = pipe->inum;
return (0);
}
static int
pipe_close(struct file *fp)
{
struct pipebuf *rpb;
struct pipebuf *wpb;
struct pipe *pipe;
pipe = (struct pipe *)((intptr_t)fp->f_data & ~(intptr_t)1);
if ((intptr_t)fp->f_data & 1) {
rpb = &pipe->bufferB;
wpb = &pipe->bufferA;
} else {
rpb = &pipe->bufferA;
wpb = &pipe->bufferB;
}
fp->f_ops = &badfileops;
fp->f_data = NULL;
funsetown(&rpb->sigio);
pipeclose(pipe, rpb, wpb);
return (0);
}
static int
pipe_shutdown(struct file *fp, int how)
{
struct pipebuf *rpb;
struct pipebuf *wpb;
struct pipe *pipe;
int error = EPIPE;
pipe = (struct pipe *)((intptr_t)fp->f_data & ~(intptr_t)1);
if ((intptr_t)fp->f_data & 1) {
rpb = &pipe->bufferB;
wpb = &pipe->bufferA;
} else {
rpb = &pipe->bufferA;
wpb = &pipe->bufferB;
}
lwkt_gettoken(&rpb->rlock);
lwkt_gettoken(&rpb->wlock);
lwkt_gettoken(&wpb->rlock);
lwkt_gettoken(&wpb->wlock);
switch(how) {
case SHUT_RDWR:
case SHUT_RD:
atomic_set_int(&rpb->state, PIPE_REOF | PIPE_WEOF);
if (rpb->state & PIPE_WANTR) {
rpb->state &= ~PIPE_WANTR;
wakeup(rpb);
}
if (rpb->state & PIPE_WANTW) {
rpb->state &= ~PIPE_WANTW;
wakeup(rpb);
}
error = 0;
if (how == SHUT_RD)
break;
case SHUT_WR:
atomic_set_int(&wpb->state, PIPE_REOF | PIPE_WEOF);
if (wpb->state & PIPE_WANTR) {
wpb->state &= ~PIPE_WANTR;
wakeup(wpb);
}
if (wpb->state & PIPE_WANTW) {
wpb->state &= ~PIPE_WANTW;
wakeup(wpb);
}
error = 0;
break;
}
pipewakeup(rpb, 1);
pipewakeup(wpb, 1);
lwkt_reltoken(&wpb->wlock);
lwkt_reltoken(&wpb->rlock);
lwkt_reltoken(&rpb->wlock);
lwkt_reltoken(&rpb->rlock);
return (error);
}
static void
pipe_free_kmem(struct pipebuf *pb)
{
if (pb->buffer != NULL) {
kmem_free(kernel_map, (vm_offset_t)pb->buffer, pb->size);
pb->buffer = NULL;
pb->object = NULL;
}
}
static void
pipeclose(struct pipe *pipe, struct pipebuf *rpb, struct pipebuf *wpb)
{
globaldata_t gd;
if (pipe == NULL)
return;
lwkt_gettoken(&rpb->rlock);
lwkt_gettoken(&rpb->wlock);
if (rpb->state & PIPE_CLOSED) {
lwkt_reltoken(&rpb->wlock);
lwkt_reltoken(&rpb->rlock);
return;
}
atomic_set_int(&rpb->state, PIPE_CLOSED | PIPE_REOF | PIPE_WEOF);
pipewakeup(rpb, 1);
if (rpb->state & (PIPE_WANTR | PIPE_WANTW)) {
rpb->state &= ~(PIPE_WANTR | PIPE_WANTW);
wakeup(rpb);
}
lwkt_reltoken(&rpb->wlock);
lwkt_reltoken(&rpb->rlock);
lwkt_gettoken(&wpb->rlock);
lwkt_gettoken(&wpb->wlock);
atomic_set_int(&wpb->state, PIPE_REOF | PIPE_WEOF);
pipewakeup(wpb, 1);
if (wpb->state & (PIPE_WANTR | PIPE_WANTW)) {
wpb->state &= ~(PIPE_WANTR | PIPE_WANTW);
wakeup(wpb);
}
if (SLIST_FIRST(&wpb->kq.ki_note))
KNOTE(&wpb->kq.ki_note, 0);
lwkt_reltoken(&wpb->wlock);
lwkt_reltoken(&wpb->rlock);
if (atomic_fetchadd_int(&pipe->open_count, -1) == 1) {
gd = mycpu;
if (gd->gd_pipeqcount >= pipe_maxcache) {
mtx_lock(&pipe_gdlocks[gd->gd_cpuid].mtx);
pipe_free_kmem(rpb);
pipe_free_kmem(wpb);
mtx_unlock(&pipe_gdlocks[gd->gd_cpuid].mtx);
kfree(pipe, M_PIPE);
} else {
rpb->state = 0;
wpb->state = 0;
pipe->next = gd->gd_pipeq;
gd->gd_pipeq = pipe;
++gd->gd_pipeqcount;
}
}
}
static int
pipe_kqfilter(struct file *fp, struct knote *kn)
{
struct pipebuf *rpb;
struct pipebuf *wpb;
struct pipe *pipe;
pipe = (struct pipe *)((intptr_t)fp->f_data & ~(intptr_t)1);
if ((intptr_t)fp->f_data & 1) {
rpb = &pipe->bufferB;
wpb = &pipe->bufferA;
} else {
rpb = &pipe->bufferA;
wpb = &pipe->bufferB;
}
switch (kn->kn_filter) {
case EVFILT_READ:
kn->kn_fop = &pipe_rfiltops;
break;
case EVFILT_WRITE:
kn->kn_fop = &pipe_wfiltops;
break;
default:
return (EOPNOTSUPP);
}
if (rpb == &pipe->bufferA)
kn->kn_hook = (caddr_t)(void *)((intptr_t)pipe | 0);
else
kn->kn_hook = (caddr_t)(void *)((intptr_t)pipe | 1);
knote_insert(&rpb->kq.ki_note, kn);
return (0);
}
static void
filt_pipedetach(struct knote *kn)
{
struct pipebuf *rpb;
struct pipebuf *wpb;
struct pipe *pipe;
pipe = (struct pipe *)((intptr_t)kn->kn_hook & ~(intptr_t)1);
if ((intptr_t)kn->kn_hook & 1) {
rpb = &pipe->bufferB;
wpb = &pipe->bufferA;
} else {
rpb = &pipe->bufferA;
wpb = &pipe->bufferB;
}
knote_remove(&rpb->kq.ki_note, kn);
}
static int
filt_piperead(struct knote *kn, long hint)
{
struct pipebuf *rpb;
struct pipebuf *wpb;
struct pipe *pipe;
int ready = 0;
pipe = (struct pipe *)((intptr_t)kn->kn_fp->f_data & ~(intptr_t)1);
if ((intptr_t)kn->kn_fp->f_data & 1) {
rpb = &pipe->bufferB;
wpb = &pipe->bufferA;
} else {
rpb = &pipe->bufferA;
wpb = &pipe->bufferB;
}
#if 0
lwkt_gettoken(&rpb->rlock);
lwkt_gettoken(&rpb->wlock);
#endif
kn->kn_data = rpb->windex - rpb->rindex;
if (kn->kn_data < 0)
kn->kn_data = 0;
if (rpb->state & PIPE_REOF) {
if (kn->kn_data == 0)
kn->kn_flags |= EV_NODATA;
kn->kn_flags |= EV_EOF;
if (wpb->state & PIPE_CLOSED) {
kn->kn_flags |= EV_HUP;
ready = 1;
}
}
#if 0
lwkt_reltoken(&rpb->wlock);
lwkt_reltoken(&rpb->rlock);
#endif
if (!ready && (kn->kn_sfflags & NOTE_HUPONLY) == 0)
ready = kn->kn_data > 0;
return (ready);
}
static int
filt_pipewrite(struct knote *kn, long hint)
{
struct pipebuf *rpb;
struct pipebuf *wpb;
struct pipe *pipe;
int ready = 0;
pipe = (struct pipe *)((intptr_t)kn->kn_fp->f_data & ~(intptr_t)1);
if ((intptr_t)kn->kn_fp->f_data & 1) {
rpb = &pipe->bufferB;
wpb = &pipe->bufferA;
} else {
rpb = &pipe->bufferA;
wpb = &pipe->bufferB;
}
kn->kn_data = 0;
if (wpb->state & PIPE_CLOSED) {
kn->kn_flags |= EV_EOF | EV_HUP | EV_NODATA;
return (1);
}
#if 0
lwkt_gettoken(&wpb->rlock);
lwkt_gettoken(&wpb->wlock);
#endif
if (wpb->state & PIPE_WEOF) {
kn->kn_flags |= EV_EOF | EV_HUP | EV_NODATA;
ready = 1;
}
if (!ready) {
kn->kn_data = wpb->size - (wpb->windex - wpb->rindex);
if (kn->kn_data < 0)
kn->kn_data = 0;
}
#if 0
lwkt_reltoken(&wpb->wlock);
lwkt_reltoken(&wpb->rlock);
#endif
if (!ready)
ready = kn->kn_data >= PIPE_BUF;
return (ready);
}