Skip to content
Snippets Groups Projects
memcontrol.c 79.4 KiB
Newer Older
  • Learn to ignore specific revisions
  •  * the number of reference from swap_cgroup and free mem_cgroup when
     * it goes down to 0.
     *
     * Removal of cgroup itself succeeds regardless of refs from swap.
     */
    
    
    static void __mem_cgroup_free(struct mem_cgroup *mem)
    
    	mem_cgroup_remove_from_trees(mem);
    
    KAMEZAWA Hiroyuki's avatar
    KAMEZAWA Hiroyuki committed
    	free_css_id(&mem_cgroup_subsys, &mem->css);
    
    
    	for_each_node_state(node, N_POSSIBLE)
    		free_mem_cgroup_per_zone_info(mem, node);
    
    
    	if (mem_cgroup_size() < PAGE_SIZE)
    
    static void mem_cgroup_get(struct mem_cgroup *mem)
    {
    	atomic_inc(&mem->refcnt);
    }
    
    static void mem_cgroup_put(struct mem_cgroup *mem)
    {
    
    	if (atomic_dec_and_test(&mem->refcnt)) {
    		struct mem_cgroup *parent = parent_mem_cgroup(mem);
    
    		__mem_cgroup_free(mem);
    
    		if (parent)
    			mem_cgroup_put(parent);
    	}
    
    /*
     * Returns the parent mem_cgroup in memcgroup hierarchy with hierarchy enabled.
     */
    static struct mem_cgroup *parent_mem_cgroup(struct mem_cgroup *mem)
    {
    	if (!mem->res.parent)
    		return NULL;
    	return mem_cgroup_from_res_counter(mem->res.parent, res);
    }
    
    #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
    static void __init enable_swap_cgroup(void)
    {
    
    	if (!mem_cgroup_disabled() && really_do_swap_account)
    
    		do_swap_account = 1;
    }
    #else
    static void __init enable_swap_cgroup(void)
    {
    }
    #endif
    
    
    static int mem_cgroup_soft_limit_tree_init(void)
    {
    	struct mem_cgroup_tree_per_node *rtpn;
    	struct mem_cgroup_tree_per_zone *rtpz;
    	int tmp, node, zone;
    
    	for_each_node_state(node, N_POSSIBLE) {
    		tmp = node;
    		if (!node_state(node, N_NORMAL_MEMORY))
    			tmp = -1;
    		rtpn = kzalloc_node(sizeof(*rtpn), GFP_KERNEL, tmp);
    		if (!rtpn)
    			return 1;
    
    		soft_limit_tree.rb_tree_per_node[node] = rtpn;
    
    		for (zone = 0; zone < MAX_NR_ZONES; zone++) {
    			rtpz = &rtpn->rb_tree_per_zone[zone];
    			rtpz->rb_root = RB_ROOT;
    			spin_lock_init(&rtpz->lock);
    		}
    	}
    	return 0;
    }
    
    
    Li Zefan's avatar
    Li Zefan committed
    static struct cgroup_subsys_state * __ref
    
    mem_cgroup_create(struct cgroup_subsys *ss, struct cgroup *cont)
    {
    
    	struct mem_cgroup *mem, *parent;
    
    KAMEZAWA Hiroyuki's avatar
    KAMEZAWA Hiroyuki committed
    	long error = -ENOMEM;
    
    	mem = mem_cgroup_alloc();
    	if (!mem)
    
    KAMEZAWA Hiroyuki's avatar
    KAMEZAWA Hiroyuki committed
    		return ERR_PTR(error);
    
    	for_each_node_state(node, N_POSSIBLE)
    		if (alloc_mem_cgroup_per_zone_info(mem, node))
    			goto free_out;
    
    	if (cont->parent == NULL) {
    
    		enable_swap_cgroup();
    
    		if (mem_cgroup_soft_limit_tree_init())
    			goto free_out;
    
    
    		parent = mem_cgroup_from_cont(cont->parent);
    
    		mem->use_hierarchy = parent->use_hierarchy;
    	}
    
    	if (parent && parent->use_hierarchy) {
    		res_counter_init(&mem->res, &parent->res);
    		res_counter_init(&mem->memsw, &parent->memsw);
    
    		/*
    		 * We increment refcnt of the parent to ensure that we can
    		 * safely access it on res_counter_charge/uncharge.
    		 * This refcnt will be decremented when freeing this
    		 * mem_cgroup(see mem_cgroup_put).
    		 */
    		mem_cgroup_get(parent);
    
    	} else {
    		res_counter_init(&mem->res, NULL);
    		res_counter_init(&mem->memsw, NULL);
    	}
    
    KAMEZAWA Hiroyuki's avatar
    KAMEZAWA Hiroyuki committed
    	mem->last_scanned_child = 0;
    
    	spin_lock_init(&mem->reclaim_param_lock);
    
    KOSAKI Motohiro's avatar
    KOSAKI Motohiro committed
    	if (parent)
    		mem->swappiness = get_swappiness(parent);
    
    	atomic_set(&mem->refcnt, 1);
    
    	return &mem->css;
    
    	__mem_cgroup_free(mem);
    
    KAMEZAWA Hiroyuki's avatar
    KAMEZAWA Hiroyuki committed
    	return ERR_PTR(error);
    
    static int mem_cgroup_pre_destroy(struct cgroup_subsys *ss,
    
    					struct cgroup *cont)
    {
    	struct mem_cgroup *mem = mem_cgroup_from_cont(cont);
    
    
    	return mem_cgroup_force_empty(mem, false);
    
    static void mem_cgroup_destroy(struct cgroup_subsys *ss,
    				struct cgroup *cont)
    {
    
    	struct mem_cgroup *mem = mem_cgroup_from_cont(cont);
    
    	mem_cgroup_put(mem);
    
    }
    
    static int mem_cgroup_populate(struct cgroup_subsys *ss,
    				struct cgroup *cont)
    {
    
    	int ret;
    
    	ret = cgroup_add_files(cont, ss, mem_cgroup_files,
    				ARRAY_SIZE(mem_cgroup_files));
    
    	if (!ret)
    		ret = register_memsw_files(cont, ss);
    	return ret;
    
    static void mem_cgroup_move_task(struct cgroup_subsys *ss,
    				struct cgroup *cont,
    				struct cgroup *old_cont,
    
    				struct task_struct *p,
    				bool threadgroup)
    
    	 * FIXME: It's better to move charges of this process from old
    	 * memcg to new memcg. But it's just on TODO-List now.
    
    struct cgroup_subsys mem_cgroup_subsys = {
    	.name = "memory",
    	.subsys_id = mem_cgroup_subsys_id,
    	.create = mem_cgroup_create,
    
    	.pre_destroy = mem_cgroup_pre_destroy,
    
    	.destroy = mem_cgroup_destroy,
    	.populate = mem_cgroup_populate,
    
    	.attach = mem_cgroup_move_task,
    
    KAMEZAWA Hiroyuki's avatar
    KAMEZAWA Hiroyuki committed
    	.use_id = 1,
    
    
    #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
    
    static int __init disable_swap_account(char *s)
    {
    	really_do_swap_account = 0;
    	return 1;
    }
    __setup("noswapaccount", disable_swap_account);
    #endif