device_pm_check_callbacks() uses dev->power spinlock, which on PREEMPT_RT sleeps. However some PM domains on PREEMPT_RT might be using raw spinlocks as genpd_lock(), thus dev_pm_domain_set() must not call device_pm_check_callbacks(). In fact device_pm_check_callbacks() is not strictly related to dev_pm_domain_set() and calls for these two can be made separately. Add new helper dev_pm_domain_set_no_cb() which will only set PM domain but will not check the callbacks, leaving the checl to the caller. Cc: Adrien Thierry <athierry@xxxxxxxxxx> Cc: Brian Masney <bmasney@xxxxxxxxxx> Cc: linux-rt-users@xxxxxxxxxxxxxxx Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@xxxxxxxxxx> --- drivers/base/power/common.c | 27 +++++++++++++++++++++++++-- include/linux/pm_domain.h | 3 +++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c index 72115917e0bd..f81cab6990ad 100644 --- a/drivers/base/power/common.c +++ b/drivers/base/power/common.c @@ -218,6 +218,30 @@ EXPORT_SYMBOL_GPL(dev_pm_domain_start); * This function must be called with the device lock held. */ void dev_pm_domain_set(struct device *dev, struct dev_pm_domain *pd) +{ + if (dev->pm_domain == pd) + return; + + dev_pm_domain_set_no_cb(dev, pd); + device_pm_check_callbacks(dev); +} +EXPORT_SYMBOL_GPL(dev_pm_domain_set); + +/** + * dev_pm_domain_set_no_cb - Set PM domain of a device. + * @dev: Device whose PM domain is to be set. + * @pd: PM domain to be set, or NULL. + * + * Sets the PM domain the device belongs to. The PM domain of a device needs + * to be set before its probe finishes (it's bound to a driver). + * + * This is exactly like dev_pm_domain_set(), however device_pm_check_callbacks() + * is not called and the caller is responsible to invoke + * device_pm_check_callbacks() with device lock held. + * + * This function must be called with the device lock held. + */ +void dev_pm_domain_set_no_cb(struct device *dev, struct dev_pm_domain *pd) { if (dev->pm_domain == pd) return; @@ -225,6 +249,5 @@ void dev_pm_domain_set(struct device *dev, struct dev_pm_domain *pd) WARN(pd && device_is_bound(dev), "PM domains can only be changed for unbound devices\n"); dev->pm_domain = pd; - device_pm_check_callbacks(dev); } -EXPORT_SYMBOL_GPL(dev_pm_domain_set); +EXPORT_SYMBOL_GPL(dev_pm_domain_set_no_cb); diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h index 0a1600244963..352d0c76bfec 100644 --- a/include/linux/pm_domain.h +++ b/include/linux/pm_domain.h @@ -438,6 +438,7 @@ struct device *dev_pm_domain_attach_by_name(struct device *dev, void dev_pm_domain_detach(struct device *dev, bool power_off); int dev_pm_domain_start(struct device *dev); void dev_pm_domain_set(struct device *dev, struct dev_pm_domain *pd); +void dev_pm_domain_set_no_cb(struct device *dev, struct dev_pm_domain *pd); #else static inline int dev_pm_domain_attach(struct device *dev, bool power_on) { @@ -460,6 +461,8 @@ static inline int dev_pm_domain_start(struct device *dev) } static inline void dev_pm_domain_set(struct device *dev, struct dev_pm_domain *pd) {} +static inline void dev_pm_domain_set_no_cb(struct device *dev, + struct dev_pm_domain *pd) {} #endif #endif /* _LINUX_PM_DOMAIN_H */ -- 2.34.1