Commit 8208d170 authored by Marc Zyngier's avatar Marc Zyngier

irqchip/gic-v3-its: Align PCI Multi-MSI allocation on their size

The way we allocate events works fine in most cases, except
when multiple PCI devices share an ITS-visible DevID, and that
one of them is trying to use MultiMSI allocation.

In that case, our allocation is not guaranteed to be zero-based
anymore, and we have to make sure we allocate it on a boundary
that is compatible with the PCI Multi-MSI constraints.

Fix this by allocating the full region upfront instead of iterating
over the number of MSIs. MSI-X are always allocated one by one,
so this shouldn't change anything on that front.

Fixes: b48ac83d ("irqchip: GICv3: ITS: MSI support")
Cc: stable@vger.kernel.org
Reported-by: default avatarArd Biesheuvel <ard.biesheuvel@linaro.org>
Tested-by: default avatarArd Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: default avatarMarc Zyngier <marc.zyngier@arm.com>
parent 8fa4e55b
...@@ -2399,13 +2399,14 @@ static void its_free_device(struct its_device *its_dev) ...@@ -2399,13 +2399,14 @@ static void its_free_device(struct its_device *its_dev)
kfree(its_dev); kfree(its_dev);
} }
static int its_alloc_device_irq(struct its_device *dev, irq_hw_number_t *hwirq) static int its_alloc_device_irq(struct its_device *dev, int nvecs, irq_hw_number_t *hwirq)
{ {
int idx; int idx;
idx = find_first_zero_bit(dev->event_map.lpi_map, idx = bitmap_find_free_region(dev->event_map.lpi_map,
dev->event_map.nr_lpis); dev->event_map.nr_lpis,
if (idx == dev->event_map.nr_lpis) get_count_order(nvecs));
if (idx < 0)
return -ENOSPC; return -ENOSPC;
*hwirq = dev->event_map.lpi_base + idx; *hwirq = dev->event_map.lpi_base + idx;
...@@ -2501,21 +2502,21 @@ static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq, ...@@ -2501,21 +2502,21 @@ static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
int err; int err;
int i; int i;
for (i = 0; i < nr_irqs; i++) { err = its_alloc_device_irq(its_dev, nr_irqs, &hwirq);
err = its_alloc_device_irq(its_dev, &hwirq); if (err)
if (err) return err;
return err;
err = its_irq_gic_domain_alloc(domain, virq + i, hwirq); for (i = 0; i < nr_irqs; i++) {
err = its_irq_gic_domain_alloc(domain, virq + i, hwirq + i);
if (err) if (err)
return err; return err;
irq_domain_set_hwirq_and_chip(domain, virq + i, irq_domain_set_hwirq_and_chip(domain, virq + i,
hwirq, &its_irq_chip, its_dev); hwirq + i, &its_irq_chip, its_dev);
irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(virq + i))); irqd_set_single_target(irq_desc_get_irq_data(irq_to_desc(virq + i)));
pr_debug("ID:%d pID:%d vID:%d\n", pr_debug("ID:%d pID:%d vID:%d\n",
(int)(hwirq - its_dev->event_map.lpi_base), (int)(hwirq + i - its_dev->event_map.lpi_base),
(int) hwirq, virq + i); (int)(hwirq + i), virq + i);
} }
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment