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
215 NR_INACTIVE_FILE, /* " " " " " */215 NR_INACTIVE_FILE, /* " " " " " */
216 NR_ACTIVE_FILE, /* " " " " " */216 NR_ACTIVE_FILE, /* " " " " " */
217 NR_UNEVICTABLE, /* " " " " " */217 NR_UNEVICTABLE, /* " " " " " */
218 NR_SLAB_RECLAIMABLE,218 NR_SLAB_RECLAIMABLE, /* Please do not reorder this item */
219 NR_SLAB_UNRECLAIMABLE,219 NR_SLAB_UNRECLAIMABLE, /* and this one without looking at
220 * memcg_flush_percpu_vmstats() first. */
220 NR_ISOLATED_ANON, /* Temporary isolated pages from anon lru */221 NR_ISOLATED_ANON, /* Temporary isolated pages from anon lru */
221 NR_ISOLATED_FILE, /* Temporary isolated pages from file lru */222 NR_ISOLATED_FILE, /* Temporary isolated pages from file lru */
222 WORKINGSET_NODES,223 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
3260 }3260 }
3261}3261}
3262 3262
3263static void memcg_flush_percpu_vmstats(struct mem_cgroup *memcg)3263static void memcg_flush_percpu_vmstats(struct mem_cgroup *memcg, bool slab_only)
3264{3264{
3265 unsigned long stat[MEMCG_NR_STAT];3265 unsigned long stat[MEMCG_NR_STAT];
3266 struct mem_cgroup *mi;3266 struct mem_cgroup *mi;
3267 int node, cpu, i;3267 int node, cpu, i;
3268 int min_idx, max_idx;
3268 3269
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++)
3270 stat[i] = 0;3279 stat[i] = 0;
3271 3280
3272 for_each_online_cpu(cpu)3281 for_each_online_cpu(cpu)
3273 for (i = 0; i < MEMCG_NR_STAT; i++)3282 for (i = min_idx; i < max_idx; i++)
3274 stat[i] += raw_cpu_read(memcg->vmstats_percpu->stat[i]);3283 stat[i] += raw_cpu_read(memcg->vmstats_percpu->stat[i]);
3275 3284
3276 for (mi = memcg; mi; mi = parent_mem_cgroup(mi))3285 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++)
3278 atomic_long_add(stat[i], &mi->vmstats[i]);3287 atomic_long_add(stat[i], &mi->vmstats[i]);
3279 3288
3289 if (!slab_only)
3290 max_idx = NR_VM_NODE_STAT_ITEMS;
3291
3280 for_each_node(node) {3292 for_each_node(node) {
3281 struct mem_cgroup_per_node *pn = memcg->nodeinfo[node];3293 struct mem_cgroup_per_node *pn = memcg->nodeinfo[node];
3282 struct mem_cgroup_per_node *pi;3294 struct mem_cgroup_per_node *pi;
3283 3295
3284 for (i = 0; i < NR_VM_NODE_STAT_ITEMS; i++)3296 for (i = min_idx; i < max_idx; i++)
3285 stat[i] = 0;3297 stat[i] = 0;
3286 3298
3287 for_each_online_cpu(cpu)3299 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++)
3289 stat[i] += raw_cpu_read(3301 stat[i] += raw_cpu_read(
3290 pn->lruvec_stat_cpu->count[i]);3302 pn->lruvec_stat_cpu->count[i]);
3291 3303
3292 for (pi = pn; pi; pi = parent_nodeinfo(pi, node))3304 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++)
3294 atomic_long_add(stat[i], &pi->lruvec_stat[i]);3306 atomic_long_add(stat[i], &pi->lruvec_stat[i]);
3295 }3307 }
3296}3308}
@@ -3363,7 +3375,14 @@ static void memcg_offline_kmem(struct mem_cgroup *memcg) · 66 unmodified lines
3363 if (!parent)3375 if (!parent)
3364 parent = root_mem_cgroup;3376 parent = root_mem_cgroup;
3365 3377
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 */
3366 memcg_deactivate_kmem_caches(memcg, parent);3384 memcg_deactivate_kmem_caches(memcg, parent);
3385 memcg_flush_percpu_vmstats(memcg, true);
3367 3386
3368 kmemcg_id = memcg->kmemcg_id;3387 kmemcg_id = memcg->kmemcg_id;
3369 BUG_ON(kmemcg_id < 0);3388 BUG_ON(kmemcg_id < 0);
@@ -4740,7 +4759,7 @@ static void __mem_cgroup_free(struct mem_cgroup *memcg) · 1370 unmodified lines
4740 * Flush percpu vmstats and vmevents to guarantee the value correctness4759 * Flush percpu vmstats and vmevents to guarantee the value correctness
4741 * on parent's and all ancestor levels.4760 * on parent's and all ancestor levels.
4742 */4761 */
4743 memcg_flush_percpu_vmstats(memcg);4762 memcg_flush_percpu_vmstats(memcg, false);
4744 memcg_flush_percpu_vmevents(memcg);4763 memcg_flush_percpu_vmevents(memcg);
4745 for_each_node(node)4764 for_each_node(node)
4746 free_mem_cgroup_per_node_info(memcg, node);4765 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