aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFelipe Cerqueira <felipec@mpi-sws.org>2013-02-11 10:36:35 -0500
committerBjoern Brandenburg <bbb@mpi-sws.org>2014-06-07 05:30:32 -0400
commit14e6b5665983e393f6c073f635f2aaf947ff7448 (patch)
tree1abbd5409f3476c5abd45d015e851be5c03f08fc
parentdacc06b7f1cd1086ec08704eafea0e1009a69108 (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/Kconfig3
-rw-r--r--arch/x86/Kconfig3
-rw-r--r--arch/x86/include/asm/entry_arch.h1
-rw-r--r--arch/x86/include/asm/hw_irq.h3
-rw-r--r--arch/x86/include/asm/irq_vectors.h6
-rw-r--r--arch/x86/kernel/entry_64.S2
-rw-r--r--arch/x86/kernel/irqinit.c3
-rw-r--r--arch/x86/kernel/smp.c23
-rw-r--r--include/linux/hrtimer.h32
-rw-r--r--include/linux/smp.h5
-rw-r--r--kernel/hrtimer.c95
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"
2249config ARCH_HAS_FEATHER_TRACE 2249config ARCH_HAS_FEATHER_TRACE
2250 def_bool n 2250 def_bool n
2251 2251
2252config ARCH_HAS_SEND_PULL_TIMERS
2253 def_bool n
2254
2252source "litmus/Kconfig" 2255source "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"
2350config ARCH_HAS_FEATHER_TRACE 2350config ARCH_HAS_FEATHER_TRACE
2351 def_bool y 2351 def_bool y
2352 2352
2353config ARCH_HAS_SEND_PULL_TIMERS
2354 def_bool y
2355
2353source "litmus/Kconfig" 2356source "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 @@
13BUILD_INTERRUPT(reschedule_interrupt,RESCHEDULE_VECTOR) 13BUILD_INTERRUPT(reschedule_interrupt,RESCHEDULE_VECTOR)
14BUILD_INTERRUPT(call_function_interrupt,CALL_FUNCTION_VECTOR) 14BUILD_INTERRUPT(call_function_interrupt,CALL_FUNCTION_VECTOR)
15BUILD_INTERRUPT(call_function_single_interrupt,CALL_FUNCTION_SINGLE_VECTOR) 15BUILD_INTERRUPT(call_function_single_interrupt,CALL_FUNCTION_SINGLE_VECTOR)
16BUILD_INTERRUPT(pull_timers_interrupt,PULL_TIMERS_VECTOR)
16BUILD_INTERRUPT(irq_move_cleanup_interrupt,IRQ_MOVE_CLEANUP_VECTOR) 17BUILD_INTERRUPT(irq_move_cleanup_interrupt,IRQ_MOVE_CLEANUP_VECTOR)
17BUILD_INTERRUPT(reboot_interrupt,REBOOT_VECTOR) 18BUILD_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);
77extern void call_function_interrupt(void); 77extern void call_function_interrupt(void);
78extern void call_function_single_interrupt(void); 78extern void call_function_single_interrupt(void);
79 79
80extern 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))
82extern unsigned long io_apic_irqs; 84extern unsigned long io_apic_irqs;
@@ -166,6 +168,7 @@ extern asmlinkage void smp_irq_move_cleanup_interrupt(void);
166extern void smp_reschedule_interrupt(struct pt_regs *); 168extern void smp_reschedule_interrupt(struct pt_regs *);
167extern void smp_call_function_interrupt(struct pt_regs *); 169extern void smp_call_function_interrupt(struct pt_regs *);
168extern void smp_call_function_single_interrupt(struct pt_regs *); 170extern void smp_call_function_single_interrupt(struct pt_regs *);
171extern void smp_pull_timers_interrupt(struct pt_regs *);
169#ifdef CONFIG_X86_32 172#ifdef CONFIG_X86_32
170extern void smp_invalidate_interrupt(struct pt_regs *); 173extern 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
1184apicinterrupt RESCHEDULE_VECTOR \ 1184apicinterrupt RESCHEDULE_VECTOR \
1185 reschedule_interrupt smp_reschedule_interrupt 1185 reschedule_interrupt smp_reschedule_interrupt
1186apicinterrupt PULL_TIMERS_VECTOR \
1187 pull_timers_interrupt smp_pull_timers_interrupt
1186#endif 1188#endif
1187 1189
1188apicinterrupt ERROR_APIC_VECTOR \ 1190apicinterrupt 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 */
169void 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
300extern void hrtimer_pull(void);
301
302void 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
288struct smp_ops smp_ops = { 311struct 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 */
180struct hrtimer_cpu_base { 181struct 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 */
211struct 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
196static inline void hrtimer_set_expires(struct hrtimer *timer, ktime_t time) 221static 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
395extern void hrtimer_start_on_info_init(struct hrtimer_start_on_info *info);
396extern 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
369extern int hrtimer_cancel(struct hrtimer *timer); 401extern int hrtimer_cancel(struct hrtimer *timer);
370extern int hrtimer_try_to_cancel(struct hrtimer *timer); 402extern 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,
84void kick_all_cpus_sync(void); 84void kick_all_cpus_sync(void);
85 85
86/* 86/*
87 * sends a 'pull timer' event to a remote CPU
88 */
89extern 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}
1065EXPORT_SYMBOL_GPL(hrtimer_start); 1067EXPORT_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 */
1074void 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 */
1083void 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 */
1109int 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