diff options
author | 2012-03-25 20:33:52 +0000 | |
---|---|---|
committer | 2012-03-25 20:33:52 +0000 | |
commit | 89ce1a60979493ef5f665ad261f9f5af951e1e0a (patch) | |
tree | 8daf10e78ef9581872d089d567306489d0c3f236 | |
parent | strsignal() was added to POSIX-2008; strerror_r() was in the base (diff) | |
download | wireguard-openbsd-89ce1a60979493ef5f665ad261f9f5af951e1e0a.tar.xz wireguard-openbsd-89ce1a60979493ef5f665ad261f9f5af951e1e0a.zip |
release the file ref to the kqueue while in kevent(), so that close()
can terminate. a new ref on the kqueue itself allows us to free it
properly in that case. wakeups were missing too (for both kevent and poll).
similar to netbsd pr46248. fixes a number of threaded ports.
this version of the fix from matthew.
ok tedu guenther matthew
-rw-r--r-- | sys/kern/kern_event.c | 42 | ||||
-rw-r--r-- | sys/sys/eventvar.h | 4 |
2 files changed, 39 insertions, 7 deletions
diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c index 619f4706cf8..0dba7f3412b 100644 --- a/sys/kern/kern_event.c +++ b/sys/kern/kern_event.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_event.c,v 1.44 2012/03/19 09:05:39 guenther Exp $ */ +/* $OpenBSD: kern_event.c,v 1.45 2012/03/25 20:33:54 deraadt Exp $ */ /*- * Copyright (c) 1999,2000,2001 Jonathan Lemon <jlemon@FreeBSD.org> @@ -53,7 +53,7 @@ #include <sys/syscallargs.h> #include <sys/timeout.h> -int kqueue_scan(struct file *fp, int maxevents, +int kqueue_scan(struct kqueue *kq, int maxevents, struct kevent *ulistp, const struct timespec *timeout, struct proc *p, int *retval); @@ -139,6 +139,23 @@ struct filterops *sysfilt_ops[] = { &timer_filtops, /* EVFILT_TIMER */ }; +void KQREF(struct kqueue *); +void KQRELE(struct kqueue *); + +void +KQREF(struct kqueue *kq) +{ + ++kq->kq_refs; +} + +void +KQRELE(struct kqueue *kq) +{ + if (--kq->kq_refs == 0) { + pool_put(&kqueue_pool, kq); + } +} + void kqueue_init(void); void @@ -435,6 +452,7 @@ sys_kqueue(struct proc *p, void *v, register_t *retval) kq = pool_get(&kqueue_pool, PR_WAITOK|PR_ZERO); TAILQ_INIT(&kq->kq_head); fp->f_data = (caddr_t)kq; + KQREF(kq); *retval = fd; if (fdp->fd_knlistsize < 0) fdp->fd_knlistsize = 0; /* this process has a kq */ @@ -516,9 +534,14 @@ sys_kevent(struct proc *p, void *v, register_t *retval) goto done; } - error = kqueue_scan(fp, SCARG(uap, nevents), SCARG(uap, eventlist), + KQREF(kq); + FRELE(fp); + error = kqueue_scan(kq, SCARG(uap, nevents), SCARG(uap, eventlist), SCARG(uap, timeout), p, &n); + KQRELE(kq); *retval = n; + return (error); + done: FRELE(fp); return (error); @@ -659,10 +682,9 @@ done: } int -kqueue_scan(struct file *fp, int maxevents, struct kevent *ulistp, +kqueue_scan(struct kqueue *kq, 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, rtv, ttv; struct knote *kn, marker; @@ -708,6 +730,11 @@ retry: } start: + if (kq->kq_state & KQ_DYING) { + error = EBADF; + goto done; + } + kevp = kq->kq_kev; s = splhigh(); if (kq->kq_count == 0) { @@ -893,9 +920,12 @@ kqueue_close(struct file *fp, struct proc *p) } } } - pool_put(&kqueue_pool, kq); fp->f_data = NULL; + kq->kq_state |= KQ_DYING; + kqueue_wakeup(kq); + KQRELE(kq); + return (0); } diff --git a/sys/sys/eventvar.h b/sys/sys/eventvar.h index b7d1633fdd5..8407d5ad0bb 100644 --- a/sys/sys/eventvar.h +++ b/sys/sys/eventvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: eventvar.h,v 1.2 2000/11/16 20:31:30 mickey Exp $ */ +/* $OpenBSD: eventvar.h,v 1.3 2012/03/25 20:33:52 deraadt Exp $ */ /*- * Copyright (c) 1999,2000 Jonathan Lemon <jlemon@FreeBSD.org> @@ -37,11 +37,13 @@ struct kqueue { TAILQ_HEAD(kqlist, knote) kq_head; /* list of pending event */ int kq_count; /* number of pending events */ + int kq_refs; /* number of references */ struct selinfo kq_sel; struct filedesc *kq_fdp; int kq_state; #define KQ_SEL 0x01 #define KQ_SLEEP 0x02 +#define KQ_DYING 0x04 struct kevent kq_kev[KQ_NEVENTS]; }; |