Commit 591d2fb0 authored by Thomas Gleixner's avatar Thomas Gleixner
Browse files

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: default avatarThomas Gleixner <tglx@linutronix.de>
LKML-Reference: <new-submission>
parent aea1f796
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -64,11 +64,13 @@
 * IRQTF_RUNTHREAD - signals that the interrupt handler thread should run
 * IRQTF_RUNTHREAD - signals that the interrupt handler thread should run
 * IRQTF_DIED      - handler thread died
 * IRQTF_DIED      - handler thread died
 * IRQTF_WARNED    - warning "IRQ_WAKE_THREAD w/o thread_fn" has been printed
 * IRQTF_WARNED    - warning "IRQ_WAKE_THREAD w/o thread_fn" has been printed
 * IRQTF_AFFINITY  - irq thread is requested to adjust affinity
 */
 */
enum {
enum {
	IRQTF_RUNTHREAD,
	IRQTF_RUNTHREAD,
	IRQTF_DIED,
	IRQTF_DIED,
	IRQTF_WARNED,
	IRQTF_WARNED,
	IRQTF_AFFINITY,
};
};


typedef irqreturn_t (*irq_handler_t)(int, void *);
typedef irqreturn_t (*irq_handler_t)(int, void *);
+1 −2
Original line number Original line Diff line number Diff line
@@ -42,8 +42,7 @@ static inline void unregister_handler_proc(unsigned int irq,


extern int irq_select_affinity_usr(unsigned int irq);
extern int irq_select_affinity_usr(unsigned int irq);


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


/*
/*
 * Debugging printout:
 * Debugging printout:
+44 −6
Original line number Original line Diff line number Diff line
@@ -80,14 +80,22 @@ int irq_can_set_affinity(unsigned int irq)
	return 1;
	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;
	struct irqaction *action = desc->action;


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


	return ret;
	return ret;
@@ -443,6 +451,34 @@ static int irq_wait_for_interrupt(struct irqaction *action)
	return -1;
	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
 * Interrupt handler thread
 */
 */
@@ -458,6 +494,8 @@ static int irq_thread(void *data)


	while (!irq_wait_for_interrupt(action)) {
	while (!irq_wait_for_interrupt(action)) {


		irq_thread_check_affinity(desc, action);

		atomic_inc(&desc->threads_active);
		atomic_inc(&desc->threads_active);


		spin_lock_irq(&desc->lock);
		spin_lock_irq(&desc->lock);
+1 −1
Original line number Original line Diff line number Diff line
@@ -45,7 +45,7 @@ void move_masked_irq(int irq)
		   < nr_cpu_ids))
		   < nr_cpu_ids))
		if (!desc->chip->set_affinity(irq, desc->pending_mask)) {
		if (!desc->chip->set_affinity(irq, desc->pending_mask)) {
			cpumask_copy(desc->affinity, 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);
	cpumask_clear(desc->pending_mask);