Commit 56f3547b authored by Feng Tang's avatar Feng Tang Committed by Linus Torvalds

mm: adjust vm_committed_as_batch according to vm overcommit policy

When checking a performance change for will-it-scale scalability mmap test
[1], we found very high lock contention for spinlock of percpu counter
'vm_committed_as':

    94.14%     0.35%  [kernel.kallsyms]         [k] _raw_spin_lock_irqsave
    48.21% _raw_spin_lock_irqsave;percpu_counter_add_batch;__vm_enough_memory;mmap_region;do_mmap;
    45.91% _raw_spin_lock_irqsave;percpu_counter_add_batch;__do_munmap;

Actually this heavy lock contention is not always necessary.  The
'vm_committed_as' needs to be very precise when the strict
OVERCOMMIT_NEVER policy is set, which requires a rather small batch number
for the percpu counter.

So keep 'batch' number unchanged for strict OVERCOMMIT_NEVER policy, and
lift it to 64X for OVERCOMMIT_ALWAYS and OVERCOMMIT_GUESS policies.  Also
add a sysctl handler to adjust it when the policy is reconfigured.

Benchmark with the same testcase in [1] shows 53% improvement on a 8C/16T
desktop, and 2097%(20X) on a 4S/72C/144T server.  We tested with test
platforms in 0day (server, desktop and laptop), and 80%+ platforms shows
improvements with that test.  And whether it shows improvements depends on
if the test mmap size is bigger than the batch number computed.

And if the lift is 16X, 1/3 of the platforms will show improvements,
though it should help the mmap/unmap usage generally, as Michal Hocko
mentioned:

: I believe that there are non-synthetic worklaods which would benefit from
: a larger batch.  E.g.  large in memory databases which do large mmaps
: during startups from multiple threads.

