Эх сурвалжийг харах

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>
Thomas Gleixner 16 жил өмнө
parent
commit
f6d87f4bd2

+ 2 - 6
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_MOVE_PENDING	0x00200000	/* need to re-target IRQ destination */
 #define IRQ_NO_BALANCING	0x00400000	/* IRQ is excluded from balancing */
 #define IRQ_NO_BALANCING	0x00400000	/* IRQ is excluded from balancing */
 #define IRQ_SPURIOUS_DISABLED	0x00800000	/* IRQ was disabled by the spurious trap */
 #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
 #ifdef CONFIG_IRQ_PER_CPU
 # define CHECK_IRQ_PER_CPU(var) ((var) & 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
 #ifdef CONFIG_GENERIC_PENDING_IRQ
 
 
-void set_pending_irq(unsigned int irq, cpumask_t mask);
 void move_native_irq(int irq);
 void move_native_irq(int irq);
 void move_masked_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 */
 #endif /* CONFIG_GENERIC_PENDING_IRQ */
 
 
 #else /* CONFIG_SMP */
 #else /* CONFIG_SMP */

+ 2 - 0
kernel/irq/internals.h

@@ -25,6 +25,8 @@ static inline void unregister_handler_proc(unsigned int irq,
 					   struct irqaction *action) { }
 					   struct irqaction *action) { }
 #endif
 #endif
 
 
+extern int irq_select_affinity_usr(unsigned int irq);
+
 /*
 /*
  * Debugging printout:
  * Debugging printout:
  */
  */

+ 48 - 10
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)
 int irq_set_affinity(unsigned int irq, cpumask_t cpumask)
 {
 {
 	struct irq_desc *desc = irq_to_desc(irq);
 	struct irq_desc *desc = irq_to_desc(irq);
+	unsigned long flags;
 
 
 	if (!desc->chip->set_affinity)
 	if (!desc->chip->set_affinity)
 		return -EINVAL;
 		return -EINVAL;
 
 
+	spin_lock_irqsave(&desc->lock, flags);
+
 #ifdef CONFIG_GENERIC_PENDING_IRQ
 #ifdef CONFIG_GENERIC_PENDING_IRQ
 	if (desc->status & IRQ_MOVE_PCNTXT || desc->status & IRQ_DISABLED) {
 	if (desc->status & IRQ_MOVE_PCNTXT || desc->status & IRQ_DISABLED) {
-		unsigned long flags;
-
-		spin_lock_irqsave(&desc->lock, flags);
 		desc->affinity = cpumask;
 		desc->affinity = cpumask;
 		desc->chip->set_affinity(irq, 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
 #else
 	desc->affinity = cpumask;
 	desc->affinity = cpumask;
 	desc->chip->set_affinity(irq, cpumask);
 	desc->chip->set_affinity(irq, cpumask);
 #endif
 #endif
+	desc->status |= IRQ_AFFINITY_SET;
+	spin_unlock_irqrestore(&desc->lock, flags);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -107,24 +110,59 @@ int irq_set_affinity(unsigned int irq, cpumask_t cpumask)
 /*
 /*
  * Generic version of the affinity autoselector.
  * 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;
 	cpumask_t mask;
-	struct irq_desc *desc;
 
 
 	if (!irq_can_set_affinity(irq))
 	if (!irq_can_set_affinity(irq))
 		return 0;
 		return 0;
 
 
 	cpus_and(mask, cpu_online_map, irq_default_affinity);
 	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->affinity = mask;
 	desc->chip->set_affinity(irq, mask);
 	desc->chip->set_affinity(irq, mask);
 
 
 	return 0;
 	return 0;
 }
 }
+#else
+static inline int do_irq_select_affinity(unsigned int irq, struct irq_desc *d)
+{
+	return irq_select_affinity(irq);
+}
 #endif
 #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
 #endif
 
 
 /**
 /**
@@ -446,7 +484,7 @@ __setup_irq(unsigned int irq, struct irq_desc * desc, struct irqaction *new)
 			desc->depth = 1;
 			desc->depth = 1;
 
 
 		/* Set default affinity mask once everything is setup */
 		/* 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)
 	} else if ((new->flags & IRQF_TRIGGER_MASK)
 			&& (new->flags & IRQF_TRIGGER_MASK)
 			&& (new->flags & IRQF_TRIGGER_MASK)

+ 0 - 11
kernel/irq/migration.c

@@ -1,17 +1,6 @@
 
 
 #include <linux/irq.h>
 #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)
 void move_masked_irq(int irq)
 {
 {
 	struct irq_desc *desc = irq_to_desc(irq);
 	struct irq_desc *desc = irq_to_desc(irq);

+ 1 - 1
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))
 	if (!cpus_intersects(new_value, cpu_online_map))
 		/* Special case for empty set - allow the architecture
 		/* Special case for empty set - allow the architecture
 		   code to set default SMP affinity. */
 		   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);
 	irq_set_affinity(irq, new_value);