@@ -2119,6 +2119,56 @@ static void drain_all_stock(struct mem_cgroup *root_memcg)
mutex_unlock(&percpu_charge_mutex);
}
+/*
+ * Flush all per-cpu stats and events into atomics.
+ * Try to minimize the number of atomic writes by gathering data from
+ * all cpus locally, and then make one atomic update.
+ * No locking is required, because no one has an access to
+ * the offlined percpu data.
+ */
+static void memcg_flush_offline_percpu(struct mem_cgroup *memcg)
+{
+ struct memcg_vmstats_percpu __percpu *vmstats_percpu;
+ struct lruvec_stat __percpu *lruvec_stat_cpu;
+ struct mem_cgroup_per_node *pn;
+ int cpu, i;
+ long x;
+
+ vmstats_percpu = memcg->vmstats_percpu_offlined;
+
+ for (i = 0; i < MEMCG_NR_STAT; i++) {
+ int nid;
+
+ x = 0;
+ for_each_possible_cpu(cpu)
+ x += per_cpu(vmstats_percpu->stat[i], cpu);
+ if (x)
+ atomic_long_add(x, &memcg->vmstats[i]);
+
+ if (i >= NR_VM_NODE_STAT_ITEMS)
+ continue;
+
+ for_each_node(nid) {
+ pn = mem_cgroup_nodeinfo(memcg, nid);
+ lruvec_stat_cpu = pn->lruvec_stat_cpu_offlined;
+
+ x = 0;
+ for_each_possible_cpu(cpu)
+ x += per_cpu(lruvec_stat_cpu->count[i], cpu);
+ if (x)
+ atomic_long_add(x, &pn->lruvec_stat[i]);
+ }
+ }
+
+ for (i = 0; i < NR_VM_EVENT_ITEMS; i++) {
+ x = 0;
+ for_each_possible_cpu(cpu)
+ x += per_cpu(vmstats_percpu->events[i], cpu);
+ if (x)
+ atomic_long_add(x, &memcg->vmevents[i]);
+ }
+}
+
static int memcg_hotplug_cpu_dead(unsigned int cpu)
{
struct memcg_vmstats_percpu __percpu *vmstats_percpu;
@@ -4618,6 +4668,8 @@ static void percpu_rcu_free(struct rcu_head *rcu)
struct mem_cgroup *memcg = container_of(rcu, struct mem_cgroup, rcu);
int node;
+ memcg_flush_offline_percpu(memcg);
+
for_each_node(node) {
struct mem_cgroup_per_node *pn = memcg->nodeinfo[node];