Skip to content

Commit 9dbb8e3

Browse files
jhovoldMarc Zyngier
authored andcommitted
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]
1 parent f743f54 commit 9dbb8e3

File tree

2 files changed

+43
-20
lines changed

2 files changed

+43
-20
lines changed

include/linux/irqdomain.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ struct irq_domain_chip_generic;
125125
* core code.
126126
* @flags: Per irq_domain flags
127127
* @mapcount: The number of mapped interrupts
128+
* @mutex: Domain lock, hierarchical domains use root domain's lock
129+
* @root: Pointer to root domain, or containing structure if non-hierarchical
128130
*
129131
* Optional elements:
130132
* @fwnode: Pointer to firmware node associated with the irq_domain. Pretty easy
@@ -152,6 +154,8 @@ struct irq_domain {
152154
void *host_data;
153155
unsigned int flags;
154156
unsigned int mapcount;
157+
struct mutex mutex;
158+
struct irq_domain *root;
155159

156160
/* Optional data */
157161
struct fwnode_handle *fwnode;

kernel/irq/irqdomain.c

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,17 @@ static struct irq_domain *__irq_domain_create(struct fwnode_handle *fwnode,
215215

216216
domain->revmap_size = size;
217217

218+
/*
219+
* Hierarchical domains use the domain lock of the root domain
220+
* (innermost domain).
221+
*
222+
* For non-hierarchical domains (as for root domains), the root
223+
* pointer is set to the domain itself so that &domain->root->mutex
224+
* always points to the right lock.
225+
*/
226+
mutex_init(&domain->mutex);
227+
domain->root = domain;
228+
218229
irq_domain_check_hierarchy(domain);
219230

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

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

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

562577
hwirq = irq_data->hwirq;
563578

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

566581
irq_set_status_flags(irq, IRQ_NOREQUEST);
567582

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

586-
mutex_unlock(&irq_domain_mutex);
601+
mutex_unlock(&domain->root->mutex);
587602
}
588603

589604
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,
633648
{
634649
int ret;
635650

636-
mutex_lock(&irq_domain_mutex);
651+
mutex_lock(&domain->root->mutex);
637652
ret = irq_domain_associate_locked(domain, virq, hwirq);
638-
mutex_unlock(&irq_domain_mutex);
653+
mutex_unlock(&domain->root->mutex);
639654

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

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

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

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

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

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

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

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

11591174
if (domain) {
1175+
domain->root = parent->root;
11601176
domain->parent = parent;
11611177
domain->flags |= flags;
11621178

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

1558-
mutex_lock(&irq_domain_mutex);
1574+
mutex_lock(&domain->root->mutex);
15591575
ret = irq_domain_alloc_irqs_locked(domain, irq_base, nr_irqs, node, arg,
15601576
realloc, affinity);
1561-
mutex_unlock(&irq_domain_mutex);
1577+
mutex_unlock(&domain->root->mutex);
15621578

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

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

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

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

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

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

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

17231739
irq_data->parent_data = NULL;
17241740

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

17311747
irq_domain_fix_revmap(irq_data);
17321748

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

17351751
kfree(parent_irq_data);
17361752

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

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

1755-
mutex_lock(&irq_domain_mutex);
1772+
domain = data->domain;
1773+
1774+
mutex_lock(&domain->root->mutex);
17561775
for (i = 0; i < nr_irqs; i++)
17571776
irq_domain_remove_irq(virq + i);
1758-
irq_domain_free_irqs_hierarchy(data->domain, virq, nr_irqs);
1759-
mutex_unlock(&irq_domain_mutex);
1777+
irq_domain_free_irqs_hierarchy(domain, virq, nr_irqs);
1778+
mutex_unlock(&domain->root->mutex);
17601779

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

0 commit comments

Comments
 (0)