diff options
Diffstat (limited to 'xen/common/ac_timer.c')
-rw-r--r-- | xen/common/ac_timer.c | 245 |
1 files changed, 139 insertions, 106 deletions
diff --git a/xen/common/ac_timer.c b/xen/common/ac_timer.c index 9bb5d7e301..73ac893e08 100644 --- a/xen/common/ac_timer.c +++ b/xen/common/ac_timer.c @@ -23,9 +23,9 @@ #include <xeno/errno.h> #include <xeno/sched.h> #include <xeno/lib.h> -#include <xeno/config.h> #include <xeno/smp.h> -#include <xeno/init.h> + +#include <xeno/perfc.h> #include <xeno/time.h> #include <xeno/ac_timer.h> @@ -34,20 +34,16 @@ #include <asm/system.h> #include <asm/desc.h> - -#undef AC_TIMER_TRACE -#undef AC_TIMER_STATS - #ifdef AC_TIMER_TRACE #define TRC(_x) _x #else #define TRC(_x) #endif -/* +/***************************************************************************** * We pull handlers off the timer list this far in future, * rather than reprogramming the time hardware. - */ + *****************************************************************************/ #define TIMER_SLOP (50*1000) /* ns */ /* A timer list per CPU */ @@ -55,47 +51,35 @@ typedef struct ac_timers_st { spinlock_t lock; struct list_head timers; - struct ac_timer *prev, *curr; + s_time_t max_diff; } __cacheline_aligned ac_timers_t; static ac_timers_t ac_timers[NR_CPUS]; -#ifdef AC_TIMER_STATS -#define BUCKETS 1000 -#define MAX_STATS -typedef struct act_stats_st -{ - u32 count; - u32 times[2*(BUCKETS)]; -} __cacheline_aligned act_stats_t; -static act_stats_t act_stats[NR_CPUS]; - -#endif - /* local prototypes */ static int detach_ac_timer(struct ac_timer *timer); -/*static void ac_timer_debug(unsigned long);*/ -/* + +/***************************************************************************** * add a timer. * return value: * 0: success * 1: failure, timer in the past or timeout value to small * -1: failure, timer uninitialised * fail - */ + *****************************************************************************/ int add_ac_timer(struct ac_timer *timer) { - int cpu = smp_processor_id(); - unsigned long flags; - s_time_t now; + int cpu = smp_processor_id(); + unsigned long flags; + s_time_t now; /* make sure timeout value is in the future */ - + now = NOW(); - if (timer->expires <= now) { + if (timer->expires <= now) { TRC(printk("ACT[%02d] add_ac_timer:now=0x%08X%08X>expire=0x%08X%08X\n", - cpu, (u32)(now>>32), (u32)now, - (u32)(timer->expires>>32), (u32)timer->expires)); + cpu, (u32)(now>>32), (u32)now, + (u32)(timer->expires>>32), (u32)timer->expires)); return 1; } spin_lock_irqsave(&ac_timers[cpu].lock, flags); @@ -104,79 +88,89 @@ int add_ac_timer(struct ac_timer *timer) * reprogramm the timer */ if (list_empty(&ac_timers[cpu].timers)) { - /* Reprogramm and add to head of list */ if (!reprogram_ac_timer(timer->expires)) { + printk("ACT[%02d] add at head failed\n", cpu); spin_unlock_irqrestore(&ac_timers[cpu].lock, flags); return 1; /* failed */ } list_add(&timer->timer_list, &ac_timers[cpu].timers); } else { struct list_head *pos; - struct ac_timer *t; + struct ac_timer *t; - list_for_each(pos, &ac_timers[cpu].timers) { - t = list_entry(pos, struct ac_timer, timer_list); - if (t->expires > timer->expires) + list_for_each(pos, &ac_timers[cpu].timers) { + t = list_entry(pos, struct ac_timer, timer_list); + if (t->expires > timer->expires) break; - } - list_add (&(timer->timer_list), pos->prev); + } + list_add (&(timer->timer_list), pos->prev); - if (timer->timer_list.prev == &ac_timers[cpu].timers) { - /* added at head */ + if (timer->timer_list.prev == &ac_timers[cpu].timers) { + /* added at head */ if (!reprogram_ac_timer(timer->expires)) { - detach_ac_timer(timer); + printk("ACT[%02d] add at head failed\n", cpu); + detach_ac_timer(timer); spin_unlock_irqrestore(&ac_timers[cpu].lock, flags); return 1; /* failed */ } - } + } } spin_unlock_irqrestore(&ac_timers[cpu].lock, flags); return 0; } -/* - * remove a timer +/***************************************************************************** + * detach a timer (no locking) * return values: * 0: success * -1: bogus timer - */ + *****************************************************************************/ static int detach_ac_timer(struct ac_timer *timer) { - TRC(int cpu = smp_processor_id()); TRC(printk("ACT [%02d] detach(): \n", cpu)); list_del(&timer->timer_list); timer->timer_list.next = NULL; return 0; } -/* +/***************************************************************************** * remove a timer * return values: * 0: success * -1: bogus timer - */ + *****************************************************************************/ int rem_ac_timer(struct ac_timer *timer) { - int cpu = smp_processor_id(); + int cpu = smp_processor_id(); int res = 0; unsigned long flags; TRC(printk("ACT [%02d] remove(): timo=%lld \n", cpu, timer->expires)); - spin_lock_irqsave(&ac_timers[cpu].lock, flags); - if (!timer->timer_list.next == NULL) - res = detach_ac_timer(timer); + if (timer->timer_list.next) { + res = detach_ac_timer(timer); + + if (timer->timer_list.prev == &ac_timers[cpu].timers) { + /* just removed the head */ + if (list_empty(&ac_timers[cpu].timers)) { + reprogram_ac_timer((s_time_t) 0); + } + /* XXX should actaully reprogramm APIC to new head */ + } + } else + res = -1; + spin_unlock_irqrestore(&ac_timers[cpu].lock, flags); return res; } -/* +/***************************************************************************** * modify a timer, i.e., set a new timeout value * return value: * 0: sucess * -1: error - */ + *****************************************************************************/ int mod_ac_timer(struct ac_timer *timer, s_time_t new_time) { if (rem_ac_timer(timer) != 0) @@ -187,69 +181,59 @@ int mod_ac_timer(struct ac_timer *timer, s_time_t new_time) return 0; } -/* +/***************************************************************************** * do_ac_timer * deal with timeouts and run the handlers - */ + *****************************************************************************/ void do_ac_timer(void) { - int cpu = smp_processor_id(); - unsigned long flags; - struct ac_timer *t; + int cpu = smp_processor_id(); + unsigned long flags; + struct ac_timer *t; + s_time_t diff, now = NOW(); + long max; spin_lock_irqsave(&ac_timers[cpu].lock, flags); do_timer_again: TRC(printk("ACT [%02d] do(): now=%lld\n", cpu, NOW())); - - /* Sanity: is the timer list empty? */ - if ( list_empty(&ac_timers[cpu].timers) ) - printk("ACT[%02d] do_ac_timer(): timer irq without timer\n", cpu); - -#ifdef AC_TIMER_STATS - { - s32 diff; - u32 i; - diff = ((s32)(NOW() - t->expires)) / 1000; /* delta in us */ - if (diff < -BUCKETS) - diff = -BUCKETS; - else if (diff > BUCKETS) - diff = BUCKETS; - act_stats[cpu].times[diff+BUCKETS]++; - act_stats[cpu].count++; - - if (act_stats[cpu].count >= 5000) { - printk("ACT Stats\n"); - for (i=0; i < 2*BUCKETS; i++) { - if (act_stats[cpu].times[i] != 0) - printk("ACT [%02d]: %3dus: %5d\n", - cpu,i-BUCKETS, act_stats[cpu].times[i]); - act_stats[cpu].times[i]=0; - } - act_stats[cpu].count = 0; - printk("\n"); - } + + /* Sanity: is the timer list empty? */ + if ( list_empty(&ac_timers[cpu].timers) ) { + /* + * XXX RN: This shouldn't happen, but does! Two possibilities: + * - Race condition between removing and reseting APIC + * - setting an APIC timeout value of 0 causes an immediate + * timer interrupt to fire. + * None of these should be critical! + */ + spin_unlock_irqrestore(&ac_timers[cpu].lock, flags); + return; } -#endif /* Handle all timeouts in the near future. */ while ( !list_empty(&ac_timers[cpu].timers) ) { - t = list_entry(ac_timers[cpu].timers.next, - struct ac_timer, timer_list); + t = list_entry(ac_timers[cpu].timers.next,struct ac_timer, timer_list); if ( t->expires > (NOW() + TIMER_SLOP) ) break; + + /* do some stats */ + diff = (now - t->expires); + if (diff > 0x7fffffff) diff = 0x7fffffff; /* THIS IS BAD! */ + max = perfc_valuea(ac_timer_max, cpu); + if (diff > max) perfc_seta(ac_timer_max, cpu, diff); + detach_ac_timer(t); spin_unlock_irqrestore(&ac_timers[cpu].lock, flags); if ( t->function != NULL ) t->function(t->data); spin_lock_irqsave(&ac_timers[cpu].lock, flags); } - + /* If list not empty then reprogram timer to new head of list */ if ( !list_empty(&ac_timers[cpu].timers) ) { - t = list_entry(ac_timers[cpu].timers.next, - struct ac_timer, timer_list); + t = list_entry(ac_timers[cpu].timers.next,struct ac_timer, timer_list); if ( t->expires > 0 ) { TRC(printk("ACT [%02d] do(): reprog timo=%lld\n",cpu,t->expires)); @@ -259,21 +243,23 @@ void do_ac_timer(void) goto do_timer_again; } } + } else { + reprogram_ac_timer((s_time_t) 0); } spin_unlock_irqrestore(&ac_timers[cpu].lock, flags); TRC(printk("ACT [%02d] do(): end\n", cpu)); } -/* +/***************************************************************************** * debug dump_queue * arguments: queue head, name of queue - */ + *****************************************************************************/ static void dump_tqueue(struct list_head *queue, char *name) { struct list_head *list; int loop = 0; - struct ac_timer *t; + struct ac_timer *t; printk ("QUEUE %s %lx n: %lx, p: %lx\n", name, (unsigned long)queue, (unsigned long) queue->next, (unsigned long) queue->prev); @@ -288,19 +274,21 @@ static void dump_tqueue(struct list_head *queue, char *name) return; } - -static void dump_timerq(u_char key, void *dev_id, struct pt_regs *regs) +void dump_timerq(u_char key, void *dev_id, struct pt_regs *regs) { u_long flags; s_time_t now = NOW(); + int i; - printk("Dumping ac_timer queues for cpu 0: NOW=0x%08X%08X\n", + printk("Dumping ac_timer queues: NOW=0x%08X%08X\n", (u32)(now>>32), (u32)now); - - spin_lock_irqsave(&ac_timers[0].lock, flags); - dump_tqueue(&ac_timers[0].timers, "ac_time"); - spin_unlock_irqrestore(&ac_timers[0].lock, flags); - printk("\n"); + for (i = 0; i < smp_num_cpus; i++) { + printk("CPU[%02d] ", i); + spin_lock_irqsave(&ac_timers[i].lock, flags); + dump_tqueue(&ac_timers[i].timers, "ac_time"); + spin_unlock_irqrestore(&ac_timers[i].lock, flags); + printk("\n"); + } return; } @@ -316,6 +304,51 @@ void __init ac_timer_init(void) INIT_LIST_HEAD(&ac_timers[i].timers); spin_lock_init(&ac_timers[i].lock); } - - add_key_handler('a', dump_timerq, "dump ac_timer queues"); } + +/***************************************************************************** + * GRAVEYARD + *****************************************************************************/ + +#if 0 + +#ifdef AC_TIMER_STATS +#define BUCKETS 1000 +#define MAX_STATS +typedef struct act_stats_st +{ + u32 count; + u32 times[2*(BUCKETS)]; +} __cacheline_aligned act_stats_t; +static act_stats_t act_stats[NR_CPUS]; + +#endif + +#ifdef AC_TIMER_STATS + { + XXX this is at the wrong place + s32 diff; + u32 i; + diff = ((s32)(NOW() - t->expires)) / 1000; /* delta in us */ + if (diff < -BUCKETS) + diff = -BUCKETS; + else if (diff > BUCKETS) + diff = BUCKETS; + act_stats[cpu].times[diff+BUCKETS]++; + act_stats[cpu].count++; + + if (act_stats[cpu].count >= 5000) { + printk("ACT Stats\n"); + for (i=0; i < 2*BUCKETS; i++) { + if (act_stats[cpu].times[i] != 0) + printk("ACT [%02d]: %3dus: %5d\n", + cpu,i-BUCKETS, act_stats[cpu].times[i]); + act_stats[cpu].times[i]=0; + } + act_stats[cpu].count = 0; + printk("\n"); + } + } +#endif + +#endif /* 0 */ |