Skip to content

Commit 211fb32

Browse files
robclarkintel-lab-lkp
authored andcommitted
PM / QoS: Fix constraints alloc vs reclaim locking
In the process of adding lockdep annotation for drm GPU scheduler's job_run() to detect potential deadlock against shrinker/reclaim, I hit this lockdep splat: ====================================================== WARNING: possible circular locking dependency detected 6.2.0-rc8-debug+ torvalds#558 Tainted: G W ------------------------------------------------------ ring0/125 is trying to acquire lock: ffffffd6d6ce0f28 (dev_pm_qos_mtx){+.+.}-{3:3}, at: dev_pm_qos_update_request+0x38/0x68 but task is already holding lock: ffffff8087239208 (&gpu->active_lock){+.+.}-{3:3}, at: msm_gpu_submit+0xec/0x178 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #4 (&gpu->active_lock){+.+.}-{3:3}: __mutex_lock+0xcc/0x3c8 mutex_lock_nested+0x30/0x44 msm_gpu_submit+0xec/0x178 msm_job_run+0x78/0x150 drm_sched_main+0x290/0x370 kthread+0xf0/0x100 ret_from_fork+0x10/0x20 -> #3 (dma_fence_map){++++}-{0:0}: __dma_fence_might_wait+0x74/0xc0 dma_resv_lockdep+0x1f4/0x2f4 do_one_initcall+0x104/0x2bc kernel_init_freeable+0x344/0x34c kernel_init+0x30/0x134 ret_from_fork+0x10/0x20 -> #2 (mmu_notifier_invalidate_range_start){+.+.}-{0:0}: fs_reclaim_acquire+0x80/0xa8 slab_pre_alloc_hook.constprop.0+0x40/0x25c __kmem_cache_alloc_node+0x60/0x1cc __kmalloc+0xd8/0x100 topology_parse_cpu_capacity+0x8c/0x178 get_cpu_for_node+0x88/0xc4 parse_cluster+0x1b0/0x28c parse_cluster+0x8c/0x28c init_cpu_topology+0x168/0x188 smp_prepare_cpus+0x24/0xf8 kernel_init_freeable+0x18c/0x34c kernel_init+0x30/0x134 ret_from_fork+0x10/0x20 -> #1 (fs_reclaim){+.+.}-{0:0}: __fs_reclaim_acquire+0x3c/0x48 fs_reclaim_acquire+0x54/0xa8 slab_pre_alloc_hook.constprop.0+0x40/0x25c __kmem_cache_alloc_node+0x60/0x1cc kmalloc_trace+0x50/0xa8 dev_pm_qos_constraints_allocate+0x38/0x100 __dev_pm_qos_add_request+0xb0/0x1e8 dev_pm_qos_add_request+0x58/0x80 dev_pm_qos_expose_latency_limit+0x60/0x13c register_cpu+0x12c/0x130 topology_init+0xac/0xbc do_one_initcall+0x104/0x2bc kernel_init_freeable+0x344/0x34c kernel_init+0x30/0x134 ret_from_fork+0x10/0x20 -> #0 (dev_pm_qos_mtx){+.+.}-{3:3}: __lock_acquire+0xe00/0x1060 lock_acquire+0x1e0/0x2f8 __mutex_lock+0xcc/0x3c8 mutex_lock_nested+0x30/0x44 dev_pm_qos_update_request+0x38/0x68 msm_devfreq_boost+0x40/0x70 msm_devfreq_active+0xc0/0xf0 msm_gpu_submit+0x10c/0x178 msm_job_run+0x78/0x150 drm_sched_main+0x290/0x370 kthread+0xf0/0x100 ret_from_fork+0x10/0x20 other info that might help us debug this: Chain exists of: dev_pm_qos_mtx --> dma_fence_map --> &gpu->active_lock Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(&gpu->active_lock); lock(dma_fence_map); lock(&gpu->active_lock); lock(dev_pm_qos_mtx); *** DEADLOCK *** 3 locks held by ring0/123: #0: ffffff8087251170 (&gpu->lock){+.+.}-{3:3}, at: msm_job_run+0x64/0x150 #1: ffffffd00b0e57e8 (dma_fence_map){++++}-{0:0}, at: msm_job_run+0x68/0x150 #2: ffffff8087251208 (&gpu->active_lock){+.+.}-{3:3}, at: msm_gpu_submit+0xec/0x178 stack backtrace: CPU: 6 PID: 123 Comm: ring0 Not tainted 6.2.0-rc8-debug+ torvalds#559 Hardware name: Google Lazor (rev1 - 2) with LTE (DT) Call trace: dump_backtrace.part.0+0xb4/0xf8 show_stack+0x20/0x38 dump_stack_lvl+0x9c/0xd0 dump_stack+0x18/0x34 print_circular_bug+0x1b4/0x1f0 check_noncircular+0x78/0xac __lock_acquire+0xe00/0x1060 lock_acquire+0x1e0/0x2f8 __mutex_lock+0xcc/0x3c8 mutex_lock_nested+0x30/0x44 dev_pm_qos_update_request+0x38/0x68 msm_devfreq_boost+0x40/0x70 msm_devfreq_active+0xc0/0xf0 msm_gpu_submit+0x10c/0x178 msm_job_run+0x78/0x150 drm_sched_main+0x290/0x370 kthread+0xf0/0x100 ret_from_fork+0x10/0x20 The issue is that dev_pm_qos_mtx is held in the runpm suspend/resume (or freq change) path, but it is also held across allocations that could recurse into shrinker. Solve this by changing dev_pm_qos_constraints_allocate() into a function that can be called unconditionally before the device qos object is needed and before aquiring dev_pm_qos_mtx. This way the allocations can be done without holding the mutex. In the case that we raced with another thread to allocate the qos object, detect this *after* acquiring the dev_pm_qos_mtx and simply free the redundant allocations. Signed-off-by: Rob Clark <[email protected]>
1 parent 5281b50 commit 211fb32

