diff options
author | 2000-11-16 20:02:15 +0000 | |
---|---|---|
committer | 2000-11-16 20:02:15 +0000 | |
commit | 1a12e8a7ab11e47f6fb18cdbc03b05e8885dfcad (patch) | |
tree | 21c49b483d84ca5ca2b0b9f8491c15f7b579b1f3 | |
parent | Add uscanner.c. (diff) | |
download | wireguard-openbsd-1a12e8a7ab11e47f6fb18cdbc03b05e8885dfcad.tar.xz wireguard-openbsd-1a12e8a7ab11e47f6fb18cdbc03b05e8885dfcad.zip |
support kernel event queues, from FreeBSD by Jonathan Lemon,
okay art@, millert@
-rw-r--r-- | sys/conf/files | 3 | ||||
-rw-r--r-- | sys/kern/kern_descrip.c | 29 | ||||
-rw-r--r-- | sys/kern/kern_event.c | 887 | ||||
-rw-r--r-- | sys/kern/kern_exec.c | 7 | ||||
-rw-r--r-- | sys/kern/kern_exit.c | 7 | ||||
-rw-r--r-- | sys/kern/kern_fork.c | 7 | ||||
-rw-r--r-- | sys/kern/kern_sig.c | 55 | ||||
-rw-r--r-- | sys/kern/sys_pipe.c | 64 | ||||
-rw-r--r-- | sys/kern/syscalls.master | 7 | ||||
-rw-r--r-- | sys/kern/uipc_socket.c | 114 | ||||
-rw-r--r-- | sys/kern/uipc_socket2.c | 4 | ||||
-rw-r--r-- | sys/kern/uipc_syscalls.c | 8 | ||||
-rw-r--r-- | sys/miscfs/tcfs/tcfs_keytab.h | 3 | ||||
-rw-r--r-- | sys/sys/event.h | 165 | ||||
-rw-r--r-- | sys/sys/eventvar.h | 46 | ||||
-rw-r--r-- | sys/sys/file.h | 3 | ||||
-rw-r--r-- | sys/sys/filedesc.h | 7 | ||||
-rw-r--r-- | sys/sys/malloc.h | 8 | ||||
-rw-r--r-- | sys/sys/proc.h | 6 | ||||
-rw-r--r-- | sys/sys/queue.h | 15 | ||||
-rw-r--r-- | sys/sys/select.h | 5 | ||||
-rw-r--r-- | sys/sys/socketvar.h | 6 | ||||
-rw-r--r-- | sys/sys/tty.h | 4 |
23 files changed, 1433 insertions, 27 deletions
diff --git a/sys/conf/files b/sys/conf/files index 37357b7e816..639d057c205 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $OpenBSD: files,v 1.179 2000/11/16 05:53:59 aaron Exp $ +# $OpenBSD: files,v 1.180 2000/11/16 20:02:21 provos Exp $ # $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -365,6 +365,7 @@ file kern/init_sysent.c file kern/kern_acct.c file kern/kern_clock.c file kern/kern_descrip.c +file kern/kern_event.c file kern/kern_exec.c file kern/kern_exit.c file kern/kern_fork.c diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index 48027b553b3..d1ef891fbb1 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_descrip.c,v 1.22 2000/09/27 16:13:46 mickey Exp $ */ +/* $OpenBSD: kern_descrip.c,v 1.23 2000/11/16 20:02:16 provos Exp $ */ /* $NetBSD: kern_descrip.c,v 1.42 1996/03/30 22:24:38 christos Exp $ */ /* @@ -61,6 +61,7 @@ #include <sys/conf.h> #include <sys/mount.h> #include <sys/syscallargs.h> +#include <sys/event.h> #include <vm/vm.h> @@ -463,6 +464,8 @@ fdrelease(p, fd) *fpp = NULL; *pf = 0; fd_unused(fdp, fd); + if (fd < fdp->fd_knlistsize) + knote_fdclose(p, fd); return (closef(fp, p)); } @@ -805,6 +808,7 @@ fdinit(p) newfdp->fd_fd.fd_nfiles = NDFILE; newfdp->fd_fd.fd_himap = newfdp->fd_dhimap; newfdp->fd_fd.fd_lomap = newfdp->fd_dlomap; + newfdp->fd_fd.fd_knlistsize = -1; newfdp->fd_fd.fd_freefile = 0; newfdp->fd_fd.fd_lastfile = 0; @@ -881,8 +885,23 @@ fdcopy(p) bcopy(fdp->fd_ofileflags, newfdp->fd_ofileflags, i * sizeof(char)); bcopy(fdp->fd_himap, newfdp->fd_himap, NDHISLOTS(i) * sizeof(u_int)); bcopy(fdp->fd_lomap, newfdp->fd_lomap, NDLOSLOTS(i) * sizeof(u_int)); + + /* + * kq descriptors cannot be copied. + */ + if (newfdp->fd_knlistsize != -1) { + fpp = newfdp->fd_ofiles; + for (i = 0; i <= newfdp->fd_lastfile; i++, fpp++) + if (*fpp != NULL && (*fpp)->f_type == DTYPE_KQUEUE) + fdremove(newfdp, i); + newfdp->fd_knlist = NULL; + newfdp->fd_knlistsize = -1; + newfdp->fd_knhash = NULL; + newfdp->fd_knhashmask = 0; + } + fpp = newfdp->fd_ofiles; - for (i = newfdp->fd_lastfile; i >= 0; i--, fpp++) + for (i = 0; i <= newfdp->fd_lastfile; i++, fpp++) if (*fpp != NULL) { /* * XXX Gruesome hack. If count gets too high, fail @@ -890,7 +909,7 @@ fdcopy(p) * permit it to indicate failure yet. */ if ((*fpp)->f_count == LONG_MAX-2) - *fpp = NULL; + fdremove(newfdp, i); else (*fpp)->f_count++; } @@ -928,6 +947,10 @@ fdfree(p) vrele(fdp->fd_cdir); if (fdp->fd_rdir) vrele(fdp->fd_rdir); + if (fdp->fd_knlist) + FREE(fdp->fd_knlist, M_TEMP); + if (fdp->fd_knhash) + FREE(fdp->fd_knhash, M_TEMP); FREE(fdp, M_FILEDESC); } diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c new file mode 100644 index 00000000000..7726c5b0554 --- /dev/null +++ b/sys/kern/kern_event.c @@ -0,0 +1,887 @@ +/*- + * Copyright (c) 1999,2000 Jonathan Lemon <jlemon@FreeBSD.org> + * All rights reserved. + * + * 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 AND CONTRIBUTORS ``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. + * + * $FreeBSD: src/sys/kern/kern_event.c,v 1.15 2000/08/07 16:45:42 jlemon Exp $ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/proc.h> +#include <sys/malloc.h> +#include <sys/unistd.h> +#include <sys/file.h> +#include <sys/filedesc.h> +#include <sys/fcntl.h> +#include <sys/select.h> +#include <sys/queue.h> +#include <sys/event.h> +#include <sys/eventvar.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/stat.h> +#include <sys/uio.h> +#include <sys/mount.h> +#include <sys/syscallargs.h> + +int filt_nullattach(struct knote *kn); +int filt_rwtypattach(struct knote *kn); +int filt_kqattach(struct knote *kn); +void filt_kqdetach(struct knote *kn); +int filt_kqueue(struct knote *kn, long hint); +int filt_procattach(struct knote *kn); +void filt_procdetach(struct knote *kn); +int filt_proc(struct knote *kn, long hint); + +int kqueue_scan(struct file *fp, int maxevents, + struct kevent *ulistp, const struct timespec *timeout, + struct proc *p, int *retval); + +int kqueue_read(struct file *fp, off_t *poff, struct uio *uio, + struct ucred *cred); +int kqueue_write(struct file *fp, off_t *poff, struct uio *uio, + struct ucred *cred); +int kqueue_ioctl(struct file *fp, u_long com, caddr_t data, + struct proc *p); +int kqueue_select(struct file *fp, int which, struct proc *p); +int kqueue_close(struct file *fp, struct proc *p); +void kqueue_wakeup(struct kqueue *kq); + +void knote_attach(struct knote *kn, struct filedesc *fdp); +void knote_drop(struct knote *kn, struct proc *p); +void knote_enqueue(struct knote *kn); +void knote_dequeue(struct knote *kn); +struct knote *knote_alloc(void); +void knote_free(struct knote *kn); + +#define KNOTE_ACTIVATE(kn) do { \ + kn->kn_status |= KN_ACTIVE; \ + if ((kn->kn_status & (KN_QUEUED | KN_DISABLED)) == 0) \ + knote_enqueue(kn); \ +} while(0) + +#define KN_HASHSIZE 64 /* XXX should be tunable */ +#define KN_HASH(val, mask) (((val) ^ (val >> 8)) & (mask)) + +struct fileops kqueueops = { + kqueue_read, + kqueue_write, + kqueue_ioctl, + kqueue_select, + kqueue_close +}; + +extern struct filterops so_rwfiltops[]; +extern struct filterops pipe_rwfiltops[]; +#ifdef notyet +extern struct filterops fifo_rwfiltops[]; +extern struct filterops vn_rwfiltops[]; +#endif + +struct filterops kq_rwfiltops[] = { + { 1, filt_kqattach, filt_kqdetach, filt_kqueue }, + { 1, filt_nullattach, NULL, NULL }, +}; + +extern struct filterops sig_filtops; +#ifdef notyet +extern struct filterops aio_filtops; +extern struct filterops vn_filtops; +#endif + +struct filterops rwtype_filtops = + { 1, filt_rwtypattach, NULL, NULL }; +struct filterops proc_filtops = + { 0, filt_procattach, filt_procdetach, filt_proc }; + +/* + * XXX + * These must match the order of defines in <sys/file.h> + */ +struct filterops *rwtypfilt_sw[] = { + NULL, /* 0 */ + NULL, /* vn_rwfiltops, */ /* DTYPE_VNODE */ + so_rwfiltops, /* DTYPE_SOCKET */ + pipe_rwfiltops, /* DTYPE_PIPE */ + /* fifo_rwfiltops, */ /* DTYPE_FIFO */ + kq_rwfiltops, /* DTYPE_KQUEUE */ +}; + +/* + * table for for all system-defined filters. + */ +struct filterops *sysfilt_ops[] = { + &rwtype_filtops, /* EVFILT_READ */ + &rwtype_filtops, /* EVFILT_WRITE */ + NULL, /*&aio_filtops,*/ /* EVFILT_AIO */ + NULL, /*&vn_filtops,*/ /* EVFILT_VNODE */ + &proc_filtops, /* EVFILT_PROC */ + &sig_filtops, /* EVFILT_SIGNAL */ +}; + +int +filt_nullattach(struct knote *kn) +{ + return (ENXIO); +} + +/* + * file-type specific attach routine for read/write filters + */ +int +filt_rwtypattach(struct knote *kn) +{ + struct filterops *fops; + + fops = rwtypfilt_sw[kn->kn_fp->f_type]; + if (fops == NULL) + return (EINVAL); + kn->kn_fop = &fops[~kn->kn_filter]; /* convert to 0-base index */ + return (kn->kn_fop->f_attach(kn)); +} + +int +filt_kqattach(struct knote *kn) +{ + struct kqueue *kq = (struct kqueue *)kn->kn_fp->f_data; + + SLIST_INSERT_HEAD(&kq->kq_sel.si_note, kn, kn_selnext); + return (0); +} + +void +filt_kqdetach(struct knote *kn) +{ + struct kqueue *kq = (struct kqueue *)kn->kn_fp->f_data; + + SLIST_REMOVE(&kq->kq_sel.si_note, kn, knote, kn_selnext); +} + +/*ARGSUSED*/ +int +filt_kqueue(struct knote *kn, long hint) +{ + struct kqueue *kq = (struct kqueue *)kn->kn_fp->f_data; + + kn->kn_data = kq->kq_count; + return (kn->kn_data > 0); +} + +int +filt_procattach(struct knote *kn) +{ + struct proc *p; + + p = pfind(kn->kn_id); + if (p == NULL) + return (ESRCH); + if (suser(p->p_ucred, &p->p_acflag) != 0) + return (EACCES); + + kn->kn_ptr.p_proc = p; + kn->kn_flags |= EV_CLEAR; /* automatically set */ + + /* + * internal flag indicating registration done by kernel + */ + if (kn->kn_flags & EV_FLAG1) { + kn->kn_data = kn->kn_sdata; /* ppid */ + kn->kn_fflags = NOTE_CHILD; + kn->kn_flags &= ~EV_FLAG1; + } + + /* XXX lock the proc here while adding to the list? */ + SLIST_INSERT_HEAD(&p->p_klist, kn, kn_selnext); + + return (0); +} + +/* + * The knote may be attached to a different process, which may exit, + * leaving nothing for the knote to be attached to. So when the process + * exits, the knote is marked as DETACHED and also flagged as ONESHOT so + * it will be deleted when read out. However, as part of the knote deletion, + * this routine is called, so a check is needed to avoid actually performing + * a detach, because the original process does not exist any more. + */ +void +filt_procdetach(struct knote *kn) +{ + struct proc *p = kn->kn_ptr.p_proc; + + if (kn->kn_status & KN_DETACHED) + return; + + /* XXX locking? this might modify another process. */ + SLIST_REMOVE(&p->p_klist, kn, knote, kn_selnext); +} + +int +filt_proc(struct knote *kn, long hint) +{ + u_int event; + + /* + * mask off extra data + */ + event = (u_int)hint & NOTE_PCTRLMASK; + + /* + * if the user is interested in this event, record it. + */ + if (kn->kn_sfflags & event) + kn->kn_fflags |= event; + + /* + * process is gone, so flag the event as finished. + */ + if (event == NOTE_EXIT) { + kn->kn_status |= KN_DETACHED; + kn->kn_flags |= (EV_EOF | EV_ONESHOT); + return (1); + } + + /* + * process forked, and user wants to track the new process, + * so attach a new knote to it, and immediately report an + * event with the parent's pid. + */ + if ((event == NOTE_FORK) && (kn->kn_sfflags & NOTE_TRACK)) { + struct kevent kev; + int error; + + /* + * register knote with new process. + */ + kev.ident = hint & NOTE_PDATAMASK; /* pid */ + kev.filter = kn->kn_filter; + kev.flags = kn->kn_flags | EV_ADD | EV_ENABLE | EV_FLAG1; + kev.fflags = kn->kn_sfflags; + kev.data = kn->kn_id; /* parent */ + kev.udata = kn->kn_kevent.udata; /* preserve udata */ + error = kqueue_register(kn->kn_kq, &kev, NULL); + if (error) + kn->kn_fflags |= NOTE_TRACKERR; + } + + return (kn->kn_fflags != 0); +} + +int +sys_kqueue(struct proc *p, void *v, register_t *retval) +{ + struct filedesc *fdp = p->p_fd; + struct kqueue *kq; + struct file *fp; + int fd, error; + + error = falloc(p, &fp, &fd); + if (error) + return (error); + fp->f_flag = FREAD | FWRITE; + fp->f_type = DTYPE_KQUEUE; + fp->f_ops = &kqueueops; + kq = malloc(sizeof(struct kqueue), M_TEMP, M_WAITOK); + bzero(kq, sizeof(*kq)); + TAILQ_INIT(&kq->kq_head); + fp->f_data = (caddr_t)kq; + *retval = fd; + if (fdp->fd_knlistsize < 0) + fdp->fd_knlistsize = 0; /* this process has a kq */ + kq->kq_fdp = fdp; + return (error); +} + +int +sys_kevent(struct proc *p, void *v, register_t *retval) +{ + struct filedesc* fdp = p->p_fd; + struct sys_kevent_args /* { + syscallarg(int) fd; + syscallarg(const struct kevent *) changelist; + syscallarg(int) nchanges; + syscallarg(struct kevent *) eventlist; + syscallarg(int) nevents; + syscallarg(const struct timespec *) timeout; + } */ *uap = v; + struct kevent *kevp; + struct kqueue *kq; + struct file *fp; + struct timespec ts; + int i, n, nerrors, error; + + if (((u_int)SCARG(uap, fd)) >= fdp->fd_nfiles || + (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL || + (fp->f_type != DTYPE_KQUEUE)) + return (EBADF); + + if (SCARG(uap, timeout) != NULL) { + error = copyin(SCARG(uap, timeout), &ts, sizeof(ts)); + if (error) + return error; + SCARG(uap, timeout) = &ts; + } + + kq = (struct kqueue *)fp->f_data; + nerrors = 0; + + while (SCARG(uap, nchanges) > 0) { + n = SCARG(uap, nchanges) > KQ_NEVENTS + ? KQ_NEVENTS : SCARG(uap, nchanges); + error = copyin(SCARG(uap, changelist), kq->kq_kev, + n * sizeof(struct kevent)); + if (error) + return (error); + for (i = 0; i < n; i++) { + kevp = &kq->kq_kev[i]; + kevp->flags &= ~EV_SYSFLAGS; + error = kqueue_register(kq, kevp, p); + if (error) { + if (SCARG(uap, nevents) != 0) { + kevp->flags = EV_ERROR; + kevp->data = error; + (void) copyout((caddr_t)kevp, + (caddr_t)SCARG(uap, eventlist), + sizeof(*kevp)); + SCARG(uap, eventlist)++; + SCARG(uap, nevents)--; + nerrors++; + } else { + return (error); + } + } + } + SCARG(uap, nchanges) -= n; + SCARG(uap, changelist) += n; + } + if (nerrors) { + *retval = nerrors; + return (0); + } + + error = kqueue_scan(fp, SCARG(uap, nevents), SCARG(uap, eventlist), + SCARG(uap, timeout), p, &n); + *retval = n; + return (error); +} + +int +kqueue_register(struct kqueue *kq, struct kevent *kev, struct proc *p) +{ + struct filedesc *fdp = kq->kq_fdp; + struct filterops *fops = NULL; + struct file *fp = NULL; + struct knote *kn = NULL; + int s, error = 0; + + if (kev->filter < 0) { + if (kev->filter + EVFILT_SYSCOUNT < 0) + return (EINVAL); + fops = sysfilt_ops[~kev->filter]; /* to 0-base index */ + } + + if (fops == NULL) { + /* + * XXX + * filter attach routine is responsible for insuring that + * the identifier can be attached to it. + */ + printf("unknown filter: %d\n", kev->filter); + return (EINVAL); + } + + if (fops->f_isfd) { + /* validate descriptor */ + if ((u_int)kev->ident >= fdp->fd_nfiles || + (fp = fdp->fd_ofiles[kev->ident]) == NULL) + return (EBADF); + + if (kev->ident < fdp->fd_knlistsize) { + SLIST_FOREACH(kn, &fdp->fd_knlist[kev->ident], kn_link) + if (kq == kn->kn_kq && + kev->filter == kn->kn_filter) + break; + } + } else { + if (fdp->fd_knhashmask != 0) { + struct klist *list; + + list = &fdp->fd_knhash[ + KN_HASH((u_long)kev->ident, fdp->fd_knhashmask)]; + SLIST_FOREACH(kn, list, kn_link) + if (kev->ident == kn->kn_id && + kq == kn->kn_kq && + kev->filter == kn->kn_filter) + break; + } + } + + if (kn == NULL && ((kev->flags & EV_ADD) == 0)) + return (ENOENT); + + /* + * kn now contains the matching knote, or NULL if no match + */ + if (kev->flags & EV_ADD) { + + if (kn == NULL) { + kn = knote_alloc(); + if (kn == NULL) + return (ENOMEM); + if (fp != NULL) + fp->f_count++; + kn->kn_fp = fp; + kn->kn_kq = kq; + kn->kn_fop = fops; + + kn->kn_sfflags = kev->fflags; + kn->kn_sdata = kev->data; + kev->fflags = 0; + kev->data = 0; + kn->kn_kevent = *kev; + + knote_attach(kn, fdp); + if ((error = fops->f_attach(kn)) != 0) { + knote_drop(kn, p); + goto done; + } + } else { + /* + * The user may change some filter values after the + * initial EV_ADD, but doing so will not reset any + * filter which have already been triggered. + */ + kn->kn_sfflags = kev->fflags; + kn->kn_sdata = kev->data; + kn->kn_kevent.udata = kev->udata; + } + + s = splhigh(); + if (kn->kn_fop->f_event(kn, 0)) + KNOTE_ACTIVATE(kn); + splx(s); + + } else if (kev->flags & EV_DELETE) { + kn->kn_fop->f_detach(kn); + knote_drop(kn, p); + goto done; + } + + if ((kev->flags & EV_DISABLE) && + ((kn->kn_status & KN_DISABLED) == 0)) { + s = splhigh(); + kn->kn_status |= KN_DISABLED; + splx(s); + } + + if ((kev->flags & EV_ENABLE) && (kn->kn_status & KN_DISABLED)) { + s = splhigh(); + kn->kn_status &= ~KN_DISABLED; + if ((kn->kn_status & KN_ACTIVE) && + ((kn->kn_status & KN_QUEUED) == 0)) + knote_enqueue(kn); + splx(s); + } + +done: + return (error); +} + +int +kqueue_scan(struct file *fp, int maxevents, struct kevent *ulistp, + const struct timespec *tsp, struct proc *p, int *retval) +{ + struct kqueue *kq = (struct kqueue *)fp->f_data; + struct kevent *kevp; + struct timeval atv, ttv; + struct knote *kn, marker; + int s, count, timeout, nkev = 0, error = 0; + + count = maxevents; + if (count == 0) + goto done; + + if (tsp != NULL) { + TIMESPEC_TO_TIMEVAL(&atv, tsp); + if (itimerfix(&atv)) { + error = EINVAL; + goto done; + } + if (tsp->tv_sec == 0 && tsp->tv_nsec == 0) + timeout = -1; + else + timeout = atv.tv_sec > 24 * 60 * 60 ? + 24 * 60 * 60 * hz : hzto(&atv); + timeradd(&atv, &time, &atv); + } else { + atv.tv_sec = 0; + atv.tv_usec = 0; + timeout = 0; + } + goto start; + +retry: + if (atv.tv_sec || atv.tv_usec) { + if (timercmp(&time, &atv, >=)) + goto done; + ttv = atv; + timersub(&ttv, &ttv, &time); + timeout = ttv.tv_sec > 24 * 60 * 60 ? + 24 * 60 * 60 * hz : hzto(&ttv); + } + +start: + kevp = kq->kq_kev; + s = splhigh(); + if (kq->kq_count == 0) { + if (timeout < 0) { + error = EWOULDBLOCK; + } else { + kq->kq_state |= KQ_SLEEP; + error = tsleep(kq, PSOCK | PCATCH, "kqread", timeout); + } + splx(s); + if (error == 0) + goto retry; + /* don't restart after signals... */ + if (error == ERESTART) + error = EINTR; + else if (error == EWOULDBLOCK) + error = 0; + goto done; + } + + TAILQ_INSERT_TAIL(&kq->kq_head, &marker, kn_tqe); + while (count) { + kn = TAILQ_FIRST(&kq->kq_head); + TAILQ_REMOVE(&kq->kq_head, kn, kn_tqe); + if (kn == &marker) { + splx(s); + if (count == maxevents) + goto retry; + goto done; + } + if (kn->kn_status & KN_DISABLED) { + kn->kn_status &= ~KN_QUEUED; + kq->kq_count--; + continue; + } + if ((kn->kn_flags & EV_ONESHOT) == 0 && + kn->kn_fop->f_event(kn, 0) == 0) { + kn->kn_status &= ~(KN_QUEUED | KN_ACTIVE); + kq->kq_count--; + continue; + } + *kevp = kn->kn_kevent; + kevp++; + nkev++; + if (kn->kn_flags & EV_ONESHOT) { + kn->kn_status &= ~KN_QUEUED; + kq->kq_count--; + splx(s); + kn->kn_fop->f_detach(kn); + knote_drop(kn, p); + s = splhigh(); + } else if (kn->kn_flags & EV_CLEAR) { + kn->kn_data = 0; + kn->kn_fflags = 0; + kn->kn_status &= ~(KN_QUEUED | KN_ACTIVE); + kq->kq_count--; + } else { + TAILQ_INSERT_TAIL(&kq->kq_head, kn, kn_tqe); + } + count--; + if (nkev == KQ_NEVENTS) { + splx(s); + error = copyout((caddr_t)&kq->kq_kev, (caddr_t)ulistp, + sizeof(struct kevent) * nkev); + ulistp += nkev; + nkev = 0; + kevp = kq->kq_kev; + s = splhigh(); + if (error) + break; + } + } + TAILQ_REMOVE(&kq->kq_head, &marker, kn_tqe); + splx(s); +done: + if (nkev != 0) + error = copyout((caddr_t)&kq->kq_kev, (caddr_t)ulistp, + sizeof(struct kevent) * nkev); + *retval = maxevents - count; + return (error); +} + +/* + * XXX + * This could be expanded to call kqueue_scan, if desired. + */ +/*ARGSUSED*/ +int +kqueue_read(struct file *fp, off_t *poff, struct uio *uio, struct ucred *cred) +{ + return (ENXIO); +} + +/*ARGSUSED*/ +int +kqueue_write(struct file *fp, off_t *poff, struct uio *uio, struct ucred *cred) + +{ + return (ENXIO); +} + +/*ARGSUSED*/ +int +kqueue_ioctl(struct file *fp, u_long com, caddr_t data, struct proc *p) +{ + return (ENOTTY); +} + +/*ARGSUSED*/ +int +kqueue_select(struct file *fp, int which, struct proc *p) +{ + struct kqueue *kq = (struct kqueue *)fp->f_data; + int res = 0; + int s = splnet(); + + if (which == FREAD) { + if (kq->kq_count) { + res = 1; + } else { + selrecord(p, &kq->kq_sel); + kq->kq_state |= KQ_SEL; + } + } + splx(s); + return (res); +} + +/*ARGSUSED*/ +int +kqueue_close(struct file *fp, struct proc *p) +{ + struct kqueue *kq = (struct kqueue *)fp->f_data; + struct filedesc *fdp = p->p_fd; + struct knote **knp, *kn, *kn0; + int i; + + for (i = 0; i < fdp->fd_knlistsize; i++) { + knp = &SLIST_FIRST(&fdp->fd_knlist[i]); + kn = *knp; + while (kn != NULL) { + kn0 = SLIST_NEXT(kn, kn_link); + if (kq == kn->kn_kq) { + kn->kn_fop->f_detach(kn); + closef(kn->kn_fp, p); + knote_free(kn); + *knp = kn0; + } else { + knp = &SLIST_NEXT(kn, kn_link); + } + kn = kn0; + } + } + if (fdp->fd_knhashmask != 0) { + for (i = 0; i < fdp->fd_knhashmask + 1; i++) { + knp = &SLIST_FIRST(&fdp->fd_knhash[i]); + kn = *knp; + while (kn != NULL) { + kn0 = SLIST_NEXT(kn, kn_link); + if (kq == kn->kn_kq) { + kn->kn_fop->f_detach(kn); + /* XXX non-fd release of kn->kn_ptr */ + knote_free(kn); + *knp = kn0; + } else { + knp = &SLIST_NEXT(kn, kn_link); + } + kn = kn0; + } + } + } + free(kq, M_TEMP); + fp->f_data = NULL; + + return (0); +} + +void +kqueue_wakeup(struct kqueue *kq) +{ + + if (kq->kq_state & KQ_SLEEP) { + kq->kq_state &= ~KQ_SLEEP; + wakeup(kq); + } + if (kq->kq_state & KQ_SEL) { + kq->kq_state &= ~KQ_SEL; + selwakeup(&kq->kq_sel); + } + KNOTE(&kq->kq_sel.si_note, 0); +} + +/* + * walk down a list of knotes, activating them if their event has triggered. + */ +void +knote(struct klist *list, long hint) +{ + struct knote *kn; + + SLIST_FOREACH(kn, list, kn_selnext) + if (kn->kn_fop->f_event(kn, hint)) + KNOTE_ACTIVATE(kn); +} + +/* + * remove all knotes from a specified klist + */ +void +knote_remove(struct proc *p, struct klist *list) +{ + struct knote *kn; + + while ((kn = SLIST_FIRST(list)) != NULL) { + kn->kn_fop->f_detach(kn); + knote_drop(kn, p); + } +} + +/* + * remove all knotes referencing a specified fd + */ +void +knote_fdclose(struct proc *p, int fd) +{ + struct filedesc *fdp = p->p_fd; + struct klist *list = &fdp->fd_knlist[fd]; + + knote_remove(p, list); +} + +void +knote_attach(struct knote *kn, struct filedesc *fdp) +{ + struct klist *list; + int size; + + if (! kn->kn_fop->f_isfd) { + if (fdp->fd_knhashmask == 0) + fdp->fd_knhash = hashinit(KN_HASHSIZE, M_TEMP, + M_WAITOK, &fdp->fd_knhashmask); + list = &fdp->fd_knhash[KN_HASH(kn->kn_id, fdp->fd_knhashmask)]; + goto done; + } + + if (fdp->fd_knlistsize <= kn->kn_id) { + size = fdp->fd_knlistsize; + while (size <= kn->kn_id) + size += KQEXTENT; + MALLOC(list, struct klist *, + size * sizeof(struct klist *), M_TEMP, M_WAITOK); + bcopy((caddr_t)fdp->fd_knlist, (caddr_t)list, + fdp->fd_knlistsize * sizeof(struct klist *)); + bzero((caddr_t)list + + fdp->fd_knlistsize * sizeof(struct klist *), + (size - fdp->fd_knlistsize) * sizeof(struct klist *)); + if (fdp->fd_knlist != NULL) + FREE(fdp->fd_knlist, M_TEMP); + fdp->fd_knlistsize = size; + fdp->fd_knlist = list; + } + list = &fdp->fd_knlist[kn->kn_id]; +done: + SLIST_INSERT_HEAD(list, kn, kn_link); + kn->kn_status = 0; +} + +/* + * should be called at spl == 0, since we don't want to hold spl + * while calling closef and free. + */ +void +knote_drop(struct knote *kn, struct proc *p) +{ + struct filedesc *fdp = p->p_fd; + struct klist *list; + + if (kn->kn_fop->f_isfd) + list = &fdp->fd_knlist[kn->kn_id]; + else + list = &fdp->fd_knhash[KN_HASH(kn->kn_id, fdp->fd_knhashmask)]; + + SLIST_REMOVE(list, kn, knote, kn_link); + if (kn->kn_status & KN_QUEUED) + knote_dequeue(kn); + if (kn->kn_fop->f_isfd) + closef(kn->kn_fp, p); + knote_free(kn); +} + + +void +knote_enqueue(struct knote *kn) +{ + struct kqueue *kq = kn->kn_kq; + int s = splhigh(); + + KASSERT((kn->kn_status & KN_QUEUED) == 0); + + TAILQ_INSERT_TAIL(&kq->kq_head, kn, kn_tqe); + kn->kn_status |= KN_QUEUED; + kq->kq_count++; + splx(s); + kqueue_wakeup(kq); +} + +void +knote_dequeue(struct knote *kn) +{ + struct kqueue *kq = kn->kn_kq; + int s = splhigh(); + + KASSERT(kn->kn_status & KN_QUEUED); + + TAILQ_REMOVE(&kq->kq_head, kn, kn_tqe); + kn->kn_status &= ~KN_QUEUED; + kq->kq_count--; + splx(s); +} + +struct knote * +knote_alloc(void) +{ + return (malloc(sizeof (struct knote), M_KNOTE, M_NOWAIT)); +} + +void +knote_free(struct knote *kn) +{ + free(kn, M_KNOTE); +} diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c index 162313d55e4..71c068a9348 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_exec.c,v 1.45 2000/11/10 18:15:46 art Exp $ */ +/* $OpenBSD: kern_exec.c,v 1.46 2000/11/16 20:02:16 provos Exp $ */ /* $NetBSD: kern_exec.c,v 1.75 1996/02/09 18:59:28 christos Exp $ */ /*- @@ -599,6 +599,11 @@ sys_execve(p, v, retval) VOP_CLOSE(pack.ep_vp, FREAD, cred, p); vput(pack.ep_vp); + /* + * notify others that we exec'd + */ + KNOTE(&p->p_klist, NOTE_EXEC); + /* setup new registers and do misc. setup. */ if(pack.ep_emul->e_fixup != NULL) { if((*pack.ep_emul->e_fixup)(p, &pack) != 0) diff --git a/sys/kern/kern_exit.c b/sys/kern/kern_exit.c index cb2aea9696a..96c19a5d6ed 100644 --- a/sys/kern/kern_exit.c +++ b/sys/kern/kern_exit.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_exit.c,v 1.27 2000/06/06 18:50:32 art Exp $ */ +/* $OpenBSD: kern_exit.c,v 1.28 2000/11/16 20:02:16 provos Exp $ */ /* $NetBSD: kern_exit.c,v 1.39 1996/04/22 01:38:25 christos Exp $ */ /* @@ -264,6 +264,11 @@ exit1(p, rv) p->p_pctcpu = 0; /* + * notify interested parties of our demise. + */ + KNOTE(&p->p_klist, NOTE_EXIT); + + /* * Notify parent that we're gone. If we have P_NOZOMBIE or parent has * the P_NOCLDWAIT flag set, notify process 1 instead (and hope it * will handle this situation). diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c index aa7457f3e7b..3a8f4173220 100644 --- a/sys/kern/kern_fork.c +++ b/sys/kern/kern_fork.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_fork.c,v 1.35 2000/11/09 17:02:25 art Exp $ */ +/* $OpenBSD: kern_fork.c,v 1.36 2000/11/16 20:02:17 provos Exp $ */ /* $NetBSD: kern_fork.c,v 1.29 1996/02/09 18:59:34 christos Exp $ */ /* @@ -425,6 +425,11 @@ again: #endif /* + * tell any interested parties about the new process + */ + KNOTE(&p1->p_klist, NOTE_FORK | p2->p_pid); + + /* * Preserve synchronization semantics of vfork. If waiting for * child to exec or exit, set P_PPWAIT on child, and sleep on our * proc (in case of exit). diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c index bb5c923f364..08a902b8c32 100644 --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_sig.c,v 1.39 2000/11/10 18:15:47 art Exp $ */ +/* $OpenBSD: kern_sig.c,v 1.40 2000/11/16 20:02:17 provos Exp $ */ /* $NetBSD: kern_sig.c,v 1.54 1996/04/22 01:38:32 christos Exp $ */ /* @@ -46,8 +46,10 @@ #include <sys/param.h> #include <sys/signalvar.h> #include <sys/resourcevar.h> +#include <sys/queue.h> #include <sys/namei.h> #include <sys/vnode.h> +#include <sys/event.h> #include <sys/proc.h> #include <sys/systm.h> #include <sys/timeb.h> @@ -75,6 +77,13 @@ #include <uvm/uvm_extern.h> #endif +int filt_sigattach(struct knote *kn); +void filt_sigdetach(struct knote *kn); +int filt_signal(struct knote *kn, long hint); + +struct filterops sig_filtops = + { 0, filt_sigattach, filt_sigdetach, filt_signal }; + void stop __P((struct proc *p)); void killproc __P((struct proc *, char *)); int cansignal __P((struct proc *, struct pcred *, struct proc *, int)); @@ -683,6 +692,9 @@ psignal(p, signum) if ((u_int)signum >= NSIG || signum == 0) panic("psignal signal number"); + + KNOTE(&p->p_klist, NOTE_SIGNAL | signum); + mask = sigmask(signum); prop = sigprop[signum]; @@ -1331,3 +1343,44 @@ initsiginfo(si, sig, code, type, val) } } } + +int +filt_sigattach(struct knote *kn) +{ + struct proc *p = curproc; + + kn->kn_ptr.p_proc = p; + kn->kn_flags |= EV_CLEAR; /* automatically set */ + + /* XXX lock the proc here while adding to the list? */ + SLIST_INSERT_HEAD(&p->p_klist, kn, kn_selnext); + + return (0); +} + +void +filt_sigdetach(struct knote *kn) +{ + struct proc *p = kn->kn_ptr.p_proc; + + SLIST_REMOVE(&p->p_klist, kn, knote, kn_selnext); +} + +/* + * signal knotes are shared with proc knotes, so we apply a mask to + * the hint in order to differentiate them from process hints. This + * could be avoided by using a signal-specific knote list, but probably + * isn't worth the trouble. + */ +int +filt_signal(struct knote *kn, long hint) +{ + + if (hint & NOTE_SIGNAL) { + hint &= ~NOTE_SIGNAL; + + if (kn->kn_id == hint) + kn->kn_data++; + } + return (kn->kn_data != 0); +} diff --git a/sys/kern/sys_pipe.c b/sys/kern/sys_pipe.c index 497d716288b..28dfd3dc210 100644 --- a/sys/kern/sys_pipe.c +++ b/sys/kern/sys_pipe.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sys_pipe.c,v 1.24 2000/04/19 08:34:54 csapuntz Exp $ */ +/* $OpenBSD: sys_pipe.c,v 1.25 2000/11/16 20:02:17 provos Exp $ */ /* * Copyright (c) 1996 John S. Dyson @@ -45,6 +45,7 @@ #include <sys/kernel.h> #include <sys/mount.h> #include <sys/syscallargs.h> +#include <sys/event.h> #include <vm/vm.h> #include <vm/vm_prot.h> @@ -71,6 +72,15 @@ int pipe_ioctl __P((struct file *, u_long, caddr_t, struct proc *)); static struct fileops pipeops = { pipe_read, pipe_write, pipe_ioctl, pipe_select, pipe_close }; +int filt_pipeattach(struct knote *kn); +void filt_pipedetach(struct knote *kn); +int filt_piperead(struct knote *kn, long hint); +int filt_pipewrite(struct knote *kn, long hint); + +struct filterops pipe_rwfiltops[] = { + { 1, filt_pipeattach, filt_pipedetach, filt_piperead }, + { 1, filt_pipeattach, filt_pipedetach, filt_pipewrite }, +}; /* * Default pipe buffer size(s), this can be kind-of large now because pipe @@ -260,6 +270,7 @@ pipeselwakeup(cpipe) } if ((cpipe->pipe_state & PIPE_ASYNC) && cpipe->pipe_pgid != NO_PID) gsignal(cpipe->pipe_pgid, SIGIO); + KNOTE(&cpipe->pipe_sel.si_note, 0); } /* ARGSUSED */ @@ -745,3 +756,54 @@ pipeclose(cpipe) } } #endif + +int +filt_pipeattach(struct knote *kn) +{ + struct pipe *rpipe = (struct pipe *)kn->kn_fp->f_data; + + SLIST_INSERT_HEAD(&rpipe->pipe_sel.si_note, kn, kn_selnext); + return (0); +} + +void +filt_pipedetach(struct knote *kn) +{ + struct pipe *rpipe = (struct pipe *)kn->kn_fp->f_data; + + SLIST_REMOVE(&rpipe->pipe_sel.si_note, kn, knote, kn_selnext); +} + +/*ARGSUSED*/ +int +filt_piperead(struct knote *kn, long hint) +{ + struct pipe *rpipe = (struct pipe *)kn->kn_fp->f_data; + struct pipe *wpipe = rpipe->pipe_peer; + + kn->kn_data = rpipe->pipe_buffer.cnt; + + if ((rpipe->pipe_state & PIPE_EOF) || + (wpipe == NULL) || (wpipe->pipe_state & PIPE_EOF)) { + kn->kn_flags |= EV_EOF; + return (1); + } + return (kn->kn_data > 0); +} + +/*ARGSUSED*/ +int +filt_pipewrite(struct knote *kn, long hint) +{ + struct pipe *rpipe = (struct pipe *)kn->kn_fp->f_data; + struct pipe *wpipe = rpipe->pipe_peer; + + if ((wpipe == NULL) || (wpipe->pipe_state & PIPE_EOF)) { + kn->kn_data = 0; + kn->kn_flags |= EV_EOF; + return (1); + } + kn->kn_data = wpipe->pipe_buffer.size - wpipe->pipe_buffer.cnt; + + return (kn->kn_data >= PIPE_BUF); +} diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index 7d4cb0c94bb..7232e9e1ca5 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -1,4 +1,4 @@ -; $OpenBSD: syscalls.master,v 1.42 2000/09/12 17:25:56 millert Exp $ +; $OpenBSD: syscalls.master,v 1.43 2000/11/16 20:02:17 provos Exp $ ; $NetBSD: syscalls.master,v 1.32 1996/04/23 10:24:21 mycroft Exp $ ; @(#)syscalls.master 8.2 (Berkeley) 1/13/94 @@ -525,3 +525,8 @@ 268 STD { ssize_t sys_pwritev(int fd, \ const struct iovec *iovp, int iovcnt, \ int pad, off_t offset); } +269 STD { int sys_kqueue(void); } +270 STD { int sys_kevent(int fd, \ + const struct kevent *changelist, int nchanges, \ + struct kevent *eventlist, int nevents, \ + const struct timespec *timeout); } diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c index 3dce36b6462..82d9575ed05 100644 --- a/sys/kern/uipc_socket.c +++ b/sys/kern/uipc_socket.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uipc_socket.c,v 1.27 1999/10/14 08:18:49 cmetz Exp $ */ +/* $OpenBSD: uipc_socket.c,v 1.28 2000/11/16 20:02:19 provos Exp $ */ /* $NetBSD: uipc_socket.c,v 1.21 1996/02/04 02:17:52 christos Exp $ */ /* @@ -44,12 +44,29 @@ #include <sys/mbuf.h> #include <sys/domain.h> #include <sys/kernel.h> +#include <sys/event.h> #include <sys/protosw.h> #include <sys/socket.h> #include <sys/socketvar.h> #include <sys/signalvar.h> #include <sys/resourcevar.h> +int filt_sorattach(struct knote *kn); +void filt_sordetach(struct knote *kn); +int filt_soread(struct knote *kn, long hint); +int filt_sowattach(struct knote *kn); +void filt_sowdetach(struct knote *kn); +int filt_sowrite(struct knote *kn, long hint); +int filt_solisten(struct knote *kn, long hint); + +struct filterops solisten_filtops = + { 1, filt_sorattach, filt_sordetach, filt_solisten }; + +struct filterops so_rwfiltops[] = { + { 1, filt_sorattach, filt_sordetach, filt_soread }, + { 1, filt_sowattach, filt_sowdetach, filt_sowrite }, +}; + #ifndef SOMINCONN #define SOMINCONN 80 #endif /* SOMINCONN */ @@ -1093,3 +1110,98 @@ sohasoutofband(so) csignal(so->so_pgid, SIGURG, so->so_siguid, so->so_sigeuid); selwakeup(&so->so_rcv.sb_sel); } + +int +filt_sorattach(struct knote *kn) +{ + struct socket *so = (struct socket *)kn->kn_fp->f_data; + int s = splnet(); + + if (so->so_options & SO_ACCEPTCONN) + kn->kn_fop = &solisten_filtops; + SLIST_INSERT_HEAD(&so->so_rcv.sb_sel.si_note, kn, kn_selnext); + so->so_rcv.sb_flags |= SB_KNOTE; + splx(s); + return (0); +} + +void +filt_sordetach(struct knote *kn) +{ + struct socket *so = (struct socket *)kn->kn_fp->f_data; + int s = splnet(); + + SLIST_REMOVE(&so->so_rcv.sb_sel.si_note, kn, knote, kn_selnext); + if (SLIST_EMPTY(&so->so_rcv.sb_sel.si_note)) + so->so_rcv.sb_flags &= ~SB_KNOTE; + splx(s); +} + +/*ARGSUSED*/ +int +filt_soread(struct knote *kn, long hint) +{ + struct socket *so = (struct socket *)kn->kn_fp->f_data; + + kn->kn_data = so->so_rcv.sb_cc; + if (so->so_state & SS_CANTRCVMORE) { + kn->kn_flags |= EV_EOF; + return (1); + } + if (so->so_error) /* temporary udp error */ + return (1); + return (kn->kn_data >= so->so_rcv.sb_lowat); +} + +int +filt_sowattach(struct knote *kn) +{ + struct socket *so = (struct socket *)kn->kn_fp->f_data; + int s = splnet(); + + SLIST_INSERT_HEAD(&so->so_snd.sb_sel.si_note, kn, kn_selnext); + so->so_snd.sb_flags |= SB_KNOTE; + splx(s); + return (0); +} + +void +filt_sowdetach(struct knote *kn) +{ + struct socket *so = (struct socket *)kn->kn_fp->f_data; + int s = splnet(); + + SLIST_REMOVE(&so->so_snd.sb_sel.si_note, kn, knote, kn_selnext); + if (SLIST_EMPTY(&so->so_snd.sb_sel.si_note)) + so->so_snd.sb_flags &= ~SB_KNOTE; + splx(s); +} + +/*ARGSUSED*/ +int +filt_sowrite(struct knote *kn, long hint) +{ + struct socket *so = (struct socket *)kn->kn_fp->f_data; + + kn->kn_data = sbspace(&so->so_snd); + if (so->so_state & SS_CANTSENDMORE) { + kn->kn_flags |= EV_EOF; + return (1); + } + if (so->so_error) /* temporary udp error */ + return (1); + if (((so->so_state & SS_ISCONNECTED) == 0) && + (so->so_proto->pr_flags & PR_CONNREQUIRED)) + return (0); + return (kn->kn_data >= so->so_snd.sb_lowat); +} + +/*ARGSUSED*/ +int +filt_solisten(struct knote *kn, long hint) +{ + struct socket *so = (struct socket *)kn->kn_fp->f_data; + + kn->kn_data = so->so_qlen; + return (so->so_qlen != 0); +} diff --git a/sys/kern/uipc_socket2.c b/sys/kern/uipc_socket2.c index 4dd7419a52a..68cb8c9eca2 100644 --- a/sys/kern/uipc_socket2.c +++ b/sys/kern/uipc_socket2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uipc_socket2.c,v 1.14 2000/02/29 19:16:46 itojun Exp $ */ +/* $OpenBSD: uipc_socket2.c,v 1.15 2000/11/16 20:02:19 provos Exp $ */ /* $NetBSD: uipc_socket2.c,v 1.11 1996/02/04 02:17:55 christos Exp $ */ /* @@ -47,6 +47,7 @@ #include <sys/socket.h> #include <sys/socketvar.h> #include <sys/signalvar.h> +#include <sys/event.h> /* * Primitive routines for operating on sockets and socket buffers @@ -324,6 +325,7 @@ sowakeup(so, sb) } if (so->so_state & SS_ASYNC) csignal(so->so_pgid, SIGIO, so->so_siguid, so->so_sigeuid); + KNOTE(&sb->sb_sel.si_note, 0); } /* diff --git a/sys/kern/uipc_syscalls.c b/sys/kern/uipc_syscalls.c index 786b1ed0bb5..33ddae83b58 100644 --- a/sys/kern/uipc_syscalls.c +++ b/sys/kern/uipc_syscalls.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uipc_syscalls.c,v 1.33 2000/11/10 18:15:48 art Exp $ */ +/* $OpenBSD: uipc_syscalls.c,v 1.34 2000/11/16 20:02:19 provos Exp $ */ /* $NetBSD: uipc_syscalls.c,v 1.19 1996/02/09 19:00:48 christos Exp $ */ /* @@ -43,6 +43,7 @@ #include <sys/file.h> #include <sys/buf.h> #include <sys/malloc.h> +#include <sys/event.h> #include <sys/mbuf.h> #include <sys/protosw.h> #include <sys/socket.h> @@ -195,11 +196,16 @@ sys_accept(p, v, retval) return (error); } *retval = tmpfd; + + /* connection has been removed from the listen queue */ + KNOTE(&so->so_rcv.sb_sel.si_note, 0); + { struct socket *aso = so->so_q; if (soqremque(aso, 1) == 0) panic("accept"); so = aso; } + fp->f_type = DTYPE_SOCKET; fp->f_flag = FREAD|FWRITE; fp->f_ops = &socketops; diff --git a/sys/miscfs/tcfs/tcfs_keytab.h b/sys/miscfs/tcfs/tcfs_keytab.h index e5a286080f1..82b2e3bd353 100644 --- a/sys/miscfs/tcfs/tcfs_keytab.h +++ b/sys/miscfs/tcfs/tcfs_keytab.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tcfs_keytab.h,v 1.4 2000/06/18 16:23:10 provos Exp $ */ +/* $OpenBSD: tcfs_keytab.h,v 1.5 2000/11/16 20:02:21 provos Exp $ */ /* * Copyright 2000 The TCFS Project at http://tcfs.dia.unisa.it/ * All rights reserved. @@ -79,6 +79,7 @@ typedef struct _kn { unsigned int kn_type; void *kn_key; +#undef kn_data tcfs_grp_data *kn_data; struct _kn *kn_n; diff --git a/sys/sys/event.h b/sys/sys/event.h new file mode 100644 index 00000000000..7ed150fa4fd --- /dev/null +++ b/sys/sys/event.h @@ -0,0 +1,165 @@ +/*- + * Copyright (c) 1999,2000 Jonathan Lemon <jlemon@FreeBSD.org> + * All rights reserved. + * + * 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 AND CONTRIBUTORS ``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. + * + * $FreeBSD: src/sys/sys/event.h,v 1.7 2000/05/26 02:06:54 jake Exp $ + */ + +#ifndef _SYS_EVENT_H_ +#define _SYS_EVENT_H_ + +#define EVFILT_READ (-1) +#define EVFILT_WRITE (-2) +#define EVFILT_AIO (-3) /* attached to aio requests */ +#define EVFILT_VNODE (-4) /* attached to vnodes */ +#define EVFILT_PROC (-5) /* attached to struct proc */ +#define EVFILT_SIGNAL (-6) /* attached to struct proc */ + +#define EVFILT_SYSCOUNT 6 + +struct kevent { + u_int ident; /* identifier for this event */ + short filter; /* filter for event */ + u_short flags; + u_int fflags; + int data; + void *udata; /* opaque user data identifier */ +}; + +/* actions */ +#define EV_ADD 0x0001 /* add event to kq (implies enable) */ +#define EV_DELETE 0x0002 /* delete event from kq */ +#define EV_ENABLE 0x0004 /* enable event */ +#define EV_DISABLE 0x0008 /* disable event (not reported) */ + +/* flags */ +#define EV_ONESHOT 0x0010 /* only report one occurrence */ +#define EV_CLEAR 0x0020 /* clear event state after reporting */ + +#define EV_SYSFLAGS 0xF000 /* reserved by system */ +#define EV_FLAG1 0x2000 /* filter-specific flag */ + +/* returned values */ +#define EV_EOF 0x8000 /* EOF detected */ +#define EV_ERROR 0x4000 /* error, data contains errno */ + +/* + * data/hint flags for EVFILT_VNODE, shared with userspace + */ +#define NOTE_DELETE 0x0001 /* vnode was removed */ +#define NOTE_WRITE 0x0002 /* data contents changed */ +#define NOTE_EXTEND 0x0004 /* size increased */ +#define NOTE_ATTRIB 0x0008 /* attributes changed */ +#define NOTE_LINK 0x0010 /* link count changed */ +#define NOTE_RENAME 0x0020 /* vnode was renamed */ + +/* + * data/hint flags for EVFILT_PROC, shared with userspace + */ +#define NOTE_EXIT 0x80000000 /* process exited */ +#define NOTE_FORK 0x40000000 /* process forked */ +#define NOTE_EXEC 0x20000000 /* process exec'd */ +#define NOTE_PCTRLMASK 0xf0000000 /* mask for hint bits */ +#define NOTE_PDATAMASK 0x000fffff /* mask for pid */ + +/* additional flags for EVFILT_PROC */ +#define NOTE_TRACK 0x00000001 /* follow across forks */ +#define NOTE_TRACKERR 0x00000002 /* could not track child */ +#define NOTE_CHILD 0x00000004 /* am a child process */ + +/* + * This is currently visible to userland to work around broken + * programs which pull in <sys/proc.h> or <sys/select.h>. + */ +#include <sys/queue.h> +struct knote; +SLIST_HEAD(klist, knote); + +#ifdef _KERNEL + +#define KNOTE(list, hint) if ((list) != NULL) knote(list, hint) + +/* + * Flag indicating hint is a signal. Used by EVFILT_SIGNAL, and also + * shared by EVFILT_PROC (all knotes attached to p->p_klist) + */ +#define NOTE_SIGNAL 0x08000000 + +struct filterops { + int f_isfd; /* true if ident == filedescriptor */ + int (*f_attach) __P((struct knote *kn)); + void (*f_detach) __P((struct knote *kn)); + int (*f_event) __P((struct knote *kn, long hint)); +}; + +struct knote { + SLIST_ENTRY(knote) kn_link; /* for fd */ + SLIST_ENTRY(knote) kn_selnext; /* for struct selinfo */ + TAILQ_ENTRY(knote) kn_tqe; + struct kqueue *kn_kq; /* which queue we are on */ + struct kevent kn_kevent; + int kn_status; + int kn_sfflags; /* saved filter flags */ + int kn_sdata; /* saved data field */ + union { + struct file *p_fp; /* file data pointer */ + struct proc *p_proc; /* proc pointer */ + } kn_ptr; + struct filterops *kn_fop; +#define KN_ACTIVE 0x01 /* event has been triggered */ +#define KN_QUEUED 0x02 /* event is on queue */ +#define KN_DISABLED 0x04 /* event is disabled */ +#define KN_DETACHED 0x08 /* knote is detached */ + +#define kn_id kn_kevent.ident +#define kn_filter kn_kevent.filter +#define kn_flags kn_kevent.flags +#define kn_fflags kn_kevent.fflags +#define kn_data kn_kevent.data +#define kn_fp kn_ptr.p_fp +}; + +struct proc; + +extern void knote(struct klist *list, long hint); +extern void knote_remove(struct proc *p, struct klist *list); +extern void knote_fdclose(struct proc *p, int fd); +extern int kqueue_register(struct kqueue *kq, + struct kevent *kev, struct proc *p); + +#else /* !_KERNEL */ + +#include <sys/cdefs.h> +struct timespec; + +__BEGIN_DECLS +int kqueue __P((void)); +int kevent __P((int kq, const struct kevent *changelist, int nchanges, + struct kevent *eventlist, int nevents, + const struct timespec *timeout)); +__END_DECLS + +#endif /* !_KERNEL */ + +#endif /* !_SYS_EVENT_H_ */ diff --git a/sys/sys/eventvar.h b/sys/sys/eventvar.h new file mode 100644 index 00000000000..e927b7abd18 --- /dev/null +++ b/sys/sys/eventvar.h @@ -0,0 +1,46 @@ +/*- + * Copyright (c) 1999,2000 Jonathan Lemon <jlemon@FreeBSD.org> + * All rights reserved. + * + * 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 AND CONTRIBUTORS ``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. + * + * $FreeBSD: src/sys/sys/eventvar.h,v 1.3 2000/05/26 02:06:54 jake Exp $ + */ + +#ifndef _SYS_EVENTVAR_H_ +#define _SYS_EVENTVAR_H_ + +#define KQ_NEVENTS 8 /* minimize copy{in,out} calls */ +#define KQEXTENT 256 /* linear growth by this amount */ + +struct kqueue { + TAILQ_HEAD(kqlist, knote) kq_head; /* list of pending event */ + int kq_count; /* number of pending events */ + struct selinfo kq_sel; + struct filedesc *kq_fdp; + int kq_state; +#define KQ_SEL 0x01 +#define KQ_SLEEP 0x02 + struct kevent kq_kev[KQ_NEVENTS]; +}; + +#endif /* !_SYS_EVENTVAR_H_ */ diff --git a/sys/sys/file.h b/sys/sys/file.h index dc37d91edf5..35482f912b9 100644 --- a/sys/sys/file.h +++ b/sys/sys/file.h @@ -1,4 +1,4 @@ -/* $OpenBSD: file.h,v 1.8 2000/05/24 15:14:59 deraadt Exp $ */ +/* $OpenBSD: file.h,v 1.9 2000/11/16 20:02:20 provos Exp $ */ /* $NetBSD: file.h,v 1.11 1995/03/26 20:24:13 jtc Exp $ */ /* @@ -55,6 +55,7 @@ struct file { #define DTYPE_VNODE 1 /* file */ #define DTYPE_SOCKET 2 /* communications endpoint */ #define DTYPE_PIPE 3 /* pipe */ +#define DTYPE_KQUEUE 4 /* event queue */ short f_type; /* descriptor type */ long f_count; /* reference count */ long f_msgcount; /* references from message queue */ diff --git a/sys/sys/filedesc.h b/sys/sys/filedesc.h index e936413eac3..85b4c3d8530 100644 --- a/sys/sys/filedesc.h +++ b/sys/sys/filedesc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: filedesc.h,v 1.8 2000/04/24 06:26:23 provos Exp $ */ +/* $OpenBSD: filedesc.h,v 1.9 2000/11/16 20:02:20 provos Exp $ */ /* $NetBSD: filedesc.h,v 1.14 1996/04/09 20:55:28 cgd Exp $ */ /* @@ -71,6 +71,11 @@ struct filedesc { int fd_freefile; /* approx. next free file */ u_short fd_cmask; /* mask for file creation */ u_short fd_refcnt; /* reference count */ + + int fd_knlistsize; /* size of knlist */ + struct klist *fd_knlist; /* list of attached knotes */ + u_long fd_knhashmask; /* size of knhash */ + struct klist *fd_knhash; /* hash table for attached knotes */ }; /* diff --git a/sys/sys/malloc.h b/sys/sys/malloc.h index 6e9ba0c5fef..fd510d0e459 100644 --- a/sys/sys/malloc.h +++ b/sys/sys/malloc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: malloc.h,v 1.29 2000/08/12 05:59:50 deraadt Exp $ */ +/* $OpenBSD: malloc.h,v 1.30 2000/11/16 20:02:20 provos Exp $ */ /* $NetBSD: malloc.h,v 1.39 1998/07/12 19:52:01 augustss Exp $ */ /* @@ -159,7 +159,6 @@ #define M_USB 101 /* USB general */ #define M_USBDEV 102 /* USB device driver */ #define M_USBHC 103 /* USB host controller */ - /* KAME IPv6 */ #define M_IP6OPT 123 /* IPv6 options */ @@ -173,6 +172,8 @@ #define M_DEBUG 106 /* MALLOC_DEBUG structures */ +#define M_KNOTE 107 /* kernel event queue */ + #define M_TEMP 127 /* misc temporary data buffers */ #define M_LAST 128 /* Must be last type + 1 */ @@ -285,7 +286,8 @@ "pipe", /* 104 M_PIPE */ \ "memdesc", /* 105 M_MEMDESC */ \ "malloc debug", /* 106 M_DEBUG */ \ - NULL, NULL, NULL, NULL, NULL, \ + "knote", /* 107 M_KNOTE */ \ + NULL, NULL, NULL, NULL, \ NULL, NULL, NULL, NULL, NULL, \ NULL, NULL, NULL, NULL, NULL, \ NULL, \ diff --git a/sys/sys/proc.h b/sys/sys/proc.h index dd8b7d7924f..cf7ad3ae628 100644 --- a/sys/sys/proc.h +++ b/sys/sys/proc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: proc.h,v 1.33 2000/11/08 05:38:47 art Exp $ */ +/* $OpenBSD: proc.h,v 1.34 2000/11/16 20:02:20 provos Exp $ */ /* $NetBSD: proc.h,v 1.44 1996/04/22 01:23:21 christos Exp $ */ /*- @@ -48,6 +48,7 @@ #include <sys/select.h> /* For struct selinfo. */ #include <sys/queue.h> #include <sys/timeout.h> /* For struct timeout. */ +#include <sys/event.h> /* For struct klist */ /* * One structure allocated per session. @@ -173,7 +174,8 @@ struct proc { int p_holdcnt; /* If non-zero, don't swap. */ struct emul *p_emul; /* Emulation information */ - long p_spare[1]; /* pad to 256, avoid shifting eproc. */ + struct klist p_klist; /* knotes attached to this process */ + /* pad to 256, avoid shifting eproc. */ /* End area that is zeroed on creation. */ diff --git a/sys/sys/queue.h b/sys/sys/queue.h index 269af413c35..d2aeeba377d 100644 --- a/sys/sys/queue.h +++ b/sys/sys/queue.h @@ -1,4 +1,4 @@ -/* $OpenBSD: queue.h,v 1.16 2000/09/07 19:47:59 art Exp $ */ +/* $OpenBSD: queue.h,v 1.17 2000/11/16 20:02:20 provos Exp $ */ /* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ /* @@ -136,6 +136,19 @@ struct { \ (head)->slh_first = (head)->slh_first->field.sle_next; \ } while (0) +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = (head)->slh_first; \ + while( curelm->field.sle_next != (elm) ) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ +} while (0) + /* * List definitions. */ diff --git a/sys/sys/select.h b/sys/sys/select.h index 28be22899ad..d6cada6fc36 100644 --- a/sys/sys/select.h +++ b/sys/sys/select.h @@ -1,4 +1,4 @@ -/* $OpenBSD: select.h,v 1.3 1997/01/27 23:21:21 deraadt Exp $ */ +/* $OpenBSD: select.h,v 1.4 2000/11/16 20:02:20 provos Exp $ */ /* $NetBSD: select.h,v 1.10 1995/03/26 20:24:38 jtc Exp $ */ /*- @@ -39,12 +39,15 @@ #ifndef _SYS_SELECT_H_ #define _SYS_SELECT_H_ +#include <sys/event.h> /* for struct klist */ + /* * Used to maintain information about processes that wish to be * notified when I/O becomes possible. */ struct selinfo { pid_t si_selpid; /* process to be notified */ + struct klist si_note; /* kernel note list */ short si_flags; /* see below */ }; #define SI_COLL 0x0001 /* collision occurred */ diff --git a/sys/sys/socketvar.h b/sys/sys/socketvar.h index 073e912ff74..034fa0fad73 100644 --- a/sys/sys/socketvar.h +++ b/sys/sys/socketvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: socketvar.h,v 1.18 2000/04/19 08:34:51 csapuntz Exp $ */ +/* $OpenBSD: socketvar.h,v 1.19 2000/11/16 20:02:20 provos Exp $ */ /* $NetBSD: socketvar.h,v 1.18 1996/02/09 18:25:38 christos Exp $ */ /*- @@ -95,6 +95,7 @@ struct socket { #define SB_SEL 0x08 /* someone is selecting */ #define SB_ASYNC 0x10 /* ASYNC I/O, need signals */ #define SB_NOINTR 0x40 /* operations not interruptible */ +#define SB_KNOTE 0x80 /* kernel note attached */ void *so_internal; /* Space for svr4 stream data */ void (*so_upcall) __P((struct socket *so, caddr_t arg, int waitf)); @@ -128,7 +129,8 @@ struct socket { /* * Do we need to notify the other side when I/O is possible? */ -#define sb_notify(sb) (((sb)->sb_flags & (SB_WAIT|SB_SEL|SB_ASYNC)) != 0) +#define sb_notify(sb) (((sb)->sb_flags & (SB_WAIT|SB_SEL|SB_ASYNC| \ + SB_KNOTE)) != 0) /* * How much space is there in a socket buffer (so->so_snd or so->so_rcv)? diff --git a/sys/sys/tty.h b/sys/sys/tty.h index 381beb88ac9..36f822b1095 100644 --- a/sys/sys/tty.h +++ b/sys/sys/tty.h @@ -1,4 +1,4 @@ -/* $OpenBSD: tty.h,v 1.6 2000/08/13 03:38:45 ericj Exp $ */ +/* $OpenBSD: tty.h,v 1.7 2000/11/16 20:02:21 provos Exp $ */ /* $NetBSD: tty.h,v 1.30.4.1 1996/06/02 09:08:13 mrg Exp $ */ /*- @@ -42,8 +42,8 @@ */ #include <sys/termios.h> -#include <sys/select.h> /* For struct selinfo. */ #include <sys/queue.h> +#include <sys/select.h> /* For struct selinfo. */ #include <sys/timeout.h> #ifndef REAL_CLISTS |