mm/vmalloc.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 2faaa2976447..0d0b96ed8948 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -2688,7 +2688,7 @@ static int __init proc_vmalloc_init(void) } module_init(proc_vmalloc_init); -void get_vmalloc_info(struct vmalloc_info *vmi) +static void calc_vmalloc_info(struct vmalloc_info *vmi) { struct vmap_area *va; unsigned long free_area_size; @@ -2735,5 +2735,51 @@ void get_vmalloc_info(struct vmalloc_info *vmi) out: rcu_read_unlock(); } -#endif +/* + * Calculating the vmalloc information is expensive, and nobody really + * deeply cares about it anyway. Yet, some versions of glibc end up + * reading /proc/meminfo a lot, not because they care about the vmalloc + * fields, but because they care about the total memory info. + * + * So to alleviate that expense, we cache the vmalloc information for + * a second. NOTE! This is fundamentally racy, since the accesses to + * the two fields in "struct vmalloc_info" and the cache timeout are + * all entirely unsynchronized. We just don't care. + */ +void get_vmalloc_info(struct vmalloc_info *vmi) +{ + static unsigned long cache_timeout = INITIAL_JIFFIES; + static struct vmalloc_info cached_info; + unsigned long now = jiffies, last = READ_ONCE(cache_timeout); + + if (now - last < HZ) { + *vmi = cached_info; + return; + } + + /* + * We update the cache timeout early, because we (again) do + * not care if somebody else comes in and sees slightly stale + * information. We'd rather return more stale information + * than waste time with multiple CPU's all calculating the + * new state. + * + * Note: the barriers are here not to fix any races, but to + * avoid the compiling spreading out the updates to these + * variables any more than necessary. + * + * Also note that we calculate the new state into the 'vmi' + * buffer that is passed in, and private to the caller. That + * is intentional: we do not want to update the cached info + * incrementally during the calculations. + */ + WRITE_ONCE(cache_timeout, now); + barrier(); + + calc_vmalloc_info(vmi); + + barrier(); + cached_info = *vmi; +} +#endif