diff --git a/sys/net/bpf.c b/sys/net/bpf.c index 034ead1731dc..0d93db504eb8 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 7f61d7009536..7db41935ec5d 100644 --- a/sys/net/bpfdesc.h +++ b/sys/net/bpfdesc.h @@ -43,6 +43,7 @@ #ifndef _NET_BPFDESC_H_ #define _NET_BPFDESC_H_ +#include #include /* @@ -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)