PCI: Introduce /sys/bus/pci/devices/.../remove

This patch adds an attribute named "remove" to a PCI device's sysfs
directory.  Writing a non-zero value to this attribute will remove the PCI
device and any children of it.

Trent Piepho wrote the original implementation and documentation.

Thanks to Vegard Nossum for testing under kmemcheck and finding locking
issues with the sysfs interface.

Cc: Trent Piepho <xyzzy@speakeasy.org>
Tested-by: Vegard Nossum <vegard.nossum@gmail.com>
Signed-off-by: Alex Chiang <achiang@hp.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
This commit is contained in:
Alex Chiang 2009-03-20 14:56:36 -06:00 committed by Jesse Barnes
parent 705b1aaa82
commit 77c27c7b49
3 changed files with 54 additions and 0 deletions

View file

@ -66,6 +66,14 @@ Description:
re-discover previously removed devices. re-discover previously removed devices.
Depends on CONFIG_HOTPLUG. Depends on CONFIG_HOTPLUG.
What: /sys/bus/pci/devices/.../remove
Date: January 2009
Contact: Linux PCI developers <linux-pci@vger.kernel.org>
Description:
Writing a non-zero value to this attribute will
hot-remove the PCI device and any of its children.
Depends on CONFIG_HOTPLUG.
What: /sys/bus/pci/devices/.../vpd What: /sys/bus/pci/devices/.../vpd
Date: February 2008 Date: February 2008
Contact: Ben Hutchings <bhutchings@solarflare.com> Contact: Ben Hutchings <bhutchings@solarflare.com>

View file

@ -12,6 +12,7 @@ that support it. For example, a given bus might look like this:
| |-- enable | |-- enable
| |-- irq | |-- irq
| |-- local_cpus | |-- local_cpus
| |-- remove
| |-- resource | |-- resource
| |-- resource0 | |-- resource0
| |-- resource1 | |-- resource1
@ -36,6 +37,7 @@ files, each with their own function.
enable Whether the device is enabled (ascii, rw) enable Whether the device is enabled (ascii, rw)
irq IRQ number (ascii, ro) irq IRQ number (ascii, ro)
local_cpus nearby CPU mask (cpumask, ro) local_cpus nearby CPU mask (cpumask, ro)
remove remove device from kernel's list (ascii, wo)
resource PCI resource host addresses (ascii, ro) resource PCI resource host addresses (ascii, ro)
resource0..N PCI resource N, if present (binary, mmap) resource0..N PCI resource N, if present (binary, mmap)
resource0_wc..N_wc PCI WC map resource N, if prefetchable (binary, mmap) resource0_wc..N_wc PCI WC map resource N, if prefetchable (binary, mmap)
@ -46,6 +48,7 @@ files, each with their own function.
ro - read only file ro - read only file
rw - file is readable and writable rw - file is readable and writable
wo - write only file
mmap - file is mmapable mmap - file is mmapable
ascii - file contains ascii text ascii - file contains ascii text
binary - file contains binary data binary - file contains binary data
@ -73,6 +76,13 @@ that the device must be enabled for a rom read to return data succesfully.
In the event a driver is not bound to the device, it can be enabled using the In the event a driver is not bound to the device, it can be enabled using the
'enable' file, documented above. 'enable' file, documented above.
The 'remove' file is used to remove the PCI device, by writing a non-zero
integer to the file. This does not involve any kind of hot-plug functionality,
e.g. powering off the device. The device is removed from the kernel's list of
PCI devices, the sysfs directory for it is removed, and the device will be
removed from any drivers attached to it. Removal of PCI root buses is
disallowed.
Accessing legacy resources through sysfs Accessing legacy resources through sysfs
---------------------------------------- ----------------------------------------

View file

@ -243,6 +243,39 @@ struct bus_attribute pci_bus_attrs[] = {
__ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, bus_rescan_store), __ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, bus_rescan_store),
__ATTR_NULL __ATTR_NULL
}; };
static void remove_callback(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
mutex_lock(&pci_remove_rescan_mutex);
pci_remove_bus_device(pdev);
mutex_unlock(&pci_remove_rescan_mutex);
}
static ssize_t
remove_store(struct device *dev, struct device_attribute *dummy,
const char *buf, size_t count)
{
int ret = 0;
unsigned long val;
struct pci_dev *pdev = to_pci_dev(dev);
if (strict_strtoul(buf, 0, &val) < 0)
return -EINVAL;
if (pci_is_root_bus(pdev->bus))
return -EBUSY;
/* An attribute cannot be unregistered by one of its own methods,
* so we have to use this roundabout approach.
*/
if (val)
ret = device_schedule_callback(dev, remove_callback);
if (ret)
count = ret;
return count;
}
#endif #endif
struct device_attribute pci_dev_attrs[] = { struct device_attribute pci_dev_attrs[] = {
@ -263,6 +296,9 @@ struct device_attribute pci_dev_attrs[] = {
__ATTR(broken_parity_status,(S_IRUGO|S_IWUSR), __ATTR(broken_parity_status,(S_IRUGO|S_IWUSR),
broken_parity_status_show,broken_parity_status_store), broken_parity_status_show,broken_parity_status_store),
__ATTR(msi_bus, 0644, msi_bus_show, msi_bus_store), __ATTR(msi_bus, 0644, msi_bus_show, msi_bus_store),
#ifdef CONFIG_HOTPLUG
__ATTR(remove, (S_IWUSR|S_IWGRP), NULL, remove_store),
#endif
__ATTR_NULL, __ATTR_NULL,
}; };