diff options
author | Michael S. Tsirkin <mst@redhat.com> | 2009-07-27 16:37:48 -0400 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2009-09-09 16:29:24 -0400 |
commit | 711d57796f5ce2d02d6e62c9034afbb16aedda31 (patch) | |
tree | 935861fee775b171cafc96de57fe4fbfa19892eb /drivers | |
parent | 5228a828ee044834d78abdf25306bf46b19dcc4d (diff) |
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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pci/pci-sysfs.c | 37 | ||||
-rw-r--r-- | drivers/pci/pci.c | 16 | ||||
-rw-r--r-- | drivers/pci/pci.h | 1 |
3 files changed, 54 insertions, 0 deletions
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 85ebd02a64a7..0f6382f090ee 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c | |||
@@ -916,6 +916,24 @@ int __attribute__ ((weak)) pcibios_add_platform_entries(struct pci_dev *dev) | |||
916 | return 0; | 916 | return 0; |
917 | } | 917 | } |
918 | 918 | ||
919 | static ssize_t reset_store(struct device *dev, | ||
920 | struct device_attribute *attr, const char *buf, | ||
921 | size_t count) | ||
922 | { | ||
923 | struct pci_dev *pdev = to_pci_dev(dev); | ||
924 | unsigned long val; | ||
925 | ssize_t result = strict_strtoul(buf, 0, &val); | ||
926 | |||
927 | if (result < 0) | ||
928 | return result; | ||
929 | |||
930 | if (val != 1) | ||
931 | return -EINVAL; | ||
932 | return pci_reset_function(pdev); | ||
933 | } | ||
934 | |||
935 | static struct device_attribute reset_attr = __ATTR(reset, 0200, NULL, reset_store); | ||
936 | |||
919 | static int pci_create_capabilities_sysfs(struct pci_dev *dev) | 937 | static int pci_create_capabilities_sysfs(struct pci_dev *dev) |
920 | { | 938 | { |
921 | int retval; | 939 | int retval; |
@@ -943,7 +961,22 @@ static int pci_create_capabilities_sysfs(struct pci_dev *dev) | |||
943 | /* Active State Power Management */ | 961 | /* Active State Power Management */ |
944 | pcie_aspm_create_sysfs_dev_files(dev); | 962 | pcie_aspm_create_sysfs_dev_files(dev); |
945 | 963 | ||
964 | if (!pci_probe_reset_function(dev)) { | ||
965 | retval = device_create_file(&dev->dev, &reset_attr); | ||
966 | if (retval) | ||
967 | goto error; | ||
968 | dev->reset_fn = 1; | ||
969 | } | ||
946 | return 0; | 970 | return 0; |
971 | |||
972 | error: | ||
973 | pcie_aspm_remove_sysfs_dev_files(dev); | ||
974 | if (dev->vpd && dev->vpd->attr) { | ||
975 | sysfs_remove_bin_file(&dev->dev.kobj, dev->vpd->attr); | ||
976 | kfree(dev->vpd->attr); | ||
977 | } | ||
978 | |||
979 | return retval; | ||
947 | } | 980 | } |
948 | 981 | ||
949 | int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) | 982 | 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) | |||
1037 | } | 1070 | } |
1038 | 1071 | ||
1039 | pcie_aspm_remove_sysfs_dev_files(dev); | 1072 | pcie_aspm_remove_sysfs_dev_files(dev); |
1073 | if (dev->reset_fn) { | ||
1074 | device_remove_file(&dev->dev, &reset_attr); | ||
1075 | dev->reset_fn = 0; | ||
1076 | } | ||
1040 | } | 1077 | } |
1041 | 1078 | ||
1042 | /** | 1079 | /** |
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 7b70312181d7..7d55039ffa05 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
@@ -2262,6 +2262,22 @@ int __pci_reset_function(struct pci_dev *dev) | |||
2262 | EXPORT_SYMBOL_GPL(__pci_reset_function); | 2262 | EXPORT_SYMBOL_GPL(__pci_reset_function); |
2263 | 2263 | ||
2264 | /** | 2264 | /** |
2265 | * pci_probe_reset_function - check whether the device can be safely reset | ||
2266 | * @dev: PCI device to reset | ||
2267 | * | ||
2268 | * Some devices allow an individual function to be reset without affecting | ||
2269 | * other functions in the same device. The PCI device must be responsive | ||
2270 | * to PCI config space in order to use this function. | ||
2271 | * | ||
2272 | * Returns 0 if the device function can be reset or negative if the | ||
2273 | * device doesn't support resetting a single function. | ||
2274 | */ | ||
2275 | int pci_probe_reset_function(struct pci_dev *dev) | ||
2276 | { | ||
2277 | return pci_dev_reset(dev, 1); | ||
2278 | } | ||
2279 | |||
2280 | /** | ||
2265 | * pci_reset_function - quiesce and reset a PCI device function | 2281 | * pci_reset_function - quiesce and reset a PCI device function |
2266 | * @dev: PCI device to reset | 2282 | * @dev: PCI device to reset |
2267 | * | 2283 | * |
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 5ff4d25bf0e9..73d9d92715a0 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h | |||
@@ -16,6 +16,7 @@ extern void pci_cleanup_rom(struct pci_dev *dev); | |||
16 | extern int pci_mmap_fits(struct pci_dev *pdev, int resno, | 16 | extern int pci_mmap_fits(struct pci_dev *pdev, int resno, |
17 | struct vm_area_struct *vma); | 17 | struct vm_area_struct *vma); |
18 | #endif | 18 | #endif |
19 | int pci_probe_reset_function(struct pci_dev *dev); | ||
19 | 20 | ||
20 | /** | 21 | /** |
21 | * struct pci_platform_pm_ops - Firmware PM callbacks | 22 | * struct pci_platform_pm_ops - Firmware PM callbacks |