From 68da4e0eaaab421f228eac57cbe7505b136464af Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 10 Nov 2021 12:01:14 -0600 Subject: [PATCH 1/2] Revert "PCI: Remove struct pci_dev->driver" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit b5f9c644eb1baafcd349ad134e2110773f8d0a38. Revert b5f9c644eb1b ("PCI: Remove struct pci_dev->driver"), which is needed to revert 2a4d9408c9e8 ("PCI: Use to_pci_driver() instead of pci_dev->driver"). 2a4d9408c9e8 caused a NULL pointer dereference reported by Robert Święcki. Details in the revert of that commit. Fixes: 2a4d9408c9e8 ("PCI: Use to_pci_driver() instead of pci_dev->driver") Link: https://lore.kernel.org/linux-i2c/CAP145pgdrdiMAT7=-iB1DMgA7t_bMqTcJL4N0=6u8kNY3EU0dw@mail.gmail.com/ Reported-by: Robert Święcki Tested-by: Robert Święcki Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-driver.c | 4 ++++ include/linux/pci.h | 1 + 2 files changed, 5 insertions(+) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 1d98c974381c..4c1f46dbfa87 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -319,10 +319,12 @@ static long local_pci_probe(void *_ddi) * its remove routine. */ pm_runtime_get_sync(dev); + pci_dev->driver = pci_drv; rc = pci_drv->probe(pci_dev, ddi->id); if (!rc) return rc; if (rc < 0) { + pci_dev->driver = NULL; pm_runtime_put_sync(dev); return rc; } @@ -388,6 +390,7 @@ static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev, * @pci_dev: PCI device being probed * * returns 0 on success, else error. + * side-effect: pci_dev->driver is set to drv when drv claims pci_dev. */ static int __pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev) { @@ -462,6 +465,7 @@ static void pci_device_remove(struct device *dev) pm_runtime_put_noidle(dev); } pcibios_free_irq(pci_dev); + pci_dev->driver = NULL; pci_iov_remove(pci_dev); /* Undo the runtime PM settings in local_pci_probe() */ diff --git a/include/linux/pci.h b/include/linux/pci.h index b4dbcc86b3f1..e58888e21ab7 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -342,6 +342,7 @@ struct pci_dev { u16 pcie_flags_reg; /* Cached PCIe Capabilities Register */ unsigned long *dma_alias_mask;/* Mask of enabled devfn aliases */ + struct pci_driver *driver; /* Driver bound to this device */ u64 dma_mask; /* Mask of the bits of bus address this device implements. Normally this is 0xffffffff. You only need to change From e0217c5ba10d7bf640f038b2feae58e630f2f958 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 10 Nov 2021 12:03:34 -0600 Subject: [PATCH 2/2] Revert "PCI: Use to_pci_driver() instead of pci_dev->driver" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 2a4d9408c9e8b6f6fc150c66f3fef755c9e20d4a. Robert reported a NULL pointer dereference caused by the PCI core (local_pci_probe()) calling the i2c_designware_pci driver's .runtime_resume() method before the .probe() method. i2c_dw_pci_resume() depends on initialization done by i2c_dw_pci_probe(). Prior to 2a4d9408c9e8 ("PCI: Use to_pci_driver() instead of pci_dev->driver"), pci_pm_runtime_resume() avoided calling the .runtime_resume() method because pci_dev->driver had not been set yet. 2a4d9408c9e8 and b5f9c644eb1b ("PCI: Remove struct pci_dev->driver"), removed pci_dev->driver, replacing it by device->driver, which *has* been set by this time, so pci_pm_runtime_resume() called the .runtime_resume() method when it previously had not. Fixes: 2a4d9408c9e8 ("PCI: Use to_pci_driver() instead of pci_dev->driver") Link: https://lore.kernel.org/linux-i2c/CAP145pgdrdiMAT7=-iB1DMgA7t_bMqTcJL4N0=6u8kNY3EU0dw@mail.gmail.com/ Reported-by: Robert Święcki Tested-by: Robert Święcki Signed-off-by: Bjorn Helgaas --- drivers/pci/iov.c | 24 +++++++++--------------- drivers/pci/pci-driver.c | 33 ++++++++++++++++----------------- drivers/pci/pci.c | 17 ++++++++--------- drivers/pci/pcie/err.c | 8 ++++---- 4 files changed, 37 insertions(+), 45 deletions(-) diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 1d7a7c5b5307..0267977c9f17 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -164,15 +164,13 @@ static ssize_t sriov_vf_total_msix_show(struct device *dev, char *buf) { struct pci_dev *pdev = to_pci_dev(dev); - struct pci_driver *pdrv; u32 vf_total_msix = 0; device_lock(dev); - pdrv = to_pci_driver(dev->driver); - if (!pdrv || !pdrv->sriov_get_vf_total_msix) + if (!pdev->driver || !pdev->driver->sriov_get_vf_total_msix) goto unlock; - vf_total_msix = pdrv->sriov_get_vf_total_msix(pdev); + vf_total_msix = pdev->driver->sriov_get_vf_total_msix(pdev); unlock: device_unlock(dev); return sysfs_emit(buf, "%u\n", vf_total_msix); @@ -185,7 +183,6 @@ static ssize_t sriov_vf_msix_count_store(struct device *dev, { struct pci_dev *vf_dev = to_pci_dev(dev); struct pci_dev *pdev = pci_physfn(vf_dev); - struct pci_driver *pdrv; int val, ret = 0; if (kstrtoint(buf, 0, &val) < 0) @@ -195,14 +192,13 @@ static ssize_t sriov_vf_msix_count_store(struct device *dev, return -EINVAL; device_lock(&pdev->dev); - pdrv = to_pci_driver(dev->driver); - if (!pdrv || !pdrv->sriov_set_msix_vec_count) { + if (!pdev->driver || !pdev->driver->sriov_set_msix_vec_count) { ret = -EOPNOTSUPP; goto err_pdev; } device_lock(&vf_dev->dev); - if (to_pci_driver(vf_dev->dev.driver)) { + if (vf_dev->driver) { /* * A driver is already attached to this VF and has configured * itself based on the current MSI-X vector count. Changing @@ -212,7 +208,7 @@ static ssize_t sriov_vf_msix_count_store(struct device *dev, goto err_dev; } - ret = pdrv->sriov_set_msix_vec_count(vf_dev, val); + ret = pdev->driver->sriov_set_msix_vec_count(vf_dev, val); err_dev: device_unlock(&vf_dev->dev); @@ -379,7 +375,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_driver *pdrv; int ret = 0; u16 num_vfs; @@ -395,15 +390,14 @@ static ssize_t sriov_numvfs_store(struct device *dev, goto exit; /* is PF driver loaded */ - pdrv = to_pci_driver(dev->driver); - if (!pdrv) { + if (!pdev->driver) { pci_info(pdev, "no driver bound to device; cannot configure SR-IOV\n"); ret = -ENOENT; goto exit; } /* is PF driver loaded w/callback */ - if (!pdrv->sriov_configure) { + if (!pdev->driver->sriov_configure) { pci_info(pdev, "driver does not support SR-IOV configuration via sysfs\n"); ret = -ENOENT; goto exit; @@ -411,7 +405,7 @@ static ssize_t sriov_numvfs_store(struct device *dev, if (num_vfs == 0) { /* disable VFs */ - ret = pdrv->sriov_configure(pdev, 0); + ret = pdev->driver->sriov_configure(pdev, 0); goto exit; } @@ -423,7 +417,7 @@ static ssize_t sriov_numvfs_store(struct device *dev, goto exit; } - ret = pdrv->sriov_configure(pdev, num_vfs); + ret = pdev->driver->sriov_configure(pdev, num_vfs); if (ret < 0) goto exit; diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 4c1f46dbfa87..588588cfda48 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -457,7 +457,7 @@ static int pci_device_probe(struct device *dev) static void pci_device_remove(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct pci_driver *drv = to_pci_driver(dev->driver); + struct pci_driver *drv = pci_dev->driver; if (drv->remove) { pm_runtime_get_sync(dev); @@ -493,7 +493,7 @@ static void pci_device_remove(struct device *dev) static void pci_device_shutdown(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct pci_driver *drv = to_pci_driver(dev->driver); + struct pci_driver *drv = pci_dev->driver; pm_runtime_resume(dev); @@ -589,7 +589,7 @@ static int pci_pm_reenable_device(struct pci_dev *pci_dev) static int pci_legacy_suspend(struct device *dev, pm_message_t state) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct pci_driver *drv = to_pci_driver(dev->driver); + struct pci_driver *drv = pci_dev->driver; if (drv && drv->suspend) { pci_power_t prev = pci_dev->current_state; @@ -630,7 +630,7 @@ static int pci_legacy_suspend_late(struct device *dev, pm_message_t state) static int pci_legacy_resume(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct pci_driver *drv = to_pci_driver(dev->driver); + struct pci_driver *drv = pci_dev->driver; pci_fixup_device(pci_fixup_resume, pci_dev); @@ -649,7 +649,7 @@ static void pci_pm_default_suspend(struct pci_dev *pci_dev) static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev) { - struct pci_driver *drv = to_pci_driver(pci_dev->dev.driver); + struct pci_driver *drv = pci_dev->driver; bool ret = drv && (drv->suspend || drv->resume); /* @@ -1242,11 +1242,11 @@ static int pci_pm_runtime_suspend(struct device *dev) int error; /* - * If the device has no driver, we leave it in D0, but it may go to - * D3cold when the bridge above it runtime suspends. Save its - * config space in case that happens. + * If pci_dev->driver is not set (unbound), we leave the device in D0, + * but it may go to D3cold when the bridge above it runtime suspends. + * Save its config space in case that happens. */ - if (!to_pci_driver(dev->driver)) { + if (!pci_dev->driver) { pci_save_state(pci_dev); return 0; } @@ -1303,7 +1303,7 @@ static int pci_pm_runtime_resume(struct device *dev) */ pci_restore_standard_config(pci_dev); - if (!to_pci_driver(dev->driver)) + if (!pci_dev->driver) return 0; pci_fixup_device(pci_fixup_resume_early, pci_dev); @@ -1322,13 +1322,14 @@ static int pci_pm_runtime_resume(struct device *dev) static int pci_pm_runtime_idle(struct device *dev) { + struct pci_dev *pci_dev = to_pci_dev(dev); const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; /* - * If the device has no driver, it should always remain in D0 - * regardless of the runtime PM status + * If pci_dev->driver is not set (unbound), the device should + * always remain in D0 regardless of the runtime PM status */ - if (!to_pci_driver(dev->driver)) + if (!pci_dev->driver) return 0; if (!pm) @@ -1435,10 +1436,8 @@ static struct pci_driver pci_compat_driver = { */ struct pci_driver *pci_dev_driver(const struct pci_dev *dev) { - struct pci_driver *drv = to_pci_driver(dev->dev.driver); - - if (drv) - return drv; + if (dev->driver) + return dev->driver; else { int i; for (i = 0; i <= PCI_ROM_RESOURCE; i++) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index b88db815ee01..40012d13c3c4 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5123,14 +5123,13 @@ EXPORT_SYMBOL_GPL(pci_dev_unlock); static void pci_dev_save_and_disable(struct pci_dev *dev) { - struct pci_driver *drv = to_pci_driver(dev->dev.driver); const struct pci_error_handlers *err_handler = - drv ? drv->err_handler : NULL; + dev->driver ? dev->driver->err_handler : NULL; /* - * drv->err_handler->reset_prepare() is protected against races - * with ->remove() by the device lock, which must be held by the - * caller. + * 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_prepare) err_handler->reset_prepare(dev); @@ -5155,15 +5154,15 @@ static void pci_dev_save_and_disable(struct pci_dev *dev) static void pci_dev_restore(struct pci_dev *dev) { - struct pci_driver *drv = to_pci_driver(dev->dev.driver); const struct pci_error_handlers *err_handler = - drv ? drv->err_handler : NULL; + dev->driver ? dev->driver->err_handler : NULL; pci_restore_state(dev); /* - * drv->err_handler->reset_done() is protected against races with - * ->remove() by the device lock, which must be held by the caller. + * 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); diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c index 356b9317297e..0c5a143025af 100644 --- a/drivers/pci/pcie/err.c +++ b/drivers/pci/pcie/err.c @@ -54,7 +54,7 @@ static int report_error_detected(struct pci_dev *dev, const struct pci_error_handlers *err_handler; device_lock(&dev->dev); - pdrv = to_pci_driver(dev->dev.driver); + pdrv = dev->driver; if (!pci_dev_set_io_state(dev, state) || !pdrv || !pdrv->err_handler || @@ -98,7 +98,7 @@ static int report_mmio_enabled(struct pci_dev *dev, void *data) const struct pci_error_handlers *err_handler; device_lock(&dev->dev); - pdrv = to_pci_driver(dev->dev.driver); + pdrv = dev->driver; if (!pdrv || !pdrv->err_handler || !pdrv->err_handler->mmio_enabled) @@ -119,7 +119,7 @@ static int report_slot_reset(struct pci_dev *dev, void *data) const struct pci_error_handlers *err_handler; device_lock(&dev->dev); - pdrv = to_pci_driver(dev->dev.driver); + pdrv = dev->driver; if (!pdrv || !pdrv->err_handler || !pdrv->err_handler->slot_reset) @@ -139,7 +139,7 @@ static int report_resume(struct pci_dev *dev, void *data) const struct pci_error_handlers *err_handler; device_lock(&dev->dev); - pdrv = to_pci_driver(dev->dev.driver); + pdrv = dev->driver; if (!pci_dev_set_io_state(dev, pci_channel_io_normal) || !pdrv || !pdrv->err_handler ||