[svn-commits] sruffell: linux/trunk r9891 - /linux/trunk/drivers/dahdi/dahdi-base.c

SVN commits to the Digium repositories svn-commits at lists.digium.com
Mon Apr 4 11:26:10 CDT 2011


Author: sruffell
Date: Mon Apr  4 11:26:05 2011
New Revision: 9891

URL: http://svnview.digium.com/svn/dahdi?view=rev&rev=9891
Log:
dahdi: Group dahdi timers into "rates" for improved CPU utilization.

Most of the timers added to the kernel will be configured to fire at the
same rate. This allows one "wake_up" from interrupt context to wake up
all the waiters.  On one test system, I saw about a ~10% improvement in
CPU utilization when 10,000 timers were opened.

Signed-off-by: Shaun Ruffell <sruffell at digium.com>
Acked-by: Michael Spiceland <mspiceland at digium.com>
Acked-by: Kinsey Moore <kmoore at digium.com>

Modified:
    linux/trunk/drivers/dahdi/dahdi-base.c

Modified: linux/trunk/drivers/dahdi/dahdi-base.c
URL: http://svnview.digium.com/svn/dahdi/linux/trunk/drivers/dahdi/dahdi-base.c?view=diff&rev=9891&r1=9890&r2=9891
==============================================================================
--- linux/trunk/drivers/dahdi/dahdi-base.c (original)
+++ linux/trunk/drivers/dahdi/dahdi-base.c Mon Apr  4 11:26:05 2011
@@ -289,18 +289,44 @@
 
 #endif
 
+/**
+ * struct dahdi_timer_rate - A collection of timers of a given rate.
+ * @rate_samples: Samples until these timers fire.
+ * @cur_samples:  Samples processed since last firing.
+ * @sel:	  Wait queue for timer waiters.
+ * @timers:	  List of individual timers at this rate.
+ * @node:	  For storing on dahdi_timer_rates list.
+ *
+ */
+struct dahdi_timer_rate {
+	int rate_samples;
+	int cur_samples;
+	wait_queue_head_t sel;
+	struct list_head timers;
+	struct list_head node;
+};
+
+/**
+ * struct dahdi_timer - A dahdi timer opened from user space.
+ * @ping:	1 if we should operate in "continuous mode".
+ * @tripped:	Count of times we've tripped.
+ * @rate:	The rate this timer fires at.
+ * @node:	To store on rate->timers list.
+ *
+ * When a timer is in "continuous" mode, it should always return immediately
+ * from a poll call from user space.
+ *
+ */
 struct dahdi_timer {
-	int ms;			/* Countdown */
-	int pos;		/* Position */
-	int ping;		/* Whether we've been ping'd */
-	int tripped;	/* Whether we're tripped */
-	struct list_head list;
-	wait_queue_head_t sel;
+	atomic_t ping;
+	atomic_t tripped;
+	struct dahdi_timer_rate *rate;
+	struct list_head node;
 };
 
-static LIST_HEAD(dahdi_timers);
-
+static LIST_HEAD(dahdi_timer_rates);
 static DEFINE_SPINLOCK(dahdi_timer_lock);
+static DEFINE_MUTEX(dahdi_timer_mutex);
 
 #define DEFAULT_TONE_ZONE (-1)
 
