aboutsummaryrefslogtreecommitdiffstats
path: root/xen/common/ac_timer.c
diff options
context:
space:
mode:
Diffstat (limited to 'xen/common/ac_timer.c')
-rw-r--r--xen/common/ac_timer.c245
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 */