Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 25043da9 authored by Marc Zyngier's avatar Marc Zyngier Committed by Greg Kroah-Hartman
Browse files

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



commit 8208d1708b88b412ca97f50a6d951242c88cbbac upstream.

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>
[ardb: rebased onto v4.9.153, should apply cleanly onto v4.4.y as well]
Signed-off-by: default avatarArd Biesheuvel <ard.biesheuvel@linaro.org>
parent a9c87582
Loading
Loading
Loading
Loading
+13 −12
Original line number Diff line number Diff line
@@ -1372,13 +1372,14 @@ static void its_free_device(struct its_device *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;

	idx = find_first_zero_bit(dev->event_map.lpi_map,
				  dev->event_map.nr_lpis);
	if (idx == dev->event_map.nr_lpis)
	idx = bitmap_find_free_region(dev->event_map.lpi_map,
				      dev->event_map.nr_lpis,
				      get_count_order(nvecs));
	if (idx < 0)
		return -ENOSPC;

	*hwirq = dev->event_map.lpi_base + idx;
@@ -1464,20 +1465,20 @@ static int its_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
	int err;
	int i;

	for (i = 0; i < nr_irqs; i++) {
		err = its_alloc_device_irq(its_dev, &hwirq);
	err = its_alloc_device_irq(its_dev, nr_irqs, &hwirq);
	if (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)
			return err;

		irq_domain_set_hwirq_and_chip(domain, virq + i,
					      hwirq, &its_irq_chip, its_dev);
					      hwirq + i, &its_irq_chip, its_dev);
		pr_debug("ID:%d pID:%d vID:%d\n",
			 (int)(hwirq - its_dev->event_map.lpi_base),
			 (int) hwirq, virq + i);
			 (int)(hwirq + i - its_dev->event_map.lpi_base),
			 (int)(hwirq + i), virq + i);
	}

	return 0;