MSI-X initialization in PCIe port service driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

I don't have any platform with MSI-X capable PCI Express port, so I've
never tried the current MSI-X configuration code of PCIe port service
driver. But I think it has at least the following problems.

- To setup MSI-X for PCIe port services, we need to know which MSI-X
  table entries are used for which services. For example, MSI-X entry
  for Hot Plug and PME is indicated by Interrupt Message Number field
  in the PCI Express Capabilities register, and MSI-X entry for
  Advanced Error Reporting is indicated by Root Error Status register.
  But current PCIe port service driver core is doing nothing about
  that, and it would cause unexpected interrupt behaviour.

- Current PCIe port service driver tries to allocate vectors for Hot
  Plug, PME, AER and VC individually. So PCIe port service driver
  would request four vectors in maximum. But it should never be four
  because Hot Plug and PME always share the interrupt. In addition,
  I'm wondering if VC needs vectors, though I don't know about VC
  very much.

I made a sample patch to explain the required fix I'm thinking. Please
note that it is *NOT* tested with MSI-X capable port because I don't
have any platform with MSI-X capable port as I mentioned above (I
confirmed MSI and INTx work with this patch).

Thanks,
Kenji Kaneshige


######################################################################

                        THIS IS A SAMPLE PATCH

     THIS IS *NOT* TESTED ON THE MACHINE WITH MSI-X CAPABLE PORT

######################################################################

Fix MSI-X configuration code in PCIe port services.

I think current MSI-X configuration code in PCIe port service driver
has at least the following problems.

- To setup MSI-X for PCIe port services, we need to know which MSI-X
  table entries are used for which services. For example, MSI-X entry
  for Hot Plug and PME is indicated by Interrupt Message Number field
  in the PCI Express Capabilities register, and MSI-X entry for
  Advanced Error Reporting is indicated by Root Error Status register.
  But current PCIe port service driver core is doing nothing about
  that, and it would cause unexpected interrupt behaviour.

- Current PCIe port service driver tries to allocate vectors for Hot
  Plug, PME, AER and VC individually. So PCIe port service driver
  would reqest four vectors in muximum. But it should never be four
  because Hot Plug and PME always share the interrupt. In addition,
  I'm wondering if VC needs vectors, though I don't know about VC
  very much.

Signed-off-by: Kenji Kaneshige <kaneshige.kenji@xxxxxxxxxxxxxx>

---
 drivers/pci/pcie/portdrv_core.c |  110 +++++++++++++++++++++++++++-------------
 1 file changed, 75 insertions(+), 35 deletions(-)

Index: 20081217/drivers/pci/pcie/portdrv_core.c
===================================================================
--- 20081217.orig/drivers/pci/pcie/portdrv_core.c
+++ 20081217/drivers/pci/pcie/portdrv_core.c
@@ -127,51 +127,91 @@ static int is_msi_quirked(struct pci_dev
 	}
 	return quirk;
 }
+
+static int port_msix_init(struct pci_dev *dev, int *vectors, int capabilities)
+{
+	int i, err, idx, idx_hppme = -1, idx_aer = -1;
+	struct msix_entry msix_entries[PCIE_PORT_DEVICE_MAXSERVICES];
+
+	if (!pci_find_capability(dev, PCI_CAP_ID_MSIX))
+		return -ENODEV;
+	/*
+	 * We need to find out which MSI-X entries are used for which
+	 * services before calling pci_enable_msix(). On the other
+	 * hand, we need to enable MSI-X using pci_enable_msix() to
+	 * find out this information. Against this dilemma, enabling
+	 * MSI-X using pci_enable_msix() with a dummy msix_entry here.
+	 */
+	msix_entries[0].entry = 0;
+	if ((err = pci_enable_msix(dev, msix_entries, 1)))
+		return err;
+	/* Find out which MSI-X entries are used for which services */
+	idx = 0;
+	if (capabilities & (PCIE_PORT_SERVICE_HP | PCIE_PORT_SERVICE_PME)) {
+		u16 reg16;
+		int pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+		pci_read_config_word(dev, pos + PCIE_CAPABILITIES_REG, &reg16);
+		msix_entries[idx].entry = (reg16 >> 9) & 0x1f;
+		idx_hppme = idx++;
+	}
+	if (capabilities & PCIE_PORT_SERVICE_AER) {
+		u32 reg32;
+		int pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+		pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &reg32);
+		msix_entries[idx].entry = (reg32 >> 27);
+		idx_aer = idx++;
+	}
+	/* Disable dummy MSI-X setting */
+	pci_disable_msix(dev);
+	/* Enable MSI-X with proper msix_entries */
+	if ((err = pci_enable_msix(dev, msix_entries, idx)))
+		return err;
+	for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
+		int service = (1 << i);
+		if (!(capabilities & service))
+			continue;
+		if (service & (PCIE_PORT_SERVICE_HP | PCIE_PORT_SERVICE_PME))
+			vectors[i] = msix_entries[idx_hppme].vector;
+		else if (service & PCIE_PORT_SERVICE_AER)
+			vectors[i] = msix_entries[idx_aer].vector;
+		else
+			vectors[i] = 0;
+	}
+	return 0;
+}
+
+static int port_msi_init(struct pci_dev *dev, int *vectors, int capabilities)
+{
+	int err, i;
+	if (!pci_find_capability(dev, PCI_CAP_ID_MSI))
+		return -ENODEV;
+	if ((err = pci_enable_msi(dev)))
+		return err;
+	for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
+		vectors[i] = dev->irq;
+	return 0;
+}
 	
 static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask)
 {
-	int i, pos, nvec, status = -EINVAL;
-	int interrupt_mode = PCIE_PORT_INTx_MODE;
+	int i;
 
 	/* Set INTx as default */
-	for (i = 0, nvec = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
-		if (mask & (1 << i)) 
-			nvec++;
+	for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
 		vectors[i] = dev->irq;
-	}
 	
 	/* Check MSI quirk */
 	if (is_msi_quirked(dev))
-		return interrupt_mode;
+		return PCIE_PORT_INTx_MODE;
 
-	/* Select MSI-X over MSI if supported */		
-	pos = pci_find_capability(dev, PCI_CAP_ID_MSIX);
-	if (pos) {
-		struct msix_entry msix_entries[PCIE_PORT_DEVICE_MAXSERVICES] = 
-			{{0, 0}, {0, 1}, {0, 2}, {0, 3}};
-		status = pci_enable_msix(dev, msix_entries, nvec);
-		if (!status) {
-			int j = 0;
-
-			interrupt_mode = PCIE_PORT_MSIX_MODE;
-			for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
-				if (mask & (1 << i)) 
-					vectors[i] = msix_entries[j++].vector;
-			}
-		}
-	} 
-	if (status) {
-		pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
-		if (pos) {
-			status = pci_enable_msi(dev);
-			if (!status) {
-				interrupt_mode = PCIE_PORT_MSI_MODE;
-				for (i = 0;i < PCIE_PORT_DEVICE_MAXSERVICES;i++)
-					vectors[i] = dev->irq;
-			}
-		}
-	} 
-	return interrupt_mode;
+	/* Select MSI-X over MSI if supported */
+	if (!port_msix_init(dev, vectors, mask))
+		return PCIE_PORT_MSIX_MODE;
+
+	if (!port_msi_init(dev, vectors, mask))
+		return PCIE_PORT_MSI_MODE;
+
+	return PCIE_PORT_INTx_MODE;
 }
 
 static int get_port_device_capability(struct pci_dev *dev)

--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux