From: Liu Shixin liushixin2@huawei.com
hulk inclusion category: bugfix bugzilla: 175105 CVE: NA
-------------------------------------------------
Command 'echo 2 > /proc/sys/vm/drop_caches' could lead to a loop in drop_slab_node for a long while. Add /proc/sys/vm/drop_caches_loop_limit to break loop in drop_slab_node.
Signed-off-by: Liu Shixin liushixin2@huawei.com Reviewed-by: tong tiangen tongtiangen@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- fs/drop_caches.c | 1 + include/linux/mm.h | 1 + kernel/sysctl.c | 8 ++++++++ mm/vmscan.c | 15 +++++++++++++++ 4 files changed, 25 insertions(+)
diff --git a/fs/drop_caches.c b/fs/drop_caches.c index dc1a1d5d825b4..1f866b32cd150 100644 --- a/fs/drop_caches.c +++ b/fs/drop_caches.c @@ -13,6 +13,7 @@
/* A global variable is a bit ugly, but it keeps the code simple */ int sysctl_drop_caches; +unsigned int sysctl_drop_caches_loop_limit __read_mostly;
static void drop_pagecache_sb(struct super_block *sb, void *unused) { diff --git a/include/linux/mm.h b/include/linux/mm.h index c0e5a33230365..99fd979c03d5b 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -2766,6 +2766,7 @@ extern bool process_shares_mm(struct task_struct *p, struct mm_struct *mm);
#ifdef CONFIG_SYSCTL extern int sysctl_drop_caches; +extern unsigned int sysctl_drop_caches_loop_limit; int drop_caches_sysctl_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *); #endif diff --git a/kernel/sysctl.c b/kernel/sysctl.c index af4d97b9dfce6..1733b8c71b117 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -1477,6 +1477,14 @@ static struct ctl_table vm_table[] = { .extra1 = &one, .extra2 = &four, }, + { + .procname = "drop_caches_loop_limit", + .data = &sysctl_drop_caches_loop_limit, + .maxlen = sizeof(unsigned int), + .mode = 0644, + .proc_handler = proc_douintvec, + }, + #ifdef CONFIG_COMPACTION { .procname = "compact_memory", diff --git a/mm/vmscan.c b/mm/vmscan.c index d04c8ed5c22e5..6b32cd0fa8c94 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -735,6 +735,9 @@ static unsigned long shrink_slab(gfp_t gfp_mask, int nid, void drop_slab_node(int nid) { unsigned long freed; +#ifdef CONFIG_SYSCTL + unsigned int counts = 0; +#endif
do { struct mem_cgroup *memcg = NULL; @@ -747,6 +750,18 @@ void drop_slab_node(int nid) do { freed += shrink_slab(GFP_KERNEL, nid, memcg, 0); } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL)) != NULL); + +#ifdef CONFIG_SYSCTL + if (unlikely(sysctl_drop_caches_loop_limit)) { + counts++; + if (counts >= sysctl_drop_caches_loop_limit) { + pr_info("%s (%d): drop_caches early break: %u loops\n", + current->comm, task_pid_nr(current), + counts); + return; + } + } +#endif } while (freed > 10); }