Skip to content

Commit b458ff7

Browse files
Mani-Sadhasivamkwilczynski
authored andcommitted
PCI/pwrctl: Ensure that pwrctl drivers are probed before PCI client drivers
As per the kernel device driver model, a pwrctl device is the supplier for the PCI device, but the device link that enforces the supplier-consumer relationship was previously created by the pwrctl driver. Therefore, the driver model didn't prevent probing PCI client drivers before probing the corresponding pwrctl drivers. This may lead to a race condition if the PCI device was already powered on by the bootloader (before the pwrctl driver). If the bootloader did not power on the PCI device, this wouldn't create any problem as the pwrctl driver will be the one powering on the device, so the PCI client driver always gets probed afterward. But if the device was already powered on, then the device will be seen by the PCI core and the PCI client driver may get probed before its pwrctl driver. This creates a race condition as the pwrctl driver may change the device power state while the device is being accessed by the client driver. One such issue was already reported on the Qcom X13s platform with the WLAN device and fixed with a hack in the WCN pwrseq driver by a9aaf1f ("power: sequencing: request the WLAN enable GPIO as-is"). A cleaner way to fix the above mentioned race condition is to ensure that the pwrctl drivers are always probed before the client drivers. If the PCI device is associated with a pwrctl platform device with a power supply, add a device link between the PCI device and the pwrctl device before device_attach() in pci_bus_add_device(). Note that there is no need to explicitly remove the device link as that will be taken care of by the driver core when the PCI device gets removed. Fixes: 4565d26 ("PCI/pwrctl: Add PCI power control core code") Fixes: 8fb1861 ("PCI/pwrctl: Create platform devices for child OF nodes of the port node") Link: https://lore.kernel.org/r/[email protected] Tested-by: Bartosz Golaszewski <[email protected]> Tested-by: Krishna chaitanya chundru <[email protected]> Signed-off-by: Manivannan Sadhasivam <[email protected]> [bhelgaas: squash fix from https://lore.kernel.org/r/[email protected] for SPARCv9 issue reported by Jonathan Currier <[email protected]>] Signed-off-by: Bjorn Helgaas <[email protected]> [kwilczynski: wrap code to 80 columns] Signed-off-by: Krzysztof Wilczyński <[email protected]> Reviewed-by: Bartosz Golaszewski <[email protected]> Cc: [email protected] # Depends on power supply check
1 parent 278dd09 commit b458ff7

File tree

2 files changed

+56
-29
lines changed

2 files changed

+56
-29
lines changed

drivers/pci/bus.c

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,46 @@ void __weak pcibios_resource_survey_bus(struct pci_bus *bus) { }
321321

322322
void __weak pcibios_bus_add_device(struct pci_dev *pdev) { }
323323

324+
/*
325+
* Create pwrctl devices (if required) for the PCI devices to handle the power
326+
* state.
327+
*/
328+
static void pci_pwrctl_create_devices(struct pci_dev *dev)
329+
{
330+
struct device_node *np = dev_of_node(&dev->dev);
331+
struct device *parent = &dev->dev;
332+
struct platform_device *pdev;
333+
334+
/*
335+
* First ensure that we are starting from a PCI bridge and it has a
336+
* corresponding devicetree node.
337+
*/
338+
if (np && pci_is_bridge(dev)) {
339+
/*
340+
* Now look for the child PCI device nodes and create pwrctl
341+
* devices for them. The pwrctl device drivers will manage the
342+
* power state of the devices.
343+
*/
344+
for_each_available_child_of_node_scoped(np, child) {
345+
/*
346+
* First check whether the pwrctl device really needs to
347+
* be created or not. This is decided based on at least
348+
* one of the power supplies being defined in the
349+
* devicetree node of the device.
350+
*/
351+
if (!of_pci_supply_present(child)) {
352+
pci_dbg(dev, "skipping OF node: %s\n", child->name);
353+
return;
354+
}
355+
356+
/* Now create the pwrctl device */
357+
pdev = of_platform_device_create(child, NULL, parent);
358+
if (!pdev)
359+
pci_err(dev, "failed to create OF node: %s\n", child->name);
360+
}
361+
}
362+
}
363+
324364
/**
325365
* pci_bus_add_device - start driver for a single device
326366
* @dev: device to add
@@ -345,31 +385,28 @@ void pci_bus_add_device(struct pci_dev *dev)
345385
pci_proc_attach_device(dev);
346386
pci_bridge_d3_update(dev);
347387

388+
pci_pwrctl_create_devices(dev);
389+
390+
/*
391+
* If the PCI device is associated with a pwrctl device with a
392+
* power supply, create a device link between the PCI device and
393+
* pwrctl device. This ensures that pwrctl drivers are probed
394+
* before PCI client drivers.
395+
*/
396+
pdev = of_find_device_by_node(dn);
397+
if (pdev && of_pci_supply_present(dn)) {
398+
if (!device_link_add(&dev->dev, &pdev->dev,
399+
DL_FLAG_AUTOREMOVE_CONSUMER))
400+
pci_err(dev, "failed to add device link to power control device %s\n",
401+
pdev->name);
402+
}
403+
348404
dev->match_driver = !dn || of_device_is_available(dn);
349405
retval = device_attach(&dev->dev);
350406
if (retval < 0 && retval != -EPROBE_DEFER)
351407
pci_warn(dev, "device attach failed (%d)\n", retval);
352408

353409
pci_dev_assign_added(dev, true);
354-
355-
if (dev_of_node(&dev->dev) && pci_is_bridge(dev)) {
356-
for_each_available_child_of_node_scoped(dn, child) {
357-
/*
358-
* First check whether the pwrctl device needs to be
359-
* created or not. This is decided based on at least
360-
* one of the power supplies being defined in the
361-
* devicetree node of the device.
362-
*/
363-
if (!of_pci_supply_present(child)) {
364-
pci_dbg(dev, "skipping OF node: %s\n", child->name);
365-
continue;
366-
}
367-
368-
pdev = of_platform_device_create(child, NULL, &dev->dev);
369-
if (!pdev)
370-
pci_err(dev, "failed to create OF node: %s\n", child->name);
371-
}
372-
}
373410
}
374411
EXPORT_SYMBOL_GPL(pci_bus_add_device);
375412

drivers/pci/pwrctl/core.c

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,6 @@ static int pci_pwrctl_notify(struct notifier_block *nb, unsigned long action,
3333
*/
3434
dev->of_node_reused = true;
3535
break;
36-
case BUS_NOTIFY_BOUND_DRIVER:
37-
pwrctl->link = device_link_add(dev, pwrctl->dev,
38-
DL_FLAG_AUTOREMOVE_CONSUMER);
39-
if (!pwrctl->link)
40-
dev_err(pwrctl->dev, "Failed to add device link\n");
41-
break;
42-
case BUS_NOTIFY_UNBOUND_DRIVER:
43-
if (pwrctl->link)
44-
device_link_remove(dev, pwrctl->dev);
45-
break;
4636
}
4737

4838
return NOTIFY_DONE;

0 commit comments

Comments
 (0)