Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mm: don't miss the last page because of round-off error

I've noticed, that dying memory cgroups are often pinned in memory by a
single pagecache page. Even under moderate memory pressure they sometimes
stayed in such state for a long time. That looked strange.

My investigation showed that the problem is caused by applying the LRU
pressure balancing math:

scan = div64_u64(scan * fraction[lru], denominator),

where

denominator = fraction[anon] + fraction[file] + 1.

Because fraction[lru] is always less than denominator, if the initial scan
size is 1, the result is always 0.

This means the last page is not scanned and has
no chances to be reclaimed.

Fix this by rounding up the result of the division.

In practice this change significantly improves the speed of dying cgroups
reclaim.

[guro@fb.com: prevent double calculation of DIV64_U64_ROUND_UP() arguments]
Link: http://lkml.kernel.org/r/20180829213311.GA13501@castle
Link: http://lkml.kernel.org/r/20180827162621.30187-3-guro@fb.com
Signed-off-by: Roman Gushchin <guro@fb.com>
Reviewed-by: Andrew Morton <akpm@linux-foundation.org>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Tejun Heo <tj@kernel.org>
Cc: Rik van Riel <riel@surriel.com>
Cc: Konstantin Khlebnikov <koct9i@gmail.com>
Cc: Matthew Wilcox <willy@infradead.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Roman Gushchin and committed by
Linus Torvalds
68600f62 591edfb1

+7 -2
+3
include/linux/math64.h
··· 281 281 } 282 282 #endif /* mul_u64_u32_div */ 283 283 284 + #define DIV64_U64_ROUND_UP(ll, d) \ 285 + ({ u64 _tmp = (d); div64_u64((ll) + _tmp - 1, _tmp); }) 286 + 284 287 #endif /* _LINUX_MATH64_H */
+4 -2
mm/vmscan.c
··· 2456 2456 /* 2457 2457 * Scan types proportional to swappiness and 2458 2458 * their relative recent reclaim efficiency. 2459 + * Make sure we don't miss the last page 2460 + * because of a round-off error. 2459 2461 */ 2460 - scan = div64_u64(scan * fraction[file], 2461 - denominator); 2462 + scan = DIV64_U64_ROUND_UP(scan * fraction[file], 2463 + denominator); 2462 2464 break; 2463 2465 case SCAN_FILE: 2464 2466 case SCAN_ANON: