aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Baldwin <jhb@FreeBSD.org>2016-04-27 16:31:12 +0000
committerJohn Baldwin <jhb@FreeBSD.org>2016-04-27 16:31:12 +0000
commit3d0338a09278a0cc38785321f102de9775c57ed3 (patch)
tree1dfb549dd2b02d536680e720de4a0da58313b464
parenta907c6914c5879870b2597a63253cea0a5b7bdb8 (diff)
downloadsrc-3d0338a09278a0cc38785321f102de9775c57ed3.tar.gz
src-3d0338a09278a0cc38785321f102de9775c57ed3.zip
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
Notes
Notes: svn path=/head/; revision=298708
-rw-r--r--sys/dev/cardbus/cardbus.c1
-rw-r--r--sys/dev/pci/pci.c98
-rw-r--r--sys/dev/pci/pci_private.h1
-rw-r--r--sys/mips/nlm/xlp_pci.c1
-rw-r--r--sys/powerpc/ofw/ofw_pcibus.c1
-rw-r--r--sys/sparc64/pci/ofw_pcibus.c1
6 files changed, 103 insertions, 0 deletions
diff --git a/sys/dev/cardbus/cardbus.c b/sys/dev/cardbus/cardbus.c
index a6037e15c1ea..765c1700dd28 100644
--- a/sys/dev/cardbus/cardbus.c
+++ b/sys/dev/cardbus/cardbus.c
@@ -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),
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index 532351ea31ed..bdb3c5e4fb7b 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -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 domain, int busno)
#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,
diff --git a/sys/dev/pci/pci_private.h b/sys/dev/pci/pci_private.h
index 222ffb89063a..a9958ca627db 100644
--- a/sys/dev/pci/pci_private.h
+++ b/sys/dev/pci/pci_private.h
@@ -57,6 +57,7 @@ void pci_add_resources(device_t bus, device_t dev, int force,
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);
diff --git a/sys/mips/nlm/xlp_pci.c b/sys/mips/nlm/xlp_pci.c
index 9e911469d086..43b1574ae83d 100644
--- a/sys/mips/nlm/xlp_pci.c
+++ b/sys/mips/nlm/xlp_pci.c
@@ -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
};
diff --git a/sys/powerpc/ofw/ofw_pcibus.c b/sys/powerpc/ofw/ofw_pcibus.c
index 01e2343ebca6..05aa06c3961a 100644
--- a/sys/powerpc/ofw/ofw_pcibus.c
+++ b/sys/powerpc/ofw/ofw_pcibus.c
@@ -77,6 +77,7 @@ static device_method_t ofw_pcibus_methods[] = {
/* 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),
diff --git a/sys/sparc64/pci/ofw_pcibus.c b/sys/sparc64/pci/ofw_pcibus.c
index 11f19cf93e5a..2fa289fa6b91 100644
--- a/sys/sparc64/pci/ofw_pcibus.c
+++ b/sys/sparc64/pci/ofw_pcibus.c
@@ -81,6 +81,7 @@ static device_method_t ofw_pcibus_methods[] = {
/* 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),