diff options
author | Felipe Cerqueira <felipec@mpi-sws.org> | 2013-02-11 10:36:35 -0500 |
---|---|---|
committer | Bjoern Brandenburg <bbb@mpi-sws.org> | 2014-06-07 05:30:32 -0400 |
commit | 14e6b5665983e393f6c073f635f2aaf947ff7448 (patch) | |
tree | 1abbd5409f3476c5abd45d015e851be5c03f08fc | |
parent | dacc06b7f1cd1086ec08704eafea0e1009a69108 (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 | 3 | ||||
-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 | 23 | ||||
-rw-r--r-- | include/linux/hrtimer.h | 32 | ||||
-rw-r--r-- | include/linux/smp.h | 5 | ||||
-rw-r--r-- | kernel/hrtimer.c | 95 |
11 files changed, 176 insertions, 0 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 5903bc94a3be..4053810524d1 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig | |||
@@ -2249,5 +2249,8 @@ source "arch/arm/kvm/Kconfig" | |||
2249 | config ARCH_HAS_FEATHER_TRACE | 2249 | config ARCH_HAS_FEATHER_TRACE |
2250 | def_bool n | 2250 | def_bool n |
2251 | 2251 | ||
2252 | config ARCH_HAS_SEND_PULL_TIMERS | ||
2253 | def_bool n | ||
2254 | |||
2252 | source "litmus/Kconfig" | 2255 | source "litmus/Kconfig" |
2253 | 2256 | ||
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 171cdc9cc5db..b0695266a4cb 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
@@ -2350,4 +2350,7 @@ source "lib/Kconfig" | |||
2350 | config ARCH_HAS_FEATHER_TRACE | 2350 | config ARCH_HAS_FEATHER_TRACE |
2351 | def_bool y | 2351 | def_bool y |
2352 | 2352 | ||
2353 | config ARCH_HAS_SEND_PULL_TIMERS | ||
2354 | def_bool y | ||
2355 | |||
2353 | source "litmus/Kconfig" | 2356 | source "litmus/Kconfig" |
diff --git a/arch/x86/include/asm/entry_arch.h b/arch/x86/include/asm/entry_arch.h index 9bd4ecac72be..3a3c2f1ac1c4 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_INTERRUPT(irq_move_cleanup_interrupt,IRQ_MOVE_CLEANUP_VECTOR) | 17 | BUILD_INTERRUPT(irq_move_cleanup_interrupt,IRQ_MOVE_CLEANUP_VECTOR) |
17 | BUILD_INTERRUPT(reboot_interrupt,REBOOT_VECTOR) | 18 | BUILD_INTERRUPT(reboot_interrupt,REBOOT_VECTOR) |
18 | #endif | 19 | #endif |
diff --git a/arch/x86/include/asm/hw_irq.h b/arch/x86/include/asm/hw_irq.h index 1da97efad08a..672de9367d27 100644 --- a/arch/x86/include/asm/hw_irq.h +++ b/arch/x86/include/asm/hw_irq.h | |||
@@ -77,6 +77,8 @@ extern void threshold_interrupt(void); | |||
77 | extern void call_function_interrupt(void); | 77 | extern void call_function_interrupt(void); |
78 | extern void call_function_single_interrupt(void); | 78 | extern void call_function_single_interrupt(void); |
79 | 79 | ||
80 | extern void pull_timers_interrupt(void); | ||
81 | |||
80 | /* IOAPIC */ | 82 | /* IOAPIC */ |
81 | #define IO_APIC_IRQ(x) (((x) >= NR_IRQS_LEGACY) || ((1<<(x)) & io_apic_irqs)) | 83 | #define IO_APIC_IRQ(x) (((x) >= NR_IRQS_LEGACY) || ((1<<(x)) & io_apic_irqs)) |
82 | extern unsigned long io_apic_irqs; | 84 | extern unsigned long io_apic_irqs; |
@@ -166,6 +168,7 @@ extern asmlinkage void smp_irq_move_cleanup_interrupt(void); | |||
166 | extern void smp_reschedule_interrupt(struct pt_regs *); | 168 | extern void smp_reschedule_interrupt(struct pt_regs *); |
167 | extern void smp_call_function_interrupt(struct pt_regs *); | 169 | extern void smp_call_function_interrupt(struct pt_regs *); |
168 | extern void smp_call_function_single_interrupt(struct pt_regs *); | 170 | extern void smp_call_function_single_interrupt(struct pt_regs *); |
171 | extern void smp_pull_timers_interrupt(struct pt_regs *); | ||
169 | #ifdef CONFIG_X86_32 | 172 | #ifdef CONFIG_X86_32 |
170 | extern void smp_invalidate_interrupt(struct pt_regs *); | 173 | extern void smp_invalidate_interrupt(struct pt_regs *); |
171 | #else | 174 | #else |
diff --git a/arch/x86/include/asm/irq_vectors.h b/arch/x86/include/asm/irq_vectors.h index 5702d7e3111d..224116be0e22 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 | #define FPU_IRQ 13 | 135 | #define FPU_IRQ 13 |
diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index 7ac938a4bfab..2a5433723cbc 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S | |||
@@ -1183,6 +1183,8 @@ apicinterrupt CALL_FUNCTION_VECTOR \ | |||
1183 | call_function_interrupt smp_call_function_interrupt | 1183 | call_function_interrupt smp_call_function_interrupt |
1184 | apicinterrupt RESCHEDULE_VECTOR \ | 1184 | apicinterrupt RESCHEDULE_VECTOR \ |
1185 | reschedule_interrupt smp_reschedule_interrupt | 1185 | reschedule_interrupt smp_reschedule_interrupt |
1186 | apicinterrupt PULL_TIMERS_VECTOR \ | ||
1187 | pull_timers_interrupt smp_pull_timers_interrupt | ||
1186 | #endif | 1188 | #endif |
1187 | 1189 | ||
1188 | apicinterrupt ERROR_APIC_VECTOR \ | 1190 | apicinterrupt ERROR_APIC_VECTOR \ |
diff --git a/arch/x86/kernel/irqinit.c b/arch/x86/kernel/irqinit.c index a2a1fbc594ff..77979d9bd90d 100644 --- a/arch/x86/kernel/irqinit.c +++ b/arch/x86/kernel/irqinit.c | |||
@@ -145,6 +145,9 @@ static void __init smp_intr_init(void) | |||
145 | alloc_intr_gate(CALL_FUNCTION_SINGLE_VECTOR, | 145 | alloc_intr_gate(CALL_FUNCTION_SINGLE_VECTOR, |
146 | call_function_single_interrupt); | 146 | call_function_single_interrupt); |
147 | 147 | ||
148 | /* IPI for hrtimer pulling on remote cpus */ | ||
149 | alloc_intr_gate(PULL_TIMERS_VECTOR, pull_timers_interrupt); | ||
150 | |||
148 | /* Low priority IPI to cleanup after moving an irq */ | 151 | /* Low priority IPI to cleanup after moving an irq */ |
149 | set_intr_gate(IRQ_MOVE_CLEANUP_VECTOR, irq_move_cleanup_interrupt); | 152 | set_intr_gate(IRQ_MOVE_CLEANUP_VECTOR, irq_move_cleanup_interrupt); |
150 | set_bit(IRQ_MOVE_CLEANUP_VECTOR, used_vectors); | 153 | set_bit(IRQ_MOVE_CLEANUP_VECTOR, used_vectors); |
diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c index 48d2b7ded422..a52ef7fd6862 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> |
@@ -163,6 +165,16 @@ static int smp_stop_nmi_callback(unsigned int val, struct pt_regs *regs) | |||
163 | return NMI_HANDLED; | 165 | return NMI_HANDLED; |
164 | } | 166 | } |
165 | 167 | ||
168 | /* trigger timers on remote cpu */ | ||
169 | void smp_send_pull_timers(int cpu) | ||
170 | { | ||
171 | if (unlikely(cpu_is_offline(cpu))) { | ||
172 | WARN_ON(1); | ||
173 | return; | ||
174 | } | ||
175 | apic->send_IPI_mask(cpumask_of(cpu), PULL_TIMERS_VECTOR); | ||
176 | } | ||
177 | |||
166 | /* | 178 | /* |
167 | * this function calls the 'stop' function on all other CPUs in the system. | 179 | * this function calls the 'stop' function on all other CPUs in the system. |
168 | */ | 180 | */ |
@@ -285,6 +297,17 @@ static int __init nonmi_ipi_setup(char *str) | |||
285 | 297 | ||
286 | __setup("nonmi_ipi", nonmi_ipi_setup); | 298 | __setup("nonmi_ipi", nonmi_ipi_setup); |
287 | 299 | ||
300 | extern void hrtimer_pull(void); | ||
301 | |||
302 | void smp_pull_timers_interrupt(struct pt_regs *regs) | ||
303 | { | ||
304 | ack_APIC_irq(); | ||
305 | irq_enter(); | ||
306 | TRACE("pull timer interrupt\n"); | ||
307 | hrtimer_pull(); | ||
308 | irq_exit(); | ||
309 | } | ||
310 | |||
288 | struct smp_ops smp_ops = { | 311 | struct smp_ops smp_ops = { |
289 | .smp_prepare_boot_cpu = native_smp_prepare_boot_cpu, | 312 | .smp_prepare_boot_cpu = native_smp_prepare_boot_cpu, |
290 | .smp_prepare_cpus = native_smp_prepare_cpus, | 313 | .smp_prepare_cpus = native_smp_prepare_cpus, |
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index d19a5c2d2270..93def50764dd 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h | |||
@@ -176,6 +176,7 @@ enum hrtimer_base_type { | |||
176 | * @nr_hangs: Total number of hrtimer interrupt hangs | 176 | * @nr_hangs: Total number of hrtimer interrupt hangs |
177 | * @max_hang_time: Maximum time spent in hrtimer_interrupt | 177 | * @max_hang_time: Maximum time spent in hrtimer_interrupt |
178 | * @clock_base: array of clock bases for this cpu | 178 | * @clock_base: array of clock bases for this cpu |
179 | * @to_pull: LITMUS^RT list of timers to be pulled on this cpu | ||
179 | */ | 180 | */ |
180 | struct hrtimer_cpu_base { | 181 | struct hrtimer_cpu_base { |
181 | raw_spinlock_t lock; | 182 | raw_spinlock_t lock; |
@@ -191,8 +192,32 @@ struct hrtimer_cpu_base { | |||
191 | ktime_t max_hang_time; | 192 | ktime_t max_hang_time; |
192 | #endif | 193 | #endif |
193 | struct hrtimer_clock_base clock_base[HRTIMER_MAX_CLOCK_BASES]; | 194 | struct hrtimer_clock_base clock_base[HRTIMER_MAX_CLOCK_BASES]; |
195 | struct list_head to_pull; | ||
194 | }; | 196 | }; |
195 | 197 | ||
198 | #ifdef CONFIG_ARCH_HAS_SEND_PULL_TIMERS | ||
199 | |||
200 | #define HRTIMER_START_ON_INACTIVE 0 | ||
201 | #define HRTIMER_START_ON_QUEUED 1 | ||
202 | |||
203 | /* | ||
204 | * struct hrtimer_start_on_info - save timer info on remote cpu | ||
205 | * @list: list of hrtimer_start_on_info on remote cpu (to_pull) | ||
206 | * @timer: timer to be triggered on remote cpu | ||
207 | * @time: time event | ||
208 | * @mode: timer mode | ||
209 | * @state: activity flag | ||
210 | */ | ||
211 | struct hrtimer_start_on_info { | ||
212 | struct list_head list; | ||
213 | struct hrtimer *timer; | ||
214 | ktime_t time; | ||
215 | enum hrtimer_mode mode; | ||
216 | atomic_t state; | ||
217 | }; | ||
218 | |||
219 | #endif | ||
220 | |||
196 | static inline void hrtimer_set_expires(struct hrtimer *timer, ktime_t time) | 221 | static inline void hrtimer_set_expires(struct hrtimer *timer, ktime_t time) |
197 | { | 222 | { |
198 | timer->node.expires = time; | 223 | timer->node.expires = time; |
@@ -366,6 +391,13 @@ __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim, | |||
366 | unsigned long delta_ns, | 391 | unsigned long delta_ns, |
367 | const enum hrtimer_mode mode, int wakeup); | 392 | const enum hrtimer_mode mode, int wakeup); |
368 | 393 | ||
394 | #ifdef CONFIG_ARCH_HAS_SEND_PULL_TIMERS | ||
395 | extern void hrtimer_start_on_info_init(struct hrtimer_start_on_info *info); | ||
396 | extern int hrtimer_start_on(int cpu, struct hrtimer_start_on_info *info, | ||
397 | struct hrtimer *timer, ktime_t time, | ||
398 | const enum hrtimer_mode mode); | ||
399 | #endif | ||
400 | |||
369 | extern int hrtimer_cancel(struct hrtimer *timer); | 401 | extern int hrtimer_cancel(struct hrtimer *timer); |
370 | extern int hrtimer_try_to_cancel(struct hrtimer *timer); | 402 | extern int hrtimer_try_to_cancel(struct hrtimer *timer); |
371 | 403 | ||
diff --git a/include/linux/smp.h b/include/linux/smp.h index c8488763277f..4f78ea77662c 100644 --- a/include/linux/smp.h +++ b/include/linux/smp.h | |||
@@ -84,6 +84,11 @@ int smp_call_function_any(const struct cpumask *mask, | |||
84 | void kick_all_cpus_sync(void); | 84 | void kick_all_cpus_sync(void); |
85 | 85 | ||
86 | /* | 86 | /* |
87 | * sends a 'pull timer' event to a remote CPU | ||
88 | */ | ||
89 | extern void smp_send_pull_timers(int cpu); | ||
90 | |||
91 | /* | ||
87 | * Generic and arch helpers | 92 | * Generic and arch helpers |
88 | */ | 93 | */ |
89 | #ifdef CONFIG_USE_GENERIC_SMP_HELPERS | 94 | #ifdef CONFIG_USE_GENERIC_SMP_HELPERS |
diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 2288fbdada16..c7f0c79b2cb5 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c | |||
@@ -48,6 +48,8 @@ | |||
48 | #include <linux/sched/rt.h> | 48 | #include <linux/sched/rt.h> |
49 | #include <linux/timer.h> | 49 | #include <linux/timer.h> |
50 | 50 | ||
51 | #include <litmus/debug_trace.h> | ||
52 | |||
51 | #include <asm/uaccess.h> | 53 | #include <asm/uaccess.h> |
52 | 54 | ||
53 | #include <trace/events/timer.h> | 55 | #include <trace/events/timer.h> |
@@ -1064,6 +1066,98 @@ hrtimer_start(struct hrtimer *timer, ktime_t tim, const enum hrtimer_mode mode) | |||
1064 | } | 1066 | } |
1065 | EXPORT_SYMBOL_GPL(hrtimer_start); | 1067 | EXPORT_SYMBOL_GPL(hrtimer_start); |
1066 | 1068 | ||
1069 | #if defined(CONFIG_ARCH_HAS_SEND_PULL_TIMERS) && defined(CONFIG_SMP) | ||
1070 | |||
1071 | /** | ||
1072 | * hrtimer_start_on_info_init - Initialize hrtimer_start_on_info | ||
1073 | */ | ||
1074 | void hrtimer_start_on_info_init(struct hrtimer_start_on_info *info) | ||
1075 | { | ||
1076 | memset(info, 0, sizeof(struct hrtimer_start_on_info)); | ||
1077 | atomic_set(&info->state, HRTIMER_START_ON_INACTIVE); | ||
1078 | } | ||
1079 | |||
1080 | /** | ||
1081 | * hrtimer_pull - PULL_TIMERS_VECTOR callback on remote cpu | ||
1082 | */ | ||
1083 | void hrtimer_pull(void) | ||
1084 | { | ||
1085 | struct hrtimer_cpu_base *base = &__get_cpu_var(hrtimer_bases); | ||
1086 | struct hrtimer_start_on_info *info; | ||
1087 | struct list_head *pos, *safe, list; | ||
1088 | |||
1089 | raw_spin_lock(&base->lock); | ||
1090 | list_replace_init(&base->to_pull, &list); | ||
1091 | raw_spin_unlock(&base->lock); | ||
1092 | |||
1093 | list_for_each_safe(pos, safe, &list) { | ||
1094 | info = list_entry(pos, struct hrtimer_start_on_info, list); | ||
1095 | TRACE("pulled timer 0x%x\n", info->timer); | ||
1096 | list_del(pos); | ||
1097 | hrtimer_start(info->timer, info->time, info->mode); | ||
1098 | } | ||
1099 | } | ||
1100 | |||
1101 | /** | ||
1102 | * hrtimer_start_on - trigger timer arming on remote cpu | ||
1103 | * @cpu: remote cpu | ||
1104 | * @info: save timer information for enqueuing on remote cpu | ||
1105 | * @timer: timer to be pulled | ||
1106 | * @time: expire time | ||
1107 | * @mode: timer mode | ||
1108 | */ | ||
1109 | int hrtimer_start_on(int cpu, struct hrtimer_start_on_info* info, | ||
1110 | struct hrtimer *timer, ktime_t time, | ||
1111 | const enum hrtimer_mode mode) | ||
1112 | { | ||
1113 | unsigned long flags; | ||
1114 | struct hrtimer_cpu_base* base; | ||
1115 | int in_use = 0, was_empty; | ||
1116 | |||
1117 | /* serialize access to info through the timer base */ | ||
1118 | lock_hrtimer_base(timer, &flags); | ||
1119 | |||
1120 | in_use = (atomic_read(&info->state) != HRTIMER_START_ON_INACTIVE); | ||
1121 | if (!in_use) { | ||
1122 | INIT_LIST_HEAD(&info->list); | ||
1123 | info->timer = timer; | ||
1124 | info->time = time; | ||
1125 | info->mode = mode; | ||
1126 | /* mark as in use */ | ||
1127 | atomic_set(&info->state, HRTIMER_START_ON_QUEUED); | ||
1128 | } | ||
1129 | |||
1130 | unlock_hrtimer_base(timer, &flags); | ||
1131 | |||
1132 | if (!in_use) { | ||
1133 | /* initiate pull */ | ||
1134 | preempt_disable(); | ||
1135 | if (cpu == smp_processor_id()) { | ||
1136 | /* start timer locally; we may get called | ||
1137 | * with rq->lock held, do not wake up anything | ||
1138 | */ | ||
1139 | TRACE("hrtimer_start_on: starting on local CPU\n"); | ||
1140 | __hrtimer_start_range_ns(info->timer, info->time, | ||
1141 | 0, info->mode, 0); | ||
1142 | } else { | ||
1143 | TRACE("hrtimer_start_on: pulling to remote CPU\n"); | ||
1144 | base = &per_cpu(hrtimer_bases, cpu); | ||
1145 | raw_spin_lock_irqsave(&base->lock, flags); | ||
1146 | was_empty = list_empty(&base->to_pull); | ||
1147 | list_add(&info->list, &base->to_pull); | ||
1148 | raw_spin_unlock_irqrestore(&base->lock, flags); | ||
1149 | if (was_empty) | ||
1150 | /* only send IPI if other no else | ||
1151 | * has done so already | ||
1152 | */ | ||
1153 | smp_send_pull_timers(cpu); | ||
1154 | } | ||
1155 | preempt_enable(); | ||
1156 | } | ||
1157 | return in_use; | ||
1158 | } | ||
1159 | |||
1160 | #endif | ||
1067 | 1161 | ||
1068 | /** | 1162 | /** |
1069 | * hrtimer_try_to_cancel - try to deactivate a timer | 1163 | * hrtimer_try_to_cancel - try to deactivate a timer |
@@ -1667,6 +1761,7 @@ static void __cpuinit init_hrtimers_cpu(int cpu) | |||
1667 | } | 1761 | } |
1668 | 1762 | ||
1669 | hrtimer_init_hres(cpu_base); | 1763 | hrtimer_init_hres(cpu_base); |
1764 | INIT_LIST_HEAD(&cpu_base->to_pull); | ||
1670 | } | 1765 | } |
1671 | 1766 | ||
1672 | #ifdef CONFIG_HOTPLUG_CPU | 1767 | #ifdef CONFIG_HOTPLUG_CPU |