]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.12-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 13 Aug 2017 22:26:07 +0000 (15:26 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 13 Aug 2017 22:26:07 +0000 (15:26 -0700)
added patches:
pci-add-pci_reset_function_locked.patch
pci-protect-pci_error_handlers-reset_notify-usage-with-device_lock.patch
pci-remove-__pci_dev_reset-and-pci_dev_reset.patch
xhci-reset-renesas-upd72020x-usb-controller-for-32-bit-dma-issue.patch

queue-4.12/pci-add-pci_reset_function_locked.patch [new file with mode: 0644]
queue-4.12/pci-protect-pci_error_handlers-reset_notify-usage-with-device_lock.patch [new file with mode: 0644]
queue-4.12/pci-remove-__pci_dev_reset-and-pci_dev_reset.patch [new file with mode: 0644]
queue-4.12/series
queue-4.12/xhci-reset-renesas-upd72020x-usb-controller-for-32-bit-dma-issue.patch [new file with mode: 0644]

diff --git a/queue-4.12/pci-add-pci_reset_function_locked.patch b/queue-4.12/pci-add-pci_reset_function_locked.patch
new file mode 100644 (file)
index 0000000..1964b07
--- /dev/null
@@ -0,0 +1,84 @@
+From a477b9cd37aa81a490dfa3265b7ff4f2c5a92463 Mon Sep 17 00:00:00 2001
+From: Marc Zyngier <marc.zyngier@arm.com>
+Date: Tue, 1 Aug 2017 20:11:02 -0500
+Subject: PCI: Add pci_reset_function_locked()
+
+From: Marc Zyngier <marc.zyngier@arm.com>
+
+commit a477b9cd37aa81a490dfa3265b7ff4f2c5a92463 upstream.
+
+The implementation of PCI workarounds may require that the device is reset
+from its probe function.  This implies that the PCI device lock is already
+held, and makes calling pci_reset_function() impossible (since it will
+itself try to take that lock).
+
+Add pci_reset_function_locked(), which is the equivalent of
+pci_reset_function(), except that it requires the PCI device lock to be
+already held by the caller.
+
+Tested-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
+Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
+[bhelgaas: folded in fix for conflict with 52354b9d1f46 ("PCI: Remove
+__pci_dev_reset() and pci_dev_reset()")]
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/pci/pci.c   |   35 +++++++++++++++++++++++++++++++++++
+ include/linux/pci.h |    1 +
+ 2 files changed, 36 insertions(+)
+
+--- a/drivers/pci/pci.c
++++ b/drivers/pci/pci.c
+@@ -4291,6 +4291,41 @@ int pci_reset_function(struct pci_dev *d
+ EXPORT_SYMBOL_GPL(pci_reset_function);
+ /**
++ * pci_reset_function_locked - quiesce and reset a PCI device function
++ * @dev: PCI device to reset
++ *
++ * 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.
++ *
++ * This function does not just reset the PCI portion of a device, but
++ * clears all the state associated with the device.  This function differs
++ * from __pci_reset_function() in that it saves and restores device state
++ * over the reset.  It also differs from pci_reset_function() in that it
++ * requires the PCI device lock to be held.
++ *
++ * Returns 0 if the device function was successfully reset or negative if the
++ * device doesn't support resetting a single function.
++ */
++int pci_reset_function_locked(struct pci_dev *dev)
++{
++      int rc;
++
++      rc = pci_probe_reset_function(dev);
++      if (rc)
++              return rc;
++
++      pci_dev_save_and_disable(dev);
++
++      rc = __pci_reset_function_locked(dev);
++
++      pci_dev_restore(dev);
++
++      return rc;
++}
++EXPORT_SYMBOL_GPL(pci_reset_function_locked);
++
++/**
+  * pci_try_reset_function - quiesce and reset a PCI device function
+  * @dev: PCI device to reset
+  *
+--- a/include/linux/pci.h
++++ b/include/linux/pci.h
+@@ -1049,6 +1049,7 @@ void pcie_flr(struct pci_dev *dev);
+ 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_function_locked(struct pci_dev *dev);
+ int pci_try_reset_function(struct pci_dev *dev);
+ int pci_probe_reset_slot(struct pci_slot *slot);
+ int pci_reset_slot(struct pci_slot *slot);
diff --git a/queue-4.12/pci-protect-pci_error_handlers-reset_notify-usage-with-device_lock.patch b/queue-4.12/pci-protect-pci_error_handlers-reset_notify-usage-with-device_lock.patch
new file mode 100644 (file)
index 0000000..0cc933f
--- /dev/null
@@ -0,0 +1,115 @@
+From b014e96d1abbd67404bbe2018937b46466299e9e Mon Sep 17 00:00:00 2001
+From: Christoph Hellwig <hch@lst.de>
+Date: Thu, 1 Jun 2017 13:10:37 +0200
+Subject: PCI: Protect pci_error_handlers->reset_notify() usage with device_lock()
+
+From: Christoph Hellwig <hch@lst.de>
+
+commit b014e96d1abbd67404bbe2018937b46466299e9e upstream.
+
+Every method in struct device_driver or structures derived from it like
+struct pci_driver MUST provide exclusion vs the driver's ->remove() method,
+usually by using device_lock().
+
+Protect use of pci_error_handlers->reset_notify() by holding the device
+lock while calling it.
+
+Note:
+
+  - pci_dev_lock() calls device_lock() in addition to blocking user-space
+    config accesses.
+
+  - pci_err_handlers->reset_notify() is used inside
+    pci_dev_save_and_disable() and pci_dev_restore().  We could hold the
+    device lock directly in pci_reset_notify(), but we expand the region
+    since we have several calls following each other.
+
+Without this, ->reset_notify() may race with ->remove() calls, which can be
+easily triggered in NVMe.
+
+[bhelgaas: changelog, add pci_reset_notify() comment]
+[bhelgaas: fold in fix from Dan Carpenter <dan.carpenter@oracle.com>:
+http://lkml.kernel.org/r/20170701135323.x5vaj4e2wcs2mcro@mwanda]
+Link: http://lkml.kernel.org/r/20170601111039.8913-2-hch@lst.de
+Reported-by: Rakesh Pandit <rakesh@tuxera.com>
+Tested-by: Rakesh Pandit <rakesh@tuxera.com>
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/pci/pci.c |   26 ++++++++++++++++++--------
+ 1 file changed, 18 insertions(+), 8 deletions(-)
+
+--- a/drivers/pci/pci.c
++++ b/drivers/pci/pci.c
+@@ -4141,6 +4141,12 @@ static void pci_reset_notify(struct pci_
+ {
+       const struct pci_error_handlers *err_handler =
+                       dev->driver ? dev->driver->err_handler : NULL;
++
++      /*
++       * dev->driver->err_handler->reset_notify() is protected against
++       * races with ->remove() by the device lock, which must be held by
++       * the caller.
++       */
+       if (err_handler && err_handler->reset_notify)
+               err_handler->reset_notify(dev, prepare);
+ }
+@@ -4276,11 +4282,13 @@ int pci_reset_function(struct pci_dev *d
+       if (rc)
+               return rc;
++      pci_dev_lock(dev);
+       pci_dev_save_and_disable(dev);
+-      rc = pci_dev_reset(dev, 0);
++      rc = __pci_dev_reset(dev, 0);
+       pci_dev_restore(dev);
++      pci_dev_unlock(dev);
+       return rc;
+ }
+@@ -4300,16 +4308,14 @@ int pci_try_reset_function(struct pci_de
+       if (rc)
+               return rc;
+-      pci_dev_save_and_disable(dev);
++      if (!pci_dev_trylock(dev))
++              return -EAGAIN;
+-      if (pci_dev_trylock(dev)) {
+-              rc = __pci_dev_reset(dev, 0);
+-              pci_dev_unlock(dev);
+-      } else
+-              rc = -EAGAIN;
++      pci_dev_save_and_disable(dev);
++      rc = __pci_dev_reset(dev, 0);
++      pci_dev_unlock(dev);
+       pci_dev_restore(dev);
+-
+       return rc;
+ }
+ EXPORT_SYMBOL_GPL(pci_try_reset_function);
+@@ -4459,7 +4465,9 @@ static void pci_bus_save_and_disable(str
+       struct pci_dev *dev;
+       list_for_each_entry(dev, &bus->devices, bus_list) {
++              pci_dev_lock(dev);
+               pci_dev_save_and_disable(dev);
++              pci_dev_unlock(dev);
+               if (dev->subordinate)
+                       pci_bus_save_and_disable(dev->subordinate);
+       }
+@@ -4474,7 +4482,9 @@ static void pci_bus_restore(struct pci_b
+       struct pci_dev *dev;
+       list_for_each_entry(dev, &bus->devices, bus_list) {
++              pci_dev_lock(dev);
+               pci_dev_restore(dev);
++              pci_dev_unlock(dev);
+               if (dev->subordinate)
+                       pci_bus_restore(dev->subordinate);
+       }
diff --git a/queue-4.12/pci-remove-__pci_dev_reset-and-pci_dev_reset.patch b/queue-4.12/pci-remove-__pci_dev_reset-and-pci_dev_reset.patch
new file mode 100644 (file)
index 0000000..58999ba
--- /dev/null
@@ -0,0 +1,194 @@
+From 52354b9d1f46aae7386db7bb8ec8484b5488087f Mon Sep 17 00:00:00 2001
+From: Christoph Hellwig <hch@lst.de>
+Date: Thu, 1 Jun 2017 13:10:39 +0200
+Subject: PCI: Remove __pci_dev_reset() and pci_dev_reset()
+
+From: Christoph Hellwig <hch@lst.de>
+
+commit 52354b9d1f46aae7386db7bb8ec8484b5488087f upstream.
+
+Implement the reset probing / reset chain directly in
+__pci_probe_reset_function() and __pci_reset_function_locked()
+respectively.
+
+Link: http://lkml.kernel.org/r/20170601111039.8913-4-hch@lst.de
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/pci/pci.c |  108 ++++++++++++++++++++++++++----------------------------
+ 1 file changed, 52 insertions(+), 56 deletions(-)
+
+--- a/drivers/pci/pci.c
++++ b/drivers/pci/pci.c
+@@ -4069,40 +4069,6 @@ static int pci_dev_reset_slot_function(s
+       return pci_reset_hotplug_slot(dev->slot->hotplug, probe);
+ }
+-static int __pci_dev_reset(struct pci_dev *dev, int probe)
+-{
+-      int rc;
+-
+-      might_sleep();
+-
+-      rc = pci_dev_specific_reset(dev, probe);
+-      if (rc != -ENOTTY)
+-              goto done;
+-
+-      if (pcie_has_flr(dev)) {
+-              if (!probe)
+-                      pcie_flr(dev);
+-              rc = 0;
+-              goto done;
+-      }
+-
+-      rc = pci_af_flr(dev, probe);
+-      if (rc != -ENOTTY)
+-              goto done;
+-
+-      rc = pci_pm_reset(dev, 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;
+-}
+-
+ static void pci_dev_lock(struct pci_dev *dev)
+ {
+       pci_cfg_access_lock(dev);
+@@ -4179,21 +4145,6 @@ static void pci_dev_restore(struct pci_d
+       pci_reset_notify(dev, false);
+ }
+-static int pci_dev_reset(struct pci_dev *dev, int probe)
+-{
+-      int rc;
+-
+-      if (!probe)
+-              pci_dev_lock(dev);
+-
+-      rc = __pci_dev_reset(dev, probe);
+-
+-      if (!probe)
+-              pci_dev_unlock(dev);
+-
+-      return rc;
+-}
+-
+ /**
+  * __pci_reset_function - reset a PCI device function
+  * @dev: PCI device to reset
+@@ -4213,7 +4164,13 @@ static int pci_dev_reset(struct pci_dev
+  */
+ int __pci_reset_function(struct pci_dev *dev)
+ {
+-      return pci_dev_reset(dev, 0);
++      int ret;
++
++      pci_dev_lock(dev);
++      ret = __pci_reset_function_locked(dev);
++      pci_dev_unlock(dev);
++
++      return ret;
+ }
+ EXPORT_SYMBOL_GPL(__pci_reset_function);
+@@ -4238,7 +4195,27 @@ EXPORT_SYMBOL_GPL(__pci_reset_function);
+  */
+ int __pci_reset_function_locked(struct pci_dev *dev)
+ {
+-      return __pci_dev_reset(dev, 0);
++      int rc;
++
++      might_sleep();
++
++      rc = pci_dev_specific_reset(dev, 0);
++      if (rc != -ENOTTY)
++              return rc;
++      if (pcie_has_flr(dev)) {
++              pcie_flr(dev);
++              return 0;
++      }
++      rc = pci_af_flr(dev, 0);
++      if (rc != -ENOTTY)
++              return rc;
++      rc = pci_pm_reset(dev, 0);
++      if (rc != -ENOTTY)
++              return rc;
++      rc = pci_dev_reset_slot_function(dev, 0);
++      if (rc != -ENOTTY)
++              return rc;
++      return pci_parent_bus_reset(dev, 0);
+ }
+ EXPORT_SYMBOL_GPL(__pci_reset_function_locked);
+@@ -4255,7 +4232,26 @@ EXPORT_SYMBOL_GPL(__pci_reset_function_l
+  */
+ int pci_probe_reset_function(struct pci_dev *dev)
+ {
+-      return pci_dev_reset(dev, 1);
++      int rc;
++
++      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;
++      rc = pci_dev_reset_slot_function(dev, 1);
++      if (rc != -ENOTTY)
++              return rc;
++
++      return pci_parent_bus_reset(dev, 1);
+ }
+ /**
+@@ -4278,14 +4274,14 @@ int pci_reset_function(struct pci_dev *d
+ {
+       int rc;
+-      rc = pci_dev_reset(dev, 1);
++      rc = pci_probe_reset_function(dev);
+       if (rc)
+               return rc;
+       pci_dev_lock(dev);
+       pci_dev_save_and_disable(dev);
+-      rc = __pci_dev_reset(dev, 0);
++      rc = __pci_reset_function_locked(dev);
+       pci_dev_restore(dev);
+       pci_dev_unlock(dev);
+@@ -4304,7 +4300,7 @@ int pci_try_reset_function(struct pci_de
+ {
+       int rc;
+-      rc = pci_dev_reset(dev, 1);
++      rc = pci_probe_reset_function(dev);
+       if (rc)
+               return rc;
+@@ -4312,7 +4308,7 @@ int pci_try_reset_function(struct pci_de
+               return -EAGAIN;
+       pci_dev_save_and_disable(dev);
+-      rc = __pci_dev_reset(dev, 0);
++      rc = __pci_reset_function_locked(dev);
+       pci_dev_unlock(dev);
+       pci_dev_restore(dev);
index 191ce228d7a6adaf7730d7954f31db66e6b7b8e3..8939028b238584f112800556075b61ed7bf22584 100644 (file)
@@ -45,3 +45,7 @@ usb-check-for-dropped-connection-before-switching-to-full-speed.patch
 usb-core-unlink-urbs-from-the-tail-of-the-endpoint-s-urb_list.patch
 usb-quirks-add-no-lpm-quirk-for-moshi-usb-to-ethernet-adapter.patch
 usb-xhci-add-quirk-for-certain-failing-hp-keyboard-on-reset-after-resume.patch
+pci-protect-pci_error_handlers-reset_notify-usage-with-device_lock.patch
+pci-remove-__pci_dev_reset-and-pci_dev_reset.patch
+pci-add-pci_reset_function_locked.patch
+xhci-reset-renesas-upd72020x-usb-controller-for-32-bit-dma-issue.patch
diff --git a/queue-4.12/xhci-reset-renesas-upd72020x-usb-controller-for-32-bit-dma-issue.patch b/queue-4.12/xhci-reset-renesas-upd72020x-usb-controller-for-32-bit-dma-issue.patch
new file mode 100644 (file)
index 0000000..51a46d7
--- /dev/null
@@ -0,0 +1,86 @@
+From 8466489ef5ba48272ba4fa4ea9f8f403306de4c7 Mon Sep 17 00:00:00 2001
+From: Marc Zyngier <marc.zyngier@arm.com>
+Date: Tue, 1 Aug 2017 20:11:08 -0500
+Subject: xhci: Reset Renesas uPD72020x USB controller for 32-bit DMA issue
+
+From: Marc Zyngier <marc.zyngier@arm.com>
+
+commit 8466489ef5ba48272ba4fa4ea9f8f403306de4c7 upstream.
+
+The Renesas uPD72020x XHCI controller seems to suffer from a really
+annoying bug, where it may retain some of its DMA programming across a XHCI
+reset, and despite the driver correctly programming new DMA addresses.
+This is visible if the device has been using 64-bit DMA addresses, and is
+then switched to using 32-bit DMA addresses.  The top 32 bits of the
+address (now zero) are ignored are replaced by the 32 bits from the
+*previous* programming.  Sticking with 64-bit DMA always works, but doesn't
+seem very appropriate.
+
+A PCI reset of the device restores the normal functionality, which is done
+at probe time.  Unfortunately, this has to be done before any quirk has
+been discovered, hence the intrusive nature of the fix.
+
+Tested-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
+Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Acked-by: Mathias Nyman <mathias.nyman@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/host/pci-quirks.c |   20 ++++++++++++++++++++
+ drivers/usb/host/pci-quirks.h |    1 +
+ drivers/usb/host/xhci-pci.c   |    7 +++++++
+ 3 files changed, 28 insertions(+)
+
+--- a/drivers/usb/host/pci-quirks.c
++++ b/drivers/usb/host/pci-quirks.c
+@@ -1157,3 +1157,23 @@ static void quirk_usb_early_handoff(stru
+ }
+ DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID,
+                       PCI_CLASS_SERIAL_USB, 8, quirk_usb_early_handoff);
++
++bool usb_xhci_needs_pci_reset(struct pci_dev *pdev)
++{
++      /*
++       * Our dear uPD72020{1,2} friend only partially resets when
++       * asked to via the XHCI interface, and may end up doing DMA
++       * at the wrong addresses, as it keeps the top 32bit of some
++       * addresses from its previous programming under obscure
++       * circumstances.
++       * Give it a good wack at probe time. Unfortunately, this
++       * needs to happen before we've had a chance to discover any
++       * quirk, or the system will be in a rather bad state.
++       */
++      if (pdev->vendor == PCI_VENDOR_ID_RENESAS &&
++          (pdev->device == 0x0014 || pdev->device == 0x0015))
++              return true;
++
++      return false;
++}
++EXPORT_SYMBOL_GPL(usb_xhci_needs_pci_reset);
+--- a/drivers/usb/host/pci-quirks.h
++++ b/drivers/usb/host/pci-quirks.h
+@@ -15,6 +15,7 @@ void usb_asmedia_modifyflowcontrol(struc
+ void usb_enable_intel_xhci_ports(struct pci_dev *xhci_pdev);
+ void usb_disable_xhci_ports(struct pci_dev *xhci_pdev);
+ void sb800_prefetch(struct device *dev, int on);
++bool usb_xhci_needs_pci_reset(struct pci_dev *pdev);
+ #else
+ struct pci_dev;
+ static inline void usb_amd_quirk_pll_disable(void) {}
+--- a/drivers/usb/host/xhci-pci.c
++++ b/drivers/usb/host/xhci-pci.c
+@@ -285,6 +285,13 @@ static int xhci_pci_probe(struct pci_dev
+       driver = (struct hc_driver *)id->driver_data;
++      /* For some HW implementation, a XHCI reset is just not enough... */
++      if (usb_xhci_needs_pci_reset(dev)) {
++              dev_info(&dev->dev, "Resetting\n");
++              if (pci_reset_function_locked(dev))
++                      dev_warn(&dev->dev, "Reset failed");
++      }
++
+       /* Prevent runtime suspending between USB-2 and USB-3 initialization */
+       pm_runtime_get_noresume(&dev->dev);