diff options
| author | Thomas Gleixner <tglx@linutronix.de> | 2008-11-07 13:18:30 +0100 | 
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2008-11-09 22:23:49 +0100 | 
| commit | f6d87f4bd259cf33e092cd1a8fde05f291c47af1 (patch) | |
| tree | abaa66af3c80fb18a20004b8d97261c680551792 | |
| parent | 8b805ef617cf0e02f6d18b891f8deb6246421b01 (diff) | |
| download | olio-linux-3.10-f6d87f4bd259cf33e092cd1a8fde05f291c47af1.tar.xz olio-linux-3.10-f6d87f4bd259cf33e092cd1a8fde05f291c47af1.zip  | |
genirq: keep affinities set from userspace across free/request_irq()
Impact: preserve user-modified affinities on interrupts
Kumar Galak noticed that commit
18404756765c713a0be4eb1082920c04822ce588 (genirq: Expose default irq
affinity mask (take 3))
overrides an already set affinity setting across a free /
request_irq(). Happens e.g. with ifdown/ifup of a network device.
Change the logic to mark the affinities as set and keep them
intact. This also fixes the unlocked access to irq_desc in
irq_select_affinity() when called from irq_affinity_proc_write()
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
| -rw-r--r-- | include/linux/irq.h | 8 | ||||
| -rw-r--r-- | kernel/irq/internals.h | 2 | ||||
| -rw-r--r-- | kernel/irq/manage.c | 58 | ||||
| -rw-r--r-- | kernel/irq/migration.c | 11 | ||||
| -rw-r--r-- | kernel/irq/proc.c | 2 | 
5 files changed, 53 insertions, 28 deletions
diff --git a/include/linux/irq.h b/include/linux/irq.h index d058c57be02..36b186eb318 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -63,7 +63,8 @@ typedef	void (*irq_flow_handler_t)(unsigned int irq,  #define IRQ_MOVE_PENDING	0x00200000	/* need to re-target IRQ destination */  #define IRQ_NO_BALANCING	0x00400000	/* IRQ is excluded from balancing */  #define IRQ_SPURIOUS_DISABLED	0x00800000	/* IRQ was disabled by the spurious trap */ -#define IRQ_MOVE_PCNTXT	0x01000000	/* IRQ migration from process context */ +#define IRQ_MOVE_PCNTXT		0x01000000	/* IRQ migration from process context */ +#define IRQ_AFFINITY_SET	0x02000000	/* IRQ affinity was set from userspace*/  #ifdef CONFIG_IRQ_PER_CPU  # define CHECK_IRQ_PER_CPU(var) ((var) & IRQ_PER_CPU) @@ -210,7 +211,6 @@ extern int setup_irq(unsigned int irq, struct irqaction *new);  #ifdef CONFIG_GENERIC_PENDING_IRQ -void set_pending_irq(unsigned int irq, cpumask_t mask);  void move_native_irq(int irq);  void move_masked_irq(int irq); @@ -228,10 +228,6 @@ static inline void move_masked_irq(int irq)  {  } -static inline void set_pending_irq(unsigned int irq, cpumask_t mask) -{ -} -  #endif /* CONFIG_GENERIC_PENDING_IRQ */  #else /* CONFIG_SMP */ diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index c9767e64198..64c1c7253da 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -25,6 +25,8 @@ static inline void unregister_handler_proc(unsigned int irq,  					   struct irqaction *action) { }  #endif +extern int irq_select_affinity_usr(unsigned int irq); +  /*   * Debugging printout:   */ diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index c498a1b8c62..634a2a95510 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -82,24 +82,27 @@ int irq_can_set_affinity(unsigned int irq)  int irq_set_affinity(unsigned int irq, cpumask_t cpumask)  {  	struct irq_desc *desc = irq_to_desc(irq); +	unsigned long flags;  	if (!desc->chip->set_affinity)  		return -EINVAL; +	spin_lock_irqsave(&desc->lock, flags); +  #ifdef CONFIG_GENERIC_PENDING_IRQ  	if (desc->status & IRQ_MOVE_PCNTXT || desc->status & IRQ_DISABLED) { -		unsigned long flags; - -		spin_lock_irqsave(&desc->lock, flags);  		desc->affinity = cpumask;  		desc->chip->set_affinity(irq, cpumask); -		spin_unlock_irqrestore(&desc->lock, flags); -	} else -		set_pending_irq(irq, cpumask); +	} else { +		desc->status |= IRQ_MOVE_PENDING; +		desc->pending_mask = cpumask; +	}  #else  	desc->affinity = cpumask;  	desc->chip->set_affinity(irq, cpumask);  #endif +	desc->status |= IRQ_AFFINITY_SET; +	spin_unlock_irqrestore(&desc->lock, flags);  	return 0;  } @@ -107,24 +110,59 @@ int irq_set_affinity(unsigned int irq, cpumask_t cpumask)  /*   * Generic version of the affinity autoselector.   */ -int irq_select_affinity(unsigned int irq) +int do_irq_select_affinity(unsigned int irq, struct irq_desc *desc)  {  	cpumask_t mask; -	struct irq_desc *desc;  	if (!irq_can_set_affinity(irq))  		return 0;  	cpus_and(mask, cpu_online_map, irq_default_affinity); -	desc = irq_to_desc(irq); +	/* +	 * Preserve an userspace affinity setup, but make sure that +	 * one of the targets is online. +	 */ +	if (desc->status & IRQ_AFFINITY_SET) { +		if (cpus_intersects(desc->affinity, cpu_online_map)) +			mask = desc->affinity; +		else +			desc->status &= ~IRQ_AFFINITY_SET; +	} +  	desc->affinity = mask;  	desc->chip->set_affinity(irq, mask);  	return 0;  } +#else +static inline int do_irq_select_affinity(unsigned int irq, struct irq_desc *d) +{ +	return irq_select_affinity(irq); +}  #endif +/* + * Called when affinity is set via /proc/irq + */ +int irq_select_affinity_usr(unsigned int irq) +{ +	struct irq_desc *desc = irq_to_desc(irq); +	unsigned long flags; +	int ret; + +	spin_lock_irqsave(&desc->lock, flags); +	ret = do_irq_select_affinity(irq, desc); +	spin_unlock_irqrestore(&desc->lock, flags); + +	return ret; +} + +#else +static inline int do_select_irq_affinity(int irq, struct irq_desc *desc) +{ +	return 0; +}  #endif  /** @@ -446,7 +484,7 @@ __setup_irq(unsigned int irq, struct irq_desc * desc, struct irqaction *new)  			desc->depth = 1;  		/* Set default affinity mask once everything is setup */ -		irq_select_affinity(irq); +		do_irq_select_affinity(irq, desc);  	} else if ((new->flags & IRQF_TRIGGER_MASK)  			&& (new->flags & IRQF_TRIGGER_MASK) diff --git a/kernel/irq/migration.c b/kernel/irq/migration.c index 90b920d3f52..9db681d9581 100644 --- a/kernel/irq/migration.c +++ b/kernel/irq/migration.c @@ -1,17 +1,6 @@  #include <linux/irq.h> -void set_pending_irq(unsigned int irq, cpumask_t mask) -{ -	struct irq_desc *desc = irq_to_desc(irq); -	unsigned long flags; - -	spin_lock_irqsave(&desc->lock, flags); -	desc->status |= IRQ_MOVE_PENDING; -	desc->pending_mask = mask; -	spin_unlock_irqrestore(&desc->lock, flags); -} -  void move_masked_irq(int irq)  {  	struct irq_desc *desc = irq_to_desc(irq); diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c index 4d161c70ba5..d257e7d6a8a 100644 --- a/kernel/irq/proc.c +++ b/kernel/irq/proc.c @@ -62,7 +62,7 @@ static ssize_t irq_affinity_proc_write(struct file *file,  	if (!cpus_intersects(new_value, cpu_online_map))  		/* Special case for empty set - allow the architecture  		   code to set default SMP affinity. */ -		return irq_select_affinity(irq) ? -EINVAL : count; +		return irq_select_affinity_usr(irq) ? -EINVAL : count;  	irq_set_affinity(irq, new_value);  |