Commit 73dea47f authored by Andi Kleen's avatar Andi Kleen Committed by Linus Torvalds

[PATCH] x86_64: Allow to run main time keeping from the local APIC interrupt

Another piece from the no-idle-tick patch.

This can be enabled with the "apicmaintimer" option.

This is mainly useful when the PIT/HPET interrupt is unreliable.
Note there are some systems that are known to stop the APIC
timer in C3. For those it will never work, but this case
should be automatically detected.

It also only works with PM timer right now. When HPET is used
the way the main timer handler computes the delay doesn't work.

It should be a bit more efficient because there is one less
regular interrupt to process on the boot processor.

Requires earlier bugfix from Venkatesh
Signed-off-by: default avatarAndi Kleen <ak@suse.de>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 76b461c2
...@@ -40,6 +40,13 @@ APICs ...@@ -40,6 +40,13 @@ APICs
no_timer_check Don't check the IO-APIC timer. This can work around no_timer_check Don't check the IO-APIC timer. This can work around
problems with incorrect timer initialization on some boards. problems with incorrect timer initialization on some boards.
apicmaintimer Run time keeping from the local APIC timer instead
of using the PIT/HPET interrupt for this. This is useful
when the PIT/HPET interrupts are unreliable.
noapicmaintimer Don't do time keeping using the APIC timer.
Useful when this option was auto selected, but doesn't work.
Early Console Early Console
syntax: earlyprintk=vga syntax: earlyprintk=vga
......
...@@ -35,8 +35,11 @@ ...@@ -35,8 +35,11 @@
#include <asm/mach_apic.h> #include <asm/mach_apic.h>
#include <asm/nmi.h> #include <asm/nmi.h>
#include <asm/idle.h> #include <asm/idle.h>
#include <asm/proto.h>
#include <asm/timex.h>
int apic_verbosity; int apic_verbosity;
int apic_runs_main_timer;
int disable_apic_timer __initdata; int disable_apic_timer __initdata;
...@@ -702,9 +705,17 @@ static void setup_APIC_timer(unsigned int clocks) ...@@ -702,9 +705,17 @@ static void setup_APIC_timer(unsigned int clocks)
c2 |= inb_p(0x40) << 8; c2 |= inb_p(0x40) << 8;
} while (c2 - c1 < 300); } while (c2 - c1 < 300);
} }
__setup_APIC_LVTT(clocks); __setup_APIC_LVTT(clocks);
/* Turn off PIT interrupt if we use APIC timer as main timer.
Only works with the PM timer right now
TBD fix it for HPET too. */
if (vxtime.mode == VXTIME_PMTMR &&
smp_processor_id() == boot_cpu_id &&
apic_runs_main_timer == 1 &&
!cpu_isset(boot_cpu_id, timer_interrupt_broadcast_ipi_mask)) {
stop_timer_interrupt();
apic_runs_main_timer++;
}
local_irq_restore(flags); local_irq_restore(flags);
} }
...@@ -872,6 +883,8 @@ void smp_local_timer_interrupt(struct pt_regs *regs) ...@@ -872,6 +883,8 @@ void smp_local_timer_interrupt(struct pt_regs *regs)
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
update_process_times(user_mode(regs)); update_process_times(user_mode(regs));
#endif #endif
if (apic_runs_main_timer > 1 && smp_processor_id() == boot_cpu_id)
main_timer_handler(regs);
/* /*
* We take the 'long' return path, and there every subsystem * We take the 'long' return path, and there every subsystem
* grabs the appropriate locks (kernel lock/ irq lock). * grabs the appropriate locks (kernel lock/ irq lock).
...@@ -1081,10 +1094,27 @@ static __init int setup_nolapic(char *str) ...@@ -1081,10 +1094,27 @@ static __init int setup_nolapic(char *str)
static __init int setup_noapictimer(char *str) static __init int setup_noapictimer(char *str)
{ {
if (str[0] != ' ' && str[0] != 0)
return -1;
disable_apic_timer = 1; disable_apic_timer = 1;
return 0; return 0;
} }
static __init int setup_apicmaintimer(char *str)
{
apic_runs_main_timer = 1;
nohpet = 1;
return 0;
}
__setup("apicmaintimer", setup_apicmaintimer);
static __init int setup_noapicmaintimer(char *str)
{
apic_runs_main_timer = -1;
return 0;
}
__setup("noapicmaintimer", setup_noapicmaintimer);
/* dummy parsing: see setup.c */ /* dummy parsing: see setup.c */
__setup("disableapic", setup_disableapic); __setup("disableapic", setup_disableapic);
......
...@@ -51,7 +51,7 @@ extern int using_apic_timer; ...@@ -51,7 +51,7 @@ extern int using_apic_timer;
DEFINE_SPINLOCK(rtc_lock); DEFINE_SPINLOCK(rtc_lock);
DEFINE_SPINLOCK(i8253_lock); DEFINE_SPINLOCK(i8253_lock);
static int nohpet __initdata = 0; int nohpet __initdata = 0;
static int notsc __initdata = 0; static int notsc __initdata = 0;
#undef HPET_HACK_ENABLE_DANGEROUS #undef HPET_HACK_ENABLE_DANGEROUS
...@@ -345,7 +345,7 @@ static noinline void handle_lost_ticks(int lost, struct pt_regs *regs) ...@@ -345,7 +345,7 @@ static noinline void handle_lost_ticks(int lost, struct pt_regs *regs)
#endif #endif
} }
static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) void main_timer_handler(struct pt_regs *regs)
{ {
static unsigned long rtc_update = 0; static unsigned long rtc_update = 0;
unsigned long tsc; unsigned long tsc;
...@@ -458,12 +458,17 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -458,12 +458,17 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
} }
write_sequnlock(&xtime_lock); write_sequnlock(&xtime_lock);
}
static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
if (apic_runs_main_timer > 1)
return IRQ_HANDLED;
main_timer_handler(regs);
#ifdef CONFIG_X86_LOCAL_APIC #ifdef CONFIG_X86_LOCAL_APIC
if (using_apic_timer) if (using_apic_timer)
smp_send_timer_broadcast_ipi(); smp_send_timer_broadcast_ipi();
#endif #endif
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -843,17 +848,43 @@ static int hpet_reenable(void) ...@@ -843,17 +848,43 @@ static int hpet_reenable(void)
return hpet_timer_stop_set_go(hpet_tick); return hpet_timer_stop_set_go(hpet_tick);
} }
void __init pit_init(void) #define PIT_MODE 0x43
#define PIT_CH0 0x40
static void __init __pit_init(int val, u8 mode)
{ {
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&i8253_lock, flags); spin_lock_irqsave(&i8253_lock, flags);
outb_p(0x34, 0x43); /* binary, mode 2, LSB/MSB, ch 0 */ outb_p(mode, PIT_MODE);
outb_p(LATCH & 0xff, 0x40); /* LSB */ outb_p(val & 0xff, PIT_CH0); /* LSB */
outb_p(LATCH >> 8, 0x40); /* MSB */ outb_p(val >> 8, PIT_CH0); /* MSB */
spin_unlock_irqrestore(&i8253_lock, flags); spin_unlock_irqrestore(&i8253_lock, flags);
} }
void __init pit_init(void)
{
__pit_init(LATCH, 0x34); /* binary, mode 2, LSB/MSB, ch 0 */
}
void __init pit_stop_interrupt(void)
{
__pit_init(0, 0x30); /* mode 0 */
}
void __init stop_timer_interrupt(void)
{
char *name;
if (vxtime.hpet_address) {
name = "HPET";
hpet_timer_stop_set_go(0);
} else {
name = "PIT";
pit_stop_interrupt();
}
printk(KERN_INFO "timer: %s interrupt stopped.\n", name);
}
int __init time_setup(char *str) int __init time_setup(char *str)
{ {
report_lost_ticks = 1; report_lost_ticks = 1;
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#define APIC_DEBUG 2 #define APIC_DEBUG 2
extern int apic_verbosity; extern int apic_verbosity;
extern int apic_runs_main_timer;
/* /*
* Define the default level of output to be very little * Define the default level of output to be very little
......
...@@ -49,6 +49,8 @@ extern u32 pmtmr_ioport; ...@@ -49,6 +49,8 @@ extern u32 pmtmr_ioport;
#endif #endif
extern unsigned long long monotonic_base; extern unsigned long long monotonic_base;
extern int sysctl_vsyscall; extern int sysctl_vsyscall;
extern int nohpet;
extern unsigned long vxtime_hz;
extern void do_softirq_thunk(void); extern void do_softirq_thunk(void);
...@@ -69,6 +71,9 @@ extern void free_bootmem_generic(unsigned long phys, unsigned len); ...@@ -69,6 +71,9 @@ extern void free_bootmem_generic(unsigned long phys, unsigned len);
extern void load_gs_index(unsigned gs); extern void load_gs_index(unsigned gs);
extern void stop_timer_interrupt(void);
extern void main_timer_handler(struct pt_regs *regs);
extern unsigned long end_pfn_map; extern unsigned long end_pfn_map;
extern void show_trace(unsigned long * rsp); extern void show_trace(unsigned long * rsp);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment