PCI: expose function reset capability in sysfs

Some devices allow an individual function to be reset without affecting
other functions in the same device: that's what pci_reset_function does.
For devices that have this support, expose reset attribite in sysfs.

This is useful e.g. for virtualization, where a qemu userspace
process wants to reset the device when the guest is reset,
to emulate machine reboot as closely as possible.

Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
This commit is contained in:
Michael S. Tsirkin 2009-07-27 23:37:48 +03:00 committed by Jesse Barnes
parent 5228a828ee
commit 711d57796f
5 changed files with 65 additions and 0 deletions

View file

@ -84,6 +84,16 @@ Description:
from this part of the device tree. from this part of the device tree.
Depends on CONFIG_HOTPLUG. Depends on CONFIG_HOTPLUG.
What: /sys/bus/pci/devices/.../reset
Date: July 2009
Contact: Michael S. Tsirkin <mst@redhat.com>
Description:
Some devices allow an individual function to be reset
without affecting other functions in the same device.
For devices that have this support, a file named reset
will be present in sysfs. Writing 1 to this file
will perform reset.
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

@ -916,6 +916,24 @@ int __attribute__ ((weak)) pcibios_add_platform_entries(struct pci_dev *dev)
return 0; return 0;
} }
static ssize_t reset_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
struct pci_dev *pdev = to_pci_dev(dev);
unsigned long val;
ssize_t result = strict_strtoul(buf, 0, &val);
if (result < 0)
return result;
if (val != 1)
return -EINVAL;
return pci_reset_function(pdev);
}
static struct device_attribute reset_attr = __ATTR(reset, 0200, NULL, reset_store);
static int pci_create_capabilities_sysfs(struct pci_dev *dev) static int pci_create_capabilities_sysfs(struct pci_dev *dev)
{ {
int retval; int retval;
@ -943,7 +961,22 @@ static int pci_create_capabilities_sysfs(struct pci_dev *dev)
/* Active State Power Management */ /* Active State Power Management */
pcie_aspm_create_sysfs_dev_files(dev); pcie_aspm_create_sysfs_dev_files(dev);
if (!pci_probe_reset_function(dev)) {
retval = device_create_file(&dev->dev, &reset_attr);
if (retval)
goto error;
dev->reset_fn = 1;
}
return 0; return 0;
error:
pcie_aspm_remove_sysfs_dev_files(dev);
if (dev->vpd && dev->vpd->attr) {
sysfs_remove_bin_file(&dev->dev.kobj, dev->vpd->attr);
kfree(dev->vpd->attr);
}
return retval;
} }
int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev)
@ -1037,6 +1070,10 @@ static void pci_remove_capabilities_sysfs(struct pci_dev *dev)
} }
pcie_aspm_remove_sysfs_dev_files(dev); pcie_aspm_remove_sysfs_dev_files(dev);
if (dev->reset_fn) {
device_remove_file(&dev->dev, &reset_attr);
dev->reset_fn = 0;
}
} }
/** /**

View file

@ -2261,6 +2261,22 @@ int __pci_reset_function(struct pci_dev *dev)
} }
EXPORT_SYMBOL_GPL(__pci_reset_function); EXPORT_SYMBOL_GPL(__pci_reset_function);
/**
* pci_probe_reset_function - check whether the device can be safely reset
* @dev: PCI device to reset
*
* Some devices allow an individual function to be reset without affecting
* other functions in the same device. The PCI device must be responsive
* to PCI config space in order to use this function.
*
* Returns 0 if the device function can be reset or negative if the
* device doesn't support resetting a single function.
*/
int pci_probe_reset_function(struct pci_dev *dev)
{
return pci_dev_reset(dev, 1);
}
/** /**
* pci_reset_function - quiesce and reset a PCI device function * pci_reset_function - quiesce and reset a PCI device function
* @dev: PCI device to reset * @dev: PCI device to reset

View file

@ -16,6 +16,7 @@ extern void pci_cleanup_rom(struct pci_dev *dev);
extern int pci_mmap_fits(struct pci_dev *pdev, int resno, extern int pci_mmap_fits(struct pci_dev *pdev, int resno,
struct vm_area_struct *vma); struct vm_area_struct *vma);
#endif #endif
int pci_probe_reset_function(struct pci_dev *dev);
/** /**
* struct pci_platform_pm_ops - Firmware PM callbacks * struct pci_platform_pm_ops - Firmware PM callbacks

View file

@ -276,6 +276,7 @@ struct pci_dev {
unsigned int state_saved:1; unsigned int state_saved:1;
unsigned int is_physfn:1; unsigned int is_physfn:1;
unsigned int is_virtfn:1; unsigned int is_virtfn:1;
unsigned int reset_fn:1;
pci_dev_flags_t dev_flags; pci_dev_flags_t dev_flags;
atomic_t enable_cnt; /* pci_enable_device has been called */ atomic_t enable_cnt; /* pci_enable_device has been called */