@@ -2730,48 +2756,47 @@
 static int dahdi_timing_open(struct file *file)
 {
 	struct dahdi_timer *t;
+
+	t = kzalloc(sizeof(*t), GFP_KERNEL);
+	if (!t)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&t->node);
+	file->private_data = t;
+
+	return 0;
+}
+
+/**
+ * _dahdi_remove_timer - Removes the timer from the rate it's associated with.
+ *
+ * Must be called with dahdi_timer_mutex held.
+ */
+static void _dahdi_remove_timer(struct dahdi_timer *timer)
+{
 	unsigned long flags;
-
-	if (!(t = kzalloc(sizeof(*t), GFP_KERNEL)))
-		return -ENOMEM;
-
-	init_waitqueue_head(&t->sel);
-	INIT_LIST_HEAD(&t->list);
-	file->private_data = t;
+	struct dahdi_timer_rate *const rate = timer->rate;
 
 	spin_lock_irqsave(&dahdi_timer_lock, flags);
-	list_add(&t->list, &dahdi_timers);
+	list_del(&timer->node);
+	if (rate && list_empty(&rate->timers)) {
+		list_del(&rate->node);
+		kfree(rate);
+	}
 	spin_unlock_irqrestore(&dahdi_timer_lock, flags);
-
-	return 0;
+	timer->rate = NULL;
 }
 
 static int dahdi_timer_release(struct file *file)
 {
-	struct dahdi_timer *t, *cur, *next;
-	unsigned long flags;
-
-	if (!(t = file->private_data))
+	struct dahdi_timer *timer = file->private_data;
+	if (!timer)
 		return 0;
 
-	spin_lock_irqsave(&dahdi_timer_lock, flags);
-
-	list_for_each_entry_safe(cur, next, &dahdi_timers, list) {
-		if (t == cur) {
-			list_del(&cur->list);
-			break;
-		}
-	}
-
-	spin_unlock_irqrestore(&dahdi_timer_lock, flags);
-
-	if (!cur) {
-		module_printk(KERN_NOTICE, "Timer: Not on list??\n");
-		return 0;
-	}
-
-	kfree(cur);
-
+	mutex_lock(&dahdi_timer_mutex);
+	_dahdi_remove_timer(timer);
+	mutex_unlock(&dahdi_timer_mutex);
+	file->private_data = NULL;
 	return 0;
 }
 
@@ -3658,48 +3683,80 @@
 	}
 }
 
