2023-02-13 10:43:50

by Johan Hovold

[permalink] [raw]
Subject: [PATCH v6 00/20] irqdomain: fix mapping race and rework locking

Parallel probing (e.g. due to asynchronous probing) of devices that
share interrupts can currently result in two mappings for the same
hardware interrupt to be created.

This series fixes this mapping race and reworks the irqdomain locking so
that in the end the global irq_domain_mutex is only used for managing
the likewise global irq_domain_list, while domain operations (e.g. IRQ
allocations) use per-domain (hierarchy) locking.

Johan


Changes in v6
- use common exit path in irq_create_fwspec_mapping() (6/20)
- fix domain registration race (7/20, new)
- fix x86/uv commit prefix (13/20)
- use &domain->root->mutex also for non-hierarchichal domains (20/20)
- fix kernel doc typo (20/20)

Changes in v5
- reorder patches (since we do care about stable after all)
- tweak commit messages and add stable tags
- use '__locked' suffix for new helper functions
- drop a line break from an argument list (11/19)

Changes in v4
- add a comment to __irq_domain_add() as further documentation of the
domain lock and root pointer (19/19)
- add a comment documenting that the lockdep assertion in
irq_domain_set_mapping() also verifies that the domains in a
hierarchy point to the same root (19/19)

Changes in v3
- drop dead and bogus code (1--3/19, new)
- fix racy mapcount accesses (5/19, new)
- drop revmap mutex (6/19, new)
- use irq_domain_mutex to address mapping race (9/19)
- clean up irq_domain_push/pop_irq() (10/19, new)
- use irq_domain_create_hierarchy() to construct hierarchies
(11--18/19, new)
- switch to per-domain locking (19/19, new)

Changes in v2
- split out redundant-lookup cleanup (1/4)
- use a per-domain mutex to address mapping race (2/4)
- move kernel-doc to exported function (2/4)
- fix association race (3/4, new)
- use per-domain mutex for associations (4/4, new)


Johan Hovold (19):
irqdomain: Fix association race
irqdomain: Fix disassociation race
irqdomain: Drop bogus fwspec-mapping error handling
irqdomain: Look for existing mapping only once
irqdomain: Refactor __irq_domain_alloc_irqs()
irqdomain: Fix mapping-creation race
irqdomain: Drop revmap mutex
irqdomain: Drop dead domain-name assignment
irqdomain: Drop leftover brackets
irqdomain: Clean up irq_domain_push/pop_irq()
x86/ioapic: Use irq_domain_create_hierarchy()
x86/uv: Use irq_domain_create_hierarchy()
irqchip/alpine-msi: Use irq_domain_add_hierarchy()
irqchip/gic-v2m: Use irq_domain_create_hierarchy()
irqchip/gic-v3-its: Use irq_domain_create_hierarchy()
irqchip/gic-v3-mbi: Use irq_domain_create_hierarchy()
irqchip/loongson-pch-msi: Use irq_domain_create_hierarchy()
irqchip/mvebu-odmi: Use irq_domain_create_hierarchy()
irqdomain: Switch to per-domain locking

Marc Zyngier (1):
irqdomain: Fix domain registration race

arch/x86/kernel/apic/io_apic.c | 7 +-
arch/x86/platform/uv/uv_irq.c | 7 +-
drivers/irqchip/irq-alpine-msi.c | 8 +-
drivers/irqchip/irq-gic-v2m.c | 5 +-
drivers/irqchip/irq-gic-v3-its.c | 13 +-
drivers/irqchip/irq-gic-v3-mbi.c | 5 +-
drivers/irqchip/irq-loongson-pch-msi.c | 9 +-
drivers/irqchip/irq-mvebu-odmi.c | 13 +-
include/linux/irqdomain.h | 6 +-
kernel/irq/irqdomain.c | 408 +++++++++++++++----------
10 files changed, 279 insertions(+), 202 deletions(-)

--
2.39.1



2023-02-13 10:43:53

by Johan Hovold

[permalink] [raw]
Subject: [PATCH v6 10/20] irqdomain: Drop leftover brackets

Drop some unnecessary brackets that were left in place when the
corresponding code was updated.

Reviewed-by: Philippe Mathieu-Daudé <[email protected]>
Tested-by: Hsin-Yi Wang <[email protected]>
Tested-by: Mark-PK Tsai <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
---
kernel/irq/irqdomain.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 9bba31de6928..7785b3352e60 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -210,9 +210,8 @@ static struct irq_domain *__irq_domain_create(struct fwnode_handle *fwnode,
domain->host_data = host_data;
domain->hwirq_max = hwirq_max;

- if (direct_max) {
+ if (direct_max)
domain->flags |= IRQ_DOMAIN_FLAG_NO_MAP;
- }

domain->revmap_size = size;

@@ -652,9 +651,8 @@ void irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__,
of_node_full_name(of_node), irq_base, (int)hwirq_base, count);

- for (i = 0; i < count; i++) {
+ for (i = 0; i < count; i++)
irq_domain_associate(domain, irq_base + i, hwirq_base + i);
- }
}
EXPORT_SYMBOL_GPL(irq_domain_associate_many);

--
2.39.1


2023-02-13 10:43:56

by Johan Hovold

[permalink] [raw]
Subject: [PATCH v6 02/20] irqdomain: Fix disassociation race

The global irq_domain_mutex is held when mapping interrupts from
non-hierarchical domains but currently not when disposing them.

This specifically means that updates of the domain mapcount is racy
(currently only used for statistics in debugfs).

Make sure to hold the global irq_domain_mutex also when disposing
mappings from non-hierarchical domains.

Fixes: 9dc6be3d4193 ("genirq/irqdomain: Add map counter")
Cc: [email protected] # 4.13
Tested-by: Hsin-Yi Wang <[email protected]>
Tested-by: Mark-PK Tsai <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
---
kernel/irq/irqdomain.c | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 561689a3f050..981cd636275e 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -538,6 +538,9 @@ static void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)
return;

hwirq = irq_data->hwirq;
+
+ mutex_lock(&irq_domain_mutex);
+
irq_set_status_flags(irq, IRQ_NOREQUEST);

/* remove chip and handler */
@@ -557,6 +560,8 @@ static void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)

/* Clear reverse map for this hwirq */
irq_domain_clear_mapping(domain, hwirq);
+
+ mutex_unlock(&irq_domain_mutex);
}

static int irq_domain_associate_locked(struct irq_domain *domain, unsigned int virq,
--
2.39.1


2023-02-13 10:43:59

by Johan Hovold

[permalink] [raw]
Subject: [PATCH v6 04/20] irqdomain: Look for existing mapping only once

Avoid looking for an existing mapping twice when creating a new mapping
using irq_create_fwspec_mapping() by factoring out the actual allocation
which is shared with irq_create_mapping_affinity().

The new helper function will also be used to fix a shared-interrupt
mapping race, hence the Fixes tag.

Fixes: b62b2cf5759b ("irqdomain: Fix handling of type settings for existing mappings")
Cc: [email protected] # 4.8
Tested-by: Hsin-Yi Wang <[email protected]>
Tested-by: Mark-PK Tsai <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
---
kernel/irq/irqdomain.c | 60 +++++++++++++++++++++++-------------------
1 file changed, 33 insertions(+), 27 deletions(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index b4326c364ae7..3d6a14efae62 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -682,6 +682,34 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain)
EXPORT_SYMBOL_GPL(irq_create_direct_mapping);
#endif

+static unsigned int __irq_create_mapping_affinity(struct irq_domain *domain,
+ irq_hw_number_t hwirq,
+ const struct irq_affinity_desc *affinity)
+{
+ struct device_node *of_node = irq_domain_get_of_node(domain);
+ int virq;
+
+ pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq);
+
+ /* Allocate a virtual interrupt number */
+ virq = irq_domain_alloc_descs(-1, 1, hwirq, of_node_to_nid(of_node),
+ affinity);
+ if (virq <= 0) {
+ pr_debug("-> virq allocation failed\n");
+ return 0;
+ }
+
+ if (irq_domain_associate(domain, virq, hwirq)) {
+ irq_free_desc(virq);
+ return 0;
+ }
+
+ pr_debug("irq %lu on domain %s mapped to virtual irq %u\n",
+ hwirq, of_node_full_name(of_node), virq);
+
+ return virq;
+}
+
/**
* irq_create_mapping_affinity() - Map a hardware interrupt into linux irq space
* @domain: domain owning this hardware interrupt or NULL for default domain
@@ -694,14 +722,11 @@ EXPORT_SYMBOL_GPL(irq_create_direct_mapping);
* on the number returned from that call.
*/
unsigned int irq_create_mapping_affinity(struct irq_domain *domain,
- irq_hw_number_t hwirq,
- const struct irq_affinity_desc *affinity)
+ irq_hw_number_t hwirq,
+ const struct irq_affinity_desc *affinity)
{
- struct device_node *of_node;
int virq;

- pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq);
-
/* Look for default domain if necessary */
if (domain == NULL)
domain = irq_default_domain;
@@ -709,34 +734,15 @@ unsigned int irq_create_mapping_affinity(struct irq_domain *domain,
WARN(1, "%s(, %lx) called with NULL domain\n", __func__, hwirq);
return 0;
}
- pr_debug("-> using domain @%p\n", domain);
-
- of_node = irq_domain_get_of_node(domain);

/* Check if mapping already exists */
virq = irq_find_mapping(domain, hwirq);
if (virq) {
- pr_debug("-> existing mapping on virq %d\n", virq);
+ pr_debug("existing mapping on virq %d\n", virq);
return virq;
}

- /* Allocate a virtual interrupt number */
- virq = irq_domain_alloc_descs(-1, 1, hwirq, of_node_to_nid(of_node),
- affinity);
- if (virq <= 0) {
- pr_debug("-> virq allocation failed\n");
- return 0;
- }
-
- if (irq_domain_associate(domain, virq, hwirq)) {
- irq_free_desc(virq);
- return 0;
- }
-
- pr_debug("irq %lu on domain %s mapped to virtual irq %u\n",
- hwirq, of_node_full_name(of_node), virq);
-
- return virq;
+ return __irq_create_mapping_affinity(domain, hwirq, affinity);
}
EXPORT_SYMBOL_GPL(irq_create_mapping_affinity);

@@ -841,7 +847,7 @@ unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
return 0;
} else {
/* Create mapping */
- virq = irq_create_mapping(domain, hwirq);
+ virq = __irq_create_mapping_affinity(domain, hwirq, NULL);
if (!virq)
return virq;
}
--
2.39.1


2023-02-13 10:44:02

by Johan Hovold

[permalink] [raw]
Subject: [PATCH v6 06/20] irqdomain: Fix mapping-creation race

Parallel probing of devices that share interrupts (e.g. when a driver
uses asynchronous probing) can currently result in two mappings for the
same hardware interrupt to be created due to missing serialisation.

Make sure to hold the irq_domain_mutex when creating mappings so that
looking for an existing mapping before creating a new one is done
atomically.

