diff options
| author | Bjoern Brandenburg <bbb@mpi-sws.org> | 2015-08-09 07:18:46 -0400 |
|---|---|---|
| committer | Bjoern Brandenburg <bbb@mpi-sws.org> | 2015-08-09 06:21:17 -0400 |
| commit | 5014e7011964ff46b2d73cf91a05ed9eed5a8fa2 (patch) | |
| tree | 76fad060cf673112d92a4f5f2d9b9423383610f6 | |
| parent | fc6ac04ddc314b9cff5bdb92c8330569658076a3 (diff) | |
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.
| -rw-r--r-- | arch/arm/Kconfig | 3 | ||||
| -rw-r--r-- | arch/x86/Kconfig | 3 | ||||
| -rw-r--r-- | arch/x86/include/asm/entry_arch.h | 1 | ||||
| -rw-r--r-- | arch/x86/include/asm/hw_irq.h | 4 | ||||
| -rw-r--r-- | arch/x86/include/asm/irq_vectors.h | 6 | ||||
| -rw-r--r-- | arch/x86/kernel/entry_64.S | 2 | ||||
| -rw-r--r-- | arch/x86/kernel/irqinit.c | 3 | ||||
| -rw-r--r-- | arch/x86/kernel/smp.c | 38 | ||||
| -rw-r--r-- | include/linux/hrtimer.h | 32 | ||||
| -rw-r--r-- | include/linux/smp.h | 5 | ||||
| -rw-r--r-- | kernel/time/hrtimer.c | 95 |
11 files changed, 191 insertions, 1 deletions
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" | |||
| 2123 | config ARCH_HAS_FEATHER_TRACE | 2123 | config ARCH_HAS_FEATHER_TRACE |
| 2124 | def_bool n | 2124 | def_bool n |
| 2125 | 2125 | ||
| 2126 | config ARCH_HAS_SEND_PULL_TIMERS | ||
| 2127 | def_bool n | ||
| 2128 | |||
| 2126 | source "litmus/Kconfig" | 2129 | source "litmus/Kconfig" |
| 2127 | 2130 | ||
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" | |||
| 2598 | config ARCH_HAS_FEATHER_TRACE | 2598 | config ARCH_HAS_FEATHER_TRACE |
| 2599 | def_bool y | 2599 | def_bool y |
| 2600 | 2600 | ||
| 2601 | config ARCH_HAS_SEND_PULL_TIMERS | ||
| 2602 | def_bool y | ||
| 2603 | |||
| 2601 | source "litmus/Kconfig" | 2604 | 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 @@ | |||
| 13 | BUILD_INTERRUPT(reschedule_interrupt,RESCHEDULE_VECTOR) | 13 | BUILD_INTERRUPT(reschedule_interrupt,RESCHEDULE_VECTOR) |
| 14 | BUILD_INTERRUPT(call_function_interrupt,CALL_FUNCTION_VECTOR) | 14 | BUILD_INTERRUPT(call_function_interrupt,CALL_FUNCTION_VECTOR) |
| 15 | BUILD_INTERRUPT(call_function_single_interrupt,CALL_FUNCTION_SINGLE_VECTOR) | 15 | BUILD_INTERRUPT(call_function_single_interrupt,CALL_FUNCTION_SINGLE_VECTOR) |
| 16 | BUILD_INTERRUPT(pull_timers_interrupt,PULL_TIMERS_VECTOR) | ||
| 16 | BUILD_INTERRUPT3(irq_move_cleanup_interrupt, IRQ_MOVE_CLEANUP_VECTOR, | 17 | BUILD_INTERRUPT3(irq_move_cleanup_interrupt, IRQ_MOVE_CLEANUP_VECTOR, |
| 17 | smp_irq_move_cleanup_interrupt) | 18 | smp_irq_move_cleanup_interrupt) |
| 18 | BUILD_INTERRUPT3(reboot_interrupt, REBOOT_VECTOR, smp_reboot_interrupt) | 19 | 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); | |||
| 77 | extern asmlinkage void call_function_interrupt(void); | 77 | extern asmlinkage void call_function_interrupt(void); |
| 78 | extern asmlinkage void call_function_single_interrupt(void); | 78 | extern asmlinkage void call_function_single_interrupt(void); |
| 79 | 79 | ||
| 80 | extern asmlinkage void pull_timers_interrupt(void); | ||
| 81 | |||
| 80 | #ifdef CONFIG_TRACING | 82 | #ifdef CONFIG_TRACING |
| 81 | /* Interrupt handlers registered during init_IRQ */ | 83 | /* Interrupt handlers registered during init_IRQ */ |
| 82 | extern void trace_apic_timer_interrupt(void); | 84 | extern void trace_apic_timer_interrupt(void); |
| @@ -89,6 +91,7 @@ extern void trace_reschedule_interrupt(void); | |||
| 89 | extern void trace_threshold_interrupt(void); | 91 | extern void trace_threshold_interrupt(void); |
| 90 | extern void trace_call_function_interrupt(void); | 92 | extern void trace_call_function_interrupt(void); |
| 91 | extern void trace_call_function_single_interrupt(void); | 93 | extern void trace_call_function_single_interrupt(void); |
| 94 | extern void trace_pull_timers_interrupt(void); | ||
| 92 | #define trace_irq_move_cleanup_interrupt irq_move_cleanup_interrupt | 95 | #define trace_irq_move_cleanup_interrupt irq_move_cleanup_interrupt |
| 93 | #define trace_reboot_interrupt reboot_interrupt | 96 | #define trace_reboot_interrupt reboot_interrupt |
| 94 | #define trace_kvm_posted_intr_ipi kvm_posted_intr_ipi | 97 | #define trace_kvm_posted_intr_ipi kvm_posted_intr_ipi |
| @@ -179,6 +182,7 @@ extern __visible void smp_reschedule_interrupt(struct pt_regs *); | |||
| 179 | extern __visible void smp_call_function_interrupt(struct pt_regs *); | 182 | extern __visible void smp_call_function_interrupt(struct pt_regs *); |
| 180 | extern __visible void smp_call_function_single_interrupt(struct pt_regs *); | 183 | extern __visible void smp_call_function_single_interrupt(struct pt_regs *); |
| 181 | extern __visible void smp_invalidate_interrupt(struct pt_regs *); | 184 | extern __visible void smp_invalidate_interrupt(struct pt_regs *); |
| 185 | extern __visible void smp_pull_timers_interrupt(struct pt_regs *); | ||
| 182 | #endif | 186 | #endif |
| 183 | 187 | ||
| 184 | extern char irq_entries_start[]; | 188 | 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 @@ | |||
| 124 | */ | 124 | */ |
| 125 | #define LOCAL_TIMER_VECTOR 0xef | 125 | #define LOCAL_TIMER_VECTOR 0xef |
| 126 | 126 | ||
| 127 | /* | ||
| 128 | * LITMUS^RT pull timers IRQ vector. | ||
| 129 | * Make sure it's not used by Linux. | ||
| 130 | */ | ||
| 131 | #define PULL_TIMERS_VECTOR 0xdf | ||
| 132 | |||
| 127 | #define NR_VECTORS 256 | 133 | #define NR_VECTORS 256 |
| 128 | 134 | ||
| 129 | #ifdef CONFIG_X86_LOCAL_APIC | 135 | #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 \ | |||
| 947 | call_function_interrupt smp_call_function_interrupt | 947 | call_function_interrupt smp_call_function_interrupt |
| 948 | apicinterrupt RESCHEDULE_VECTOR \ | 948 | apicinterrupt RESCHEDULE_VECTOR \ |
| 949 | reschedule_interrupt smp_reschedule_interrupt | 949 | reschedule_interrupt smp_reschedule_interrupt |
| 950 | apicinterrupt PULL_TIMERS_VECTOR \ | ||
| 951 | pull_timers_interrupt smp_pull_timers_interrupt | ||
| 950 | #endif | 952 | #endif |
| 951 | 953 | ||
| 952 | apicinterrupt ERROR_APIC_VECTOR \ | 954 | 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) | |||
| 115 | alloc_intr_gate(CALL_FUNCTION_SINGLE_VECTOR, | 115 | alloc_intr_gate(CALL_FUNCTION_SINGLE_VECTOR, |
| 116 | call_function_single_interrupt); | 116 | call_function_single_interrupt); |
| 117 | 117 | ||
| 118 | /* IPI for hrtimer pulling on remote cpus */ | ||
| 119 | alloc_intr_gate(PULL_TIMERS_VECTOR, pull_timers_interrupt); | ||
| 120 | |||
| 118 | /* Low priority IPI to cleanup after moving an irq */ | 121 | /* Low priority IPI to cleanup after moving an irq */ |
| 119 | set_intr_gate(IRQ_MOVE_CLEANUP_VECTOR, irq_move_cleanup_interrupt); | 122 | set_intr_gate(IRQ_MOVE_CLEANUP_VECTOR, irq_move_cleanup_interrupt); |
| 120 | set_bit(IRQ_MOVE_CLEANUP_VECTOR, used_vectors); | 123 | 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 @@ | |||
| 24 | #include <linux/cpu.h> | 24 | #include <linux/cpu.h> |
| 25 | #include <linux/gfp.h> | 25 | #include <linux/gfp.h> |
| 26 | 26 | ||
| 27 | #include <litmus/debug_trace.h> | ||
| 28 | |||
| 27 | #include <asm/mtrr.h> | 29 | #include <asm/mtrr.h> |
| 28 | #include <asm/tlbflush.h> | 30 | #include <asm/tlbflush.h> |
| 29 | #include <asm/mmu_context.h> | 31 | #include <asm/mmu_context.h> |
| @@ -164,6 +166,16 @@ static int smp_stop_nmi_callback(unsigned int val, struct pt_regs *regs) | |||
| 164 | return NMI_HANDLED; | 166 | return NMI_HANDLED; |
| 165 | } | 167 | } |
| 166 | 168 | ||
| 169 | /* trigger timers on remote cpu */ | ||
| 170 | void smp_send_pull_timers(int cpu) | ||
| 171 | { | ||
| 172 | if (unlikely(cpu_is_offline(cpu))) { | ||
| 173 | WARN_ON(1); | ||
| 174 | return; | ||
| 175 | } | ||
| 176 | apic->send_IPI_mask(cpumask_of(cpu), PULL_TIMERS_VECTOR); | ||
| 177 | } | ||
| 178 | |||
| 167 | /* | 179 | /* |
| 168 | * this function calls the 'stop' function on all other CPUs in the system. | 180 | * this function calls the 'stop' function on all other CPUs in the system. |
| 169 | */ | 181 | */ |
| @@ -216,7 +228,7 @@ static void native_stop_other_cpus(int wait) | |||
| 216 | while (num_online_cpus() > 1 && (wait || timeout--)) | 228 | while (num_online_cpus() > 1 && (wait || timeout--)) |
| 217 | udelay(1); | 229 | udelay(1); |
| 218 | } | 230 | } |
| 219 | 231 | ||
| 220 | /* if the REBOOT_VECTOR didn't work, try with the NMI */ | 232 | /* if the REBOOT_VECTOR didn't work, try with the NMI */ |
| 221 | if ((num_online_cpus() > 1) && (!smp_no_nmi_ipi)) { | 233 | if ((num_online_cpus() > 1) && (!smp_no_nmi_ipi)) { |
| 222 | if (register_nmi_handler(NMI_LOCAL, smp_stop_nmi_callback, | 234 | if (register_nmi_handler(NMI_LOCAL, smp_stop_nmi_callback, |
| @@ -341,6 +353,30 @@ static int __init nonmi_ipi_setup(char *str) | |||
| 341 | 353 | ||
| 342 | __setup("nonmi_ipi", nonmi_ipi_setup); | 354 | __setup("nonmi_ipi", nonmi_ipi_setup); |
| 343 | 355 | ||
| 356 | extern void hrtimer_pull(void); | ||
| 357 | |||
| 358 | static inline void __smp_pull_timers_interrupt(void) | ||
| 359 | { | ||
| 360 | TRACE("pull timer interrupt\n"); | ||
| 361 | hrtimer_pull(); | ||
| 362 | } | ||
| 363 | |||
| 364 | __visible void smp_pull_timers_interrupt(struct pt_regs *regs) | ||
| 365 | { | ||
| 366 | smp_entering_irq(); | ||
| 367 | __smp_pull_timers_interrupt(); | ||
| 368 | exiting_irq(); | ||
| 369 | } | ||
| 370 | |||
| 371 | __visible void smp_trace_pull_timers_interrupt(struct pt_regs *regs) | ||
| 372 | { | ||
| 373 | smp_entering_irq(); | ||
| 374 | trace_call_function_single_entry(PULL_TIMERS_VECTOR); | ||
| 375 | __smp_pull_timers_interrupt(); | ||
| 376 | trace_call_function_single_exit(PULL_TIMERS_VECTOR); | ||
| 377 | exiting_irq(); | ||
| 378 | } | ||
| 379 | |||
| 344 | struct smp_ops smp_ops = { | 380 | struct smp_ops smp_ops = { |
| 345 | .smp_prepare_boot_cpu = native_smp_prepare_boot_cpu, | 381 | .smp_prepare_boot_cpu = native_smp_prepare_boot_cpu, |
| 346 | .smp_prepare_cpus = native_smp_prepare_cpus, | 382 | .smp_prepare_cpus = native_smp_prepare_cpus, |
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 05f6df1fdf5b..d1be5d2cb8fc 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h | |||
| @@ -178,6 +178,7 @@ enum hrtimer_base_type { | |||
| 178 | * @nr_hangs: Total number of hrtimer interrupt hangs | 178 | * @nr_hangs: Total number of hrtimer interrupt hangs |
| 179 | * @max_hang_time: Maximum time spent in hrtimer_interrupt | 179 | * @max_hang_time: Maximum time spent in hrtimer_interrupt |
| 180 | * @clock_base: array of clock bases for this cpu | 180 | * @clock_base: array of clock bases for this cpu |
| 181 | * @to_pull: LITMUS^RT list of timers to be pulled on this cpu | ||
| 181 | */ | 182 | */ |
| 182 | struct hrtimer_cpu_base { | 183 | struct hrtimer_cpu_base { |
| 183 | raw_spinlock_t lock; | 184 | raw_spinlock_t lock; |
| @@ -195,8 +196,32 @@ struct hrtimer_cpu_base { | |||
| 195 | ktime_t max_hang_time; | 196 | ktime_t max_hang_time; |
| 196 | #endif | 197 | #endif |
| 197 | struct hrtimer_clock_base clock_base[HRTIMER_MAX_CLOCK_BASES]; | 198 | struct hrtimer_clock_base clock_base[HRTIMER_MAX_CLOCK_BASES]; |
| 199 | struct list_head to_pull; | ||
| 198 | }; | 200 | }; |
| 199 | 201 | ||
| 202 | #ifdef CONFIG_ARCH_HAS_SEND_PULL_TIMERS | ||
| 203 | |||
| 204 | #define HRTIMER_START_ON_INACTIVE 0 | ||
| 205 | #define HRTIMER_START_ON_QUEUED 1 | ||
| 206 | |||
| 207 | /* | ||
| 208 | * struct hrtimer_start_on_info - save timer info on remote cpu | ||
| 209 | * @list: list of hrtimer_start_on_info on remote cpu (to_pull) | ||
| 210 | * @timer: timer to be triggered on remote cpu | ||
| 211 | * @time: time event | ||
| 212 | * @mode: timer mode | ||
| 213 | * @state: activity flag | ||
| 214 | */ | ||
| 215 | struct hrtimer_start_on_info { | ||
| 216 | struct list_head list; | ||
| 217 | struct hrtimer *timer; | ||
| 218 | ktime_t time; | ||
| 219 | enum hrtimer_mode mode; | ||
| 220 | atomic_t state; | ||
| 221 | }; | ||
| 222 | |||
| 223 | #endif | ||
| 224 | |||
| 200 | static inline void hrtimer_set_expires(struct hrtimer *timer, ktime_t time) | 225 | static inline void hrtimer_set_expires(struct hrtimer *timer, ktime_t time) |
| 201 | { | 226 | { |
| 202 | timer->node.expires = time; | 227 | timer->node.expires = time; |
| @@ -362,6 +387,13 @@ __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, | |||
| 362 | unsigned long delta_ns, | 387 | unsigned long delta_ns, |
| 363 | const enum hrtimer_mode mode, int wakeup); | 388 | const enum hrtimer_mode mode, int wakeup); |
| 364 | 389 | ||
| 390 | #ifdef CONFIG_ARCH_HAS_SEND_PULL_TIMERS | ||
| 391 | extern void hrtimer_start_on_info_init(struct hrtimer_start_on_info *info); | ||
| 392 | extern int hrtimer_start_on(int cpu, struct hrtimer_start_on_info *info, | ||
| 393 | struct hrtimer *timer, ktime_t time, | ||
| 394 | const enum hrtimer_mode mode); | ||
| 395 | #endif | ||
| 396 | |||
| 365 | extern int hrtimer_cancel(struct hrtimer *timer); | 397 | extern int hrtimer_cancel(struct hrtimer *timer); |
| 366 | extern int hrtimer_try_to_cancel(struct hrtimer *timer); | 398 | extern int hrtimer_try_to_cancel(struct hrtimer *timer); |
| 367 | 399 | ||
diff --git a/include/linux/smp.h b/include/linux/smp.h index c4414074bd88..dfc63fe33a63 100644 --- a/include/linux/smp.h +++ b/include/linux/smp.h | |||
| @@ -103,6 +103,11 @@ void kick_all_cpus_sync(void); | |||
| 103 | void wake_up_all_idle_cpus(void); | 103 | void wake_up_all_idle_cpus(void); |
| 104 | 104 | ||
| 105 | /* | 105 | /* |
| 106 | * sends a 'pull timer' event to a remote CPU | ||
| 107 | */ | ||
| 108 | extern void smp_send_pull_timers(int cpu); | ||
| 109 | |||
| 110 | /* | ||
| 106 | * Generic and arch helpers | 111 | * Generic and arch helpers |
| 107 | */ | 112 | */ |
| 108 | void __init call_function_init(void); | 113 | void __init call_function_init(void); |
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index 93ef7190bdea..22f9156f19d2 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c | |||
| @@ -50,6 +50,8 @@ | |||
| 50 | #include <linux/timer.h> | 50 | #include <linux/timer.h> |
| 51 | #include <linux/freezer.h> | 51 | #include <linux/freezer.h> |
| 52 | 52 | ||
| 53 | #include <litmus/debug_trace.h> | ||
| 54 | |||
| 53 | #include <asm/uaccess.h> | 55 | #include <asm/uaccess.h> |
| 54 | 56 | ||
| 55 | #include <trace/events/timer.h> | 57 | #include <trace/events/timer.h> |
| @@ -1045,6 +1047,98 @@ hrtimer_start(struct hrtimer *timer, ktime_t tim, const enum hrtimer_mode mode) | |||
| 1045 | } | 1047 | } |
| 1046 | EXPORT_SYMBOL_GPL(hrtimer_start); | 1048 | EXPORT_SYMBOL_GPL(hrtimer_start); |
| 1047 | 1049 | ||
| 1050 | #if defined(CONFIG_ARCH_HAS_SEND_PULL_TIMERS) && defined(CONFIG_SMP) | ||
| 1051 | |||
| 1052 | /** | ||
| 1053 | * hrtimer_start_on_info_init - Initialize hrtimer_start_on_info | ||
| 1054 | */ | ||
| 1055 | void hrtimer_start_on_info_init(struct hrtimer_start_on_info *info) | ||
| 1056 | { | ||
| 1057 | memset(info, 0, sizeof(struct hrtimer_start_on_info)); | ||
| 1058 | atomic_set(&info->state, HRTIMER_START_ON_INACTIVE); | ||
| 1059 | } | ||
| 1060 | |||
| 1061 | /** | ||
| 1062 | * hrtimer_pull - PULL_TIMERS_VECTOR callback on remote cpu | ||
| 1063 | */ | ||
| 1064 | void hrtimer_pull(void) | ||
| 1065 | { | ||
| 1066 | struct hrtimer_cpu_base *base = this_cpu_ptr(&hrtimer_bases); | ||
| 1067 | struct hrtimer_start_on_info *info; | ||
| 1068 | struct list_head *pos, *safe, list; | ||
| 1069 | |||
| 1070 | raw_spin_lock(&base->lock); | ||
| 1071 | list_replace_init(&base->to_pull, &list); | ||
| 1072 | raw_spin_unlock(&base->lock); | ||
| 1073 | |||
| 1074 | list_for_each_safe(pos, safe, &list) { | ||
| 1075 | info = list_entry(pos, struct hrtimer_start_on_info, list); | ||
| 1076 | TRACE("pulled timer 0x%x\n", info->timer); | ||
| 1077 | list_del(pos); | ||
| 1078 | hrtimer_start(info->timer, info->time, info->mode); | ||
| 1079 | } | ||
| 1080 | } | ||
| 1081 | |||
| 1082 | /** | ||
| 1083 | * hrtimer_start_on - trigger timer arming on remote cpu | ||
| 1084 | * @cpu: remote cpu | ||
| 1085 | * @info: save timer information for enqueuing on remote cpu | ||
| 1086 | * @timer: timer to be pulled | ||
| 1087 | * @time: expire time | ||
| 1088 | * @mode: timer mode | ||
| 1089 | */ | ||
| 1090 | int hrtimer_start_on(int cpu, struct hrtimer_start_on_info* info, | ||
| 1091 | struct hrtimer *timer, ktime_t time, | ||
| 1092 | const enum hrtimer_mode mode) | ||
| 1093 | { | ||
| 1094 | unsigned long flags; | ||
| 1095 | struct hrtimer_cpu_base* base; | ||
| 1096 | int in_use = 0, was_empty; | ||
| 1097 | |||
| 1098 | /* serialize access to info through the timer base */ | ||
| 1099 | lock_hrtimer_base(timer, &flags); | ||
| 1100 | |||
| 1101 | in_use = (atomic_read(&info->state) != HRTIMER_START_ON_INACTIVE); | ||
| 1102 | if (!in_use) { | ||
| 1103 | INIT_LIST_HEAD(&info->list); | ||
| 1104 | info->timer = timer; | ||
| 1105 | info->time = time; | ||
| 1106 | info->mode = mode; | ||
| 1107 | /* mark as in use */ | ||
| 1108 | atomic_set(&info->state, HRTIMER_START_ON_QUEUED); | ||
| 1109 | } | ||
| 1110 | |||
| 1111 | unlock_hrtimer_base(timer, &flags); | ||
| 1112 | |||
| 1113 | if (!in_use) { | ||
| 1114 | /* initiate pull */ | ||
| 1115 | preempt_disable(); | ||
| 1116 | if (cpu == smp_processor_id()) { | ||
| 1117 | /* start timer locally; we may get called | ||
| 1118 | * with rq->lock held, do not wake up anything | ||
| 1119 | */ | ||
| 1120 | TRACE("hrtimer_start_on: starting on local CPU\n"); | ||
| 1121 | __hrtimer_start_range_ns(info->timer, info->time, | ||
| 1122 | 0, info->mode, 0); | ||
| 1123 | } else { | ||
| 1124 | TRACE("hrtimer_start_on: pulling to remote CPU\n"); | ||
| 1125 | base = &per_cpu(hrtimer_bases, cpu); | ||
| 1126 | raw_spin_lock_irqsave(&base->lock, flags); | ||
| 1127 | was_empty = list_empty(&base->to_pull); | ||
| 1128 | list_add(&info->list, &base->to_pull); | ||
| 1129 | raw_spin_unlock_irqrestore(&base->lock, flags); | ||
| 1130 | if (was_empty) | ||
| 1131 | /* only send IPI if other no else | ||
| 1132 | * has done so already | ||
| 1133 | */ | ||
| 1134 | smp_send_pull_timers(cpu); | ||
| 1135 | } | ||
| 1136 | preempt_enable(); | ||
| 1137 | } | ||
| 1138 | return in_use; | ||
| 1139 | } | ||
| 1140 | |||
| 1141 | #endif | ||
| 1048 | 1142 | ||
| 1049 | /** | 1143 | /** |
| 1050 | * hrtimer_try_to_cancel - try to deactivate a timer | 1144 | * hrtimer_try_to_cancel - try to deactivate a timer |
| @@ -1626,6 +1720,7 @@ static void init_hrtimers_cpu(int cpu) | |||
| 1626 | 1720 | ||
| 1627 | cpu_base->cpu = cpu; | 1721 | cpu_base->cpu = cpu; |
| 1628 | hrtimer_init_hres(cpu_base); | 1722 | hrtimer_init_hres(cpu_base); |
| 1723 | INIT_LIST_HEAD(&cpu_base->to_pull); | ||
| 1629 | } | 1724 | } |
| 1630 | 1725 | ||
| 1631 | #ifdef CONFIG_HOTPLUG_CPU | 1726 | #ifdef CONFIG_HOTPLUG_CPU |