-static int dahdi_timer_ioctl(struct file *file, unsigned int cmd, unsigned long data, struct dahdi_timer *timer)
+static int
+dahdi_timer_ioctl(struct file *file, unsigned int cmd,
+		  unsigned long data, struct dahdi_timer *timer)
 {
 	int j;
+	struct dahdi_timer_rate *rate, *c_rate;
 	unsigned long flags;
+
 	switch(cmd) {
 	case DAHDI_TIMERCONFIG:
 		get_user(j, (int __user *)data);
 		if (j < 0)
 			j = 0;
-		spin_lock_irqsave(&dahdi_timer_lock, flags);
-		timer->ms = timer->pos = j;
-		spin_unlock_irqrestore(&dahdi_timer_lock, flags);
+
+		mutex_lock(&dahdi_timer_mutex);
+		if (timer->rate)
+			_dahdi_remove_timer(timer);
+
+		rate = NULL;
+		list_for_each_entry(c_rate, &dahdi_timer_rates, node) {
+			if (c_rate->rate_samples == j) {
+				rate = c_rate;
+				break;
+			}
+		}
+		if (!rate) {
+			/* If we didn't find a rate, we need to create a new
+			 * one */
+			rate = kzalloc(sizeof(*rate), GFP_KERNEL);
+			if (!rate) {
+				mutex_unlock(&dahdi_timer_mutex);
+				return -ENOMEM;
+			}
+			init_waitqueue_head(&rate->sel);
+			rate->rate_samples = rate->cur_samples = j;
+			INIT_LIST_HEAD(&rate->timers);
+			timer->rate = rate;
+			list_add_tail(&timer->node, &rate->timers);
+			spin_lock_irqsave(&dahdi_timer_lock, flags);
+			list_add_tail(&rate->node, &dahdi_timer_rates);
+			spin_unlock_irqrestore(&dahdi_timer_lock, flags);
+		} else {
+			/* Otherwise, just add this new timer to the existing
+			 * rates. */
+			timer->rate = rate;
+			spin_lock_irqsave(&dahdi_timer_lock, flags);
+			list_add_tail(&timer->node, &rate->timers);
+			spin_unlock_irqrestore(&dahdi_timer_lock, flags);
+		}
+		mutex_unlock(&dahdi_timer_mutex);
 		break;
+
 	case DAHDI_TIMERACK:
 		get_user(j, (int __user *)data);
-		spin_lock_irqsave(&dahdi_timer_lock, flags);
-		if ((j < 1) || (j > timer->tripped))
-			j = timer->tripped;
-		timer->tripped -= j;
-		spin_unlock_irqrestore(&dahdi_timer_lock, flags);
+		if ((j < 1) || (j > atomic_read(&timer->tripped)))
+			j = atomic_read(&timer->tripped);
+		atomic_sub(j, &timer->tripped);
 		break;
 	case DAHDI_GETEVENT:  /* Get event on queue */
 		j = DAHDI_EVENT_NONE;
-		spin_lock_irqsave(&dahdi_timer_lock, flags);
 		  /* set up for no event */
-		if (timer->tripped)
+		if (atomic_read(&timer->tripped))
 			j = DAHDI_EVENT_TIMER_EXPIRED;
-		if (timer->ping)
+		if (atomic_read(&timer->ping))
 			j = DAHDI_EVENT_TIMER_PING;
-		spin_unlock_irqrestore(&dahdi_timer_lock, flags);
 		put_user(j, (int __user *)data);
 		break;
 	case DAHDI_TIMERPING:
-		spin_lock_irqsave(&dahdi_timer_lock, flags);
-		timer->ping = 1;
-		wake_up_interruptible(&timer->sel);
-		spin_unlock_irqrestore(&dahdi_timer_lock, flags);
+		atomic_set(&timer->ping, 1);
+		if (timer->rate)
+			wake_up_interruptible(&timer->rate->sel);
 		break;
 	case DAHDI_TIMERPONG:
-		spin_lock_irqsave(&dahdi_timer_lock, flags);
-		timer->ping = 0;
-		spin_unlock_irqrestore(&dahdi_timer_lock, flags);
+		atomic_set(&timer->ping, 0);
 		break;
 	default:
 		return -ENOTTY;
@@ -8493,21 +8550,26 @@
 
 static void process_timers(void)
 {
-	struct dahdi_timer *cur;
-
-	if (list_empty(&dahdi_timers))
+	struct dahdi_timer_rate *cur_rate;
+	struct dahdi_timer *cur_timer;
+
+	if (list_empty(&dahdi_timer_rates))
 		return;
 
 	spin_lock(&dahdi_timer_lock);
-	list_for_each_entry(cur, &dahdi_timers, list) {
-		if (cur->ms) {
-			cur->pos -= DAHDI_CHUNKSIZE;
-			if (cur->pos <= 0) {
-				cur->tripped++;
-				cur->pos = cur->ms;
-				wake_up_interruptible(&cur->sel);
-			}
-		}
+	list_for_each_entry(cur_rate, &dahdi_timer_rates, node) {
+		if (!cur_rate->rate_samples)
+			continue;
+
+		cur_rate->cur_samples -= DAHDI_CHUNKSIZE;
+		if (cur_rate->cur_samples > 0)
+			continue;
+
+		list_for_each_entry(cur_timer, &cur_rate->timers, node)
+			atomic_inc(&cur_timer->tripped);
+
+		cur_rate->cur_samples = cur_rate->rate_samples;
+		wake_up_interruptible(&cur_rate->sel);
 	}
 	spin_unlock(&dahdi_timer_lock);
 }
@@ -8515,17 +8577,16 @@
 static unsigned int dahdi_timer_poll(struct file *file, struct poll_table_struct *wait_table)
 {
 	struct dahdi_timer *timer = file->private_data;
-	unsigned long flags;
-	int ret = 0;
-	if (timer) {
-		poll_wait(file, &timer->sel, wait_table);
-		spin_lock_irqsave(&dahdi_timer_lock, flags);
-		if (timer->tripped || timer->ping)
-			ret |= POLLPRI;
-		spin_unlock_irqrestore(&dahdi_timer_lock, flags);
-	} else
-		ret = -EINVAL;
-	return ret;
+	struct dahdi_timer_rate *rate = timer->rate;
+
+	if (!rate || !timer)
+		return -EINVAL;
+
+	poll_wait(file, &rate->sel, wait_table);
+	if (atomic_read(&timer->tripped) || atomic_read(&timer->ping))
+		return POLLPRI;
+
+	return 0;
 }
 
 /* device poll routine */




More information about the svn-commits mailing list