svn commit: r237502 - in projects/calloutng/sys: kern sys

Davide Italiano davide at FreeBSD.org
Sat Jun 23 19:39:04 UTC 2012


Author: davide
Date: Sat Jun 23 19:39:03 2012
New Revision: 237502
URL: http://svn.freebsd.org/changeset/base/237502

Log:
  First commit that concerns event aggregation. Augment the callout structure
  so that consumers other than actual time at which callout should fire may
  specify a tolerance interval.
  
  Rather than looking for the next callout event in callout_tick()
  determine a range [t-delta;t'+delta'] deriving it from the tolerance
  parameter specified by clients so that's suitable for a given number of events,
  and schedule an interrupt in the middle of such range.
  
  Add some comments to better specify what we're doing.
  
  Suggested by:		mav
  Reviewed by:		mav

Modified:
  projects/calloutng/sys/kern/kern_timeout.c
  projects/calloutng/sys/sys/_callout.h
  projects/calloutng/sys/sys/callout.h

Modified: projects/calloutng/sys/kern/kern_timeout.c
==============================================================================
--- projects/calloutng/sys/kern/kern_timeout.c	Sat Jun 23 19:15:12 2012	(r237501)
+++ projects/calloutng/sys/kern/kern_timeout.c	Sat Jun 23 19:39:03 2012	(r237502)
@@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/interrupt.h>
 #include <sys/kernel.h>
 #include <sys/ktr.h>
+#include <sys/limits.h>
 #include <sys/lock.h>
 #include <sys/malloc.h>
 #include <sys/mutex.h>
