diff options
| author | Thomas Gleixner <tglx@linutronix.de> | 2009-07-21 05:09:39 -0400 |
|---|---|---|
| committer | Thomas Gleixner <tglx@linutronix.de> | 2009-07-21 08:35:07 -0400 |
| commit | 591d2fb02ea80472d846c0b8507007806bdd69cc (patch) | |
| tree | c7962a95a47bbdf664f15a504eff24c351f33613 | |
| parent | aea1f7964ae6cba5eb419a958956deb9016b3341 (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.h | 2 | ||||
| -rw-r--r-- | kernel/irq/internals.h | 3 | ||||
| -rw-r--r-- | kernel/irq/manage.c | 50 | ||||
| -rw-r--r-- | kernel/irq/migration.c | 2 |
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 | */ |
| 68 | enum { | 69 | enum { |
| 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 | ||
| 74 | typedef irqreturn_t (*irq_handler_t)(int, void *); | 76 | typedef 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 | ||
| 43 | extern int irq_select_affinity_usr(unsigned int irq); | 43 | extern int irq_select_affinity_usr(unsigned int irq); |
| 44 | 44 | ||
| 45 | extern void | 45 | extern void irq_set_thread_affinity(struct irq_desc *desc); |
| 46 | irq_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 | ||
| 83 | void | 83 | /** |
| 84 | irq_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 | */ | ||
| 92 | void 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 | */ | ||
| 457 | static void | ||
| 458 | irq_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 | */ |
| 449 | static int irq_thread(void *data) | 485 | static 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); |
