summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorderaadt <deraadt@openbsd.org>2012-03-25 20:33:52 +0000
committerderaadt <deraadt@openbsd.org>2012-03-25 20:33:52 +0000
commit89ce1a60979493ef5f665ad261f9f5af951e1e0a (patch)
tree8daf10e78ef9581872d089d567306489d0c3f236
parentstrsignal() was added to POSIX-2008; strerror_r() was in the base (diff)
downloadwireguard-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.c42
-rw-r--r--sys/sys/eventvar.h4
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];
};