summaryrefslogtreecommitdiffstats
path: root/sys/net
diff options
context:
space:
mode:
authorjdp <jdp@FreeBSD.org>2001-12-14 22:17:54 +0000
committerjdp <jdp@FreeBSD.org>2001-12-14 22:17:54 +0000
commit69c3d327d1923296a67d5d0fd2802e63ab16a492 (patch)
treeeeacb40d73299ff032d264a933e2b0b0dc3c2af2 /sys/net
parent1d5fbcbdb0c947a393f140ca657e2d6561cb89a9 (diff)
downloadFreeBSD-src-69c3d327d1923296a67d5d0fd2802e63ab16a492.zip
FreeBSD-src-69c3d327d1923296a67d5d0fd2802e63ab16a492.tar.gz
Make bpf's read timeout feature work more correctly with
select/poll, and therefore with pthreads. I doubt there is any way to make this 100% semantically identical to the way it behaves in unthreaded programs with blocking reads, but the solution here should do the right thing for all reasonable usage patterns. The basic idea is to schedule a callout for the read timeout when a select/poll is done. When the callout fires, it ends the select if it is still in progress, or marks the state as "timed out" if the select has already ended for some other reason. Additional logic in bpfread then does the right thing in the case where the timeout has fired. Note, I co-opted the bd_state member of the bpf_d structure. It has been present in the structure since the initial import of 4.4-lite, but as far as I can tell it has never been used. PR: kern/22063 and bin/31649 MFC after: 3 days
Diffstat (limited to 'sys/net')
-rw-r--r--sys/net/bpf.c59
-rw-r--r--sys/net/bpfdesc.h7
2 files changed, 60 insertions, 6 deletions
diff --git a/sys/net/bpf.c b/sys/net/bpf.c
index 034ead1..0d93db5 100644
--- a/sys/net/bpf.c
+++ b/sys/net/bpf.c
@@ -100,6 +100,7 @@ static void bpf_mcopy __P((const void *, void *, size_t));
static int bpf_movein __P((struct uio *, int,
struct mbuf **, struct sockaddr *, int *));
static int bpf_setif __P((struct bpf_d *, struct ifreq *));
+static void bpf_timed_out __P((void *));
static __inline void
bpf_wakeup __P((struct bpf_d *));
static void catchpacket __P((struct bpf_d *, u_char *, u_int,
@@ -343,6 +344,7 @@ bpfopen(dev, flags, fmt, td)
d->bd_sig = SIGIO;
d->bd_seesent = 1;
mtx_init(&d->bd_mtx, devtoname(dev), MTX_DEF);
+ callout_init(&d->bd_callout, 1);
return (0);
}
@@ -361,6 +363,11 @@ bpfclose(dev, flags, fmt, td)
{
struct bpf_d *d = dev->si_drv1;
+ BPFD_LOCK(d);
+ if (d->bd_state == BPF_WAITING)
+ callout_stop(&d->bd_callout);
+ d->bd_state = BPF_IDLE;
+ BPFD_UNLOCK(d);
funsetown(d->bd_sigio);
mtx_lock(&bpf_mtx);
if (d->bd_bif)
@@ -395,6 +402,7 @@ bpfread(dev, uio, ioflag)
int ioflag;
{
struct bpf_d *d = dev->si_drv1;
+ int timed_out;
int error;
/*
@@ -405,13 +413,17 @@ bpfread(dev, uio, ioflag)
return (EINVAL);
BPFD_LOCK(d);
+ if (d->bd_state == BPF_WAITING)
+ callout_stop(&d->bd_callout);
+ timed_out = (d->bd_state == BPF_TIMED_OUT);
+ d->bd_state = BPF_IDLE;
/*
* If the hold buffer is empty, then do a timed sleep, which
* ends when the timeout expires or when enough packets
* have arrived to fill the store buffer.
*/
while (d->bd_hbuf == 0) {
- if (d->bd_immediate && d->bd_slen != 0) {
+ if ((d->bd_immediate || timed_out) && d->bd_slen != 0) {
/*
* A packet(s) either arrived since the previous
* read or arrived while we were asleep.
@@ -493,6 +505,10 @@ static __inline void
bpf_wakeup(d)
register struct bpf_d *d;
{
+ if (d->bd_state == BPF_WAITING) {
+ callout_stop(&d->bd_callout);
+ d->bd_state = BPF_IDLE;
+ }
wakeup((caddr_t)d);
if (d->bd_async && d->bd_sig && d->bd_sigio)
pgsigio(d->bd_sigio, d->bd_sig, 0);
@@ -502,6 +518,21 @@ bpf_wakeup(d)
d->bd_sel.si_pid = 0;
}
+static void
+bpf_timed_out(arg)
+ void *arg;
+{
+ struct bpf_d *d = (struct bpf_d *)arg;
+
+ BPFD_LOCK(d);
+ if (d->bd_state == BPF_WAITING) {
+ d->bd_state = BPF_TIMED_OUT;
+ if (d->bd_slen != 0)
+ bpf_wakeup(d);
+ }
+ BPFD_UNLOCK(d);
+}
+
static int
bpfwrite(dev, uio, ioflag)
dev_t dev;
@@ -595,6 +626,12 @@ bpfioctl(dev, cmd, addr, flags, td)
struct bpf_d *d = dev->si_drv1;
int error = 0;
+ BPFD_LOCK(d);
+ if (d->bd_state == BPF_WAITING)
+ callout_stop(&d->bd_callout);
+ d->bd_state = BPF_IDLE;
+ BPFD_UNLOCK(d);
+
switch (cmd) {
default:
@@ -987,10 +1024,19 @@ bpfpoll(dev, events, td)
* if (d->b_slen != 0 ||
* (d->bd_hbuf != NULL && d->bd_hlen != 0)
*/
- if (d->bd_hlen != 0 || (d->bd_immediate && d->bd_slen != 0))
+ if (d->bd_hlen != 0 ||
+ ((d->bd_immediate || d->bd_state == BPF_TIMED_OUT) &&
+ d->bd_slen != 0))
revents |= events & (POLLIN | POLLRDNORM);
- else
+ else {
selrecord(td, &d->bd_sel);
+ /* Start the read timeout if necessary. */
+ if (d->bd_rtout > 0 && d->bd_state == BPF_IDLE) {
+ callout_reset(&d->bd_callout, d->bd_rtout,
+ bpf_timed_out, d);
+ d->bd_state = BPF_WAITING;
+ }
+ }
}
BPFD_UNLOCK(d);
return (revents);
@@ -1133,10 +1179,11 @@ catchpacket(d, pkt, pktlen, snaplen, cpfn)
bpf_wakeup(d);
curlen = 0;
}
- else if (d->bd_immediate)
+ else if (d->bd_immediate || d->bd_state == BPF_TIMED_OUT)
/*
- * Immediate mode is set. A packet arrived so any
- * reads should be woken up.
+ * Immediate mode is set, or the read timeout has
+ * already expired during a select call. A packet
+ * arrived, so the reader should be woken up.
*/
bpf_wakeup(d);
diff --git a/sys/net/bpfdesc.h b/sys/net/bpfdesc.h
index 7f61d70..7db4193 100644
--- a/sys/net/bpfdesc.h
+++ b/sys/net/bpfdesc.h
@@ -43,6 +43,7 @@
#ifndef _NET_BPFDESC_H_
#define _NET_BPFDESC_H_
+#include <sys/callout.h>
#include <sys/selinfo.h>
/*
@@ -90,8 +91,14 @@ struct bpf_d {
struct selinfo bd_sel; /* bsd select info */
#endif
struct mtx bd_mtx; /* mutex for this descriptor */
+ struct callout bd_callout; /* for BPF timeouts with select */
};
+/* Values for bd_state */
+#define BPF_IDLE 0 /* no select in progress */
+#define BPF_WAITING 1 /* waiting for read timeout in select */
+#define BPF_TIMED_OUT 2 /* read timeout has expired in select */
+
#define BPFD_LOCK(bd) mtx_lock(&(bd)->bd_mtx)
#define BPFD_UNLOCK(bd) mtx_unlock(&(bd)->bd_mtx)
OpenPOWER on IntegriCloud