Fixes: 765230b5f084 ("driver-core: add asynchronous probing support for drivers")
Fixes: b62b2cf5759b ("irqdomain: Fix handling of type settings for existing mappings")
Link: https://lore.kernel.org/r/[email protected]
Cc: [email protected] # 4.8
Cc: Dmitry Torokhov <[email protected]>
Cc: Jon Hunter <[email protected]>
Tested-by: Hsin-Yi Wang <[email protected]>
Tested-by: Mark-PK Tsai <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
---
kernel/irq/irqdomain.c | 64 ++++++++++++++++++++++++++++++------------
1 file changed, 46 insertions(+), 18 deletions(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 7b57949bc79c..bfda4adc05c0 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -25,6 +25,9 @@ static DEFINE_MUTEX(irq_domain_mutex);

static struct irq_domain *irq_default_domain;

+static int irq_domain_alloc_irqs_locked(struct irq_domain *domain, int irq_base,
+ unsigned int nr_irqs, int node, void *arg,
+ bool realloc, const struct irq_affinity_desc *affinity);
static void irq_domain_check_hierarchy(struct irq_domain *domain);

struct irqchip_fwid {
@@ -682,9 +685,9 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain)
EXPORT_SYMBOL_GPL(irq_create_direct_mapping);
#endif

-static unsigned int __irq_create_mapping_affinity(struct irq_domain *domain,
- irq_hw_number_t hwirq,
- const struct irq_affinity_desc *affinity)
+static unsigned int irq_create_mapping_affinity_locked(struct irq_domain *domain,
+ irq_hw_number_t hwirq,
+ const struct irq_affinity_desc *affinity)
{
struct device_node *of_node = irq_domain_get_of_node(domain);
int virq;
@@ -699,7 +702,7 @@ static unsigned int __irq_create_mapping_affinity(struct irq_domain *domain,
return 0;
}

- if (irq_domain_associate(domain, virq, hwirq)) {
+ if (irq_domain_associate_locked(domain, virq, hwirq)) {
irq_free_desc(virq);
return 0;
}
@@ -735,14 +738,20 @@ unsigned int irq_create_mapping_affinity(struct irq_domain *domain,
return 0;
}

+ mutex_lock(&irq_domain_mutex);
+
/* Check if mapping already exists */
virq = irq_find_mapping(domain, hwirq);
if (virq) {
pr_debug("existing mapping on virq %d\n", virq);
- return virq;
+ goto out;
}

- return __irq_create_mapping_affinity(domain, hwirq, affinity);
+ virq = irq_create_mapping_affinity_locked(domain, hwirq, affinity);
+out:
+ mutex_unlock(&irq_domain_mutex);
+
+ return virq;
}
EXPORT_SYMBOL_GPL(irq_create_mapping_affinity);

@@ -809,6 +818,8 @@ unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
if (WARN_ON(type & ~IRQ_TYPE_SENSE_MASK))
type &= IRQ_TYPE_SENSE_MASK;

+ mutex_lock(&irq_domain_mutex);
+
/*
* If we've already configured this interrupt,
* don't do it again, or hell will break loose.
@@ -821,7 +832,7 @@ unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
* interrupt number.
*/
if (type == IRQ_TYPE_NONE || type == irq_get_trigger_type(virq))
- return virq;
+ goto out;

/*
* If the trigger type has not been set yet, then set
@@ -829,35 +840,45 @@ unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
*/
if (irq_get_trigger_type(virq) == IRQ_TYPE_NONE) {
irq_data = irq_get_irq_data(virq);
- if (!irq_data)
- return 0;
+ if (!irq_data) {
+ virq = 0;
+ goto out;
+ }

irqd_set_trigger_type(irq_data, type);
- return virq;
+ goto out;
}

pr_warn("type mismatch, failed to map hwirq-%lu for %s!\n",
hwirq, of_node_full_name(to_of_node(fwspec->fwnode)));
- return 0;
+ virq = 0;
+ goto out;
}

if (irq_domain_is_hierarchy(domain)) {
- virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, fwspec);
- if (virq <= 0)
- return 0;
+ virq = irq_domain_alloc_irqs_locked(domain, -1, 1, NUMA_NO_NODE,
+ fwspec, false, NULL);
+ if (virq <= 0) {
+ virq = 0;
+ goto out;
+ }
} else {
/* Create mapping */
- virq = __irq_create_mapping_affinity(domain, hwirq, NULL);
+ virq = irq_create_mapping_affinity_locked(domain, hwirq, NULL);
if (!virq)
- return virq;
+ goto out;
}

irq_data = irq_get_irq_data(virq);
- if (WARN_ON(!irq_data))
- return 0;
+ if (WARN_ON(!irq_data)) {
+ virq = 0;
+ goto out;
+ }

/* Store trigger type */
irqd_set_trigger_type(irq_data, type);
+out:
+ mutex_unlock(&irq_domain_mutex);

return virq;
}
@@ -1888,6 +1909,13 @@ void irq_domain_set_info(struct irq_domain *domain, unsigned int virq,
irq_set_handler_data(virq, handler_data);
}

+static int irq_domain_alloc_irqs_locked(struct irq_domain *domain, int irq_base,
+ unsigned int nr_irqs, int node, void *arg,
+ bool realloc, const struct irq_affinity_desc *affinity)
+{
+ return -EINVAL;
+}
+
static void irq_domain_check_hierarchy(struct irq_domain *domain)
{
}
--
2.39.1


2023-02-13 10:44:04

by Johan Hovold

[permalink] [raw]
Subject: [PATCH v6 12/20] x86/ioapic: Use irq_domain_create_hierarchy()

Use the irq_domain_create_hierarchy() helper to create the hierarchical
domain, which both serves as documentation and avoids poking at
irqdomain internals.

Reviewed-by: Philippe Mathieu-Daudé <[email protected]>
Tested-by: Hsin-Yi Wang <[email protected]>
Tested-by: Mark-PK Tsai <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
---
arch/x86/kernel/apic/io_apic.c | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c
index a868b76cd3d4..1f83b052bb74 100644
--- a/arch/x86/kernel/apic/io_apic.c
+++ b/arch/x86/kernel/apic/io_apic.c
@@ -2364,9 +2364,8 @@ static int mp_irqdomain_create(int ioapic)
return -ENODEV;
}

- ip->irqdomain = irq_domain_create_linear(fn, hwirqs, cfg->ops,
- (void *)(long)ioapic);
-
+ ip->irqdomain = irq_domain_create_hierarchy(parent, 0, hwirqs, fn, cfg->ops,
+ (void *)(long)ioapic);
if (!ip->irqdomain) {
/* Release fw handle if it was allocated above */
if (!cfg->dev)
@@ -2374,8 +2373,6 @@ static int mp_irqdomain_create(int ioapic)
return -ENOMEM;
}

