Private nodes folios are charged like any other - the node's per-cpu
lruvec counters are increment even for N_MEMORY_PRIVATE.
The memcg-level totals in memory.stat therefore include this memory,
but the per-node views do not:
- mem_cgroup_css_rstat_flush() folds the per-node percpu deltas into the
aggregate only for N_MEMORY nodes, so a private node's lruvec aggregate
is never refreshed.
- memory.numa_stat (v2) and the v1 numa_stat skip private nodes, so they
never emit an Nx= field for one.
So memory.numa_stat does not sum to memory.stat once a cgroup has memory
on a private node.
Include N_MEMORY_PRIVATE in the flush/display process so private-node
memory is folded in and reported per node.
This is pure accounting of memory that already exists and is already
charged, so it is unconditional - regardless of future capabilities.
Signed-off-by: Gregory Price <[email protected]>
---
mm/memcontrol-v1.c | 8 ++++++--
mm/memcontrol.c | 11 +++++++++--
2 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/mm/memcontrol-v1.c b/mm/memcontrol-v1.c
index 2dc599484d006..5e464e52dfb9a 100644
--- a/mm/memcontrol-v1.c
+++ b/mm/memcontrol-v1.c
@@ -2132,14 +2132,18 @@ static int memcg_numa_stat_show(struct seq_file *m,
void *v)
const struct numa_stat *stat;
int nid;
struct mem_cgroup *memcg = mem_cgroup_from_seq(m);
+ nodemask_t reportable;
mem_cgroup_flush_stats(memcg);
+ /* Private nodes hold cgroup memory too, report them. */
+ nodes_or(reportable, node_states[N_MEMORY],
node_states[N_MEMORY_PRIVATE]);
+
for (stat = stats; stat < ARRAY_END(stats); stat++) {
seq_printf(m, "%s=%lu", stat->name,
mem_cgroup_nr_lru_pages(memcg, stat->lru_mask,
false));
- for_each_node_state(nid, N_MEMORY)
+ for_each_node_mask(nid, reportable)
seq_printf(m, " N%d=%lu", nid,
mem_cgroup_node_nr_lru_pages(memcg, nid,
stat->lru_mask, false));
@@ -2151,7 +2155,7 @@ static int memcg_numa_stat_show(struct seq_file *m, void
*v)
seq_printf(m, "hierarchical_%s=%lu", stat->name,
mem_cgroup_nr_lru_pages(memcg, stat->lru_mask,
true));
- for_each_node_state(nid, N_MEMORY)
+ for_each_node_mask(nid, reportable)
seq_printf(m, " N%d=%lu", nid,
mem_cgroup_node_nr_lru_pages(memcg, nid,
stat->lru_mask, true));
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index f0dde52dc9e0e..4d79e238bc57d 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -4496,6 +4496,7 @@ static void mem_cgroup_css_rstat_flush(struct
cgroup_subsys_state *css, int cpu)
struct mem_cgroup *parent = parent_mem_cgroup(memcg);
struct memcg_vmstats_percpu *statc;
struct aggregate_control ac;
+ nodemask_t reportable;
int nid;
flush_nmi_stats(memcg, parent);
@@ -4524,7 +4525,9 @@ static void mem_cgroup_css_rstat_flush(struct
cgroup_subsys_state *css, int cpu)
};
mem_cgroup_stat_aggregate(&ac);
- for_each_node_state(nid, N_MEMORY) {
+ /* Private nodes must also be accounted or numa_stat is misleading */
+ nodes_or(reportable, node_states[N_MEMORY],
node_states[N_MEMORY_PRIVATE]);
+ for_each_node_mask(nid, reportable) {
struct mem_cgroup_per_node *pn = memcg->nodeinfo[nid];
struct lruvec_stats *lstats = pn->lruvec_stats;
struct lruvec_stats *plstats = NULL;
@@ -4947,9 +4950,13 @@ static int memory_numa_stat_show(struct seq_file *m,
void *v)
{
int i;
struct mem_cgroup *memcg = mem_cgroup_from_seq(m);
+ nodemask_t reportable;
mem_cgroup_flush_stats(memcg);
+ /* Private nodes hold cgroup memory too, report them. */
+ nodes_or(reportable, node_states[N_MEMORY],
node_states[N_MEMORY_PRIVATE]);
+
for (i = 0; i < ARRAY_SIZE(memory_stats); i++) {
int nid;
@@ -4957,7 +4964,7 @@ static int memory_numa_stat_show(struct seq_file *m, void
*v)
continue;
seq_printf(m, "%s", memory_stats[i].name);
- for_each_node_state(nid, N_MEMORY) {
+ for_each_node_mask(nid, reportable) {
u64 size;
struct lruvec *lruvec;
--
2.53.0-Meta