From bbe8512a93b0078c43fb5aa6f265059376647bc7 Mon Sep 17 00:00:00 2001 From: shodanshok Date: Wed, 21 Aug 2024 19:00:33 +0200 Subject: [PATCH] Ignore zfs_arc_shrinker_limit in direct reclaim mode zfs_arc_shrinker_limit (default: 10000) avoids ARC collapse due to excessive memory reclaim. However, when the kernel is in direct reclaim mode (ie: low on memory), limiting ARC reclaim increases OOM risk. This is especially true on system without (or with inadequate) swap. This patch ignores zfs_arc_shrinker_limit when the kernel is in direct reclaim mode, avoiding most OOM. It also restores "echo 3 > /proc/sys/vm/drop_caches" ability to correctly drop (almost) all ARC. Reviewed-by: Brian Behlendorf Reviewed-by: Adam Moss Signed-off-by: Gionatan Danti Closes #16313 --- man/man4/zfs.4 | 1 + module/os/linux/zfs/arc_os.c | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/man/man4/zfs.4 b/man/man4/zfs.4 index 2be3a8414a..20bb95c1ae 100644 --- a/man/man4/zfs.4 +++ b/man/man4/zfs.4 @@ -838,6 +838,7 @@ This is a limit on how many pages the ARC shrinker makes available for eviction in response to one page allocation attempt. Note that in practice, the kernel's shrinker can ask us to evict up to about four times this for one allocation attempt. +To reduce OOM risk, this limit is applied for kswapd reclaims only. .Pp The default limit of .Sy 10000 Pq in practice, Em 160 MiB No per allocation attempt with 4 KiB pages diff --git a/module/os/linux/zfs/arc_os.c b/module/os/linux/zfs/arc_os.c index 75a9ea5322..c6b9cb2ddb 100644 --- a/module/os/linux/zfs/arc_os.c +++ b/module/os/linux/zfs/arc_os.c @@ -201,9 +201,9 @@ arc_shrinker_count(struct shrinker *shrink, struct shrink_control *sc) * See also the comment above zfs_arc_shrinker_limit. */ int64_t can_free = btop(arc_evictable_memory()); - int64_t limit = zfs_arc_shrinker_limit != 0 ? - zfs_arc_shrinker_limit : INT64_MAX; - return (MIN(can_free, limit)); + if (current_is_kswapd() && zfs_arc_shrinker_limit) + can_free = MIN(can_free, zfs_arc_shrinker_limit); + return (can_free); } static unsigned long