mirror of
https://gitee.com/bianbu-linux/linux-6.6
synced 2025-04-24 14:07:52 -04: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
4 changed files with 142 additions and 0 deletions
|
@ -121,6 +121,23 @@ Description:
|
||||||
child buses, and re-discover devices removed earlier
|
child buses, and re-discover devices removed earlier
|
||||||
from this part of the device tree.
|
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
|
What: /sys/bus/pci/devices/.../reset
|
||||||
Date: July 2009
|
Date: July 2009
|
||||||
Contact: Michael S. Tsirkin <mst@redhat.com>
|
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_config_attr_group,
|
||||||
&pci_dev_rom_attr_group,
|
&pci_dev_rom_attr_group,
|
||||||
&pci_dev_reset_attr_group,
|
&pci_dev_reset_attr_group,
|
||||||
|
&pci_dev_reset_method_attr_group,
|
||||||
&pci_dev_vpd_attr_group,
|
&pci_dev_vpd_attr_group,
|
||||||
#ifdef CONFIG_DMI
|
#ifdef CONFIG_DMI
|
||||||
&pci_dev_smbios_attr_group,
|
&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" },
|
{ 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
|
* __pci_reset_function_locked - reset a PCI device function while holding
|
||||||
* the @dev mutex lock.
|
* 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;
|
extern const struct attribute_group aspm_ctrl_attr_group;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
extern const struct attribute_group pci_dev_reset_method_attr_group;
|
||||||
|
|
||||||
#endif /* DRIVERS_PCI_H */
|
#endif /* DRIVERS_PCI_H */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue