Skip to content
Snippets Groups Projects
memcontrol.c 87.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		cb->fill(cb, memcg_stat_strings[i].local_name, mystat.stat[i]);
    
    	/* Hierarchical information */
    
    	{
    		unsigned long long limit, memsw_limit;
    		memcg_get_hierarchical_limit(mem_cont, &limit, &memsw_limit);
    		cb->fill(cb, "hierarchical_memory_limit", limit);
    		if (do_swap_account)
    			cb->fill(cb, "hierarchical_memsw_limit", memsw_limit);
    	}
    
    	memset(&mystat, 0, sizeof(mystat));
    	mem_cgroup_get_total_stat(mem_cont, &mystat);
    
    	for (i = 0; i < NR_MCS_STAT; i++) {
    		if (i == MCS_SWAP && !do_swap_account)
    			continue;
    
    		cb->fill(cb, memcg_stat_strings[i].total_name, mystat.stat[i]);
    
    #ifdef CONFIG_DEBUG_VM
    
    	cb->fill(cb, "inactive_ratio", calc_inactive_ratio(mem_cont, NULL));
    
    
    	{
    		int nid, zid;
    		struct mem_cgroup_per_zone *mz;
    		unsigned long recent_rotated[2] = {0, 0};
    		unsigned long recent_scanned[2] = {0, 0};
    
    		for_each_online_node(nid)
    			for (zid = 0; zid < MAX_NR_ZONES; zid++) {
    				mz = mem_cgroup_zoneinfo(mem_cont, nid, zid);
    
    				recent_rotated[0] +=
    					mz->reclaim_stat.recent_rotated[0];
    				recent_rotated[1] +=
    					mz->reclaim_stat.recent_rotated[1];
    				recent_scanned[0] +=
    					mz->reclaim_stat.recent_scanned[0];
    				recent_scanned[1] +=
    					mz->reclaim_stat.recent_scanned[1];
    			}
    		cb->fill(cb, "recent_rotated_anon", recent_rotated[0]);
    		cb->fill(cb, "recent_rotated_file", recent_rotated[1]);
    		cb->fill(cb, "recent_scanned_anon", recent_scanned[0]);
    		cb->fill(cb, "recent_scanned_file", recent_scanned[1]);
    	}
    #endif
    
    
    KOSAKI Motohiro's avatar
    KOSAKI Motohiro committed
    static u64 mem_cgroup_swappiness_read(struct cgroup *cgrp, struct cftype *cft)
    {
    	struct mem_cgroup *memcg = mem_cgroup_from_cont(cgrp);
    
    	return get_swappiness(memcg);
    }
    
    static int mem_cgroup_swappiness_write(struct cgroup *cgrp, struct cftype *cft,
    				       u64 val)
    {
    	struct mem_cgroup *memcg = mem_cgroup_from_cont(cgrp);
    	struct mem_cgroup *parent;
    
    KOSAKI Motohiro's avatar
    KOSAKI Motohiro committed
    	if (val > 100)
    		return -EINVAL;
    
    	if (cgrp->parent == NULL)
    		return -EINVAL;
    
    	parent = mem_cgroup_from_cont(cgrp->parent);
    
    KOSAKI Motohiro's avatar
    KOSAKI Motohiro committed
    	/* If under hierarchy, only empty-root can set this value */
    	if ((parent->use_hierarchy) ||
    
    	    (memcg->use_hierarchy && !list_empty(&cgrp->children))) {
    		cgroup_unlock();
    
    KOSAKI Motohiro's avatar
    KOSAKI Motohiro committed
    		return -EINVAL;
    
    KOSAKI Motohiro's avatar
    KOSAKI Motohiro committed
    
    	spin_lock(&memcg->reclaim_param_lock);
    	memcg->swappiness = val;
    	spin_unlock(&memcg->reclaim_param_lock);
    
    
    KOSAKI Motohiro's avatar
    KOSAKI Motohiro committed
    	return 0;
    }
    
    
    static struct cftype mem_cgroup_files[] = {
    	{
    
    		.name = "usage_in_bytes",
    
    		.private = MEMFILE_PRIVATE(_MEM, RES_USAGE),
    
    		.read_u64 = mem_cgroup_read,
    
    	{
    		.name = "max_usage_in_bytes",
    
    		.private = MEMFILE_PRIVATE(_MEM, RES_MAX_USAGE),
    
    		.trigger = mem_cgroup_reset,
    
    		.read_u64 = mem_cgroup_read,
    	},
    
    		.name = "limit_in_bytes",
    
    		.private = MEMFILE_PRIVATE(_MEM, RES_LIMIT),
    
    		.read_u64 = mem_cgroup_read,
    
    	{
    		.name = "soft_limit_in_bytes",
    		.private = MEMFILE_PRIVATE(_MEM, RES_SOFT_LIMIT),
    		.write_string = mem_cgroup_write,
    		.read_u64 = mem_cgroup_read,
    	},
    
    	{
    		.name = "failcnt",
    
    		.private = MEMFILE_PRIVATE(_MEM, RES_FAILCNT),
    
    		.trigger = mem_cgroup_reset,
    
    		.read_u64 = mem_cgroup_read,
    
    		.read_map = mem_control_stat_show,
    
    	{
    		.name = "force_empty",
    		.trigger = mem_cgroup_force_empty_write,
    	},
    
    	{
    		.name = "use_hierarchy",
    		.write_u64 = mem_cgroup_hierarchy_write,
    		.read_u64 = mem_cgroup_hierarchy_read,
    	},
    
    KOSAKI Motohiro's avatar
    KOSAKI Motohiro committed
    	{
    		.name = "swappiness",
    		.read_u64 = mem_cgroup_swappiness_read,
    		.write_u64 = mem_cgroup_swappiness_write,
    	},
    
    	{
    		.name = "move_charge_at_immigrate",
    		.read_u64 = mem_cgroup_move_charge_read,
    		.write_u64 = mem_cgroup_move_charge_write,
    	},
    
    #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
    static struct cftype memsw_cgroup_files[] = {
    	{
    		.name = "memsw.usage_in_bytes",
    		.private = MEMFILE_PRIVATE(_MEMSWAP, RES_USAGE),
    		.read_u64 = mem_cgroup_read,
    	},
    	{
    		.name = "memsw.max_usage_in_bytes",
    		.private = MEMFILE_PRIVATE(_MEMSWAP, RES_MAX_USAGE),
    		.trigger = mem_cgroup_reset,
    		.read_u64 = mem_cgroup_read,
    	},
    	{
    		.name = "memsw.limit_in_bytes",
    		.private = MEMFILE_PRIVATE(_MEMSWAP, RES_LIMIT),
    		.write_string = mem_cgroup_write,
    		.read_u64 = mem_cgroup_read,
    	},
    	{
    		.name = "memsw.failcnt",
    		.private = MEMFILE_PRIVATE(_MEMSWAP, RES_FAILCNT),
    		.trigger = mem_cgroup_reset,
    		.read_u64 = mem_cgroup_read,
    	},
    };
    
    static int register_memsw_files(struct cgroup *cont, struct cgroup_subsys *ss)
    {
    	if (!do_swap_account)
    		return 0;
    	return cgroup_add_files(cont, ss, memsw_cgroup_files,
    				ARRAY_SIZE(memsw_cgroup_files));
    };
    #else
    static int register_memsw_files(struct cgroup *cont, struct cgroup_subsys *ss)
    {
    	return 0;
    }
    #endif
    
    
    static int alloc_mem_cgroup_per_zone_info(struct mem_cgroup *mem, int node)
    {
    	struct mem_cgroup_per_node *pn;
    
    	int zone, tmp = node;
    
    	/*
    	 * This routine is called against possible nodes.
    	 * But it's BUG to call kmalloc() against offline node.
    	 *
    	 * TODO: this routine can waste much memory for nodes which will
    	 *       never be onlined. It's better to use memory hotplug callback
    	 *       function.
    	 */
    
    	if (!node_state(node, N_NORMAL_MEMORY))
    		tmp = -1;
    	pn = kmalloc_node(sizeof(*pn), GFP_KERNEL, tmp);
    
    	mem->info.nodeinfo[node] = pn;
    	memset(pn, 0, sizeof(*pn));
    
    
    	for (zone = 0; zone < MAX_NR_ZONES; zone++) {
    		mz = &pn->zoneinfo[zone];
    
    		for_each_lru(l)
    			INIT_LIST_HEAD(&mz->lists[l]);
    
    		mz->usage_in_excess = 0;
    
    		mz->on_tree = false;
    		mz->mem = mem;
    
    static void free_mem_cgroup_per_zone_info(struct mem_cgroup *mem, int node)
    {
    	kfree(mem->info.nodeinfo[node]);
    }
    
    
    static int mem_cgroup_size(void)
    {
    	int cpustat_size = nr_cpu_ids * sizeof(struct mem_cgroup_stat_cpu);
    	return sizeof(struct mem_cgroup) + cpustat_size;
    }
    
    
    static struct mem_cgroup *mem_cgroup_alloc(void)
    {
    	struct mem_cgroup *mem;
    
    	int size = mem_cgroup_size();
    
    	if (size < PAGE_SIZE)
    		mem = kmalloc(size, GFP_KERNEL);
    
    		mem = vmalloc(size);
    
    		memset(mem, 0, size);
    
    /*
     * At destroying mem_cgroup, references from swap_cgroup can remain.
     * (scanning all at force_empty is too costly...)
     *
     * Instead of clearing all references at force_empty, we remember
     * 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;
    
    		for_each_possible_cpu(cpu) {
    			struct memcg_stock_pcp *stock =
    						&per_cpu(memcg_stock, cpu);
    			INIT_WORK(&stock->work, drain_local_stock);
    		}
    		hotcpu_notifier(memcg_stock_cpu_callback, 0);
    
    		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);
    
    	mem->move_charge_at_immigrate = 0;
    
    	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;
    
    /* Handlers for move charge at task migration. */
    static int mem_cgroup_can_move_charge(void)
    {
    	return 0;
    }
    
    static int mem_cgroup_can_attach(struct cgroup_subsys *ss,
    				struct cgroup *cgroup,
    				struct task_struct *p,
    				bool threadgroup)
    {
    	int ret = 0;
    	struct mem_cgroup *mem = mem_cgroup_from_cont(cgroup);
    
    	if (mem->move_charge_at_immigrate) {
    		struct mm_struct *mm;
    		struct mem_cgroup *from = mem_cgroup_from_task(p);
    
    		VM_BUG_ON(from == mem);
    
    		mm = get_task_mm(p);
    		if (!mm)
    			return 0;
    
    		/* We move charges only when we move a owner of the mm */
    		if (mm->owner == p)
    			ret = mem_cgroup_can_move_charge();
    
    		mmput(mm);
    	}
    	return ret;
    }
    
    static void mem_cgroup_cancel_attach(struct cgroup_subsys *ss,
    				struct cgroup *cgroup,
    				struct task_struct *p,
    				bool threadgroup)
    {
    }
    
    static void mem_cgroup_move_charge(void)
    {
    }
    
    
    static void mem_cgroup_move_task(struct cgroup_subsys *ss,
    				struct cgroup *cont,
    				struct cgroup *old_cont,
    
    				struct task_struct *p,
    				bool threadgroup)
    
    	mem_cgroup_move_charge();
    
    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,
    
    	.can_attach = mem_cgroup_can_attach,
    	.cancel_attach = mem_cgroup_cancel_attach,
    
    	.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