[PATCH 12/48] staging: etnaviv: avoid lockdep circular dependency warning
Lucas Stach
l.stach at pengutronix.de
Fri Sep 25 04:57:24 PDT 2015
From: Russell King <rmk+kernel at arm.linux.org.uk>
Avoid the below circular dependency warning by avoiding calling
pm_runtime_get_sync() at a point where struct_mutex is held, and we
may well provoke the power domain to be resumed.
We avoid this simply by referencing the runtime PM in the
etnaviv_gem_submit() function just before we take the lock, and
drop it after we drop the lock. We leave the rest of the runtime
PM management in place - the runtime PM inside etnaviv_gpu_submit()
won't trigger a resume as the resume will have been done by
etnaviv_gem_submit() outside of the lock.
======================================================
[ INFO: possible circular locking dependency detected ]
4.1.0-rc2+ #1618 Not tainted
-------------------------------------------------------
bash/1261 is trying to acquire lock:
(&mm->mmap_sem){++++++}, at: [<c010d9bc>] might_fault+0x44/0x98
but task is already holding lock:
(&sb->s_type->i_mutex_key#3){+.+.+.}, at: [<c013f940>] iterate_dir+0x3c/0x108
which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #6 (&sb->s_type->i_mutex_key#3){+.+.+.}:
[<c075c084>] mutex_lock_nested+0x5c/0x3dc
[<c02c4204>] start_creating+0x58/0xb4
[<c02c4330>] debugfs_create_dir+0x14/0xcc
[<c035ba3c>] create_regulator+0xcc/0x1fc
[<c035db54>] _regulator_get+0x10c/0x208
[<c035dc68>] regulator_get_optional+0x18/0x1c
[<c035ecc0>] _devm_regulator_get+0x84/0xc0
[<c035ed10>] devm_regulator_get_optional+0x14/0x18
[<c0026594>] imx_gpc_probe+0x20/0x110
[<c03cfb3c>] platform_drv_probe+0x50/0xac
[<c03ce230>] driver_probe_device+0x1d4/0x278
[<c03ce370>] __driver_attach+0x9c/0xa0
[<c03cc794>] bus_for_each_dev+0x5c/0x90
[<c03cdc50>] driver_attach+0x24/0x28
[<c03cd8a4>] bus_add_driver+0xe0/0x1dc
[<c03ceb9c>] driver_register+0x80/0xfc
[<c03cf9c4>] __platform_driver_register+0x50/0x64
[<c09f2948>] imx_pgc_init+0x18/0x20
[<c00097bc>] do_one_initcall+0x88/0x1dc
[<c09e9e4c>] kernel_init_freeable+0x114/0x1e0
[<c0751720>] kernel_init+0x10/0xec
[<c000fa78>] ret_from_fork+0x14/0x3c
-> #5 (&rdev->mutex){+.+.+.}:
[<c075c084>] mutex_lock_nested+0x5c/0x3dc
[<c035d830>] regulator_enable+0x48/0x12c
[<c0026230>] imx6q_pm_pu_power_on+0x20/0x170
[<c03ddc48>] genpd_power_on+0x38/0xf4
[<c03df0e4>] __pm_genpd_poweron+0x1a4/0x1b4
[<c03def2c>] pm_genpd_poweron+0x28/0x3c
[<c03df64c>] genpd_dev_pm_attach+0xb4/0x12c
[<c03d54d0>] dev_pm_domain_attach+0x10/0x14
[<c03cfb20>] platform_drv_probe+0x34/0xac
[<c03ce230>] driver_probe_device+0x1d4/0x278
[<c03ce370>] __driver_attach+0x9c/0xa0
[<c03cc794>] bus_for_each_dev+0x5c/0x90
[<c03cdc50>] driver_attach+0x24/0x28
[<c03cd8a4>] bus_add_driver+0xe0/0x1dc
[<c03ceb9c>] driver_register+0x80/0xfc
[<c03cf9c4>] __platform_driver_register+0x50/0x64
[<c0a2701c>] etnaviv_init+0x18/0x4c
[<c00097bc>] do_one_initcall+0x88/0x1dc
[<c09e9e4c>] kernel_init_freeable+0x114/0x1e0
[<c0751720>] kernel_init+0x10/0xec
[<c000fa78>] ret_from_fork+0x14/0x3c
-> #4 (&genpd->lock){+.+...}:
[<c075c084>] mutex_lock_nested+0x5c/0x3dc
[<c03df18c>] pm_genpd_runtime_resume+0x98/0x230
[<c03d6828>] __rpm_callback+0x3c/0x78
[<c03d6894>] rpm_callback+0x30/0x90
[<c03d78d4>] rpm_resume+0x3c4/0x5a4
[<c03d7d50>] __pm_runtime_resume+0x70/0x90
[<c053de14>] etnaviv_gpu_submit+0x30/0x220
[<c053c1c8>] etnaviv_ioctl_gem_submit+0x9a4/0xaf0
[<c03a01c4>] drm_ioctl+0x2a4/0x4e4
[<c013efb4>] do_vfs_ioctl+0x84/0x66c
[<c013f5d8>] SyS_ioctl+0x3c/0x60
[<c000f9a0>] ret_fast_syscall+0x0/0x54
-> #3 (reservation_ww_class_mutex){+.+.+.}:
[<c075c468>] __ww_mutex_lock_interruptible+0x64/0x6f8
[<c053bca0>] etnaviv_ioctl_gem_submit+0x47c/0xaf0
[<c03a01c4>] drm_ioctl+0x2a4/0x4e4
[<c013efb4>] do_vfs_ioctl+0x84/0x66c
[<c013f5d8>] SyS_ioctl+0x3c/0x60
[<c000f9a0>] ret_fast_syscall+0x0/0x54
-> #2 (reservation_ww_class_acquire){+.+.+.}:
[<c053b990>] etnaviv_ioctl_gem_submit+0x16c/0xaf0
[<c03a01c4>] drm_ioctl+0x2a4/0x4e4
[<c013efb4>] do_vfs_ioctl+0x84/0x66c
[<c013f5d8>] SyS_ioctl+0x3c/0x60
[<c000f9a0>] ret_fast_syscall+0x0/0x54
-> #1 (&dev->struct_mutex){+.+.+.}:
[<c075c084>] mutex_lock_nested+0x5c/0x3dc
[<c039ea20>] drm_gem_mmap+0x3c/0xe0
[<c03bb724>] drm_gem_cma_mmap+0x14/0x2c
[<c0115bf4>] mmap_region+0x3d0/0x6a4
[<c01161ac>] do_mmap_pgoff+0x2e4/0x374
[<c0101764>] vm_mmap_pgoff+0x6c/0x9c
[<c011475c>] SyS_mmap_pgoff+0x94/0xb8
[<c000f9a0>] ret_fast_syscall+0x0/0x54
-> #0 (&mm->mmap_sem){++++++}:
[<c006d880>] lock_acquire+0xa0/0x120
[<c010d9dc>] might_fault+0x64/0x98
[<c013f670>] filldir64+0x74/0x184
[<c01555f4>] dcache_readdir+0x1c8/0x28c
[<c013f98c>] iterate_dir+0x88/0x108
[<c013fb7c>] SyS_getdents64+0x7c/0xec
[<c000f9a0>] ret_fast_syscall+0x0/0x54
other info that might help us debug this:
Chain exists of:
&mm->mmap_sem --> &rdev->mutex --> &sb->s_type->i_mutex_key#3
Possible unsafe locking scenario:
CPU0 CPU1
---- ----
lock(&sb->s_type->i_mutex_key#3);
lock(&rdev->mutex);
lock(&sb->s_type->i_mutex_key#3);
lock(&mm->mmap_sem);
*** DEADLOCK ***
1 lock held by bash/1261:
#0: (&sb->s_type->i_mutex_key#3){+.+.+.}, at: [<c013f940>] iterate_dir+0x3c/0x108
stack backtrace:
CPU: 0 PID: 1261 Comm: bash Not tainted 4.1.0-rc2+ #1618
Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree)
Backtrace:
[<c00130c8>] (dump_backtrace) from [<c0013264>] (show_stack+0x18/0x1c)
r6:c0c27a28 r5:c0c26d68 r4:00000000 r3:00000000
[<c001324c>] (show_stack) from [<c0757dd4>] (dump_stack+0x7c/0x98)
[<c0757d58>] (dump_stack) from [<c0754e90>] (print_circular_bug+0x28c/0x2e0)
r4:c0c39e58 r3:ecd43200
[<c0754c04>] (print_circular_bug) from [<c006d024>] (__lock_acquire+0x1944/0x1c34)
r10:c14aee7c r8:ecd436a0 r7:ecd43200 r6:c149d2dc r5:00000001 r4:ecd436b8
[<c006b6e0>] (__lock_acquire) from [<c006d880>] (lock_acquire+0xa0/0x120)
r10:c0aeab14 r9:60070013 r8:00000001 r7:00000000 r6:00000000 r5:ed244e88
r4:00000000
[<c006d7e0>] (lock_acquire) from [<c010d9dc>] (might_fault+0x64/0x98)
r10:edc02508 r9:00000000 r8:0000001c r7:00000018 r6:00000001 r5:00d16028
r4:00000000
[<c010d978>] (might_fault) from [<c013f670>] (filldir64+0x74/0x184)
r4:eb935f60
[<c013f5fc>] (filldir64) from [<c01555f4>] (dcache_readdir+0x1c8/0x28c)
r10:edc02508 r9:eb935f60 r8:e6d1b09c r7:e8074780 r6:e6d1b000 r5:eb935f60
r4:edc02508
[<c015542c>] (dcache_readdir) from [<c013f98c>] (iterate_dir+0x88/0x108)
r10:edc044b8 r9:eb935f60 r8:e8074788 r7:00000000 r6:e8074780 r5:00000000
r4:00000000
[<c013f904>] (iterate_dir) from [<c013fb7c>] (SyS_getdents64+0x7c/0xec)
r10:00000000 r9:eb934000 r8:e8074780 r7:e8074780 r6:00008000 r5:00d16008
r4:000a6b4c
[<c013fb00>] (SyS_getdents64) from [<c000f9a0>] (ret_fast_syscall+0x0/0x54)
r10:00000000 r8:c000fb84 r7:000000d9 r6:00d1600c r5:00d16008 r4:000a6b4c
Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
---
drivers/staging/etnaviv/etnaviv_gem_submit.c | 23 +++++++++++++++++++++++
drivers/staging/etnaviv/etnaviv_gpu.c | 25 ++++++++++++++++++++++++-
drivers/staging/etnaviv/etnaviv_gpu.h | 2 ++
3 files changed, 49 insertions(+), 1 deletion(-)
diff --git a/drivers/staging/etnaviv/etnaviv_gem_submit.c b/drivers/staging/etnaviv/etnaviv_gem_submit.c
index eb3c5bd68418..647d16fee98b 100644
--- a/drivers/staging/etnaviv/etnaviv_gem_submit.c
+++ b/drivers/staging/etnaviv/etnaviv_gem_submit.c
@@ -313,6 +313,27 @@ int etnaviv_ioctl_gem_submit(struct drm_device *dev, void *data,
if (args->nr_cmds > MAX_CMDS)
return -EINVAL;
+ /*
+ * Avoid big circular locking dependency loops:
+ * - reading debugfs results in mmap_sem depending on i_mutex_key#3
+ * (iterate_dir -> filldir64)
+ * - struct_mutex depends on mmap_sem
+ * (vm_mmap_pgoff -> drm_gem_mmap)
+ * then if we try to do a get_sync() under struct_mutex,
+ * - genpd->lock depends on struct_mutex
+ * (etnaviv_ioctl_gem_submit -> pm_genpd_runtime_resume)
+ * - (regulator) rdev->mutex depends on genpd->lock
+ * (pm_genpd_poweron -> regulator_enable)
+ * - i_mutex_key#3 depends on rdev->mutex
+ * (create_regulator -> debugfs::start_creating)
+ * and lockdep rightfully explodes.
+ *
+ * Avoid this by getting runtime PM outside of the struct_mutex lock.
+ */
+ ret = etnaviv_gpu_pm_get_sync(gpu);
+ if (ret < 0)
+ return ret;
+
mutex_lock(&dev->struct_mutex);
submit = submit_create(dev, gpu, args->nr_bos);
@@ -412,6 +433,8 @@ out:
submit_cleanup(submit, !!ret);
mutex_unlock(&dev->struct_mutex);
+ etnaviv_gpu_pm_put(gpu);
+
/*
* If we're returning -EAGAIN, it could be due to the userptr code
* wanting to run its workqueue outside of the struct_mutex.
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.c b/drivers/staging/etnaviv/etnaviv_gpu.c
index dd885870cc7c..570bcd60025b 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.c
+++ b/drivers/staging/etnaviv/etnaviv_gpu.c
@@ -857,6 +857,17 @@ void etnaviv_gpu_retire(struct etnaviv_gpu *gpu)
queue_work(priv->wq, &gpu->retire_work);
}
+int etnaviv_gpu_pm_get_sync(struct etnaviv_gpu *gpu)
+{
+ return pm_runtime_get_sync(gpu->dev);
+}
+
+void etnaviv_gpu_pm_put(struct etnaviv_gpu *gpu)
+{
+ pm_runtime_mark_last_busy(gpu->dev);
+ pm_runtime_put_autosuspend(gpu->dev);
+}
+
/* add bo's to gpu's ring, and kick gpu: */
int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
struct etnaviv_gem_submit *submit, struct etnaviv_file_private *ctx)
@@ -1234,15 +1245,27 @@ static int etnaviv_gpu_rpm_suspend(struct device *dev)
static int etnaviv_gpu_rpm_resume(struct device *dev)
{
struct etnaviv_gpu *gpu = dev_get_drvdata(dev);
+ struct drm_device *drm = gpu->drm;
int ret;
+ /* We must never runtime-PM resume holding struct_mutex */
+ if (drm && WARN_ON_ONCE(mutex_is_locked(&drm->struct_mutex)))
+ return -EDEADLK;
+
ret = etnaviv_gpu_resume(gpu);
if (ret)
return ret;
/* Re-initialise the basic hardware state */
- if (gpu->drm && gpu->buffer)
+ if (drm && gpu->buffer) {
+ ret = mutex_lock_killable(&drm->struct_mutex);
+ if (ret) {
+ etnaviv_gpu_suspend(gpu);
+ return ret;
+ }
etnaviv_gpu_hw_init(gpu);
+ mutex_unlock(&drm->struct_mutex);
+ }
return 0;
}
diff --git a/drivers/staging/etnaviv/etnaviv_gpu.h b/drivers/staging/etnaviv/etnaviv_gpu.h
index e0068cc7b7d6..21e45e875e73 100644
--- a/drivers/staging/etnaviv/etnaviv_gpu.h
+++ b/drivers/staging/etnaviv/etnaviv_gpu.h
@@ -152,6 +152,8 @@ void etnaviv_gpu_debugfs(struct etnaviv_gpu *gpu, struct seq_file *m);
void etnaviv_gpu_retire(struct etnaviv_gpu *gpu);
int etnaviv_gpu_submit(struct etnaviv_gpu *gpu,
struct etnaviv_gem_submit *submit, struct etnaviv_file_private *ctx);
+int etnaviv_gpu_pm_get_sync(struct etnaviv_gpu *gpu);
+void etnaviv_gpu_pm_put(struct etnaviv_gpu *gpu);
extern struct platform_driver etnaviv_gpu_driver;
--
2.5.1
More information about the dri-devel
mailing list