@@ -275,6 +275,10 @@ struct mem_cgroup {
/* memory.stat */
struct memcg_vmstats_percpu __rcu /* __percpu */ *vmstats_percpu;
+ struct memcg_vmstats_percpu __percpu *vmstats_percpu_offlined;
+
+ /* used to release non-used percpu memory */
+ struct rcu_head rcu;
MEMCG_PADDING(_pad2_);
@@ -4470,7 +4470,7 @@ static void __mem_cgroup_free(struct mem_cgroup *memcg)
for_each_node(node)
free_mem_cgroup_per_node_info(memcg, node);
- free_percpu(memcg->vmstats_percpu);
+ WARN_ON_ONCE(memcg->vmstats_percpu != NULL);
kfree(memcg);
}
@@ -4613,6 +4613,26 @@ static int mem_cgroup_css_online(struct cgroup_subsys_state *css)
return 0;
}
+static void percpu_rcu_free(struct rcu_head *rcu)
+{
+ struct mem_cgroup *memcg = container_of(rcu, struct mem_cgroup, rcu);
+
+ free_percpu(memcg->vmstats_percpu_offlined);
+ WARN_ON_ONCE(memcg->vmstats_percpu);
+
+ css_put(&memcg->css);
+}
+
+static void mem_cgroup_offline_percpu(struct mem_cgroup *memcg)
+{
+ memcg->vmstats_percpu_offlined = (struct memcg_vmstats_percpu __percpu*)
+ rcu_dereference(memcg->vmstats_percpu);
+ rcu_assign_pointer(memcg->vmstats_percpu, NULL);
+
+ css_get(&memcg->css);
+ call_rcu(&memcg->rcu, percpu_rcu_free);
+}
+
static void mem_cgroup_css_offline(struct cgroup_subsys_state *css)
{
struct mem_cgroup *memcg = mem_cgroup_from_css(css);
@@ -4639,6 +4659,8 @@ static void mem_cgroup_css_offline(struct cgroup_subsys_state *css)
drain_all_stock(memcg);
mem_cgroup_id_put(memcg);
+
+ mem_cgroup_offline_percpu(memcg);
}
static void mem_cgroup_css_released(struct cgroup_subsys_state *css)