[dahdi-commits] sruffell: linux/trunk r9891 - /linux/trunk/drivers/dahdi/dahdi-base.c
SVN commits to the DAHDI project
dahdi-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 dahdi-commits
mailing list