diff mbox

Support for ARI probing

Message ID 20090623184105.GI19977@parisc-linux.org (mailing list archive)
State RFC, archived
Headers show

Commit Message

Matthew Wilcox June 23, 2009, 6:41 p.m. UTC
This patch fixes two problems.

One is that devices which support ARI may not have all their functions
discovered.  The main PCI code will find them all, if they're
contiguous, but if they're hotplugged, the hotplug drivers will only
call pci_scan_slot() for devfn 0, and thus will only scan the first
8 functions.

The second is that the functions in an ARI device can be discovered
through walking a linked list in extended config space.  In this case,
we don't have to check one devfn in eight, we can just stop looking
when we reach the end of the linked list.  I'm unconvinced this is an
improvement in speed, given that there will be multiple successful reads
from extended config space instead of multiple failed reads from config
space which doesn't exist ... anyway, it makes the code look better.

I tried a few different options for different ways to discover functions.
The next_fn pointer seemed like the best way to go.

This patch ignores the pcibios_scan_all_fns() work that Alex has been
doing; it should probably be rebased on top of that.

An improvement would note that all PCIe devices which have a Downstream
Port as a parent can only be probed with a devfn of 0, and so return
early in that case too, not just if ARI is set.

Looking for feedback at this point ...
diff mbox

Patch

diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 40e75f6..62fb22c 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1041,6 +1041,26 @@  struct pci_dev *__ref pci_scan_single_device(struct pci_bus *bus, int devfn)
 }
 EXPORT_SYMBOL(pci_scan_single_device);
 
+static unsigned next_ari_fn(struct pci_dev *dev, unsigned fn)
+{
+	u16 cap;
+	unsigned pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI);
+	if (!pos)
+		return 0;
+	pci_read_config_word(dev, pos + 4, &cap);
+	return cap >> 8;
+}
+
+static unsigned next_trad_fn(struct pci_dev *dev, unsigned fn)
+{
+	return (fn + 1) % 8;
+}
+
+static unsigned no_next_fn(struct pci_dev *dev, unsigned fn)
+{
+	return 0;
+}
+
 /**
  * pci_scan_slot - scan a PCI slot on a bus for devices.
  * @bus: PCI bus to scan
@@ -1054,22 +1074,28 @@  EXPORT_SYMBOL(pci_scan_single_device);
  */
 int pci_scan_slot(struct pci_bus *bus, int devfn)
 {
-	int fn, nr = 0;
+	unsigned fn, nr = 0;
 	struct pci_dev *dev;
+	unsigned (*next_fn)(struct pci_dev *, unsigned) = no_next_fn;
+
+	if (pci_ari_enabled(bus) && (devfn > 0))
+		return 0; /* Already scanned the entire slot */
 
 	dev = pci_scan_single_device(bus, devfn);
 	if (dev && !dev->is_added)	/* new device? */
 		nr++;
 
-	if ((dev && dev->multifunction) ||
-	    (!dev && pcibios_scan_all_fns(bus, devfn))) {
-		for (fn = 1; fn < 8; fn++) {
-			dev = pci_scan_single_device(bus, devfn + fn);
-			if (dev) {
-				if (!dev->is_added)
-					nr++;
-				dev->multifunction = 1;
-			}
+	if (pci_ari_enabled(bus))
+		next_fn = next_ari_fn;
+	else if (dev && dev->multifunction)
+		next_fn = next_trad_fn;
+
+	for (fn = next_fn(dev, 0); fn > 0; fn = next_fn(dev, fn)) {
+		dev = pci_scan_single_device(bus, devfn + fn);
+		if (dev) {
+			if (!dev->is_added)
+				nr++;
+			dev->multifunction = 1;
 		}
 	}