mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-16 08:44:21 +08:00
PCI: Allow userspace to query and set device reset mechanism
Add "reset_method" sysfs attribute to enable user to query and set preferred device reset methods and their ordering. [bhelgaas: on invalid sysfs input, return error and preserve previous config, as in earlier patch versions] Co-developed-by: Alex Williamson <alex.williamson@redhat.com> Link: https://lore.kernel.org/r/20210817180500.1253-6-ameynarkhede03@gmail.com Signed-off-by: Alex Williamson <alex.williamson@redhat.com> Signed-off-by: Amey Narkhede <ameynarkhede03@gmail.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Raphael Norwitz <raphael.norwitz@nutanix.com>
This commit is contained in:
parent
4ec36dfeb1
commit
d88f521da3
@ -121,6 +121,23 @@ Description:
|
||||
child buses, and re-discover devices removed earlier
|
||||
from this part of the device tree.
|
||||
|
||||
What: /sys/bus/pci/devices/.../reset_method
|
||||
Date: August 2021
|
||||
Contact: Amey Narkhede <ameynarkhede03@gmail.com>
|
||||
Description:
|
||||
Some devices allow an individual function to be reset
|
||||
without affecting other functions in the same slot.
|
||||
|
||||
For devices that have this support, a file named
|
||||
reset_method is present in sysfs. Reading this file
|
||||
gives names of the supported and enabled reset methods and
|
||||
their ordering. Writing a space-separated list of names of
|
||||
reset methods sets the reset methods and ordering to be
|
||||
used when resetting the device. Writing an empty string
|
||||
disables the ability to reset the device. Writing
|
||||
"default" enables all supported reset methods in the
|
||||
default ordering.
|
||||
|
||||
What: /sys/bus/pci/devices/.../reset
|
||||
Date: July 2009
|
||||
Contact: Michael S. Tsirkin <mst@redhat.com>
|
||||
|
@ -1491,6 +1491,7 @@ const struct attribute_group *pci_dev_groups[] = {
|
||||
&pci_dev_config_attr_group,
|
||||
&pci_dev_rom_attr_group,
|
||||
&pci_dev_reset_attr_group,
|
||||
&pci_dev_reset_method_attr_group,
|
||||
&pci_dev_vpd_attr_group,
|
||||
#ifdef CONFIG_DMI
|
||||
&pci_dev_smbios_attr_group,
|
||||
|
@ -5132,6 +5132,128 @@ static const struct pci_reset_fn_method pci_reset_fn_methods[] = {
|
||||
{ pci_reset_bus_function, .name = "bus" },
|
||||
};
|
||||
|
||||
static ssize_t reset_method_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
ssize_t len = 0;
|
||||
int i, m;
|
||||
|
||||
for (i = 0; i < PCI_NUM_RESET_METHODS; i++) {
|
||||
m = pdev->reset_methods[i];
|
||||
if (!m)
|
||||
break;
|
||||
|
||||
len += sysfs_emit_at(buf, len, "%s%s", len ? " " : "",
|
||||
pci_reset_fn_methods[m].name);
|
||||
}
|
||||
|
||||
if (len)
|
||||
len += sysfs_emit_at(buf, len, "\n");
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int reset_method_lookup(const char *name)
|
||||
{
|
||||
int m;
|
||||
|
||||
for (m = 1; m < PCI_NUM_RESET_METHODS; m++) {
|
||||
if (sysfs_streq(name, pci_reset_fn_methods[m].name))
|
||||
return m;
|
||||
}
|
||||
|
||||
return 0; /* not found */
|
||||
}
|
||||
|
||||
static ssize_t reset_method_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
char *options, *name;
|
||||
int m, n;
|
||||
u8 reset_methods[PCI_NUM_RESET_METHODS] = { 0 };
|
||||
|
||||
if (sysfs_streq(buf, "")) {
|
||||
pdev->reset_methods[0] = 0;
|
||||
pci_warn(pdev, "All device reset methods disabled by user");
|
||||
return count;
|
||||
}
|
||||
|
||||
if (sysfs_streq(buf, "default")) {
|
||||
pci_init_reset_methods(pdev);
|
||||
return count;
|
||||
}
|
||||
|
||||
options = kstrndup(buf, count, GFP_KERNEL);
|
||||
if (!options)
|
||||
return -ENOMEM;
|
||||
|
||||
n = 0;
|
||||
while ((name = strsep(&options, " ")) != NULL) {
|
||||
if (sysfs_streq(name, ""))
|
||||
continue;
|
||||
|
||||
name = strim(name);
|
||||
|
||||
m = reset_method_lookup(name);
|
||||
if (!m) {
|
||||
pci_err(pdev, "Invalid reset method '%s'", name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (pci_reset_fn_methods[m].reset_fn(pdev, 1)) {
|
||||
pci_err(pdev, "Unsupported reset method '%s'", name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (n == PCI_NUM_RESET_METHODS - 1) {
|
||||
pci_err(pdev, "Too many reset methods\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
reset_methods[n++] = m;
|
||||
}
|
||||
|
||||
reset_methods[n] = 0;
|
||||
|
||||
/* Warn if dev-specific supported but not highest priority */
|
||||
if (pci_reset_fn_methods[1].reset_fn(pdev, 1) == 0 &&
|
||||
reset_methods[0] != 1)
|
||||
pci_warn(pdev, "Device-specific reset disabled/de-prioritized by user");
|
||||
memcpy(pdev->reset_methods, reset_methods, sizeof(pdev->reset_methods));
|
||||
kfree(options);
|
||||
return count;
|
||||
|
||||
error:
|
||||
/* Leave previous methods unchanged */
|
||||
kfree(options);
|
||||
return -EINVAL;
|
||||
}
|
||||
static DEVICE_ATTR_RW(reset_method);
|
||||
|
||||
static struct attribute *pci_dev_reset_method_attrs[] = {
|
||||
&dev_attr_reset_method.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static umode_t pci_dev_reset_method_attr_is_visible(struct kobject *kobj,
|
||||
struct attribute *a, int n)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
|
||||
|
||||
if (!pci_reset_supported(pdev))
|
||||
return 0;
|
||||
|
||||
return a->mode;
|
||||
}
|
||||
|
||||
const struct attribute_group pci_dev_reset_method_attr_group = {
|
||||
.attrs = pci_dev_reset_method_attrs,
|
||||
.is_visible = pci_dev_reset_method_attr_is_visible,
|
||||
};
|
||||
|
||||
/**
|
||||
* __pci_reset_function_locked - reset a PCI device function while holding
|
||||
* the @dev mutex lock.
|
||||
|
@ -718,4 +718,6 @@ static inline int pci_acpi_program_hp_params(struct pci_dev *dev)
|
||||
extern const struct attribute_group aspm_ctrl_attr_group;
|
||||
#endif
|
||||
|
||||
extern const struct attribute_group pci_dev_reset_method_attr_group;
|
||||
|
||||
#endif /* DRIVERS_PCI_H */
|
||||
|
Loading…
Reference in New Issue
Block a user