[1] https://lore.kernel.org/lkml/20200305062138.GI5972@shao2-debian/Signed-off-by: default avatarFeng Tang <feng.tang@intel.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Acked-by: default avatarMichal Hocko <mhocko@suse.com>
Cc: Matthew Wilcox (Oracle) <willy@infradead.org>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Mel Gorman <mgorman@suse.de>
Cc: Qian Cai <cai@lca.pw>
Cc: Kees Cook <keescook@chromium.org>
Cc: Andi Kleen <andi.kleen@intel.com>
Cc: Tim Chen <tim.c.chen@intel.com>
Cc: Dave Hansen <dave.hansen@intel.com>
Cc: Huang Ying <ying.huang@intel.com>
Cc: Christoph Lameter <cl@linux.com>
Cc: Dennis Zhou <dennis@kernel.org>
Cc: Haiyang Zhang <haiyangz@microsoft.com>
Cc: kernel test robot <rong.a.chen@intel.com>
Cc: "K. Y. Srinivasan" <kys@microsoft.com>
Cc: Tejun Heo <tj@kernel.org>
Link: http://lkml.kernel.org/r/1589611660-89854-4-git-send-email-feng.tang@intel.com
Link: http://lkml.kernel.org/r/1592725000-73486-4-git-send-email-feng.tang@intel.com
Link: http://lkml.kernel.org/r/1594389708-60781-5-git-send-email-feng.tang@intel.comSigned-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 0a4954a8
...@@ -206,6 +206,8 @@ int overcommit_ratio_handler(struct ctl_table *, int, void *, size_t *, ...@@ -206,6 +206,8 @@ int overcommit_ratio_handler(struct ctl_table *, int, void *, size_t *,
loff_t *); loff_t *);
int overcommit_kbytes_handler(struct ctl_table *, int, void *, size_t *, int overcommit_kbytes_handler(struct ctl_table *, int, void *, size_t *,
loff_t *); loff_t *);
int overcommit_policy_handler(struct ctl_table *, int, void *, size_t *,
loff_t *);
#define nth_page(page,n) pfn_to_page(page_to_pfn((page)) + (n)) #define nth_page(page,n) pfn_to_page(page_to_pfn((page)) + (n))
......
...@@ -57,8 +57,12 @@ extern struct percpu_counter vm_committed_as; ...@@ -57,8 +57,12 @@ extern struct percpu_counter vm_committed_as;
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
extern s32 vm_committed_as_batch; extern s32 vm_committed_as_batch;
extern void mm_compute_batch(int overcommit_policy);
#else #else
#define vm_committed_as_batch 0 #define vm_committed_as_batch 0
static inline void mm_compute_batch(int overcommit_policy)
{
}
#endif #endif
unsigned long vm_memory_committed(void); unsigned long vm_memory_committed(void);
......
...@@ -2671,7 +2671,7 @@ static struct ctl_table vm_table[] = { ...@@ -2671,7 +2671,7 @@ static struct ctl_table vm_table[] = {
.data = &sysctl_overcommit_memory, .data = &sysctl_overcommit_memory,
.maxlen = sizeof(sysctl_overcommit_memory), .maxlen = sizeof(sysctl_overcommit_memory),
.mode = 0644, .mode = 0644,
.proc_handler = proc_dointvec_minmax, .proc_handler = overcommit_policy_handler,
.extra1 = SYSCTL_ZERO, .extra1 = SYSCTL_ZERO,
.extra2 = &two, .extra2 = &two,
}, },
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/memory.h> #include <linux/memory.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/mman.h>
#include "internal.h" #include "internal.h"
#ifdef CONFIG_DEBUG_MEMORY_INIT #ifdef CONFIG_DEBUG_MEMORY_INIT
...@@ -144,14 +145,23 @@ EXPORT_SYMBOL_GPL(mm_kobj); ...@@ -144,14 +145,23 @@ EXPORT_SYMBOL_GPL(mm_kobj);
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
s32 vm_committed_as_batch = 32; s32 vm_committed_as_batch = 32;
static void __meminit mm_compute_batch(void) void mm_compute_batch(int overcommit_policy)
{ {
u64 memsized_batch; u64 memsized_batch;
s32 nr = num_present_cpus(); s32 nr = num_present_cpus();
s32 batch = max_t(s32, nr*2, 32); s32 batch = max_t(s32, nr*2, 32);
unsigned long ram_pages = totalram_pages();
/* batch size set to 0.4% of (total memory/#cpus), or max int32 */ /*
memsized_batch = min_t(u64, (totalram_pages()/nr)/256, 0x7fffffff); * For policy OVERCOMMIT_NEVER, set batch size to 0.4% of
* (total memory/#cpus), and lift it to 25% for other policies
* to easy the possible lock contention for percpu_counter
* vm_committed_as, while the max limit is INT_MAX
*/
if (overcommit_policy == OVERCOMMIT_NEVER)
memsized_batch = min_t(u64, ram_pages/nr/256, INT_MAX);
else
memsized_batch = min_t(u64, ram_pages/nr/4, INT_MAX);
vm_committed_as_batch = max_t(s32, memsized_batch, batch); vm_committed_as_batch = max_t(s32, memsized_batch, batch);
} }
...@@ -162,7 +172,7 @@ static int __meminit mm_compute_batch_notifier(struct notifier_block *self, ...@@ -162,7 +172,7 @@ static int __meminit mm_compute_batch_notifier(struct notifier_block *self,
switch (action) { switch (action) {
case MEM_ONLINE: case MEM_ONLINE:
case MEM_OFFLINE: case MEM_OFFLINE:
mm_compute_batch(); mm_compute_batch(sysctl_overcommit_memory);
default: default:
break; break;
} }
...@@ -176,7 +186,7 @@ static struct notifier_block compute_batch_nb __meminitdata = { ...@@ -176,7 +186,7 @@ static struct notifier_block compute_batch_nb __meminitdata = {
static int __init mm_compute_batch_init(void) static int __init mm_compute_batch_init(void)
{ {
mm_compute_batch(); mm_compute_batch(sysctl_overcommit_memory);
register_hotmemory_notifier(&compute_batch_nb); register_hotmemory_notifier(&compute_batch_nb);
return 0; return 0;
......
...@@ -746,6 +746,47 @@ int overcommit_ratio_handler(struct ctl_table *table, int write, void *buffer, ...@@ -746,6 +746,47 @@ int overcommit_ratio_handler(struct ctl_table *table, int write, void *buffer,
return ret; return ret;
} }
static void sync_overcommit_as(struct work_struct *dummy)
{
percpu_counter_sync(&vm_committed_as);
}
int overcommit_policy_handler(struct ctl_table *table, int write, void *buffer,
size_t *lenp, loff_t *ppos)
{
struct ctl_table t;
int new_policy;
int ret;
/*
* The deviation of sync_overcommit_as could be big with loose policy
* like OVERCOMMIT_ALWAYS/OVERCOMMIT_GUESS. When changing policy to
* strict OVERCOMMIT_NEVER, we need to reduce the deviation to comply
* with the strict "NEVER", and to avoid possible race condtion (even
* though user usually won't too frequently do the switching to policy
* OVERCOMMIT_NEVER), the switch is done in the following order:
* 1. changing the batch
* 2. sync percpu count on each CPU
* 3. switch the policy
*/
if (write) {
t = *table;
t.data = &new_policy;
ret = proc_dointvec_minmax(&t, write, buffer, lenp, ppos);
if (ret)
return ret;
mm_compute_batch(new_policy);
if (new_policy == OVERCOMMIT_NEVER)
schedule_on_each_cpu(sync_overcommit_as);
sysctl_overcommit_memory = new_policy;
} else {
ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
}
return ret;
}
int overcommit_kbytes_handler(struct ctl_table *table, int write, void *buffer, int overcommit_kbytes_handler(struct ctl_table *table, int write, void *buffer,
size_t *lenp, loff_t *ppos) size_t *lenp, loff_t *ppos)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment