Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 27 Apr 2016 16:31:12 +0000 (UTC)
From:      John Baldwin <jhb@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r298708 - in head/sys: dev/cardbus dev/pci mips/nlm powerpc/ofw sparc64/pci
Message-ID:  <201604271631.u3RGVCjE001316@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: jhb
Date: Wed Apr 27 16:31:12 2016
New Revision: 298708
URL: https://svnweb.freebsd.org/changeset/base/298708

Log:
  Implement a PCI bus rescan method.
  
  Rescanning a PCI bus uses the following steps:
  - Fetch the current set of child devices and save it in the 'devlist'
    array.
  - Allocate a parallel array 'unchanged' initalized with NULL pointers.
  - Scan the bus checking each slot (and each function on slots with a
    multifunction device).
  - If a valid function is found, look for a matching device in the 'devlist'
    array.  If a device is found, save the pointer in the 'unchanged' array.
    If a device is not found, add a new device.
  - After the scan has finished, walk the 'devlist' array deleting any
    devices that do not have a matching pointer in the 'unchanged' array.
  - Finally, fetch an updated set of child devices and explicitly attach any
    devices that are not present in the 'unchanged' array.
  
  This builds on the previous changes to move subclass data management into
  pci_alloc_devinfo(), pci_child_added(), and bus_child_deleted().
  
  Subclasses of the PCI bus use custom rescan logic explicitly override the
  rescan method to disable rescans.
  
  Differential Revision:	https://reviews.freebsd.org/D6018

Modified:
  head/sys/dev/cardbus/cardbus.c
  head/sys/dev/pci/pci.c
  head/sys/dev/pci/pci_private.h
  head/sys/mips/nlm/xlp_pci.c
  head/sys/powerpc/ofw/ofw_pcibus.c
  head/sys/sparc64/pci/ofw_pcibus.c

Modified: head/sys/dev/cardbus/cardbus.c
==============================================================================
--- head/sys/dev/cardbus/cardbus.c	Wed Apr 27 16:29:03 2016	(r298707)
+++ head/sys/dev/cardbus/cardbus.c	Wed Apr 27 16:31:12 2016	(r298708)
@@ -346,6 +346,7 @@ static device_method_t cardbus_methods[]
 	DEVMETHOD(bus_get_dma_tag,	bus_generic_get_dma_tag),
 	DEVMETHOD(bus_read_ivar,	cardbus_read_ivar),
 	DEVMETHOD(bus_driver_added,	cardbus_driver_added),
+	DEVMETHOD(bus_rescan,		kobj_error_method),
 
 	/* Card Interface */
 	DEVMETHOD(card_attach_card,	cardbus_attach_card),

Modified: head/sys/dev/pci/pci.c
==============================================================================
--- head/sys/dev/pci/pci.c	Wed Apr 27 16:29:03 2016	(r298707)
+++ head/sys/dev/pci/pci.c	Wed Apr 27 16:31:12 2016	(r298708)
@@ -168,6 +168,7 @@ static device_method_t pci_methods[] = {
 	DEVMETHOD(bus_remap_intr,	pci_remap_intr_method),
 	DEVMETHOD(bus_suspend_child,	pci_suspend_child),
 	DEVMETHOD(bus_resume_child,	pci_resume_child),
+	DEVMETHOD(bus_rescan,		pci_rescan_method),
 
 	/* PCI interface */
 	DEVMETHOD(pci_read_config,	pci_read_config_method),
@@ -3917,6 +3918,103 @@ pci_add_children(device_t dev, int domai
 #undef REG
 }
 
+int
+pci_rescan_method(device_t dev)
+{
+#define	REG(n, w)	PCIB_READ_CONFIG(pcib, busno, s, f, n, w)
+	device_t pcib = device_get_parent(dev);
+	struct pci_softc *sc;
+	device_t child, *devlist, *unchanged;
+	int devcount, error, i, j, maxslots, oldcount;
+	int busno, domain, s, f, pcifunchigh;
+	uint8_t hdrtype;
+
+	/* No need to check for ARI on a rescan. */
+	error = device_get_children(dev, &devlist, &devcount);
+	if (error)
+		return (error);
+	if (devcount != 0) {
+		unchanged = malloc(devcount * sizeof(device_t), M_TEMP,
+		    M_NOWAIT | M_ZERO);
+		if (unchanged == NULL) {
+			free(devlist, M_TEMP);
+			return (ENOMEM);
+		}
+	} else
+		unchanged = NULL;
+
+	sc = device_get_softc(dev);
+	domain = pcib_get_domain(dev);
+	busno = pcib_get_bus(dev);
+	maxslots = PCIB_MAXSLOTS(pcib);
+	for (s = 0; s <= maxslots; s++) {
+		/* If function 0 is not present, skip to the next slot. */
+		f = 0;
+		if (REG(PCIR_VENDOR, 2) == 0xffff)
+			continue;
+		pcifunchigh = 0;
+		hdrtype = REG(PCIR_HDRTYPE, 1);
+		if ((hdrtype & PCIM_HDRTYPE) > PCI_MAXHDRTYPE)
+			continue;
+		if (hdrtype & PCIM_MFDEV)
+			pcifunchigh = PCIB_MAXFUNCS(pcib);
+		for (f = 0; f <= pcifunchigh; f++) {
+			if (REG(PCIR_VENDOR, 2) == 0xfff)
+				continue;
+
+			/*
+			 * Found a valid function.  Check if a
+			 * device_t for this device already exists.
+			 */
+			for (i = 0; i < devcount; i++) {
+				child = devlist[i];
+				if (child == NULL)
+					continue;
+				if (pci_get_slot(child) == s &&
+				    pci_get_function(child) == f) {
+					unchanged[i] = child;
+					goto next_func;
+				}
+			}
+
+			pci_identify_function(pcib, dev, domain, busno, s, f);
+		next_func:;
+		}
+	}
+
+	/* Remove devices that are no longer present. */
+	for (i = 0; i < devcount; i++) {
+		if (unchanged[i] != NULL)
+			continue;
+		device_delete_child(dev, devlist[i]);
+	}
+
+	free(devlist, M_TEMP);
+	oldcount = devcount;
+
+	/* Try to attach the devices just added. */
+	error = device_get_children(dev, &devlist, &devcount);
+	if (error) {
+		free(unchanged, M_TEMP);
+		return (error);
+	}
+
+	for (i = 0; i < devcount; i++) {
+		for (j = 0; j < oldcount; j++) {
+			if (devlist[i] == unchanged[j])
+				goto next_device;
+		}
+
+		device_probe_and_attach(devlist[i]);
+	next_device:;
+	}
+
+	free(unchanged, M_TEMP);
+	free(devlist, M_TEMP);
+	return (0);
+#undef REG
+}
+
 #ifdef PCI_IOV
 device_t
 pci_add_iov_child(device_t bus, device_t pf, uint16_t rid, uint16_t vid,

Modified: head/sys/dev/pci/pci_private.h
==============================================================================
--- head/sys/dev/pci/pci_private.h	Wed Apr 27 16:29:03 2016	(r298707)
+++ head/sys/dev/pci/pci_private.h	Wed Apr 27 16:31:12 2016	(r298708)
@@ -57,6 +57,7 @@ void		pci_add_resources(device_t bus, de
 void		pci_add_resources_ea(device_t bus, device_t dev, int alloc_iov);
 struct pci_devinfo *pci_alloc_devinfo_method(device_t dev);
 int		pci_attach_common(device_t dev);
+int		pci_rescan_method(device_t dev);
 void		pci_driver_added(device_t dev, driver_t *driver);
 int		pci_ea_is_enabled(device_t dev, int rid);
 int		pci_print_child(device_t dev, device_t child);

Modified: head/sys/mips/nlm/xlp_pci.c
==============================================================================
--- head/sys/mips/nlm/xlp_pci.c	Wed Apr 27 16:29:03 2016	(r298707)
+++ head/sys/mips/nlm/xlp_pci.c	Wed Apr 27 16:31:12 2016	(r298708)
@@ -154,6 +154,7 @@ static device_method_t xlp_pci_methods[]
 	/* Device interface */
 	DEVMETHOD(device_probe,		xlp_pci_probe),
 	DEVMETHOD(device_attach,	xlp_pci_attach),
+	DEVMETHOD(bus_rescan,		kobj_error_method),
 	DEVMETHOD_END
 };
 

Modified: head/sys/powerpc/ofw/ofw_pcibus.c
==============================================================================
--- head/sys/powerpc/ofw/ofw_pcibus.c	Wed Apr 27 16:29:03 2016	(r298707)
+++ head/sys/powerpc/ofw/ofw_pcibus.c	Wed Apr 27 16:31:12 2016	(r298708)
@@ -77,6 +77,7 @@ static device_method_t ofw_pcibus_method
 	/* Bus interface */
 	DEVMETHOD(bus_child_deleted,	ofw_pcibus_child_deleted),
 	DEVMETHOD(bus_child_pnpinfo_str, ofw_pcibus_child_pnpinfo_str_method),
+	DEVMETHOD(bus_rescan,		kobj_error_method),
 
 	/* PCI interface */
 	DEVMETHOD(pci_alloc_devinfo,	ofw_pcibus_alloc_devinfo),

Modified: head/sys/sparc64/pci/ofw_pcibus.c
==============================================================================
--- head/sys/sparc64/pci/ofw_pcibus.c	Wed Apr 27 16:29:03 2016	(r298707)
+++ head/sys/sparc64/pci/ofw_pcibus.c	Wed Apr 27 16:31:12 2016	(r298708)
@@ -81,6 +81,7 @@ static device_method_t ofw_pcibus_method
 	/* Bus interface */
 	DEVMETHOD(bus_child_deleted,	ofw_pcibus_child_deleted),
 	DEVMETHOD(bus_child_pnpinfo_str, ofw_pcibus_pnpinfo_str),
+	DEVMETHOD(bus_rescan,		kobj_error_method),
 
 	/* PCI interface */
 	DEVMETHOD(pci_alloc_devinfo,	ofw_pcibus_alloc_devinfo),



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201604271631.u3RGVCjE001316>