mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [RFC PATCH 0/8] pci: extended reset capabilities
@ 2013-05-01 18:42 Alex Williamson
  2013-05-01 18:43 ` [RFC PATCH 1/8] pci: Create pci_reset_bridge_secondary_bus() Alex Williamson
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: Alex Williamson @ 2013-05-01 18:42 UTC (permalink / raw)
  To: bhelgaas; +Cc: linux-kernel

PCI currently provides pci_reset_function() which will try several
reset mechanisms including PCIe FLR, AF FLR, PM reset, and only if
none of those are available and the device is alone on the bus, a
secondary bus reset.  This is rather limiting because many devices
don't support FLR and don't have useful PM resets.  A driver, like
vfio, that may control all of the devices on a bus needs an interface
to reset a bus even if it has multiple devices.

This series adds pci_reset_slot() and pci_reset_bus() to allow that.
pci_reset_slot() also corrects a problem in the secondary bus reset
path where a bridge supporting pciehp with surprise removal may
detect a device presence change during secondary bus reset and
attempt to remove and re-add the device.  This series allows a driver
to determine which reset mechanisms are available for a device and
upgrade to a slot or bus reset depending on what the platform drivers
support.

I still need to integrate this all the way through to vfio, but I'd
like some feedback on the PCI changes before investing too much in
that.  I'm finding with attempting to support graphics devices
exposed through vfio that they are typically multifunction devices
(func0 = gfx, func1 = audio) where both devices are grouped in the
same iommu group, thus vfio controls both of them.  They don't
support any kind of FLR and a PM reset does nothing.  Also, host
drivers or even userspace drivers, leave the device in an unusable
state, so many users are having problems.  I think these interfaces
should significantly improve our repeatability for such devices.
Thanks,

Alex

---

Alex Williamson (8):
      pci: Create pci_reset_bridge_secondary_bus()
      pci: Add hotplug_slot_ops.reset_slot()
      pci: Add pci_hp_reset_slot
      pci: Implement reset_slot for pciehp
      pci: Add reset_slot option to pci_dev_reset
      pci: Split out pci_dev lock/unlock and save/restore
      pci: Add slot and bus reset interfaces
      pci: Add reset probe functions


 drivers/pci/hotplug/pci_hotplug_core.c |   24 ++
 drivers/pci/hotplug/pciehp.h           |    1 
 drivers/pci/hotplug/pciehp_core.c      |   12 +
 drivers/pci/hotplug/pciehp_hpc.c       |   31 +++
 drivers/pci/pci.c                      |  341 ++++++++++++++++++++++++++++++--
 include/linux/pci.h                    |    6 +
 include/linux/pci_hotplug.h            |   12 +
 7 files changed, 401 insertions(+), 26 deletions(-)

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [RFC PATCH 1/8] pci: Create pci_reset_bridge_secondary_bus()
  2013-05-01 18:42 [RFC PATCH 0/8] pci: extended reset capabilities Alex Williamson
@ 2013-05-01 18:43 ` Alex Williamson
  2013-05-01 18:43 ` [RFC PATCH 2/8] pci: Add hotplug_slot_ops.reset_slot() Alex Williamson
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Alex Williamson @ 2013-05-01 18:43 UTC (permalink / raw)
  To: bhelgaas; +Cc: linux-kernel

Move the secondary bus reset code from pci_parent_bus_reset() into its own
function.  Export it as we'll later be calling it from hotplug controllers.

Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
---
 drivers/pci/pci.c   |   32 +++++++++++++++++++++++---------
 include/linux/pci.h |    1 +
 2 files changed, 24 insertions(+), 9 deletions(-)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index a899d8b..7250ca7 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -3205,9 +3205,30 @@ static int pci_pm_reset(struct pci_dev *dev, int probe)
 	return 0;
 }
 
-static int pci_parent_bus_reset(struct pci_dev *dev, int probe)
+/**
+ * pci_reset_bridge_secondary_bus - Reset the secondary bus on a PCI bridge.
+ * @dev: Bridge device
+ *
+ * Use the bridge control register to assert reset on the secondary bus.
+ * Devices on the secondary bus are left in power-on state.
+ */
+void pci_reset_bridge_secondary_bus(struct pci_dev *dev)
 {
 	u16 ctrl;
+
+	pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &ctrl);
+	ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
+	pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl);
+	msleep(100);
+
+	ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
+	pci_write_config_word(dev, PCI_BRIDGE_CONTROL, ctrl);
+	msleep(100);
+}
+EXPORT_SYMBOL_GPL(pci_reset_bridge_secondary_bus);
+
+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)
@@ -3220,14 +3241,7 @@ static int pci_parent_bus_reset(struct pci_dev *dev, int probe)
 	if (probe)
 		return 0;
 
-	pci_read_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, &ctrl);
-	ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
-	pci_write_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, ctrl);
-	msleep(100);
-
-	ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
-	pci_write_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, ctrl);
-	msleep(100);
+	pci_reset_bridge_secondary_bus(dev->bus->self);
 
 	return 0;
 }
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 8aaca26..fc85d68 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -922,6 +922,7 @@ int pcie_set_mps(struct pci_dev *dev, int mps);
 int __pci_reset_function(struct pci_dev *dev);
 int __pci_reset_function_locked(struct pci_dev *dev);
 int pci_reset_function(struct pci_dev *dev);
+void pci_reset_bridge_secondary_bus(struct pci_dev *dev);
 void pci_update_resource(struct pci_dev *dev, int resno);
 int __must_check pci_assign_resource(struct pci_dev *dev, int i);
 int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align);


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [RFC PATCH 2/8] pci: Add hotplug_slot_ops.reset_slot()
  2013-05-01 18:42 [RFC PATCH 0/8] pci: extended reset capabilities Alex Williamson
  2013-05-01 18:43 ` [RFC PATCH 1/8] pci: Create pci_reset_bridge_secondary_bus() Alex Williamson
