From: Greg Kroah-Hartman Date: Sun, 13 Aug 2017 22:26:07 +0000 (-0700) Subject: 4.12-stable patches X-Git-Tag: v3.18.66~8 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=0b0f39ade5f6cb9f2c8dd98b8ace71ff62a5c615;p=thirdparty%2Fkernel%2Fstable-queue.git 4.12-stable patches 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 --- 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 index 00000000000..1964b0729ea --- /dev/null +++ b/queue-4.12/pci-add-pci_reset_function_locked.patch @@ -0,0 +1,84 @@ +From a477b9cd37aa81a490dfa3265b7ff4f2c5a92463 Mon Sep 17 00:00:00 2001 +From: Marc Zyngier +Date: Tue, 1 Aug 2017 20:11:02 -0500 +Subject: PCI: Add pci_reset_function_locked() + +From: Marc Zyngier + +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 +Signed-off-by: Marc Zyngier +[bhelgaas: folded in fix for conflict with 52354b9d1f46 ("PCI: Remove +__pci_dev_reset() and pci_dev_reset()")] +Signed-off-by: Bjorn Helgaas +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..0cc933f3be9 --- /dev/null +++ b/queue-4.12/pci-protect-pci_error_handlers-reset_notify-usage-with-device_lock.patch @@ -0,0 +1,115 @@ +From b014e96d1abbd67404bbe2018937b46466299e9e Mon Sep 17 00:00:00 2001 +From: Christoph Hellwig +Date: Thu, 1 Jun 2017 13:10:37 +0200 +Subject: PCI: Protect pci_error_handlers->reset_notify() usage with device_lock() + +From: Christoph Hellwig + +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 : +http://lkml.kernel.org/r/20170701135323.x5vaj4e2wcs2mcro@mwanda] +Link: http://lkml.kernel.org/r/20170601111039.8913-2-hch@lst.de +Reported-by: Rakesh Pandit +Tested-by: Rakesh Pandit +Signed-off-by: Christoph Hellwig +Signed-off-by: Bjorn Helgaas +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..58999ba2b9b --- /dev/null +++ b/queue-4.12/pci-remove-__pci_dev_reset-and-pci_dev_reset.patch @@ -0,0 +1,194 @@ +From 52354b9d1f46aae7386db7bb8ec8484b5488087f Mon Sep 17 00:00:00 2001 +From: Christoph Hellwig +Date: Thu, 1 Jun 2017 13:10:39 +0200 +Subject: PCI: Remove __pci_dev_reset() and pci_dev_reset() + +From: Christoph Hellwig + +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 +Signed-off-by: Bjorn Helgaas +Signed-off-by: Greg Kroah-Hartman + +--- + 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); diff --git a/queue-4.12/series b/queue-4.12/series index 191ce228d7a..8939028b238 100644 --- a/queue-4.12/series +++ b/queue-4.12/series @@ -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 index 00000000000..51a46d70ce4 --- /dev/null +++ b/queue-4.12/xhci-reset-renesas-upd72020x-usb-controller-for-32-bit-dma-issue.patch @@ -0,0 +1,86 @@ +From 8466489ef5ba48272ba4fa4ea9f8f403306de4c7 Mon Sep 17 00:00:00 2001 +From: Marc Zyngier +Date: Tue, 1 Aug 2017 20:11:08 -0500 +Subject: xhci: Reset Renesas uPD72020x USB controller for 32-bit DMA issue + +From: Marc Zyngier + +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 +Signed-off-by: Marc Zyngier +Signed-off-by: Bjorn Helgaas +Acked-by: Mathias Nyman +Signed-off-by: Greg Kroah-Hartman + +--- + 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); +