Skip to content

Commit

Permalink
genirq: Delegate irq affinity setting to the irq thread
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Thomas Gleixner committed Jul 21, 2009
1 parent aea1f79 commit 591d2fb
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 9 deletions.
2 changes: 2 additions & 0 deletions include/linux/interrupt.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,13 @@
* IRQTF_RUNTHREAD - signals that the interrupt handler thread should run
* IRQTF_DIED - handler thread died
* IRQTF_WARNED - warning "IRQ_WAKE_THREAD w/o thread_fn" has been printed
* IRQTF_AFFINITY - irq thread is requested to adjust affinity
*/
enum {
IRQTF_RUNTHREAD,
IRQTF_DIED,
IRQTF_WARNED,
IRQTF_AFFINITY,
};

typedef irqreturn_t (*irq_handler_t)(int, void *);
Expand Down
3 changes: 1 addition & 2 deletions kernel/irq/internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ static inline void unregister_handler_proc(unsigned int irq,

extern int irq_select_affinity_usr(unsigned int irq);

extern void
irq_set_thread_affinity(struct irq_desc *desc, const struct cpumask *cpumask);
extern void irq_set_thread_affinity(struct irq_desc *desc);

/*
* Debugging printout:
Expand Down
50 changes: 44 additions & 6 deletions kernel/irq/manage.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,22 @@ int irq_can_set_affinity(unsigned int irq)
return 1;
}

void
irq_set_thread_affinity(struct irq_desc *desc, const struct cpumask *cpumask)
/**
* irq_set_thread_affinity - Notify irq threads to adjust affinity
* @desc: irq descriptor which has affitnity changed
*
* We just set IRQTF_AFFINITY and delegate the affinity setting
* to the interrupt thread itself. We can not call
* set_cpus_allowed_ptr() here as we hold desc->lock and this
* code can be called from hard interrupt context.
*/
void irq_set_thread_affinity(struct irq_desc *desc)
{
struct irqaction *action = desc->action;

while (action) {
if (action->thread)
set_cpus_allowed_ptr(action->thread, cpumask);
set_bit(IRQTF_AFFINITY, &action->thread_flags);
action = action->next;
}
}
Expand All @@ -112,7 +120,7 @@ int irq_set_affinity(unsigned int irq, const struct cpumask *cpumask)
if (desc->status & IRQ_MOVE_PCNTXT) {
if (!desc->chip->set_affinity(irq, cpumask)) {
cpumask_copy(desc->affinity, cpumask);
irq_set_thread_affinity(desc, cpumask);
irq_set_thread_affinity(desc);
}
}
else {
Expand All @@ -122,7 +130,7 @@ int irq_set_affinity(unsigned int irq, const struct cpumask *cpumask)
#else
if (!desc->chip->set_affinity(irq, cpumask)) {
cpumask_copy(desc->affinity, cpumask);
irq_set_thread_affinity(desc, cpumask);
irq_set_thread_affinity(desc);
}
#endif
desc->status |= IRQ_AFFINITY_SET;
Expand Down Expand Up @@ -176,7 +184,7 @@ int irq_select_affinity_usr(unsigned int irq)
spin_lock_irqsave(&desc->lock, flags);
ret = setup_affinity(irq, desc);
if (!ret)
irq_set_thread_affinity(desc, desc->affinity);
irq_set_thread_affinity(desc);
spin_unlock_irqrestore(&desc->lock, flags);

return ret;
Expand Down Expand Up @@ -443,6 +451,34 @@ static int irq_wait_for_interrupt(struct irqaction *action)
return -1;
}

/*
* Check whether we need to change the affinity of the interrupt thread.
*/
static void
irq_thread_check_affinity(struct irq_desc *desc, struct irqaction *action)
{
cpumask_var_t mask;

if (!test_and_clear_bit(IRQTF_AFFINITY, &action->thread_flags))
return;

/*
* In case we are out of memory we set IRQTF_AFFINITY again and
* try again next time
*/
if (!alloc_cpumask_var(&mask, GFP_KERNEL)) {
set_bit(IRQTF_AFFINITY, &action->thread_flags);
return;
}

spin_lock_irq(&desc->lock);
cpumask_copy(mask, desc->affinity);
spin_unlock_irq(&desc->lock);

set_cpus_allowed_ptr(current, mask);
free_cpumask_var(mask);
}

/*
* Interrupt handler thread
*/
Expand All @@ -458,6 +494,8 @@ static int irq_thread(void *data)

while (!irq_wait_for_interrupt(action)) {

irq_thread_check_affinity(desc, action);

atomic_inc(&desc->threads_active);

spin_lock_irq(&desc->lock);
Expand Down
2 changes: 1 addition & 1 deletion kernel/irq/migration.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ void move_masked_irq(int irq)
< nr_cpu_ids))
if (!desc->chip->set_affinity(irq, desc->pending_mask)) {
cpumask_copy(desc->affinity, desc->pending_mask);
irq_set_thread_affinity(desc, desc->pending_mask);
irq_set_thread_affinity(desc);
}

cpumask_clear(desc->pending_mask);
Expand Down

0 comments on commit 591d2fb

Please sign in to comment.