PCI: add rescan to /sys/.../pci_bus/.../
Yinghai Lu [Fri, 13 May 2011 00:11:39 +0000 (17:11 -0700)]
After remove the device from /sys, we have to rescan all or
find out the bridge and access /sys../device/rescan there.

this patch add /sys/.../pci_bus/.../rescan. So user can rescan more easy.
that is more clean and easy to understand.

like after remove 0000:c4:00.0, you can rescan 0000:c4 directly.

-v2: According to Jesse, use function instead of exposing attr, so could hide
#ifdef in header file.
     also add code to remove rescan file in remove path.
-v3: GregKH pointed out that we should use dev_attrs to avoid racing.
     So add pcibus_attrs and make it to be member of pcibus_attrs.
-v4: Change name to pcibus_dev_attrs according to GregKH

Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
Signed-off-by: Yinghai Lu <yinghai@kernel.org>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>

Documentation/ABI/testing/sysfs-bus-pci
drivers/pci/pci-sysfs.c
drivers/pci/pci.h
drivers/pci/probe.c

index 36bf454..349ecf2 100644 (file)
@@ -74,6 +74,15 @@ Description:
                hot-remove the PCI device and any of its children.
                Depends on CONFIG_HOTPLUG.
 
+What:          /sys/bus/pci/devices/.../pci_bus/.../rescan
+Date:          May 2011
+Contact:       Linux PCI developers <linux-pci@vger.kernel.org>
+Description:
+               Writing a non-zero value to this attribute will
+               force a rescan of the bus and all child buses,
+               and re-discover devices removed earlier from this
+               part of the device tree.  Depends on CONFIG_HOTPLUG.
+
 What:          /sys/bus/pci/devices/.../rescan
 Date:          January 2009
 Contact:       Linux PCI developers <linux-pci@vger.kernel.org>
index f8deb3e..c690abc 100644 (file)
@@ -318,6 +318,25 @@ remove_store(struct device *dev, struct device_attribute *dummy,
                count = ret;
        return count;
 }
+
+static ssize_t
+dev_bus_rescan_store(struct device *dev, struct device_attribute *attr,
+                const char *buf, size_t count)
+{
+       unsigned long val;
+       struct pci_bus *bus = to_pci_bus(dev);
+
+       if (strict_strtoul(buf, 0, &val) < 0)
+               return -EINVAL;
+
+       if (val) {
+               mutex_lock(&pci_remove_rescan_mutex);
+               pci_rescan_bus(bus);
+               mutex_unlock(&pci_remove_rescan_mutex);
+       }
+       return count;
+}
+
 #endif
 
 struct device_attribute pci_dev_attrs[] = {
@@ -347,6 +366,13 @@ struct device_attribute pci_dev_attrs[] = {
        __ATTR_NULL,
 };
 
+struct device_attribute pcibus_dev_attrs[] = {
+#ifdef CONFIG_HOTPLUG
+       __ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, dev_bus_rescan_store),
+#endif
+       __ATTR_NULL,
+};
+
 static ssize_t
 boot_vga_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
index a6ec200..dcf640a 100644 (file)
@@ -156,6 +156,7 @@ static inline int pci_no_d1d2(struct pci_dev *dev)
 
 }
 extern struct device_attribute pci_dev_attrs[];
+extern struct device_attribute pcibus_dev_attrs[];
 extern struct device_attribute dev_attr_cpuaffinity;
 extern struct device_attribute dev_attr_cpulistaffinity;
 #ifdef CONFIG_HOTPLUG
index 44cbbba..c471295 100644 (file)
@@ -95,6 +95,7 @@ static void release_pcibus_dev(struct device *dev)
 static struct class pcibus_class = {
        .name           = "pci_bus",
        .dev_release    = &release_pcibus_dev,
+       .dev_attrs      = pcibus_dev_attrs,
 };
 
 static int __init pcibus_class_init(void)