PERFORCE change 138092 for review
Christian S.J. Peron
csjp at FreeBSD.org
Wed Mar 19 13:32:02 UTC 2008
http://perforce.freebsd.org/chv.cgi?CH=138092
Change 138092 by csjp at ibm01 on 2008/03/19 13:31:55
Fix an interesting scenario which is the result of using SIGALARM for timer
events. The delivery of the signal perfectly coincided with the timeout
associated with the bpf descriptor, resulting in select(2) being interrupted
constantly before it could time out. Subsequently, packets could never be
processed in this case.
To work around this, we query the timer before each call to select, and we
we are interrupted, we adjust the timeout to compensate for the interruption.
So, if we schedule a 10 second timeout, and get interrupted 5 seconds in,
we will adjust the next timeout to 5 seconds.
Affected files ...
.. //depot/projects/zcopybpf/src/contrib/libpcap/pcap-bpf.c#25 edit
.. //depot/projects/zcopybpf/src/contrib/libpcap/pcap-int.h#8 edit
Differences ...
==== //depot/projects/zcopybpf/src/contrib/libpcap/pcap-bpf.c#25 (text+ko) ====
@@ -197,9 +197,12 @@
{
struct bpf_zbuf bz;
struct timeval tv;
+ struct timespec cur;
fd_set r_set;
int data, r;
+ int tmout, expire;
+#define TSTOMILLI(ts) (((ts)->tv_sec * 1000) + ((ts)->tv_nsec / 1000000))
/*
* Start out by seeing whether anything is waiting by checking the
* next shared memory buffer for data.
@@ -207,27 +210,57 @@
data = pcap_next_zbuf_shm(p, cc);
if (data)
return (data);
-
+ /*
+ * If a previous sleep was interrupted due to signal delivery, make
+ * sure that the timeout gets adjusted accordingly. This requires
+ * that we analyze when the timeout should be been expired, and
+ * subtract the current time from that. If after this operation,
+ * our timeout is less then or equal to zero, handle it like a
+ * regular timeout.
+ */
+ tmout = p->to_ms;
+ if (tmout)
+ (void) clock_gettime(CLOCK_MONOTONIC, &cur);
+ if (p->interrupted && p->to_ms) {
+ expire = TSTOMILLI(&p->firstsel) + p->to_ms;
+ tmout = expire - TSTOMILLI(&cur);
+ if (tmout <= 0) {
+ p->interrupted = 0;
+ data = pcap_next_zbuf_shm(p, cc);
+ if (data)
+ return (data);
+ if (ioctl(p->fd, BIOCROTZBUF, &bz) < 0) {
+ (void) snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
+ "BIOCROTZBUF: %s", strerror(errno));
+ return (-1);
+ }
+ return (pcap_next_zbuf_shm(p, cc));
+ }
+ }
/*
* No data in the buffer, so must use select() to wait for data or
* the next timeout.
*/
FD_ZERO(&r_set);
FD_SET(p->fd, &r_set);
- if (p->to_ms != 0) {
- tv.tv_sec = p->to_ms / 1000;
- tv.tv_usec = (p->to_ms * 1000) % 1000000;
+ if (tmout != 0) {
+ tv.tv_sec = tmout / 1000;
+ tv.tv_usec = (tmout * 1000) % 1000000;
}
r = select(p->fd + 1, &r_set, NULL, NULL, p->to_ms != 0 ? &tv :
NULL);
- if (r < 0 && errno == EINTR)
+ if (r < 0 && errno == EINTR) {
+ if (!p->interrupted && p->to_ms) {
+ p->interrupted = 1;
+ p->firstsel = cur;
+ }
return (0);
- else if (r < 0) {
+ } else if (r < 0) {
(void) snprintf(p->errbuf, PCAP_ERRBUF_SIZE,
"select: %s", strerror(errno));
return (-1);
}
-
+ p->interrupted = 0;
/*
* Check again for data, which may exist now that we've either been
* woken up as a result of data or timed out. Try the "there's data"
==== //depot/projects/zcopybpf/src/contrib/libpcap/pcap-int.h#8 (text+ko) ====
@@ -192,6 +192,8 @@
u_int zbufsize;
u_int timeout;
u_int zerocopy;
+ u_int interrupted;
+ struct timespec firstsel;
/*
* If there's currently a buffer being actively processed, then it is
More information about the p4-projects
mailing list