Replace the existing broadcast implementation and use the clockevents based broadcasting mechanism Signed-of-by: Thomas Gleixner Signed-off-by: Andrew Morton --- arch/i386/Kconfig | 4 arch/i386/kernel/apic.c | 184 +++++++++++--------------------- arch/i386/kernel/smpboot.c | 5 arch/i386/kernel/time.c | 5 drivers/acpi/processor_idle.c | 20 +++ include/asm-i386/apic.h | 5 include/asm-i386/mpspec.h | 1 7 files changed, 91 insertions(+), 133 deletions(-) diff -puN arch/i386/Kconfig~i386-apic-timer-use-clockevents-broadcast arch/i386/Kconfig --- a/arch/i386/Kconfig~i386-apic-timer-use-clockevents-broadcast +++ a/arch/i386/Kconfig @@ -22,6 +22,10 @@ config GENERIC_CLOCKEVENTS bool default y +config GENERIC_CLOCKEVENTS_BROADCAST + bool + default y + config LOCKDEP_SUPPORT bool default y diff -puN arch/i386/kernel/apic.c~i386-apic-timer-use-clockevents-broadcast arch/i386/kernel/apic.c --- a/arch/i386/kernel/apic.c~i386-apic-timer-use-clockevents-broadcast +++ a/arch/i386/kernel/apic.c @@ -53,12 +53,6 @@ #endif /* - * cpu_mask that denotes the CPUs that needs timer interrupt coming in as - * IPIs in place of local APIC timers - */ -static cpumask_t timer_bcast_ipi; - -/* * Knob to control our willingness to enable the local APIC. * * -1=force-disable, +1=force-enable @@ -76,6 +70,7 @@ static void lapic_next_event(unsigned lo struct clock_event_device *evt); static void lapic_timer_setup(enum clock_event_mode mode, struct clock_event_device *evt); +static void lapic_timer_broadcast(cpumask_t *mask); static void apic_pm_activate(void); /* @@ -99,9 +94,6 @@ static struct clock_event_device lapic_c }; static DEFINE_PER_CPU(struct clock_event_device, lapic_events); -/* Using APIC to generate smp_local_timer_interrupt? */ -int using_apic_timer __read_mostly = 0; - /* Local APIC was disabled by the BIOS and enabled by the kernel */ static int enabled_via_apicbase; @@ -185,7 +177,6 @@ int lapic_get_maxlvt(void) static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen) { unsigned int lvtt_value, tmp_value; - int cpu = smp_processor_id(); lvtt_value = LOCAL_TIMER_VECTOR; if (!oneshot) @@ -225,12 +216,24 @@ static void lapic_next_event(unsigned lo static void lapic_timer_setup(enum clock_event_mode mode, struct clock_event_device *evt) { - int cpu = smp_processor_id(); unsigned long flags; + unsigned int v; local_irq_save(flags); - __setup_APIC_LVTT(calibration_result, mode != CLOCK_EVT_PERIODIC, - cpu_isset(cpu, timer_bcast_ipi)); + + switch (mode) { + case CLOCK_EVT_PERIODIC: + case CLOCK_EVT_ONESHOT: + __setup_APIC_LVTT(calibration_result, + mode != CLOCK_EVT_PERIODIC, 1); + break; + case CLOCK_EVT_SHUTDOWN: + v = apic_read(APIC_LVTT); + v |= (APIC_LVT_MASKED | LOCAL_TIMER_VECTOR); + apic_write_around(APIC_LVTT, v); + break; + } + local_irq_restore(flags); } @@ -324,7 +327,9 @@ void __init setup_boot_APIC_clock(void) apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n" "calibrating APIC timer ...\n"); - using_apic_timer = 1; + + /* Register broadcast function */ + clockevents_register_broadcast(lapic_timer_broadcast); local_irq_disable(); @@ -484,96 +489,42 @@ void __devinit setup_secondary_APIC_cloc setup_APIC_timer(); } -void disable_APIC_timer(void) -{ - if (using_apic_timer) { - unsigned long v; - - v = apic_read(APIC_LVTT); - /* - * When an illegal vector value (0-15) is written to an LVT - * entry and delivery mode is Fixed, the APIC may signal an - * illegal vector error, with out regard to whether the mask - * bit is set or whether an interrupt is actually seen on - * input. - * - * Boot sequence might call this function when the LVTT has - * '0' vector value. So make sure vector field is set to - * valid value. - */ - v |= (APIC_LVT_MASKED | LOCAL_TIMER_VECTOR); - apic_write_around(APIC_LVTT, v); - } -} - -void enable_APIC_timer(void) -{ - int cpu = smp_processor_id(); - - if (using_apic_timer && !cpu_isset(cpu, timer_bcast_ipi)) { - unsigned long v; - - v = apic_read(APIC_LVTT); - apic_write_around(APIC_LVTT, v & ~APIC_LVT_MASKED); - } -} - void switch_APIC_timer_to_ipi(void *cpumask) { + struct clock_event_device *levt = &__get_cpu_var(lapic_events); cpumask_t mask = *(cpumask_t *)cpumask; int cpu = smp_processor_id(); - if (cpu_isset(cpu, mask) && - !cpu_isset(cpu, timer_bcast_ipi)) { - disable_APIC_timer(); - cpu_set(cpu, timer_bcast_ipi); -#ifdef CONFIG_HIGH_RES_TIMERS - /* - * C3 stops the local apic timer. We can not make high - * resolution timers and dynamic ticks work with one global - * timer. Disable the NEXTEVT capability, so high resolution / - * dyntick mode gets disabled too. - * - * There is a solution for this problem, but this is beyond the - * scope of this initial patchset: - * - * When the local apic timer is unusable in C3, then we can - * utilize the PIT to provide a global wakeup, which can be - * directed to the CPU which has the earliest wakeup - * point. Once the CPU is up again, the local apic is resumed - * and can be used for the per cpu clock events again. It's not - * hard to provide the infrastructure, but I need more insight - * into the ACPI code to get it right. - * - * Disable the highres/dyntick feature in this case for now, - * until somebody beats the ACPI clue into me. :) - * - * tglx - */ - printk(KERN_INFO "Disabling NO_HZ and high resolution timers " - "due to timer broadcasting (C3 stops local apic)\n"); - for_each_possible_cpu(cpu) - per_cpu(lapic_events, cpu).capabilities &= - ~CLOCK_CAP_NEXTEVT; -#endif - } + if (cpu_isset(cpu, mask)) + clockevents_set_global_broadcast(levt, 1); } EXPORT_SYMBOL(switch_APIC_timer_to_ipi); void switch_ipi_to_APIC_timer(void *cpumask) { + struct clock_event_device *levt = &__get_cpu_var(lapic_events); cpumask_t mask = *(cpumask_t *)cpumask; int cpu = smp_processor_id(); - if (cpu_isset(cpu, mask) && - cpu_isset(cpu, timer_bcast_ipi)) { - cpu_clear(cpu, timer_bcast_ipi); - enable_APIC_timer(); - } + if (cpu_isset(cpu, mask)) + clockevents_set_global_broadcast(levt, 0); } EXPORT_SYMBOL(switch_ipi_to_APIC_timer); /* + * The guts of the apic timer interrupt + */ +fastcall void local_apic_timer_interrupt(struct pt_regs *regs) +{ + int cpu = smp_processor_id(); + struct clock_event_device *evt = &per_cpu(lapic_events, cpu); + + per_cpu(irq_stat, cpu).apic_timer_irqs++; + + evt->event_handler(regs); +} + +/* * Local APIC timer interrupt. This is the most natural way for doing * local interrupts, but local timer interrupts can be emulated by * broadcast interrupts too. [in case the hw doesn't support APIC timers] @@ -585,13 +536,6 @@ EXPORT_SYMBOL(switch_ipi_to_APIC_timer); fastcall void smp_apic_timer_interrupt(struct pt_regs *regs) { struct pt_regs *old_regs = set_irq_regs(regs); - int cpu = smp_processor_id(); - struct clock_event_device *evt = &per_cpu(lapic_events, cpu); - - /* - * the NMI deadlock-detector uses this. - */ - per_cpu(irq_stat, cpu).apic_timer_irqs++; /* * NOTE! We'd better ACK the irq immediately, @@ -604,42 +548,39 @@ fastcall void smp_apic_timer_interrupt(s * interrupt lock, which is the WrongThing (tm) to do. */ irq_enter(); - evt->event_handler(regs); + local_apic_timer_interrupt(regs); irq_exit(); set_irq_regs(old_regs); } -#ifndef CONFIG_SMP -static void up_apic_timer_interrupt_call(void) +/* + * Local APIC timer broadcast function + */ +static void lapic_timer_broadcast(cpumask_t *cpumask) { int cpu = smp_processor_id(); - struct clock_event_device *evt = &per_cpu(lapic_events, cpu); - - /* - * the NMI deadlock-detector uses this. - */ - per_cpu(irq_stat, cpu).apic_timer_irqs++; - - evt->event_handler(get_irq_regs()); -} -#endif - -void smp_send_timer_broadcast_ipi(void) -{ cpumask_t mask; - cpus_and(mask, cpu_online_map, timer_bcast_ipi); - if (!cpus_empty(mask)) { + cpus_and(mask, cpu_online_map, *cpumask); + if (cpu_isset(cpu, mask)) { + cpu_clear(cpu, mask); + local_apic_timer_interrupt(get_irq_regs()); + } #ifdef CONFIG_SMP + if (!cpus_empty(mask)) send_IPI_mask(mask, LOCAL_TIMER_VECTOR); -#else - /* - * We can directly call the apic timer interrupt handler - * in UP case. Minus all irq related functions - */ - up_apic_timer_interrupt_call(); #endif - } +} + +/* + * Local APIC set next event broadcast + */ +void lapic_timer_idle_broadcast(int broadcast) +{ + int cpu = smp_processor_id(); + struct clock_event_device *evt = &per_cpu(lapic_events, cpu); + + clockevents_set_broadcast(evt, broadcast); } int setup_profiling_timer(unsigned int multiplier) @@ -1052,6 +993,11 @@ void __devinit setup_local_APIC(void) printk(KERN_INFO "No ESR for 82489DX.\n"); } + /* Disabled the local apic timer */ + value = apic_read(APIC_LVTT); + value |= (APIC_LVT_MASKED | LOCAL_TIMER_VECTOR); + apic_write_around(APIC_LVTT, value); + setup_apic_nmi_watchdog(NULL); apic_pm_activate(); } diff -puN arch/i386/kernel/smpboot.c~i386-apic-timer-use-clockevents-broadcast arch/i386/kernel/smpboot.c --- a/arch/i386/kernel/smpboot.c~i386-apic-timer-use-clockevents-broadcast +++ a/arch/i386/kernel/smpboot.c @@ -438,9 +438,7 @@ static void __devinit smp_callin(void) /* * Save our processor parameters */ - smp_store_cpu_info(cpuid); - - disable_APIC_timer(); + smp_store_cpu_info(cpuid); /* * Allow the master to continue. @@ -557,7 +555,6 @@ static void __devinit start_secondary(vo enable_NMI_through_LVT0(NULL); enable_8259A_irq(0); } - enable_APIC_timer(); /* * low-memory mappings have been cleared, flush them from * the local TLBs too. diff -puN arch/i386/kernel/time.c~i386-apic-timer-use-clockevents-broadcast arch/i386/kernel/time.c --- a/arch/i386/kernel/time.c~i386-apic-timer-use-clockevents-broadcast +++ a/arch/i386/kernel/time.c @@ -193,11 +193,6 @@ irqreturn_t timer_interrupt(int irq, voi outb_p( irq_v|0x80, 0x61 ); /* reset the IRQ */ } -#ifdef CONFIG_X86_LOCAL_APIC - if (using_apic_timer) - smp_send_timer_broadcast_ipi(); -#endif - return IRQ_HANDLED; } diff -puN drivers/acpi/processor_idle.c~i386-apic-timer-use-clockevents-broadcast drivers/acpi/processor_idle.c --- a/drivers/acpi/processor_idle.c~i386-apic-timer-use-clockevents-broadcast +++ a/drivers/acpi/processor_idle.c @@ -281,11 +281,27 @@ static void acpi_propagate_timer_broadca on_each_cpu(switch_ipi_to_APIC_timer, &mask, 1, 1); } +/* Power(C) State timer broadcast control */ +static void acpi_state_timer_broadcast(struct acpi_processor *pr, + struct acpi_processor_cx *cx, + int broadcast) +{ + int state = cx - pr->power.states; + + if (state >= pr->power.timer_broadcast_on_state) + lapic_timer_idle_broadcast(broadcast); +} + #else static void acpi_timer_check_state(int state, struct acpi_processor *pr, struct acpi_processor_cx *cstate) { } static void acpi_propagate_timer_broadcast(struct acpi_processor *pr) { } +static void acpi_state_timer_broadcast(struct acpi_processor *pr, + struct acpi_processor_cx *cx, + int broadcast) +{ +} #endif @@ -431,6 +447,7 @@ static void acpi_processor_idle(void) /* Get start time (ticks) */ t1 = inl(acpi_fadt.xpm_tmr_blk.address); /* Invoke C2 */ + acpi_state_timer_broadcast(pr, cx, 1); acpi_cstate_enter(cx); /* Get end time (ticks) */ t2 = inl(acpi_fadt.xpm_tmr_blk.address); @@ -445,6 +462,7 @@ static void acpi_processor_idle(void) /* Compute time (ticks) that we were actually asleep */ sleep_ticks = ticks_elapsed(t1, t2) - cx->latency_ticks - C2_OVERHEAD; + acpi_state_timer_broadcast(pr, cx, 0); break; case ACPI_STATE_C3: @@ -467,6 +485,7 @@ static void acpi_processor_idle(void) /* Get start time (ticks) */ t1 = inl(acpi_fadt.xpm_tmr_blk.address); /* Invoke C3 */ + acpi_state_timer_broadcast(pr, cx, 1); acpi_cstate_enter(cx); /* Get end time (ticks) */ t2 = inl(acpi_fadt.xpm_tmr_blk.address); @@ -487,6 +506,7 @@ static void acpi_processor_idle(void) /* Compute time (ticks) that we were actually asleep */ sleep_ticks = ticks_elapsed(t1, t2) - cx->latency_ticks - C3_OVERHEAD; + acpi_state_timer_broadcast(pr, cx, 0); break; default: diff -puN include/asm-i386/apic.h~i386-apic-timer-use-clockevents-broadcast include/asm-i386/apic.h --- a/include/asm-i386/apic.h~i386-apic-timer-use-clockevents-broadcast +++ a/include/asm-i386/apic.h @@ -109,12 +109,9 @@ extern void smp_local_timer_interrupt (v extern void setup_boot_APIC_clock (void); extern void setup_secondary_APIC_clock (void); extern int APIC_init_uniprocessor (void); -extern void disable_APIC_timer(void); -extern void enable_APIC_timer(void); - +extern void lapic_timer_idle_broadcast(int broadcast); extern void enable_NMI_through_LVT0 (void * dummy); -void smp_send_timer_broadcast_ipi(void); void switch_APIC_timer_to_ipi(void *cpumask); void switch_ipi_to_APIC_timer(void *cpumask); #define ARCH_APICTIMER_STOPS_ON_C3 1 diff -puN include/asm-i386/mpspec.h~i386-apic-timer-use-clockevents-broadcast include/asm-i386/mpspec.h --- a/include/asm-i386/mpspec.h~i386-apic-timer-use-clockevents-broadcast +++ a/include/asm-i386/mpspec.h @@ -23,7 +23,6 @@ extern struct mpc_config_intsrc mp_irqs extern int mpc_default_type; extern unsigned long mp_lapic_addr; extern int pic_mode; -extern int using_apic_timer; #ifdef CONFIG_ACPI extern void mp_register_lapic (u8 id, u8 enabled); _