diff mbox series

[v4,1/5] genirq/affinity: Add irq_update_affinity_desc()

Message ID 1606757759-6076-2-git-send-email-john.garry@huawei.com (mailing list archive)
State Not Applicable, archived
Headers show
Series Support managed interrupts for platform devices | expand

Commit Message

John Garry Nov. 30, 2020, 5:35 p.m. UTC
Add a function to allow the affinity of an interrupt be switched to
managed, such that interrupts allocated for platform devices may be
managed.

Suggested-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: John Garry <john.garry@huawei.com>
---
 include/linux/interrupt.h |  8 +++++
 kernel/irq/manage.c       | 63 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 71 insertions(+)

Comments

Thomas Gleixner Nov. 30, 2020, 11:19 p.m. UTC | #1
On Tue, Dec 01 2020 at 01:35, John Garry wrote:

> Add a function to allow the affinity of an interrupt be switched to
> managed, such that interrupts allocated for platform devices may be
> managed.

Could you please add a paragraph which explains the limitations of that
interface?

> +/**
> + * irq_update_affinity_desc - Update affinity management for an interrupt
> + * @irq:	The interrupt number to update
> + * @affinity:	Pointer to the affinity descriptor
> + *
> + * This interface can be used to configure the affinity management of
> + * interrupts which have been allocated already.

Same here.

Thanks,

        tglx
John Garry Dec. 1, 2020, 3:22 p.m. UTC | #2
Hi Thomas,

I'll go with something extra like:

> 
>> Add a function to allow the affinity of an interrupt be switched to
>> managed, such that interrupts allocated for platform devices may be
>> managed.
> 
> Could you please add a paragraph which explains the limitations of that
> interface?

This new interface has certain limitations, and attempts to use it in 
the following circumstances will fail:
- For when the kernel is configured for generic IRQ reservation mode (in 
config GENERIC_IRQ_RESERVATION_MODE). The reason being that it could 
conflict with managed vs. non-managed interrupt accounting there.
- The interrupt is already started, which should not be the case during init
- The interrupt is already configured as managed, which means double init

> 
>> +/**
>> + * irq_update_affinity_desc - Update affinity management for an interrupt
>> + * @irq:	The interrupt number to update
>> + * @affinity:	Pointer to the affinity descriptor
>> + *
>> + * This interface can be used to configure the affinity management of
>> + * interrupts which have been allocated already.
> 

/*
  * There are certain limitations on when it may be used - attempts to 
use it for when the kernel is configured for generic IRQ reservation 
mode (in config GENERIC_IRQ_RESERVATION_MODE) will fail, as it may 
conflict with managed/non-managed interrupt accounting. In addition, 
attempts to use it on an interrupt which is already started or which has 
already been configured as managed will also fail, as these mean invalid 
init state or double init.

...

Let me know if not good, if not I'll post again soon.

Thanks,
John
Thomas Gleixner Dec. 1, 2020, 5:28 p.m. UTC | #3
On Tue, Dec 01 2020 at 15:22, John Garry wrote:
> /*
>   * There are certain limitations on when it may be used - attempts to 
> use it for when the kernel is configured for generic IRQ reservation 
> mode (in config GENERIC_IRQ_RESERVATION_MODE) will fail, as it may 
> conflict with managed/non-managed interrupt accounting. In addition, 
> attempts to use it on an interrupt which is already started or which has 
> already been configured as managed will also fail, as these mean invalid 
> init state or double init.
>
> ...
>
> Let me know if not good, if not I'll post again soon.

LGTM
diff mbox series

Patch

diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index ee8299eb1f52..870b3251e174 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -352,6 +352,8 @@  extern int irq_can_set_affinity(unsigned int irq);
 extern int irq_select_affinity(unsigned int irq);
 
 extern int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m);
+extern int irq_update_affinity_desc(unsigned int irq,
+				    struct irq_affinity_desc *affinity);
 
 extern int
 irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify);
@@ -387,6 +389,12 @@  static inline int irq_set_affinity_hint(unsigned int irq,
 	return -EINVAL;
 }
 
+static inline int irq_update_affinity_desc(unsigned int irq,
+					   struct irq_affinity_desc *affinity)
+{
+	return -EINVAL;
+}
+
 static inline int
 irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify)
 {
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index c460e0496006..791691cb9005 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -371,6 +371,69 @@  int irq_set_affinity_locked(struct irq_data *data, const struct cpumask *mask,
 	return ret;
 }
 
+/**
+ * irq_update_affinity_desc - Update affinity management for an interrupt
+ * @irq:	The interrupt number to update
+ * @affinity:	Pointer to the affinity descriptor
+ *
+ * This interface can be used to configure the affinity management of
+ * interrupts which have been allocated already.
+ */
+int irq_update_affinity_desc(unsigned int irq,
+			     struct irq_affinity_desc *affinity)
+{
+	struct irq_desc *desc;
+	unsigned long flags;
+	bool activated;
+	int ret = 0;
+
+	/*
+	 * Supporting this with the reservation scheme used by x86 needs
+	 * some more thought. Fail it for now.
+	 */
+	if (IS_ENABLED(CONFIG_GENERIC_IRQ_RESERVATION_MODE))
+		return -EOPNOTSUPP;
+
+	desc = irq_get_desc_buslock(irq, &flags, 0);
+	if (!desc)
+		return -EINVAL;
+
+	/* Requires the interrupt to be shut down */
+	if (irqd_is_started(&desc->irq_data)) {
+		ret = -EBUSY;
+		goto out_unlock;
+	}
+
+	/* Interrupts which are already managed cannot be modified */
+	if (irqd_affinity_is_managed(&desc->irq_data)) {
+		ret = -EBUSY;
+		goto out_unlock;
+	}
+
+	/*
+	 * Deactivate the interrupt. That's required to undo
+	 * anything an earlier activation has established.
+	 */
+	activated = irqd_is_activated(&desc->irq_data);
+	if (activated)
+		irq_domain_deactivate_irq(&desc->irq_data);
+
+	if (affinity->is_managed) {
+		irqd_set(&desc->irq_data, IRQD_AFFINITY_MANAGED);
+		irqd_set(&desc->irq_data, IRQD_MANAGED_SHUTDOWN);
+	}
+
+	cpumask_copy(desc->irq_common_data.affinity, &affinity->mask);
+
+	/* Restore the activation state */
+	if (activated)
+		irq_domain_activate_irq(&desc->irq_data, false);
+
+out_unlock:
+	irq_put_desc_busunlock(desc, flags);
+	return ret;
+}
+
 int __irq_set_affinity(unsigned int irq, const struct cpumask *mask, bool force)
 {
 	struct irq_desc *desc = irq_to_desc(irq);