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>

authored by Thomas Gleixner and committed by Ingo Molnar f6d87f4b 8b805ef6

+53 -28
+2 -6
include/linux/irq.h
··· 63 63 #define IRQ_MOVE_PENDING 0x00200000 /* need to re-target IRQ destination */ 64 64 #define IRQ_NO_BALANCING 0x00400000 /* IRQ is excluded from balancing */ 65 65 #define IRQ_SPURIOUS_DISABLED 0x00800000 /* IRQ was disabled by the spurious trap */ 66 - #define IRQ_MOVE_PCNTXT 0x01000000 /* IRQ migration from process context */ 66 + #define IRQ_MOVE_PCNTXT 0x01000000 /* IRQ migration from process context */ 67 + #define IRQ_AFFINITY_SET 0x02000000 /* IRQ affinity was set from userspace*/ 67 68 68 69 #ifdef CONFIG_IRQ_PER_CPU 69 70 # define CHECK_IRQ_PER_CPU(var) ((var) & IRQ_PER_CPU) ··· 211 210 212 211 #ifdef CONFIG_GENERIC_PENDING_IRQ 213 212 214 - void set_pending_irq(unsigned int irq, cpumask_t mask); 215 213 void move_native_irq(int irq); 216 214 void move_masked_irq(int irq); 217 215 ··· 225 225 } 226 226 227 227 static inline void move_masked_irq(int irq) 228 - { 229 - } 230 - 231 - static inline void set_pending_irq(unsigned int irq, cpumask_t mask) 232 228 { 233 229 } 234 230
+2
kernel/irq/internals.h
··· 25 25 struct irqaction *action) { } 26 26 #endif 27 27 28 + extern int irq_select_affinity_usr(unsigned int irq); 29 + 28 30 /* 29 31 * Debugging printout: 30 32 */
+48 -10
kernel/irq/manage.c
··· 82 82 int irq_set_affinity(unsigned int irq, cpumask_t cpumask) 83 83 { 84 84 struct irq_desc *desc = irq_to_desc(irq); 85 + unsigned long flags; 85 86 86 87 if (!desc->chip->set_affinity) 87 88 return -EINVAL; 88 89 90 + spin_lock_irqsave(&desc->lock, flags); 91 + 89 92 #ifdef CONFIG_GENERIC_PENDING_IRQ 90 93 if (desc->status & IRQ_MOVE_PCNTXT || desc->status & IRQ_DISABLED) { 91 - unsigned long flags; 92 - 93 - spin_lock_irqsave(&desc->lock, flags); 94 94 desc->affinity = cpumask; 95 95 desc->chip->set_affinity(irq, cpumask); 96 - spin_unlock_irqrestore(&desc->lock, flags); 97 - } else 98 - set_pending_irq(irq, cpumask); 96 + } else { 97 + desc->status |= IRQ_MOVE_PENDING; 98 + desc->pending_mask = cpumask; 99 + } 99 100 #else 100 101 desc->affinity = cpumask; 101 102 desc->chip->set_affinity(irq, cpumask); 102 103 #endif 104 + desc->status |= IRQ_AFFINITY_SET; 105 + spin_unlock_irqrestore(&desc->lock, flags); 103 106 return 0; 104 107 } 105 108 ··· 110 107 /* 111 108 * Generic version of the affinity autoselector. 112 109 */ 113 - int irq_select_affinity(unsigned int irq) 110 + int do_irq_select_affinity(unsigned int irq, struct irq_desc *desc) 114 111 { 115 112 cpumask_t mask; 116 - struct irq_desc *desc; 117 113 118 114 if (!irq_can_set_affinity(irq)) 119 115 return 0; 120 116 121 117 cpus_and(mask, cpu_online_map, irq_default_affinity); 122 118 123 - desc = irq_to_desc(irq); 119 + /* 120 + * Preserve an userspace affinity setup, but make sure that 121 + * one of the targets is online. 122 + */ 123 + if (desc->status & IRQ_AFFINITY_SET) { 124 + if (cpus_intersects(desc->affinity, cpu_online_map)) 125 + mask = desc->affinity; 126 + else 127 + desc->status &= ~IRQ_AFFINITY_SET; 128 + } 129 + 124 130 desc->affinity = mask; 125 131 desc->chip->set_affinity(irq, mask); 126 132 127 133 return 0; 128 134 } 135 + #else 136 + static inline int do_irq_select_affinity(unsigned int irq, struct irq_desc *d) 137 + { 138 + return irq_select_affinity(irq); 139 + } 129 140 #endif 130 141 142 + /* 143 + * Called when affinity is set via /proc/irq 144 + */ 145 + int irq_select_affinity_usr(unsigned int irq) 146 + { 147 + struct irq_desc *desc = irq_to_desc(irq); 148 + unsigned long flags; 149 + int ret; 150 + 151 + spin_lock_irqsave(&desc->lock, flags); 152 + ret = do_irq_select_affinity(irq, desc); 153 + spin_unlock_irqrestore(&desc->lock, flags); 154 + 155 + return ret; 156 + } 157 + 158 + #else 159 + static inline int do_select_irq_affinity(int irq, struct irq_desc *desc) 160 + { 161 + return 0; 162 + } 131 163 #endif 132 164 133 165 /** ··· 484 446 desc->depth = 1; 485 447 486 448 /* Set default affinity mask once everything is setup */ 487 - irq_select_affinity(irq); 449 + do_irq_select_affinity(irq, desc); 488 450 489 451 } else if ((new->flags & IRQF_TRIGGER_MASK) 490 452 && (new->flags & IRQF_TRIGGER_MASK)
-11
kernel/irq/migration.c
··· 1 1 2 2 #include <linux/irq.h> 3 3 4 - void set_pending_irq(unsigned int irq, cpumask_t mask) 5 - { 6 - struct irq_desc *desc = irq_to_desc(irq); 7 - unsigned long flags; 8 - 9 - spin_lock_irqsave(&desc->lock, flags); 10 - desc->status |= IRQ_MOVE_PENDING; 11 - desc->pending_mask = mask; 12 - spin_unlock_irqrestore(&desc->lock, flags); 13 - } 14 - 15 4 void move_masked_irq(int irq) 16 5 { 17 6 struct irq_desc *desc = irq_to_desc(irq);
+1 -1
kernel/irq/proc.c
··· 62 62 if (!cpus_intersects(new_value, cpu_online_map)) 63 63 /* Special case for empty set - allow the architecture 64 64 code to set default SMP affinity. */ 65 - return irq_select_affinity(irq) ? -EINVAL : count; 65 + return irq_select_affinity_usr(irq) ? -EINVAL : count; 66 66 67 67 irq_set_affinity(irq, new_value); 68 68