History

mm: memcontrol: flush percpu slab vmstats on kmem offlining

I've noticed that the "slab" value in memory.stat is sometimes 0, even if some children memory cgroups have a non-zero "slab" value. The following investigation showed that this is the result of the kmem_cache reparenting in combination with the per-cpu batching of slab vmstats. At the offlining some vmstat value may leave in the percpu cache, not being propagated upwards by the cgroup hierarchy. It means that stats on ancestor levels are lower than actual. Later when slab pages are released, the precise number of pages is substracted on the parent level, making the value negative. We don't show negative values, 0 is printed instead. To fix this issue, let's flush percpu slab memcg and lruvec stats on memcg offlining. This guarantees that numbers on all ancestor levels are accurate and match the actual number of outstanding slab pages.

Roman Gushchin committed 7 years ago

Commit bee07b31 parent 846d2db2 files · +30 −10Browse files

Unified · Split

2 files
  1. include/linux/mmzone.h +3 −2
  2. mm/memcontrol.c +27 −8
include/linux/mmzone.h+3−2
@@ -215,8 +215,9 @@ enum node_stat_item { · 214 unmodified lines
215215 NR_INACTIVE_FILE, /* " " " " " */
216216 NR_ACTIVE_FILE, /* " " " " " */
217217 NR_UNEVICTABLE, /* " " " " " */
218− NR_SLAB_RECLAIMABLE,
219− NR_SLAB_UNRECLAIMABLE,
218+ NR_SLAB_RECLAIMABLE, /* Please do not reorder this item */
219+ NR_SLAB_UNRECLAIMABLE, /* and this one without looking at
220+ * memcg_flush_percpu_vmstats() first. */
220221 NR_ISOLATED_ANON, /* Temporary isolated pages from anon lru */
221222 NR_ISOLATED_FILE, /* Temporary isolated pages from file lru */
222223 WORKINGSET_NODES,
mm/memcontrol.c+27−8
@@ -3260,37 +3260,49 @@ static u64 mem_cgroup_read_u64(struct cgroup_subsys_state *css, · 3259 unmodified lines
32603260 }
32613261 }
32623262
3263−static void memcg_flush_percpu_vmstats(struct mem_cgroup *memcg)
3263+static void memcg_flush_percpu_vmstats(struct mem_cgroup *memcg, bool slab_only)
32643264 {
32653265 unsigned long stat[MEMCG_NR_STAT];
32663266 struct mem_cgroup *mi;
32673267 int node, cpu, i;
3268+ int min_idx, max_idx;
32683269
3269− for (i = 0; i < MEMCG_NR_STAT; i++)
3270+ if (slab_only) {
3271+ min_idx = NR_SLAB_RECLAIMABLE;
3272+ max_idx = NR_SLAB_UNRECLAIMABLE;
3273+ } else {
3274+ min_idx = 0;
3275+ max_idx = MEMCG_NR_STAT;
3276+ }
3277+
3278+ for (i = min_idx; i < max_idx; i++)
32703279 stat[i] = 0;
32713280
32723281 for_each_online_cpu(cpu)
3273− for (i = 0; i < MEMCG_NR_STAT; i++)
3282+ for (i = min_idx; i < max_idx; i++)
32743283 stat[i] += raw_cpu_read(memcg->vmstats_percpu->stat[i]);
32753284
32763285 for (mi = memcg; mi; mi = parent_mem_cgroup(mi))
3277− for (i = 0; i < MEMCG_NR_STAT; i++)
3286+ for (i = min_idx; i < max_idx; i++)
32783287 atomic_long_add(stat[i], &mi->vmstats[i]);
32793288
3289+ if (!slab_only)
3290+ max_idx = NR_VM_NODE_STAT_ITEMS;
3291+
32803292 for_each_node(node) {
32813293 struct mem_cgroup_per_node *pn = memcg->nodeinfo[node];
32823294 struct mem_cgroup_per_node *pi;
32833295
3284− for (i = 0; i < NR_VM_NODE_STAT_ITEMS; i++)
3296+ for (i = min_idx; i < max_idx; i++)
32853297 stat[i] = 0;
32863298
32873299 for_each_online_cpu(cpu)
3288− for (i = 0; i < NR_VM_NODE_STAT_ITEMS; i++)
3300+ for (i = min_idx; i < max_idx; i++)
32893301 stat[i] += raw_cpu_read(
32903302 pn->lruvec_stat_cpu->count[i]);
32913303
32923304 for (pi = pn; pi; pi = parent_nodeinfo(pi, node))
3293− for (i = 0; i < NR_VM_NODE_STAT_ITEMS; i++)
3305+ for (i = min_idx; i < max_idx; i++)
32943306 atomic_long_add(stat[i], &pi->lruvec_stat[i]);
32953307 }
32963308 }
@@ -3363,7 +3375,14 @@ static void memcg_offline_kmem(struct mem_cgroup *memcg) · 66 unmodified lines
33633375 if (!parent)
33643376 parent = root_mem_cgroup;
33653377
3378+ /*
3379+ * Deactivate and reparent kmem_caches. Then flush percpu
3380+ * slab statistics to have precise values at the parent and
3381+ * all ancestor levels. It's required to keep slab stats
3382+ * accurate after the reparenting of kmem_caches.
3383+ */
33663384 memcg_deactivate_kmem_caches(memcg, parent);
3385+ memcg_flush_percpu_vmstats(memcg, true);
33673386
33683387 kmemcg_id = memcg->kmemcg_id;
33693388 BUG_ON(kmemcg_id < 0);
@@ -4740,7 +4759,7 @@ static void __mem_cgroup_free(struct mem_cgroup *memcg) · 1370 unmodified lines
47404759 * Flush percpu vmstats and vmevents to guarantee the value correctness
47414760 * on parent's and all ancestor levels.
47424761 */
4743− memcg_flush_percpu_vmstats(memcg);
4762+ memcg_flush_percpu_vmstats(memcg, false);
47444763 memcg_flush_percpu_vmevents(memcg);
47454764 for_each_node(node)
47464765 free_mem_cgroup_per_node_info(memcg, node);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t