Skip to content

Commit

Permalink
memcg: always free struct memcg through schedule_work()
Browse files Browse the repository at this point in the history
Right now we free struct memcg with kfree right after a rcu grace period,
but defer it if we need to use vfree() to get rid of that memory area.  We
do that by need, because we need vfree to be called in a process context.

This patch unifies this behavior, by ensuring that even kfree will happen
in a separate thread.  The goal is to have a stable place to call the
upcoming jump label destruction function outside the realm of the
complicated and quite far-reaching cgroup lock (that can't be held when
holding either the cpu_hotplug.lock or jump_label_mutex)

[akpm@linux-foundation.org: tweak comment]
Signed-off-by: Glauber Costa <glommer@parallels.com>
Cc: Tejun Heo <tj@kernel.org>
Cc: Li Zefan <lizefan@huawei.com>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Acked-by: Michal Hocko <mhocko@suse.cz>
Cc: David Miller <davem@davemloft.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Glauber Costa authored and Linus Torvalds committed May 29, 2012
1 parent fa9add6 commit 3afe36b
Showing 1 changed file with 13 additions and 11 deletions.
24 changes: 13 additions & 11 deletions mm/memcontrol.c
Original file line number Diff line number Diff line change
Expand Up @@ -258,8 +258,8 @@ struct mem_cgroup {
*/
struct rcu_head rcu_freeing;
/*
* But when using vfree(), that cannot be done at
* interrupt time, so we must then queue the work.
* We also need some space for a worker in deferred freeing.
* By the time we call it, rcu_freeing is no longer in use.
*/
struct work_struct work_freeing;
};
Expand Down Expand Up @@ -4702,23 +4702,28 @@ static struct mem_cgroup *mem_cgroup_alloc(void)
}

/*
* Helpers for freeing a vzalloc()ed mem_cgroup by RCU,
* Helpers for freeing a kmalloc()ed/vzalloc()ed mem_cgroup by RCU,
* but in process context. The work_freeing structure is overlaid
* on the rcu_freeing structure, which itself is overlaid on memsw.
*/
static void vfree_work(struct work_struct *work)
static void free_work(struct work_struct *work)
{
struct mem_cgroup *memcg;
int size = sizeof(struct mem_cgroup);

memcg = container_of(work, struct mem_cgroup, work_freeing);
vfree(memcg);
if (size < PAGE_SIZE)
kfree(memcg);
else
vfree(memcg);
}
static void vfree_rcu(struct rcu_head *rcu_head)

static void free_rcu(struct rcu_head *rcu_head)
{
struct mem_cgroup *memcg;

memcg = container_of(rcu_head, struct mem_cgroup, rcu_freeing);
INIT_WORK(&memcg->work_freeing, vfree_work);
INIT_WORK(&memcg->work_freeing, free_work);
schedule_work(&memcg->work_freeing);
}

Expand All @@ -4744,10 +4749,7 @@ static void __mem_cgroup_free(struct mem_cgroup *memcg)
free_mem_cgroup_per_zone_info(memcg, node);

free_percpu(memcg->stat);
if (sizeof(struct mem_cgroup) < PAGE_SIZE)
kfree_rcu(memcg, rcu_freeing);
else
call_rcu(&memcg->rcu_freeing, vfree_rcu);
call_rcu(&memcg->rcu_freeing, free_rcu);
}

static void mem_cgroup_get(struct mem_cgroup *memcg)
Expand Down

0 comments on commit 3afe36b

Please sign in to comment.