From a4f4fa681add289ebfec6d776376ad7a2ffda669 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Brucker Date: Tue, 30 May 2017 09:25:48 -0700 Subject: PCI: Cache PRI and PASID bits in pci_dev Device drivers need to check if an IOMMU enabled ATS, PRI and PASID in order to know when they can use the SVM API. Cache PRI and PASID bits in the pci_dev structure, similarly to what is currently done for ATS. Signed-off-by: Jean-Philippe Brucker Signed-off-by: Bjorn Helgaas --- drivers/pci/ats.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index eeb9fb2b47aa..21264976fa13 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -153,6 +153,9 @@ int pci_enable_pri(struct pci_dev *pdev, u32 reqs) u32 max_requests; int pos; + if (WARN_ON(pdev->pri_enabled)) + return -EBUSY; + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI); if (!pos) return -EINVAL; @@ -170,6 +173,8 @@ int pci_enable_pri(struct pci_dev *pdev, u32 reqs) control |= PCI_PRI_CTRL_ENABLE; pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control); + pdev->pri_enabled = 1; + return 0; } EXPORT_SYMBOL_GPL(pci_enable_pri); @@ -185,6 +190,9 @@ void pci_disable_pri(struct pci_dev *pdev) u16 control; int pos; + if (WARN_ON(!pdev->pri_enabled)) + return; + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI); if (!pos) return; @@ -192,6 +200,8 @@ void pci_disable_pri(struct pci_dev *pdev) pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control); control &= ~PCI_PRI_CTRL_ENABLE; pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control); + + pdev->pri_enabled = 0; } EXPORT_SYMBOL_GPL(pci_disable_pri); @@ -207,6 +217,9 @@ int pci_reset_pri(struct pci_dev *pdev) u16 control; int pos; + if (WARN_ON(pdev->pri_enabled)) + return -EBUSY; + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI); if (!pos) return -EINVAL; @@ -239,6 +252,9 @@ int pci_enable_pasid(struct pci_dev *pdev, int features) u16 control, supported; int pos; + if (WARN_ON(pdev->pasid_enabled)) + return -EBUSY; + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID); if (!pos) return -EINVAL; @@ -259,6 +275,8 @@ int pci_enable_pasid(struct pci_dev *pdev, int features) pci_write_config_word(pdev, pos + PCI_PASID_CTRL, control); + pdev->pasid_enabled = 1; + return 0; } EXPORT_SYMBOL_GPL(pci_enable_pasid); @@ -273,11 +291,16 @@ void pci_disable_pasid(struct pci_dev *pdev) u16 control = 0; int pos; + if (WARN_ON(!pdev->pasid_enabled)) + return; + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID); if (!pos) return; pci_write_config_word(pdev, pos + PCI_PASID_CTRL, control); + + pdev->pasid_enabled = 0; } EXPORT_SYMBOL_GPL(pci_disable_pasid); -- cgit v1.2.3 From 4ebeb1ec56d4c54a56b6f43c2603d9a4688c83ba Mon Sep 17 00:00:00 2001 From: CQ Tang Date: Tue, 30 May 2017 09:25:49 -0700 Subject: PCI: Restore PRI and PASID state after Function-Level Reset After a Function-Level Reset, PCI states need to be restored. Save PASID features and PRI reqs cached. [bhelgaas: search for capability only if PRI/PASID were enabled] Signed-off-by: CQ Tang Signed-off-by: Ashok Raj Signed-off-by: Bjorn Helgaas Cc: Joerg Roedel Cc: Jean-Phillipe Brucker Cc: David Woodhouse --- drivers/pci/ats.c | 64 +++++++++++++++++++++++++++++++++++++++++-------------- drivers/pci/pci.c | 3 +++ 2 files changed, 51 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index 21264976fa13..ad8ddbbbf245 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -160,17 +160,16 @@ int pci_enable_pri(struct pci_dev *pdev, u32 reqs) if (!pos) return -EINVAL; - pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control); pci_read_config_word(pdev, pos + PCI_PRI_STATUS, &status); - if ((control & PCI_PRI_CTRL_ENABLE) || - !(status & PCI_PRI_STATUS_STOPPED)) + if (!(status & PCI_PRI_STATUS_STOPPED)) return -EBUSY; pci_read_config_dword(pdev, pos + PCI_PRI_MAX_REQ, &max_requests); reqs = min(max_requests, reqs); + pdev->pri_reqs_alloc = reqs; pci_write_config_dword(pdev, pos + PCI_PRI_ALLOC_REQ, reqs); - control |= PCI_PRI_CTRL_ENABLE; + control = PCI_PRI_CTRL_ENABLE; pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control); pdev->pri_enabled = 1; @@ -205,6 +204,28 @@ void pci_disable_pri(struct pci_dev *pdev) } EXPORT_SYMBOL_GPL(pci_disable_pri); +/** + * pci_restore_pri_state - Restore PRI + * @pdev: PCI device structure + */ +void pci_restore_pri_state(struct pci_dev *pdev) +{ + u16 control = PCI_PRI_CTRL_ENABLE; + u32 reqs = pdev->pri_reqs_alloc; + int pos; + + if (!pdev->pri_enabled) + return; + + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI); + if (!pos) + return; + + pci_write_config_dword(pdev, pos + PCI_PRI_ALLOC_REQ, reqs); + pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control); +} +EXPORT_SYMBOL_GPL(pci_restore_pri_state); + /** * pci_reset_pri - Resets device's PRI state * @pdev: PCI device structure @@ -224,12 +245,7 @@ int pci_reset_pri(struct pci_dev *pdev) if (!pos) return -EINVAL; - pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control); - if (control & PCI_PRI_CTRL_ENABLE) - return -EBUSY; - - control |= PCI_PRI_CTRL_RESET; - + control = PCI_PRI_CTRL_RESET; pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control); return 0; @@ -259,12 +275,7 @@ int pci_enable_pasid(struct pci_dev *pdev, int features) if (!pos) return -EINVAL; - pci_read_config_word(pdev, pos + PCI_PASID_CTRL, &control); pci_read_config_word(pdev, pos + PCI_PASID_CAP, &supported); - - if (control & PCI_PASID_CTRL_ENABLE) - return -EINVAL; - supported &= PCI_PASID_CAP_EXEC | PCI_PASID_CAP_PRIV; /* User wants to enable anything unsupported? */ @@ -272,6 +283,7 @@ int pci_enable_pasid(struct pci_dev *pdev, int features) return -EINVAL; control = PCI_PASID_CTRL_ENABLE | features; + pdev->pasid_features = features; pci_write_config_word(pdev, pos + PCI_PASID_CTRL, control); @@ -284,7 +296,6 @@ EXPORT_SYMBOL_GPL(pci_enable_pasid); /** * pci_disable_pasid - Disable the PASID capability * @pdev: PCI device structure - * */ void pci_disable_pasid(struct pci_dev *pdev) { @@ -304,6 +315,27 @@ void pci_disable_pasid(struct pci_dev *pdev) } EXPORT_SYMBOL_GPL(pci_disable_pasid); +/** + * pci_restore_pasid_state - Restore PASID capabilities + * @pdev: PCI device structure + */ +void pci_restore_pasid_state(struct pci_dev *pdev) +{ + u16 control; + int pos; + + if (!pdev->pasid_enabled) + return; + + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID); + if (!pos) + return; + + control = PCI_PASID_CTRL_ENABLE | pdev->pasid_features; + pci_write_config_word(pdev, pos + PCI_PASID_CTRL, control); +} +EXPORT_SYMBOL_GPL(pci_restore_pasid_state); + /** * pci_pasid_features - Check which PASID features are supported * @pdev: PCI device structure diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index b01bd5bba8e6..3b38e98e68df 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -1173,6 +1174,8 @@ void pci_restore_state(struct pci_dev *dev) /* PCI Express register must be restored first */ pci_restore_pcie_state(dev); + pci_restore_pasid_state(dev); + pci_restore_pri_state(dev); pci_restore_ats_state(dev); pci_restore_vc_state(dev); -- cgit v1.2.3 From d40b7fd2cbcbeeff36e52aef2aa44e03a2ab7345 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Wed, 7 Jun 2017 13:00:48 -0600 Subject: PCI: Mark Intel XXV710 NIC INTx masking as broken Just like the other XL710 and X710 variants, the XXV710 device IDs appear to have the same hardware bug, the status register doesn't report pending interrupts resulting in "irq xx: nobody cared..." errors from the spurious interrupt handler when we try to use it with device assignment. Reported-by: Stefan Assmann Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas Acked-by: Jesse Brandeburg --- drivers/pci/quirks.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 085fb787aa9e..fb8214938c0d 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -3236,6 +3236,10 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1588, quirk_broken_intx_masking); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x1589, quirk_broken_intx_masking); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x158a, + quirk_broken_intx_masking); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x158b, + quirk_broken_intx_masking); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x37d0, quirk_broken_intx_masking); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x37d1, -- cgit v1.2.3 From 17530e71e0166a37f8e20a9b7bcf1d50ae3cff8e Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Mon, 22 May 2017 15:50:23 -0700 Subject: PCI: Protect pci_driver->sriov_configure() usage with device_lock() 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_driver->sriov_configure() by holding the device lock while calling it. The PCI core sets the pci_dev->driver pointer in local_pci_probe() before calling ->probe() and only clears it after ->remove(). This means driver's ->sriov_configure() callback will happily race with probe() and remove(), most likely leading to BUGs, since drivers don't expect this. Remove the iov lock completely, since we remove the last user. [bhelgaas: changelog, thanks to Christoph for locking rule] Link: http://lkml.kernel.org/r/20170522225023.14010-1-jakub.kicinski@netronome.com Signed-off-by: Jakub Kicinski Signed-off-by: Bjorn Helgaas Reviewed-by: Christoph Hellwig --- drivers/pci/iov.c | 4 ---- drivers/pci/pci-sysfs.c | 5 ++--- drivers/pci/pci.h | 1 - 3 files changed, 2 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index d9dc7363ac77..120485d6f352 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -461,8 +461,6 @@ found: else iov->dev = dev; - mutex_init(&iov->lock); - dev->sriov = iov; dev->is_physfn = 1; rc = compute_max_vf_buses(dev); @@ -491,8 +489,6 @@ static void sriov_release(struct pci_dev *dev) if (dev != dev->sriov->dev) pci_dev_put(dev->sriov->dev); - mutex_destroy(&dev->sriov->lock); - kfree(dev->sriov); dev->sriov = NULL; } diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 31e99613a12e..7755559558df 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -472,7 +472,6 @@ static ssize_t sriov_numvfs_store(struct device *dev, const char *buf, size_t count) { struct pci_dev *pdev = to_pci_dev(dev); - struct pci_sriov *iov = pdev->sriov; int ret; u16 num_vfs; @@ -483,7 +482,7 @@ static ssize_t sriov_numvfs_store(struct device *dev, if (num_vfs > pci_sriov_get_totalvfs(pdev)) return -ERANGE; - mutex_lock(&iov->dev->sriov->lock); + device_lock(&pdev->dev); if (num_vfs == pdev->sriov->num_VFs) goto exit; @@ -518,7 +517,7 @@ static ssize_t sriov_numvfs_store(struct device *dev, num_vfs, ret); exit: - mutex_unlock(&iov->dev->sriov->lock); + device_unlock(&pdev->dev); if (ret < 0) return ret; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index f8113e5b9812..93f4044b8f4b 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -272,7 +272,6 @@ struct pci_sriov { u16 driver_max_VFs; /* max num VFs driver supports */ struct pci_dev *dev; /* lowest numbered PF */ struct pci_dev *self; /* this PF */ - struct mutex lock; /* lock for setting sriov_numvfs in sysfs */ resource_size_t barsz[PCI_SRIOV_NUM_BARS]; /* VF BAR size */ bool drivers_autoprobe; /* auto probing of VFs by driver */ }; -- cgit v1.2.3 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() 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 --- drivers/pci/pci.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 3b38e98e68df..f4587f6f8739 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4143,6 +4143,12 @@ static void pci_reset_notify(struct pci_dev *dev, bool prepare) { 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); } @@ -4278,11 +4284,13 @@ int pci_reset_function(struct pci_dev *dev) 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; } @@ -4302,16 +4310,14 @@ int pci_try_reset_function(struct pci_dev *dev) 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); @@ -4461,7 +4467,9 @@ static void pci_bus_save_and_disable(struct pci_bus *bus) 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); } @@ -4476,7 +4484,9 @@ static void pci_bus_restore(struct pci_bus *bus) 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); } -- cgit v1.2.3 From 775755ed3c65fb2d31f9268162495d76eaa2c281 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 1 Jun 2017 13:10:38 +0200 Subject: PCI: Split ->reset_notify() method into ->reset_prepare() and ->reset_done() The pci_error_handlers->reset_notify() method had a flag to indicate whether to prepare for or clean up after a reset. The prepare and done cases have no shared functionality whatsoever, so split them into separate methods. [bhelgaas: changelog, update locking comments] Link: http://lkml.kernel.org/r/20170601111039.8913-3-hch@lst.de Signed-off-by: Christoph Hellwig Signed-off-by: Bjorn Helgaas --- drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 36 +++++-------- drivers/net/wireless/marvell/mwifiex/pcie.c | 75 ++++++++++++++++------------ drivers/nvme/host/pci.c | 15 +++--- drivers/pci/pci.c | 34 ++++++------- 4 files changed, 80 insertions(+), 80 deletions(-) (limited to 'drivers') diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index 3e26d27ad213..63784576ae8b 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -2348,30 +2348,19 @@ static void fm10k_io_resume(struct pci_dev *pdev) netif_device_attach(netdev); } -/** - * fm10k_io_reset_notify - called when PCI function is reset - * @pdev: Pointer to PCI device - * - * This callback is called when the PCI function is reset such as from - * /sys/class/net//device/reset or similar. When prepare is true, it - * means we should prepare for a function reset. If prepare is false, it means - * the function reset just occurred. - */ -static void fm10k_io_reset_notify(struct pci_dev *pdev, bool prepare) +static void fm10k_io_reset_prepare(struct pci_dev *pdev) { - struct fm10k_intfc *interface = pci_get_drvdata(pdev); - int err = 0; - - if (prepare) { - /* warn incase we have any active VF devices */ - if (pci_num_vf(pdev)) - dev_warn(&pdev->dev, - "PCIe FLR may cause issues for any active VF devices\n"); + /* warn incase we have any active VF devices */ + if (pci_num_vf(pdev)) + dev_warn(&pdev->dev, + "PCIe FLR may cause issues for any active VF devices\n"); + fm10k_prepare_suspend(pci_get_drvdata(pdev)); +} - fm10k_prepare_suspend(interface); - } else { - err = fm10k_handle_resume(interface); - } +static void fm10k_io_reset_done(struct pci_dev *pdev) +{ + struct fm10k_intfc *interface = pci_get_drvdata(pdev); + int err = fm10k_handle_resume(interface); if (err) { dev_warn(&pdev->dev, @@ -2384,7 +2373,8 @@ static const struct pci_error_handlers fm10k_err_handler = { .error_detected = fm10k_io_error_detected, .slot_reset = fm10k_io_slot_reset, .resume = fm10k_io_resume, - .reset_notify = fm10k_io_reset_notify, + .reset_prepare = fm10k_io_reset_prepare, + .reset_done = fm10k_io_reset_done, }; static struct pci_driver fm10k_driver = { diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index ac62bce50e96..279adf124fc9 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -346,11 +346,13 @@ static const struct pci_device_id mwifiex_ids[] = { MODULE_DEVICE_TABLE(pci, mwifiex_ids); -static void mwifiex_pcie_reset_notify(struct pci_dev *pdev, bool prepare) +/* + * Cleanup all software without cleaning anything related to PCIe and HW. + */ +static void mwifiex_pcie_reset_prepare(struct pci_dev *pdev) { struct pcie_service_card *card = pci_get_drvdata(pdev); struct mwifiex_adapter *adapter = card->adapter; - int ret; if (!adapter) { dev_err(&pdev->dev, "%s: adapter structure is not valid\n", @@ -359,37 +361,48 @@ static void mwifiex_pcie_reset_notify(struct pci_dev *pdev, bool prepare) } mwifiex_dbg(adapter, INFO, - "%s: vendor=0x%4.04x device=0x%4.04x rev=%d %s\n", - __func__, pdev->vendor, pdev->device, - pdev->revision, - prepare ? "Pre-FLR" : "Post-FLR"); - - if (prepare) { - /* Kernel would be performing FLR after this notification. - * Cleanup all software without cleaning anything related to - * PCIe and HW. - */ - mwifiex_shutdown_sw(adapter); - adapter->surprise_removed = true; - clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &card->work_flags); - clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &card->work_flags); - } else { - /* Kernel stores and restores PCIe function context before and - * after performing FLR respectively. Reconfigure the software - * and firmware including firmware redownload - */ - adapter->surprise_removed = false; - ret = mwifiex_reinit_sw(adapter); - if (ret) { - dev_err(&pdev->dev, "reinit failed: %d\n", ret); - return; - } - } + "%s: vendor=0x%4.04x device=0x%4.04x rev=%d Pre-FLR\n", + __func__, pdev->vendor, pdev->device, pdev->revision); + + mwifiex_shutdown_sw(adapter); + adapter->surprise_removed = true; + clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &card->work_flags); + clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &card->work_flags); mwifiex_dbg(adapter, INFO, "%s, successful\n", __func__); } -static const struct pci_error_handlers mwifiex_pcie_err_handler[] = { - { .reset_notify = mwifiex_pcie_reset_notify, }, +/* + * Kernel stores and restores PCIe function context before and after performing + * FLR respectively. Reconfigure the software and firmware including firmware + * redownload. + */ +static void mwifiex_pcie_reset_done(struct pci_dev *pdev) +{ + struct pcie_service_card *card = pci_get_drvdata(pdev); + struct mwifiex_adapter *adapter = card->adapter; + int ret; + + if (!adapter) { + dev_err(&pdev->dev, "%s: adapter structure is not valid\n", + __func__); + return; + } + + mwifiex_dbg(adapter, INFO, + "%s: vendor=0x%4.04x device=0x%4.04x rev=%d Post-FLR\n", + __func__, pdev->vendor, pdev->device, pdev->revision); + + adapter->surprise_removed = false; + ret = mwifiex_reinit_sw(adapter); + if (ret) + dev_err(&pdev->dev, "reinit failed: %d\n", ret); + else + mwifiex_dbg(adapter, INFO, "%s, successful\n", __func__); +} + +static const struct pci_error_handlers mwifiex_pcie_err_handler = { + .reset_prepare = mwifiex_pcie_reset_prepare, + .reset_done = mwifiex_pcie_reset_done, }; #ifdef CONFIG_PM_SLEEP @@ -410,7 +423,7 @@ static struct pci_driver __refdata mwifiex_pcie = { }, #endif .shutdown = mwifiex_pcie_shutdown, - .err_handler = mwifiex_pcie_err_handler, + .err_handler = &mwifiex_pcie_err_handler, }; /* diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index fed803232edc..9a3d69b8df98 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -2145,14 +2145,14 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) return result; } -static void nvme_reset_notify(struct pci_dev *pdev, bool prepare) +static void nvme_reset_prepare(struct pci_dev *pdev) { - struct nvme_dev *dev = pci_get_drvdata(pdev); + nvme_dev_disable(pci_get_drvdata(pdev), false); +} - if (prepare) - nvme_dev_disable(dev, false); - else - nvme_reset(dev); +static void nvme_reset_done(struct pci_dev *pdev) +{ + nvme_reset(pci_get_drvdata(pdev)); } static void nvme_shutdown(struct pci_dev *pdev) @@ -2275,7 +2275,8 @@ static const struct pci_error_handlers nvme_err_handler = { .error_detected = nvme_error_detected, .slot_reset = nvme_slot_reset, .resume = nvme_error_resume, - .reset_notify = nvme_reset_notify, + .reset_prepare = nvme_reset_prepare, + .reset_done = nvme_reset_done, }; static const struct pci_device_id nvme_id_table[] = { diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index f4587f6f8739..56407eb1dc88 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4130,32 +4130,18 @@ static void pci_dev_unlock(struct pci_dev *dev) pci_cfg_access_unlock(dev); } -/** - * pci_reset_notify - notify device driver of reset - * @dev: device to be notified of reset - * @prepare: 'true' if device is about to be reset; 'false' if reset attempt - * completed - * - * Must be called prior to device access being disabled and after device - * access is restored. - */ -static void pci_reset_notify(struct pci_dev *dev, bool prepare) +static void pci_dev_save_and_disable(struct pci_dev *dev) { const struct pci_error_handlers *err_handler = dev->driver ? dev->driver->err_handler : NULL; /* - * dev->driver->err_handler->reset_notify() is protected against + * dev->driver->err_handler->reset_prepare() 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); -} - -static void pci_dev_save_and_disable(struct pci_dev *dev) -{ - pci_reset_notify(dev, true); + if (err_handler && err_handler->reset_prepare) + err_handler->reset_prepare(dev); /* * Wake-up device prior to save. PM registers default to D0 after @@ -4177,8 +4163,18 @@ static void pci_dev_save_and_disable(struct pci_dev *dev) static void pci_dev_restore(struct pci_dev *dev) { + const struct pci_error_handlers *err_handler = + dev->driver ? dev->driver->err_handler : NULL; + pci_restore_state(dev); - pci_reset_notify(dev, false); + + /* + * dev->driver->err_handler->reset_done() is protected against + * races with ->remove() by the device lock, which must be held by + * the caller. + */ + if (err_handler && err_handler->reset_done) + err_handler->reset_done(dev); } static int pci_dev_reset(struct pci_dev *dev, int probe) -- cgit v1.2.3 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() 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 --- drivers/pci/pci.c | 108 ++++++++++++++++++++++++++---------------------------- 1 file changed, 52 insertions(+), 56 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 56407eb1dc88..7a75502ece1d 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -4071,40 +4071,6 @@ static int pci_dev_reset_slot_function(struct pci_dev *dev, int probe) 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); @@ -4177,21 +4143,6 @@ static void pci_dev_restore(struct pci_dev *dev) err_handler->reset_done(dev); } -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 @@ -4211,7 +4162,13 @@ static int pci_dev_reset(struct pci_dev *dev, int probe) */ 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); @@ -4236,7 +4193,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); @@ -4253,7 +4230,26 @@ EXPORT_SYMBOL_GPL(__pci_reset_function_locked); */ 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); } /** @@ -4276,14 +4272,14 @@ int pci_reset_function(struct pci_dev *dev) { 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); @@ -4302,7 +4298,7 @@ int pci_try_reset_function(struct pci_dev *dev) { int rc; - rc = pci_dev_reset(dev, 1); + rc = pci_probe_reset_function(dev); if (rc) return rc; @@ -4310,7 +4306,7 @@ int pci_try_reset_function(struct pci_dev *dev) 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); -- cgit v1.2.3