Commit 733a572e authored by Tejun Heo's avatar Tejun Heo Committed by Jens Axboe

memcg: make mem_cgroup_read_{stat|event}() iterate possible cpus instead of online

cpu_possible_mask represents the CPUs which are actually possible
during that boot instance.  For systems which don't support CPU
hotplug, this will match cpu_online_mask exactly in most cases.  Even
for systems which support CPU hotplug, the number of possible CPU
slots is highly unlikely to diverge greatly from the number of online
CPUs.  The only cases where the difference between possible and online
caused problems were when the boot code failed to initialize the
possible mask and left it fully set at NR_CPUS - 1.

As such, most per-cpu constructs allocate for all possible CPUs and
often iterate over the possibles, which also has the benefit of
avoiding the blocking CPU hotplug synchronization.

memcg open codes per-cpu stat counting for mem_cgroup_read_stat() and
mem_cgroup_read_events(), which iterates over online CPUs and handles
CPU hotplug operations explicitly.  This complexity doesn't actually
buy anything.  Switch to iterating over the possibles and drop the
explicit CPU hotplug handling.

Eventually, we want to convert memcg to use percpu_counter instead of
its own custom implementation which also benefits from quick access
w/o summing for cases where larger error margin is acceptable.

This will allow mem_cgroup_read_stat() to be called from non-sleepable
contexts which will be used by cgroup writeback.
Signed-off-by: default avatarTejun Heo <tj@kernel.org>
Cc: Michal Hocko <mhocko@suse.cz>
Acked-by: default avatarJohannes Weiner <hannes@cmpxchg.org>
Signed-off-by: default avatarJens Axboe <axboe@fb.com>
parent 108dad65
...@@ -324,11 +324,6 @@ struct mem_cgroup { ...@@ -324,11 +324,6 @@ struct mem_cgroup {
* percpu counter. * percpu counter.
*/ */
struct mem_cgroup_stat_cpu __percpu *stat; struct mem_cgroup_stat_cpu __percpu *stat;
/*
* used when a cpu is offlined or other synchronizations
* See mem_cgroup_read_stat().
*/
struct mem_cgroup_stat_cpu nocpu_base;
spinlock_t pcp_counter_lock; spinlock_t pcp_counter_lock;
#if defined(CONFIG_MEMCG_KMEM) && defined(CONFIG_INET) #if defined(CONFIG_MEMCG_KMEM) && defined(CONFIG_INET)
...@@ -834,15 +829,8 @@ static long mem_cgroup_read_stat(struct mem_cgroup *memcg, ...@@ -834,15 +829,8 @@ static long mem_cgroup_read_stat(struct mem_cgroup *memcg,
long val = 0; long val = 0;
int cpu; int cpu;
get_online_cpus(); for_each_possible_cpu(cpu)
for_each_online_cpu(cpu)
val += per_cpu(memcg->stat->count[idx], cpu); val += per_cpu(memcg->stat->count[idx], cpu);
#ifdef CONFIG_HOTPLUG_CPU
spin_lock(&memcg->pcp_counter_lock);
val += memcg->nocpu_base.count[idx];
spin_unlock(&memcg->pcp_counter_lock);
#endif
put_online_cpus();
return val; return val;
} }
...@@ -852,15 +840,8 @@ static unsigned long mem_cgroup_read_events(struct mem_cgroup *memcg, ...@@ -852,15 +840,8 @@ static unsigned long mem_cgroup_read_events(struct mem_cgroup *memcg,
unsigned long val = 0; unsigned long val = 0;
int cpu; int cpu;
get_online_cpus(); for_each_possible_cpu(cpu)
for_each_online_cpu(cpu)
val += per_cpu(memcg->stat->events[idx], cpu); val += per_cpu(memcg->stat->events[idx], cpu);
#ifdef CONFIG_HOTPLUG_CPU
spin_lock(&memcg->pcp_counter_lock);
val += memcg->nocpu_base.events[idx];
spin_unlock(&memcg->pcp_counter_lock);
#endif
put_online_cpus();
return val; return val;
} }
...@@ -2210,37 +2191,12 @@ static void drain_all_stock(struct mem_cgroup *root_memcg) ...@@ -2210,37 +2191,12 @@ static void drain_all_stock(struct mem_cgroup *root_memcg)
mutex_unlock(&percpu_charge_mutex); mutex_unlock(&percpu_charge_mutex);
} }
/*
* This function drains percpu counter value from DEAD cpu and
* move it to local cpu. Note that this function can be preempted.
*/
static void mem_cgroup_drain_pcp_counter(struct mem_cgroup *memcg, int cpu)
{
int i;
spin_lock(&memcg->pcp_counter_lock);
for (i = 0; i < MEM_CGROUP_STAT_NSTATS; i++) {
long x = per_cpu(memcg->stat->count[i], cpu);
per_cpu(memcg->stat->count[i], cpu) = 0;
memcg->nocpu_base.count[i] += x;
}
for (i = 0; i < MEM_CGROUP_EVENTS_NSTATS; i++) {
unsigned long x = per_cpu(memcg->stat->events[i], cpu);
per_cpu(memcg->stat->events[i], cpu) = 0;
memcg->nocpu_base.events[i] += x;
}
spin_unlock(&memcg->pcp_counter_lock);
}
static int memcg_cpu_hotplug_callback(struct notifier_block *nb, static int memcg_cpu_hotplug_callback(struct notifier_block *nb,
unsigned long action, unsigned long action,
void *hcpu) void *hcpu)
{ {
int cpu = (unsigned long)hcpu; int cpu = (unsigned long)hcpu;
struct memcg_stock_pcp *stock; struct memcg_stock_pcp *stock;
struct mem_cgroup *iter;
if (action == CPU_ONLINE) if (action == CPU_ONLINE)
return NOTIFY_OK; return NOTIFY_OK;
...@@ -2248,9 +2204,6 @@ static int memcg_cpu_hotplug_callback(struct notifier_block *nb, ...@@ -2248,9 +2204,6 @@ static int memcg_cpu_hotplug_callback(struct notifier_block *nb,
if (action != CPU_DEAD && action != CPU_DEAD_FROZEN) if (action != CPU_DEAD && action != CPU_DEAD_FROZEN)
return NOTIFY_OK; return NOTIFY_OK;
for_each_mem_cgroup(iter)
mem_cgroup_drain_pcp_counter(iter, cpu);
stock = &per_cpu(memcg_stock, cpu); stock = &per_cpu(memcg_stock, cpu);
drain_stock(stock); drain_stock(stock);
return NOTIFY_OK; return NOTIFY_OK;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment