summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Norris <briannorris@chromium.org>2024-11-26 13:04:34 -0800
committerBjorn Helgaas <bhelgaas@google.com>2024-11-30 11:41:25 -0600
commit5c8418cf4025388bedd4d65ada993f7d3786cc3a (patch)
tree6889b90cae1e7330bef286d7f0c89aa7b8ed3205
parent10099266dec8275a6899e6a27dcdfebbcc726cc7 (diff)
downloadlwn-5c8418cf4025388bedd4d65ada993f7d3786cc3a.tar.gz
lwn-5c8418cf4025388bedd4d65ada993f7d3786cc3a.zip
PCI/pwrctrl: Unregister platform device only if one actually exists
If a PCI device has an associated device_node with power supplies, pci_bus_add_device() creates platform devices for use by pwrctrl. When the PCI device is removed, pci_stop_dev() uses of_find_device_by_node() to locate the related platform device, then unregisters it. But when we remove a PCI device with no associated device node, dev_of_node(dev) is NULL, and of_find_device_by_node(NULL) returns the first device with "dev->of_node == NULL". The result is that we (a) mistakenly unregister a completely unrelated platform device, leading to issues like the first trace below, and (b) dereference the NULL pointer from dev_of_node() when clearing OF_POPULATED, as in the second trace. Unregister a platform device only if there is one associated with this PCI device. This resolves issues seen when doing: # echo 1 > /sys/bus/pci/devices/.../remove Sample issue from unregistering the wrong platform device: WARNING: CPU: 0 PID: 5095 at drivers/regulator/core.c:5885 regulator_unregister+0x140/0x160 Call trace: regulator_unregister+0x140/0x160 devm_rdev_release+0x1c/0x30 release_nodes+0x68/0x100 devres_release_all+0x98/0xf8 device_unbind_cleanup+0x20/0x70 device_release_driver_internal+0x1f4/0x240 device_release_driver+0x20/0x40 bus_remove_device+0xd8/0x170 device_del+0x154/0x380 device_unregister+0x28/0x88 of_device_unregister+0x1c/0x30 pci_stop_bus_device+0x154/0x1b0 pci_stop_and_remove_bus_device_locked+0x28/0x48 remove_store+0xa0/0xb8 dev_attr_store+0x20/0x40 sysfs_kf_write+0x4c/0x68 Later NULL pointer dereference for of_node_clear_flag(NULL, OF_POPULATED): Unable to handle kernel NULL pointer dereference at virtual address 00000000000000c0 Call trace: pci_stop_bus_device+0x190/0x1b0 pci_stop_and_remove_bus_device_locked+0x28/0x48 remove_store+0xa0/0xb8 dev_attr_store+0x20/0x40 sysfs_kf_write+0x4c/0x68 Link: https://lore.kernel.org/r/20241126210443.4052876-1-briannorris@chromium.org Fixes: 681725afb6b9 ("PCI/pwrctl: Remove pwrctl device without iterating over all children of pwrctl parent") Reported-by: Saurabh Sengar <ssengar@linux.microsoft.com> Closes: https://lore.kernel.org/r/1732890621-19656-1-git-send-email-ssengar@linux.microsoft.com Signed-off-by: Brian Norris <briannorris@chromium.org> [bhelgaas: commit log] Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
-rw-r--r--drivers/pci/remove.c9
1 files changed, 7 insertions, 2 deletions
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index 963b8d2855c1..efc37fcb73e2 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -19,14 +19,19 @@ static void pci_free_resources(struct pci_dev *dev)
static void pci_pwrctrl_unregister(struct device *dev)
{
+ struct device_node *np;
struct platform_device *pdev;
- pdev = of_find_device_by_node(dev_of_node(dev));
+ np = dev_of_node(dev);
+ if (!np)
+ return;
+
+ pdev = of_find_device_by_node(np);
if (!pdev)
return;
of_device_unregister(pdev);
- of_node_clear_flag(dev_of_node(dev), OF_POPULATED);
+ of_node_clear_flag(np, OF_POPULATED);
}
static void pci_stop_dev(struct pci_dev *dev)