@@ -358,7 +359,7 @@ get_bucket(struct bintime *bt)
 void
 callout_tick(void)
 {
-	struct bintime limit, next, now;
+	struct bintime limit, max, min, next, now, tmp_max, tmp_min;
 	struct callout *tmp;
 	struct callout_cpu *cc;
 	struct callout_tailq *sc;
@@ -375,25 +376,21 @@ callout_tick(void)
 	cpu = curcpu;
 	first = callout_hash(&cc->cc_softticks);
 	last = callout_hash(&now);
-	next.sec = -1;
-	next.frac = -1;
-	future = ((last + hz/4) & callwheelmask); 
 	/* 
 	 * Check if we wrapped around the entire wheel from the last scan.
 	 * In case, we need to scan entirely the wheel for pending callouts.
 	 */
-	if (last - first >= callwheelsize) { 
-		first &= callwheelmask;
-		last = (first - 1) & callwheelmask;
-	}
-	else {
-		first &= callwheelmask;
-		last &= callwheelmask;
-	}
+	last = (last - first >= callwheelsize) ? (first - 1) & callwheelmask : 
+	    last & callwheelmask;
+	first &= callwheelmask;
 	for (;;) {	
 		sc = &cc->cc_callwheel[first];
 		TAILQ_FOREACH(tmp, sc, c_links.tqe) {
 			if (bintime_cmp(&tmp->c_time, &now, <=)) {
+				/* 
+				 * Consumer told us the callout may be run
+				 * directly from hardware interrupt context.
+				 */
 				if (tmp->c_flags & CALLOUT_DIRECT) {
 					tmp->c_func(tmp->c_arg);
 					TAILQ_REMOVE(sc, tmp, c_links.tqe);
@@ -410,31 +407,69 @@ callout_tick(void)
 		}	
 		if (first == last)
 			break;
-		first = ((first + 1) & callwheelmask);
+		first = (first + 1) & callwheelmask;
 	}
+	future = ((last + hz/4) & callwheelmask); 
+	max.sec = max.frac = INT_MAX;
+	min.sec = min.frac = INT_MAX;
 	limit.sec = 0;
 	limit.frac = (uint64_t)1 << (64 - 2);
 	bintime_add(&limit, &now);		
-	for (;;) {
+	/* 
+	 * Look for the first bucket in the future that contains some event,
+	 * up to some point,  so that we can look for aggregation. 
+	 */ 
+	for (;;) { 
 		sc = &cc->cc_callwheel[last];
 		TAILQ_FOREACH(tmp, sc, c_links.tqe) {
-			if (bintime_cmp(&tmp->c_time, &limit, <=)) {
-				if (next.sec == -1 ||
-				    bintime_cmp(&tmp->c_time, &next, <)) {
-					next = tmp->c_time;
-					cpu = tmp->c_cpu;
-				}
+			tmp_max = tmp_min = tmp->c_time; 
+			bintime_add(&tmp_max, &tmp->c_precision);
+			bintime_sub(&tmp_min, &tmp->c_precision);
+			/*
+			 * This is the fist event we're going to process or 
+			 * event maximal time is less than present minimal. 
+			 * In both cases, take it.
+			 */
+			 if (bintime_cmp(&tmp_max, &min, <)) {
+				max = tmp_max;
+				min = tmp_min;
+				continue;	
 			}
-		}
-		if ((last == future) || (next.sec != -1))
+			/*
+			 * Event minimal time is bigger than present maximal  
+		 	 * time, so it cannot be aggregated.
+			 */
+			if (bintime_cmp(&tmp_min, &max, >))
+				continue;
+			/*
+			 * If neither of the two previous happened, just take 
+			 * the intersection of events.
+			 */	
+			min = (bintime_cmp(&tmp_min, &min, >)) ? tmp_min : min;
+			max = (bintime_cmp(&tmp_max, &max, >)) ? tmp_max : max;
+		}		
+		if (last == future || 
+		    (max.sec != INT_MAX && min.sec != INT_MAX))
 			break;
-		last = ((last + 1) & callwheelmask);
-	}	
-	if (next.sec == -1) { 
+		last = (last + 1) & callwheelmask;
+	}
+	if (max.sec == INT_MAX && min.sec == INT_MAX) { 
 		next.sec = 0;	
 		next.frac = (uint64_t)1 << (64 - 2);	
 		bintime_add(&next, &now);
 	}
+	/*
+	 * Now that we found something to aggregate, schedule an interrupt in 
+	 * the middle of the previously calculated range.
+	 */
+	else {
+		bintime_add(&max, &min);
+		next = max;
+		next.frac >>= 1;
+		if (next.sec & 1)	
+			next.frac |= ((uint64_t)1 << 63);
+		next.sec >>= 1;
+	}
 	cc->cc_firsttick = next;
 	if (callout_new_inserted != NULL) 
 		(*callout_new_inserted)(cpu, next);
@@ -478,6 +513,7 @@ callout_cc_add(struct callout *c, struct
     struct bintime to_bintime, void (*func)(void *), void *arg, int cpu, 
     int flags)
 {
+	struct timeval tv;
 	int bucket;	
 	
 	CC_LOCK_ASSERT(cc);
@@ -486,11 +522,28 @@ callout_cc_add(struct callout *c, struct
 	}
 	c->c_arg = arg;
 	c->c_flags |= (CALLOUT_ACTIVE | CALLOUT_PENDING);
-	if (flags & C_DIRECT)
+	if (flags & C_DIRECT_EXEC)
 		c->c_flags |= CALLOUT_DIRECT;
 	c->c_flags &= ~CALLOUT_PROCESSED;
 	c->c_func = func;
 	c->c_time = to_bintime; 
+	tv.tv_sec = 0;
+	if (flags & C_10US) { 
+		tv.tv_usec = 10;
+		timeval2bintime(&tv, &c->c_precision);
+	}
+	else if (flags & C_100US) {
+		tv.tv_usec = 100;
+		timeval2bintime(&tv, &c->c_precision);
+	} 
+	else if (flags & C_1MS) {
+		tv.tv_usec = 1000;
+		timeval2bintime(&tv, &c->c_precision);
+	} 
+	else { 
+		c->c_precision.sec = 0;
+		c->c_precision.frac = 0;
+	}
 	bucket = get_bucket(&c->c_time);	
 	TAILQ_INSERT_TAIL(&cc->cc_callwheel[bucket & callwheelmask], 
 	    c, c_links.tqe);

Modified: projects/calloutng/sys/sys/_callout.h
==============================================================================
--- projects/calloutng/sys/sys/_callout.h	Sat Jun 23 19:15:12 2012	(r237501)
+++ projects/calloutng/sys/sys/_callout.h	Sat Jun 23 19:39:03 2012	(r237502)
@@ -53,6 +53,7 @@ struct callout {
 	} c_links;
 	TAILQ_ENTRY(callout) c_staiter;
 	struct bintime c_time;			/* ticks to the event */
+	struct bintime c_precision;		/* delta allowed wrt opt */		
 	void	*c_arg;				/* function argument */
 	void	(*c_func)(void *);		/* function to call */
 	struct lock_object *c_lock;		/* lock to handle */

Modified: projects/calloutng/sys/sys/callout.h
==============================================================================
--- projects/calloutng/sys/sys/callout.h	Sat Jun 23 19:15:12 2012	(r237501)
+++ projects/calloutng/sys/sys/callout.h	Sat Jun 23 19:39:03 2012	(r237502)
@@ -50,12 +50,15 @@
 #define	CALLOUT_PROCESSED	0x0080 /* callout in wheel or processing list? */
 #define	CALLOUT_DIRECT 		0x1000 /* allow exec from hw int context */
 
+#define	C_DIRECT_EXEC		0x0001 /* direct execution of callout */
+#define	C_10US			0x0002 /* precision field */
+#define	C_100US			0x0004 /* precision field */	
+#define	C_1MS			0x0008 /* precision field */	
+
 struct callout_handle {
 	struct callout *callout;
 };
 
-#define	C_DIRECT		0x0001 /* direct execution of callout */
-
 #ifdef _KERNEL
 extern int ncallout;
 


More information about the svn-src-projects mailing list