From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6EC60C001DC for ; Thu, 22 Jun 2023 17:42:05 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id B3BA18D0002; Thu, 22 Jun 2023 13:42:04 -0400 (EDT) Received: by kanga.kvack.org (Postfix, from userid 40) id AEBF08D0001; Thu, 22 Jun 2023 13:42:04 -0400 (EDT) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id 98C5C8D0002; Thu, 22 Jun 2023 13:42:04 -0400 (EDT) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (smtprelay0011.hostedemail.com [216.40.44.11]) by kanga.kvack.org (Postfix) with ESMTP id 8C2AC8D0001 for ; Thu, 22 Jun 2023 13:42:04 -0400 (EDT) Received: from smtpin26.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay01.hostedemail.com (Postfix) with ESMTP id 34EDC1C8F94 for ; Thu, 22 Jun 2023 17:42:04 +0000 (UTC) X-FDA: 80931102168.26.9BE1D69 Received: from mail-pl1-f178.google.com (mail-pl1-f178.google.com [209.85.214.178]) by imf24.hostedemail.com (Postfix) with ESMTP id 1CD08180012 for ; Thu, 22 Jun 2023 17:42:00 +0000 (UTC) Authentication-Results: imf24.hostedemail.com; dkim=pass header.d=gmail.com header.s=20221208 header.b=GUhWgwtW; spf=pass (imf24.hostedemail.com: domain of mmpgouride@gmail.com designates 209.85.214.178 as permitted sender) smtp.mailfrom=mmpgouride@gmail.com; dmarc=pass (policy=none) header.from=gmail.com ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=hostedemail.com; s=arc-20220608; t=1687455721; h=from:from:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:dkim-signature; bh=YR6riefaBxO/qJacf8c9XUzeqz5GLT8THdv+ODXmKSw=; b=q9rifp/493BWhRQBuzDOzQI9iyB0ECMsSCZoO852MLn2l49M9RMskjL1lTf+oUJzXl69dL KpejOWj9o5BqFBtdj2fEX5Z0EafHNzD8yRnFym+2VvdieIhznTDCGOGjfEhJkOG+6+GMe1 q+DvM/gT0iwpJ6FQhFZU8GjosxZFOqo= ARC-Authentication-Results: i=1; imf24.hostedemail.com; dkim=pass header.d=gmail.com header.s=20221208 header.b=GUhWgwtW; spf=pass (imf24.hostedemail.com: domain of mmpgouride@gmail.com designates 209.85.214.178 as permitted sender) smtp.mailfrom=mmpgouride@gmail.com; dmarc=pass (policy=none) header.from=gmail.com ARC-Seal: i=1; s=arc-20220608; d=hostedemail.com; t=1687455721; a=rsa-sha256; cv=none; b=0FMpEZh8g792fToNTbuAXPRRxqE9qbWhLJSBR5G08aYVYR5ZBnwdyKa9Iuzq4lIcEIlnOV DAii63DB+3p3TAq4ZfRX++NFCdv69I/0dPqTVVz5nngKuMIkEJ0ia1ue67CKktwlgn2/ZA A1z+O4IWll6hV5OXZpn1ruqsweI6Uxs= Received: by mail-pl1-f178.google.com with SMTP id d9443c01a7336-1b50394a7f2so44476145ad.1 for ; Thu, 22 Jun 2023 10:42:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1687455720; x=1690047720; h=to:references:message-id:content-transfer-encoding:cc:date :in-reply-to:from:subject:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=YR6riefaBxO/qJacf8c9XUzeqz5GLT8THdv+ODXmKSw=; b=GUhWgwtWtKrZDnuSJCgYa1cOw3G6fiKz0lIoUCvLREEz71vtcRYCluXM5YqEsRm9ht Wn3m02SHfuwML1dU476ZrelAyvPgytRnH6hfCrsEGmk5ZKgs3nce1OiWf4U8+FvY56Wh GDA/5zHheVC9RMAFUkTyeJiT8xFBcA8sCkmGJvx5ANxTn7C6VnAVryToqgVv5/gq3VOW 4jhoVMGXImhjBO7rYVEvL60C/y+4lw+SHMprbFFYboLGhwPmB8UPwDlVhO1pxlhvbzso KErSYpLEtubkl3M447W8C9h0SJECCFplCNsy9zdLmyrhsytB8LHHZhCPFLxQ3YSYRMRm Q9Ng== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1687455720; x=1690047720; h=to:references:message-id:content-transfer-encoding:cc:date :in-reply-to:from:subject:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=YR6riefaBxO/qJacf8c9XUzeqz5GLT8THdv+ODXmKSw=; b=boumECRHdznSkE1N+iQvSDrXGro54AwQ/1yz/C3RTqQkqmSbmoC2a+hAJ9086fGbPx e4hpK0pLA/+xcPavDSLdlEyH2RkE/KW/67loEHALzIaNK67ruXE6pW1FZ2CUAcsw9Ajr sPYhHrAO32kd05wqkbmcQ4J5avmbAMSx5+gNKve7YD6GMQ22yijsgRq4QOdtEwLnN3N8 V2I0KSqWeFEAM4TbMUjkz/zuOVJ9x/dO/yk/yQt8wjABEQy7ZPIMOwDFxqTVb4sFIRIO CQXtQ3kOXXFxCeABd2Vb4l4FCzUCZhnsIDrSFOcDSFCIk+KUIuxIeskzC18Mmf4bVNY0 r7XA== X-Gm-Message-State: AC+VfDxD+FTl7k3yxAfOCG1CGuIXLk9yj0PyBASSpNO6ABgKOb7qdd5B irmjBABuYERsEKtNI6pzKYs= X-Google-Smtp-Source: ACHHUZ7evwZkAV8a0wVuAaIAW+pO3mkR7GW9pnxAUzkJDJVxa9Kkl4NfZezomSDymMDqt+DaH+7SkA== X-Received: by 2002:a17:902:ecc8:b0:1b5:561a:5ca9 with SMTP id a8-20020a170902ecc800b001b5561a5ca9mr14043372plh.50.1687455719660; Thu, 22 Jun 2023 10:41:59 -0700 (PDT) Received: from [127.0.0.1] ([2402:d0c0:2:a2a::1]) by smtp.gmail.com with ESMTPSA id jk14-20020a170903330e00b001b3d756a6f4sm5715243plb.13.2023.06.22.10.41.52 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Thu, 22 Jun 2023 10:41:59 -0700 (PDT) Content-Type: text/plain; charset=utf-8 Mime-Version: 1.0 (Mac OS X Mail 14.0 \(3654.60.0.2.21\)) Subject: Re: [PATCH 24/29] mm: vmscan: make global slab shrink lockless From: Alan Huang In-Reply-To: Date: Fri, 23 Jun 2023 01:41:47 +0800 Cc: Vlastimil Babka , akpm@linux-foundation.org, Dave Chinner , tkhai@ya.ru, roman.gushchin@linux.dev, "Darrick J. Wong" , brauner@kernel.org, paulmck@kernel.org, tytso@mit.edu, linux-kernel@vger.kernel.org, linux-mm@kvack.org, intel-gfx@lists.freedesktop.org, dri-devel@lists.freedesktop.org, linux-arm-msm@vger.kernel.org, dm-devel@redhat.com, linux-raid@vger.kernel.org, linux-bcache@vger.kernel.org, virtualization@lists.linux-foundation.org, linux-fsdevel@vger.kernel.org, linux-ext4@vger.kernel.org, linux-nfs@vger.kernel.org, linux-xfs@vger.kernel.org, linux-btrfs@vger.kernel.org Content-Transfer-Encoding: quoted-printable Message-Id: <43CEA22D-3FF5-40CB-BF07-0FB9829EF778@gmail.com> References: <20230622085335.77010-1-zhengqi.arch@bytedance.com> <20230622085335.77010-25-zhengqi.arch@bytedance.com> To: Qi Zheng X-Mailer: Apple Mail (2.3654.60.0.2.21) X-Rspamd-Queue-Id: 1CD08180012 X-Rspam-User: X-Stat-Signature: q41xyim7a6tzp5sabo47gfe4dnpa81zd X-Rspamd-Server: rspam01 X-HE-Tag: 1687455720-314080 X-HE-Meta: U2FsdGVkX1+kmV54ZzswwQiBk+cLi0Jn/y7R7IQCnuE0LAea1BBy84RVjLNXinNEXmXGNTujf6FUKy8AGm0c+yusTBt5vYUxzHXlwNlxjM3DmrXmHxH9N3zvOjsfD0cooyroiFvEkU4BNDQVtf7MWG4LOtauqAOnrF7pJhs8MfLe74HuJIXSu8yC/CW8N57MdCjOYWqL33MkDvaH9IK1rE73MD+jkJ/Q62stccQv95WBUTeEa/EG3gLZ4iICY0C1ZewjMF/n3zSPDKj/1Zr4KBknhLbY4bqS6k0ozXHswi1UovS9m2qMvwfCo80JVvg8WqSwFTHrwDxKGYFOprTIwurQ3AP7QucqhGB64xRqYcC6qB/sZHdGxoyzBd3tzzxKuiw2i1yjqxjL+iVft4ztSpMJrtj/zfk6Id+xVplZkpmUUjxhUZdo3cOSku3yCKMCRHrvhuVUb+Mpk360C2bDoz7VWhNRvdyELxqPsKx0PH+FjczSMi7ch0hla7R9I0nc0CQpjRvRP5l3AiNOWGN98niYZyf+D9AHMA3JPE+dRFeVcD2iBXIH4VO12woA4U3Ok4n0rwyNV9bHFX7AabyxXf2ZRUFhtLRNLP+NXgnTQiXoJqD8lf+cj7VE5iDnH1dXeybZKVLkIQD4GRPg7vxLNcV3Vph3Oa0gfQWzmBznHZumB+B+nIjEXpj/r5E4rwj4df82zI9vkR20Am3N0AqFzuAG41eWXmmiAkh0cpBPSNGLAU2P3C5LE5reEKxKcUXW3a+KTnrkyzYCZCLiEp7OGvKA3k5t+bGakR6Q4207aPzhxcJmW6YWCNLqF9iqNTKSoZPDcLweHZgYkB74/ItYLnhXa/cEHr+6F+7w05wa2YLm6jqjEyOlQuhHYbVsXmL8n2XeuyMBeqKb8g2kIU+4UfuV6+i/AdYE1IVV9+t3deibyhvQHrzUOHSzkgyZq7+UwcmHDLHCgtCedB/vjXD MvLFQuxk f0tjv0hGB/Ob75O4Nx4ligBWr9lfZ8ioSqFByxKwAWZ7kQJkOLb5D7UxtXQP99feWan5QoELnQJERiRicAoK0eI6O/KWbZBrKtUSSvtVN/i7r3TT8PURQ4OGUOCjcyHYlTow6/u6/UlNLz3ZQLPqeX3vIpq4Id24RsjfLHzsb5N7lSEHGvKdTeDyzPUb49vQ1SkNtQ4c9eZIH4MEVzbDaQgUmEwubrWnL/O1Io91V9nlsm1Yu8VshQ2XbBs/IFwdZnW6Dmae+OpyYnR3ZNYYbiK1pBPgJPpGLwckd62ESct7soUEqlWpqESu0AHe2NbDmf4guKxg67qdKDt/oeONMYx4qyzpzcduLIj8TIipgJpQxsLddFUtMxu35A6jgfPMo6LqyufLoFY3IPmuBievxplOHpGzQOyF65eHvamJvXe/6qSiAFvvqEFIWOh7k9Dipb3GURBtHnshUvJUF0D0qKqmIauD+YznAl6+ihAvseVS+oFv6KuXbyq9+7IJWJGcuP6ed8Trfdbis0vUTEygiQyOXx3r0MXUCvFgVEHWbdfwZX7Zv5qSHheKpsgeX1WiNAZ6bALUmTTx/N8+PIxU9zw8Cz1TIVvhTSpIzh7UknDz44zE= X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: > 2023=E5=B9=B46=E6=9C=8823=E6=97=A5 =E4=B8=8A=E5=8D=8812:42=EF=BC=8CQi = Zheng =E5=86=99=E9=81=93=EF=BC=9A >=20 >=20 >=20 > On 2023/6/22 23:12, Vlastimil Babka wrote: >> On 6/22/23 10:53, Qi Zheng wrote: >>> The shrinker_rwsem is a global read-write lock in >>> shrinkers subsystem, which protects most operations >>> such as slab shrink, registration and unregistration >>> of shrinkers, etc. This can easily cause problems in >>> the following cases. >>>=20 >>> 1) When the memory pressure is high and there are many >>> filesystems mounted or unmounted at the same time, >>> slab shrink will be affected (down_read_trylock() >>> failed). >>>=20 >>> Such as the real workload mentioned by Kirill Tkhai: >>>=20 >>> ``` >>> One of the real workloads from my experience is start >>> of an overcommitted node containing many starting >>> containers after node crash (or many resuming containers >>> after reboot for kernel update). In these cases memory >>> pressure is huge, and the node goes round in long reclaim. >>> ``` >>>=20 >>> 2) If a shrinker is blocked (such as the case mentioned >>> in [1]) and a writer comes in (such as mount a fs), >>> then this writer will be blocked and cause all >>> subsequent shrinker-related operations to be blocked. >>>=20 >>> Even if there is no competitor when shrinking slab, there >>> may still be a problem. If we have a long shrinker list >>> and we do not reclaim enough memory with each shrinker, >>> then the down_read_trylock() may be called with high >>> frequency. Because of the poor multicore scalability of >>> atomic operations, this can lead to a significant drop >>> in IPC (instructions per cycle). >>>=20 >>> We used to implement the lockless slab shrink with >>> SRCU [1], but then kernel test robot reported -88.8% >>> regression in stress-ng.ramfs.ops_per_sec test case [2], >>> so we reverted it [3]. >>>=20 >>> This commit uses the refcount+RCU method [4] proposed by >>> by Dave Chinner to re-implement the lockless global slab >>> shrink. The memcg slab shrink is handled in the subsequent >>> patch. >>>=20 >>> Currently, the shrinker instances can be divided into >>> the following three types: >>>=20 >>> a) global shrinker instance statically defined in the kernel, >>> such as workingset_shadow_shrinker. >>>=20 >>> b) global shrinker instance statically defined in the kernel >>> modules, such as mmu_shrinker in x86. >>>=20 >>> c) shrinker instance embedded in other structures. >>>=20 >>> For case a, the memory of shrinker instance is never freed. >>> For case b, the memory of shrinker instance will be freed >>> after the module is unloaded. But we will call synchronize_rcu() >>> in free_module() to wait for RCU read-side critical section to >>> exit. For case c, the memory of shrinker instance will be >>> dynamically freed by calling kfree_rcu(). So we can use >>> rcu_read_{lock,unlock}() to ensure that the shrinker instance >>> is valid. >>>=20 >>> The shrinker::refcount mechanism ensures that the shrinker >>> instance will not be run again after unregistration. So the >>> structure that records the pointer of shrinker instance can be >>> safely freed without waiting for the RCU read-side critical >>> section. >>>=20 >>> In this way, while we implement the lockless slab shrink, we >>> don't need to be blocked in unregister_shrinker() to wait >>> RCU read-side critical section. >>>=20 >>> The following are the test results: >>>=20 >>> stress-ng --timeout 60 --times --verify --metrics-brief --ramfs 9 & >>>=20 >>> 1) Before applying this patchset: >>>=20 >>> setting to a 60 second run per stressor >>> dispatching hogs: 9 ramfs >>> stressor bogo ops real time usr time sys time bogo ops/s = bogo ops/s >>> (secs) (secs) (secs) (real time) = (usr+sys time) >>> ramfs 880623 60.02 7.71 226.93 14671.45 = 3753.09 >>> ramfs: >>> 1 System Management Interrupt >>> for a 60.03s run time: >>> 5762.40s available CPU time >>> 7.71s user time ( 0.13%) >>> 226.93s system time ( 3.94%) >>> 234.64s total time ( 4.07%) >>> load average: 8.54 3.06 2.11 >>> passed: 9: ramfs (9) >>> failed: 0 >>> skipped: 0 >>> successful run completed in 60.03s (1 min, 0.03 secs) >>>=20 >>> 2) After applying this patchset: >>>=20 >>> setting to a 60 second run per stressor >>> dispatching hogs: 9 ramfs >>> stressor bogo ops real time usr time sys time bogo ops/s = bogo ops/s >>> (secs) (secs) (secs) (real time) = (usr+sys time) >>> ramfs 847562 60.02 7.44 230.22 14120.66 = 3566.23 >>> ramfs: >>> 4 System Management Interrupts >>> for a 60.12s run time: >>> 5771.95s available CPU time >>> 7.44s user time ( 0.13%) >>> 230.22s system time ( 3.99%) >>> 237.66s total time ( 4.12%) >>> load average: 8.18 2.43 0.84 >>> passed: 9: ramfs (9) >>> failed: 0 >>> skipped: 0 >>> successful run completed in 60.12s (1 min, 0.12 secs) >>>=20 >>> We can see that the ops/s has hardly changed. >>>=20 >>> [1]. = https://lore.kernel.org/lkml/20230313112819.38938-1-zhengqi.arch@bytedance= .com/ >>> [2]. = https://lore.kernel.org/lkml/202305230837.db2c233f-yujie.liu@intel.com/ >>> [3]. = https://lore.kernel.org/all/20230609081518.3039120-1-qi.zheng@linux.dev/ >>> [4]. = https://lore.kernel.org/lkml/ZIJhou1d55d4H1s0@dread.disaster.area/ >>>=20 >>> Signed-off-by: Qi Zheng >>> --- >>> include/linux/shrinker.h | 6 ++++++ >>> mm/vmscan.c | 33 ++++++++++++++------------------- >>> 2 files changed, 20 insertions(+), 19 deletions(-) >>>=20 >>> diff --git a/include/linux/shrinker.h b/include/linux/shrinker.h >>> index 7bfeb2f25246..b0c6c2df9db8 100644 >>> --- a/include/linux/shrinker.h >>> +++ b/include/linux/shrinker.h >>> @@ -74,6 +74,7 @@ struct shrinker { >>> refcount_t refcount; >>> struct completion completion_wait; >>> + struct rcu_head rcu; >>> void *private_data; >>> @@ -123,6 +124,11 @@ struct shrinker = *shrinker_alloc_and_init(count_objects_cb count, >>> void shrinker_free(struct shrinker *shrinker); >>> void unregister_and_free_shrinker(struct shrinker *shrinker); >>> +static inline bool shrinker_try_get(struct shrinker *shrinker) >>> +{ >>> + return refcount_inc_not_zero(&shrinker->refcount); >>> +} >>> + >>> static inline void shrinker_put(struct shrinker *shrinker) >>> { >>> if (refcount_dec_and_test(&shrinker->refcount)) >>> diff --git a/mm/vmscan.c b/mm/vmscan.c >>> index 6f9c4750effa..767569698946 100644 >>> --- a/mm/vmscan.c >>> +++ b/mm/vmscan.c >>> @@ -57,6 +57,7 @@ >>> #include >>> #include >>> #include >>> +#include >>> #include >>> #include >>> @@ -742,7 +743,7 @@ void register_shrinker_prepared(struct shrinker = *shrinker) >>> down_write(&shrinker_rwsem); >>> refcount_set(&shrinker->refcount, 1); >>> init_completion(&shrinker->completion_wait); >>> - list_add_tail(&shrinker->list, &shrinker_list); >>> + list_add_tail_rcu(&shrinker->list, &shrinker_list); >>> shrinker->flags |=3D SHRINKER_REGISTERED; >>> shrinker_debugfs_add(shrinker); >>> up_write(&shrinker_rwsem); >>> @@ -800,7 +801,7 @@ void unregister_shrinker(struct shrinker = *shrinker) >>> wait_for_completion(&shrinker->completion_wait); >>> down_write(&shrinker_rwsem); >>> - list_del(&shrinker->list); >>> + list_del_rcu(&shrinker->list); >>> shrinker->flags &=3D ~SHRINKER_REGISTERED; >>> if (shrinker->flags & SHRINKER_MEMCG_AWARE) >>> unregister_memcg_shrinker(shrinker); >>> @@ -845,7 +846,7 @@ EXPORT_SYMBOL(shrinker_free); >>> void unregister_and_free_shrinker(struct shrinker *shrinker) >>> { >>> unregister_shrinker(shrinker); >>> - kfree(shrinker); >>> + kfree_rcu(shrinker, rcu); >>> } >>> EXPORT_SYMBOL(unregister_and_free_shrinker); >>> @@ -1067,33 +1068,27 @@ static unsigned long shrink_slab(gfp_t = gfp_mask, int nid, >>> if (!mem_cgroup_disabled() && !mem_cgroup_is_root(memcg)) >>> return shrink_slab_memcg(gfp_mask, nid, memcg, = priority); >>> - if (!down_read_trylock(&shrinker_rwsem)) >>> - goto out; >>> - >>> - list_for_each_entry(shrinker, &shrinker_list, list) { >>> + rcu_read_lock(); >>> + list_for_each_entry_rcu(shrinker, &shrinker_list, list) { >>> struct shrink_control sc =3D { >>> .gfp_mask =3D gfp_mask, >>> .nid =3D nid, >>> .memcg =3D memcg, >>> }; >>> + if (!shrinker_try_get(shrinker)) >>> + continue; >>> + rcu_read_unlock(); >> I don't think you can do this unlock? >>> + >>> ret =3D do_shrink_slab(&sc, shrinker, priority); >>> if (ret =3D=3D SHRINK_EMPTY) >>> ret =3D 0; >>> freed +=3D ret; >>> - /* >>> - * Bail out if someone want to register a new shrinker = to >>> - * prevent the registration from being stalled for long = periods >>> - * by parallel ongoing shrinking. >>> - */ >>> - if (rwsem_is_contended(&shrinker_rwsem)) { >>> - freed =3D freed ? : 1; >>> - break; >>> - } >>> - } >>> - up_read(&shrinker_rwsem); >>> -out: >>> + rcu_read_lock(); >> That new rcu_read_lock() won't help AFAIK, the whole >> list_for_each_entry_rcu() needs to be under the single = rcu_read_lock() to be >> safe. >=20 > In the unregister_shrinker() path, we will wait for the refcount to = zero > before deleting the shrinker from the linked list. Here, we first took > the rcu lock, and then decrement the refcount of this shrinker. >=20 > shrink_slab unregister_shrinker > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D = =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > =09 > /* wait for B */ > wait_for_completion() > rcu_read_lock() >=20 > shrinker_put() --> (B) > list_del_rcu() > /* wait for rcu_read_unlock() */ > kfree_rcu() >=20 > /* > * so this shrinker will not be freed here, > * and can be used to traverse the next node > * normally? > */ > list_for_each_entry() >=20 > shrinker_try_get() > rcu_read_unlock() >=20 > Did I miss something? After calling rcu_read_unlock(), the next shrinker in the list can be = freed, so in the next iteration, use after free might happen? Is that right? >=20 >> IIUC this is why Dave in [4] suggests unifying shrink_slab() with >> shrink_slab_memcg(), as the latter doesn't iterate the list but uses = IDR. >>> + shrinker_put(shrinker); >>> + } >>> + rcu_read_unlock(); >>> cond_resched(); >>> return freed; >>> }