[Devel] [PATCH rh7 v2 1/2] mm: fix division by zero in dcache_is_low()

Andrey Ryabinin aryabinin at virtuozzo.com
Tue Jul 25 14:58:01 MSK 2017


At first we check if sysctl_vfs_cache_min_ratio <= 0 and
if it's not we use it as denominator. If sysctl_vfs_cache_min_ratio
set to zero after the check but before division it would cause
division by zero, hence the kernel crash:

	divide error: 0000 [#1] SMP
	RIP: 0010:[<ffffffff81206fb5>]  [<ffffffff81206fb5>] mem_cgroup_dcache_is_low+0x55/0x80
	Call Trace:
	 super_cache_count+0xf4/0x180
	 shrink_slab+0x166/0x410
	 shrink_zone+0x11a/0x2d0
	 do_try_to_free_pages+0x1a0/0x570
	 try_to_free_mem_cgroup_pages+0xc6/0x160
	 mem_cgroup_reclaim+0x6b/0x180 [kpatch_cumulative_26_1_r1]
	 try_charge+0x18d/0x4e0 [kpatch_cumulative_26_1_r1]
	 mem_cgroup_try_charge+0x78/0x130
	 add_to_page_cache_locked+0x97/0x300
	 alloc_pages_current+0xaa/0x170
	 add_to_page_cache_lru+0x37/0xb0
	 grab_cache_page_write_begin+0x89/0xd0
	 ext4_da_write_begin+0xad/0x3a0 [ext4]
	 generic_file_buffered_write_iter+0x107/0x280
	 generic_file_write_iter+0x183/0x3c0
	 generic_file_aio_write+0x8b/0xb0
	 generic_file_aio_write+0x59/0xa0
	 ext4_file_write+0xdb/0x470 [ext4]
	 set_fd_set+0x21/0x30
	 core_sys_select+0x245/0x300
	 do_sync_write+0x90/0xe0
	 vfs_write+0xbd/0x1e0
	 SyS_write+0x7f/0xe0
	 system_call_fastpath+0x16/0x1b

Use READ_ONCE to cache sysctl_vfs_cache_min_ratio value into the
local variable, so we could use the cached value and not worry
about racy sysctl_vfs_cache_min_ratio updates.

https://jira.sw.ru/browse/PSBM-69018
Signed-off-by: Andrey Ryabinin <aryabinin at virtuozzo.com>
---

Changes since v1:
 - Pass vfs_cache_min_ratio into mem_cgroup_dcache_is_low() to 
     get rid of load+branch.
	
 fs/super.c                 | 7 ++++---
 include/linux/memcontrol.h | 5 +++--
 mm/memcontrol.c            | 4 ++--
 3 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/fs/super.c b/fs/super.c
index 7470621cbf4..aefaf15050f 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -51,18 +51,19 @@ static char *sb_writers_name[SB_FREEZE_LEVELS] = {
 static bool dcache_is_low(struct mem_cgroup *memcg)
 {
 	unsigned long anon, file, dcache;
+	int vfs_cache_min_ratio = READ_ONCE(sysctl_vfs_cache_min_ratio);
 
-	if (sysctl_vfs_cache_min_ratio <= 0)
+	if (vfs_cache_min_ratio <= 0)
 		return false;
 
 	if (memcg)
-		return mem_cgroup_dcache_is_low(memcg);
+		return mem_cgroup_dcache_is_low(memcg, vfs_cache_min_ratio);
 
 	anon = global_page_state(NR_ANON_PAGES);
 	file = global_page_state(NR_FILE_PAGES);
 	dcache = global_page_state(NR_SLAB_RECLAIMABLE);
 
-	return dcache / sysctl_vfs_cache_min_ratio <
+	return dcache / vfs_cache_min_ratio <
 			(anon + file + dcache) / 100;
 }
 
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index f6747e4c506..1a52e58ab7d 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -96,7 +96,7 @@ void mem_cgroup_iter_break(struct mem_cgroup *, struct mem_cgroup *);
 /*
  * For memory reclaim.
  */
-bool mem_cgroup_dcache_is_low(struct mem_cgroup *memcg);
+bool mem_cgroup_dcache_is_low(struct mem_cgroup *memcg, int vfs_cache_min_ratio);
 bool mem_cgroup_low(struct mem_cgroup *root, struct mem_cgroup *memcg);
 bool mem_cgroup_cleancache_disabled(struct page *page);
 int mem_cgroup_select_victim_node(struct mem_cgroup *memcg);
@@ -315,7 +315,8 @@ static inline void mem_cgroup_put(struct mem_cgroup *memcg)
 {
 }
 
-static inline bool mem_cgroup_dcache_is_low(struct mem_cgroup *memcg)
+static inline bool mem_cgroup_dcache_is_low(struct mem_cgroup *memcg,
+	int vfs_cache_min_ratio)
 {
 	return false;
 }
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index b1bc0924f6d..2dfe42073a0 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -1585,7 +1585,7 @@ int mem_cgroup_inactive_anon_is_low(struct lruvec *lruvec)
 	return inactive * inactive_ratio < active;
 }
 
-bool mem_cgroup_dcache_is_low(struct mem_cgroup *memcg)
+bool mem_cgroup_dcache_is_low(struct mem_cgroup *memcg, int vfs_cache_min_ratio)
 {
 	unsigned long anon, file, dcache;
 
@@ -1593,7 +1593,7 @@ bool mem_cgroup_dcache_is_low(struct mem_cgroup *memcg)
 	file = mem_cgroup_read_stat(memcg, MEM_CGROUP_STAT_CACHE);
 	dcache = mem_cgroup_read_stat(memcg, MEM_CGROUP_STAT_SLAB_RECLAIMABLE);
 
-	return dcache / sysctl_vfs_cache_min_ratio <
+	return dcache / vfs_cache_min_ratio <
 			(anon + file + dcache) / 100;
 }
 
-- 
2.13.0



More information about the Devel mailing list