Skip to contentSkip to conversation

genirq/affinity: Defer affinity setting if irq chip is busy

PATCH In review Thomas Gleixner 3 participants 3 messages Jun 4, 2018 – Jun 6, 2018

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 7/8 genirq/affinity: Defer affinity setting if irq chip is busy

The case that interrupt affinity setting fails with -EBUSY can be handled in the kernel completely by using the already available generic pending infrastructure.

If a irq_chip::set_affinity() fails with -EBUSY, handle it like the interrupts for which irq_chip::set_affinity() can only be invoked from interrupt context. Copy the new affinity mask to irq_desc::pending_mask and set the affinity pending bit. The next raised interrupt for the affected irq will check the pending bit and try to set the new affinity from the handler. This avoids that -EBUSY is returned when an affinity change is requested from user space and the previous change has not been cleaned up. The new affinity will take effect when the next interrupt is raised from the device.

Fixes dccfe3147b42 ("x86/vector: Simplify vector move cleanup")
---
 kernel/irq/manage.c |   37 +++++++++++++++++++++++++++++++++++--
 1 file changed, 35 insertions(+), 2 deletions(-)
kernel/irq/manage.c +35 -2
@@ -204,6 +204,39 @@ int irq_do_set_affinity(struct irq_data
 	return ret;
 }
 