- ip->irqdomain->parent = parent;
-
if (cfg->type == IOAPIC_DOMAIN_LEGACY ||
cfg->type == IOAPIC_DOMAIN_STRICT)
ioapic_dynirq_base = max(ioapic_dynirq_base,
--
2.39.1


2023-02-13 10:44:07

by Johan Hovold

[permalink] [raw]
Subject: [PATCH v6 09/20] irqdomain: Drop dead domain-name assignment

Since commit d59f6617eef0 ("genirq: Allow fwnode to carry name
information only") an IRQ domain is always given a name during
allocation (e.g. used for the debugfs entry).

Drop the leftover name assignment when allocating the first IRQ.

Tested-by: Hsin-Yi Wang <[email protected]>
Tested-by: Mark-PK Tsai <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
---
kernel/irq/irqdomain.c | 8 --------
1 file changed, 8 deletions(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index f2247186f71d..9bba31de6928 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -619,10 +619,6 @@ static int irq_domain_associate_locked(struct irq_domain *domain, unsigned int v
irq_data->hwirq = 0;
return ret;
}
-
- /* If not already assigned, give the domain the chip's name */
- if (!domain->name && irq_data->chip)
- domain->name = irq_data->chip->name;
}

domain->mapcount++;
@@ -1182,10 +1178,6 @@ static void irq_domain_insert_irq(int virq)

domain->mapcount++;
irq_domain_set_mapping(domain, data->hwirq, data);
-
- /* If not already assigned, give the domain the chip's name */
- if (!domain->name && data->chip)
- domain->name = data->chip->name;
}

irq_clear_status_flags(virq, IRQ_NOREQUEST);
--
2.39.1


2023-02-13 10:44:10

by Johan Hovold

[permalink] [raw]
Subject: [PATCH v6 15/20] irqchip/gic-v2m: Use irq_domain_create_hierarchy()

Use the irq_domain_create_hierarchy() helper to create the hierarchical
domain, which both serves as documentation and avoids poking at
irqdomain internals.

Reviewed-by: Philippe Mathieu-Daudé <[email protected]>
Tested-by: Hsin-Yi Wang <[email protected]>
Tested-by: Mark-PK Tsai <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
---
drivers/irqchip/irq-gic-v2m.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c
index f4d7eeb13951..f1e75b35a52a 100644
--- a/drivers/irqchip/irq-gic-v2m.c
+++ b/drivers/irqchip/irq-gic-v2m.c
@@ -287,15 +287,14 @@ static __init int gicv2m_allocate_domains(struct irq_domain *parent)
if (!v2m)
return 0;

- inner_domain = irq_domain_create_tree(v2m->fwnode,
- &gicv2m_domain_ops, v2m);
+ inner_domain = irq_domain_create_hierarchy(parent, 0, 0, v2m->fwnode,
+ &gicv2m_domain_ops, v2m);
if (!inner_domain) {
pr_err("Failed to create GICv2m domain\n");
return -ENOMEM;
}

irq_domain_update_bus_token(inner_domain, DOMAIN_BUS_NEXUS);
- inner_domain->parent = parent;
pci_domain = pci_msi_create_irq_domain(v2m->fwnode,
&gicv2m_msi_domain_info,
inner_domain);
--
2.39.1


2023-02-13 10:44:12

by Johan Hovold

[permalink] [raw]
Subject: [PATCH v6 20/20] irqdomain: Switch to per-domain locking

The IRQ domain structures are currently protected by the global
irq_domain_mutex. Switch to using more fine-grained per-domain locking,
which can speed up parallel probing by reducing lock contention.

On a recent arm64 laptop, the total time spent waiting for the locks
during boot drops from 160 to 40 ms on average, while the maximum
aggregate wait time drops from 550 to 90 ms over ten runs for example.

Note that the domain lock of the root domain (innermost domain) must be
used for hierarchical domains. For non-hierarchical domains (as for root
domains), the new root pointer is set to the domain itself so that
&domain->root->mutex always points to the right lock.

Also note that hierarchical domains should be constructed using
irq_domain_create_hierarchy() (or irq_domain_add_hierarchy()) to avoid
having racing allocations access a not fully initialised domain. As a
safeguard, the lockdep assertion in irq_domain_set_mapping() will catch
any offenders that also fail to set the root domain pointer.

Tested-by: Hsin-Yi Wang <[email protected]>
Tested-by: Mark-PK Tsai <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
---
include/linux/irqdomain.h | 4 +++
kernel/irq/irqdomain.c | 59 ++++++++++++++++++++++++++-------------
2 files changed, 43 insertions(+), 20 deletions(-)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 16399de00b48..d320d15d4fba 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -125,6 +125,8 @@ struct irq_domain_chip_generic;
* core code.
* @flags: Per irq_domain flags
* @mapcount: The number of mapped interrupts
+ * @mutex: Domain lock, hierarchical domains use root domain's lock
+ * @root: Pointer to root domain, or containing structure if non-hierarchical
*
* Optional elements:
* @fwnode: Pointer to firmware node associated with the irq_domain. Pretty easy
@@ -152,6 +154,8 @@ struct irq_domain {
void *host_data;
unsigned int flags;
unsigned int mapcount;
+ struct mutex mutex;
+ struct irq_domain *root;

/* Optional data */
struct fwnode_handle *fwnode;
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 2213d972f083..aa5b7eeeceb8 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -215,6 +215,17 @@ static struct irq_domain *__irq_domain_create(struct fwnode_handle *fwnode,

domain->revmap_size = size;

+ /*
+ * Hierarchical domains use the domain lock of the root domain
+ * (innermost domain).
+ *
+ * For non-hierarchical domains (as for root domains), the root
+ * pointer is set to the domain itself so that &domain->root->mutex
+ * always points to the right lock.
+ */
+ mutex_init(&domain->mutex);
+ domain->root = domain;
+
irq_domain_check_hierarchy(domain);

return domain;
@@ -524,7 +535,7 @@ static bool irq_domain_is_nomap(struct irq_domain *domain)
static void irq_domain_clear_mapping(struct irq_domain *domain,
irq_hw_number_t hwirq)
{
- lockdep_assert_held(&irq_domain_mutex);
+ lockdep_assert_held(&domain->root->mutex);

if (irq_domain_is_nomap(domain))
return;
@@ -539,7 +550,11 @@ static void irq_domain_set_mapping(struct irq_domain *domain,
irq_hw_number_t hwirq,
struct irq_data *irq_data)
{
- lockdep_assert_held(&irq_domain_mutex);
+ /*
+ * This also makes sure that all domains point to the same root when
+ * called from irq_domain_insert_irq() for each domain in a hierarchy.
+ */
+ lockdep_assert_held(&domain->root->mutex);

if (irq_domain_is_nomap(domain))
return;
@@ -561,7 +576,7 @@ static void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)

hwirq = irq_data->hwirq;

- mutex_lock(&irq_domain_mutex);
+ mutex_lock(&domain->root->mutex);

irq_set_status_flags(irq, IRQ_NOREQUEST);

@@ -583,7 +598,7 @@ static void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)
/* Clear reverse map for this hwirq */
irq_domain_clear_mapping(domain, hwirq);

- mutex_unlock(&irq_domain_mutex);
+ mutex_unlock(&domain->root->mutex);
}

static int irq_domain_associate_locked(struct irq_domain *domain, unsigned int virq,
@@ -633,9 +648,9 @@ int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
{
int ret;

- mutex_lock(&irq_domain_mutex);
+ mutex_lock(&domain->root->mutex);
ret = irq_domain_associate_locked(domain, virq, hwirq);
- mutex_unlock(&irq_domain_mutex);
+ mutex_unlock(&domain->root->mutex);

return ret;
}
@@ -752,7 +767,7 @@ unsigned int irq_create_mapping_affinity(struct irq_domain *domain,
return 0;
}

- mutex_lock(&irq_domain_mutex);
+ mutex_lock(&domain->root->mutex);

/* Check if mapping already exists */
virq = irq_find_mapping(domain, hwirq);
@@ -763,7 +778,7 @@ unsigned int irq_create_mapping_affinity(struct irq_domain *domain,

virq = irq_create_mapping_affinity_locked(domain, hwirq, affinity);
out:
- mutex_unlock(&irq_domain_mutex);
+ mutex_unlock(&domain->root->mutex);

return virq;
}
@@ -832,7 +847,7 @@ unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
if (WARN_ON(type & ~IRQ_TYPE_SENSE_MASK))
type &= IRQ_TYPE_SENSE_MASK;

- mutex_lock(&irq_domain_mutex);
+ mutex_lock(&domain->root->mutex);

/*
* If we've already configured this interrupt,
@@ -892,7 +907,7 @@ unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
/* Store trigger type */
irqd_set_trigger_type(irq_data, type);
out:
- mutex_unlock(&irq_domain_mutex);
+ mutex_unlock(&domain->root->mutex);

return virq;
}
@@ -1157,6 +1172,7 @@ struct irq_domain *irq_domain_create_hierarchy(struct irq_domain *parent,
domain = __irq_domain_create(fwnode, 0, ~0, 0, ops, host_data);

if (domain) {
+ domain->root = parent->root;
domain->parent = parent;
domain->flags |= flags;

@@ -1555,10 +1571,10 @@ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
return -EINVAL;
}

- mutex_lock(&irq_domain_mutex);
+ mutex_lock(&domain->root->mutex);
ret = irq_domain_alloc_irqs_locked(domain, irq_base, nr_irqs, node, arg,
realloc, affinity);
- mutex_unlock(&irq_domain_mutex);
+ mutex_unlock(&domain->root->mutex);

return ret;
}
@@ -1569,7 +1585,7 @@ static void irq_domain_fix_revmap(struct irq_data *d)
{
void __rcu **slot;

- lockdep_assert_held(&irq_domain_mutex);
+ lockdep_assert_held(&d->domain->root->mutex);

if (irq_domain_is_nomap(d->domain))
return;
@@ -1635,7 +1651,7 @@ int irq_domain_push_irq(struct irq_domain *domain, int virq, void *arg)
if (!parent_irq_data)
return -ENOMEM;

- mutex_lock(&irq_domain_mutex);
+ mutex_lock(&domain->root->mutex);

/* Copy the original irq_data. */
*parent_irq_data = *irq_data;
@@ -1663,7 +1679,7 @@ int irq_domain_push_irq(struct irq_domain *domain, int virq, void *arg)
irq_domain_fix_revmap(parent_irq_data);
irq_domain_set_mapping(domain, irq_data->hwirq, irq_data);
error:
- mutex_unlock(&irq_domain_mutex);
+ mutex_unlock(&domain->root->mutex);

return rv;
}
@@ -1718,7 +1734,7 @@ int irq_domain_pop_irq(struct irq_domain *domain, int virq)
if (WARN_ON(!parent_irq_data))
return -EINVAL;

- mutex_lock(&irq_domain_mutex);
+ mutex_lock(&domain->root->mutex);

irq_data->parent_data = NULL;

@@ -1730,7 +1746,7 @@ int irq_domain_pop_irq(struct irq_domain *domain, int virq)

irq_domain_fix_revmap(irq_data);

- mutex_unlock(&irq_domain_mutex);
+ mutex_unlock(&domain->root->mutex);

kfree(parent_irq_data);

@@ -1746,17 +1762,20 @@ EXPORT_SYMBOL_GPL(irq_domain_pop_irq);
void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs)
{
struct irq_data *data = irq_get_irq_data(virq);
+ struct irq_domain *domain;
int i;

if (WARN(!data || !data->domain || !data->domain->ops->free,
"NULL pointer, cannot free irq\n"))
return;

- mutex_lock(&irq_domain_mutex);
+ domain = data->domain;
+
+ mutex_lock(&domain->root->mutex);
for (i = 0; i < nr_irqs; i++)
irq_domain_remove_irq(virq + i);
- irq_domain_free_irqs_hierarchy(data->domain, virq, nr_irqs);
- mutex_unlock(&irq_domain_mutex);
+ irq_domain_free_irqs_hierarchy(domain, virq, nr_irqs);
+ mutex_unlock(&domain->root->mutex);

irq_domain_free_irq_data(virq, nr_irqs);
irq_free_descs(virq, nr_irqs);
--
2.39.1


2023-02-13 10:44:15

by Johan Hovold

[permalink] [raw]
Subject: [PATCH v6 14/20] irqchip/alpine-msi: Use irq_domain_add_hierarchy()

Use the irq_domain_add_hierarchy() helper to create the hierarchical
domain, which both serves as documentation and avoids poking at
irqdomain internals.

Reviewed-by: Philippe Mathieu-Daudé <[email protected]>
Tested-by: Hsin-Yi Wang <[email protected]>
Tested-by: Mark-PK Tsai <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
---
drivers/irqchip/irq-alpine-msi.c | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/drivers/irqchip/irq-alpine-msi.c b/drivers/irqchip/irq-alpine-msi.c
index 5ddb8e578ac6..604459372fdd 100644
--- a/drivers/irqchip/irq-alpine-msi.c
+++ b/drivers/irqchip/irq-alpine-msi.c
@@ -204,16 +204,14 @@ static int alpine_msix_init_domains(struct alpine_msix_data *priv,
return -ENXIO;
}

- middle_domain = irq_domain_add_tree(NULL,
- &alpine_msix_middle_domain_ops,
- priv);
+ middle_domain = irq_domain_add_hierarchy(gic_domain, 0, 0, NULL,
+ &alpine_msix_middle_domain_ops,
+ priv);
if (!middle_domain) {
pr_err("Failed to create the MSIX middle domain\n");
return -ENOMEM;
}

- middle_domain->parent = gic_domain;
-
msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node),
&alpine_msix_domain_info,
middle_domain);
--
2.39.1


2023-02-13 10:44:18

by Johan Hovold

[permalink] [raw]
Subject: [PATCH v6 17/20] irqchip/gic-v3-mbi: Use irq_domain_create_hierarchy()

Use the irq_domain_create_hierarchy() helper to create the hierarchical
domain, which both serves as documentation and avoids poking at
irqdomain internals.

Reviewed-by: Philippe Mathieu-Daudé <[email protected]>
Tested-by: Hsin-Yi Wang <[email protected]>
Tested-by: Mark-PK Tsai <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
---
drivers/irqchip/irq-gic-v3-mbi.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/drivers/irqchip/irq-gic-v3-mbi.c b/drivers/irqchip/irq-gic-v3-mbi.c
index e1efdec9e9ac..dbb8b1efda44 100644
--- a/drivers/irqchip/irq-gic-v3-mbi.c
+++ b/drivers/irqchip/irq-gic-v3-mbi.c
@@ -233,13 +233,12 @@ static int mbi_allocate_domains(struct irq_domain *parent)
struct irq_domain *nexus_domain, *pci_domain, *plat_domain;
int err;

- nexus_domain = irq_domain_create_tree(parent->fwnode,
- &mbi_domain_ops, NULL);
+ nexus_domain = irq_domain_create_hierarchy(parent, 0, 0, parent->fwnode,
+ &mbi_domain_ops, NULL);
if (!nexus_domain)
return -ENOMEM;

irq_domain_update_bus_token(nexus_domain, DOMAIN_BUS_NEXUS);
- nexus_domain->parent = parent;

err = mbi_allocate_pci_domain(nexus_domain, &pci_domain);

--
2.39.1


2023-02-13 10:44:22

by Johan Hovold

[permalink] [raw]
Subject: [PATCH v6 16/20] irqchip/gic-v3-its: Use irq_domain_create_hierarchy()

Use the irq_domain_create_hierarchy() helper to create the hierarchical
domain, which both serves as documentation and avoids poking at
irqdomain internals.

Note that the domain host_data was first set to the struct its_node
during allocation only to immediately be overwritten with the struct
msi_domain_info.

Tested-by: Hsin-Yi Wang <[email protected]>
Tested-by: Mark-PK Tsai <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
---
drivers/irqchip/irq-gic-v3-its.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 973ede0197e3..5634d29b644d 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -4909,18 +4909,19 @@ static int its_init_domain(struct fwnode_handle *handle, struct its_node *its)
if (!info)
return -ENOMEM;

- inner_domain = irq_domain_create_tree(handle, &its_domain_ops, its);
+ info->ops = &its_msi_domain_ops;
+ info->data = its;
+
+ inner_domain = irq_domain_create_hierarchy(its_parent,
+ its->msi_domain_flags, 0,
+ handle, &its_domain_ops,
+ info);
if (!inner_domain) {
kfree(info);
return -ENOMEM;
}

- inner_domain->parent = its_parent;
irq_domain_update_bus_token(inner_domain, DOMAIN_BUS_NEXUS);
- inner_domain->flags |= its->msi_domain_flags;
- info->ops = &its_msi_domain_ops;
- info->data = its;
- inner_domain->host_data = info;

