mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Alex Williamson <alex.williamson@redhat.com>
To: bhelgaas@google.com
Cc: linux-kernel@vger.kernel.org
Subject: [RFC PATCH 7/8] pci: Add slot and bus reset interfaces
Date: Wed, 01 May 2013 12:43:37 -0600	[thread overview]
Message-ID: <20130501184337.28515.21429.stgit@bling.home> (raw)
In-Reply-To: <20130501181828.28515.68783.stgit@bling.home>

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);


  parent reply	other threads:[~2013-05-01 18:43 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 ` Alex Williamson [this message]
2013-05-01 18:43 ` [RFC PATCH 8/8] pci: Add reset probe functions Alex Williamson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20130501184337.28515.21429.stgit@bling.home \
    --to=alex.williamson@redhat.com \
    --cc=bhelgaas@google.com \
    --cc=linux-kernel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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®