Skip to content

Commit d4a45c6

Browse files
author
Marc Zyngier
committed
irqdomain: Protect the linear revmap with RCU
It is pretty odd that the radix tree uses RCU while the linear portion doesn't, leading to potential surprises for the users, depending on how the irqdomain has been created. Fix this by moving the update of the linear revmap under the mutex, and the lookup under the RCU read-side lock. The mutex name is updated to reflect that it doesn't only cover the radix-tree anymore. Signed-off-by: Marc Zyngier <[email protected]>
1 parent 48b15a7 commit d4a45c6

File tree

2 files changed

+26
-28
lines changed

2 files changed

+26
-28
lines changed

include/linux/irqdomain.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ struct irq_domain_chip_generic;
151151
* Revmap data, used internally by irq_domain
152152
* @revmap_size: Size of the linear map table @revmap[]
153153
* @revmap_tree: Radix map tree for hwirqs that don't fit in the linear map
154+
* @revmap_mutex: Lock for the revmap
154155
* @revmap: Linear table of irq_data pointers
155156
*/
156157
struct irq_domain {
@@ -173,8 +174,8 @@ struct irq_domain {
173174
irq_hw_number_t hwirq_max;
174175
unsigned int revmap_size;
175176
struct radix_tree_root revmap_tree;
176-
struct mutex revmap_tree_mutex;
177-
struct irq_data *revmap[];
177+
struct mutex revmap_mutex;
178+
struct irq_data __rcu *revmap[];
178179
};
179180

180181
/* Irq domain flags */

kernel/irq/irqdomain.c

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size,
213213

214214
/* Fill structure */
215215
INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
216-
mutex_init(&domain->revmap_tree_mutex);
216+
mutex_init(&domain->revmap_mutex);
217217
domain->ops = ops;
218218
domain->host_data = host_data;
219219
domain->hwirq_max = hwirq_max;
@@ -504,13 +504,12 @@ static void irq_domain_clear_mapping(struct irq_domain *domain,
504504
if (irq_domain_is_nomap(domain))
505505
return;
506506

507-
if (hwirq < domain->revmap_size) {
508-
domain->revmap[hwirq] = NULL;
509-
} else {
510-
mutex_lock(&domain->revmap_tree_mutex);
507+
mutex_lock(&domain->revmap_mutex);
508+
if (hwirq < domain->revmap_size)
509+
rcu_assign_pointer(domain->revmap[hwirq], NULL);
510+
else
511511
radix_tree_delete(&domain->revmap_tree, hwirq);
512-
mutex_unlock(&domain->revmap_tree_mutex);
513-
}
512+
mutex_unlock(&domain->revmap_mutex);
514513
}
515514

516515
static void irq_domain_set_mapping(struct irq_domain *domain,
@@ -520,13 +519,12 @@ static void irq_domain_set_mapping(struct irq_domain *domain,
520519
if (irq_domain_is_nomap(domain))
521520
return;
522521

523-
if (hwirq < domain->revmap_size) {
524-
domain->revmap[hwirq] = irq_data;
525-
} else {
526-
mutex_lock(&domain->revmap_tree_mutex);
522+
mutex_lock(&domain->revmap_mutex);
523+
if (hwirq < domain->revmap_size)
524+
rcu_assign_pointer(domain->revmap[hwirq], irq_data);
525+
else
527526
radix_tree_insert(&domain->revmap_tree, hwirq, irq_data);
528-
mutex_unlock(&domain->revmap_tree_mutex);
529-
}
527+
mutex_unlock(&domain->revmap_mutex);
530528
}
531529

532530
static void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)
@@ -911,12 +909,12 @@ unsigned int irq_find_mapping(struct irq_domain *domain,
911909
return 0;
912910
}
913911

912+
rcu_read_lock();
914913
/* Check if the hwirq is in the linear revmap. */
915914
if (hwirq < domain->revmap_size)
916-
return domain->revmap[hwirq]->irq;
917-
918-
rcu_read_lock();
919-
data = radix_tree_lookup(&domain->revmap_tree, hwirq);
915+
data = rcu_dereference(domain->revmap[hwirq]);
916+
else
917+
data = radix_tree_lookup(&domain->revmap_tree, hwirq);
920918
rcu_read_unlock();
921919
return data ? data->irq : 0;
922920
}
@@ -1499,18 +1497,17 @@ static void irq_domain_fix_revmap(struct irq_data *d)
14991497
if (irq_domain_is_nomap(d->domain))
15001498
return;
15011499

1500+
/* Fix up the revmap. */
1501+
mutex_lock(&d->domain->revmap_mutex);
15021502
if (d->hwirq < d->domain->revmap_size) {
15031503
/* Not using radix tree */
1504-
d->domain->revmap[d->hwirq] = d;
1505-
return;
1504+
rcu_assign_pointer(d->domain->revmap[d->hwirq], d);
1505+
} else {
1506+
slot = radix_tree_lookup_slot(&d->domain->revmap_tree, d->hwirq);
1507+
if (slot)
1508+
radix_tree_replace_slot(&d->domain->revmap_tree, slot, d);
15061509
}
1507-
1508-
/* Fix up the revmap. */
1509-
mutex_lock(&d->domain->revmap_tree_mutex);
1510-
slot = radix_tree_lookup_slot(&d->domain->revmap_tree, d->hwirq);
1511-
if (slot)
1512-
radix_tree_replace_slot(&d->domain->revmap_tree, slot, d);
1513-
mutex_unlock(&d->domain->revmap_tree_mutex);
1510+
mutex_unlock(&d->domain->revmap_mutex);
15141511
}
15151512

15161513
/**

0 commit comments

Comments
 (0)