From 5014e7011964ff46b2d73cf91a05ed9eed5a8fa2 Mon Sep 17 00:00:00 2001 From: Bjoern Brandenburg Date: Sun, 9 Aug 2015 13:18:46 +0200 Subject: Add hrtimer_start_on() support This patch adds hrtimer_start_on(), which allows arming timers on remote CPUs. This is needed to avoided timer interrupts on "shielded" CPUs and is also useful for implementing semi-partitioned schedulers. --- arch/arm/Kconfig | 3 +++ arch/x86/Kconfig | 3 +++ arch/x86/include/asm/entry_arch.h | 1 + arch/x86/include/asm/hw_irq.h | 4 ++++ arch/x86/include/asm/irq_vectors.h | 6 ++++++ arch/x86/kernel/entry_64.S | 2 ++ arch/x86/kernel/irqinit.c | 3 +++ arch/x86/kernel/smp.c | 38 +++++++++++++++++++++++++++++++++++++- 8 files changed, 59 insertions(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index ce948d46a1b3..d3bfc3a4e56a 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -2123,5 +2123,8 @@ source "arch/arm/kvm/Kconfig" config ARCH_HAS_FEATHER_TRACE def_bool n +config ARCH_HAS_SEND_PULL_TIMERS + def_bool n + source "litmus/Kconfig" diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 6331227b4686..f9c9a4d70b24 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -2598,4 +2598,7 @@ source "lib/Kconfig" config ARCH_HAS_FEATHER_TRACE def_bool y +config ARCH_HAS_SEND_PULL_TIMERS + def_bool y + source "litmus/Kconfig" diff --git a/arch/x86/include/asm/entry_arch.h b/arch/x86/include/asm/entry_arch.h index dc5fa661465f..e657219d7964 100644 --- a/arch/x86/include/asm/entry_arch.h +++ b/arch/x86/include/asm/entry_arch.h @@ -13,6 +13,7 @@ BUILD_INTERRUPT(reschedule_interrupt,RESCHEDULE_VECTOR) BUILD_INTERRUPT(call_function_interrupt,CALL_FUNCTION_VECTOR) BUILD_INTERRUPT(call_function_single_interrupt,CALL_FUNCTION_SINGLE_VECTOR) +BUILD_INTERRUPT(pull_timers_interrupt,PULL_TIMERS_VECTOR) BUILD_INTERRUPT3(irq_move_cleanup_interrupt, IRQ_MOVE_CLEANUP_VECTOR, smp_irq_move_cleanup_interrupt) BUILD_INTERRUPT3(reboot_interrupt, REBOOT_VECTOR, smp_reboot_interrupt) diff --git a/arch/x86/include/asm/hw_irq.h b/arch/x86/include/asm/hw_irq.h index e9571ddabc4f..3e860d1f9416 100644 --- a/arch/x86/include/asm/hw_irq.h +++ b/arch/x86/include/asm/hw_irq.h @@ -77,6 +77,8 @@ extern asmlinkage void threshold_interrupt(void); extern asmlinkage void call_function_interrupt(void); extern asmlinkage void call_function_single_interrupt(void); +extern asmlinkage void pull_timers_interrupt(void); + #ifdef CONFIG_TRACING /* Interrupt handlers registered during init_IRQ */ extern void trace_apic_timer_interrupt(void); @@ -89,6 +91,7 @@ extern void trace_reschedule_interrupt(void); extern void trace_threshold_interrupt(void); extern void trace_call_function_interrupt(void); extern void trace_call_function_single_interrupt(void); +extern void trace_pull_timers_interrupt(void); #define trace_irq_move_cleanup_interrupt irq_move_cleanup_interrupt #define trace_reboot_interrupt reboot_interrupt #define trace_kvm_posted_intr_ipi kvm_posted_intr_ipi @@ -179,6 +182,7 @@ extern __visible void smp_reschedule_interrupt(struct pt_regs *); extern __visible void smp_call_function_interrupt(struct pt_regs *); extern __visible void smp_call_function_single_interrupt(struct pt_regs *); extern __visible void smp_invalidate_interrupt(struct pt_regs *); +extern __visible void smp_pull_timers_interrupt(struct pt_regs *); #endif extern char irq_entries_start[]; diff --git a/arch/x86/include/asm/irq_vectors.h b/arch/x86/include/asm/irq_vectors.h index 666c89ec4bd7..82545836e1a1 100644 --- a/arch/x86/include/asm/irq_vectors.h +++ b/arch/x86/include/asm/irq_vectors.h @@ -124,6 +124,12 @@ */ #define LOCAL_TIMER_VECTOR 0xef +/* + * LITMUS^RT pull timers IRQ vector. + * Make sure it's not used by Linux. + */ +#define PULL_TIMERS_VECTOR 0xdf + #define NR_VECTORS 256 #ifdef CONFIG_X86_LOCAL_APIC diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index 02c2eff7478d..d810f5fea04f 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S @@ -947,6 +947,8 @@ apicinterrupt CALL_FUNCTION_VECTOR \ call_function_interrupt smp_call_function_interrupt apicinterrupt RESCHEDULE_VECTOR \ reschedule_interrupt smp_reschedule_interrupt +apicinterrupt PULL_TIMERS_VECTOR \ + pull_timers_interrupt smp_pull_timers_interrupt #endif apicinterrupt ERROR_APIC_VECTOR \ diff --git a/arch/x86/kernel/irqinit.c b/arch/x86/kernel/irqinit.c index cd10a6437264..834496ed9536 100644 --- a/arch/x86/kernel/irqinit.c +++ b/arch/x86/kernel/irqinit.c @@ -115,6 +115,9 @@ static void __init smp_intr_init(void) alloc_intr_gate(CALL_FUNCTION_SINGLE_VECTOR, call_function_single_interrupt); + /* IPI for hrtimer pulling on remote cpus */ + alloc_intr_gate(PULL_TIMERS_VECTOR, pull_timers_interrupt); + /* Low priority IPI to cleanup after moving an irq */ set_intr_gate(IRQ_MOVE_CLEANUP_VECTOR, irq_move_cleanup_interrupt); set_bit(IRQ_MOVE_CLEANUP_VECTOR, used_vectors); diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c index be8e1bde07aa..7ce9fb210d55 100644 --- a/arch/x86/kernel/smp.c +++ b/arch/x86/kernel/smp.c @@ -24,6 +24,8 @@ #include #include +#include + #include #include #include @@ -164,6 +166,16 @@ static int smp_stop_nmi_callback(unsigned int val, struct pt_regs *regs) return NMI_HANDLED; } +/* trigger timers on remote cpu */ +void smp_send_pull_timers(int cpu) +{ + if (unlikely(cpu_is_offline(cpu))) { + WARN_ON(1); + return; + } + apic->send_IPI_mask(cpumask_of(cpu), PULL_TIMERS_VECTOR); +} + /* * this function calls the 'stop' function on all other CPUs in the system. */ @@ -216,7 +228,7 @@ static void native_stop_other_cpus(int wait) while (num_online_cpus() > 1 && (wait || timeout--)) udelay(1); } - + /* if the REBOOT_VECTOR didn't work, try with the NMI */ if ((num_online_cpus() > 1) && (!smp_no_nmi_ipi)) { if (register_nmi_handler(NMI_LOCAL, smp_stop_nmi_callback, @@ -341,6 +353,30 @@ static int __init nonmi_ipi_setup(char *str) __setup("nonmi_ipi", nonmi_ipi_setup); +extern void hrtimer_pull(void); + +static inline void __smp_pull_timers_interrupt(void) +{ + TRACE("pull timer interrupt\n"); + hrtimer_pull(); +} + +__visible void smp_pull_timers_interrupt(struct pt_regs *regs) +{ + smp_entering_irq(); + __smp_pull_timers_interrupt(); + exiting_irq(); +} + +__visible void smp_trace_pull_timers_interrupt(struct pt_regs *regs) +{ + smp_entering_irq(); + trace_call_function_single_entry(PULL_TIMERS_VECTOR); + __smp_pull_timers_interrupt(); + trace_call_function_single_exit(PULL_TIMERS_VECTOR); + exiting_irq(); +} + struct smp_ops smp_ops = { .smp_prepare_boot_cpu = native_smp_prepare_boot_cpu, .smp_prepare_cpus = native_smp_prepare_cpus, -- cgit v1.2.2