Skip to content

Commit 8f62819

Browse files
Bartosz Golaszewskibjorn-helgaas
authored andcommitted
PCI/pwrctl: Rescan bus on a separate thread
If we trigger the bus rescan from sysfs, we'll try to lock the PCI rescan mutex recursively and deadlock - the platform device will be populated and probed on the same thread that handles the sysfs write. Add a workqueue to the pwrctl code on which we schedule the rescan for controlled PCI devices. While at it: add a new interface for initializing the pwrctl context where we'd now assign the parent device address and initialize the workqueue. Link: https://lore.kernel.org/r/[email protected] Fixes: 4565d26 ("PCI/pwrctl: Add PCI power control core code") Reported-by: Konrad Dybcio <[email protected]> Signed-off-by: Bartosz Golaszewski <[email protected]> Signed-off-by: Bjorn Helgaas <[email protected]> Reviewed-by: Manivannan Sadhasivam <[email protected]>
1 parent f153658 commit 8f62819

File tree

3 files changed

+27
-4
lines changed

3 files changed

+27
-4
lines changed

drivers/pci/pwrctl/core.c

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,28 @@ static int pci_pwrctl_notify(struct notifier_block *nb, unsigned long action,
4848
return NOTIFY_DONE;
4949
}
5050

51+
static void rescan_work_func(struct work_struct *work)
52+
{
53+
struct pci_pwrctl *pwrctl = container_of(work, struct pci_pwrctl, work);
54+
55+
pci_lock_rescan_remove();
56+
pci_rescan_bus(to_pci_dev(pwrctl->dev->parent)->bus);
57+
pci_unlock_rescan_remove();
58+
}
59+
60+
/**
61+
* pci_pwrctl_init() - Initialize the PCI power control context struct
62+
*
63+
* @pwrctl: PCI power control data
64+
* @dev: Parent device
65+
*/
66+
void pci_pwrctl_init(struct pci_pwrctl *pwrctl, struct device *dev)
67+
{
68+
pwrctl->dev = dev;
69+
INIT_WORK(&pwrctl->work, rescan_work_func);
70+
}
71+
EXPORT_SYMBOL_GPL(pci_pwrctl_init);
72+
5173
/**
5274
* pci_pwrctl_device_set_ready() - Notify the pwrctl subsystem that the PCI
5375
* device is powered-up and ready to be detected.
@@ -74,9 +96,7 @@ int pci_pwrctl_device_set_ready(struct pci_pwrctl *pwrctl)
7496
if (ret)
7597
return ret;
7698

77-
pci_lock_rescan_remove();
78-
pci_rescan_bus(to_pci_dev(pwrctl->dev->parent)->bus);
79-
pci_unlock_rescan_remove();
99+
schedule_work(&pwrctl->work);
80100

81101
return 0;
82102
}

drivers/pci/pwrctl/pci-pwrctl-pwrseq.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ static int pci_pwrctl_pwrseq_probe(struct platform_device *pdev)
5050
if (ret)
5151
return ret;
5252

53-
data->ctx.dev = dev;
53+
pci_pwrctl_init(&data->ctx, dev);
5454

5555
ret = devm_pci_pwrctl_device_set_ready(dev, &data->ctx);
5656
if (ret)

include/linux/pci-pwrctl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#define __PCI_PWRCTL_H__
88

99
#include <linux/notifier.h>
10+
#include <linux/workqueue.h>
1011

1112
struct device;
1213
struct device_link;
@@ -41,8 +42,10 @@ struct pci_pwrctl {
4142
/* Private: don't use. */
4243
struct notifier_block nb;
4344
struct device_link *link;
45+
struct work_struct work;
4446
};
4547

48+
void pci_pwrctl_init(struct pci_pwrctl *pwrctl, struct device *dev);
4649
int pci_pwrctl_device_set_ready(struct pci_pwrctl *pwrctl);
4750
void pci_pwrctl_device_unset_ready(struct pci_pwrctl *pwrctl);
4851
int devm_pci_pwrctl_device_set_ready(struct device *dev,

0 commit comments

Comments
 (0)