File tree

1 file changed

+41
-19
lines changed

1 file changed

+41
-19
lines changed

drivers/base/power/qos.c

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -185,18 +185,24 @@ static int apply_constraint(struct dev_pm_qos_request *req,
185185
}
186186

187187
/*
188-
* dev_pm_qos_constraints_allocate
188+
* dev_pm_qos_constraints_ensure_allocated
189189
* @dev: device to allocate data for
190190
*
191-
* Called at the first call to add_request, for constraint data allocation
192-
* Must be called with the dev_pm_qos_mtx mutex held
191+
* Called to ensure that devices qos is allocated, before acquiring
192+
* dev_pm_qos_mtx.
193193
*/
194-
static int dev_pm_qos_constraints_allocate(struct device *dev)
194+
static int dev_pm_qos_constraints_ensure_allocated(struct device *dev)
195195
{
196196
struct dev_pm_qos *qos;
197197
struct pm_qos_constraints *c;
198198
struct blocking_notifier_head *n;
199199

200+
if (!dev)
201+
return -ENODEV;
202+
203+
if (!IS_ERR_OR_NULL(dev->power.qos))
204+
return 0;
205+
200206
qos = kzalloc(sizeof(*qos), GFP_KERNEL);
201207
if (!qos)
202208
return -ENOMEM;
@@ -227,10 +233,26 @@ static int dev_pm_qos_constraints_allocate(struct device *dev)
227233

228234
INIT_LIST_HEAD(&qos->flags.list);
229235

236+
mutex_lock(&dev_pm_qos_mtx);
237+
238+
if (!IS_ERR_OR_NULL(dev->power.qos)) {
239+
/*
240+
* We have raced with another task to create the qos.
241+
* No biggie, just free the resources we've allocated
242+
* outside of dev_pm_qos_mtx and move on with life.
243+
*/
244+
kfree(n);
245+
kfree(qos);
246+
goto unlock;
247+
}
248+
230249
spin_lock_irq(&dev->power.lock);
231250
dev->power.qos = qos;
232251
spin_unlock_irq(&dev->power.lock);
233252

253+
unlock:
254+
mutex_unlock(&dev_pm_qos_mtx);
255+
234256
return 0;
235257
}
236258

@@ -331,17 +353,15 @@ static int __dev_pm_qos_add_request(struct device *dev,
331353
{
332354
int ret = 0;
333355

334-
if (!dev || !req || dev_pm_qos_invalid_req_type(dev, type))
356+
if (!req || dev_pm_qos_invalid_req_type(dev, type))
335357
return -EINVAL;
336358

337359
if (WARN(dev_pm_qos_request_active(req),
338360
"%s() called for already added request\n", __func__))
339361
return -EINVAL;
340362

341-
if (IS_ERR(dev->power.qos))
363+
if (IS_ERR_OR_NULL(dev->power.qos))
342364
ret = -ENODEV;
343-
else if (!dev->power.qos)
344-
ret = dev_pm_qos_constraints_allocate(dev);
345365

346366
trace_dev_pm_qos_add_request(dev_name(dev), type, value);
347367
if (ret)
@@ -390,6 +410,10 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req,
390410
{
391411
int ret;
392412

413+
ret = dev_pm_qos_constraints_ensure_allocated(dev);
414+
if (ret)
415+
return ret;
416+
393417
mutex_lock(&dev_pm_qos_mtx);
394418
ret = __dev_pm_qos_add_request(dev, req, type, value);
395419
mutex_unlock(&dev_pm_qos_mtx);
@@ -537,15 +561,11 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier,
537561
{
538562
int ret = 0;
539563

540-
mutex_lock(&dev_pm_qos_mtx);
541-
542-
if (IS_ERR(dev->power.qos))
543-
ret = -ENODEV;
544-
else if (!dev->power.qos)
545-
ret = dev_pm_qos_constraints_allocate(dev);
546-
564+
ret = dev_pm_qos_constraints_ensure_allocated(dev);
547565
if (ret)
548-
goto unlock;
566+
return ret;
567+
568+
mutex_lock(&dev_pm_qos_mtx);
549569

550570
switch (type) {
551571
case DEV_PM_QOS_RESUME_LATENCY:
@@ -565,7 +585,6 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier,
565585
ret = -EINVAL;
566586
}
567587

568-
unlock:
569588
mutex_unlock(&dev_pm_qos_mtx);
570589
return ret;
571590
}
@@ -905,10 +924,13 @@ int dev_pm_qos_update_user_latency_tolerance(struct device *dev, s32 val)
905924
{
906925
int ret;
907926

927+
ret = dev_pm_qos_constraints_ensure_allocated(dev);
928+
if (ret)
929+
return ret;
930+
908931
mutex_lock(&dev_pm_qos_mtx);
909932

910-
if (IS_ERR_OR_NULL(dev->power.qos)
911-
|| !dev->power.qos->latency_tolerance_req) {
933+
if (!dev->power.qos->latency_tolerance_req) {
912934
struct dev_pm_qos_request *req;
913935

914936
if (val < 0) {

0 commit comments

Comments
 (0)