From d5bae26f91263d6bff745673f998bafde2a05833 Mon Sep 17 00:00:00 2001 From: Shaun Ruffell Date: Mon, 4 Apr 2011 16:26:05 +0000 Subject: 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 Acked-by: Michael Spiceland Acked-by: Kinsey Moore git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@9891 a0bf4364-ded3-4de4-8d8a-66a801d63aff --- drivers/dahdi/dahdi-base.c | 209 +++++++++++++++++++++++++++++---------------- 1 file changed, 135 insertions(+), 74 deletions(-) (limited to 'drivers/dahdi') diff --git a/drivers/dahdi/dahdi-base.c b/drivers/dahdi/dahdi-base.c index 2de2874..d9b8408 100644 --- a/drivers/dahdi/dahdi-base.c +++ b/drivers/dahdi/dahdi-base.c @@ -289,18 +289,44 @@ static inline void dahdi_kernel_fpu_end(void) #endif -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; +/** + * 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; }; -static LIST_HEAD(dahdi_timers); +/** + * 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 { + atomic_t ping; + atomic_t tripped; + struct dahdi_timer_rate *rate; + struct list_head node; +}; +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 initialize_channel(struct dahdi_chan *chan) static int dahdi_timing_open(struct file *file) { struct dahdi_timer *t; - unsigned long flags; - if (!(t = kzalloc(sizeof(*t), GFP_KERNEL))) + t = kzalloc(sizeof(*t), GFP_KERNEL); + if (!t) return -ENOMEM; - init_waitqueue_head(&t->sel); - INIT_LIST_HEAD(&t->list); + INIT_LIST_HEAD(&t->node); file->private_data = t; - spin_lock_irqsave(&dahdi_timer_lock, flags); - list_add(&t->list, &dahdi_timers); - spin_unlock_irqrestore(&dahdi_timer_lock, flags); - return 0; } -static int dahdi_timer_release(struct file *file) +/** + * _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) { - struct dahdi_timer *t, *cur, *next; unsigned long flags; - - if (!(t = file->private_data)) - return 0; + struct dahdi_timer_rate *const rate = timer->rate; 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; - } + list_del(&timer->node); + if (rate && list_empty(&rate->timers)) { + list_del(&rate->node); + kfree(rate); } - spin_unlock_irqrestore(&dahdi_timer_lock, flags); + timer->rate = NULL; +} - if (!cur) { - module_printk(KERN_NOTICE, "Timer: Not on list??\n"); +static int dahdi_timer_release(struct file *file) +{ + struct dahdi_timer *timer = file->private_data; + if (!timer) 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 @@ void dahdi_alarm_notify(struct dahdi_span *span) } } -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 @@ int dahdi_hdlc_getbuf(struct dahdi_chan *ss, unsigned char *bufptr, unsigned int static void process_timers(void) { - struct dahdi_timer *cur; + struct dahdi_timer_rate *cur_rate; + struct dahdi_timer *cur_timer; - if (list_empty(&dahdi_timers)) + 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 void process_timers(void) 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 */ -- cgit v1.2.3