@ 2013-05-01 18:43 ` Alex Williamson
  2013-05-01 18:43 ` [RFC PATCH 3/8] pci: Add pci_hp_reset_slot Alex Williamson
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Alex Williamson @ 2013-05-01 18:43 UTC (permalink / raw)
  To: bhelgaas; +Cc: linux-kernel

This optional callback allows htoplug controllers to perform slot
specific resets.  These may be necessary in cases where a normal
secondary bus reset can interact with controller logic and expose
spurious hotplugs.

Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
---
 include/linux/pci_hotplug.h |    4 ++++
 1 file changed, 4 insertions(+)

diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h
index 8db71dc..bd32109 100644
--- a/include/linux/pci_hotplug.h
+++ b/include/linux/pci_hotplug.h
@@ -63,6 +63,9 @@ enum pcie_link_width {
  * @get_adapter_status: Called to get see if an adapter is present in the slot or not.
  *	If this field is NULL, the value passed in the struct hotplug_slot_info
  *	will be used when this value is requested by a user.
+ * @reset_slot: Optional interface to allow override of a bus reset for the
+ *	slot for cases where a secondary bus reset can result in spurious
+ *	hotplug events or where a slot can be reset independent of the bus.
  *
  * The table of function pointers that is passed to the hotplug pci core by a
  * hotplug pci driver.  These functions are called by the hotplug pci core when
@@ -80,6 +83,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);
 };
 
 /**


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [RFC PATCH 3/8] pci: Add pci_hp_reset_slot
  2013-05-01 18:42 [RFC PATCH 0/8] pci: extended reset capabilities Alex Williamson
  2013-05-01 18:43 ` [RFC PATCH 1/8] pci: Create pci_reset_bridge_secondary_bus() Alex Williamson
  2013-05-01 18:43 ` [RFC PATCH 2/8] pci: Add hotplug_slot_ops.reset_slot() Alex Williamson
@ 2013-05-01 18:43 ` Alex Williamson
  2013-05-01 18:43 ` [RFC PATCH 4/8] pci: Implement reset_slot for pciehp Alex Williamson
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Alex Williamson @ 2013-05-01 18:43 UTC (permalink / raw)
  To: bhelgaas; +Cc: linux-kernel

Provide an interface to get to hotplug controller reset_slot callback

Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
---
 drivers/pci/hotplug/pci_hotplug_core.c |   24 ++++++++++++++++++++++++
 include/linux/pci_hotplug.h            |    8 ++++++++
 2 files changed, 32 insertions(+)

diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c
index ec20f74..a63e9c3 100644
--- a/drivers/pci/hotplug/pci_hotplug_core.c
+++ b/drivers/pci/hotplug/pci_hotplug_core.c
@@ -528,6 +528,29 @@ int pci_hp_change_slot_info(struct hotplug_slot *hotplug,
 	return 0;
 }
 
+/**
+ * pci_hp_reset_slot - reset slot
+ *
+ * @hotplug: pointer to hotplug slot to reset
+ * @probe: reset slot (0) or just probe
+ *
+ * Returns 0 if successful, anything else for an error.
+ */
+int pci_hp_reset_slot(struct hotplug_slot *hotplug, int probe)
+{
+	int result = -ENOTTY;
+
+	if (!hotplug || !try_module_get(hotplug->ops->owner))
+		return result;
+
+	if (hotplug->ops->reset_slot)
+		result = hotplug->ops->reset_slot(hotplug, probe);
+
+	module_put(hotplug->ops->owner);
+
+	return result;
+}
+
 static int __init pci_hotplug_init (void)
 {
 	int result;
@@ -561,3 +584,4 @@ MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
 EXPORT_SYMBOL_GPL(__pci_hp_register);
 EXPORT_SYMBOL_GPL(pci_hp_deregister);
 EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);
