aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2009-07-21 05:09:39 -0400
committerThomas Gleixner <tglx@linutronix.de>2009-07-21 08:35:07 -0400
commit591d2fb02ea80472d846c0b8507007806bdd69cc (patch)
treec7962a95a47bbdf664f15a504eff24c351f33613
parentaea1f7964ae6cba5eb419a958956deb9016b3341 (diff)
genirq: Delegate irq affinity setting to the irq thread
irq_set_thread_affinity() calls set_cpus_allowed_ptr() which might sleep, but irq_set_thread_affinity() is called with desc->lock held and can be called from hard interrupt context as well. The code has another bug as it does not hold a ref on the task struct as required by set_cpus_allowed_ptr(). Just set the IRQTF_AFFINITY bit in action->thread_flags. The next time the thread runs it migrates itself. Solves all of the above problems nicely. Add kerneldoc to irq_set_thread_affinity() while at it. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> LKML-Reference: <new-submission>
-rw-r--r--include/linux/interrupt.h2
-rw-r--r--kernel/irq/internals.h3
-rw-r--r--kernel/irq/manage.c50
-rw-r--r--kernel/irq/migration.c2
4 files changed, 48 insertions, 9 deletions
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index 2721f07e9354..88b056ac5629 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -64,11 +64,13 @@
64 * IRQTF_RUNTHREAD - signals that the interrupt handler thread should run 64 * IRQTF_RUNTHREAD - signals that the interrupt handler thread should run
65 * IRQTF_DIED - handler thread died 65 * IRQTF_DIED - handler thread died
66 * IRQTF_WARNED - warning "IRQ_WAKE_THREAD w/o thread_fn" has been printed 66 * IRQTF_WARNED - warning "IRQ_WAKE_THREAD w/o thread_fn" has been printed
67 * IRQTF_AFFINITY - irq thread is requested to adjust affinity
67 */ 68 */
68enum { 69enum {
69 IRQTF_RUNTHREAD, 70 IRQTF_RUNTHREAD,
70 IRQTF_DIED, 71 IRQTF_DIED,
71 IRQTF_WARNED, 72 IRQTF_WARNED,
73 IRQTF_AFFINITY,
72}; 74};
73 75
74typedef irqreturn_t (*irq_handler_t)(int, void *); 76typedef irqreturn_t (*irq_handler_t)(int, void *);
diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h
index 73468253143b..e70ed5592eb9 100644
--- a/kernel/irq/internals.h
+++ b/kernel/irq/internals.h
@@ -42,8 +42,7 @@ static inline void unregister_handler_proc(unsigned int irq,
42 42
43extern int irq_select_affinity_usr(unsigned int irq); 43extern int irq_select_affinity_usr(unsigned int irq);
44 44
45extern void 45extern void irq_set_thread_affinity(struct irq_desc *desc);
46irq_set_thread_affinity(struct irq_desc *desc, const struct cpumask *cpumask);
47 46
48/* 47/*
49 * Debugging printout: 48 * Debugging printout:
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 50da67672901..f0de36f13a44 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -80,14 +80,22 @@ int irq_can_set_affinity(unsigned int irq)
80 return 1; 80 return 1;
81} 81}
82 82
83void 83/**
84irq_set_thread_affinity(struct irq_desc *desc, const struct cpumask *cpumask) 84 * irq_set_thread_affinity - Notify irq threads to adjust affinity
85 * @desc: irq descriptor which has affitnity changed
86 *
87 * We just set IRQTF_AFFINITY and delegate the affinity setting
88 * to the interrupt thread itself. We can not call
89 * set_cpus_allowed_ptr() here as we hold desc->lock and this
90 * code can be called from hard interrupt context.
91 */
92void irq_set_thread_affinity(struct irq_desc *desc)
85{ 93{
86 struct irqaction *action = desc->action; 94 struct irqaction *action = desc->action;
87 95
88 while (action) { 96 while (action) {
89 if (action->thread) 97 if (action->thread)
90 set_cpus_allowed_ptr(action->thread, cpumask); 98 set_bit(IRQTF_AFFINITY, &action->thread_flags);
91 action = action->next; 99 action = action->next;
92 } 100 }
93} 101}
@@ -112,7 +120,7 @@ int irq_set_affinity(unsigned int irq, const struct cpumask *cpumask)
112 if (desc->status & IRQ_MOVE_PCNTXT) { 120 if (desc->status & IRQ_MOVE_PCNTXT) {
113 if (!desc->chip->set_affinity(irq, cpumask)) { 121 if (!desc->chip->set_affinity(irq, cpumask)) {
114 cpumask_copy(desc->affinity, cpumask); 122 cpumask_copy(desc->affinity, cpumask);
115 irq_set_thread_affinity(desc, cpumask); 123 irq_set_thread_affinity(desc);
116 } 124 }
117 } 125 }
118 else { 126 else {
@@ -122,7 +130,7 @@ int irq_set_affinity(unsigned int irq, const struct cpumask *cpumask)
122#else 130#else
123 if (!desc->chip->set_affinity(irq, cpumask)) { 131 if (!desc->chip->set_affinity(irq, cpumask)) {
124 cpumask_copy(desc->affinity, cpumask); 132 cpumask_copy(desc->affinity, cpumask);
125 irq_set_thread_affinity(desc, cpumask); 133 irq_set_thread_affinity(desc);
126 } 134 }
127#endif 135#endif
128 desc->status |= IRQ_AFFINITY_SET; 136 desc->status |= IRQ_AFFINITY_SET;
@@ -176,7 +184,7 @@ int irq_select_affinity_usr(unsigned int irq)
176 spin_lock_irqsave(&desc->lock, flags); 184 spin_lock_irqsave(&desc->lock, flags);
177 ret = setup_affinity(irq, desc); 185 ret = setup_affinity(irq, desc);
178 if (!ret) 186 if (!ret)
179 irq_set_thread_affinity(desc, desc->affinity); 187 irq_set_thread_affinity(desc);
180 spin_unlock_irqrestore(&desc->lock, flags); 188 spin_unlock_irqrestore(&desc->lock, flags);
181 189
182 return ret; 190 return ret;
@@ -444,6 +452,34 @@ static int irq_wait_for_interrupt(struct irqaction *action)
444} 452}
445 453
446/* 454/*
455 * Check whether we need to change the affinity of the interrupt thread.
456 */
457static void
458irq_thread_check_affinity(struct irq_desc *desc, struct irqaction *action)
459{
460 cpumask_var_t mask;
461
462 if (!test_and_clear_bit(IRQTF_AFFINITY, &action->thread_flags))
463 return;
464
465 /*
466 * In case we are out of memory we set IRQTF_AFFINITY again and
467 * try again next time
468 */
469 if (!alloc_cpumask_var(&mask, GFP_KERNEL)) {
470 set_bit(IRQTF_AFFINITY, &action->thread_flags);
471 return;
472 }
473
474 spin_lock_irq(&desc->lock);
475 cpumask_copy(mask, desc->affinity);
476 spin_unlock_irq(&desc->lock);
477
478 set_cpus_allowed_ptr(current, mask);
479 free_cpumask_var(mask);
480}
481
482/*
447 * Interrupt handler thread 483 * Interrupt handler thread
448 */ 484 */
449static int irq_thread(void *data) 485static int irq_thread(void *data)
@@ -458,6 +494,8 @@ static int irq_thread(void *data)
458 494
459 while (!irq_wait_for_interrupt(action)) { 495 while (!irq_wait_for_interrupt(action)) {
460 496
497 irq_thread_check_affinity(desc, action);
498
461 atomic_inc(&desc->threads_active); 499 atomic_inc(&desc->threads_active);
462 500
463 spin_lock_irq(&desc->lock); 501 spin_lock_irq(&desc->lock);
diff --git a/kernel/irq/migration.c b/kernel/irq/migration.c
index cfe767ca1545..fcb6c96f2627 100644
--- a/kernel/irq/migration.c
+++ b/kernel/irq/migration.c
@@ -45,7 +45,7 @@ void move_masked_irq(int irq)
45 < nr_cpu_ids)) 45 < nr_cpu_ids))
46 if (!desc->chip->set_affinity(irq, desc->pending_mask)) { 46 if (!desc->chip->set_affinity(irq, desc->pending_mask)) {
47 cpumask_copy(desc->affinity, desc->pending_mask); 47 cpumask_copy(desc->affinity, desc->pending_mask);
48 irq_set_thread_affinity(desc, desc->pending_mask); 48 irq_set_thread_affinity(desc);
49 } 49 }
50 50
51 cpumask_clear(desc->pending_mask); 51 cpumask_clear(desc->pending_mask);