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 /kernel/irq/manage.c | |
| 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>
Diffstat (limited to 'kernel/irq/manage.c')
| -rw-r--r-- | kernel/irq/manage.c | 58 | 
1 files changed, 48 insertions, 10 deletions
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)  |