Commit 3786fd03 authored by Gerd Bayer's avatar Gerd Bayer Committed by ZhangPeng
Browse files

s390/pci: Allow allocation of more than 1 MSI interrupt

stable inclusion
from stable-v6.6.44
commit b4d781ddaee39340b806b7fb430d2ee892c9bbdb
bugzilla: https://gitee.com/openeuler/kernel/issues/IAHMJO

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=b4d781ddaee39340b806b7fb430d2ee892c9bbdb



--------------------------------

[ Upstream commit ab42fcb511fd9d241bbab7cc3ca04e34e9fc0666 ]

On a PCI adapter that provides up to 8 MSI interrupt sources the s390
implementation of PCI interrupts rejected to accommodate them, although
the underlying hardware is able to support that.

For MSI-X it is sufficient to allocate a single irq_desc per msi_desc,
but for MSI multiple irq descriptors are attached to and controlled by
a single msi descriptor. Add the appropriate loops to maintain multiple
irq descriptors and tie/untie them to/from the appropriate AIBV bit, if
a device driver allocates more than 1 MSI interrupt.

Common PCI code passes on requests to allocate a number of interrupt
vectors based on the device drivers' demand and the PCI functions'
capabilities. However, the root-complex of s390 systems support just a
limited number of interrupt vectors per PCI function.
Produce a kernel log message to inform about any architecture-specific
capping that might be done.

With this change, we had a PCI adapter successfully raising
interrupts to its device driver via all 8 sources.

Fixes: a384c892 ("s390/PCI: Fix single MSI only check")
Signed-off-by: default avatarGerd Bayer <gbayer@linux.ibm.com>
Reviewed-by: default avatarNiklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: default avatarVasily Gorbik <gor@linux.ibm.com>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
Signed-off-by: default avatarZhangPeng <zhangpeng362@huawei.com>
parent 7b9f3032
Loading
Loading
Loading
Loading
+42 −20
Original line number Diff line number Diff line
@@ -298,8 +298,8 @@ static int __alloc_airq(struct zpci_dev *zdev, int msi_vecs,

int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
{
	unsigned int hwirq, msi_vecs, irqs_per_msi, i, cpu;
	struct zpci_dev *zdev = to_zpci(pdev);
	unsigned int hwirq, msi_vecs, cpu;
	struct msi_desc *msi;
	struct msi_msg msg;
	unsigned long bit;
@@ -309,30 +309,46 @@ int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
	zdev->aisb = -1UL;
	zdev->msi_first_bit = -1U;

	if (type == PCI_CAP_ID_MSI && nvec > 1)
		return 1;
	msi_vecs = min_t(unsigned int, nvec, zdev->max_msi);
	if (msi_vecs < nvec) {
		pr_info("%s requested %d irqs, allocate system limit of %d",
			pci_name(pdev), nvec, zdev->max_msi);
	}

	rc = __alloc_airq(zdev, msi_vecs, &bit);
	if (rc < 0)
		return rc;

	/* Request MSI interrupts */
	/*
	 * Request MSI interrupts:
	 * When using MSI, nvec_used interrupt sources and their irq
	 * descriptors are controlled through one msi descriptor.
	 * Thus the outer loop over msi descriptors shall run only once,
	 * while two inner loops iterate over the interrupt vectors.
	 * When using MSI-X, each interrupt vector/irq descriptor
	 * is bound to exactly one msi descriptor (nvec_used is one).
	 * So the inner loops are executed once, while the outer iterates
	 * over the MSI-X descriptors.
	 */
	hwirq = bit;
	msi_for_each_desc(msi, &pdev->dev, MSI_DESC_NOTASSOCIATED) {
		rc = -EIO;
		if (hwirq - bit >= msi_vecs)
			break;
		irq = __irq_alloc_descs(-1, 0, 1, 0, THIS_MODULE,
		irqs_per_msi = min_t(unsigned int, msi_vecs, msi->nvec_used);
		irq = __irq_alloc_descs(-1, 0, irqs_per_msi, 0, THIS_MODULE,
					(irq_delivery == DIRECTED) ?
					msi->affinity : NULL);
		if (irq < 0)
			return -ENOMEM;
		rc = irq_set_msi_desc(irq, msi);

		for (i = 0; i < irqs_per_msi; i++) {
			rc = irq_set_msi_desc_off(irq, i, msi);
			if (rc)
				return rc;
		irq_set_chip_and_handler(irq, &zpci_irq_chip,
			irq_set_chip_and_handler(irq + i, &zpci_irq_chip,
						 handle_percpu_irq);
		}

		msg.data = hwirq - bit;
		if (irq_delivery == DIRECTED) {
			if (msi->affinity)
@@ -345,31 +361,35 @@ int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
			msg.address_lo |= (cpu_addr << 8);

			for_each_possible_cpu(cpu) {
				airq_iv_set_data(zpci_ibv[cpu], hwirq, irq);
				for (i = 0; i < irqs_per_msi; i++)
					airq_iv_set_data(zpci_ibv[cpu],
							 hwirq + i, irq + i);
			}
		} else {
			msg.address_lo = zdev->msi_addr & 0xffffffff;
			airq_iv_set_data(zdev->aibv, hwirq, irq);
			for (i = 0; i < irqs_per_msi; i++)
				airq_iv_set_data(zdev->aibv, hwirq + i, irq + i);
		}
		msg.address_hi = zdev->msi_addr >> 32;
		pci_write_msi_msg(irq, &msg);
		hwirq++;
		hwirq += irqs_per_msi;
	}

	zdev->msi_first_bit = bit;
	zdev->msi_nr_irqs = msi_vecs;
	zdev->msi_nr_irqs = hwirq - bit;

	rc = zpci_set_irq(zdev);
	if (rc)
		return rc;

	return (msi_vecs == nvec) ? 0 : msi_vecs;
	return (zdev->msi_nr_irqs == nvec) ? 0 : zdev->msi_nr_irqs;
}

void arch_teardown_msi_irqs(struct pci_dev *pdev)
{
	struct zpci_dev *zdev = to_zpci(pdev);
	struct msi_desc *msi;
	unsigned int i;
	int rc;

	/* Disable interrupts */
@@ -379,8 +399,10 @@ void arch_teardown_msi_irqs(struct pci_dev *pdev)

	/* Release MSI interrupts */
	msi_for_each_desc(msi, &pdev->dev, MSI_DESC_ASSOCIATED) {
		irq_set_msi_desc(msi->irq, NULL);
		irq_free_desc(msi->irq);
		for (i = 0; i < msi->nvec_used; i++) {
			irq_set_msi_desc(msi->irq + i, NULL);
			irq_free_desc(msi->irq + i);
		}
		msi->msg.address_lo = 0;
		msi->msg.address_hi = 0;
		msi->msg.data = 0;