From 0e1bc6bd7d8477826d2acd2c9387c6627a17300e Mon Sep 17 00:00:00 2001 From: phk Date: Mon, 15 Nov 2004 14:51:44 +0000 Subject: [PATCH] Add file ops to fifofs so that we can bypass vnodes (and Giant) for the heavy-duty operations (read, write, poll/select, kqueue). Disabled for now, enable with "vfs.fifofs.fops=1" in loader.conf. --- sys/fs/fifofs/fifo_vnops.c | 165 ++++++++++++++++++++++++++++++++++++- 1 file changed, 164 insertions(+), 1 deletion(-) diff --git a/sys/fs/fifofs/fifo_vnops.c b/sys/fs/fifofs/fifo_vnops.c index d8e083f85631..af3664a96c39 100644 --- a/sys/fs/fifofs/fifo_vnops.c +++ b/sys/fs/fifofs/fifo_vnops.c @@ -32,9 +32,10 @@ #include #include +#include +#include #include #include -#include #include #include #include @@ -51,6 +52,25 @@ #include #include +static fo_rdwr_t fifo_read_f; +static fo_rdwr_t fifo_write_f; +static fo_ioctl_t fifo_ioctl_f; +static fo_poll_t fifo_poll_f; +static fo_kqfilter_t fifo_kqfilter_f; +static fo_stat_t fifo_stat_f; +static fo_close_t fifo_close_f; + +struct fileops fifo_ops_f = { + .fo_read = fifo_read_f, + .fo_write = fifo_write_f, + .fo_ioctl = fifo_ioctl_f, + .fo_poll = fifo_poll_f, + .fo_kqfilter = fifo_kqfilter_f, + .fo_stat = fifo_stat_f, + .fo_close = fifo_close_f, + .fo_flags = DFLAG_PASSABLE | DFLAG_SEEKABLE +}; + /* * This structure is associated with the FIFO vnode and stores * the state associated with the FIFO. @@ -168,7 +188,16 @@ fifo_open(ap) struct thread *td = ap->a_td; struct ucred *cred = ap->a_cred; struct socket *rso, *wso; + struct file *fp; int error; + static int once, fifofs_fops; + + if (!once) { + TUNABLE_INT_FETCH("vfs.fifofs.fops", &fifofs_fops); + if (fifofs_fops) + printf("WARNING: FIFOFS uses fops\n"); + once = 1; + } if ((fip = vp->v_fifoinfo) == NULL) { MALLOC(fip, struct fifoinfo *, sizeof(*fip), M_VNODE, M_WAITOK); @@ -279,6 +308,11 @@ fail1: } } mtx_unlock(&fifo_mtx); + fp = ap->a_td->td_proc->p_fd->fd_ofiles[ap->a_fdidx]; + if (fifofs_fops && fp->f_ops == &badfileops) { + fp->f_ops = &fifo_ops_f; + fp->f_data = fip; + } return (0); } @@ -648,3 +682,132 @@ fifo_advlock(ap) return (ap->a_flags & F_FLOCK ? EOPNOTSUPP : EINVAL); } + +static int +fifo_close_f(struct file *fp, struct thread *td) +{ + + return (vnops.fo_close(fp, td)); +} + +static int +fifo_ioctl_f(struct file *fp, u_long com, void *data, struct ucred *cred, struct thread *td) +{ + + return (vnops.fo_ioctl(fp, com, data, cred, td)); +} + +static int +fifo_kqfilter_f(struct file *fp, struct knote *kn) +{ + struct fifoinfo *fi; + struct socket *so; + struct sockbuf *sb; + + fi = fp->f_data; + switch (kn->kn_filter) { + case EVFILT_READ: + kn->kn_fop = &fiforead_filtops; + so = fi->fi_readsock; + sb = &so->so_rcv; + break; + case EVFILT_WRITE: + kn->kn_fop = &fifowrite_filtops; + so = fi->fi_writesock; + sb = &so->so_snd; + break; + default: + return (1); + } + + kn->kn_hook = (caddr_t)so; + + SOCKBUF_LOCK(sb); + knlist_add(&sb->sb_sel.si_note, kn, 1); + sb->sb_flags |= SB_KNOTE; + SOCKBUF_UNLOCK(sb); + + return (0); +} + +static int +fifo_poll_f(struct file *fp, int events, struct ucred *cred, struct thread *td) +{ + struct fifoinfo *fip; + struct file filetmp; + int levents, revents = 0; + + fip = fp->f_data; + levents = events & + (POLLIN | POLLINIGNEOF | POLLPRI | POLLRDNORM | POLLRDBAND); + if (levents) { + /* + * If POLLIN or POLLRDNORM is requested and POLLINIGNEOF is + * not, then convert the first two to the last one. This + * tells the socket poll function to ignore EOF so that we + * block if there is no writer (and no data). Callers can + * set POLLINIGNEOF to get non-blocking behavior. + */ + if (levents & (POLLIN | POLLRDNORM) && + !(levents & POLLINIGNEOF)) { + levents &= ~(POLLIN | POLLRDNORM); + levents |= POLLINIGNEOF; + } + + filetmp.f_data = fip->fi_readsock; + filetmp.f_cred = cred; + if (filetmp.f_data) + revents |= soo_poll(&filetmp, levents, cred, td); + + /* Reverse the above conversion. */ + if ((revents & POLLINIGNEOF) && !(events & POLLINIGNEOF)) { + revents |= (events & (POLLIN | POLLRDNORM)); + revents &= ~POLLINIGNEOF; + } + } + levents = events & (POLLOUT | POLLWRNORM | POLLWRBAND); + if (events) { + filetmp.f_data = fip->fi_writesock; + filetmp.f_cred = cred; + if (filetmp.f_data) { + revents |= soo_poll(&filetmp, events, cred, td); + } + } + return (revents); +} + +static int +fifo_read_f(struct file *fp, struct uio *uio, struct ucred *cred, int flags, struct thread *td) +{ + struct fifoinfo *fip; + int error, sflags; + + fip = fp->f_data; + KASSERT(uio->uio_rw == UIO_READ,("fifo_read mode")); + if (uio->uio_resid == 0) + return (0); + sflags = (fp->f_flag & FNONBLOCK) ? MSG_NBIO : 0; + error = soreceive(fip->fi_readsock, NULL, uio, NULL, NULL, &sflags); + return (error); +} + +static int +fifo_stat_f(struct file *fp, struct stat *sb, struct ucred *cred, struct thread *td) +{ + + return (vnops.fo_stat(fp, sb, cred, td)); +} + +static int +fifo_write_f(struct file *fp, struct uio *uio, struct ucred *cred, int flags, struct thread *td) +{ + struct fifoinfo *fip; + int error, sflags; + + fip = fp->f_data; + KASSERT(uio->uio_rw == UIO_WRITE,("fifo_write mode")); + sflags = (fp->f_flag & FNONBLOCK) ? MSG_NBIO : 0; + error = sosend(fip->fi_writesock, NULL, uio, 0, NULL, sflags, td); + return (error); +} +