diff options
-rw-r--r-- | include/litmus/nvidia_info.h | 3 | ||||
-rw-r--r-- | kernel/softirq.c | 150 | ||||
-rw-r--r-- | litmus/Kconfig | 11 | ||||
-rw-r--r-- | litmus/nvidia_info.c | 16 |
4 files changed, 158 insertions, 22 deletions
diff --git a/include/litmus/nvidia_info.h b/include/litmus/nvidia_info.h index f28d8f0a7f40..c5bf3dc0a210 100644 --- a/include/litmus/nvidia_info.h +++ b/include/litmus/nvidia_info.h | |||
@@ -9,5 +9,8 @@ int is_nvidia_func(void *func); | |||
9 | 9 | ||
10 | int is_nvidia_in_callstack(void); | 10 | int is_nvidia_in_callstack(void); |
11 | 11 | ||
12 | // Returns the Nvidia device # associated with the | ||
13 | // provided tasklet. | ||
14 | u32 get_nv_device_num(const struct tasklet_struct *t); | ||
12 | 15 | ||
13 | #endif | 16 | #endif |
diff --git a/kernel/softirq.c b/kernel/softirq.c index 266cea2b9721..6f977ebc2770 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c | |||
@@ -29,6 +29,10 @@ | |||
29 | #include <trace/events/irq.h> | 29 | #include <trace/events/irq.h> |
30 | 30 | ||
31 | #include <asm/irq.h> | 31 | #include <asm/irq.h> |
32 | |||
33 | #include <litmus/sched_trace.h> | ||
34 | #include <litmus/nvidia_info.h> | ||
35 | |||
32 | /* | 36 | /* |
33 | - No shared variables, all the data are CPU local. | 37 | - No shared variables, all the data are CPU local. |
34 | - If a softirq needs serialization, let it serialize itself | 38 | - If a softirq needs serialization, let it serialize itself |
@@ -54,7 +58,7 @@ EXPORT_SYMBOL(irq_stat); | |||
54 | 58 | ||
55 | static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp; | 59 | static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp; |
56 | 60 | ||
57 | static DEFINE_PER_CPU(struct task_struct *, ksoftirqd); | 61 | static DEFINE_PER_CPU(struct task_struct *, ksoftirqd) = NULL; |
58 | 62 | ||
59 | char *softirq_to_name[NR_SOFTIRQS] = { | 63 | char *softirq_to_name[NR_SOFTIRQS] = { |
60 | "HI", "TIMER", "NET_TX", "NET_RX", "BLOCK", "BLOCK_IOPOLL", | 64 | "HI", "TIMER", "NET_TX", "NET_RX", "BLOCK", "BLOCK_IOPOLL", |
@@ -177,6 +181,7 @@ void local_bh_enable_ip(unsigned long ip) | |||
177 | } | 181 | } |
178 | EXPORT_SYMBOL(local_bh_enable_ip); | 182 | EXPORT_SYMBOL(local_bh_enable_ip); |
179 | 183 | ||
184 | |||
180 | /* | 185 | /* |
181 | * We restart softirq processing MAX_SOFTIRQ_RESTART times, | 186 | * We restart softirq processing MAX_SOFTIRQ_RESTART times, |
182 | * and we fall back to softirqd after that. | 187 | * and we fall back to softirqd after that. |
@@ -186,35 +191,36 @@ EXPORT_SYMBOL(local_bh_enable_ip); | |||
186 | * we want to handle softirqs as soon as possible, but they | 191 | * we want to handle softirqs as soon as possible, but they |
187 | * should not be able to lock up the box. | 192 | * should not be able to lock up the box. |
188 | */ | 193 | */ |
194 | #ifndef LITMUS_THREAD_ALL_SOFTIRQ | ||
189 | #define MAX_SOFTIRQ_RESTART 10 | 195 | #define MAX_SOFTIRQ_RESTART 10 |
196 | #else | ||
197 | #define MAX_SOFTIRQ_RESTART 20 | ||
198 | #endif | ||
190 | 199 | ||
191 | asmlinkage void __do_softirq(void) | 200 | static void ____do_softirq(void) |
192 | { | 201 | { |
193 | struct softirq_action *h; | ||
194 | __u32 pending; | 202 | __u32 pending; |
195 | int max_restart = MAX_SOFTIRQ_RESTART; | 203 | |
204 | struct softirq_action *h; | ||
196 | int cpu; | 205 | int cpu; |
197 | 206 | ||
198 | pending = local_softirq_pending(); | 207 | pending = local_softirq_pending(); |
208 | |||
199 | account_system_vtime(current); | 209 | account_system_vtime(current); |
200 | 210 | ||
201 | __local_bh_disable((unsigned long)__builtin_return_address(0)); | ||
202 | lockdep_softirq_enter(); | ||
203 | |||
204 | cpu = smp_processor_id(); | 211 | cpu = smp_processor_id(); |
205 | restart: | ||
206 | /* Reset the pending bitmask before enabling irqs */ | ||
207 | set_softirq_pending(0); | ||
208 | 212 | ||
213 | set_softirq_pending(0); | ||
214 | |||
209 | local_irq_enable(); | 215 | local_irq_enable(); |
210 | 216 | ||
211 | h = softirq_vec; | 217 | h = softirq_vec; |
212 | 218 | ||
213 | do { | 219 | do { |
214 | if (pending & 1) { | 220 | if (pending & 1) { |
215 | int prev_count = preempt_count(); | 221 | int prev_count = preempt_count(); |
216 | kstat_incr_softirqs_this_cpu(h - softirq_vec); | 222 | kstat_incr_softirqs_this_cpu(h - softirq_vec); |
217 | 223 | ||
218 | trace_softirq_entry(h, softirq_vec); | 224 | trace_softirq_entry(h, softirq_vec); |
219 | h->action(h); | 225 | h->action(h); |
220 | trace_softirq_exit(h, softirq_vec); | 226 | trace_softirq_exit(h, softirq_vec); |
@@ -226,26 +232,68 @@ restart: | |||
226 | h->action, prev_count, preempt_count()); | 232 | h->action, prev_count, preempt_count()); |
227 | preempt_count() = prev_count; | 233 | preempt_count() = prev_count; |
228 | } | 234 | } |
229 | 235 | ||
230 | rcu_bh_qs(cpu); | 236 | rcu_bh_qs(cpu); |
231 | } | 237 | } |
232 | h++; | 238 | h++; |
233 | pending >>= 1; | 239 | pending >>= 1; |
234 | } while (pending); | 240 | } while (pending); |
235 | 241 | ||
236 | local_irq_disable(); | 242 | local_irq_disable(); |
243 | } | ||
244 | |||
245 | static void ___do_softirq(void) | ||
246 | { | ||
247 | __u32 pending; | ||
248 | |||
249 | //struct softirq_action *h; | ||
250 | int max_restart = MAX_SOFTIRQ_RESTART; | ||
251 | //int cpu; | ||
252 | |||
253 | pending = local_softirq_pending(); | ||
254 | |||
255 | restart: | ||
256 | ____do_softirq(); | ||
237 | 257 | ||
238 | pending = local_softirq_pending(); | 258 | pending = local_softirq_pending(); |
239 | if (pending && --max_restart) | 259 | if (pending && --max_restart) |
240 | goto restart; | 260 | goto restart; |
241 | 261 | ||
242 | if (pending) | 262 | if (pending) |
263 | { | ||
243 | wakeup_softirqd(); | 264 | wakeup_softirqd(); |
265 | } | ||
266 | } | ||
244 | 267 | ||
268 | asmlinkage void __do_softirq(void) | ||
269 | { | ||
270 | #ifdef LITMUS_THREAD_ALL_SOFTIRQ | ||
271 | /* Skip straight to wakeup_softirqd() if we're using | ||
272 | LITMUS_THREAD_ALL_SOFTIRQ. This will cause ALL softirqs | ||
273 | and tasklets be threaded, either on Litmus klitirqd | ||
274 | threads or Linux ksoftirqd threads. */ | ||
275 | struct task_struct *tsk = __get_cpu_var(ksoftirqd); | ||
276 | |||
277 | if(tsk) | ||
278 | { | ||
279 | if(local_softirq_pending()) | ||
280 | wakeup_softirqd(); | ||
281 | return; | ||
282 | } | ||
283 | #endif | ||
284 | |||
285 | /* | ||
286 | * 'immediate' softirq execution: | ||
287 | */ | ||
288 | __local_bh_disable((unsigned long)__builtin_return_address(0)); | ||
289 | lockdep_softirq_enter(); | ||
290 | |||
291 | ___do_softirq(); | ||
292 | |||
245 | lockdep_softirq_exit(); | 293 | lockdep_softirq_exit(); |
246 | 294 | ||
247 | account_system_vtime(current); | 295 | account_system_vtime(current); |
248 | _local_bh_enable(); | 296 | _local_bh_enable(); |
249 | } | 297 | } |
250 | 298 | ||
251 | #ifndef __ARCH_HAS_DO_SOFTIRQ | 299 | #ifndef __ARCH_HAS_DO_SOFTIRQ |
@@ -361,6 +409,26 @@ void __tasklet_schedule(struct tasklet_struct *t) | |||
361 | { | 409 | { |
362 | unsigned long flags; | 410 | unsigned long flags; |
363 | 411 | ||
412 | if(is_nvidia_func(t->func)) | ||
413 | { | ||
414 | u32 nvidiaDevice = get_nv_device_num(t); | ||
415 | |||
416 | TRACE("%s: Handling Nvidia tasklet for device %u\n", | ||
417 | __FUNCTION__, nvidiaDevice); | ||
418 | |||
419 | BUG_ON(nvidiaDevice > 7); /* for Bonham. remove for general support. */ | ||
420 | |||
421 | /* | ||
422 | TODO: | ||
423 | 1) Ask Litmus which task owns GPU <nvidiaDevice>. (API to be defined.) | ||
424 | 2) If there is an owner, set t->owner to the owner's task struct. | ||
425 | 3) Call litmus_tasklet_schedule() and return (don't execute the rest | ||
426 | of __tasklet_schedule()). | ||
427 | 4) If there is no owner, then continue on to the regular | ||
428 | tasklet processing below. | ||
429 | */ | ||
430 | } | ||
431 | |||
364 | local_irq_save(flags); | 432 | local_irq_save(flags); |
365 | t->next = NULL; | 433 | t->next = NULL; |
366 | *__get_cpu_var(tasklet_vec).tail = t; | 434 | *__get_cpu_var(tasklet_vec).tail = t; |
@@ -375,6 +443,24 @@ void __tasklet_hi_schedule(struct tasklet_struct *t) | |||
375 | { | 443 | { |
376 | unsigned long flags; | 444 | unsigned long flags; |
377 | 445 | ||
446 | if(is_nvidia_func(t->func)) | ||
447 | { | ||
448 | u32 nvidiaDevice = get_nv_device_num(t); | ||
449 | |||
450 | TRACE("%s: Handling Nvidia tasklet for device %u\n", | ||
451 | __FUNCTION__, nvidiaDevice); | ||
452 | |||
453 | /* | ||
454 | TODO: | ||
455 | 1) Ask Litmus which task owns GPU <nvidiaDevice>. (API to be defined.) | ||
456 | 2) If there is an owner, set t->owner to the owner's task struct. | ||
457 | 3) Call litmus_tasklet_hi_schedule() and return (don't execute the rest | ||
458 | of __tasklet_schedule()). | ||
459 | 4) If there is no owner, then continue on to the regular | ||
460 | tasklet processing below. | ||
461 | */ | ||
462 | } | ||
463 | |||
378 | local_irq_save(flags); | 464 | local_irq_save(flags); |
379 | t->next = NULL; | 465 | t->next = NULL; |
380 | *__get_cpu_var(tasklet_hi_vec).tail = t; | 466 | *__get_cpu_var(tasklet_hi_vec).tail = t; |
@@ -389,6 +475,24 @@ void __tasklet_hi_schedule_first(struct tasklet_struct *t) | |||
389 | { | 475 | { |
390 | BUG_ON(!irqs_disabled()); | 476 | BUG_ON(!irqs_disabled()); |
391 | 477 | ||
478 | if(is_nvidia_func(t->func)) | ||
479 | { | ||
480 | u32 nvidiaDevice = get_nv_device_num(t); | ||
481 | |||
482 | TRACE("%s: Handling Nvidia tasklet for device %u\n", | ||
483 | __FUNCTION__, nvidiaDevice); | ||
484 | |||
485 | /* | ||
486 | TODO: | ||
487 | 1) Ask Litmus which task owns GPU <nvidiaDevice>. (API to be defined.) | ||
488 | 2) If there is an owner, set t->owner to the owner's task struct. | ||
489 | 3) Call litmus_tasklet_hi_schedule first() and return | ||
490 | (don't execute the rest of __tasklet_schedule()). | ||
491 | 4) If there is no owner, then continue on to the regular | ||
492 | tasklet processing below. | ||
493 | */ | ||
494 | } | ||
495 | |||
392 | t->next = __get_cpu_var(tasklet_hi_vec).head; | 496 | t->next = __get_cpu_var(tasklet_hi_vec).head; |
393 | __get_cpu_var(tasklet_hi_vec).head = t; | 497 | __get_cpu_var(tasklet_hi_vec).head = t; |
394 | __raise_softirq_irqoff(HI_SOFTIRQ); | 498 | __raise_softirq_irqoff(HI_SOFTIRQ); |
@@ -698,6 +802,8 @@ void __init softirq_init(void) | |||
698 | 802 | ||
699 | static int run_ksoftirqd(void * __bind_cpu) | 803 | static int run_ksoftirqd(void * __bind_cpu) |
700 | { | 804 | { |
805 | unsigned long flags; | ||
806 | |||
701 | set_current_state(TASK_INTERRUPTIBLE); | 807 | set_current_state(TASK_INTERRUPTIBLE); |
702 | 808 | ||
703 | while (!kthread_should_stop()) { | 809 | while (!kthread_should_stop()) { |
@@ -716,7 +822,11 @@ static int run_ksoftirqd(void * __bind_cpu) | |||
716 | don't process */ | 822 | don't process */ |
717 | if (cpu_is_offline((long)__bind_cpu)) | 823 | if (cpu_is_offline((long)__bind_cpu)) |
718 | goto wait_to_die; | 824 | goto wait_to_die; |
719 | do_softirq(); | 825 | |
826 | local_irq_save(flags); | ||
827 | ____do_softirq(); | ||
828 | local_irq_restore(flags); | ||
829 | |||
720 | preempt_enable_no_resched(); | 830 | preempt_enable_no_resched(); |
721 | cond_resched(); | 831 | cond_resched(); |
722 | preempt_disable(); | 832 | preempt_disable(); |
diff --git a/litmus/Kconfig b/litmus/Kconfig index 42c5ff67c8c1..3e5ed05b0ccf 100644 --- a/litmus/Kconfig +++ b/litmus/Kconfig | |||
@@ -184,6 +184,17 @@ endmenu | |||
184 | 184 | ||
185 | menu "Interrupt Handling" | 185 | menu "Interrupt Handling" |
186 | 186 | ||
187 | config LITMUS_THREAD_ALL_SOFTIRQ | ||
188 | bool "Process all softirqs in ksoftirqd threads." | ||
189 | default n | ||
190 | help | ||
191 | Thread all softirqs in ksoftirqd daemon threads, | ||
192 | similar to PREEMPT_RT. I/O throughput will | ||
193 | will drop with this enabled, but latencies | ||
194 | due to interrupts will be reduced. | ||
195 | |||
196 | If unsure, say No. | ||
197 | |||
187 | config LITMUS_SOFTIRQD | 198 | config LITMUS_SOFTIRQD |
188 | bool "Spawn klitirqd interrupt handling threads." | 199 | bool "Spawn klitirqd interrupt handling threads." |
189 | depends on LITMUS_LOCKING | 200 | depends on LITMUS_LOCKING |
diff --git a/litmus/nvidia_info.c b/litmus/nvidia_info.c index 05f8356a8117..c16879f54363 100644 --- a/litmus/nvidia_info.c +++ b/litmus/nvidia_info.c | |||
@@ -14,7 +14,7 @@ int init_nvidia_info(void) | |||
14 | 14 | ||
15 | if(nvidia_mod != NULL) | 15 | if(nvidia_mod != NULL) |
16 | { | 16 | { |
17 | TRACE("%s : Found NVIDIA module. Core Code: %x to %x\n", __FUNCTION__, | 17 | TRACE("%s : Found NVIDIA module. Core Code: %p to %p\n", __FUNCTION__, |
18 | (void*)(nvidia_mod->module_core), | 18 | (void*)(nvidia_mod->module_core), |
19 | (void*)(nvidia_mod->module_core) + nvidia_mod->core_size); | 19 | (void*)(nvidia_mod->module_core) + nvidia_mod->core_size); |
20 | return(0); | 20 | return(0); |
@@ -35,7 +35,7 @@ int is_nvidia_func(void *func_addr) | |||
35 | { | 35 | { |
36 | ret = within_module_core((long unsigned int)func_addr, nvidia_mod); | 36 | ret = within_module_core((long unsigned int)func_addr, nvidia_mod); |
37 | 37 | ||
38 | TRACE("%s : %x is in NVIDIA module : %d\n", | 38 | TRACE("%s : %p is in NVIDIA module : %d\n", |
39 | __FUNCTION__, func_addr, ret); | 39 | __FUNCTION__, func_addr, ret); |
40 | } | 40 | } |
41 | else | 41 | else |
@@ -53,3 +53,15 @@ int is_nvidia_in_callstack(void) | |||
53 | TRACE("%s : NOT IMPLEMENTED!\n", __FUNCTION__); | 53 | TRACE("%s : NOT IMPLEMENTED!\n", __FUNCTION__); |
54 | return(1); | 54 | return(1); |
55 | } | 55 | } |
56 | |||
57 | |||
58 | u32 get_nv_device_num(const struct tasklet_struct *t) | ||
59 | { | ||
60 | // offset determined though observed behavior of the NV driver. | ||
61 | const int DEVICE_NUM_OFFSET = 0x278; | ||
62 | |||
63 | void* state = (void*)(t->data); | ||
64 | void* device_num_ptr = state + DEVICE_NUM_OFFSET; | ||
65 | |||
66 | return(*((u32*)device_num_ptr)); | ||
67 | } | ||