From: Balbir Singh <balbir@linux.vnet.ibm.com>
To: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Andrew Morton <akpm@linux-foundation.org>,
linux-mm@kvack.org, lizf@cn.fujitsu.com,
KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Subject: Re: [RFC][PATCH 3/5] Memory controller soft limit organize cgroups (v8)
Date: Fri, 10 Jul 2009 12:17:23 +0530 [thread overview]
Message-ID: <20090710064723.GA20129@balbir.in.ibm.com> (raw)
In-Reply-To: <20090710142135.8079cd22.kamezawa.hiroyu@jp.fujitsu.com>
* KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> [2009-07-10 14:21:35]:
> On Thu, 09 Jul 2009 22:45:01 +0530
> Balbir Singh <balbir@linux.vnet.ibm.com> wrote:
>
> > Feature: Organize cgroups over soft limit in a RB-Tree
> >
> > From: Balbir Singh <balbir@linux.vnet.ibm.com>
> >
> > Changelog v8...v7
> > 1. Make the data structures per zone per node
> >
> > Changelog v7...v6
> > 1. Refactor the check and update logic. The goal is to allow the
> > check logic to be modular, so that it can be revisited in the future
> > if something more appropriate is found to be useful.
> >
> > Changelog v6...v5
> > 1. Update the key before inserting into RB tree. Without the current change
> > it could take an additional iteration to get the key correct.
> >
> > Changelog v5...v4
> > 1. res_counter_uncharge has an additional parameter to indicate if the
> > counter was over its soft limit, before uncharge.
> >
> > Changelog v4...v3
> > 1. Optimizations to ensure we don't uncessarily get res_counter values
> > 2. Fixed a bug in usage of time_after()
> >
> > Changelog v3...v2
> > 1. Add only the ancestor to the RB-Tree
> > 2. Use css_tryget/css_put instead of mem_cgroup_get/mem_cgroup_put
> >
> > Changelog v2...v1
> > 1. Add support for hierarchies
> > 2. The res_counter that is highest in the hierarchy is returned on soft
> > limit being exceeded. Since we do hierarchical reclaim and add all
> > groups exceeding their soft limits, this approach seems to work well
> > in practice.
> >
> > This patch introduces a RB-Tree for storing memory cgroups that are over their
> > soft limit. The overall goal is to
> >
> > 1. Add a memory cgroup to the RB-Tree when the soft limit is exceeded.
> > We are careful about updates, updates take place only after a particular
> > time interval has passed
> > 2. We remove the node from the RB-Tree when the usage goes below the soft
> > limit
> >
> > The next set of patches will exploit the RB-Tree to get the group that is
> > over its soft limit by the largest amount and reclaim from it, when we
> > face memory contention.
> >
> > Signed-off-by: Balbir Singh <balbir@linux.vnet.ibm.com>
> > ---
> >
> > include/linux/res_counter.h | 6 +
> > kernel/res_counter.c | 18 ++-
> > mm/memcontrol.c | 304 +++++++++++++++++++++++++++++++++++++------
> > 3 files changed, 281 insertions(+), 47 deletions(-)
> >
> >
> > diff --git a/include/linux/res_counter.h b/include/linux/res_counter.h
> > index fcb9884..731af71 100644
> > --- a/include/linux/res_counter.h
> > +++ b/include/linux/res_counter.h
> > @@ -114,7 +114,8 @@ void res_counter_init(struct res_counter *counter, struct res_counter *parent);
> > int __must_check res_counter_charge_locked(struct res_counter *counter,
> > unsigned long val);
> > int __must_check res_counter_charge(struct res_counter *counter,
> > - unsigned long val, struct res_counter **limit_fail_at);
> > + unsigned long val, struct res_counter **limit_fail_at,
> > + struct res_counter **soft_limit_at);
> >
> > /*
> > * uncharge - tell that some portion of the resource is released
> > @@ -127,7 +128,8 @@ int __must_check res_counter_charge(struct res_counter *counter,
> > */
> >
> > void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val);
> > -void res_counter_uncharge(struct res_counter *counter, unsigned long val);
> > +void res_counter_uncharge(struct res_counter *counter, unsigned long val,
> > + bool *was_soft_limit_excess);
> >
> > static inline bool res_counter_limit_check_locked(struct res_counter *cnt)
> > {
> > diff --git a/kernel/res_counter.c b/kernel/res_counter.c
> > index 4434236..dbdade0 100644
> > --- a/kernel/res_counter.c
> > +++ b/kernel/res_counter.c
> > @@ -37,17 +37,27 @@ int res_counter_charge_locked(struct res_counter *counter, unsigned long val)
> > }
> >
> > int res_counter_charge(struct res_counter *counter, unsigned long val,
> > - struct res_counter **limit_fail_at)
> > + struct res_counter **limit_fail_at,
> > + struct res_counter **soft_limit_fail_at)
> > {
> > int ret;
> > unsigned long flags;
> > struct res_counter *c, *u;
> >
> > *limit_fail_at = NULL;
> > + if (soft_limit_fail_at)
> > + *soft_limit_fail_at = NULL;
> > local_irq_save(flags);
> > for (c = counter; c != NULL; c = c->parent) {
> > spin_lock(&c->lock);
> > ret = res_counter_charge_locked(c, val);
> > + /*
> > + * With soft limits, we return the highest ancestor
> > + * that exceeds its soft limit
> > + */
> > + if (soft_limit_fail_at &&
> > + !res_counter_soft_limit_check_locked(c))
> > + *soft_limit_fail_at = c;
> > spin_unlock(&c->lock);
> > if (ret < 0) {
> > *limit_fail_at = c;
> > @@ -75,7 +85,8 @@ void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val)
> > counter->usage -= val;
> > }
> >
> > -void res_counter_uncharge(struct res_counter *counter, unsigned long val)
> > +void res_counter_uncharge(struct res_counter *counter, unsigned long val,
> > + bool *was_soft_limit_excess)
> > {
> > unsigned long flags;
> > struct res_counter *c;
> > @@ -83,6 +94,9 @@ void res_counter_uncharge(struct res_counter *counter, unsigned long val)
> > local_irq_save(flags);
> > for (c = counter; c != NULL; c = c->parent) {
> > spin_lock(&c->lock);
> > + if (was_soft_limit_excess)
> > + *was_soft_limit_excess =
> > + !res_counter_soft_limit_check_locked(c);
> > res_counter_uncharge_locked(c, val);
> > spin_unlock(&c->lock);
> > }
> > diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> > index 3c9292b..036032b 100644
> > --- a/mm/memcontrol.c
> > +++ b/mm/memcontrol.c
> > @@ -29,6 +29,7 @@
> > #include <linux/rcupdate.h>
> > #include <linux/limits.h>
> > #include <linux/mutex.h>
> > +#include <linux/rbtree.h>
> > #include <linux/slab.h>
> > #include <linux/swap.h>
> > #include <linux/spinlock.h>
> > @@ -118,6 +119,11 @@ struct mem_cgroup_per_zone {
> > unsigned long count[NR_LRU_LISTS];
> >
> > struct zone_reclaim_stat reclaim_stat;
> > + struct rb_node tree_node; /* RB tree node */
> > + unsigned long last_tree_update;/* Last time the tree was */
> > + /* updated in jiffies */
> > + unsigned long long usage_in_excess;/* Set to the value by which */
> > + /* the soft limit is exceeded*/
> > };
>
> As pointed out in several times, plz avoid using jiffies.
>
>
>
> > /* Macro for accessing counter */
> > #define MEM_CGROUP_ZSTAT(mz, idx) ((mz)->count[(idx)])
> > @@ -131,6 +137,28 @@ struct mem_cgroup_lru_info {
> > };
> >
> > /*
> > + * Cgroups above their limits are maintained in a RB-Tree, independent of
> > + * their hierarchy representation
> > + */
> > +
> > +struct mem_cgroup_soft_limit_tree_per_zone {
> > + struct rb_root rb_root;
> > + spinlock_t lock;
> > +};
> > +
> > +struct mem_cgroup_soft_limit_tree_per_node {
> > + struct mem_cgroup_soft_limit_tree_per_zone
> > + rb_tree_per_zone[MAX_NR_ZONES];
> > +};
> > +
> > +struct mem_cgroup_soft_limit_tree {
> > + struct mem_cgroup_soft_limit_tree_per_node
> > + *rb_tree_per_node[MAX_NUMNODES];
> > +};
> > +
> > +static struct mem_cgroup_soft_limit_tree soft_limit_tree;
> > +
> __read_mostly ?
>
Yep, good point
> Isn't that structure name too long ?
>
I'll look at making it smaller.
>
>
> > +/*
> > * The memory controller data structure. The memory controller controls both
> > * page cache and RSS per cgroup. We would eventually like to provide
> > * statistics based on the statistics developed by Rik Van Riel for clock-pro,
> > @@ -187,6 +215,8 @@ struct mem_cgroup {
> > struct mem_cgroup_stat stat;
> > };
> >
> > +#define MEM_CGROUP_TREE_UPDATE_INTERVAL (HZ/4)
> > +
> > enum charge_type {
> > MEM_CGROUP_CHARGE_TYPE_CACHE = 0,
> > MEM_CGROUP_CHARGE_TYPE_MAPPED,
> > @@ -215,6 +245,164 @@ static void mem_cgroup_get(struct mem_cgroup *mem);
> > static void mem_cgroup_put(struct mem_cgroup *mem);
> > static struct mem_cgroup *parent_mem_cgroup(struct mem_cgroup *mem);
> >
> > +static struct mem_cgroup_per_zone *
> > +mem_cgroup_zoneinfo(struct mem_cgroup *mem, int nid, int zid)
> > +{
> > + return &mem->info.nodeinfo[nid]->zoneinfo[zid];
> > +}
> > +
> > +static struct mem_cgroup_per_zone *
> > +page_cgroup_zoneinfo(struct page_cgroup *pc)
> > +{
> > + struct mem_cgroup *mem = pc->mem_cgroup;
> > + int nid = page_cgroup_nid(pc);
> > + int zid = page_cgroup_zid(pc);
> > +
> > + if (!mem)
> > + return NULL;
> > +
> > + return mem_cgroup_zoneinfo(mem, nid, zid);
> > +}
>
> I'm sorry but why this function is rewritten ? Any difference ?
>
Not it was justed moved up
> > +
> > +static struct mem_cgroup_soft_limit_tree_per_zone *
> > +soft_limit_tree_node_zone(int nid, int zid)
> > +{
> > + return &soft_limit_tree.rb_tree_per_node[nid]->rb_tree_per_zone[zid];
> > +}
> > +
> > +static struct mem_cgroup_soft_limit_tree_per_zone *
> > +page_cgroup_soft_limit_tree(struct page_cgroup *pc)
> > +{
> > + int nid = page_cgroup_nid(pc);
> > + int zid = page_cgroup_zid(pc);
> > +
> > + return &soft_limit_tree.rb_tree_per_node[nid]->rb_tree_per_zone[zid];
> > +}
> > +
>
> Hm, I think it's better to use "page" rather than "page_cgroup" as arguments.
> This pointer doesn't depends on pc->mem_cgroup and zid, nid information is
> gotten from "page", originally. (and we can reduce foot print)
>
Absolutely! It is on my todo list.
>
>
>
> > +static void
> > +mem_cgroup_insert_exceeded(struct mem_cgroup *mem,
> > + struct mem_cgroup_per_zone *mz,
> > + struct mem_cgroup_soft_limit_tree_per_zone *stz)
> > +{
> > + struct rb_node **p = &stz->rb_root.rb_node;
> > + struct rb_node *parent = NULL;
> > + struct mem_cgroup_per_zone *mz_node;
> > + unsigned long flags;
> > +
> > + spin_lock_irqsave(&stz->lock, flags);
> > + mz->usage_in_excess = res_counter_soft_limit_excess(&mem->res);
>
> Hmm, can't this be
>
> mz->usage_in_excess = res_counter_soft_limit_excess(&mem->res);
> spin_lock_irqsave(&stz->lock, flags);
> ? There will be no deadlock but I just don't like spinlock under spinlock.
>
I don't think that is avoidable, we need to get resource counter
values and the next patch.
> BTW, why spin_lock_irqsave() ? spin_lock() isn't enough ? If you need to
> disable IRQ, writing reason somewhere is helpful to undestand the code.
>
Good catch, with reclaim now moving to balance_pgdat() _irqsave is not
needed.
>
> > + while (*p) {
>
> I feel this *p should be loaded after taking spinlock(&stz->lock) rather than top
> of function. No?
No.. since the root remains constant once loaded. Am I missing
something?
>
> > + parent = *p;
> > + mz_node = rb_entry(parent, struct mem_cgroup_per_zone,
> > + tree_node);
> > + if (mz->usage_in_excess < mz_node->usage_in_excess)
> > + p = &(*p)->rb_left;
> > + /*
> > + * We can't avoid mem cgroups that are over their soft
> > + * limit by the same amount
> > + */
> > + else if (mz->usage_in_excess >= mz_node->usage_in_excess)
> > + p = &(*p)->rb_right;
> > + }
> > + rb_link_node(&mz->tree_node, parent, p);
> > + rb_insert_color(&mz->tree_node, &stz->rb_root);
> > + mz->last_tree_update = jiffies;
> > + spin_unlock_irqrestore(&stz->lock, flags);
> > +}
> > +
> > +static void
> > +mem_cgroup_remove_exceeded(struct mem_cgroup *mem,
> > + struct mem_cgroup_per_zone *mz,
> > + struct mem_cgroup_soft_limit_tree_per_zone *stz)
> > +{
> > + unsigned long flags;
> > + spin_lock_irqsave(&stz->lock, flags);
> why IRQ save ? again.
>
Will remove
> > + rb_erase(&mz->tree_node, &stz->rb_root);
> > + spin_unlock_irqrestore(&stz->lock, flags);
> > +}
> > +
> > +static bool mem_cgroup_soft_limit_check(struct mem_cgroup *mem,
> > + bool over_soft_limit,
> > + struct page *page)
> > +{
> > + unsigned long next_update;
> > + struct page_cgroup *pc;
> > + struct mem_cgroup_per_zone *mz;
> > +
> > + if (!over_soft_limit)
> > + return false;
> > +
> > + pc = lookup_page_cgroup(page);
> > + if (unlikely(!pc))
> > + return false;
> > + mz = mem_cgroup_zoneinfo(mem, page_cgroup_nid(pc), page_cgroup_zid(pc));
>
> mz = page_cgroup_zoneinfo(pc)
> or
> mz = mem_cgroup_zoneinfo(mem, page_to_nid(page), page_zid(page))
>
Will change it.
> > +
> > + next_update = mz->last_tree_update + MEM_CGROUP_TREE_UPDATE_INTERVAL;
> > + if (time_after(jiffies, next_update))
> > + return true;
> > +
> > + return false;
> > +}
> > +
> > +static void mem_cgroup_update_tree(struct mem_cgroup *mem, struct page *page)
> > +{
> > + unsigned long long prev_usage_in_excess, new_usage_in_excess;
> > + bool updated_tree = false;
> > + unsigned long flags;
> > + struct page_cgroup *pc;
> > + struct mem_cgroup_per_zone *mz;
> > + struct mem_cgroup_soft_limit_tree_per_zone *stz;
> > +
> > + /*
> > + * As long as the page is around, pc's are always
> > + * around and so is the mz, in the remove path
> > + * we are yet to do the css_put(). I don't think
> > + * we need to hold page cgroup lock.
> > + */
> IIUC, at updating tree,we grab this page which is near-to-be-mapped or
> near-to-be-in-radix-treee. If so, not necessary to be annoyied.
Not sure I understand your comment about annoyied (annoyed?)
>
> > + pc = lookup_page_cgroup(page);
> > + if (unlikely(!pc))
> > + return;
>
> I bet this can be BUG_ON().
In the new version we will not need pc
>
> > + mz = mem_cgroup_zoneinfo(mem, page_cgroup_nid(pc), page_cgroup_zid(pc));
> mz = page_cgroup_zoneinfo(pc);
>
In the new version we will not need pc
> > + stz = page_cgroup_soft_limit_tree(pc);
> > +
> > + /*
> > + * We do updates in lazy mode, mem's are removed
> > + * lazily from the per-zone, per-node rb tree
> > + */
> > + prev_usage_in_excess = mz->usage_in_excess;
> > +
> > + new_usage_in_excess = res_counter_soft_limit_excess(&mem->res);
> > + if (prev_usage_in_excess) {
> > + mem_cgroup_remove_exceeded(mem, mz, stz);
> > + updated_tree = true;
> > + }
> IIUC, mz->usage_in_excess can't be used to find out mz is on-tree.
> I think you use "bool on_tree" in patch 5/5. plz use it here.
>
OK, I'll move that part of the code to this patch.
>
>
> > + if (!new_usage_in_excess)
> > + goto done;
> > + mem_cgroup_insert_exceeded(mem, mz, stz);
> > +
> > +done:
> > + if (updated_tree) {
> > + spin_lock_irqsave(&stz->lock, flags);
> > + mz->usage_in_excess = new_usage_in_excess;
> > + spin_unlock_irqrestore(&stz->lock, flags);
> > + }
> > +}
> > +
> > +static void mem_cgroup_remove_from_trees(struct mem_cgroup *mem)
> > +{
> > + int node, zone;
> > + struct mem_cgroup_per_zone *mz;
> > + struct mem_cgroup_soft_limit_tree_per_zone *stz;
> > +
> > + for_each_node_state(node, N_POSSIBLE) {
> > + for (zone = 0; zone < MAX_NR_ZONES; zone++) {
> > + mz = mem_cgroup_zoneinfo(mem, node, zone);
> > + stz = soft_limit_tree_node_zone(node, zone);
> > + mem_cgroup_remove_exceeded(mem, mz, stz);
> > + }
> > + }
> > +}
> > +
> > static void mem_cgroup_charge_statistics(struct mem_cgroup *mem,
> > struct page_cgroup *pc,
> > bool charge)
> > @@ -239,25 +427,6 @@ static void mem_cgroup_charge_statistics(struct mem_cgroup *mem,
> > put_cpu();
> > }
> >
> > -static struct mem_cgroup_per_zone *
> > -mem_cgroup_zoneinfo(struct mem_cgroup *mem, int nid, int zid)
> > -{
> > - return &mem->info.nodeinfo[nid]->zoneinfo[zid];
> > -}
> > -
> > -static struct mem_cgroup_per_zone *
> > -page_cgroup_zoneinfo(struct page_cgroup *pc)
> > -{
> > - struct mem_cgroup *mem = pc->mem_cgroup;
> > - int nid = page_cgroup_nid(pc);
> > - int zid = page_cgroup_zid(pc);
> > -
> > - if (!mem)
> > - return NULL;
> > -
> > - return mem_cgroup_zoneinfo(mem, nid, zid);
> > -}
> > -
> > static unsigned long mem_cgroup_get_local_zonestat(struct mem_cgroup *mem,
> > enum lru_list idx)
> > {
> > @@ -972,11 +1141,11 @@ done:
> > */
> > static int __mem_cgroup_try_charge(struct mm_struct *mm,
> > gfp_t gfp_mask, struct mem_cgroup **memcg,
> > - bool oom)
> > + bool oom, struct page *page)
> > {
>
>
> > - struct mem_cgroup *mem, *mem_over_limit;
> > + struct mem_cgroup *mem, *mem_over_limit, *mem_over_soft_limit;
> > int nr_retries = MEM_CGROUP_RECLAIM_RETRIES;
> > - struct res_counter *fail_res;
> > + struct res_counter *fail_res, *soft_fail_res = NULL;
> >
> > if (unlikely(test_thread_flag(TIF_MEMDIE))) {
> > /* Don't account this! */
> > @@ -1006,16 +1175,17 @@ static int __mem_cgroup_try_charge(struct mm_struct *mm,
> > int ret;
> > bool noswap = false;
> >
> > - ret = res_counter_charge(&mem->res, PAGE_SIZE, &fail_res);
> > + ret = res_counter_charge(&mem->res, PAGE_SIZE, &fail_res,
> > + &soft_fail_res);
> > if (likely(!ret)) {
> > if (!do_swap_account)
> > break;
> > ret = res_counter_charge(&mem->memsw, PAGE_SIZE,
> > - &fail_res);
> > + &fail_res, NULL);
> > if (likely(!ret))
> > break;
> > /* mem+swap counter fails */
> > - res_counter_uncharge(&mem->res, PAGE_SIZE);
> > + res_counter_uncharge(&mem->res, PAGE_SIZE, NULL);
> > noswap = true;
> > mem_over_limit = mem_cgroup_from_res_counter(fail_res,
> > memsw);
> > @@ -1053,13 +1223,24 @@ static int __mem_cgroup_try_charge(struct mm_struct *mm,
> > goto nomem;
> > }
> > }
> > + /*
> > + * Insert just the ancestor, we should trickle down to the correct
> > + * cgroup for reclaim, since the other nodes will be below their
> > + * soft limit
> > + */
> > + if (soft_fail_res) {
> > + mem_over_soft_limit =
> > + mem_cgroup_from_res_counter(soft_fail_res, res);
> > + if (mem_cgroup_soft_limit_check(mem_over_soft_limit, true,
> > + page))
> > + mem_cgroup_update_tree(mem_over_soft_limit, page);
> > + }
>
>
>
> > return 0;
> > nomem:
> > css_put(&mem->css);
> > return -ENOMEM;
> > }
> >
> > -
> > /*
> > * A helper function to get mem_cgroup from ID. must be called under
> > * rcu_read_lock(). The caller must check css_is_removed() or some if
> > @@ -1126,9 +1307,9 @@ static void __mem_cgroup_commit_charge(struct mem_cgroup *mem,
> > lock_page_cgroup(pc);
> > if (unlikely(PageCgroupUsed(pc))) {
> > unlock_page_cgroup(pc);
> > - res_counter_uncharge(&mem->res, PAGE_SIZE);
> > + res_counter_uncharge(&mem->res, PAGE_SIZE, NULL);
> > if (do_swap_account)
> > - res_counter_uncharge(&mem->memsw, PAGE_SIZE);
> > + res_counter_uncharge(&mem->memsw, PAGE_SIZE, NULL);
> > css_put(&mem->css);
> > return;
> > }
> > @@ -1205,7 +1386,7 @@ static int mem_cgroup_move_account(struct page_cgroup *pc,
> > if (pc->mem_cgroup != from)
> > goto out;
> >
> > - res_counter_uncharge(&from->res, PAGE_SIZE);
> > + res_counter_uncharge(&from->res, PAGE_SIZE, NULL);
> > mem_cgroup_charge_statistics(from, pc, false);
> >
> > page = pc->page;
> > @@ -1225,7 +1406,7 @@ static int mem_cgroup_move_account(struct page_cgroup *pc,
> > }
> >
> > if (do_swap_account)
> > - res_counter_uncharge(&from->memsw, PAGE_SIZE);
> > + res_counter_uncharge(&from->memsw, PAGE_SIZE, NULL);
> > css_put(&from->css);
> >
> > css_get(&to->css);
> > @@ -1259,7 +1440,7 @@ static int mem_cgroup_move_parent(struct page_cgroup *pc,
> > parent = mem_cgroup_from_cont(pcg);
> >
> >
> > - ret = __mem_cgroup_try_charge(NULL, gfp_mask, &parent, false);
> > + ret = __mem_cgroup_try_charge(NULL, gfp_mask, &parent, false, page);
> > if (ret || !parent)
> > return ret;
> >
> > @@ -1289,9 +1470,9 @@ uncharge:
> > /* drop extra refcnt by try_charge() */
> > css_put(&parent->css);
> > /* uncharge if move fails */
> > - res_counter_uncharge(&parent->res, PAGE_SIZE);
> > + res_counter_uncharge(&parent->res, PAGE_SIZE, NULL);
> > if (do_swap_account)
> > - res_counter_uncharge(&parent->memsw, PAGE_SIZE);
> > + res_counter_uncharge(&parent->memsw, PAGE_SIZE, NULL);
> > return ret;
> > }
> >
> > @@ -1316,7 +1497,7 @@ static int mem_cgroup_charge_common(struct page *page, struct mm_struct *mm,
> > prefetchw(pc);
> >
> > mem = memcg;
> > - ret = __mem_cgroup_try_charge(mm, gfp_mask, &mem, true);
> > + ret = __mem_cgroup_try_charge(mm, gfp_mask, &mem, true, page);
> > if (ret || !mem)
> > return ret;
> >
> > @@ -1435,14 +1616,14 @@ int mem_cgroup_try_charge_swapin(struct mm_struct *mm,
> > if (!mem)
> > goto charge_cur_mm;
> > *ptr = mem;
> > - ret = __mem_cgroup_try_charge(NULL, mask, ptr, true);
> > + ret = __mem_cgroup_try_charge(NULL, mask, ptr, true, page);
> > /* drop extra refcnt from tryget */
> > css_put(&mem->css);
> > return ret;
> > charge_cur_mm:
> > if (unlikely(!mm))
> > mm = &init_mm;
> > - return __mem_cgroup_try_charge(mm, mask, ptr, true);
> > + return __mem_cgroup_try_charge(mm, mask, ptr, true, page);
> > }
> >
> > static void
> > @@ -1479,7 +1660,7 @@ __mem_cgroup_commit_charge_swapin(struct page *page, struct mem_cgroup *ptr,
> > * This recorded memcg can be obsolete one. So, avoid
> > * calling css_tryget
> > */
> > - res_counter_uncharge(&memcg->memsw, PAGE_SIZE);
> > + res_counter_uncharge(&memcg->memsw, PAGE_SIZE, NULL);
> > mem_cgroup_put(memcg);
> > }
> > rcu_read_unlock();
> > @@ -1500,9 +1681,9 @@ void mem_cgroup_cancel_charge_swapin(struct mem_cgroup *mem)
> > return;
> > if (!mem)
> > return;
> > - res_counter_uncharge(&mem->res, PAGE_SIZE);
> > + res_counter_uncharge(&mem->res, PAGE_SIZE, NULL);
> > if (do_swap_account)
> > - res_counter_uncharge(&mem->memsw, PAGE_SIZE);
> > + res_counter_uncharge(&mem->memsw, PAGE_SIZE, NULL);
> > css_put(&mem->css);
> > }
> >
> > @@ -1516,6 +1697,7 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype)
> > struct page_cgroup *pc;
> > struct mem_cgroup *mem = NULL;
> > struct mem_cgroup_per_zone *mz;
> > + bool soft_limit_excess = false;
> >
> > if (mem_cgroup_disabled())
> > return NULL;
> > @@ -1554,9 +1736,9 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype)
> > break;
> > }
> >
> > - res_counter_uncharge(&mem->res, PAGE_SIZE);
> > + res_counter_uncharge(&mem->res, PAGE_SIZE, &soft_limit_excess);
> > if (do_swap_account && (ctype != MEM_CGROUP_CHARGE_TYPE_SWAPOUT))
> > - res_counter_uncharge(&mem->memsw, PAGE_SIZE);
> > + res_counter_uncharge(&mem->memsw, PAGE_SIZE, NULL);
> > mem_cgroup_charge_statistics(mem, pc, false);
> >
> > ClearPageCgroupUsed(pc);
> > @@ -1570,6 +1752,8 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype)
> > mz = page_cgroup_zoneinfo(pc);
> > unlock_page_cgroup(pc);
> >
> > + if (mem_cgroup_soft_limit_check(mem, soft_limit_excess, page))
> > + mem_cgroup_update_tree(mem, page);
> > /* at swapout, this memcg will be accessed to record to swap */
> > if (ctype != MEM_CGROUP_CHARGE_TYPE_SWAPOUT)
> > css_put(&mem->css);
> > @@ -1645,7 +1829,7 @@ void mem_cgroup_uncharge_swap(swp_entry_t ent)
> > * We uncharge this because swap is freed.
> > * This memcg can be obsolete one. We avoid calling css_tryget
> > */
> > - res_counter_uncharge(&memcg->memsw, PAGE_SIZE);
> > + res_counter_uncharge(&memcg->memsw, PAGE_SIZE, NULL);
> > mem_cgroup_put(memcg);
> > }
> > rcu_read_unlock();
> > @@ -1674,7 +1858,8 @@ int mem_cgroup_prepare_migration(struct page *page, struct mem_cgroup **ptr)
> > unlock_page_cgroup(pc);
> >
> > if (mem) {
> > - ret = __mem_cgroup_try_charge(NULL, GFP_KERNEL, &mem, false);
> > + ret = __mem_cgroup_try_charge(NULL, GFP_KERNEL, &mem, false,
> > + page);
> > css_put(&mem->css);
> > }
> > *ptr = mem;
> > @@ -2177,6 +2362,7 @@ static int mem_cgroup_reset(struct cgroup *cont, unsigned int event)
> > res_counter_reset_failcnt(&mem->memsw);
> > break;
> > }
> > +
> > return 0;
> > }
> noise here.
>
>
> >
> > @@ -2472,6 +2658,8 @@ static int alloc_mem_cgroup_per_zone_info(struct mem_cgroup *mem, int node)
> > mz = &pn->zoneinfo[zone];
> > for_each_lru(l)
> > INIT_LIST_HEAD(&mz->lists[l]);
> > + mz->last_tree_update = 0;
> > + mz->usage_in_excess = 0;
> > }
> > return 0;
> > }
> > @@ -2517,6 +2705,7 @@ static void __mem_cgroup_free(struct mem_cgroup *mem)
> > {
> > int node;
> >
> > + mem_cgroup_remove_from_trees(mem);
> > free_css_id(&mem_cgroup_subsys, &mem->css);
> >
> > for_each_node_state(node, N_POSSIBLE)
> > @@ -2565,6 +2754,31 @@ static void __init enable_swap_cgroup(void)
> > }
> > #endif
> >
> > +static int mem_cgroup_soft_limit_tree_init(void)
> > +{
> > + struct mem_cgroup_soft_limit_tree_per_node *rtpn;
> > + struct mem_cgroup_soft_limit_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;
> > +}
> > +
> > static struct cgroup_subsys_state * __ref
> > mem_cgroup_create(struct cgroup_subsys *ss, struct cgroup *cont)
> > {
> > @@ -2579,11 +2793,15 @@ mem_cgroup_create(struct cgroup_subsys *ss, struct cgroup *cont)
> > for_each_node_state(node, N_POSSIBLE)
> > if (alloc_mem_cgroup_per_zone_info(mem, node))
> > goto free_out;
> > +
> > /* root ? */
> > if (cont->parent == NULL) {
> > enable_swap_cgroup();
> > parent = NULL;
> > root_mem_cgroup = mem;
> > + if (mem_cgroup_soft_limit_tree_init())
> > + goto free_out;
> > +
> > } else {
> > parent = mem_cgroup_from_cont(cont->parent);
> > mem->use_hierarchy = parent->use_hierarchy;
> >
>
> Thx,
> -Kame
>
Thanks for the review,
--
Balbir
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
next prev parent reply other threads:[~2009-07-10 6:24 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-07-09 17:14 [RFC][PATCH 0/5] Memory controller soft limit patches (v8) Balbir Singh
2009-07-09 17:14 ` [RFC][PATCH 1/5] Memory controller soft limit documentation (v8) Balbir Singh
2009-07-10 5:32 ` KAMEZAWA Hiroyuki
2009-07-10 6:48 ` Balbir Singh
2009-07-09 17:14 ` [RFC][PATCH 2/5] Memory controller soft limit interface (v8) Balbir Singh
2009-07-09 17:15 ` [RFC][PATCH 3/5] Memory controller soft limit organize cgroups (v8) Balbir Singh
2009-07-10 5:21 ` KAMEZAWA Hiroyuki
2009-07-10 6:47 ` Balbir Singh [this message]
2009-07-10 7:16 ` KAMEZAWA Hiroyuki
2009-07-10 8:05 ` Balbir Singh
2009-07-10 8:14 ` KAMEZAWA Hiroyuki
2009-07-10 8:20 ` Balbir Singh
2009-07-09 17:15 ` [RFC][PATCH 4/5] Memory controller soft limit refactor reclaim flags (v8) Balbir Singh
2009-07-09 17:15 ` [RFC][PATCH 5/5] Memory controller soft limit reclaim on contention (v8) Balbir Singh
2009-07-10 5:30 ` KAMEZAWA Hiroyuki
2009-07-10 6:53 ` Balbir Singh
2009-07-10 7:30 ` KAMEZAWA Hiroyuki
2009-07-10 7:49 ` Balbir Singh
2009-07-10 10:56 ` Balbir Singh
2009-07-10 14:15 ` KAMEZAWA Hiroyuki
2009-07-10 14:22 ` Balbir Singh
2009-07-10 4:53 ` [RFC][PATCH 0/5] Memory controller soft limit patches (v8) KAMEZAWA Hiroyuki
2009-07-10 5:53 ` Balbir Singh
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20090710064723.GA20129@balbir.in.ibm.com \
--to=balbir@linux.vnet.ibm.com \
--cc=akpm@linux-foundation.org \
--cc=kamezawa.hiroyu@jp.fujitsu.com \
--cc=kosaki.motohiro@jp.fujitsu.com \
--cc=linux-mm@kvack.org \
--cc=lizf@cn.fujitsu.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).