Message ID | 20230803220202.78036-4-robdclark@gmail.com |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9f41:0:b0:3e4:2afc:c1 with SMTP id v1csp1487231vqx; Thu, 3 Aug 2023 17:20:12 -0700 (PDT) X-Google-Smtp-Source: APBJJlFpMqretg3oOVBY4H5wOiGsT+nZ63NQ7a0UOSozyNBXYIrdqsdVvbm/qTWQ0Db6I8ulCDjV X-Received: by 2002:a05:6402:520e:b0:522:580f:8304 with SMTP id s14-20020a056402520e00b00522580f8304mr16750001edd.1.1691108411823; Thu, 03 Aug 2023 17:20:11 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1691108411; cv=none; d=google.com; s=arc-20160816; b=LqU+4A+qAHgcIRsVsblr+xge7Olo4efgEpKFtoOMQwyRccwfJB4jKNs8H1Qh0yM1eP tQbKb5kFO/1meGpZN91SxJ4oTJtNvaUZZ9prYEUonWQo7S1lgB25/LBfVo0PEX6yqkTg aB6k4AhoaokQ/m416bWRnrEoxxrUZ4nm2/6fStkcGmKjIvHFIWJLT3PfXb26GK+Na/Ze D9hUt7nBqEWz3ipQEHmumWMxYl+vTAg2wPxpqXElEcjK1rTSU0PKyajFSGAj5acarpeb Fq1M2trNdkYvzIDcz/2sJMk3cotmEP8BQgQfNTwgs5MBbIm3wE0qrVU5uaiaPDSPp9Oj E52g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=DIKm3KXW0BL2JyogAnZ2vg+/kVkCrZycjm7nvt+Ji58=; fh=1UHN2vYFDg1VELwt62RkkNSS7rhAuJQ/UuEEWkjgt1M=; b=WP8etRxDHIjmjEgRyMWk5vgYk+Myj0zTb4iqZzPvcloG0fx2mWsT8zClNia+Dfn8ji 140FdqgP/LAQTHnauIMvyY42M5RfcTB+6JXZcax1di0pdfvX3i8jFeh1tqbIJzuF0y4E m+9dhSzsqVUqrOxwXzjXahBByN4wHBIBKPuSG4o9/aOhBBkUkfRZNlp3vK1CMj14+eWf HJ6uOkN0LkbbANTMxcoegojc0R3WgRWsGtvwccCH5axYopiyuorVQ2o7CZPj8RBIxycF RDjBlm2zBTtWhfkL4Za/CHIResTIXvF6qovaPRhOUpNNR8jWhCcoT1zfkhysVvzHddqT syMQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20221208 header.b=ErSK6Ina; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id u23-20020a056402065700b005222818bdecsi36467edx.602.2023.08.03.17.19.47; Thu, 03 Aug 2023 17:20:11 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20221208 header.b=ErSK6Ina; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231879AbjHCWCa (ORCPT <rfc822;tertiaryakionsight@gmail.com> + 99 others); Thu, 3 Aug 2023 18:02:30 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52836 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231947AbjHCWC0 (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Thu, 3 Aug 2023 18:02:26 -0400 Received: from mail-pl1-x62f.google.com (mail-pl1-x62f.google.com [IPv6:2607:f8b0:4864:20::62f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C0F6B3C23; Thu, 3 Aug 2023 15:02:24 -0700 (PDT) Received: by mail-pl1-x62f.google.com with SMTP id d9443c01a7336-1bbf0f36ce4so10321375ad.0; Thu, 03 Aug 2023 15:02:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1691100144; x=1691704944; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=DIKm3KXW0BL2JyogAnZ2vg+/kVkCrZycjm7nvt+Ji58=; b=ErSK6InaI2pfpjFZspAp/kNj9zIU5+BxZiqjMBfiIJhN75pDYYqTMlzG6LeNzoR/fo LmHAivo7tSOek8dLbFEFdzmj7gVNIZdFh5RoeHxdH11ujLS9ZctBk1hH04ZGh/NlaFy8 qz3SWdX4Q71xt13jtSLKWMEYPwPibuDFSfE8ePtQKLW7YKIa3AvmHgAitHCeqTWNuWTp qoa+q6SN5gc3gOeVK6XiUmjaVCaFXUBdT/uNzXyD9iVGCX+SEra+pPTYA83wxCmnTFOV WaRp0/ZtRkv6EoUMkCGBJNV6cd1s/Ozt/atKCrEMAiaoAnc8Q5vAmIRbOossilUSqRCC kuZw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1691100144; x=1691704944; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=DIKm3KXW0BL2JyogAnZ2vg+/kVkCrZycjm7nvt+Ji58=; b=Tja72HdRoKsBmkhD7m7QOPzyf1+fqFC9GKM2A/iSep3MCLuYl6SGQnoqrUrRhdRNhk 1jGDgloJ0huEL9khHTjK0BQlVr7Si3H9oFnqonLHjWbApnnclyU1VF7+iBbXtLzNegNY AxQFL3uwjZDCYvx/fEjmyI+FLH/tQ2ENLcN0pCclPZVbzbX6CkuUAoWJKva7FSpTNoSU Mh5gxlgx30Tmk4SEep0ttKH6Ehtvm16cjmRyJ/IH8MaG0ezrpLEpXWAQHo2N14lc/emx 4WlsmxtkFnV76eP/69ZXlcMSR7GqFjyq7J3kzqpT1v/H2/wkNJLzGdMbQFZ3Uw08Q0HK Jdew== X-Gm-Message-State: AOJu0YzMNSpzgvNsNYzUXmoO916V8iIc4QwlWoALApNyt7MYDv4WIXOM Dy0ZC4aVJjqqJyBsFwCLglo= X-Received: by 2002:a17:903:1205:b0:1bc:e6a:205f with SMTP id l5-20020a170903120500b001bc0e6a205fmr19215plh.20.1691100143953; Thu, 03 Aug 2023 15:02:23 -0700 (PDT) Received: from localhost ([2a00:79e1:abd:4a00:6c80:7c10:75a0:44f4]) by smtp.gmail.com with ESMTPSA id q8-20020a170902a3c800b001bba7002132sm338824plb.33.2023.08.03.15.02.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 03 Aug 2023 15:02:23 -0700 (PDT) From: Rob Clark <robdclark@gmail.com> To: dri-devel@lists.freedesktop.org Cc: linux-arm-msm@vger.kernel.org, freedreno@lists.freedesktop.org, Rob Clark <robdclark@chromium.org>, "Rafael J. Wysocki" <rafael@kernel.org>, Pavel Machek <pavel@ucw.cz>, Len Brown <len.brown@intel.com>, Greg Kroah-Hartman <gregkh@linuxfoundation.org>, linux-pm@vger.kernel.org (open list:HIBERNATION (aka Software Suspend, aka swsusp)), linux-kernel@vger.kernel.org (open list) Subject: [PATCH v3 3/9] PM / QoS: Fix constraints alloc vs reclaim locking Date: Thu, 3 Aug 2023 15:01:51 -0700 Message-ID: <20230803220202.78036-4-robdclark@gmail.com> X-Mailer: git-send-email 2.41.0 In-Reply-To: <20230803220202.78036-1-robdclark@gmail.com> References: <20230803220202.78036-1-robdclark@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM, RCVD_IN_DNSWL_BLOCKED,SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1773255694105827687 X-GMAIL-MSGID: 1773255694105827687 |
Series |
drm/msm+PM+icc: Make job_run() reclaim-safe
|
|
Commit Message
Rob Clark
Aug. 3, 2023, 10:01 p.m. UTC
From: Rob Clark <robdclark@chromium.org> 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+ #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+ #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 <robdclark@chromium.org> --- drivers/base/power/qos.c | 60 +++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 19 deletions(-)
Comments
On Fri, Aug 4, 2023 at 12:02 AM Rob Clark <robdclark@gmail.com> wrote: > > From: Rob Clark <robdclark@chromium.org> > > 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+ #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+ #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 <robdclark@chromium.org> > --- > drivers/base/power/qos.c | 60 +++++++++++++++++++++++++++------------- > 1 file changed, 41 insertions(+), 19 deletions(-) > > diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c > index 8e93167f1783..f3e0c6b65635 100644 > --- a/drivers/base/power/qos.c > +++ b/drivers/base/power/qos.c > @@ -185,18 +185,24 @@ static int apply_constraint(struct dev_pm_qos_request *req, > } > > /* > - * dev_pm_qos_constraints_allocate > + * dev_pm_qos_constraints_ensure_allocated > * @dev: device to allocate data for > * > - * Called at the first call to add_request, for constraint data allocation > - * Must be called with the dev_pm_qos_mtx mutex held > + * Called to ensure that devices qos is allocated, before acquiring > + * dev_pm_qos_mtx. > */ > -static int dev_pm_qos_constraints_allocate(struct device *dev) > +static int dev_pm_qos_constraints_ensure_allocated(struct device *dev) > { > struct dev_pm_qos *qos; > struct pm_qos_constraints *c; > struct blocking_notifier_head *n; > > + if (!dev) > + return -ENODEV; > + > + if (!IS_ERR_OR_NULL(dev->power.qos)) > + return 0; > + > qos = kzalloc(sizeof(*qos), GFP_KERNEL); > if (!qos) > return -ENOMEM; > @@ -227,10 +233,26 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) > > INIT_LIST_HEAD(&qos->flags.list); > > + mutex_lock(&dev_pm_qos_mtx); > + > + if (!IS_ERR_OR_NULL(dev->power.qos)) { > + /* > + * We have raced with another task to create the qos. > + * No biggie, just free the resources we've allocated > + * outside of dev_pm_qos_mtx and move on with life. > + */ > + kfree(n); > + kfree(qos); > + goto unlock; > + } > + > spin_lock_irq(&dev->power.lock); > dev->power.qos = qos; > spin_unlock_irq(&dev->power.lock); > > +unlock: > + mutex_unlock(&dev_pm_qos_mtx); > + > return 0; > } > > @@ -331,17 +353,15 @@ static int __dev_pm_qos_add_request(struct device *dev, > { > int ret = 0; > > - if (!dev || !req || dev_pm_qos_invalid_req_type(dev, type)) > + if (!req || dev_pm_qos_invalid_req_type(dev, type)) > return -EINVAL; > > if (WARN(dev_pm_qos_request_active(req), > "%s() called for already added request\n", __func__)) > return -EINVAL; > > - if (IS_ERR(dev->power.qos)) > + if (IS_ERR_OR_NULL(dev->power.qos)) > ret = -ENODEV; > - else if (!dev->power.qos) > - ret = dev_pm_qos_constraints_allocate(dev); > > trace_dev_pm_qos_add_request(dev_name(dev), type, value); > if (ret) > @@ -390,6 +410,10 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, > { > int ret; > > + ret = dev_pm_qos_constraints_ensure_allocated(dev); > + if (ret) > + return ret; > + It is a bit unfortunate that the mutex is dropped and then immediately re-acquired again. I don't think that this is strictly necessary. > mutex_lock(&dev_pm_qos_mtx); > ret = __dev_pm_qos_add_request(dev, req, type, value); > mutex_unlock(&dev_pm_qos_mtx); > @@ -537,15 +561,11 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier, > { > int ret = 0; > > - mutex_lock(&dev_pm_qos_mtx); > - > - if (IS_ERR(dev->power.qos)) > - ret = -ENODEV; > - else if (!dev->power.qos) > - ret = dev_pm_qos_constraints_allocate(dev); > - > + ret = dev_pm_qos_constraints_ensure_allocated(dev); > if (ret) > - goto unlock; > + return ret; > + > + mutex_lock(&dev_pm_qos_mtx); > > switch (type) { > case DEV_PM_QOS_RESUME_LATENCY: > @@ -565,7 +585,6 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier, > ret = -EINVAL; > } > > -unlock: > mutex_unlock(&dev_pm_qos_mtx); > return ret; > } > @@ -905,10 +924,13 @@ int dev_pm_qos_update_user_latency_tolerance(struct device *dev, s32 val) > { > int ret; > > + ret = dev_pm_qos_constraints_ensure_allocated(dev); > + if (ret) > + return ret; > + > mutex_lock(&dev_pm_qos_mtx); > > - if (IS_ERR_OR_NULL(dev->power.qos) > - || !dev->power.qos->latency_tolerance_req) { > + if (!dev->power.qos->latency_tolerance_req) { > struct dev_pm_qos_request *req; > > if (val < 0) { > -- > 2.41.0 >
On Fri, Aug 4, 2023 at 10:07 AM Rafael J. Wysocki <rafael@kernel.org> wrote: > > On Fri, Aug 4, 2023 at 12:02 AM Rob Clark <robdclark@gmail.com> wrote: > > > > From: Rob Clark <robdclark@chromium.org> > > > > 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+ #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+ #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 <robdclark@chromium.org> > > --- > > drivers/base/power/qos.c | 60 +++++++++++++++++++++++++++------------- > > 1 file changed, 41 insertions(+), 19 deletions(-) > > > > diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c > > index 8e93167f1783..f3e0c6b65635 100644 > > --- a/drivers/base/power/qos.c > > +++ b/drivers/base/power/qos.c > > @@ -185,18 +185,24 @@ static int apply_constraint(struct dev_pm_qos_request *req, > > } > > > > /* > > - * dev_pm_qos_constraints_allocate > > + * dev_pm_qos_constraints_ensure_allocated > > * @dev: device to allocate data for > > * > > - * Called at the first call to add_request, for constraint data allocation > > - * Must be called with the dev_pm_qos_mtx mutex held > > + * Called to ensure that devices qos is allocated, before acquiring > > + * dev_pm_qos_mtx. > > */ > > -static int dev_pm_qos_constraints_allocate(struct device *dev) > > +static int dev_pm_qos_constraints_ensure_allocated(struct device *dev) > > { > > struct dev_pm_qos *qos; > > struct pm_qos_constraints *c; > > struct blocking_notifier_head *n; > > > > + if (!dev) > > + return -ENODEV; > > + > > + if (!IS_ERR_OR_NULL(dev->power.qos)) > > + return 0; > > + > > qos = kzalloc(sizeof(*qos), GFP_KERNEL); > > if (!qos) > > return -ENOMEM; > > @@ -227,10 +233,26 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) > > > > INIT_LIST_HEAD(&qos->flags.list); > > > > + mutex_lock(&dev_pm_qos_mtx); > > + > > + if (!IS_ERR_OR_NULL(dev->power.qos)) { > > + /* > > + * We have raced with another task to create the qos. > > + * No biggie, just free the resources we've allocated > > + * outside of dev_pm_qos_mtx and move on with life. > > + */ > > + kfree(n); > > + kfree(qos); > > + goto unlock; > > + } > > + > > spin_lock_irq(&dev->power.lock); > > dev->power.qos = qos; > > spin_unlock_irq(&dev->power.lock); > > > > +unlock: > > + mutex_unlock(&dev_pm_qos_mtx); > > + > > return 0; > > } > > > > @@ -331,17 +353,15 @@ static int __dev_pm_qos_add_request(struct device *dev, > > { > > int ret = 0; > > > > - if (!dev || !req || dev_pm_qos_invalid_req_type(dev, type)) > > + if (!req || dev_pm_qos_invalid_req_type(dev, type)) > > return -EINVAL; > > > > if (WARN(dev_pm_qos_request_active(req), > > "%s() called for already added request\n", __func__)) > > return -EINVAL; > > > > - if (IS_ERR(dev->power.qos)) > > + if (IS_ERR_OR_NULL(dev->power.qos)) > > ret = -ENODEV; > > - else if (!dev->power.qos) > > - ret = dev_pm_qos_constraints_allocate(dev); > > > > trace_dev_pm_qos_add_request(dev_name(dev), type, value); > > if (ret) > > @@ -390,6 +410,10 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, > > { > > int ret; > > > > + ret = dev_pm_qos_constraints_ensure_allocated(dev); > > + if (ret) > > + return ret; > > + > > It is a bit unfortunate that the mutex is dropped and then immediately > re-acquired again. I don't think that this is strictly necessary. We could have dev_pm_qos_constraints_ensure_allocated() return with the lock held in the success case if we had to.. but that seems a bit funny looking. And the dev_pm_qos_update_user_latency_tolerance() path would need to shuffle slightly to move the kzalloc out of the lock. BR, -R > > mutex_lock(&dev_pm_qos_mtx); > > ret = __dev_pm_qos_add_request(dev, req, type, value); > > mutex_unlock(&dev_pm_qos_mtx); > > @@ -537,15 +561,11 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier, > > { > > int ret = 0; > > > > - mutex_lock(&dev_pm_qos_mtx); > > - > > - if (IS_ERR(dev->power.qos)) > > - ret = -ENODEV; > > - else if (!dev->power.qos) > > - ret = dev_pm_qos_constraints_allocate(dev); > > - > > + ret = dev_pm_qos_constraints_ensure_allocated(dev); > > if (ret) > > - goto unlock; > > + return ret; > > + > > + mutex_lock(&dev_pm_qos_mtx); > > > > switch (type) { > > case DEV_PM_QOS_RESUME_LATENCY: > > @@ -565,7 +585,6 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier, > > ret = -EINVAL; > > } > > > > -unlock: > > mutex_unlock(&dev_pm_qos_mtx); > > return ret; > > } > > @@ -905,10 +924,13 @@ int dev_pm_qos_update_user_latency_tolerance(struct device *dev, s32 val) > > { > > int ret; > > > > + ret = dev_pm_qos_constraints_ensure_allocated(dev); > > + if (ret) > > + return ret; > > + > > mutex_lock(&dev_pm_qos_mtx); > > > > - if (IS_ERR_OR_NULL(dev->power.qos) > > - || !dev->power.qos->latency_tolerance_req) { > > + if (!dev->power.qos->latency_tolerance_req) { > > struct dev_pm_qos_request *req; > > > > if (val < 0) { > > -- > > 2.41.0 > >
On Fri, Aug 4, 2023 at 8:38 PM Rob Clark <robdclark@gmail.com> wrote: > > On Fri, Aug 4, 2023 at 10:07 AM Rafael J. Wysocki <rafael@kernel.org> wrote: > > > > On Fri, Aug 4, 2023 at 12:02 AM Rob Clark <robdclark@gmail.com> wrote: > > > > > > From: Rob Clark <robdclark@chromium.org> > > > > > > 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+ #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+ #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 <robdclark@chromium.org> > > > --- > > > drivers/base/power/qos.c | 60 +++++++++++++++++++++++++++------------- > > > 1 file changed, 41 insertions(+), 19 deletions(-) > > > > > > diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c > > > index 8e93167f1783..f3e0c6b65635 100644 > > > --- a/drivers/base/power/qos.c > > > +++ b/drivers/base/power/qos.c > > > @@ -185,18 +185,24 @@ static int apply_constraint(struct dev_pm_qos_request *req, > > > } > > > > > > /* > > > - * dev_pm_qos_constraints_allocate > > > + * dev_pm_qos_constraints_ensure_allocated > > > * @dev: device to allocate data for > > > * > > > - * Called at the first call to add_request, for constraint data allocation > > > - * Must be called with the dev_pm_qos_mtx mutex held > > > + * Called to ensure that devices qos is allocated, before acquiring > > > + * dev_pm_qos_mtx. > > > */ > > > -static int dev_pm_qos_constraints_allocate(struct device *dev) > > > +static int dev_pm_qos_constraints_ensure_allocated(struct device *dev) > > > { > > > struct dev_pm_qos *qos; > > > struct pm_qos_constraints *c; > > > struct blocking_notifier_head *n; > > > > > > + if (!dev) > > > + return -ENODEV; > > > + > > > + if (!IS_ERR_OR_NULL(dev->power.qos)) > > > + return 0; > > > + > > > qos = kzalloc(sizeof(*qos), GFP_KERNEL); > > > if (!qos) > > > return -ENOMEM; > > > @@ -227,10 +233,26 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) > > > > > > INIT_LIST_HEAD(&qos->flags.list); > > > > > > + mutex_lock(&dev_pm_qos_mtx); > > > + > > > + if (!IS_ERR_OR_NULL(dev->power.qos)) { > > > + /* > > > + * We have raced with another task to create the qos. > > > + * No biggie, just free the resources we've allocated > > > + * outside of dev_pm_qos_mtx and move on with life. > > > + */ > > > + kfree(n); > > > + kfree(qos); > > > + goto unlock; > > > + } > > > + > > > spin_lock_irq(&dev->power.lock); > > > dev->power.qos = qos; > > > spin_unlock_irq(&dev->power.lock); > > > > > > +unlock: > > > + mutex_unlock(&dev_pm_qos_mtx); > > > + > > > return 0; > > > } > > > > > > @@ -331,17 +353,15 @@ static int __dev_pm_qos_add_request(struct device *dev, > > > { > > > int ret = 0; > > > > > > - if (!dev || !req || dev_pm_qos_invalid_req_type(dev, type)) > > > + if (!req || dev_pm_qos_invalid_req_type(dev, type)) > > > return -EINVAL; > > > > > > if (WARN(dev_pm_qos_request_active(req), > > > "%s() called for already added request\n", __func__)) > > > return -EINVAL; > > > > > > - if (IS_ERR(dev->power.qos)) > > > + if (IS_ERR_OR_NULL(dev->power.qos)) > > > ret = -ENODEV; > > > - else if (!dev->power.qos) > > > - ret = dev_pm_qos_constraints_allocate(dev); > > > > > > trace_dev_pm_qos_add_request(dev_name(dev), type, value); > > > if (ret) > > > @@ -390,6 +410,10 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, > > > { > > > int ret; > > > > > > + ret = dev_pm_qos_constraints_ensure_allocated(dev); > > > + if (ret) > > > + return ret; > > > + > > > > It is a bit unfortunate that the mutex is dropped and then immediately > > re-acquired again. I don't think that this is strictly necessary. > > We could have dev_pm_qos_constraints_ensure_allocated() return with > the lock held in the success case if we had to.. but that seems a bit > funny looking. And the dev_pm_qos_update_user_latency_tolerance() > path would need to shuffle slightly to move the kzalloc out of the > lock. Well, what about something like this (modulo whitespace damage by GMail), attached for completeness: --- drivers/base/power/qos.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) Index: linux-pm/drivers/base/power/qos.c =================================================================== --- linux-pm.orig/drivers/base/power/qos.c +++ linux-pm/drivers/base/power/qos.c @@ -186,26 +186,21 @@ static int apply_constraint(struct dev_p /* * dev_pm_qos_constraints_allocate - * @dev: device to allocate data for * * Called at the first call to add_request, for constraint data allocation * Must be called with the dev_pm_qos_mtx mutex held */ -static int dev_pm_qos_constraints_allocate(struct device *dev) +static struct dev_pm_qos *dev_pm_qos_constraints_allocate(void) { struct dev_pm_qos *qos; struct pm_qos_constraints *c; struct blocking_notifier_head *n; - qos = kzalloc(sizeof(*qos), GFP_KERNEL); + qos = kzalloc(sizeof(*qos) + kzalloc(3 * sizeof(*n), GFP_KERNEL); if (!qos) - return -ENOMEM; + return NULL; - n = kzalloc(3 * sizeof(*n), GFP_KERNEL); - if (!n) { - kfree(qos); - return -ENOMEM; - } + n = (struct blocking_notifier_head *)(qos + 1); c = &qos->resume_latency; plist_head_init(&c->list); @@ -227,6 +222,20 @@ static int dev_pm_qos_constraints_alloca INIT_LIST_HEAD(&qos->flags.list); + return qos; +} + +static int dev_pm_qos_constraints_add(struct device *dev, + struct dev_pm_qos *qos) +{ + if (!qos) + return -ENOMEM; + + if (!IS_ERR_OR_NULL(dev->power.qos)) { + kfree(qos); + return -ENODEV; + } + spin_lock_irq(&dev->power.lock); dev->power.qos = qos; spin_unlock_irq(&dev->power.lock); @@ -326,6 +335,7 @@ static bool dev_pm_qos_invalid_req_type( } static int __dev_pm_qos_add_request(struct device *dev, + struct dev_pm_qos *qos, struct dev_pm_qos_request *req, enum dev_pm_qos_req_type type, s32 value) { @@ -340,8 +350,10 @@ static int __dev_pm_qos_add_request(stru if (IS_ERR(dev->power.qos)) ret = -ENODEV; - else if (!dev->power.qos) - ret = dev_pm_qos_constraints_allocate(dev); + else if (dev->power.qos) + kfree(qos); + else + ret = dev_pm_qos_constraints_add(dev); trace_dev_pm_qos_add_request(dev_name(dev), type, value); if (ret) @@ -388,10 +400,11 @@ static int __dev_pm_qos_add_request(stru int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, enum dev_pm_qos_req_type type, s32 value) { + struct dev_pm_qos *qos = dev_pm_qos_constraints_allocate(); int ret; mutex_lock(&dev_pm_qos_mtx); - ret = __dev_pm_qos_add_request(dev, req, type, value); + ret = __dev_pm_qos_add_request(dev, qos, req, type, value); mutex_unlock(&dev_pm_qos_mtx); return ret; }
On Fri, Aug 4, 2023 at 12:11 PM Rafael J. Wysocki <rafael@kernel.org> wrote: > > On Fri, Aug 4, 2023 at 8:38 PM Rob Clark <robdclark@gmail.com> wrote: > > > > On Fri, Aug 4, 2023 at 10:07 AM Rafael J. Wysocki <rafael@kernel.org> wrote: > > > > > > On Fri, Aug 4, 2023 at 12:02 AM Rob Clark <robdclark@gmail.com> wrote: > > > > > > > > From: Rob Clark <robdclark@chromium.org> > > > > > > > > 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+ #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+ #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 <robdclark@chromium.org> > > > > --- > > > > drivers/base/power/qos.c | 60 +++++++++++++++++++++++++++------------- > > > > 1 file changed, 41 insertions(+), 19 deletions(-) > > > > > > > > diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c > > > > index 8e93167f1783..f3e0c6b65635 100644 > > > > --- a/drivers/base/power/qos.c > > > > +++ b/drivers/base/power/qos.c > > > > @@ -185,18 +185,24 @@ static int apply_constraint(struct dev_pm_qos_request *req, > > > > } > > > > > > > > /* > > > > - * dev_pm_qos_constraints_allocate > > > > + * dev_pm_qos_constraints_ensure_allocated > > > > * @dev: device to allocate data for > > > > * > > > > - * Called at the first call to add_request, for constraint data allocation > > > > - * Must be called with the dev_pm_qos_mtx mutex held > > > > + * Called to ensure that devices qos is allocated, before acquiring > > > > + * dev_pm_qos_mtx. > > > > */ > > > > -static int dev_pm_qos_constraints_allocate(struct device *dev) > > > > +static int dev_pm_qos_constraints_ensure_allocated(struct device *dev) > > > > { > > > > struct dev_pm_qos *qos; > > > > struct pm_qos_constraints *c; > > > > struct blocking_notifier_head *n; > > > > > > > > + if (!dev) > > > > + return -ENODEV; > > > > + > > > > + if (!IS_ERR_OR_NULL(dev->power.qos)) > > > > + return 0; > > > > + > > > > qos = kzalloc(sizeof(*qos), GFP_KERNEL); > > > > if (!qos) > > > > return -ENOMEM; > > > > @@ -227,10 +233,26 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) > > > > > > > > INIT_LIST_HEAD(&qos->flags.list); > > > > > > > > + mutex_lock(&dev_pm_qos_mtx); > > > > + > > > > + if (!IS_ERR_OR_NULL(dev->power.qos)) { > > > > + /* > > > > + * We have raced with another task to create the qos. > > > > + * No biggie, just free the resources we've allocated > > > > + * outside of dev_pm_qos_mtx and move on with life. > > > > + */ > > > > + kfree(n); > > > > + kfree(qos); > > > > + goto unlock; > > > > + } > > > > + > > > > spin_lock_irq(&dev->power.lock); > > > > dev->power.qos = qos; > > > > spin_unlock_irq(&dev->power.lock); > > > > > > > > +unlock: > > > > + mutex_unlock(&dev_pm_qos_mtx); > > > > + > > > > return 0; > > > > } > > > > > > > > @@ -331,17 +353,15 @@ static int __dev_pm_qos_add_request(struct device *dev, > > > > { > > > > int ret = 0; > > > > > > > > - if (!dev || !req || dev_pm_qos_invalid_req_type(dev, type)) > > > > + if (!req || dev_pm_qos_invalid_req_type(dev, type)) > > > > return -EINVAL; > > > > > > > > if (WARN(dev_pm_qos_request_active(req), > > > > "%s() called for already added request\n", __func__)) > > > > return -EINVAL; > > > > > > > > - if (IS_ERR(dev->power.qos)) > > > > + if (IS_ERR_OR_NULL(dev->power.qos)) > > > > ret = -ENODEV; > > > > - else if (!dev->power.qos) > > > > - ret = dev_pm_qos_constraints_allocate(dev); > > > > > > > > trace_dev_pm_qos_add_request(dev_name(dev), type, value); > > > > if (ret) > > > > @@ -390,6 +410,10 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, > > > > { > > > > int ret; > > > > > > > > + ret = dev_pm_qos_constraints_ensure_allocated(dev); > > > > + if (ret) > > > > + return ret; > > > > + > > > > > > It is a bit unfortunate that the mutex is dropped and then immediately > > > re-acquired again. I don't think that this is strictly necessary. > > > > We could have dev_pm_qos_constraints_ensure_allocated() return with > > the lock held in the success case if we had to.. but that seems a bit > > funny looking. And the dev_pm_qos_update_user_latency_tolerance() > > path would need to shuffle slightly to move the kzalloc out of the > > lock. > > Well, what about something like this (modulo whitespace damage by > GMail), attached for completeness: > There is one other path to handle, and some small details, but I think the approach could work.. let's see.. BR, -R > --- > drivers/base/power/qos.c | 37 +++++++++++++++++++++++++------------ > 1 file changed, 25 insertions(+), 12 deletions(-) > > Index: linux-pm/drivers/base/power/qos.c > =================================================================== > --- linux-pm.orig/drivers/base/power/qos.c > +++ linux-pm/drivers/base/power/qos.c > @@ -186,26 +186,21 @@ static int apply_constraint(struct dev_p > > /* > * dev_pm_qos_constraints_allocate > - * @dev: device to allocate data for > * > * Called at the first call to add_request, for constraint data allocation > * Must be called with the dev_pm_qos_mtx mutex held > */ > -static int dev_pm_qos_constraints_allocate(struct device *dev) > +static struct dev_pm_qos *dev_pm_qos_constraints_allocate(void) > { > struct dev_pm_qos *qos; > struct pm_qos_constraints *c; > struct blocking_notifier_head *n; > > - qos = kzalloc(sizeof(*qos), GFP_KERNEL); > + qos = kzalloc(sizeof(*qos) + kzalloc(3 * sizeof(*n), GFP_KERNEL); > if (!qos) > - return -ENOMEM; > + return NULL; > > - n = kzalloc(3 * sizeof(*n), GFP_KERNEL); > - if (!n) { > - kfree(qos); > - return -ENOMEM; > - } > + n = (struct blocking_notifier_head *)(qos + 1); > > c = &qos->resume_latency; > plist_head_init(&c->list); > @@ -227,6 +222,20 @@ static int dev_pm_qos_constraints_alloca > > INIT_LIST_HEAD(&qos->flags.list); > > + return qos; > +} > + > +static int dev_pm_qos_constraints_add(struct device *dev, > + struct dev_pm_qos *qos) > +{ > + if (!qos) > + return -ENOMEM; > + > + if (!IS_ERR_OR_NULL(dev->power.qos)) { > + kfree(qos); > + return -ENODEV; > + } > + > spin_lock_irq(&dev->power.lock); > dev->power.qos = qos; > spin_unlock_irq(&dev->power.lock); > @@ -326,6 +335,7 @@ static bool dev_pm_qos_invalid_req_type( > } > > static int __dev_pm_qos_add_request(struct device *dev, > + struct dev_pm_qos *qos, > struct dev_pm_qos_request *req, > enum dev_pm_qos_req_type type, s32 value) > { > @@ -340,8 +350,10 @@ static int __dev_pm_qos_add_request(stru > > if (IS_ERR(dev->power.qos)) > ret = -ENODEV; > - else if (!dev->power.qos) > - ret = dev_pm_qos_constraints_allocate(dev); > + else if (dev->power.qos) > + kfree(qos); > + else > + ret = dev_pm_qos_constraints_add(dev); > > trace_dev_pm_qos_add_request(dev_name(dev), type, value); > if (ret) > @@ -388,10 +400,11 @@ static int __dev_pm_qos_add_request(stru > int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, > enum dev_pm_qos_req_type type, s32 value) > { > + struct dev_pm_qos *qos = dev_pm_qos_constraints_allocate(); > int ret; > > mutex_lock(&dev_pm_qos_mtx); > - ret = __dev_pm_qos_add_request(dev, req, type, value); > + ret = __dev_pm_qos_add_request(dev, qos, req, type, value); > mutex_unlock(&dev_pm_qos_mtx); > return ret; > }
On Fri, Aug 4, 2023 at 10:38 PM Rob Clark <robdclark@gmail.com> wrote: > > On Fri, Aug 4, 2023 at 12:11 PM Rafael J. Wysocki <rafael@kernel.org> wrote: > > > > On Fri, Aug 4, 2023 at 8:38 PM Rob Clark <robdclark@gmail.com> wrote: > > > > > > On Fri, Aug 4, 2023 at 10:07 AM Rafael J. Wysocki <rafael@kernel.org> wrote: > > > > > > > > On Fri, Aug 4, 2023 at 12:02 AM Rob Clark <robdclark@gmail.com> wrote: > > > > > > > > > > From: Rob Clark <robdclark@chromium.org> > > > > > > > > > > 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+ #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+ #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 <robdclark@chromium.org> > > > > > --- > > > > > drivers/base/power/qos.c | 60 +++++++++++++++++++++++++++------------- > > > > > 1 file changed, 41 insertions(+), 19 deletions(-) > > > > > > > > > > diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c > > > > > index 8e93167f1783..f3e0c6b65635 100644 > > > > > --- a/drivers/base/power/qos.c > > > > > +++ b/drivers/base/power/qos.c > > > > > @@ -185,18 +185,24 @@ static int apply_constraint(struct dev_pm_qos_request *req, > > > > > } > > > > > > > > > > /* > > > > > - * dev_pm_qos_constraints_allocate > > > > > + * dev_pm_qos_constraints_ensure_allocated > > > > > * @dev: device to allocate data for > > > > > * > > > > > - * Called at the first call to add_request, for constraint data allocation > > > > > - * Must be called with the dev_pm_qos_mtx mutex held > > > > > + * Called to ensure that devices qos is allocated, before acquiring > > > > > + * dev_pm_qos_mtx. > > > > > */ > > > > > -static int dev_pm_qos_constraints_allocate(struct device *dev) > > > > > +static int dev_pm_qos_constraints_ensure_allocated(struct device *dev) > > > > > { > > > > > struct dev_pm_qos *qos; > > > > > struct pm_qos_constraints *c; > > > > > struct blocking_notifier_head *n; > > > > > > > > > > + if (!dev) > > > > > + return -ENODEV; > > > > > + > > > > > + if (!IS_ERR_OR_NULL(dev->power.qos)) > > > > > + return 0; > > > > > + > > > > > qos = kzalloc(sizeof(*qos), GFP_KERNEL); > > > > > if (!qos) > > > > > return -ENOMEM; > > > > > @@ -227,10 +233,26 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) > > > > > > > > > > INIT_LIST_HEAD(&qos->flags.list); > > > > > > > > > > + mutex_lock(&dev_pm_qos_mtx); > > > > > + > > > > > + if (!IS_ERR_OR_NULL(dev->power.qos)) { > > > > > + /* > > > > > + * We have raced with another task to create the qos. > > > > > + * No biggie, just free the resources we've allocated > > > > > + * outside of dev_pm_qos_mtx and move on with life. > > > > > + */ > > > > > + kfree(n); > > > > > + kfree(qos); > > > > > + goto unlock; > > > > > + } > > > > > + > > > > > spin_lock_irq(&dev->power.lock); > > > > > dev->power.qos = qos; > > > > > spin_unlock_irq(&dev->power.lock); > > > > > > > > > > +unlock: > > > > > + mutex_unlock(&dev_pm_qos_mtx); > > > > > + > > > > > return 0; > > > > > } > > > > > > > > > > @@ -331,17 +353,15 @@ static int __dev_pm_qos_add_request(struct device *dev, > > > > > { > > > > > int ret = 0; > > > > > > > > > > - if (!dev || !req || dev_pm_qos_invalid_req_type(dev, type)) > > > > > + if (!req || dev_pm_qos_invalid_req_type(dev, type)) > > > > > return -EINVAL; > > > > > > > > > > if (WARN(dev_pm_qos_request_active(req), > > > > > "%s() called for already added request\n", __func__)) > > > > > return -EINVAL; > > > > > > > > > > - if (IS_ERR(dev->power.qos)) > > > > > + if (IS_ERR_OR_NULL(dev->power.qos)) > > > > > ret = -ENODEV; > > > > > - else if (!dev->power.qos) > > > > > - ret = dev_pm_qos_constraints_allocate(dev); > > > > > > > > > > trace_dev_pm_qos_add_request(dev_name(dev), type, value); > > > > > if (ret) > > > > > @@ -390,6 +410,10 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, > > > > > { > > > > > int ret; > > > > > > > > > > + ret = dev_pm_qos_constraints_ensure_allocated(dev); > > > > > + if (ret) > > > > > + return ret; > > > > > + > > > > > > > > It is a bit unfortunate that the mutex is dropped and then immediately > > > > re-acquired again. I don't think that this is strictly necessary. > > > > > > We could have dev_pm_qos_constraints_ensure_allocated() return with > > > the lock held in the success case if we had to.. but that seems a bit > > > funny looking. And the dev_pm_qos_update_user_latency_tolerance() > > > path would need to shuffle slightly to move the kzalloc out of the > > > lock. > > > > Well, what about something like this (modulo whitespace damage by > > GMail), attached for completeness: > > > > There is one other path to handle, and some small details, Yes, this was just an illustration of the approach. > but I think the approach could work.. let's see.. OK
diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index 8e93167f1783..f3e0c6b65635 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -185,18 +185,24 @@ static int apply_constraint(struct dev_pm_qos_request *req, } /* - * dev_pm_qos_constraints_allocate + * dev_pm_qos_constraints_ensure_allocated * @dev: device to allocate data for * - * Called at the first call to add_request, for constraint data allocation - * Must be called with the dev_pm_qos_mtx mutex held + * Called to ensure that devices qos is allocated, before acquiring + * dev_pm_qos_mtx. */ -static int dev_pm_qos_constraints_allocate(struct device *dev) +static int dev_pm_qos_constraints_ensure_allocated(struct device *dev) { struct dev_pm_qos *qos; struct pm_qos_constraints *c; struct blocking_notifier_head *n; + if (!dev) + return -ENODEV; + + if (!IS_ERR_OR_NULL(dev->power.qos)) + return 0; + qos = kzalloc(sizeof(*qos), GFP_KERNEL); if (!qos) return -ENOMEM; @@ -227,10 +233,26 @@ static int dev_pm_qos_constraints_allocate(struct device *dev) INIT_LIST_HEAD(&qos->flags.list); + mutex_lock(&dev_pm_qos_mtx); + + if (!IS_ERR_OR_NULL(dev->power.qos)) { + /* + * We have raced with another task to create the qos. + * No biggie, just free the resources we've allocated + * outside of dev_pm_qos_mtx and move on with life. + */ + kfree(n); + kfree(qos); + goto unlock; + } + spin_lock_irq(&dev->power.lock); dev->power.qos = qos; spin_unlock_irq(&dev->power.lock); +unlock: + mutex_unlock(&dev_pm_qos_mtx); + return 0; } @@ -331,17 +353,15 @@ static int __dev_pm_qos_add_request(struct device *dev, { int ret = 0; - if (!dev || !req || dev_pm_qos_invalid_req_type(dev, type)) + if (!req || dev_pm_qos_invalid_req_type(dev, type)) return -EINVAL; if (WARN(dev_pm_qos_request_active(req), "%s() called for already added request\n", __func__)) return -EINVAL; - if (IS_ERR(dev->power.qos)) + if (IS_ERR_OR_NULL(dev->power.qos)) ret = -ENODEV; - else if (!dev->power.qos) - ret = dev_pm_qos_constraints_allocate(dev); trace_dev_pm_qos_add_request(dev_name(dev), type, value); if (ret) @@ -390,6 +410,10 @@ int dev_pm_qos_add_request(struct device *dev, struct dev_pm_qos_request *req, { int ret; + ret = dev_pm_qos_constraints_ensure_allocated(dev); + if (ret) + return ret; + mutex_lock(&dev_pm_qos_mtx); ret = __dev_pm_qos_add_request(dev, req, type, value); mutex_unlock(&dev_pm_qos_mtx); @@ -537,15 +561,11 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier, { int ret = 0; - mutex_lock(&dev_pm_qos_mtx); - - if (IS_ERR(dev->power.qos)) - ret = -ENODEV; - else if (!dev->power.qos) - ret = dev_pm_qos_constraints_allocate(dev); - + ret = dev_pm_qos_constraints_ensure_allocated(dev); if (ret) - goto unlock; + return ret; + + mutex_lock(&dev_pm_qos_mtx); switch (type) { case DEV_PM_QOS_RESUME_LATENCY: @@ -565,7 +585,6 @@ int dev_pm_qos_add_notifier(struct device *dev, struct notifier_block *notifier, ret = -EINVAL; } -unlock: mutex_unlock(&dev_pm_qos_mtx); return ret; } @@ -905,10 +924,13 @@ int dev_pm_qos_update_user_latency_tolerance(struct device *dev, s32 val) { int ret; + ret = dev_pm_qos_constraints_ensure_allocated(dev); + if (ret) + return ret; + mutex_lock(&dev_pm_qos_mtx); - if (IS_ERR_OR_NULL(dev->power.qos) - || !dev->power.qos->latency_tolerance_req) { + if (!dev->power.qos->latency_tolerance_req) { struct dev_pm_qos_request *req; if (val < 0) {