Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 19 Mar 2008 13:32:01 GMT
From:      "Christian S.J. Peron" <csjp@FreeBSD.org>
To:        Perforce Change Reviews <perforce@freebsd.org>
Subject:   PERFORCE change 138092 for review
Message-ID:  <200803191332.m2JDW1YA043967@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=138092

Change 138092 by csjp@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



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200803191332.m2JDW1YA043967>