Merge branch 'pci/reset'

- Cache PCIe Device Capabilities register (Amey Narkhede)

- Add pcie_reset_flr() with 'probe' argument (Amey Narkhede)

- Add pdev->reset_methods[] array to track reset method ordering (Amey
  Narkhede)

- Remove reset_fn field from pci_dev (Amey Narkhede)

- Add sysfs interface to query and set device reset mechanism (Amey
  Narkhede)

- Add pci_set_acpi_fwnode() to set ACPI_COMPANION (Shanker Donthineni)

- Use acpi_pci_power_manageable() instead of duplicating logic (Shanker
  Donthineni)

- Set ACPI fwnode early and at the same time with OF (Shanker Donthineni)

- Add support for ACPI _RST reset method (Shanker Donthineni)

- Change reset function 'probe' argument to bool (Amey Narkhede)

* pci/reset:
  PCI: Change the type of probe argument in reset functions
  PCI: Add support for ACPI _RST reset method
  PCI: Setup ACPI fwnode early and at the same time with OF
  PCI: Use acpi_pci_power_manageable()
  PCI: Add pci_set_acpi_fwnode() to set ACPI_COMPANION
  PCI: Allow userspace to query and set device reset mechanism
  PCI: Remove reset_fn field from pci_dev
  PCI: Add array to track reset method ordering
  PCI: Add pcie_reset_flr() with 'probe' argument
  PCI: Cache PCIe Device Capabilities register
This commit is contained in:
Bjorn Helgaas 2021-09-02 14:56:42 -05:00
commit e210d9fc09
16 changed files with 359 additions and 166 deletions

View file

@ -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>

View file

@ -306,9 +306,7 @@ static int nitrox_device_flr(struct pci_dev *pdev)
return -ENOMEM;
}
/* check flr support */
if (pcie_has_flr(pdev))
pcie_flr(pdev);
pcie_reset_flr(pdev, PCI_RESET_DO_RESET);
pci_restore_state(pdev);

View file

@ -526,7 +526,7 @@ static void octeon_destroy_resources(struct octeon_device *oct)
oct->irq_name_storage = NULL;
}
/* Soft reset the octeon device before exiting */
if (oct->pci_dev->reset_fn)
if (!pcie_reset_flr(oct->pci_dev, PCI_RESET_PROBE))
octeon_pci_flr(oct);
else
cn23xx_vf_ask_pf_to_do_flr(oct);

View file

@ -184,7 +184,7 @@ void pciehp_release_ctrl(struct controller *ctrl);
int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot);
int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot);
int pciehp_reset_slot(struct hotplug_slot *hotplug_slot, int probe);
int pciehp_reset_slot(struct hotplug_slot *hotplug_slot, bool probe);
int pciehp_get_attention_status(struct hotplug_slot *hotplug_slot, u8 *status);
int pciehp_set_raw_indicator_status(struct hotplug_slot *h_slot, u8 status);
int pciehp_get_raw_indicator_status(struct hotplug_slot *h_slot, u8 *status);

View file

