bitkeeper revision 1.88 (3e564e1cTiVK2rPAdHl9ccMD8nraEw)

ac_timer.c, apic.c:
  Fix timer code to schedule alarm handlers with some slop.
This commit is contained in:
kaf24@labyrinth.cl.cam.ac.uk committed 2003-02-21 16:04:44 +00:00
1 parent a75def4139
commit ef170179d0
2 files changed
+218 -257

No files matched your search

+65 -72
View File
@@ -218,9 +218,7 @@ int __init verify_local_APIC(void)
void __init sync_Arb_IDs(void)
{
/*
* Wait for idle.
*/
/* Wait for idle. */
apic_wait_icr_idle();
Dprintk("Synchronizing Arb IDs.\n");
@@ -508,11 +506,11 @@ void __init wait_8254_wraparound(void)
prev_count = curr_count;
curr_count = get_8254_timer_count();
delta = curr_count-prev_count;
/*
* This limit for delta seems arbitrary, but it isn't, it's
* slightly above the level of error a buggy Mercury/Neptune
* chipset timer can cause.
*/
/*
* This limit for delta seems arbitrary, but it isn't, it's slightly
* above the level of error a buggy Mercury/Neptune chipset timer can
* cause.
*/
} while (delta < 300);
}
@@ -581,7 +579,7 @@ int __init calibrate_APIC_clock(void)
* counter running for calibration. */
__setup_APIC_LVTT(1000000000);
/* The timer chip counts down to zero. Let's wait
/* The timer chip counts down to zero. Let's wait
* for a wraparound to start exact measurement:
* (the current tick might have been already half done) */
wait_8254_wraparound();
@@ -611,18 +609,18 @@ int __init calibrate_APIC_clock(void)
result/(1000000/HZ),
result%(1000000/HZ));
cpu_freq = (u64)(((t2-t1)/LOOPS)*HZ);
cpu_freq = (u64)(((t2-t1)/LOOPS)*HZ);
/* set up multipliers for accurate timer code */
bus_freq = result*HZ;
bus_cycle = (u32) (1000000000000LL/bus_freq); /* in pico seconds */
bus_scale = (1000*262144)/bus_cycle;
/* set up multipliers for accurate timer code */
bus_freq = result*HZ;
bus_cycle = (u32) (1000000000000LL/bus_freq); /* in pico seconds */
bus_scale = (1000*262144)/bus_cycle;
/* print results */
printk("..... bus_freq = %u Hz\n", bus_freq);
printk("..... bus_cycle = %u ps\n", bus_cycle);
printk("..... bus_scale = %u \n", bus_scale);
/* reset APIC to zero timeout value */
/* print results */
printk("..... bus_freq = %u Hz\n", bus_freq);
printk("..... bus_cycle = %u ps\n", bus_cycle);
printk("..... bus_scale = %u \n", bus_scale);
/* reset APIC to zero timeout value */
__setup_APIC_LVTT(0);
return result;
}
@@ -636,7 +634,7 @@ void __init setup_APIC_clocks (void)
printk("Using local APIC timer interrupts.\n");
using_apic_timer = 1;
__cli();
/* calibrate CPU0 for CPU speed and BUS speed */
/* calibrate CPU0 for CPU speed and BUS speed */
bus_freq = calibrate_APIC_clock();
/* Now set up the timer for real. */
setup_APIC_timer((void *)bus_freq);
@@ -654,41 +652,38 @@ void __init setup_APIC_clocks (void)
*/
int reprogram_ac_timer(s_time_t timeout)
{
int cpu = smp_processor_id();
s_time_t now;
s_time_t expire;
u64 apic_tmict;
int cpu = smp_processor_id();
s_time_t now;
s_time_t expire;
u64 apic_tmict;
now = NOW();
expire = timeout - now; /* value from now */
now = NOW();
expire = timeout - now; /* value from now */
if (expire <= 0) {
printk("APICT[%02d] Timeout in the past 0x%08X%08X > 0x%08X%08X\n",
cpu, (u32)(now>>32), (u32)now, (u32)(timeout>>32),(u32)timeout);
return 0; /* timeout value in the past */
}
if (expire <= 0) {
TRC(printk("APICT[%02d] Timeout in the past 0x%08X%08X > 0x%08X%08X\n",
cpu, (u32)(now>>32), (u32)now,
(u32)(timeout>>32),(u32)timeout));
return 0; /* timeout value in the past */
}
/* conversion to bus units */
apic_tmict = (((u64)bus_scale) * expire)>>18;
/* conversion to bus units */
apic_tmict = (((u64)bus_scale) * expire)>>18;
if (apic_tmict >= 0xffffffff) {
printk("APICT[%02d] Timeout value too large\n", cpu);
apic_tmict = 0xffffffff;
}
if (apic_tmict == 0) {
printk("APICT[%02d] timeout value too small\n", cpu);
return 0;
}
if (apic_tmict >= 0xffffffff) {
/* This is bad! */
printk("APICT[%02d] Timeout value too large\n", cpu);
apic_tmict = 0xffffffff;
}
if (apic_tmict == 0) {
TRC(printk("APICT[%02d] timeout value too small\n", cpu));
return 0;
}
/* programm timer */
apic_write(APIC_TMICT, (unsigned long)apic_tmict);
/* programm timer */
apic_write(APIC_TMICT, (unsigned long)apic_tmict);
TRC(printk("APICT[%02d] reprog(): expire=%lld %u\n",
cpu, expire, apic_tmict));
return 1;
TRC(printk("APICT[%02d] reprog(): expire=%lld %u\n",
cpu, expire, apic_tmict));
return 1;
}
/*
@@ -702,29 +697,28 @@ int reprogram_ac_timer(s_time_t timeout)
static s_time_t last_cpu0_tirq = 0;
inline void smp_local_timer_interrupt(struct pt_regs * regs)
{
int cpu = smp_processor_id();
s_time_t diff, now;
int cpu = smp_processor_id();
s_time_t diff, now;
/* if CPU 0 do old timer stuff */
if (cpu == 0) {
if (cpu == 0)
{
now = NOW();
diff = now - last_cpu0_tirq;
now = NOW();
diff = now - last_cpu0_tirq;
if (diff <= 0) {
printk ("System Time went backwards: %lld\n", diff);
return;
}
if (diff <= 0) {
printk ("System Time went backwards: %lld\n", diff);
return;
}
while (diff >= MILLISECS(10)) {
do_timer(regs);
diff -= MILLISECS(10);
last_cpu0_tirq += MILLISECS(10);
}
}
/* call accurate timer function */
do_ac_timer();
while (diff >= MILLISECS(10)) {
do_timer(regs);
diff -= MILLISECS(10);
last_cpu0_tirq += MILLISECS(10);
}
}
/* call accurate timer function */
do_ac_timer();
}
/*
@@ -747,9 +741,8 @@ void smp_apic_timer_interrupt(struct pt_regs * regs)
apic_timer_irqs[cpu]++;
/*
* NOTE! We'd better ACK the irq immediately,
* because timer handling can be slow.
* XXX is this save?
* NOTE! We'd better ACK the irq immediately, because timer handling can
* be slow. XXX is this save?
*/
ack_APIC_irq();
@@ -825,7 +818,7 @@ int __init APIC_init_uniprocessor (void)
* Complain if the BIOS pretends there is one.
*/
if (!cpu_has_apic&&APIC_INTEGRATED(apic_version[boot_cpu_physical_apicid]))
{
{
printk("BIOS bug, local APIC #%d not detected!...\n",
boot_cpu_physical_apicid);
return -1;
+153 -185
View File
@@ -44,6 +44,12 @@
#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 */
typedef struct ac_timers_st
{
@@ -58,8 +64,8 @@ static ac_timers_t ac_timers[NR_CPUS];
#define MAX_STATS
typedef struct act_stats_st
{
u32 count;
u32 times[2*(BUCKETS)];
u32 count;
u32 times[2*(BUCKETS)];
} __cacheline_aligned act_stats_t;
static act_stats_t act_stats[NR_CPUS];
@@ -79,71 +85,68 @@ static int detach_ac_timer(struct ac_timer *timer);
*/
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;
/* sanity checks */
/* make sure timeout value is in the future */
now = NOW();
TRC(printk("ACT [%02d] add(): now=%lld timo=%lld\n",
cpu, now, timer->expires));
if (timer->expires <= now) {
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);
return 1;
}
spin_lock_irqsave(&ac_timers[cpu].lock, flags);
/*
/* make sure timeout value is in the future */
now = NOW();
TRC(printk("ACT [%02d] add(): now=%lld timo=%lld\n",
cpu, now, timer->expires));
if (timer->expires <= now) {
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);
return 1;
}
spin_lock_irqsave(&ac_timers[cpu].lock, flags);
/*
* Add timer to the list. If it gets added to the front we have to
* reprogramm the timer
*/
if (list_empty(&ac_timers[cpu].timers)) {
/* Reprogramm and add to head of list */
if (!reprogram_ac_timer(timer->expires)) {
/* failed */
printk("ACT [%02d] add(): add at head failed\n", cpu);
spin_unlock_irqrestore(&ac_timers[cpu].lock, flags);
return 1;
}
list_add(&timer->timer_list, &ac_timers[cpu].timers);
TRC(printk("ACT [%02d] add(0x%08X%08X): added at head\n", cpu,
(u32)(timer->expires>>32), (u32)timer->expires));
} else {
struct list_head *pos;
struct ac_timer *t;
for (pos = ac_timers[cpu].timers.next;
pos != &ac_timers[cpu].timers;
pos = pos->next) {
t = list_entry(pos, struct ac_timer, timer_list);
if (t->expires > timer->expires)
break;
}
if (list_empty(&ac_timers[cpu].timers)) {
/* Reprogramm and add to head of list */
if (!reprogram_ac_timer(timer->expires)) {
/* failed */
printk("ACT [%02d] add(): add at head failed\n", cpu);
spin_unlock_irqrestore(&ac_timers[cpu].lock, flags);
return 1;
}
list_add(&timer->timer_list, &ac_timers[cpu].timers);
TRC(printk("ACT [%02d] add(0x%08X%08X): added at head\n", cpu,
(u32)(timer->expires>>32), (u32)timer->expires));
} else {
struct list_head *pos;
struct ac_timer *t;
for (pos = ac_timers[cpu].timers.next;
pos != &ac_timers[cpu].timers;
pos = pos->next) {
t = list_entry(pos, struct ac_timer, timer_list);
if (t->expires > timer->expires)
break;
}
if (pos->prev == &ac_timers[cpu].timers) {
/* added to head, reprogramm timer */
if (!reprogram_ac_timer(timer->expires)) {
/* failed */
TRC(printk("ACT [%02d] add(): add at head failed\n", cpu));
spin_unlock_irqrestore(&ac_timers[cpu].lock, flags);
return 1;
}
list_add (&(timer->timer_list), pos->prev);
TRC(printk("ACT [%02d] add(0x%08X%08X): added at head\n", cpu,
(u32)(timer->expires>>32), (u32)timer->expires));
} else {
list_add (&(timer->timer_list), pos->prev);
TRC(printk("ACT [%02d] add(0x%08X%08X): add < exp=0x%08X%08X\n",
cpu,
(u32)(timer->expires>>32), (u32)timer->expires,
(u32)(t->expires>>32), (u32)t->expires));
}
}
spin_unlock_irqrestore(&ac_timers[cpu].lock, flags);
return 0;
if (pos->prev == &ac_timers[cpu].timers) {
/* added to head, reprogramm timer */
if (!reprogram_ac_timer(timer->expires)) {
/* failed */
TRC(printk("ACT [%02d] add(): add at head failed\n", cpu));
spin_unlock_irqrestore(&ac_timers[cpu].lock, flags);
return 1;
}
list_add (&(timer->timer_list), pos->prev);
TRC(printk("ACT [%02d] add(0x%08X%08X): added at head\n", cpu,
(u32)(timer->expires>>32), (u32)timer->expires));
} else {
list_add (&(timer->timer_list), pos->prev);
TRC(printk("ACT [%02d] add(0x%08X%08X): add < exp=0x%08X%08X\n",
cpu,
(u32)(timer->expires>>32), (u32)timer->expires,
(u32)(t->expires>>32), (u32)t->expires));
}
}
spin_unlock_irqrestore(&ac_timers[cpu].lock, flags);
return 0;
}
/*
@@ -154,11 +157,11 @@ int add_ac_timer(struct ac_timer *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;
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;
}
/*
@@ -169,18 +172,17 @@ static int detach_ac_timer(struct ac_timer *timer)
*/
int rem_ac_timer(struct ac_timer *timer)
{
int cpu = smp_processor_id();
int res;
unsigned long flags;
int cpu = smp_processor_id();
int res;
unsigned long flags;
TRC(printk("ACT [%02d] remove(): timo=%lld \n", cpu, timer->expires));
/* sanity checks */
TRC(printk("ACT [%02d] remove(): timo=%lld \n", cpu, timer->expires));
spin_lock_irqsave(&ac_timers[cpu].lock, flags);
res = detach_ac_timer(timer);
spin_unlock_irqrestore(&ac_timers[cpu].lock, flags);
spin_lock_irqsave(&ac_timers[cpu].lock, flags);
res = detach_ac_timer(timer);
spin_unlock_irqrestore(&ac_timers[cpu].lock, flags);
return res;
return res;
}
/*
@@ -191,12 +193,12 @@ int rem_ac_timer(struct ac_timer *timer)
*/
int mod_ac_timer(struct ac_timer *timer, s_time_t new_time)
{
if (rem_ac_timer(timer) != 0)
return -1;
timer->expires = new_time;
if (add_ac_timer(timer) != 0)
return -1;
return 0;
if (rem_ac_timer(timer) != 0)
return -1;
timer->expires = new_time;
if (add_ac_timer(timer) != 0)
return -1;
return 0;
}
/*
@@ -205,108 +207,76 @@ int mod_ac_timer(struct ac_timer *timer, s_time_t new_time)
*/
void do_ac_timer(void)
{
int cpu = smp_processor_id();
unsigned long flags;
s_time_t now;
struct ac_timer *t;
struct list_head *tmp;
int cpu = smp_processor_id();
unsigned long flags;
struct ac_timer *t;
spin_lock_irqsave(&ac_timers[cpu].lock, flags);
spin_lock_irqsave(&ac_timers[cpu].lock, flags);
do_timer_again:
now = NOW();
TRC(printk("ACT [%02d] do(): now=%lld\n", cpu, now));
TRC(printk("ACT [%02d] do(): now=%lld\n", cpu, NOW()));
/* Sanity checks */
/* empty time list */
if (list_empty(&ac_timers[cpu].timers)) {
printk("ACT[%02d] do_ac_timer(): timer irq without timer\n", cpu);
spin_unlock_irqrestore(&ac_timers[cpu].lock, flags);
return;
}
/* execute the head of timer queue */
t = list_entry(ac_timers[cpu].timers.next, struct ac_timer, timer_list);
detach_ac_timer(t);
spin_unlock_irqrestore(&ac_timers[cpu].lock, flags);
/* 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++;
{
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");
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");
}
}
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
if (t->expires > now) {
//printk("ACT [%02d] do(): irq too early (%lld ns)\n",
// cpu, now - t->expires );
}
if (t->function != NULL)
t->function(t->data);
/* check if there are other timer functions on the list */
now = NOW();
spin_lock_irqsave(&ac_timers[cpu].lock, flags);
if (!list_empty(&ac_timers[cpu].timers)) {
list_for_each(tmp, &ac_timers[cpu].timers) {
t = list_entry(tmp, struct ac_timer, timer_list);
TRC(printk("ACT [%02d] do(): now=%lld timo=%lld\n",
cpu, now, t->expires));
if (t->expires <= now) {
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);
now = NOW();
} else {
TRC(printk("ACT [%02d] do(): break1\n", cpu));
break;
}
}
}
/* 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);
if ( t->expires > (NOW() + TIMER_SLOP) ) break;
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 reprogramm 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);
if (t->expires > 0) {
TRC(printk("ACT [%02d] do(): reprog timo=%lld\n",cpu,t->expires));
if (!reprogram_ac_timer(t->expires)) {
TRC(printk("ACT [%02d] do(): again\n", cpu));
goto do_timer_again;
}
}
}
spin_unlock_irqrestore(&ac_timers[cpu].lock, flags);
TRC(printk("ACT [%02d] do(): end\n", cpu));
/* 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);
if ( t->expires > 0 )
{
TRC(printk("ACT [%02d] do(): reprog timo=%lld\n",cpu,t->expires));
if ( !reprogram_ac_timer(t->expires) )
{
TRC(printk("ACT [%02d] do(): again\n", cpu));
goto do_timer_again;
}
}
}
spin_unlock_irqrestore(&ac_timers[cpu].lock, flags);
TRC(printk("ACT [%02d] do(): end\n", cpu));
}
/*
@@ -317,16 +287,16 @@ 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);
list_for_each (list, queue) {
t = list_entry(list, struct ac_timer, timer_list);
t = list_entry(list, struct ac_timer, timer_list);
printk (" %s %d : %lx ex=0x%08X%08X %lu n: %lx, p: %lx\n",
name, loop++,
name, loop++,
(unsigned long)list,
(u32)(t->expires>>32), (u32)t->expires, t->data,
(u32)(t->expires>>32), (u32)t->expires, t->data,
(unsigned long)list->next, (unsigned long)list->prev);
}
return;
@@ -336,32 +306,30 @@ static void dump_tqueue(struct list_head *queue, char *name)
static void dump_timerq(u_char key, void *dev_id, struct pt_regs *regs)
{
u_long flags;
s_time_t now = NOW();
s_time_t now = NOW();
printk("Dumping ac_timer queues for cpu 0: NOW=0x%08X%08X\n",
(u32)(now>>32), (u32)now);
(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");
printk("\n");
return;
}
/*
* init
*/
void __init ac_timer_init(void)
{
int i;
printk ("ACT: Initialising Accurate timers\n");
printk ("ACT: Initialising Accurate timers\n");
for (i = 0; i < NR_CPUS; i++)
{
INIT_LIST_HEAD(&ac_timers[i].timers);
spin_lock_init(&ac_timers[i].lock);
INIT_LIST_HEAD(&ac_timers[i].timers);
spin_lock_init(&ac_timers[i].lock);
}
add_key_handler('a', dump_timerq, "dump ac_timer queues");
add_key_handler('a', dump_timerq, "dump ac_timer queues");
}
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.