Skip to content

Commit 9791ec7

Browse files
author
Marc Zyngier
committed
irqchip/gic-v3-its: Plug allocation race for devices sharing a DevID
On systems or VMs where multiple devices share a single DevID (because they sit behind a PCI bridge, or because the HW is broken in funky ways), we reuse the save its_device structure in order to reflect this. It turns out that there is a distinct lack of locking when looking up the its_device, and two device being probed concurrently can result in double allocations. That's obviously not nice. A solution for this is to have a per-ITS mutex that serializes device allocation. A similar issue exists on the freeing side, which can run concurrently with the allocation. On top of now taking the appropriate lock, we also make sure that a shared device is never freed, as we have no way to currently track the life cycle of such object. Reported-by: Zheng Xiang <zhengxiang9@huawei.com> Tested-by: Zheng Xiang <zhengxiang9@huawei.com> Cc: stable@vger.kernel.org Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
1 parent 6479450 commit 9791ec7

File tree

1 file changed

+27
-5
lines changed

1 file changed

+27
-5
lines changed

drivers/irqchip/irq-gic-v3-its.c

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,14 @@ struct its_device;
9797
* The ITS structure - contains most of the infrastructure, with the
9898
* top-level MSI domain, the command queue, the collections, and the
9999
* list of devices writing to it.
100+
*
101+
* dev_alloc_lock has to be taken for device allocations, while the
102+
* spinlock must be taken to parse data structures such as the device
103+
* list.
100104
*/
101105
struct its_node {
102106
raw_spinlock_t lock;
107+
struct mutex dev_alloc_lock;
103108
struct list_head entry;
104109
void __iomem *base;
105110
phys_addr_t phys_base;
@@ -156,6 +161,7 @@ struct its_device {
156161
void *itt;
157162
u32 nr_ites;
158163
u32 device_id;
164+
bool shared;
159165
};
160166

161167
static struct {
@@ -2469,6 +2475,7 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev,
24692475
struct its_device *its_dev;
24702476
struct msi_domain_info *msi_info;
24712477
u32 dev_id;
2478+
int err = 0;
24722479

24732480
/*
24742481
* We ignore "dev" entierely, and rely on the dev_id that has
@@ -2491,25 +2498,30 @@ static int its_msi_prepare(struct irq_domain *domain, struct device *dev,
24912498
return -EINVAL;
24922499
}
24932500

2501+
mutex_lock(&its->dev_alloc_lock);
24942502
its_dev = its_find_device(its, dev_id);
24952503
if (its_dev) {
24962504
/*
24972505
* We already have seen this ID, probably through
24982506
* another alias (PCI bridge of some sort). No need to
24992507
* create the device.
25002508
*/
2509+
its_dev->shared = true;
25012510
pr_debug("Reusing ITT for devID %x\n", dev_id);
25022511
goto out;
25032512
}
25042513

25052514
its_dev = its_create_device(its, dev_id, nvec, true);
2506-
if (!its_dev)
2507-
return -ENOMEM;
2515+
if (!its_dev) {
2516+
err = -ENOMEM;
2517+
goto out;
2518+
}
25082519

25092520
pr_debug("ITT %d entries, %d bits\n", nvec, ilog2(nvec));
25102521
out:
2522+
mutex_unlock(&its->dev_alloc_lock);
25112523
info->scratchpad[0].ptr = its_dev;
2512-
return 0;
2524+
return err;
25132525
}
25142526

25152527
static struct msi_domain_ops its_msi_domain_ops = {
@@ -2613,6 +2625,7 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq,
26132625
{
26142626
struct irq_data *d = irq_domain_get_irq_data(domain, virq);
26152627
struct its_device *its_dev = irq_data_get_irq_chip_data(d);
2628+
struct its_node *its = its_dev->its;
26162629
int i;
26172630

26182631
for (i = 0; i < nr_irqs; i++) {
@@ -2627,8 +2640,14 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq,
26272640
irq_domain_reset_irq_data(data);
26282641
}
26292642

2630-
/* If all interrupts have been freed, start mopping the floor */
2631-
if (bitmap_empty(its_dev->event_map.lpi_map,
2643+
mutex_lock(&its->dev_alloc_lock);
2644+
2645+
/*
2646+
* If all interrupts have been freed, start mopping the
2647+
* floor. This is conditionned on the device not being shared.
2648+
*/
2649+
if (!its_dev->shared &&
2650+
bitmap_empty(its_dev->event_map.lpi_map,
26322651
its_dev->event_map.nr_lpis)) {
26332652
its_lpi_free(its_dev->event_map.lpi_map,
26342653
its_dev->event_map.lpi_base,
@@ -2640,6 +2659,8 @@ static void its_irq_domain_free(struct irq_domain *domain, unsigned int virq,
26402659
its_free_device(its_dev);
26412660
}
26422661

2662+
mutex_unlock(&its->dev_alloc_lock);
2663+
26432664
irq_domain_free_irqs_parent(domain, virq, nr_irqs);
26442665
}
26452666

@@ -3549,6 +3570,7 @@ static int __init its_probe_one(struct resource *res,
35493570
}
35503571

35513572
raw_spin_lock_init(&its->lock);
3573+
mutex_init(&its->dev_alloc_lock);
35523574
INIT_LIST_HEAD(&its->entry);
35533575
INIT_LIST_HEAD(&its->its_device_list);
35543576
typer = gic_read_typer(its_base + GITS_TYPER);

0 commit comments

Comments
 (0)