+#ifdef CONFIG_GENERIC_PENDING_IRQ
+static inline int irq_set_affinity_pending(struct irq_data *data,
+					   const struct cpumask *dest)
+{
+	struct irq_desc *desc = irq_data_to_desc(data);
+
+	irqd_set_move_pending(data);
+	irq_copy_pending(desc, dest);
+	return 0;
+}
+#else
+static inline int irq_set_affinity_pending(struct irq_data *data,
+					   const struct cpumask *dest)
+{
+	return -EBUSY;
+}
+#endif
+
+static int irq_try_set_affinity(struct irq_data *data,
+				const struct cpumask *dest, bool force)
+{
+	int ret = irq_do_set_affinity(data, dest, force);
+
+	/*
+	 * In case that the underlying vector management is busy and the
+	 * architecture supports the generic pending mechanism then utilize
+	 * this to avoid returning an error to user space.
+	 */
+	if (ret == -EBUSY && !force)
+		ret = irq_set_affinity_pending(data, dest);
+	return ret;
+}
+
 int irq_set_affinity_locked(struct irq_data *data, const struct cpumask *mask,
 			    bool force)
 {
@@ -214,8 +247,8 @@ int irq_set_affinity_locked(struct irq_d
 	if (!chip || !chip->irq_set_affinity)
 		return -EINVAL;
 
-	if (irq_can_move_pcntxt(data)) {
-		ret = irq_do_set_affinity(data, mask, force);
+	if (irq_can_move_pcntxt(data) && !irqd_is_setaffinity_pending(data)) {
+		ret = irq_try_set_affinity(data, mask, force);
 	} else {
 		irqd_set_move_pending(data);
 		irq_copy_pending(desc, mask);
Tested-by on v1 7/8
#1 lore raw
Quoted 14 lines · Thomas Gleixner

The case that interrupt affinity setting fails with -EBUSY can be handled in the kernel completely by using the already available generic pending infrastructure.

If a irq_chip::set_affinity() fails with -EBUSY, handle it like the interrupts for which irq_chip::set_affinity() can only be invoked from interrupt context. Copy the new affinity mask to irq_desc::pending_mask and set the affinity pending bit. The next raised interrupt for the affected irq will check the pending bit and try to set the new affinity from the handler. This avoids that -EBUSY is returned when an affinity change is requested from user space and the previous change has not been cleaned up. The new affinity will take effect when the next interrupt is raised from the device.

Quoted 3 lines
Fixes dccfe3147b42 ("x86/vector: Simplify vector move cleanup")
Quoted 59 lines
---
 kernel/irq/manage.c |   37 +++++++++++++++++++++++++++++++++++--
 1 file changed, 35 insertions(+), 2 deletions(-)
kernel/irq/manage.c +35 -2
@@ -204,6 +204,39 @@ int irq_do_set_affinity(struct irq_data
        return ret;
 }
 
+#ifdef CONFIG_GENERIC_PENDING_IRQ
+static inline int irq_set_affinity_pending(struct irq_data *data,
+                                          const struct cpumask *dest)
+{
+       struct irq_desc *desc = irq_data_to_desc(data);
+
+       irqd_set_move_pending(data);
+       irq_copy_pending(desc, dest);
+       return 0;
+}
+#else
+static inline int irq_set_affinity_pending(struct irq_data *data,
+                                          const struct cpumask *dest)
+{
+       return -EBUSY;
+}
+#endif
+
+static int irq_try_set_affinity(struct irq_data *data,
+                               const struct cpumask *dest, bool force)
+{
+       int ret = irq_do_set_affinity(data, dest, force);
+
+       /*
+        * In case that the underlying vector management is busy and the
+        * architecture supports the generic pending mechanism then utilize
+        * this to avoid returning an error to user space.
+        */
+       if (ret == -EBUSY && !force)
+               ret = irq_set_affinity_pending(data, dest);
+       return ret;
+}
+
 int irq_set_affinity_locked(struct irq_data *data, const struct cpumask *mask,
                            bool force)
 {
@@ -214,8 +247,8 @@ int irq_set_affinity_locked(struct irq_d
        if (!chip || !chip->irq_set_affinity)
                return -EINVAL;
 
-       if (irq_can_move_pcntxt(data)) {
-               ret = irq_do_set_affinity(data, mask, force);
+       if (irq_can_move_pcntxt(data) && !irqd_is_setaffinity_pending(data)) {
+               ret = irq_try_set_affinity(data, mask, force);
        } else {
                irqd_set_move_pending(data);
                irq_copy_pending(desc, mask);
#2 lore raw
In reply to Thomas Gleixner
genirq/affinity: Defer affinity setting if irq chip is busy
Commit-ID:  12f47073a40f6aa75119d8f5df4077b7f334cced
Gitweb:     https://git.kernel.org/tip/12f47073a40f6aa75119d8f5df4077b7f334cced
Author:     Thomas Gleixner <[email protected]>
AuthorDate: Mon, 4 Jun 2018 17:33:59 +0200
Committer:  Thomas Gleixner <[email protected]>
CommitDate: Wed, 6 Jun 2018 15:18:22 +0200

genirq/affinity: Defer affinity setting if irq chip is busy

The case that interrupt affinity setting fails with -EBUSY can be handled in the kernel completely by using the already available generic pending infrastructure.

If a irq_chip::set_affinity() fails with -EBUSY, handle it like the interrupts for which irq_chip::set_affinity() can only be invoked from interrupt context. Copy the new affinity mask to irq_desc::pending_mask and set the affinity pending bit. The next raised interrupt for the affected irq will check the pending bit and try to set the new affinity from the handler. This avoids that -EBUSY is returned when an affinity change is requested from user space and the previous change has not been cleaned up. The new affinity will take effect when the next interrupt is raised from the device.

Fixes dccfe3147b42 ("x86/vector: Simplify vector move cleanup")
Cc Joerg Roedel <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Song Liu <[email protected]>
Cc Dmitry Safonov <[email protected]>
Cc Mike Travis <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc Tariq Toukan <[email protected]>
---
 kernel/irq/manage.c | 37 +++++++++++++++++++++++++++++++++++--
 1 file changed, 35 insertions(+), 2 deletions(-)
kernel/irq/manage.c +35 -2
index e3336d904f64..facfecfc543c 100644
@@ -204,6 +204,39 @@ int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask,
 	return ret;
 }
 
+#ifdef CONFIG_GENERIC_PENDING_IRQ
+static inline int irq_set_affinity_pending(struct irq_data *data,
+					   const struct cpumask *dest)
+{
+	struct irq_desc *desc = irq_data_to_desc(data);
+
+	irqd_set_move_pending(data);
+	irq_copy_pending(desc, dest);
+	return 0;
+}
+#else
+static inline int irq_set_affinity_pending(struct irq_data *data,
+					   const struct cpumask *dest)
+{
+	return -EBUSY;
+}
+#endif
+
+static int irq_try_set_affinity(struct irq_data *data,
+				const struct cpumask *dest, bool force)
+{
+	int ret = irq_do_set_affinity(data, dest, force);
+
+	/*
+	 * In case that the underlying vector management is busy and the
+	 * architecture supports the generic pending mechanism then utilize
+	 * this to avoid returning an error to user space.
+	 */
+	if (ret == -EBUSY && !force)
+		ret = irq_set_affinity_pending(data, dest);
+	return ret;
+}
+
 int irq_set_affinity_locked(struct irq_data *data, const struct cpumask *mask,
 			    bool force)
 {
@@ -214,8 +247,8 @@ int irq_set_affinity_locked(struct irq_data *data, const struct cpumask *mask,
 	if (!chip || !chip->irq_set_affinity)
 		return -EINVAL;
 
-	if (irq_can_move_pcntxt(data)) {
-		ret = irq_do_set_affinity(data, mask, force);
+	if (irq_can_move_pcntxt(data) && !irqd_is_setaffinity_pending(data)) {
+		ret = irq_try_set_affinity(data, mask, force);
 	} else {
 		irqd_set_move_pending(data);
 		irq_copy_pending(desc, mask);