Blob Blame History Raw
From: Bjorn Helgaas <bhelgaas@google.com>
Date: Fri, 20 Oct 2017 08:48:06 -0500
Subject: PCI/portdrv: Factor out Interrupt Message Number lookup
Git-commit: 3321eafd2a79f15126ebaa82f1b5d7fce89c02cb
Patch-mainline: v4.15-rc1
References: bsc#1109806

Factor out Interrupt Message Number lookup from the MSI/MSI-X interrupt
setup.  One side effect is that we only have to check once to see if we
have enough vectors for all the services.  No functional change intended.

Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Acked-by: Joerg Roedel <jroedel@suse.de>
---
 drivers/pci/pcie/portdrv_core.c | 110 ++++++++++++++++++++++------------------
 1 file changed, 62 insertions(+), 48 deletions(-)

--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -43,6 +43,43 @@ static void release_pcie_device(struct d
 	kfree(to_pcie_device(dev));
 }
 
+/*
+ * Fill in *pme, *aer, *dpc with the relevant Interrupt Message Numbers if
+ * services are enabled in "mask".  Return the number of MSI/MSI-X vectors
+ * required to accommodate the largest Message Number.
+ */
+static int pcie_message_numbers(struct pci_dev *dev, int mask,
+				u32 *pme, u32 *aer, u32 *dpc)
+{
+	u32 nvec = 0, pos, reg32;
+	u16 reg16;
+
+	/*
+	 * The Interrupt Message Number indicates which vector is used, i.e.,
+	 * the MSI-X table entry or the MSI offset between the base Message
+	 * Data and the generated interrupt message.  See PCIe r3.1, sec
+	 * 7.8.2, 7.10.10, 7.31.2.
+	 */
+
+	if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) {
+		pcie_capability_read_word(dev, PCI_EXP_FLAGS, &reg16);
+		*pme = (reg16 & PCI_EXP_FLAGS_IRQ) >> 9;
+		nvec = *pme + 1;
+	}
+
+	if (mask & PCIE_PORT_SERVICE_AER) {
+		pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+		if (pos) {
+			pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS,
+					      &reg32);
+			*aer = reg32 >> 27;
+			nvec = max(nvec, *aer + 1);
+		}
+	}
+
+	return nvec;
+}
+
 /**
  * pcie_port_enable_msix - try to set up MSI-X as interrupt mode for given port
  * @dev: PCI Express port to handle
@@ -53,7 +90,8 @@ static void release_pcie_device(struct d
  */
 static int pcie_port_enable_msix(struct pci_dev *dev, int *irqs, int mask)
 {
-	int nr_entries, entry, nvec = 0;
+	int nr_entries, nvec;
+	u32 pme = 0, aer = 0, dpc = 0;
 
 	/*
 	 * Allocate as many entries as the port wants, so that we can check
@@ -66,53 +104,22 @@ static int pcie_port_enable_msix(struct
 	if (nr_entries < 0)
 		return nr_entries;
 
-	if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) {
-		u16 reg16;
-
-		/*
-		 * The code below follows the PCI Express Base Specification 2.0
-		 * stating in Section 6.1.6 that "PME and Hot-Plug Event
-		 * interrupts (when both are implemented) always share the same
-		 * MSI or MSI-X vector, as indicated by the Interrupt Message
-		 * Number field in the PCI Express Capabilities register", where
-		 * according to Section 7.8.2 of the specification "For MSI-X,
-		 * the value in this field indicates which MSI-X Table entry is
-		 * used to generate the interrupt message."
-		 */
-		pcie_capability_read_word(dev, PCI_EXP_FLAGS, &reg16);
-		entry = (reg16 & PCI_EXP_FLAGS_IRQ) >> 9;
-		if (entry >= nr_entries)
-			goto out_free_irqs;
-
-		irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pci_irq_vector(dev, entry);
-		irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pci_irq_vector(dev, entry);
-
-		nvec = max(nvec, entry + 1);
+	/* See how many and which Interrupt Message Numbers we actually use */
+	nvec = pcie_message_numbers(dev, mask, &pme, &aer, &dpc);
+	if (nvec > nr_entries) {
+		pci_free_irq_vectors(dev);
+		return -EIO;
 	}
 
-	if (mask & PCIE_PORT_SERVICE_AER) {
-		u32 reg32, pos;
-
-		/*
-		 * The code below follows Section 7.10.10 of the PCI Express
-		 * Base Specification 2.0 stating that bits 31-27 of the Root
-		 * Error Status Register contain a value indicating which of the
-		 * MSI/MSI-X vectors assigned to the port is going to be used
-		 * for AER, where "For MSI-X, the value in this register
-		 * indicates which MSI-X Table entry is used to generate the
-		 * interrupt message."
-		 */
-		pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
-		pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &reg32);
-		entry = reg32 >> 27;
-		if (entry >= nr_entries)
-			goto out_free_irqs;
-
-		irqs[PCIE_PORT_SERVICE_AER_SHIFT] = pci_irq_vector(dev, entry);
-
-		nvec = max(nvec, entry + 1);
+	/* PME and hotplug share an MSI/MSI-X vector */
+	if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) {
+		irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pci_irq_vector(dev, pme);
+		irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pci_irq_vector(dev, pme);
 	}
 
+	if (mask & PCIE_PORT_SERVICE_AER)
+		irqs[PCIE_PORT_SERVICE_AER_SHIFT] = pci_irq_vector(dev, aer);
+
 	/*
 	 * If nvec is equal to the allocated number of entries, we can just use
 	 * what we have.  Otherwise, the port has some extra entries not for the
@@ -130,10 +137,6 @@ static int pcie_port_enable_msix(struct
 	}
 
 	return 0;
-
-out_free_irqs:
-	pci_free_irq_vectors(dev);
-	return -EIO;
 }
 
 /**