+EXPORT_SYMBOL_GPL(pci_hp_reset_slot);
diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h
index bd32109..e59f797 100644
--- a/include/linux/pci_hotplug.h
+++ b/include/linux/pci_hotplug.h
@@ -135,6 +135,14 @@ int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *pbus, int nr,
 int pci_hp_deregister(struct hotplug_slot *slot);
 int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot,
 					 struct hotplug_slot_info *info);
+#ifdef CONFIG_HOTPLUG_PCI
+int pci_hp_reset_slot(struct hotplug_slot *slot, int probe);
+#else
+static inline int pci_hp_reset_slot(struct hotplug_slot *slot, int probe)
+{
+	return -ENOTTY;
+}
+#endif
 
 /* use a define to avoid include chaining to get THIS_MODULE & friends */
 #define pci_hp_register(slot, pbus, devnr, name) \


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [RFC PATCH 4/8] pci: Implement reset_slot for pciehp
  2013-05-01 18:42 [RFC PATCH 0/8] pci: extended reset capabilities Alex Williamson
                   ` (2 preceding siblings ...)
  2013-05-01 18:43 ` [RFC PATCH 3/8] pci: Add pci_hp_reset_slot Alex Williamson
@ 2013-05-01 18:43 ` Alex Williamson
  2013-05-01 18:43 ` [RFC PATCH 5/8] pci: Add reset_slot option to pci_dev_reset Alex Williamson
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Alex Williamson @ 2013-05-01 18:43 UTC (permalink / raw)
  To: bhelgaas; +Cc: linux-kernel

Disable notification around secondary bus reset.

Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
---
 drivers/pci/hotplug/pciehp.h      |    1 +
 drivers/pci/hotplug/pciehp_core.c |   12 ++++++++++++
 drivers/pci/hotplug/pciehp_hpc.c  |   31 +++++++++++++++++++++++++++++++
 3 files changed, 44 insertions(+)

diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h
index 7fb3269..541bbe6 100644
--- a/drivers/pci/hotplug/pciehp.h
+++ b/drivers/pci/hotplug/pciehp.h
@@ -155,6 +155,7 @@ void pciehp_green_led_off(struct slot *slot);
 void pciehp_green_led_blink(struct slot *slot);
 int pciehp_check_link_status(struct controller *ctrl);
 void pciehp_release_ctrl(struct controller *ctrl);