return 0;
}
--
2.39.1


2023-02-13 10:44:24

by Johan Hovold

[permalink] [raw]
Subject: [PATCH v6 18/20] irqchip/loongson-pch-msi: Use irq_domain_create_hierarchy()

Use the irq_domain_create_hierarchy() helper to create the hierarchical
domain, which both serves as documentation and avoids poking at
irqdomain internals.

Reviewed-by: Philippe Mathieu-Daudé <[email protected]>
Tested-by: Hsin-Yi Wang <[email protected]>
Tested-by: Mark-PK Tsai <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
---
drivers/irqchip/irq-loongson-pch-msi.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/drivers/irqchip/irq-loongson-pch-msi.c b/drivers/irqchip/irq-loongson-pch-msi.c
index a72ede90ffc6..6e1e1f011bb2 100644
--- a/drivers/irqchip/irq-loongson-pch-msi.c
+++ b/drivers/irqchip/irq-loongson-pch-msi.c
@@ -163,16 +163,15 @@ static int pch_msi_init_domains(struct pch_msi_data *priv,
{
struct irq_domain *middle_domain, *msi_domain;

- middle_domain = irq_domain_create_linear(domain_handle,
- priv->num_irqs,
- &pch_msi_middle_domain_ops,
- priv);
+ middle_domain = irq_domain_create_hierarchy(parent, 0, priv->num_irqs,
+ domain_handle,
+ &pch_msi_middle_domain_ops,
+ priv);
if (!middle_domain) {
pr_err("Failed to create the MSI middle domain\n");
return -ENOMEM;
}

- middle_domain->parent = parent;
irq_domain_update_bus_token(middle_domain, DOMAIN_BUS_NEXUS);

msi_domain = pci_msi_create_irq_domain(domain_handle,
--
2.39.1


2023-02-13 10:44:26

by Johan Hovold

[permalink] [raw]
Subject: [PATCH v6 13/20] x86/uv: Use irq_domain_create_hierarchy()

Use the irq_domain_create_hierarchy() helper to create the hierarchical
domain, which both serves as documentation and avoids poking at
irqdomain internals.

Reviewed-by: Philippe Mathieu-Daudé <[email protected]>
Tested-by: Hsin-Yi Wang <[email protected]>
Tested-by: Mark-PK Tsai <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
---
arch/x86/platform/uv/uv_irq.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/arch/x86/platform/uv/uv_irq.c b/arch/x86/platform/uv/uv_irq.c
index 1a536a187d74..ee21d6a36a80 100644
--- a/arch/x86/platform/uv/uv_irq.c
+++ b/arch/x86/platform/uv/uv_irq.c
@@ -166,10 +166,9 @@ static struct irq_domain *uv_get_irq_domain(void)
if (!fn)
goto out;

- uv_domain = irq_domain_create_tree(fn, &uv_domain_ops, NULL);
- if (uv_domain)
- uv_domain->parent = x86_vector_domain;
- else
+ uv_domain = irq_domain_create_hierarchy(x86_vector_domain, 0, 0, fn,
+ &uv_domain_ops, NULL);
+ if (!uv_domain)
irq_domain_free_fwnode(fn);
out:
mutex_unlock(&uv_lock);
--
2.39.1


2023-02-13 19:40:46

by tip-bot2 for Jacob Pan

[permalink] [raw]
Subject: [irqchip: irq/irqchip-next] irqchip/loongson-pch-msi: Use irq_domain_create_hierarchy()

The following commit has been merged into the irq/irqchip-next branch of irqchip:

Commit-ID: 6159c470f812eab0a2f1900c70acbb3ca7b9e14a
Gitweb: https://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms/6159c470f812eab0a2f1900c70acbb3ca7b9e14a
Author: Johan Hovold <[email protected]>
AuthorDate: Mon, 13 Feb 2023 11:43:00 +01:00
Committer: Marc Zyngier <[email protected]>
CommitterDate: Mon, 13 Feb 2023 19:31:25

irqchip/loongson-pch-msi: Use irq_domain_create_hierarchy()

Use the irq_domain_create_hierarchy() helper to create the hierarchical
domain, which both serves as documentation and avoids poking at
irqdomain internals.

Reviewed-by: Philippe Mathieu-Daudé <[email protected]>
Tested-by: Hsin-Yi Wang <[email protected]>
Tested-by: Mark-PK Tsai <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
Signed-off-by: Marc Zyngier <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
drivers/irqchip/irq-loongson-pch-msi.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/drivers/irqchip/irq-loongson-pch-msi.c b/drivers/irqchip/irq-loongson-pch-msi.c
index a72ede9..6e1e1f0 100644
--- a/drivers/irqchip/irq-loongson-pch-msi.c
+++ b/drivers/irqchip/irq-loongson-pch-msi.c
@@ -163,16 +163,15 @@ static int pch_msi_init_domains(struct pch_msi_data *priv,
{
struct irq_domain *middle_domain, *msi_domain;

- middle_domain = irq_domain_create_linear(domain_handle,
- priv->num_irqs,
- &pch_msi_middle_domain_ops,
- priv);
+ middle_domain = irq_domain_create_hierarchy(parent, 0, priv->num_irqs,
+ domain_handle,
+ &pch_msi_middle_domain_ops,
+ priv);
if (!middle_domain) {
pr_err("Failed to create the MSI middle domain\n");
return -ENOMEM;
}

- middle_domain->parent = parent;
irq_domain_update_bus_token(middle_domain, DOMAIN_BUS_NEXUS);

msi_domain = pci_msi_create_irq_domain(domain_handle,

2023-02-13 19:40:50

by tip-bot2 for Jacob Pan

[permalink] [raw]
Subject: [irqchip: irq/irqchip-next] irqchip/gic-v3-its: Use irq_domain_create_hierarchy()

The following commit has been merged into the irq/irqchip-next branch of irqchip:

Commit-ID: 1e46e040decede1723a5b11f0689942134c29d9a
Gitweb: https://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms/1e46e040decede1723a5b11f0689942134c29d9a
Author: Johan Hovold <[email protected]>
AuthorDate: Mon, 13 Feb 2023 11:42:58 +01:00
Committer: Marc Zyngier <[email protected]>
CommitterDate: Mon, 13 Feb 2023 19:31:25

irqchip/gic-v3-its: Use irq_domain_create_hierarchy()

Use the irq_domain_create_hierarchy() helper to create the hierarchical
domain, which both serves as documentation and avoids poking at
irqdomain internals.

Note that the domain host_data was first set to the struct its_node
during allocation only to immediately be overwritten with the struct
msi_domain_info.

Tested-by: Hsin-Yi Wang <[email protected]>
Tested-by: Mark-PK Tsai <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
Signed-off-by: Marc Zyngier <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
drivers/irqchip/irq-gic-v3-its.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 973ede0..5634d29 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -4909,18 +4909,19 @@ static int its_init_domain(struct fwnode_handle *handle, struct its_node *its)
if (!info)
return -ENOMEM;

- inner_domain = irq_domain_create_tree(handle, &its_domain_ops, its);
+ info->ops = &its_msi_domain_ops;
+ info->data = its;
+
+ inner_domain = irq_domain_create_hierarchy(its_parent,
+ its->msi_domain_flags, 0,
+ handle, &its_domain_ops,
+ info);
if (!inner_domain) {
kfree(info);
return -ENOMEM;
}

- inner_domain->parent = its_parent;
irq_domain_update_bus_token(inner_domain, DOMAIN_BUS_NEXUS);
- inner_domain->flags |= its->msi_domain_flags;
- info->ops = &its_msi_domain_ops;
- info->data = its;
- inner_domain->host_data = info;

return 0;
}

2023-02-13 19:40:53

by tip-bot2 for Jacob Pan

[permalink] [raw]
Subject: [irqchip: irq/irqchip-next] irqchip/gic-v3-mbi: Use irq_domain_create_hierarchy()

The following commit has been merged into the irq/irqchip-next branch of irqchip:

Commit-ID: 331f9aac03267f76a15776260eda4f47b81731f7
Gitweb: https://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms/331f9aac03267f76a15776260eda4f47b81731f7
Author: Johan Hovold <[email protected]>
AuthorDate: Mon, 13 Feb 2023 11:42:59 +01:00
Committer: Marc Zyngier <[email protected]>
CommitterDate: Mon, 13 Feb 2023 19:31:25

irqchip/gic-v3-mbi: Use irq_domain_create_hierarchy()

Use the irq_domain_create_hierarchy() helper to create the hierarchical
domain, which both serves as documentation and avoids poking at
irqdomain internals.

Reviewed-by: Philippe Mathieu-Daudé <[email protected]>
Tested-by: Hsin-Yi Wang <[email protected]>
Tested-by: Mark-PK Tsai <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
Signed-off-by: Marc Zyngier <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
drivers/irqchip/irq-gic-v3-mbi.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/drivers/irqchip/irq-gic-v3-mbi.c b/drivers/irqchip/irq-gic-v3-mbi.c
index e1efdec..dbb8b1e 100644
--- a/drivers/irqchip/irq-gic-v3-mbi.c
+++ b/drivers/irqchip/irq-gic-v3-mbi.c
@@ -233,13 +233,12 @@ static int mbi_allocate_domains(struct irq_domain *parent)
struct irq_domain *nexus_domain, *pci_domain, *plat_domain;
int err;

- nexus_domain = irq_domain_create_tree(parent->fwnode,
- &mbi_domain_ops, NULL);
+ nexus_domain = irq_domain_create_hierarchy(parent, 0, 0, parent->fwnode,
+ &mbi_domain_ops, NULL);
if (!nexus_domain)
return -ENOMEM;

irq_domain_update_bus_token(nexus_domain, DOMAIN_BUS_NEXUS);
- nexus_domain->parent = parent;

err = mbi_allocate_pci_domain(nexus_domain, &pci_domain);


2023-02-13 19:40:55

by tip-bot2 for Jacob Pan

[permalink] [raw]
Subject: [irqchip: irq/irqchip-next] irqdomain: Switch to per-domain locking

The following commit has been merged into the irq/irqchip-next branch of irqchip:

Commit-ID: 9dbb8e3452aba34e6fa4f63054b3adc66aceb7ec
Gitweb: https://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms/9dbb8e3452aba34e6fa4f63054b3adc66aceb7ec
Author: Johan Hovold <[email protected]>
AuthorDate: Mon, 13 Feb 2023 11:43:02 +01:00
Committer: Marc Zyngier <[email protected]>
CommitterDate: Mon, 13 Feb 2023 19:31:25

irqdomain: Switch to per-domain locking

The IRQ domain structures are currently protected by the global
irq_domain_mutex. Switch to using more fine-grained per-domain locking,
which can speed up parallel probing by reducing lock contention.

On a recent arm64 laptop, the total time spent waiting for the locks
during boot drops from 160 to 40 ms on average, while the maximum
aggregate wait time drops from 550 to 90 ms over ten runs for example.

Note that the domain lock of the root domain (innermost domain) must be
used for hierarchical domains. For non-hierarchical domains (as for root
domains), the new root pointer is set to the domain itself so that
&domain->root->mutex always points to the right lock.

Also note that hierarchical domains should be constructed using
irq_domain_create_hierarchy() (or irq_domain_add_hierarchy()) to avoid
having racing allocations access a not fully initialised domain. As a
safeguard, the lockdep assertion in irq_domain_set_mapping() will catch
any offenders that also fail to set the root domain pointer.

Tested-by: Hsin-Yi Wang <[email protected]>
Tested-by: Mark-PK Tsai <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
Signed-off-by: Marc Zyngier <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
include/linux/irqdomain.h | 4 +++-
kernel/irq/irqdomain.c | 59 +++++++++++++++++++++++++-------------
2 files changed, 43 insertions(+), 20 deletions(-)

diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h
index 16399de..d320d15 100644
--- a/include/linux/irqdomain.h
+++ b/include/linux/irqdomain.h
@@ -125,6 +125,8 @@ struct irq_domain_chip_generic;
* core code.
* @flags: Per irq_domain flags
* @mapcount: The number of mapped interrupts
+ * @mutex: Domain lock, hierarchical domains use root domain's lock
+ * @root: Pointer to root domain, or containing structure if non-hierarchical
*
* Optional elements:
* @fwnode: Pointer to firmware node associated with the irq_domain. Pretty easy
@@ -152,6 +154,8 @@ struct irq_domain {
void *host_data;
unsigned int flags;
unsigned int mapcount;
+ struct mutex mutex;
+ struct irq_domain *root;

/* Optional data */
struct fwnode_handle *fwnode;
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 6d480dc..1983f1b 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -215,6 +215,17 @@ static struct irq_domain *__irq_domain_create(struct fwnode_handle *fwnode,

domain->revmap_size = size;

+ /*
+ * Hierarchical domains use the domain lock of the root domain
+ * (innermost domain).
+ *
+ * For non-hierarchical domains (as for root domains), the root
+ * pointer is set to the domain itself so that &domain->root->mutex
+ * always points to the right lock.
+ */
+ mutex_init(&domain->mutex);
+ domain->root = domain;
+
irq_domain_check_hierarchy(domain);

return domain;
@@ -524,7 +535,7 @@ static bool irq_domain_is_nomap(struct irq_domain *domain)
static void irq_domain_clear_mapping(struct irq_domain *domain,
irq_hw_number_t hwirq)
{
- lockdep_assert_held(&irq_domain_mutex);
+ lockdep_assert_held(&domain->root->mutex);

if (irq_domain_is_nomap(domain))
return;
@@ -539,7 +550,11 @@ static void irq_domain_set_mapping(struct irq_domain *domain,
irq_hw_number_t hwirq,
struct irq_data *irq_data)
{
- lockdep_assert_held(&irq_domain_mutex);
+ /*
+ * This also makes sure that all domains point to the same root when
+ * called from irq_domain_insert_irq() for each domain in a hierarchy.
+ */
+ lockdep_assert_held(&domain->root->mutex);

if (irq_domain_is_nomap(domain))
return;
@@ -561,7 +576,7 @@ static void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)

hwirq = irq_data->hwirq;

- mutex_lock(&irq_domain_mutex);
+ mutex_lock(&domain->root->mutex);

irq_set_status_flags(irq, IRQ_NOREQUEST);

@@ -583,7 +598,7 @@ static void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)
/* Clear reverse map for this hwirq */
irq_domain_clear_mapping(domain, hwirq);

- mutex_unlock(&irq_domain_mutex);
+ mutex_unlock(&domain->root->mutex);
}

static int irq_domain_associate_locked(struct irq_domain *domain, unsigned int virq,
@@ -633,9 +648,9 @@ int irq_domain_associate(struct irq_domain *domain, unsigned int virq,
{
int ret;

- mutex_lock(&irq_domain_mutex);
+ mutex_lock(&domain->root->mutex);
ret = irq_domain_associate_locked(domain, virq, hwirq);
- mutex_unlock(&irq_domain_mutex);
+ mutex_unlock(&domain->root->mutex);

return ret;
}
@@ -752,7 +767,7 @@ unsigned int irq_create_mapping_affinity(struct irq_domain *domain,
return 0;
}

- mutex_lock(&irq_domain_mutex);
+ mutex_lock(&domain->root->mutex);

/* Check if mapping already exists */
virq = irq_find_mapping(domain, hwirq);
@@ -763,7 +778,7 @@ unsigned int irq_create_mapping_affinity(struct irq_domain *domain,

virq = irq_create_mapping_affinity_locked(domain, hwirq, affinity);
out:
- mutex_unlock(&irq_domain_mutex);
+ mutex_unlock(&domain->root->mutex);

return virq;
}
@@ -832,7 +847,7 @@ unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
if (WARN_ON(type & ~IRQ_TYPE_SENSE_MASK))
type &= IRQ_TYPE_SENSE_MASK;

- mutex_lock(&irq_domain_mutex);
+ mutex_lock(&domain->root->mutex);

/*
* If we've already configured this interrupt,
@@ -892,7 +907,7 @@ unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
/* Store trigger type */
irqd_set_trigger_type(irq_data, type);
out:
- mutex_unlock(&irq_domain_mutex);
+ mutex_unlock(&domain->root->mutex);

return virq;
}
@@ -1157,6 +1172,7 @@ struct irq_domain *irq_domain_create_hierarchy(struct irq_domain *parent,
domain = __irq_domain_create(fwnode, 0, ~0, 0, ops, host_data);

if (domain) {
+ domain->root = parent->root;
domain->parent = parent;
domain->flags |= flags;

@@ -1555,10 +1571,10 @@ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
return -EINVAL;
}

- mutex_lock(&irq_domain_mutex);
+ mutex_lock(&domain->root->mutex);
ret = irq_domain_alloc_irqs_locked(domain, irq_base, nr_irqs, node, arg,
realloc, affinity);
- mutex_unlock(&irq_domain_mutex);
+ mutex_unlock(&domain->root->mutex);

return ret;
}
@@ -1569,7 +1585,7 @@ static void irq_domain_fix_revmap(struct irq_data *d)
{
void __rcu **slot;

- lockdep_assert_held(&irq_domain_mutex);
+ lockdep_assert_held(&d->domain->root->mutex);

if (irq_domain_is_nomap(d->domain))
return;
@@ -1635,7 +1651,7 @@ int irq_domain_push_irq(struct irq_domain *domain, int virq, void *arg)
if (!parent_irq_data)
return -ENOMEM;

- mutex_lock(&irq_domain_mutex);
+ mutex_lock(&domain->root->mutex);

/* Copy the original irq_data. */
*parent_irq_data = *irq_data;
@@ -1663,7 +1679,7 @@ int irq_domain_push_irq(struct irq_domain *domain, int virq, void *arg)
irq_domain_fix_revmap(parent_irq_data);
irq_domain_set_mapping(domain, irq_data->hwirq, irq_data);
error:
- mutex_unlock(&irq_domain_mutex);
+ mutex_unlock(&domain->root->mutex);

return rv;
}
@@ -1718,7 +1734,7 @@ int irq_domain_pop_irq(struct irq_domain *domain, int virq)
if (WARN_ON(!parent_irq_data))
return -EINVAL;

- mutex_lock(&irq_domain_mutex);
+ mutex_lock(&domain->root->mutex);

irq_data->parent_data = NULL;

@@ -1730,7 +1746,7 @@ int irq_domain_pop_irq(struct irq_domain *domain, int virq)

irq_domain_fix_revmap(irq_data);

- mutex_unlock(&irq_domain_mutex);
+ mutex_unlock(&domain->root->mutex);

kfree(parent_irq_data);

@@ -1746,17 +1762,20 @@ EXPORT_SYMBOL_GPL(irq_domain_pop_irq);
void irq_domain_free_irqs(unsigned int virq, unsigned int nr_irqs)
{
struct irq_data *data = irq_get_irq_data(virq);
+ struct irq_domain *domain;
int i;

if (WARN(!data || !data->domain || !data->domain->ops->free,
"NULL pointer, cannot free irq\n"))
return;

- mutex_lock(&irq_domain_mutex);
+ domain = data->domain;
+
+ mutex_lock(&domain->root->mutex);
for (i = 0; i < nr_irqs; i++)
irq_domain_remove_irq(virq + i);
- irq_domain_free_irqs_hierarchy(data->domain, virq, nr_irqs);
- mutex_unlock(&irq_domain_mutex);
+ irq_domain_free_irqs_hierarchy(domain, virq, nr_irqs);
+ mutex_unlock(&domain->root->mutex);

irq_domain_free_irq_data(virq, nr_irqs);
irq_free_descs(virq, nr_irqs);

2023-02-13 19:40:58

by tip-bot2 for Jacob Pan

[permalink] [raw]
Subject: [irqchip: irq/irqchip-next] irqchip/alpine-msi: Use irq_domain_add_hierarchy()

The following commit has been merged into the irq/irqchip-next branch of irqchip:

Commit-ID: 6c889231e04dffa4b668c1f0fbc26db679600147
Gitweb: https://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms/6c889231e04dffa4b668c1f0fbc26db679600147
Author: Johan Hovold <[email protected]>
AuthorDate: Mon, 13 Feb 2023 11:42:56 +01:00
Committer: Marc Zyngier <[email protected]>
CommitterDate: Mon, 13 Feb 2023 19:31:25

irqchip/alpine-msi: Use irq_domain_add_hierarchy()

Use the irq_domain_add_hierarchy() helper to create the hierarchical
domain, which both serves as documentation and avoids poking at
irqdomain internals.

Reviewed-by: Philippe Mathieu-Daudé <[email protected]>
Tested-by: Hsin-Yi Wang <[email protected]>
Tested-by: Mark-PK Tsai <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
Signed-off-by: Marc Zyngier <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
drivers/irqchip/irq-alpine-msi.c | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/drivers/irqchip/irq-alpine-msi.c b/drivers/irqchip/irq-alpine-msi.c
index 5ddb8e5..6044593 100644
--- a/drivers/irqchip/irq-alpine-msi.c
+++ b/drivers/irqchip/irq-alpine-msi.c
@@ -204,16 +204,14 @@ static int alpine_msix_init_domains(struct alpine_msix_data *priv,
return -ENXIO;
}

- middle_domain = irq_domain_add_tree(NULL,
- &alpine_msix_middle_domain_ops,
- priv);
+ middle_domain = irq_domain_add_hierarchy(gic_domain, 0, 0, NULL,
+ &alpine_msix_middle_domain_ops,
+ priv);
if (!middle_domain) {
pr_err("Failed to create the MSIX middle domain\n");
return -ENOMEM;
}

- middle_domain->parent = gic_domain;
-
msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node),
&alpine_msix_domain_info,
middle_domain);

2023-02-13 19:41:01

by tip-bot2 for Jacob Pan

[permalink] [raw]
Subject: [irqchip: irq/irqchip-next] irqchip/gic-v2m: Use irq_domain_create_hierarchy()

The following commit has been merged into the irq/irqchip-next branch of irqchip:

Commit-ID: e6e8cd62a56f9ed0dcdf8d01147709b59a111418
Gitweb: https://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms/e6e8cd62a56f9ed0dcdf8d01147709b59a111418
Author: Johan Hovold <[email protected]>
AuthorDate: Mon, 13 Feb 2023 11:42:57 +01:00
Committer: Marc Zyngier <[email protected]>
CommitterDate: Mon, 13 Feb 2023 19:31:25

irqchip/gic-v2m: Use irq_domain_create_hierarchy()

Use the irq_domain_create_hierarchy() helper to create the hierarchical
domain, which both serves as documentation and avoids poking at
irqdomain internals.

Reviewed-by: Philippe Mathieu-Daudé <[email protected]>
Tested-by: Hsin-Yi Wang <[email protected]>
Tested-by: Mark-PK Tsai <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
Signed-off-by: Marc Zyngier <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
drivers/irqchip/irq-gic-v2m.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/drivers/irqchip/irq-gic-v2m.c b/drivers/irqchip/irq-gic-v2m.c
index f4d7eeb..f1e75b3 100644
--- a/drivers/irqchip/irq-gic-v2m.c
+++ b/drivers/irqchip/irq-gic-v2m.c
@@ -287,15 +287,14 @@ static __init int gicv2m_allocate_domains(struct irq_domain *parent)
if (!v2m)
return 0;

- inner_domain = irq_domain_create_tree(v2m->fwnode,
- &gicv2m_domain_ops, v2m);
+ inner_domain = irq_domain_create_hierarchy(parent, 0, 0, v2m->fwnode,
+ &gicv2m_domain_ops, v2m);
if (!inner_domain) {
pr_err("Failed to create GICv2m domain\n");
return -ENOMEM;
}

irq_domain_update_bus_token(inner_domain, DOMAIN_BUS_NEXUS);
- inner_domain->parent = parent;
pci_domain = pci_msi_create_irq_domain(v2m->fwnode,
&gicv2m_msi_domain_info,
inner_domain);

2023-02-13 19:41:04

by tip-bot2 for Jacob Pan

[permalink] [raw]
Subject: [irqchip: irq/irqchip-next] x86/ioapic: Use irq_domain_create_hierarchy()

The following commit has been merged into the irq/irqchip-next branch of irqchip:

Commit-ID: bc1bc1b309b42ff3faea957df7ac9382366c5eef
Gitweb: https://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms/bc1bc1b309b42ff3faea957df7ac9382366c5eef
Author: Johan Hovold <[email protected]>
AuthorDate: Mon, 13 Feb 2023 11:42:54 +01:00
Committer: Marc Zyngier <[email protected]>
CommitterDate: Mon, 13 Feb 2023 19:31:24

x86/ioapic: Use irq_domain_create_hierarchy()

Use the irq_domain_create_hierarchy() helper to create the hierarchical
domain, which both serves as documentation and avoids poking at
irqdomain internals.

Reviewed-by: Philippe Mathieu-Daudé <[email protected]>
Tested-by: Hsin-Yi Wang <[email protected]>
Tested-by: Mark-PK Tsai <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
Signed-off-by: Marc Zyngier <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
arch/x86/kernel/apic/io_apic.c | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c
index a868b76..1f83b05 100644
--- a/arch/x86/kernel/apic/io_apic.c
+++ b/arch/x86/kernel/apic/io_apic.c
@@ -2364,9 +2364,8 @@ static int mp_irqdomain_create(int ioapic)
return -ENODEV;
}

- ip->irqdomain = irq_domain_create_linear(fn, hwirqs, cfg->ops,
- (void *)(long)ioapic);
-
+ ip->irqdomain = irq_domain_create_hierarchy(parent, 0, hwirqs, fn, cfg->ops,
+ (void *)(long)ioapic);
if (!ip->irqdomain) {
/* Release fw handle if it was allocated above */
if (!cfg->dev)
@@ -2374,8 +2373,6 @@ static int mp_irqdomain_create(int ioapic)
return -ENOMEM;
}

- ip->irqdomain->parent = parent;
-
if (cfg->type == IOAPIC_DOMAIN_LEGACY ||
cfg->type == IOAPIC_DOMAIN_STRICT)
ioapic_dynirq_base = max(ioapic_dynirq_base,

2023-02-13 19:41:08

by tip-bot2 for Jacob Pan

[permalink] [raw]
Subject: [irqchip: irq/irqchip-next] x86/uv: Use irq_domain_create_hierarchy()

The following commit has been merged into the irq/irqchip-next branch of irqchip:

Commit-ID: a14e7fdd43040a2a5da2cc979063b3c958015aa9
Gitweb: https://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms/a14e7fdd43040a2a5da2cc979063b3c958015aa9
Author: Johan Hovold <[email protected]>
AuthorDate: Mon, 13 Feb 2023 11:42:55 +01:00
Committer: Marc Zyngier <[email protected]>
CommitterDate: Mon, 13 Feb 2023 19:31:24

x86/uv: Use irq_domain_create_hierarchy()

Use the irq_domain_create_hierarchy() helper to create the hierarchical
domain, which both serves as documentation and avoids poking at
irqdomain internals.

Reviewed-by: Philippe Mathieu-Daudé <[email protected]>
Tested-by: Hsin-Yi Wang <[email protected]>
Tested-by: Mark-PK Tsai <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
Signed-off-by: Marc Zyngier <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
arch/x86/platform/uv/uv_irq.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/arch/x86/platform/uv/uv_irq.c b/arch/x86/platform/uv/uv_irq.c
index 1a536a1..ee21d6a 100644
--- a/arch/x86/platform/uv/uv_irq.c
+++ b/arch/x86/platform/uv/uv_irq.c
@@ -166,10 +166,9 @@ static struct irq_domain *uv_get_irq_domain(void)
if (!fn)
goto out;

- uv_domain = irq_domain_create_tree(fn, &uv_domain_ops, NULL);
- if (uv_domain)
- uv_domain->parent = x86_vector_domain;
- else
+ uv_domain = irq_domain_create_hierarchy(x86_vector_domain, 0, 0, fn,
+ &uv_domain_ops, NULL);
+ if (!uv_domain)
irq_domain_free_fwnode(fn);
out:
mutex_unlock(&uv_lock);

2023-02-13 19:41:11

by tip-bot2 for Jacob Pan

[permalink] [raw]
Subject: [irqchip: irq/irqchip-next] irqdomain: Drop leftover brackets

The following commit has been merged into the irq/irqchip-next branch of irqchip:

Commit-ID: 4e0d86df9344bfd1951eb2571e4ef8f3d37000a4
Gitweb: https://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms/4e0d86df9344bfd1951eb2571e4ef8f3d37000a4
Author: Johan Hovold <[email protected]>
AuthorDate: Mon, 13 Feb 2023 11:42:52 +01:00
Committer: Marc Zyngier <[email protected]>
CommitterDate: Mon, 13 Feb 2023 19:31:24

irqdomain: Drop leftover brackets

Drop some unnecessary brackets that were left in place when the
corresponding code was updated.

Reviewed-by: Philippe Mathieu-Daudé <[email protected]>
Tested-by: Hsin-Yi Wang <[email protected]>
Tested-by: Mark-PK Tsai <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
Signed-off-by: Marc Zyngier <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
kernel/irq/irqdomain.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 6bd6b61..3a3213d 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -210,9 +210,8 @@ static struct irq_domain *__irq_domain_create(struct fwnode_handle *fwnode,
domain->host_data = host_data;
domain->hwirq_max = hwirq_max;

- if (direct_max) {
+ if (direct_max)
domain->flags |= IRQ_DOMAIN_FLAG_NO_MAP;
- }

domain->revmap_size = size;

@@ -652,9 +651,8 @@ void irq_domain_associate_many(struct irq_domain *domain, unsigned int irq_base,
pr_debug("%s(%s, irqbase=%i, hwbase=%i, count=%i)\n", __func__,
of_node_full_name(of_node), irq_base, (int)hwirq_base, count);

- for (i = 0; i < count; i++) {
+ for (i = 0; i < count; i++)
irq_domain_associate(domain, irq_base + i, hwirq_base + i);
- }
}
EXPORT_SYMBOL_GPL(irq_domain_associate_many);


2023-02-13 19:41:14

by tip-bot2 for Jacob Pan

[permalink] [raw]
Subject: [irqchip: irq/irqchip-next] irqdomain: Drop dead domain-name assignment

The following commit has been merged into the irq/irqchip-next branch of irqchip:

Commit-ID: 28a9ff23d8b56db09cb01cef174a205ea5e2ca49
Gitweb: https://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms/28a9ff23d8b56db09cb01cef174a205ea5e2ca49
Author: Johan Hovold <[email protected]>
AuthorDate: Mon, 13 Feb 2023 11:42:51 +01:00
Committer: Marc Zyngier <[email protected]>
CommitterDate: Mon, 13 Feb 2023 19:31:24

irqdomain: Drop dead domain-name assignment

Since commit d59f6617eef0 ("genirq: Allow fwnode to carry name
information only") an IRQ domain is always given a name during
allocation (e.g. used for the debugfs entry).

Drop the leftover name assignment when allocating the first IRQ.

Tested-by: Hsin-Yi Wang <[email protected]>
Tested-by: Mark-PK Tsai <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
Signed-off-by: Marc Zyngier <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
kernel/irq/irqdomain.c | 8 --------
1 file changed, 8 deletions(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index c7113e7..6bd6b61 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -619,10 +619,6 @@ static int irq_domain_associate_locked(struct irq_domain *domain, unsigned int v
irq_data->hwirq = 0;
return ret;
}
-
- /* If not already assigned, give the domain the chip's name */
- if (!domain->name && irq_data->chip)
- domain->name = irq_data->chip->name;
}

domain->mapcount++;
@@ -1182,10 +1178,6 @@ static void irq_domain_insert_irq(int virq)

domain->mapcount++;
irq_domain_set_mapping(domain, data->hwirq, data);
-
- /* If not already assigned, give the domain the chip's name */
- if (!domain->name && data->chip)
- domain->name = data->chip->name;
}

irq_clear_status_flags(virq, IRQ_NOREQUEST);

2023-02-13 19:41:29

by tip-bot2 for Jacob Pan

[permalink] [raw]
Subject: [irqchip: irq/irqchip-next] irqdomain: Look for existing mapping only once

The following commit has been merged into the irq/irqchip-next branch of irqchip:

Commit-ID: 6e6f75c9c98d2d246d90411ff2b6f0cd271f4cba
Gitweb: https://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms/6e6f75c9c98d2d246d90411ff2b6f0cd271f4cba
Author: Johan Hovold <[email protected]>
AuthorDate: Mon, 13 Feb 2023 11:42:46 +01:00
Committer: Marc Zyngier <[email protected]>
CommitterDate: Mon, 13 Feb 2023 19:31:24

irqdomain: Look for existing mapping only once

Avoid looking for an existing mapping twice when creating a new mapping
using irq_create_fwspec_mapping() by factoring out the actual allocation
which is shared with irq_create_mapping_affinity().

The new helper function will also be used to fix a shared-interrupt
mapping race, hence the Fixes tag.

Fixes: b62b2cf5759b ("irqdomain: Fix handling of type settings for existing mappings")
Cc: [email protected] # 4.8
Tested-by: Hsin-Yi Wang <[email protected]>
Tested-by: Mark-PK Tsai <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
Signed-off-by: Marc Zyngier <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
kernel/irq/irqdomain.c | 60 ++++++++++++++++++++++-------------------
1 file changed, 33 insertions(+), 27 deletions(-)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 9f5b96c..9f95047 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -682,6 +682,34 @@ unsigned int irq_create_direct_mapping(struct irq_domain *domain)
EXPORT_SYMBOL_GPL(irq_create_direct_mapping);
#endif

+static unsigned int __irq_create_mapping_affinity(struct irq_domain *domain,
+ irq_hw_number_t hwirq,
+ const struct irq_affinity_desc *affinity)
+{
+ struct device_node *of_node = irq_domain_get_of_node(domain);
+ int virq;
+
+ pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq);
+
+ /* Allocate a virtual interrupt number */
+ virq = irq_domain_alloc_descs(-1, 1, hwirq, of_node_to_nid(of_node),
+ affinity);
+ if (virq <= 0) {
+ pr_debug("-> virq allocation failed\n");
+ return 0;
+ }
+
+ if (irq_domain_associate(domain, virq, hwirq)) {
+ irq_free_desc(virq);
+ return 0;
+ }
+
+ pr_debug("irq %lu on domain %s mapped to virtual irq %u\n",
+ hwirq, of_node_full_name(of_node), virq);
+
+ return virq;
+}
+
/**
* irq_create_mapping_affinity() - Map a hardware interrupt into linux irq space
* @domain: domain owning this hardware interrupt or NULL for default domain
@@ -694,14 +722,11 @@ EXPORT_SYMBOL_GPL(irq_create_direct_mapping);
* on the number returned from that call.
*/
unsigned int irq_create_mapping_affinity(struct irq_domain *domain,
- irq_hw_number_t hwirq,
- const struct irq_affinity_desc *affinity)
+ irq_hw_number_t hwirq,
+ const struct irq_affinity_desc *affinity)
{
- struct device_node *of_node;
int virq;

- pr_debug("irq_create_mapping(0x%p, 0x%lx)\n", domain, hwirq);
-
/* Look for default domain if necessary */
if (domain == NULL)
domain = irq_default_domain;
@@ -709,34 +734,15 @@ unsigned int irq_create_mapping_affinity(struct irq_domain *domain,
WARN(1, "%s(, %lx) called with NULL domain\n", __func__, hwirq);
return 0;
}
- pr_debug("-> using domain @%p\n", domain);
-
- of_node = irq_domain_get_of_node(domain);

/* Check if mapping already exists */
virq = irq_find_mapping(domain, hwirq);
if (virq) {
- pr_debug("-> existing mapping on virq %d\n", virq);
+ pr_debug("existing mapping on virq %d\n", virq);
return virq;
}

- /* Allocate a virtual interrupt number */
- virq = irq_domain_alloc_descs(-1, 1, hwirq, of_node_to_nid(of_node),
- affinity);
- if (virq <= 0) {
- pr_debug("-> virq allocation failed\n");
- return 0;
- }
-
- if (irq_domain_associate(domain, virq, hwirq)) {
- irq_free_desc(virq);
- return 0;
- }
-
- pr_debug("irq %lu on domain %s mapped to virtual irq %u\n",
- hwirq, of_node_full_name(of_node), virq);
-
- return virq;
+ return __irq_create_mapping_affinity(domain, hwirq, affinity);
}
EXPORT_SYMBOL_GPL(irq_create_mapping_affinity);

@@ -841,7 +847,7 @@ unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
return 0;
} else {
/* Create mapping */
- virq = irq_create_mapping(domain, hwirq);
+ virq = __irq_create_mapping_affinity(domain, hwirq, NULL);
if (!virq)
return virq;
}

2023-02-13 19:41:32

by tip-bot2 for Jacob Pan

[permalink] [raw]
Subject: [irqchip: irq/irqchip-next] irqdomain: Fix disassociation race

The following commit has been merged into the irq/irqchip-next branch of irqchip:

Commit-ID: 3f883c38f5628f46b30bccf090faec054088e262
Gitweb: https://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms/3f883c38f5628f46b30bccf090faec054088e262
Author: Johan Hovold <[email protected]>
AuthorDate: Mon, 13 Feb 2023 11:42:44 +01:00
Committer: Marc Zyngier <[email protected]>
CommitterDate: Mon, 13 Feb 2023 19:31:24

irqdomain: Fix disassociation race

The global irq_domain_mutex is held when mapping interrupts from
non-hierarchical domains but currently not when disposing them.

This specifically means that updates of the domain mapcount is racy
(currently only used for statistics in debugfs).

Make sure to hold the global irq_domain_mutex also when disposing
mappings from non-hierarchical domains.

Fixes: 9dc6be3d4193 ("genirq/irqdomain: Add map counter")
Cc: [email protected] # 4.13
Tested-by: Hsin-Yi Wang <[email protected]>
Tested-by: Mark-PK Tsai <[email protected]>
Signed-off-by: Johan Hovold <[email protected]>
Signed-off-by: Marc Zyngier <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
---
kernel/irq/irqdomain.c | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 6661de1..f77549a 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -538,6 +538,9 @@ static void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)
return;

hwirq = irq_data->hwirq;
+
+ mutex_lock(&irq_domain_mutex);
+
irq_set_status_flags(irq, IRQ_NOREQUEST);

/* remove chip and handler */
@@ -557,6 +560,8 @@ static void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)

/* Clear reverse map for this hwirq */
irq_domain_clear_mapping(domain, hwirq);
+
+ mutex_unlock(&irq_domain_mutex);
}

static int irq_domain_associate_locked(struct irq_domain *domain, unsigned int virq,

2023-03-07 13:52:38

by David Woodhouse

[permalink] [raw]
Subject: Re: [PATCH v6 20/20] irqdomain: Switch to per-domain locking

On Mon, 2023-02-13 at 11:43 +0100, Johan Hovold wrote:
> The IRQ domain structures are currently protected by the global
> irq_domain_mutex. Switch to using more fine-grained per-domain locking,
> which can speed up parallel probing by reducing lock contention.
>
> On a recent arm64 laptop, the total time spent waiting for the locks
> during boot drops from 160 to 40 ms on average, while the maximum
> aggregate wait time drops from 550 to 90 ms over ten runs for example.
>
> Note that the domain lock of the root domain (innermost domain) must be
> used for hierarchical domains. For non-hierarchical domains (as for root
> domains), the new root pointer is set to the domain itself so that
> &domain->root->mutex always points to the right lock.
>
> Also note that hierarchical domains should be constructed using
> irq_domain_create_hierarchy() (or irq_domain_add_hierarchy()) to avoid
> having racing allocations access a not fully initialised domain. As a
> safeguard, the lockdep assertion in irq_domain_set_mapping() will catch
> any offenders that also fail to set the root domain pointer.
>
> Tested-by: Hsin-Yi Wang <[email protected]>
> Tested-by: Mark-PK Tsai <[email protected]>
> Signed-off-by: Johan Hovold <[email protected]>

Broke Xen. And it's *so* easy to test. As long as you have qemu master
branch from no older than last Thursday, that is...

$ qemu-system-x86_64 -serial mon:stdio -display none \
-accel kvm,xen-version=0x4000e,kernel-irqchip=split \
-kernel arch/x86/boot/bzImage -append "console=ttyS0"

...

[ 0.466554] BUG: kernel NULL pointer dereference, address: 00000000000000c0
[ 0.467249] #PF: supervisor read access in kernel mode
[ 0.467249] #PF: error_code(0x0000) - not-present page
[ 0.467249] PGD 0 P4D 0
[ 0.467249] Oops: 0000 [#1] PREEMPT SMP PTI
[ 0.467249] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 6.2.0-rc4+ #1206
[ 0.467249] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.1-0-g3208b098f51a-prebuilt.qemu.org 04/01/2014
[ 0.467249] RIP: 0010:irq_domain_create_hierarchy+0x2c/0x70
[ 0.467249] Code: 1e fa 0f 1f 44 00 00 41 54 49 89 fc 48 89 cf 55 89 f5 53 85 d2 74 40 89 d6 31 c9 89 d2 e8 2c fa ff ff 48 89 c3 48 85 db 74 21 <49> 8b 84 24 c0 00 00 00 09 6b 28 48 89 df 4c 89 a3 f0 00 00 00 48
[ 0.467249] RSP: 0000:ffffc90000013e60 EFLAGS: 00010286
[ 0.467249] RAX: ffff8880053a1a00 RBX: ffff8880053a1a00 RCX: 0000000000000000
[ 0.467249] RDX: 0000000000000000 RSI: 0000000000000001 RDI: ffffffff84828fa0
[ 0.467249] RBP: 0000000000000010 R08: 0000000000000003 R09: 0000000000000000
[ 0.467249] R10: 0000000025a89be7 R11: 00000000442a63fa R12: 0000000000000000
[ 0.467249] R13: ffffffff83ac1b98 R14: 0000000000000000 R15: 0000000000000000
[ 0.467249] FS: 0000000000000000(0000) GS:ffff888007a00000(0000) knlGS:0000000000000000
[ 0.467249] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 0.467249] CR2: 00000000000000c0 CR3: 0000000002824000 CR4: 00000000000006f0
[ 0.467249] Call Trace:
[ 0.467249] <TASK>
[ 0.467249] ? __pfx_pci_arch_init+0x10/0x10
[ 0.467249] __msi_create_irq_domain+0x85/0x170
[ 0.467249] ? __pfx_pci_arch_init+0x10/0x10
[ 0.467249] xen_create_pci_msi_domain+0x34/0x40
[ 0.467249] x86_create_pci_msi_domain+0x12/0x1e
[ 0.467249] pci_arch_init+0x31/0x7a
[ 0.467249] ? __pfx_pci_arch_init+0x10/0x10
[ 0.467249] do_one_initcall+0x5f/0x320
[ 0.467249] ? rcu_read_lock_sched_held+0x43/0x80
[ 0.467249] kernel_init_freeable+0x189/0x1c6
[ 0.467249] ? __pfx_kernel_init+0x10/0x10
[ 0.467249] kernel_init+0x1a/0x130
[ 0.467249] ret_from_fork+0x2c/0x50
[ 0.467249] </TASK>
[ 0.467249] Modules linked in:
[ 0.467249] CR2: 00000000000000c0
[ 0.467249] ---[ end trace 0000000000000000 ]---
[ 0.467249] RIP: 0010:irq_domain_create_hierarchy+0x2c/0x70
[ 0.467249] Code: 1e fa 0f 1f 44 00 00 41 54 49 89 fc 48 89 cf 55 89 f5 53 85 d2 74 40 89 d6 31 c9 89 d2 e8 2c fa ff ff 48 89 c3 48 85 db 74 21 <49> 8b 84 24 c0 00 00 00 09 6b 28 48 89 df 4c 89 a3 f0 00 00 00 48
[ 0.467249] RSP: 0000:ffffc90000013e60 EFLAGS: 00010286
[ 0.467249] RAX: ffff8880053a1a00 RBX: ffff8880053a1a00 RCX: 0000000000000000
[ 0.467249] RDX: 0000000000000000 RSI: 0000000000000001 RDI: ffffffff84828fa0
[ 0.467249] RBP: 0000000000000010 R08: 0000000000000003 R09: 0000000000000000
[ 0.467249] R10: 0000000025a89be7 R11: 00000000442a63fa R12: 0000000000000000
[ 0.467249] R13: ffffffff83ac1b98 R14: 0000000000000000 R15: 0000000000000000
[ 0.467249] FS: 0000000000000000(0000) GS:ffff888007a00000(0000) knlGS:0000000000000000
[ 0.467249] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 0.467249] CR2: 00000000000000c0 CR3: 0000000002824000 CR4: 00000000000006f0
[ 0.467249] Kernel panic - not syncing: Fatal exception


Attachments:
smime.p7s (5.83 kB)

2023-03-07 14:06:19

by Juergen Gross

[permalink] [raw]
Subject: Re: [PATCH v6 20/20] irqdomain: Switch to per-domain locking

On 07.03.23 14:51, David Woodhouse wrote:
> On Mon, 2023-02-13 at 11:43 +0100, Johan Hovold wrote:
>> The IRQ domain structures are currently protected by the global
>> irq_domain_mutex. Switch to using more fine-grained per-domain locking,
>> which can speed up parallel probing by reducing lock contention.
>>
>> On a recent arm64 laptop, the total time spent waiting for the locks
>> during boot drops from 160 to 40 ms on average, while the maximum
>> aggregate wait time drops from 550 to 90 ms over ten runs for example.
>>
>> Note that the domain lock of the root domain (innermost domain) must be
>> used for hierarchical domains. For non-hierarchical domains (as for root
>> domains), the new root pointer is set to the domain itself so that
>> &domain->root->mutex always points to the right lock.
>>
>> Also note that hierarchical domains should be constructed using
>> irq_domain_create_hierarchy() (or irq_domain_add_hierarchy()) to avoid
>> having racing allocations access a not fully initialised domain. As a
>> safeguard, the lockdep assertion in irq_domain_set_mapping() will catch
>> any offenders that also fail to set the root domain pointer.
>>
>> Tested-by: Hsin-Yi Wang <[email protected]>
>> Tested-by: Mark-PK Tsai <[email protected]>
>> Signed-off-by: Johan Hovold <[email protected]>
>
> Broke Xen.

Fixed with commit ad32ab9604f2.


Juergen


Attachments:
OpenPGP_0xB0DE9DD628BF132F.asc (3.03 kB)
OpenPGP public key
OpenPGP_signature (495.00 B)
OpenPGP digital signature
Download all attachments

2023-03-07 14:24:17

by David Woodhouse

[permalink] [raw]
Subject: Re: [PATCH v6 20/20] irqdomain: Switch to per-domain locking

On Tue, 2023-03-07 at 15:06 +0100, Juergen Gross wrote:
> On 07.03.23 14:51, David Woodhouse wrote:
> > On Mon, 2023-02-13 at 11:43 +0100, Johan Hovold wrote:
> > > The IRQ domain structures are currently protected by the global
> > > irq_domain_mutex. Switch to using more fine-grained per-domain locking,
> > > which can speed up parallel probing by reducing lock contention.
> > >
> > > On a recent arm64 laptop, the total time spent waiting for the locks
> > > during boot drops from 160 to 40 ms on average, while the maximum
> > > aggregate wait time drops from 550 to 90 ms over ten runs for example.
> > >
> > > Note that the domain lock of the root domain (innermost domain) must be
> > > used for hierarchical domains. For non-hierarchical domains (as for root
> > > domains), the new root pointer is set to the domain itself so that
> > > &domain->root->mutex always points to the right lock.
> > >
> > > Also note that hierarchical domains should be constructed using
> > > irq_domain_create_hierarchy() (or irq_domain_add_hierarchy()) to avoid
> > > having racing allocations access a not fully initialised domain. As a
> > > safeguard, the lockdep assertion in irq_domain_set_mapping() will catch
> > > any offenders that also fail to set the root domain pointer.
> > >
> > > Tested-by: Hsin-Yi Wang <[email protected]>
> > > Tested-by: Mark-PK Tsai <[email protected]>
> > > Signed-off-by: Johan Hovold <[email protected]>
> >
> > Broke Xen.
>
> Fixed with commit ad32ab9604f2.

Thanks.


Attachments:
smime.p7s (5.83 kB)

2023-03-08 14:44:06

by Cyril Brulebois

[permalink] [raw]
Subject: Re: [PATCH v6 06/20] irqdomain: Fix mapping-creation race

Hi Johan,

And thanks so much for this patch series.

Johan Hovold <[email protected]> (2023-02-13):
> Parallel probing of devices that share interrupts (e.g. when a driver
> uses asynchronous probing) can currently result in two mappings for the
> same hardware interrupt to be created due to missing serialisation.
>
> Make sure to hold the irq_domain_mutex when creating mappings so that
> looking for an existing mapping before creating a new one is done
> atomically.

Just for information: This patch fixes a long-standing regression
regarding Raspberry Pi devices, which have been failing to boot (at
least reliably) due to MMC timeouts for a long while; I think that
started between v5.17 and v5.19, but I couldn't bisect at the time
(I was already chasing some other regression).

Example bug report:
https://bugs.debian.org/1019700

Before trying to pinpoint when the regression appeared, I've checked
these versions, with a Debian testing userspace as of 2023-03-07:
- v6.1.12: affected.
- v6.2: affected.
- v6.3-rc1: not affected.

A bisect between v6.2 and v6.3-rc1 led me to this patch specifically.
Seeing how it's part of a patch series, and how previous patches are
preliminary ones, I've checked that cherry-picking the first 6 patches
on top of v6.1.15 indeed fixes the problem there too, and it does
(git cherry-pick v6.2-rc4..601363cc08da25747feb87c55573dd54de91d66a).


With the following systems:
- Pi 4 B, using external storage (SD card),
- CM4 Lite on CM4 IO Board, using external storage (SD card),
- CM4 on CM4 IO Board, using internal storage (eMMC),

I've been able to verify that v6.1.12 (baseline in Debian testing)
triggers this MMC timeout issue, while v6.1.15 + the aforementioned
range of cherry-picked commits no longer triggers this issue.

(Methodology: cold boot then reboot 20 times, monitoring via serial
console to keep HDMI output of the equation; affected systems stop
booting after 1-4 boots; unaffected systems boot and reboot just fine
all the time.)


This looks like a critical bugfix for Raspberry Pi users.

Seeing the stable@ mention is about 4.8, I suppose this is going to be
considered for a wide range of kernels already… but I'm happy to dig
into this further to pinpoint when the regression appeared, if that's
helpful.


Cheers,
--
Cyril Brulebois ([email protected]) <https://debamax.com/>
D-I release manager -- Release team member -- Freelance Consultant


Attachments:
(No filename) (2.39 kB)
signature.asc (833.00 B)
Download all attachments

2023-03-08 14:53:59

by Marc Zyngier

[permalink] [raw]
Subject: Re: [PATCH v6 06/20] irqdomain: Fix mapping-creation race

On Wed, 08 Mar 2023 14:41:05 +0000,
Cyril Brulebois <[email protected]> wrote:
>
> Hi Johan,
>
> And thanks so much for this patch series.
>
> Johan Hovold <[email protected]> (2023-02-13):
> > Parallel probing of devices that share interrupts (e.g. when a driver
> > uses asynchronous probing) can currently result in two mappings for the
> > same hardware interrupt to be created due to missing serialisation.
> >
> > Make sure to hold the irq_domain_mutex when creating mappings so that
> > looking for an existing mapping before creating a new one is done
> > atomically.
>
> Just for information: This patch fixes a long-standing regression
> regarding Raspberry Pi devices, which have been failing to boot (at
> least reliably) due to MMC timeouts for a long while; I think that
> started between v5.17 and v5.19, but I couldn't bisect at the time
> (I was already chasing some other regression).
>
> Example bug report:
> https://bugs.debian.org/1019700
>
> Before trying to pinpoint when the regression appeared, I've checked
> these versions, with a Debian testing userspace as of 2023-03-07:
> - v6.1.12: affected.
> - v6.2: affected.
> - v6.3-rc1: not affected.
>
> A bisect between v6.2 and v6.3-rc1 led me to this patch specifically.
> Seeing how it's part of a patch series, and how previous patches are
> preliminary ones, I've checked that cherry-picking the first 6 patches
> on top of v6.1.15 indeed fixes the problem there too, and it does
> (git cherry-pick v6.2-rc4..601363cc08da25747feb87c55573dd54de91d66a).
>
>
> With the following systems:
> - Pi 4 B, using external storage (SD card),
> - CM4 Lite on CM4 IO Board, using external storage (SD card),
> - CM4 on CM4 IO Board, using internal storage (eMMC),
>
> I've been able to verify that v6.1.12 (baseline in Debian testing)
> triggers this MMC timeout issue, while v6.1.15 + the aforementioned
> range of cherry-picked commits no longer triggers this issue.
>
> (Methodology: cold boot then reboot 20 times, monitoring via serial
> console to keep HDMI output of the equation; affected systems stop
> booting after 1-4 boots; unaffected systems boot and reboot just fine
> all the time.)
>
>
> This looks like a critical bugfix for Raspberry Pi users.
>
> Seeing the stable@ mention is about 4.8, I suppose this is going to be
> considered for a wide range of kernels already… but I'm happy to dig
> into this further to pinpoint when the regression appeared, if that's
> helpful.

If you have an interest in these patches being backported, may I
suggest you look at the backporting failures that have been
reported[1]?

Note that now that 4.9 is out of the picture, nothing is going to be
backported past 4.14.

Thanks,

M.

[1] https://lore.kernel.org/r/[email protected]

--
Without deviation from the norm, progress is not possible.

2023-03-09 07:32:38

by Johan Hovold

[permalink] [raw]
Subject: Re: [PATCH v6 06/20] irqdomain: Fix mapping-creation race

On Wed, Mar 08, 2023 at 03:41:05PM +0100, Cyril Brulebois wrote:
> Hi Johan,
>
> And thanks so much for this patch series.
>
> Johan Hovold <[email protected]> (2023-02-13):
> > Parallel probing of devices that share interrupts (e.g. when a driver
> > uses asynchronous probing) can currently result in two mappings for the
> > same hardware interrupt to be created due to missing serialisation.
> >
> > Make sure to hold the irq_domain_mutex when creating mappings so that
> > looking for an existing mapping before creating a new one is done
> > atomically.
>
> Just for information: This patch fixes a long-standing regression
> regarding Raspberry Pi devices, which have been failing to boot (at
> least reliably) due to MMC timeouts for a long while; I think that
> started between v5.17 and v5.19, but I couldn't bisect at the time
> (I was already chasing some other regression).
>
> Example bug report:
> https://bugs.debian.org/1019700
>
> Before trying to pinpoint when the regression appeared, I've checked
> these versions, with a Debian testing userspace as of 2023-03-07:
> - v6.1.12: affected.
> - v6.2: affected.
> - v6.3-rc1: not affected.
>
> A bisect between v6.2 and v6.3-rc1 led me to this patch specifically.
> Seeing how it's part of a patch series, and how previous patches are
> preliminary ones, I've checked that cherry-picking the first 6 patches
> on top of v6.1.15 indeed fixes the problem there too, and it does
> (git cherry-pick v6.2-rc4..601363cc08da25747feb87c55573dd54de91d66a).

That's good to hear, thanks for reporting.

> With the following systems:
> - Pi 4 B, using external storage (SD card),
> - CM4 Lite on CM4 IO Board, using external storage (SD card),
> - CM4 on CM4 IO Board, using internal storage (eMMC),
>
> I've been able to verify that v6.1.12 (baseline in Debian testing)
> triggers this MMC timeout issue, while v6.1.15 + the aforementioned
> range of cherry-picked commits no longer triggers this issue.
>
> (Methodology: cold boot then reboot 20 times, monitoring via serial
> console to keep HDMI output of the equation; affected systems stop
> booting after 1-4 boots; unaffected systems boot and reboot just fine
> all the time.)
>
>
> This looks like a critical bugfix for Raspberry Pi users.
>
> Seeing the stable@ mention is about 4.8, I suppose this is going to be
> considered for a wide range of kernels already… but I'm happy to dig
> into this further to pinpoint when the regression appeared, if that's
> helpful.

I took a quick look at the rpi-4 devicetree and it looks like the emmc
indeed is sharing an irq line with another device which should make is
susceptible to the mapping race.

The corresponding drivers also use asynchronous probing since commits

7320915c8861 ("mmc: Set PROBE_PREFER_ASYNCHRONOUS for drivers that existed in v4.14")
21b2cec61c04 ("mmc: Set PROBE_PREFER_ASYNCHRONOUS for drivers that existed in v4.4"

that went into 5.10. So the issue should have been there for longer but
perhaps only manifested itself around 5.17 due to changes in timing.

Johan


Attachments:
(No filename) (3.01 kB)
signature.asc (228.00 B)
Download all attachments