From 0106ef5146f9e89e4dc9354f308ecaddb9617310 Mon Sep 17 00:00:00 2001 From: Marek Szyprowski Date: Wed, 20 Jan 2016 10:13:42 +0100 Subject: PM / domains: fix lockdep issue for all subdomains During genpd_poweron, genpd->lock is acquired recursively for each parent (master) domain, which are separate objects. This confuses lockdep, which considers every operation on genpd->lock as being done on the same lock class. This leads to the following false positive warning: ============================================= [ INFO: possible recursive locking detected ] 4.4.0-rc4-xu3s #32 Not tainted --------------------------------------------- swapper/0/1 is trying to acquire lock: (&genpd->lock){+.+...}, at: [] __genpd_poweron+0x64/0x108 but task is already holding lock: (&genpd->lock){+.+...}, at: [] genpd_dev_pm_attach+0x168/0x1b8 other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock(&genpd->lock); lock(&genpd->lock); *** DEADLOCK *** May be due to missing lock nesting notation 3 locks held by swapper/0/1: #0: (&dev->mutex){......}, at: [] __driver_attach+0x48/0x98 #1: (&dev->mutex){......}, at: [] __driver_attach+0x58/0x98 #2: (&genpd->lock){+.+...}, at: [] genpd_dev_pm_attach+0x168/0x1b8 stack backtrace: CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.4.0-rc4-xu3s #32 Hardware name: SAMSUNG EXYNOS (Flattened Device Tree) [] (unwind_backtrace) from [] (show_stack+0x10/0x14) [] (show_stack) from [] (dump_stack+0x84/0xc4) [] (dump_stack) from [] (__lock_acquire+0x1f88/0x215c) [] (__lock_acquire) from [] (lock_acquire+0xa4/0xd0) [] (lock_acquire) from [] (mutex_lock_nested+0x70/0x4d4) [] (mutex_lock_nested) from [] (__genpd_poweron+0x64/0x108) [] (__genpd_poweron) from [] (genpd_dev_pm_attach+0x170/0x1b8) [] (genpd_dev_pm_attach) from [] (platform_drv_probe+0x2c/0xac) [] (platform_drv_probe) from [] (driver_probe_device+0x208/0x2fc) [] (driver_probe_device) from [] (__driver_attach+0x94/0x98) [] (__driver_attach) from [] (bus_for_each_dev+0x68/0x9c) [] (bus_for_each_dev) from [] (bus_add_driver+0x1a0/0x218) [] (bus_add_driver) from [] (driver_register+0x78/0xf8) [] (driver_register) from [] (exynos_drm_register_drivers+0x28/0x74) [] (exynos_drm_register_drivers) from [] (exynos_drm_init+0x6c/0xc4) [] (exynos_drm_init) from [] (do_one_initcall+0x90/0x1dc) [] (do_one_initcall) from [] (kernel_init_freeable+0x158/0x1f8) [] (kernel_init_freeable) from [] (kernel_init+0x8/0xe8) [] (kernel_init) from [] (ret_from_fork+0x14/0x24) This patch replaces mutex_lock with mutex_lock_nested() and uses recursion depth to annotate each genpd->lock operation with separate lockdep subclass. Reported-by: Anand Moon Signed-off-by: Marek Szyprowski Tested-by: Anand Moon Tested-by: Tobias Jakobi Acked-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index b80379012840..e02ddf65bc43 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -170,16 +170,15 @@ static void genpd_queue_power_off_work(struct generic_pm_domain *genpd) queue_work(pm_wq, &genpd->power_off_work); } -static int genpd_poweron(struct generic_pm_domain *genpd); - /** * __genpd_poweron - Restore power to a given PM domain and its masters. * @genpd: PM domain to power up. + * @depth: nesting count for lockdep. * * Restore power to @genpd and all of its masters so that it is possible to * resume a device belonging to it. */ -static int __genpd_poweron(struct generic_pm_domain *genpd) +static int __genpd_poweron(struct generic_pm_domain *genpd, unsigned int depth) { struct gpd_link *link; int ret = 0; @@ -194,11 +193,16 @@ static int __genpd_poweron(struct generic_pm_domain *genpd) * with it. */ list_for_each_entry(link, &genpd->slave_links, slave_node) { - genpd_sd_counter_inc(link->master); + struct generic_pm_domain *master = link->master; + + genpd_sd_counter_inc(master); + + mutex_lock_nested(&master->lock, depth + 1); + ret = __genpd_poweron(master, depth + 1); + mutex_unlock(&master->lock); - ret = genpd_poweron(link->master); if (ret) { - genpd_sd_counter_dec(link->master); + genpd_sd_counter_dec(master); goto err; } } @@ -230,11 +234,12 @@ static int genpd_poweron(struct generic_pm_domain *genpd) int ret; mutex_lock(&genpd->lock); - ret = __genpd_poweron(genpd); + ret = __genpd_poweron(genpd, 0); mutex_unlock(&genpd->lock); return ret; } + static int genpd_save_dev(struct generic_pm_domain *genpd, struct device *dev) { return GENPD_DEV_CALLBACK(genpd, int, save_state, dev); @@ -482,7 +487,7 @@ static int pm_genpd_runtime_resume(struct device *dev) } mutex_lock(&genpd->lock); - ret = __genpd_poweron(genpd); + ret = __genpd_poweron(genpd, 0); mutex_unlock(&genpd->lock); if (ret) -- cgit v1.2.3 From cdb300a041f5df1dfbde1367f95109b6449d1371 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 27 Jan 2016 09:10:37 +0100 Subject: PM / Domains: Fix potential deadlock while adding/removing subdomains We must preserve the same order of how we acquire and release the lock for genpd, as otherwise we may encounter deadlocks. The power on phase of a genpd starts by acquiring its lock. Then it walks the hierarchy of its parent domains to be able to power on these first, as per design of genpd. From a locking perspective this means the locks of the parents becomes acquired after the lock of the subdomain. Let's fix pm_genpd_add|remove_subdomain() to maintain the same order of acquiring/releasing the genpd lock as being applied in the power on/off sequence. Signed-off-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index e02ddf65bc43..2ff818c2ae6b 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1340,8 +1340,8 @@ int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, if (!link) return -ENOMEM; - mutex_lock(&genpd->lock); - mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING); + mutex_lock(&subdomain->lock); + mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); if (genpd->status == GPD_STATE_POWER_OFF && subdomain->status != GPD_STATE_POWER_OFF) { @@ -1364,8 +1364,8 @@ int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, genpd_sd_counter_inc(genpd); out: - mutex_unlock(&subdomain->lock); mutex_unlock(&genpd->lock); + mutex_unlock(&subdomain->lock); if (ret) kfree(link); return ret; @@ -1386,7 +1386,8 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(subdomain)) return -EINVAL; - mutex_lock(&genpd->lock); + mutex_lock(&subdomain->lock); + mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING); if (!list_empty(&subdomain->slave_links) || subdomain->device_count) { pr_warn("%s: unable to remove subdomain %s\n", genpd->name, @@ -1399,22 +1400,19 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, if (link->slave != subdomain) continue; - mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING); - list_del(&link->master_node); list_del(&link->slave_node); kfree(link); if (subdomain->status != GPD_STATE_POWER_OFF) genpd_sd_counter_dec(genpd); - mutex_unlock(&subdomain->lock); - ret = 0; break; } out: mutex_unlock(&genpd->lock); + mutex_unlock(&subdomain->lock); return ret; } -- cgit v1.2.3 From a3d09c73492e57a1189e410f67e4d2115b23a3a8 Mon Sep 17 00:00:00 2001 From: Moritz Fischer Date: Wed, 27 Jan 2016 08:29:27 +0100 Subject: PM / Domains: Fix typo in comment Acked-by: Kevin Hilman Signed-off-by: Moritz Fischer Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 2ff818c2ae6b..014024a2fee9 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -160,7 +160,7 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool timed) /** * genpd_queue_power_off_work - Queue up the execution of genpd_poweroff(). - * @genpd: PM domait to power off. + * @genpd: PM domain to power off. * * Queue up the execution of genpd_poweroff() unless it's already been done * before. -- cgit v1.2.3