+int pciehp_reset_slot(struct slot *slot, int probe);
 
 static inline const char *slot_name(struct slot *slot)
 {
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index 7d72c5e..f4a18f5 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -69,6 +69,7 @@ static int get_power_status	(struct hotplug_slot *slot, u8 *value);
 static int get_attention_status	(struct hotplug_slot *slot, u8 *value);
 static int get_latch_status	(struct hotplug_slot *slot, u8 *value);
 static int get_adapter_status	(struct hotplug_slot *slot, u8 *value);
+static int reset_slot		(struct hotplug_slot *slot, int probe);
 
 /**
  * release_slot - free up the memory used by a slot
@@ -111,6 +112,7 @@ static int init_slot(struct controller *ctrl)
 	ops->disable_slot = disable_slot;
 	ops->get_power_status = get_power_status;
 	ops->get_adapter_status = get_adapter_status;
+	ops->reset_slot = reset_slot;
 	if (MRL_SENS(ctrl))
 		ops->get_latch_status = get_latch_status;
 	if (ATTN_LED(ctrl)) {
@@ -223,6 +225,16 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
 	return pciehp_get_adapter_status(slot, value);
 }
 
+static int reset_slot(struct hotplug_slot *hotplug_slot, int probe)
+{
+	struct slot *slot = hotplug_slot->private;
+
+	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
+		 __func__, slot_name(slot));
+
+	return pciehp_reset_slot(slot, probe);
+}
+
 static int pciehp_probe(struct pcie_device *dev)
 {
 	int rc;
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index 5127f3f..b01ed53 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -749,6 +749,37 @@ static void pcie_disable_notification(struct controller *ctrl)
 		ctrl_warn(ctrl, "Cannot disable software notification\n");
 }
 
+/*
+ * pciehp has a 1:1 bus:slot relationship so we ultimately want a secondary
+ * bus reset of the bridge, but if the slot supports surprise removal we need
+ * to disable presence detection around the bus reset and clear any spurious
+ * events after.
+ */
+int pciehp_reset_slot(struct slot *slot, int probe)
+{
+	struct controller *ctrl = slot->ctrl;
+
+	if (probe)
+		return 0;
+
+	if (HP_SUPR_RM(ctrl)) {
+		pcie_write_cmd(ctrl, 0, PCI_EXP_SLTCTL_PDCE);
+		if (pciehp_poll_mode)
+			del_timer_sync(&ctrl->poll_timer);
+	}
+
+	pci_reset_bridge_secondary_bus(ctrl->pcie->port);
+
+	if (HP_SUPR_RM(ctrl)) {
+		pciehp_writew(ctrl, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDC);
+		pcie_write_cmd(ctrl, PCI_EXP_SLTCTL_PDCE, PCI_EXP_SLTCTL_PDCE);
+		if (pciehp_poll_mode)
+			int_poll_timeout(ctrl->poll_timer.data);
+	}
+
+	return 0;
+}
+
 int pcie_init_notification(struct controller *ctrl)
 {
 	if (pciehp_request_irq(ctrl))


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [RFC PATCH 5/8] pci: Add reset_slot option to pci_dev_reset
  2013-05-01 18:42 [RFC PATCH 0/8] pci: extended reset capabilities Alex Williamson
                   ` (3 preceding siblings ...)
  2013-05-01 18:43 ` [RFC PATCH 4/8] pci: Implement reset_slot for pciehp Alex Williamson
@ 2013-05-01 18:43 ` Alex Williamson
  2013-05-01 18:43 ` [RFC PATCH 6/8] pci: Split out pci_dev lock/unlock and save/restore Alex Williamson
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Alex Williamson @ 2013-05-01 18:43 UTC (permalink / raw)
  To: bhelgaas; +Cc: linux-kernel

If the hotplug controller provides a way to reset a slot, use that
before a direct parent bus reset.

Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
---
 drivers/pci/pci.c |   19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 7250ca7..eb50d90 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -22,6 +22,7 @@
 #include <linux/interrupt.h>
 #include <linux/device.h>
 #include <linux/pm_runtime.h>
+#include <linux/pci_hotplug.h>
 #include <asm-generic/pci-bridge.h>
 #include <asm/setup.h>
 #include "pci.h"
@@ -3246,6 +3247,20 @@ static int pci_parent_bus_reset(struct pci_dev *dev, int probe)
 	return 0;
 }
 
+static int pci_dev_reset_slot_function(struct pci_dev *dev, int probe)
+{
+	struct pci_dev *pdev;
+
+	if (dev->subordinate || !dev->slot)
+		return -ENOTTY;
+
+	list_for_each_entry(pdev, &dev->bus->devices, bus_list)
+		if (pdev != dev && pdev->slot == dev->slot)
+			return -ENOTTY;
+
+	return pci_hp_reset_slot(dev->slot->hotplug, probe);
+}
+
 static int __pci_dev_reset(struct pci_dev *dev, int probe)
 {
 	int rc;
@@ -3268,6 +3283,10 @@ static int __pci_dev_reset(struct pci_dev *dev, int probe)
 	if (rc != -ENOTTY)
 		goto done;
 
+	rc = pci_dev_reset_slot_function(dev, probe);
+	if (rc != -ENOTTY)
+		goto done;
+
 	rc = pci_parent_bus_reset(dev, probe);
 done:
 	return rc;


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [RFC PATCH 6/8] pci: Split out pci_dev lock/unlock and save/restore
  2013-05-01 18:42 [RFC PATCH 0/8] pci: extended reset capabilities Alex Williamson
                   ` (4 preceding siblings ...)
  2013-05-01 18:43 ` [RFC PATCH 5/8] pci: Add reset_slot option to pci_dev_reset Alex Williamson
@ 2013-05-01 18:43 ` Alex Williamson
  2013-05-01 18:43 ` [RFC PATCH 7/8] pci: Add slot and bus reset interfaces Alex Williamson
  2013-05-01 18:43 ` [RFC PATCH 8/8] pci: Add reset probe functions Alex Williamson
  7 siblings, 0 replies; 9+ messages in thread
From: Alex Williamson @ 2013-05-01 18:43 UTC (permalink / raw)
  To: bhelgaas; +Cc: linux-kernel

Only cosmetic changes to existing paths.

Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
---
 drivers/pci/pci.c |   52 +++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 35 insertions(+), 17 deletions(-)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index eb50d90..a330ebb 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -3292,22 +3292,46 @@ done:
 	return rc;
 }
 
+static void pci_dev_lock(struct pci_dev *dev)
+{
+	pci_cfg_access_lock(dev);
+	/* block PM suspend, driver probe, etc. */
+	device_lock(&dev->dev);
+}
+
+static void pci_dev_unlock(struct pci_dev *dev)
+{
+	device_unlock(&dev->dev);
+	pci_cfg_access_unlock(dev);
+}
+
+static void pci_dev_save(struct pci_dev *dev)
+{
+	pci_save_state(dev);
+	/*
+	 * both INTx and MSI are disabled after the Interrupt Disable bit
+	 * is set and the Bus Master bit is cleared.
+	 */
+	pci_write_config_word(dev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE);
+}
+
+static void pci_dev_restore(struct pci_dev *dev)
+{
+	pci_restore_state(dev);
+}
+
 static int pci_dev_reset(struct pci_dev *dev, int probe)
 {
 	int rc;
 
-	if (!probe) {
-		pci_cfg_access_lock(dev);
-		/* block PM suspend, driver probe, etc. */
-		device_lock(&dev->dev);
-	}
+	if (!probe)
+		pci_dev_lock(dev);
 
 	rc = __pci_dev_reset(dev, probe);
 
-	if (!probe) {
-		device_unlock(&dev->dev);
-		pci_cfg_access_unlock(dev);
-	}
+	if (!probe)
+		pci_dev_unlock(dev);
+
 	return rc;
 }
 /**
@@ -3398,17 +3422,11 @@ int pci_reset_function(struct pci_dev *dev)
 	if (rc)
 		return rc;
 
-	pci_save_state(dev);
-
-	/*
-	 * both INTx and MSI are disabled after the Interrupt Disable bit
-	 * is set and the Bus Master bit is cleared.
-	 */
-	pci_write_config_word(dev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE);
+	pci_dev_save(dev);
 
 	rc = pci_dev_reset(dev, 0);
 
-	pci_restore_state(dev);
+	pci_dev_restore(dev);
 
 	return rc;
 }


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [RFC PATCH 7/8] pci: Add slot and bus reset interfaces
  2013-05-01 18:42 [RFC PATCH 0/8] pci: extended reset capabilities Alex Williamson
                   ` (5 preceding siblings ...)
  2013-05-01 18:43 ` [RFC PATCH 6/8] pci: Split out pci_dev lock/unlock and save/restore Alex Williamson
@ 2013-05-01 18:43 ` Alex Williamson
  2013-05-01 18:43 ` [RFC PATCH 8/8] pci: Add reset probe functions Alex Williamson
  7 siblings, 0 replies; 9+ messages in thread
From: Alex Williamson @ 2013-05-01 18:43 UTC (permalink / raw)
  To: bhelgaas; +Cc: linux-kernel

Sometimes pci_reset_function is not sufficient.  We have cases where
devices do not support any kind of reset, but there might be multiple
functions on the bus, preventing pci_reset_function from doing a
secondary bus reset.  We also have cases where a device will advertise
that it supports a PM reset, but really does nothing on D3hot->D0
(graphics cards are notorious for this).  These devices often also
have more than one function, so even blacklisting PM reset for them
wouldn't allow a secondary bus reset through pci_reset_function.

If a driver supports multiple devices it should have the ability to
induce a bus reset when it needs to.  This patch provides that ability
through pci_reset_slot and pci_reset_bus.  It's the caller's
responsibility when using these interfaces to understand that all of
the devices in or below the slot (or on or below the bus) will be
reset and therefore should be under control of the caller.  PCI state
of all the affected devices is saved and restored around these resets,
but internal state of all of the affected devices is reset (which
should be the intention).

Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
---
 drivers/pci/pci.c   |  199 +++++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pci.h |    2 +
 2 files changed, 201 insertions(+)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index a330ebb..6d17807 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -3432,6 +3432,205 @@ int pci_reset_function(struct pci_dev *dev)
 }
 EXPORT_SYMBOL_GPL(pci_reset_function);
 
+static void pci_bus_lock(struct pci_bus *bus)
+{
+	struct pci_dev *dev;
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+		pci_dev_lock(dev);
+		if (dev->subordinate)
+			pci_bus_lock(dev->subordinate);
+	}
+}
+
+static void pci_bus_unlock(struct pci_bus *bus)
+{
+	struct pci_dev *dev;
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+		pci_dev_unlock(dev);
+		if (dev->subordinate)
+			pci_bus_unlock(dev->subordinate);
+	}
+}
+
+static void pci_slot_lock(struct pci_slot *slot)
+{
+	struct pci_dev *dev;
+
+	BUG_ON(!slot);
+
+	list_for_each_entry(dev, &slot->bus->devices, bus_list) {
+		if (dev->slot && dev->slot == slot)
+			pci_dev_lock(dev);
+		if (dev->subordinate)
+			pci_bus_lock(dev->subordinate);
+	}
+}
+
+static void pci_slot_unlock(struct pci_slot *slot)
+{
+	struct pci_dev *dev;
+
+	BUG_ON(!slot);
+
+	list_for_each_entry(dev, &slot->bus->devices, bus_list) {
+		if (dev->slot && dev->slot == slot)
+			pci_dev_unlock(dev);
+		if (dev->subordinate)
+			pci_bus_unlock(dev->subordinate);
+	}
+}
+
+static void pci_bus_save(struct pci_bus *bus)
+{
+	struct pci_dev *dev;
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+		pci_dev_save(dev);
+		if (dev->subordinate)
+			pci_bus_lock(dev->subordinate);
+	}
+}
+
+static void pci_bus_restore(struct pci_bus *bus)
+{
+	struct pci_dev *dev;
+
+	list_for_each_entry(dev, &bus->devices, bus_list) {
+		pci_dev_restore(dev);
+		if (dev->subordinate)
+			pci_bus_unlock(dev->subordinate);
+	}
+}
+
+static void pci_slot_save(struct pci_slot *slot)
+{
+	struct pci_dev *dev;
+
+	BUG_ON(!slot);
+
+	list_for_each_entry(dev, &slot->bus->devices, bus_list) {
+		if (dev->slot && dev->slot == slot)
+			pci_dev_save(dev);
+		if (dev->subordinate)
+			pci_bus_save(dev->subordinate);
+	}
+}
+
+static void pci_slot_restore(struct pci_slot *slot)
+{
+	struct pci_dev *dev;
+
+	BUG_ON(!slot);
+
+	list_for_each_entry(dev, &slot->bus->devices, bus_list) {
+		if (dev->slot && dev->slot == slot)
+			pci_dev_restore(dev);
+		if (dev->subordinate)
+			pci_bus_restore(dev->subordinate);
+	}
+}
+
+static int pci_slot_reset(struct pci_slot *slot, int probe)
+{
+	int rc;
+
+	if (!slot)
+		return -ENOTTY;
+
+	if (!probe)
+		pci_slot_lock(slot);
+
+	might_sleep();
+
+	rc = pci_hp_reset_slot(slot->hotplug, probe);
+
+	if (!probe)
+		pci_slot_unlock(slot);
+
+	return rc;
+}
+
+/**
+ * pci_reset_slot - reset a PCI slot
+ * @slot: PCI slot to reset
+ *
+ * A PCI bus may host multiple slots, each slot may support a reset mechanism
+ * independent of other slots.  For instance, some slots may support slot power
+ * control.  In the case of a 1:1 bus to slot architecture, this function may
+ * wrap the bus reset to avoid spurious slot related events, such as hotplug.
+ * Generally a slot reset should be attempted before a bus reset.  All of the
+ * function of the slot and any subordinate buses behind the slot are reset
+ * through this function.  PCI config space of all devices in the slot and
+ * behind the slot is saved before and restored after reset.
+ *
+ * Return 0 on success, non-zero on error.
+ */
+int pci_reset_slot(struct pci_slot *slot)
+{
+	int rc;
+
+	rc = pci_slot_reset(slot, 1);
+	if (rc)
+		return rc;
+
+	pci_slot_save(slot);
+
+	rc = pci_slot_reset(slot, 0);
+
+	pci_slot_restore(slot);
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pci_reset_slot);
+
+static int pci_bus_reset(struct pci_bus *bus, int probe)
+{
+	if (!bus->self)
+		return -ENOTTY;
+
+	if (probe)
+		return 0;
+
+	pci_bus_lock(bus);
+
+	might_sleep();
+
+	pci_reset_bridge_secondary_bus(bus->self);
+
+	pci_bus_unlock(bus);
+
+	return 0;
+}
+
+/**
+ * pci_reset_bus - reset a PCI bus
+ * @bus: top level PCI bus to reset
+ *
+ * Do a bus reset on the given bus and any subordinate buses, saving
+ * and restoring state of all devices.
+ *
+ * Return 0 on success, non-zero on error.
+ */
+int pci_reset_bus(struct pci_bus *bus)
+{
+	int rc;
+
+	rc = pci_bus_reset(bus, 1);
+	if (rc)
+		return rc;
+
+	pci_bus_save(bus);
+
+	rc = pci_bus_reset(bus, 0);
+
+	pci_bus_restore(bus);
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(pci_reset_bus);
+
 /**
  * pcix_get_max_mmrbc - get PCI-X maximum designed memory read byte count
  * @dev: PCI device to query
diff --git a/include/linux/pci.h b/include/linux/pci.h
index fc85d68..cf4cf10 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -922,6 +922,8 @@ int pcie_set_mps(struct pci_dev *dev, int mps);
 int __pci_reset_function(struct pci_dev *dev);
 int __pci_reset_function_locked(struct pci_dev *dev);
 int pci_reset_function(struct pci_dev *dev);
+int pci_reset_slot(struct pci_slot *slot);
+int pci_reset_bus(struct pci_bus *bus);
 void pci_reset_bridge_secondary_bus(struct pci_dev *dev);
 void pci_update_resource(struct pci_dev *dev, int resno);
 int __must_check pci_assign_resource(struct pci_dev *dev, int i);


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [RFC PATCH 8/8] pci: Add reset probe functions
  2013-05-01 18:42 [RFC PATCH 0/8] pci: extended reset capabilities Alex Williamson
                   ` (6 preceding siblings ...)
  2013-05-01 18:43 ` [RFC PATCH 7/8] pci: Add slot and bus reset interfaces Alex Williamson
@ 2013-05-01 18:43 ` Alex Williamson
  7 siblings, 0 replies; 9+ messages in thread
From: Alex Williamson @ 2013-05-01 18:43 UTC (permalink / raw)
  To: bhelgaas; +Cc: linux-kernel

Sometimes it's not enough to call pci_reset_function() and hope for the
best.  We may actually want to know which resets the device supports in
order to determine if pci_reset_function is sufficient or we should
call pci_reset_slot/bus.  Expose some helpers for this.

Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
---
 drivers/pci/pci.c   |   39 +++++++++++++++++++++++++++++++++++++++
 include/linux/pci.h |    3 +++
 2 files changed, 42 insertions(+)

diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 6d17807..798e5bd 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -3123,6 +3123,18 @@ clear:
 	return 0;
 }
 
+/**
+ * pci_probe_pcie_flr - Probe if device supports PCIe FLR
+ * @dev: PCI device to probe
+ *
+ * Return 0 if device supports PCIe FLR, non-zero otherwise.
+ */
+int pci_probe_pcie_flr(struct pci_dev *dev)
+{
+	return pcie_flr(dev, 1);
+}
+EXPORT_SYMBOL_GPL(pci_probe_pcie_flr);
+
 static int pci_af_flr(struct pci_dev *dev, int probe)
 {
 	int i;
@@ -3162,6 +3174,19 @@ clear:
 }
 
 /**
+ * pci_probe_af_flr - Probe if device supports PCI AF FLR
+ * @dev: PCI device to probe
+ *
+ * Return 0 if device supports FLR through a PCI Advanced Features capability,
+ * non-zero otherwise.
+ */
+int pci_probe_af_flr(struct pci_dev *dev)
+{
+	return pci_af_flr(dev, 1);
+}
+EXPORT_SYMBOL_GPL(pci_probe_af_flr);
+
+/**
  * 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.
@@ -3207,6 +3232,20 @@ static int pci_pm_reset(struct pci_dev *dev, int probe)
 }
 
 /**
+ * pci_probe_pm_reset - Probe if device supports PM reset
+ * @dev: PCI device to probe
+ *
+ * Return 0 if device claims to support a soft reset on D3hot->D0 transition,
+ * non-zero otherwise.  Note that "soft reset" isn't well defined and may not
+ * do much of any reset, depending on the device.
+ */
+int pci_probe_pm_reset(struct pci_dev *dev)
+{
+	return pci_pm_reset(dev, 1);
+}
+EXPORT_SYMBOL_GPL(pci_probe_pm_reset);
+
+/**
  * pci_reset_bridge_secondary_bus - Reset the secondary bus on a PCI bridge.
  * @dev: Bridge device
  *
diff --git a/include/linux/pci.h b/include/linux/pci.h
index cf4cf10..f6196a3 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -922,6 +922,9 @@ int pcie_set_mps(struct pci_dev *dev, int mps);
 int __pci_reset_function(struct pci_dev *dev);
 int __pci_reset_function_locked(struct pci_dev *dev);
 int pci_reset_function(struct pci_dev *dev);
+int pci_probe_pcie_flr(struct pci_dev *dev);
+int pci_probe_af_flr(struct pci_dev *dev);
+int pci_probe_pm_reset(struct pci_dev *dev);
 int pci_reset_slot(struct pci_slot *slot);
 int pci_reset_bus(struct pci_bus *bus);
 void pci_reset_bridge_secondary_bus(struct pci_dev *dev);


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2013-05-01 18:44 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-05-01 18:42 [RFC PATCH 0/8] pci: extended reset capabilities Alex Williamson
2013-05-01 18:43 ` [RFC PATCH 1/8] pci: Create pci_reset_bridge_secondary_bus() Alex Williamson
2013-05-01 18:43 ` [RFC PATCH 2/8] pci: Add hotplug_slot_ops.reset_slot() Alex Williamson
2013-05-01 18:43 ` [RFC PATCH 3/8] pci: Add pci_hp_reset_slot Alex Williamson
2013-05-01 18:43 ` [RFC PATCH 4/8] pci: Implement reset_slot for pciehp Alex Williamson
2013-05-01 18:43 ` [RFC PATCH 5/8] pci: Add reset_slot option to pci_dev_reset Alex Williamson
2013-05-01 18:43 ` [RFC PATCH 6/8] pci: Split out pci_dev lock/unlock and save/restore Alex Williamson
2013-05-01 18:43 ` [RFC PATCH 7/8] pci: Add slot and bus reset interfaces Alex Williamson
2013-05-01 18:43 ` [RFC PATCH 8/8] pci: Add reset probe functions Alex Williamson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®