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