@ -870,7 +870,7 @@ void pcie_disable_interrupt(struct controller *ctrl)
* momentarily, if we see that they could interfere. Also, clear any spurious
* events after.
*/
int pciehp_reset_slot(struct hotplug_slot *hotplug_slot, int probe)
int pciehp_reset_slot(struct hotplug_slot *hotplug_slot, bool probe)
{
struct controller *ctrl = to_ctrl(hotplug_slot);
struct pci_dev *pdev = ctrl_dev(ctrl);

View file

@ -526,7 +526,7 @@ static int pnv_php_enable(struct pnv_php_slot *php_slot, bool rescan)
return 0;
}
static int pnv_php_reset_slot(struct hotplug_slot *slot, int probe)
static int pnv_php_reset_slot(struct hotplug_slot *slot, bool probe)
{
struct pnv_php_slot *php_slot = to_pnv_php_slot(slot);
struct pci_dev *bridge = php_slot->pdev;

View file

@ -934,58 +934,77 @@ static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev)
static struct acpi_device *acpi_pci_find_companion(struct device *dev);
static bool acpi_pci_bridge_d3(struct pci_dev *dev)
void pci_set_acpi_fwnode(struct pci_dev *dev)
{
const struct fwnode_handle *fwnode;
struct acpi_device *adev;
struct pci_dev *root;
u8 val;
if (!ACPI_COMPANION(&dev->dev) && !pci_dev_is_added(dev))
ACPI_COMPANION_SET(&dev->dev,
acpi_pci_find_companion(&dev->dev));
}
if (!dev->is_hotplug_bridge)
return false;
/**
* pci_dev_acpi_reset - do a function level reset using _RST method
* @dev: device to reset
* @probe: if true, return 0 if device supports _RST
*/
int pci_dev_acpi_reset(struct pci_dev *dev, bool probe)
{
acpi_handle handle = ACPI_HANDLE(&dev->dev);
/* Assume D3 support if the bridge is power-manageable by ACPI. */
adev = ACPI_COMPANION(&dev->dev);
if (!adev && !pci_dev_is_added(dev)) {
adev = acpi_pci_find_companion(&dev->dev);
ACPI_COMPANION_SET(&dev->dev, adev);
if (!handle || !acpi_has_method(handle, "_RST"))
return -ENOTTY;
if (probe)
return 0;
if (ACPI_FAILURE(acpi_evaluate_object(handle, "_RST", NULL, NULL))) {
pci_warn(dev, "ACPI _RST failed\n");
return -ENOTTY;
}
if (adev && acpi_device_power_manageable(adev))
return true;
/*
* Look for a special _DSD property for the root port and if it
* is set we know the hierarchy behind it supports D3 just fine.
*/
root = pcie_find_root_port(dev);
if (!root)
return false;
adev = ACPI_COMPANION(&root->dev);
if (root == dev) {
/*
* It is possible that the ACPI companion is not yet bound
* for the root port so look it up manually here.
*/
if (!adev && !pci_dev_is_added(root))
adev = acpi_pci_find_companion(&root->dev);
}
if (!adev)
return false;
fwnode = acpi_fwnode_handle(adev);
if (fwnode_property_read_u8(fwnode, "HotPlugSupportInD3", &val))
return false;
return val == 1;
return 0;
}
static bool acpi_pci_power_manageable(struct pci_dev *dev)
{
struct acpi_device *adev = ACPI_COMPANION(&dev->dev);
return adev ? acpi_device_power_manageable(adev) : false;
if (!adev)
return false;
return acpi_device_power_manageable(adev);
}
static bool acpi_pci_bridge_d3(struct pci_dev *dev)
{
const union acpi_object *obj;
struct acpi_device *adev;
struct pci_dev *rpdev;
if (!dev->is_hotplug_bridge)
return false;
/* Assume D3 support if the bridge is power-manageable by ACPI. */
if (acpi_pci_power_manageable(dev))
return true;
/*
* The ACPI firmware will provide the device-specific properties through
* _DSD configuration object. Look for the 'HotPlugSupportInD3' property
* for the root port and if it is set we know the hierarchy behind it
* supports D3 just fine.
*/
rpdev = pcie_find_root_port(dev);
if (!rpdev)
return false;
adev = ACPI_COMPANION(&rpdev->dev);
if (!adev)
return false;
if (acpi_dev_get_property(adev, "HotPlugSupportInD3",
ACPI_TYPE_INTEGER, &obj) < 0)
return false;
return obj->integer.value == 1;
}
static int acpi_pci_set_power_state(struct pci_dev *dev, pci_power_t state)

View file

@ -1367,7 +1367,7 @@ static umode_t pci_dev_reset_attr_is_visible(struct kobject *kobj,
{
struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
if (!pdev->reset_fn)
if (!pci_reset_supported(pdev))
return 0;
return a->mode;
@ -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,

View file

@ -31,6 +31,7 @@
#include <linux/vmalloc.h>
#include <asm/dma.h>
#include <linux/aer.h>
#include <linux/bitfield.h>
#include "pci.h"
DEFINE_MUTEX(pci_slot_mutex);
@ -72,6 +73,11 @@ static void pci_dev_d3_sleep(struct pci_dev *dev)
msleep(delay);
}
bool pci_reset_supported(struct pci_dev *dev)
{
return dev->reset_methods[0] != 0;
}
#ifdef CONFIG_PCI_DOMAINS
int pci_domains_supported = 1;
#endif
@ -4621,32 +4627,12 @@ int pci_wait_for_pending_transaction(struct pci_dev *dev)
}
EXPORT_SYMBOL(pci_wait_for_pending_transaction);
/**
* pcie_has_flr - check if a device supports function level resets
* @dev: device to check
*
* Returns true if the device advertises support for PCIe function level
* resets.
*/
bool pcie_has_flr(struct pci_dev *dev)
{
u32 cap;
if (dev->dev_flags & PCI_DEV_FLAGS_NO_FLR_RESET)
return false;
pcie_capability_read_dword(dev, PCI_EXP_DEVCAP, &cap);
return cap & PCI_EXP_DEVCAP_FLR;
}
EXPORT_SYMBOL_GPL(pcie_has_flr);
/**
* pcie_flr - initiate a PCIe function level reset
* @dev: device to reset
*
* Initiate a function level reset on @dev. The caller should ensure the
* device supports FLR before calling this function, e.g. by using the
* pcie_has_flr() helper.
* Initiate a function level reset unconditionally on @dev without
* checking any flags and DEVCAP
*/
int pcie_flr(struct pci_dev *dev)
{
@ -4669,7 +4655,29 @@ int pcie_flr(struct pci_dev *dev)
}
EXPORT_SYMBOL_GPL(pcie_flr);
static int pci_af_flr(struct pci_dev *dev, int probe)
/**
* pcie_reset_flr - initiate a PCIe function level reset
* @dev: device to reset
* @probe: if true, return 0 if device can be reset this way
*
* Initiate a function level reset on @dev.
*/
int pcie_reset_flr(struct pci_dev *dev, bool probe)
{
if (dev->dev_flags & PCI_DEV_FLAGS_NO_FLR_RESET)
return -ENOTTY;
if (!(dev->devcap & PCI_EXP_DEVCAP_FLR))
return -ENOTTY;
if (probe)
return 0;
return pcie_flr(dev);
}
EXPORT_SYMBOL_GPL(pcie_reset_flr);
static int pci_af_flr(struct pci_dev *dev, bool probe)
{
int pos;
u8 cap;
@ -4716,7 +4724,7 @@ static int pci_af_flr(struct pci_dev *dev, int probe)
/**
* pci_pm_reset - Put device into PCI_D3 and back into PCI_D0.
* @dev: Device to reset.
* @probe: If set, only check if the device can be reset this way.
* @probe: if true, return 0 if the device can be reset this way.
*
* If @dev supports native PCI PM and its PCI_PM_CTRL_NO_SOFT_RESET flag is
* unset, it will be reinitialized internally when going from PCI_D3hot to
@ -4728,7 +4736,7 @@ static int pci_af_flr(struct pci_dev *dev, int probe)
* by default (i.e. unless the @dev's d3hot_delay field has a different value).
* Moreover, only devices in D0 can be reset by this function.
*/
static int pci_pm_reset(struct pci_dev *dev, int probe)
static int pci_pm_reset(struct pci_dev *dev, bool probe)
{
u16 csr;
@ -4988,7 +4996,7 @@ int pci_bridge_secondary_bus_reset(struct pci_dev *dev)
}
EXPORT_SYMBOL_GPL(pci_bridge_secondary_bus_reset);
static int pci_parent_bus_reset(struct pci_dev *dev, int probe)
static int pci_parent_bus_reset(struct pci_dev *dev, bool probe)
{
struct pci_dev *pdev;
@ -5006,7 +5014,7 @@ static int pci_parent_bus_reset(struct pci_dev *dev, int probe)
return pci_bridge_secondary_bus_reset(dev->bus->self);
}
static int pci_reset_hotplug_slot(struct hotplug_slot *hotplug, int probe)
static int pci_reset_hotplug_slot(struct hotplug_slot *hotplug, bool probe)
{
int rc = -ENOTTY;
@ -5021,7 +5029,7 @@ static int pci_reset_hotplug_slot(struct hotplug_slot *hotplug, int probe)
return rc;
}
static int pci_dev_reset_slot_function(struct pci_dev *dev, int probe)
static int pci_dev_reset_slot_function(struct pci_dev *dev, bool probe)
{
if (dev->multifunction || dev->subordinate || !dev->slot ||
dev->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET)
@ -5030,7 +5038,7 @@ static int pci_dev_reset_slot_function(struct pci_dev *dev, int probe)
return pci_reset_hotplug_slot(dev->slot->hotplug, probe);
}
static int pci_reset_bus_function(struct pci_dev *dev, int probe)
static int pci_reset_bus_function(struct pci_dev *dev, bool probe)
{
int rc;
@ -5114,6 +5122,139 @@ static void pci_dev_restore(struct pci_dev *dev)
err_handler->reset_done(dev);
}
/* dev->reset_methods[] is a 0-terminated list of indices into this array */
static const struct pci_reset_fn_method pci_reset_fn_methods[] = {
{ },
{ pci_dev_specific_reset, .name = "device_specific" },
{ pci_dev_acpi_reset, .name = "acpi" },
{ pcie_reset_flr, .name = "flr" },
{ pci_af_flr, .name = "af_flr" },
{ pci_pm_reset, .name = "pm" },
{ 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, PCI_RESET_PROBE)) {
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, PCI_RESET_PROBE) == 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.
@ -5136,66 +5277,64 @@ static void pci_dev_restore(struct pci_dev *dev)
*/
int __pci_reset_function_locked(struct pci_dev *dev)
{
int rc;
int i, m, rc = -ENOTTY;
might_sleep();
/*
* A reset method returns -ENOTTY if it doesn't support this device
* and we should try the next method.
* A reset method returns -ENOTTY if it doesn't support this device and
* we should try the next method.
*
* If it returns 0 (success), we're finished. If it returns any
* other error, we're also finished: this indicates that further
* reset mechanisms might be broken on the device.
* If it returns 0 (success), we're finished. If it returns any other
* error, we're also finished: this indicates that further reset
* mechanisms might be broken on the device.
*/
rc = pci_dev_specific_reset(dev, 0);
if (rc != -ENOTTY)
return rc;
if (pcie_has_flr(dev)) {
rc = pcie_flr(dev);
for (i = 0; i < PCI_NUM_RESET_METHODS; i++) {
m = dev->reset_methods[i];
if (!m)
return -ENOTTY;
rc = pci_reset_fn_methods[m].reset_fn(dev, PCI_RESET_DO_RESET);
if (!rc)
return 0;
if (rc != -ENOTTY)
return rc;
}
rc = pci_af_flr(dev, 0);
if (rc != -ENOTTY)
return rc;
rc = pci_pm_reset(dev, 0);
if (rc != -ENOTTY)
return rc;
return pci_reset_bus_function(dev, 0);
return -ENOTTY;
}
EXPORT_SYMBOL_GPL(__pci_reset_function_locked);
/**
* pci_probe_reset_function - check whether the device can be safely reset
* @dev: PCI device to reset
* pci_init_reset_methods - check whether device can be safely reset
* and store supported reset mechanisms.
* @dev: PCI device to check for reset mechanisms
*
* 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.
* other functions in the same device. The PCI device must be in D0-D3hot
* state.
*
* Returns 0 if the device function can be reset or negative if the
* device doesn't support resetting a single function.
* Stores reset mechanisms supported by device in reset_methods byte array
* which is a member of struct pci_dev.
*/
int pci_probe_reset_function(struct pci_dev *dev)
void pci_init_reset_methods(struct pci_dev *dev)
{
int rc;
int m, i, rc;
BUILD_BUG_ON(ARRAY_SIZE(pci_reset_fn_methods) != PCI_NUM_RESET_METHODS);
might_sleep();
rc = pci_dev_specific_reset(dev, 1);
if (rc != -ENOTTY)
return rc;
if (pcie_has_flr(dev))
return 0;
rc = pci_af_flr(dev, 1);
if (rc != -ENOTTY)
return rc;
rc = pci_pm_reset(dev, 1);
if (rc != -ENOTTY)
return rc;
i = 0;
for (m = 1; m < PCI_NUM_RESET_METHODS; m++) {
rc = pci_reset_fn_methods[m].reset_fn(dev, PCI_RESET_PROBE);
if (!rc)
dev->reset_methods[i++] = m;
else if (rc != -ENOTTY)
break;
}
return pci_reset_bus_function(dev, 1);
dev->reset_methods[i] = 0;
}
/**
@ -5218,7 +5357,7 @@ int pci_reset_function(struct pci_dev *dev)
{
int rc;
if (!dev->reset_fn)
if (!pci_reset_supported(dev))
return -ENOTTY;
pci_dev_lock(dev);
@ -5254,7 +5393,7 @@ int pci_reset_function_locked(struct pci_dev *dev)
{
int rc;
if (!dev->reset_fn)
if (!pci_reset_supported(dev))
return -ENOTTY;
pci_dev_save_and_disable(dev);
@ -5277,7 +5416,7 @@ int pci_try_reset_function(struct pci_dev *dev)
{
int rc;
if (!dev->reset_fn)
if (!pci_reset_supported(dev))
return -ENOTTY;
if (!pci_dev_trylock(dev))
@ -5505,7 +5644,7 @@ static void pci_slot_restore_locked(struct pci_slot *slot)
}
}
static int pci_slot_reset(struct pci_slot *slot, int probe)
static int pci_slot_reset(struct pci_slot *slot, bool probe)
{
int rc;
@ -5533,7 +5672,7 @@ static int pci_slot_reset(struct pci_slot *slot, int probe)
*/
int pci_probe_reset_slot(struct pci_slot *slot)
{
return pci_slot_reset(slot, 1);
return pci_slot_reset(slot, PCI_RESET_PROBE);
}
EXPORT_SYMBOL_GPL(pci_probe_reset_slot);
@ -5556,14 +5695,14 @@ static int __pci_reset_slot(struct pci_slot *slot)
{
int rc;
rc = pci_slot_reset(slot, 1);
rc = pci_slot_reset(slot, PCI_RESET_PROBE);
if (rc)
return rc;
if (pci_slot_trylock(slot)) {
pci_slot_save_and_disable_locked(slot);
might_sleep();
rc = pci_reset_hotplug_slot(slot->hotplug, 0);
rc = pci_reset_hotplug_slot(slot->hotplug, PCI_RESET_DO_RESET);
pci_slot_restore_locked(slot);
pci_slot_unlock(slot);
} else
@ -5572,7 +5711,7 @@ static int __pci_reset_slot(struct pci_slot *slot)
return rc;
}
static int pci_bus_reset(struct pci_bus *bus, int probe)
static int pci_bus_reset(struct pci_bus *bus, bool probe)
{
int ret;
@ -5618,14 +5757,14 @@ int pci_bus_error_reset(struct pci_dev *bridge)
goto bus_reset;
list_for_each_entry(slot, &bus->slots, list)
if (pci_slot_reset(slot, 0))
if (pci_slot_reset(slot, PCI_RESET_DO_RESET))
goto bus_reset;
mutex_unlock(&pci_slot_mutex);
return 0;
bus_reset:
mutex_unlock(&pci_slot_mutex);
return pci_bus_reset(bridge->subordinate, 0);
return pci_bus_reset(bridge->subordinate, PCI_RESET_DO_RESET);
}
/**
@ -5636,7 +5775,7 @@ int pci_bus_error_reset(struct pci_dev *bridge)
*/
int pci_probe_reset_bus(struct pci_bus *bus)
{
return pci_bus_reset(bus, 1);
return pci_bus_reset(bus, PCI_RESET_PROBE);
}
EXPORT_SYMBOL_GPL(pci_probe_reset_bus);
@ -5650,7 +5789,7 @@ static int __pci_reset_bus(struct pci_bus *bus)
{
int rc;
rc = pci_bus_reset(bus, 1);
rc = pci_bus_reset(bus, PCI_RESET_PROBE);
if (rc)
return rc;

View file

@ -33,7 +33,8 @@ enum pci_mmap_api {
int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vmai,
enum pci_mmap_api mmap_api);
int pci_probe_reset_function(struct pci_dev *dev);
bool pci_reset_supported(struct pci_dev *dev);
void pci_init_reset_methods(struct pci_dev *dev);
int pci_bridge_secondary_bus_reset(struct pci_dev *dev);
int pci_bus_error_reset(struct pci_dev *dev);
@ -607,13 +608,18 @@ static inline int pci_enable_ptm(struct pci_dev *dev, u8 *granularity)
struct pci_dev_reset_methods {
u16 vendor;
u16 device;
int (*reset)(struct pci_dev *dev, int probe);
int (*reset)(struct pci_dev *dev, bool probe);
};
struct pci_reset_fn_method {
int (*reset_fn)(struct pci_dev *pdev, bool probe);
char *name;
};
#ifdef CONFIG_PCI_QUIRKS
int pci_dev_specific_reset(struct pci_dev *dev, int probe);
int pci_dev_specific_reset(struct pci_dev *dev, bool probe);
#else
static inline int pci_dev_specific_reset(struct pci_dev *dev, int probe)
static inline int pci_dev_specific_reset(struct pci_dev *dev, bool probe)
{
return -ENOTTY;
}
@ -701,7 +707,15 @@ static inline int pci_aer_raw_clear_status(struct pci_dev *dev) { return -EINVAL
#ifdef CONFIG_ACPI
int pci_acpi_program_hp_params(struct pci_dev *dev);
extern const struct attribute_group pci_dev_acpi_attr_group;
void pci_set_acpi_fwnode(struct pci_dev *dev);
int pci_dev_acpi_reset(struct pci_dev *dev, bool probe);
#else
static inline int pci_dev_acpi_reset(struct pci_dev *dev, bool probe)
{
return -ENOTTY;
}
static inline void pci_set_acpi_fwnode(struct pci_dev *dev) {}
static inline int pci_acpi_program_hp_params(struct pci_dev *dev)
{
return -ENODEV;
@ -712,4 +726,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 */

View file

@ -1407,13 +1407,11 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
}
if (type == PCI_EXP_TYPE_RC_EC || type == PCI_EXP_TYPE_RC_END) {
if (pcie_has_flr(dev)) {
rc = pcie_flr(dev);
pci_info(dev, "has been reset (%d)\n", rc);
} else {
pci_info(dev, "not reset (no FLR support)\n");
rc = -ENOTTY;
}
rc = pcie_reset_flr(dev, PCI_RESET_DO_RESET);
if (!rc)
pci_info(dev, "has been reset\n");
else
pci_info(dev, "not reset (no FLR support: %d)\n", rc);
} else {
rc = pci_bus_error_reset(dev);
pci_info(dev, "%s Port link has been reset (%d)\n",

View file

@ -19,6 +19,7 @@
#include <linux/hypervisor.h>
#include <linux/irqdomain.h>
#include <linux/pm_runtime.h>
#include <linux/bitfield.h>
#include "pci.h"
#define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */
@ -1498,8 +1499,8 @@ void set_pcie_port_type(struct pci_dev *pdev)
pdev->pcie_cap = pos;
pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, &reg16);
pdev->pcie_flags_reg = reg16;
pci_read_config_word(pdev, pos + PCI_EXP_DEVCAP, &reg16);
pdev->pcie_mpss = reg16 & PCI_EXP_DEVCAP_PAYLOAD;
pci_read_config_dword(pdev, pos + PCI_EXP_DEVCAP, &pdev->devcap);
pdev->pcie_mpss = FIELD_GET(PCI_EXP_DEVCAP_PAYLOAD, pdev->devcap);
parent = pci_upstream_bridge(pdev);
if (!parent)
@ -1809,6 +1810,9 @@ int pci_setup_device(struct pci_dev *dev)
dev->error_state = pci_channel_io_normal;
set_pcie_port_type(dev);
pci_set_of_node(dev);
pci_set_acpi_fwnode(dev);
pci_dev_assign_slot(dev);
/*
@ -1946,6 +1950,7 @@ int pci_setup_device(struct pci_dev *dev)
default: /* unknown header */
pci_err(dev, "unknown header type %02x, ignoring device\n",
dev->hdr_type);
pci_release_of_node(dev);
return -EIO;
bad:
@ -2374,10 +2379,7 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
dev->vendor = l & 0xffff;
dev->device = (l >> 16) & 0xffff;
pci_set_of_node(dev);
if (pci_setup_device(dev)) {
pci_release_of_node(dev);
pci_bus_put(dev->bus);
kfree(dev);
return NULL;
@ -2428,9 +2430,7 @@ static void pci_init_capabilities(struct pci_dev *dev)
pci_rcec_init(dev); /* Root Complex Event Collector */
pcie_report_downtraining(dev);
if (pci_probe_reset_function(dev) == 0)
dev->reset_fn = 1;
pci_init_reset_methods(dev);
}
/*

View file

@ -3742,7 +3742,7 @@ DECLARE_PCI_FIXUP_SUSPEND_LATE(PCI_VENDOR_ID_INTEL,
* reset a single function if other methods (e.g. FLR, PM D0->D3) are
* not available.
*/
static int reset_intel_82599_sfp_virtfn(struct pci_dev *dev, int probe)
static int reset_intel_82599_sfp_virtfn(struct pci_dev *dev, bool probe)
{
/*
* http://www.intel.com/content/dam/doc/datasheet/82599-10-gbe-controller-datasheet.pdf
@ -3764,7 +3764,7 @@ static int reset_intel_82599_sfp_virtfn(struct pci_dev *dev, int probe)
#define NSDE_PWR_STATE 0xd0100
#define IGD_OPERATION_TIMEOUT 10000 /* set timeout 10 seconds */
static int reset_ivb_igd(struct pci_dev *dev, int probe)
static int reset_ivb_igd(struct pci_dev *dev, bool probe)
{
void __iomem *mmio_base;
unsigned long timeout;
@ -3807,7 +3807,7 @@ static int reset_ivb_igd(struct pci_dev *dev, int probe)
}
/* Device-specific reset method for Chelsio T4-based adapters */
static int reset_chelsio_generic_dev(struct pci_dev *dev, int probe)
static int reset_chelsio_generic_dev(struct pci_dev *dev, bool probe)
{
u16 old_command;
u16 msix_flags;
@ -3885,14 +3885,14 @@ static int reset_chelsio_generic_dev(struct pci_dev *dev, int probe)
* Chapter 3: NVMe control registers
* Chapter 7.3: Reset behavior
*/
static int nvme_disable_and_flr(struct pci_dev *dev, int probe)
static int nvme_disable_and_flr(struct pci_dev *dev, bool probe)
{
void __iomem *bar;
u16 cmd;
u32 cfg;
if (dev->class != PCI_CLASS_STORAGE_EXPRESS ||
!pcie_has_flr(dev) || !pci_resource_start(dev, 0))
pcie_reset_flr(dev, PCI_RESET_PROBE) || !pci_resource_start(dev, 0))
return -ENOTTY;
if (probe)
@ -3959,15 +3959,12 @@ static int nvme_disable_and_flr(struct pci_dev *dev, int probe)
* device too soon after FLR. A 250ms delay after FLR has heuristically
* proven to produce reliably working results for device assignment cases.
*/
static int delay_250ms_after_flr(struct pci_dev *dev, int probe)
static int delay_250ms_after_flr(struct pci_dev *dev, bool probe)
{
if (!pcie_has_flr(dev))
return -ENOTTY;
if (probe)
return 0;
return pcie_reset_flr(dev, PCI_RESET_PROBE);
pcie_flr(dev);
pcie_reset_flr(dev, PCI_RESET_DO_RESET);
msleep(250);
@ -3982,7 +3979,7 @@ static int delay_250ms_after_flr(struct pci_dev *dev, int probe)
#define HINIC_OPERATION_TIMEOUT 15000 /* 15 seconds */
/* Device-specific reset method for Huawei Intelligent NIC virtual functions */
static int reset_hinic_vf_dev(struct pci_dev *pdev, int probe)
static int reset_hinic_vf_dev(struct pci_dev *pdev, bool probe)
{
unsigned long timeout;
void __iomem *bar;
@ -4059,7 +4056,7 @@ static const struct pci_dev_reset_methods pci_dev_reset_methods[] = {
* because when a host assigns a device to a guest VM, the host may need
* to reset the device but probably doesn't have a driver for it.
*/
int pci_dev_specific_reset(struct pci_dev *dev, int probe)
int pci_dev_specific_reset(struct pci_dev *dev, bool probe)
{
const struct pci_dev_reset_methods *i;
@ -5669,7 +5666,7 @@ static void quirk_reset_lenovo_thinkpad_p50_nvgpu(struct pci_dev *pdev)
if (pdev->subsystem_vendor != PCI_VENDOR_ID_LENOVO ||
pdev->subsystem_device != 0x222e ||
!pdev->reset_fn)
!pci_reset_supported(pdev))
return;
if (pci_enable_device_mem(pdev))

View file

@ -19,7 +19,6 @@ static void pci_stop_dev(struct pci_dev *dev)
pci_pme_active(dev, false);
if (pci_dev_is_added(dev)) {
dev->reset_fn = 0;
device_release_driver(&dev->dev);
pci_proc_detach_device(dev);

View file

@ -49,6 +49,12 @@
PCI_STATUS_SIG_TARGET_ABORT | \
PCI_STATUS_PARITY)
/* Number of reset methods used in pci_reset_fn_methods array in pci.c */
#define PCI_NUM_RESET_METHODS 7
#define PCI_RESET_PROBE true
#define PCI_RESET_DO_RESET false
/*
* The PCI interface treats multi-function devices as independent
* devices. The slot/function address of each device is encoded
@ -333,6 +339,7 @@ struct pci_dev {
struct rcec_ea *rcec_ea; /* RCEC cached endpoint association */
struct pci_dev *rcec; /* Associated RCEC device */
#endif
u32 devcap; /* PCIe Device Capabilities */
u8 pcie_cap; /* PCIe capability offset */
u8 msi_cap; /* MSI capability offset */
u8 msix_cap; /* MSI-X capability offset */
@ -428,7 +435,6 @@ struct pci_dev {
unsigned int state_saved:1;
unsigned int is_physfn:1;
unsigned int is_virtfn:1;
unsigned int reset_fn:1;
unsigned int is_hotplug_bridge:1;
unsigned int shpc_managed:1; /* SHPC owned by shpchp */
unsigned int is_thunderbolt:1; /* Thunderbolt controller */
@ -506,6 +512,9 @@ struct pci_dev {
char *driver_override; /* Driver name to force a match */
unsigned long priv_flags; /* Private flags for the PCI driver */
/* These methods index pci_reset_fn_methods[] */
u8 reset_methods[PCI_NUM_RESET_METHODS]; /* In priority order */
};
static inline struct pci_dev *pci_physfn(struct pci_dev *dev)
@ -1229,7 +1238,7 @@ u32 pcie_bandwidth_available(struct pci_dev *dev, struct pci_dev **limiting_dev,
enum pci_bus_speed *speed,
enum pcie_link_width *width);
void pcie_print_link_status(struct pci_dev *dev);
bool pcie_has_flr(struct pci_dev *dev);
int pcie_reset_flr(struct pci_dev *dev, bool probe);
int pcie_flr(struct pci_dev *dev);
int __pci_reset_function_locked(struct pci_dev *dev);
int pci_reset_function(struct pci_dev *dev);

View file

@ -44,7 +44,7 @@ struct hotplug_slot_ops {
int (*get_attention_status) (struct hotplug_slot *slot, u8 *value);
int (*get_latch_status) (struct hotplug_slot *slot, u8 *value);
int (*get_adapter_status) (struct hotplug_slot *slot, u8 *value);
int (*reset_slot) (struct hotplug_slot *slot, int probe);
int (*reset_slot) (struct hotplug_slot *slot, bool probe);
};
/**