Skip to content

Commit

Permalink
PCI: Add flag for devices where we can't use bus reset
Browse files Browse the repository at this point in the history
Enable a mechanism for devices to quirk that they do not behave when
doing a PCI bus reset.  We require a modest level of spec compliant
behavior in order to do a reset, for instance the device should come
out of reset without throwing errors and PCI config space should be
accessible after reset.  This is too much to ask for some devices.

Link: http://lkml.kernel.org/r/20140923210318.498dacbd@dualc.maya.org
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
CC: stable@vger.kernel.org	# v3.14+
  • Loading branch information
Alex Williamson authored and Bjorn Helgaas committed Jan 16, 2015
1 parent 97bf6af commit f331a85
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 4 deletions.
40 changes: 36 additions & 4 deletions drivers/pci/pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -3271,7 +3271,8 @@ static int pci_parent_bus_reset(struct pci_dev *dev, int probe)
{
struct pci_dev *pdev;

if (pci_is_root_bus(dev->bus) || dev->subordinate || !dev->bus->self)
if (pci_is_root_bus(dev->bus) || dev->subordinate ||
!dev->bus->self || dev->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET)
return -ENOTTY;

list_for_each_entry(pdev, &dev->bus->devices, bus_list)
Expand Down Expand Up @@ -3305,7 +3306,8 @@ static int pci_dev_reset_slot_function(struct pci_dev *dev, int probe)
{
struct pci_dev *pdev;

if (dev->subordinate || !dev->slot)
if (dev->subordinate || !dev->slot ||
dev->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET)
return -ENOTTY;

list_for_each_entry(pdev, &dev->bus->devices, bus_list)
Expand Down Expand Up @@ -3557,6 +3559,20 @@ int pci_try_reset_function(struct pci_dev *dev)
}
EXPORT_SYMBOL_GPL(pci_try_reset_function);

/* Do any devices on or below this bus prevent a bus reset? */
static bool pci_bus_resetable(struct pci_bus *bus)
{
struct pci_dev *dev;

list_for_each_entry(dev, &bus->devices, bus_list) {
if (dev->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET ||
(dev->subordinate && !pci_bus_resetable(dev->subordinate)))
return false;
}

return true;
}

/* Lock devices from the top of the tree down */
static void pci_bus_lock(struct pci_bus *bus)
{
Expand Down Expand Up @@ -3607,6 +3623,22 @@ static int pci_bus_trylock(struct pci_bus *bus)
return 0;
}

/* Do any devices on or below this slot prevent a bus reset? */
static bool pci_slot_resetable(struct pci_slot *slot)
{
struct pci_dev *dev;

list_for_each_entry(dev, &slot->bus->devices, bus_list) {
if (!dev->slot || dev->slot != slot)
continue;
if (dev->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET ||
(dev->subordinate && !pci_bus_resetable(dev->subordinate)))
return false;
}

return true;
}

/* Lock devices from the top of the tree down */
static void pci_slot_lock(struct pci_slot *slot)
{
Expand Down Expand Up @@ -3728,7 +3760,7 @@ static int pci_slot_reset(struct pci_slot *slot, int probe)
{
int rc;

if (!slot)
if (!slot || !pci_slot_resetable(slot))
return -ENOTTY;

if (!probe)
Expand Down Expand Up @@ -3820,7 +3852,7 @@ EXPORT_SYMBOL_GPL(pci_try_reset_slot);

static int pci_bus_reset(struct pci_bus *bus, int probe)
{
if (!bus->self)
if (!bus->self || !pci_bus_resetable(bus))
return -ENOTTY;

if (probe)
Expand Down
2 changes: 2 additions & 0 deletions include/linux/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ enum pci_dev_flags {
PCI_DEV_FLAGS_DMA_ALIAS_DEVFN = (__force pci_dev_flags_t) (1 << 4),
/* Use a PCIe-to-PCI bridge alias even if !pci_is_pcie */
PCI_DEV_FLAG_PCIE_BRIDGE_ALIAS = (__force pci_dev_flags_t) (1 << 5),
/* Do not use bus resets for device */
PCI_DEV_FLAGS_NO_BUS_RESET = (__force pci_dev_flags_t) (1 << 6),
};

enum pci_irq_reroute_variant {
Expand Down

0 comments on commit f331a85

Please sign in to comment.