For the root memcg, lruvec_page_state() returns the rstat value, which needs a flush to be up to date. Flushing the root memcg is expensive, as it flushes every memcg in the system. Most memcg node stats only track memory charged to a memcg, so the root memcg's hierarchical value equals the node counter, which is always up to date and doesn't need a flush. For those stats, have lruvec_page_state() and lruvec_page_state_monotonic() read the node counter for the root memcg, as they already do when memcg is disabled. Callers that only read the root memcg's own lruvec then no longer need to flush it, which the following patches take advantage of. It also makes the root memcg's stats more accurate, as callers that ratelimit the flush can otherwise read values that are seconds old. The exceptions are the kernel memory stats (slab, kernel stack, page tables, secondary page tables and vmalloc), hugetlb, and WORKINGSET_NODERECLAIM, which counts reclaimed xarray nodes (slab objects). Their node counters also count memory that isn't charged to a memcg, which falls into two categories: a) the allocation isn't accounted to any memcg (eg kernel page tables, or hugetlb when hugetlb accounting is off) b) the allocation is accounted, but it comes from a task running in the root cgroup, and the root memcg isn't charged for it As such, these stats still need to read the rstat value, since the node counter value can be higher than the rstat value. One user-visible change is that the root memcg's memory.numa_stat (and the hierarchical_* entries of the cgroup v1 memory.numa_stat) now reports the node counters for the non-excluded stats, as it reads lruvec_page_state(). This is more accurate, but as a result, it may not exactly match the root memcg's memory.stat, which still reads the rstat values. Signed-off-by: Joanne Koong --- mm/memcontrol.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 091401cecf39..196c0382181b 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -504,13 +504,67 @@ struct lruvec_stats { long state_pending[NR_MEMCG_NODE_STAT_ITEMS]; }; +/* + * Whether to read the node counter for @idx instead of @lruvec's rstat value. + * This is only done for the root memcg for stats where every update also + * updates a memcg's stats, so the root memcg's hierarchical value equals the + * node counter. The node counter is preferred since it is always up to date, + * and avoids an rstat flush which for the root memcg flushes every cgroup in + * the system. + */ +static bool lruvec_use_node_stat(const struct lruvec *lruvec, + enum node_stat_item idx) +{ + if (!mem_cgroup_is_root(lruvec_memcg(lruvec))) + return false; + + switch (idx) { + case NR_INACTIVE_ANON: + case NR_ACTIVE_ANON: + case NR_INACTIVE_FILE: + case NR_ACTIVE_FILE: + case NR_UNEVICTABLE: + case WORKINGSET_REFAULT_ANON: + case WORKINGSET_REFAULT_FILE: + case WORKINGSET_ACTIVATE_ANON: + case WORKINGSET_ACTIVATE_FILE: + case WORKINGSET_RESTORE_ANON: + case WORKINGSET_RESTORE_FILE: +#ifdef CONFIG_NUMA_BALANCING + case PGPROMOTE_SUCCESS: +#endif + case PGDEMOTE_KSWAPD: + case PGDEMOTE_DIRECT: + case PGDEMOTE_KHUGEPAGED: + case PGDEMOTE_PROACTIVE: + case PGSTEAL_KSWAPD: + case PGSTEAL_DIRECT: + case PGSTEAL_KHUGEPAGED: + case PGSTEAL_PROACTIVE: + case PGSTEAL_ANON: + case PGSTEAL_FILE: + case PGSCAN_KSWAPD: + case PGSCAN_DIRECT: + case PGSCAN_KHUGEPAGED: + case PGSCAN_PROACTIVE: + case PGSCAN_ANON: + case PGSCAN_FILE: + case PGROTATE_ANON: + case PGROTATE_FILE: + case PGREFILL: + return true; + default: + return false; + } +} + unsigned long lruvec_page_state(const struct lruvec *lruvec, enum node_stat_item idx) { const struct mem_cgroup_per_node *pn; long x; int i; - if (mem_cgroup_disabled()) + if (mem_cgroup_disabled() || lruvec_use_node_stat(lruvec, idx)) return node_page_state(lruvec_pgdat(lruvec), idx); i = memcg_stats_index(idx); @@ -551,7 +605,7 @@ unsigned long lruvec_page_state_monotonic(const struct lruvec *lruvec, const struct mem_cgroup_per_node *pn; int i; - if (mem_cgroup_disabled()) + if (mem_cgroup_disabled() || lruvec_use_node_stat(lruvec, idx)) return node_page_state_monotonic(lruvec_pgdat(lruvec), idx); i = memcg_stats_index(idx); -- 2.52.1