2008-02-07 15:13:50 +07:00
|
|
|
/* memcontrol.c - Memory Controller
|
|
|
|
*
|
|
|
|
* Copyright IBM Corporation, 2007
|
|
|
|
* Author Balbir Singh <balbir@linux.vnet.ibm.com>
|
|
|
|
*
|
2008-02-07 15:13:51 +07:00
|
|
|
* Copyright 2007 OpenVZ SWsoft Inc
|
|
|
|
* Author: Pavel Emelianov <xemul@openvz.org>
|
|
|
|
*
|
2010-03-11 06:22:24 +07:00
|
|
|
* Memory thresholds
|
|
|
|
* Copyright (C) 2009 Nokia Corporation
|
|
|
|
* Author: Kirill A. Shutemov
|
|
|
|
*
|
2008-02-07 15:13:50 +07:00
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/res_counter.h>
|
|
|
|
#include <linux/memcontrol.h>
|
|
|
|
#include <linux/cgroup.h>
|
2008-02-07 15:13:51 +07:00
|
|
|
#include <linux/mm.h>
|
2010-03-11 06:22:14 +07:00
|
|
|
#include <linux/hugetlb.h>
|
2009-01-08 09:07:56 +07:00
|
|
|
#include <linux/pagemap.h>
|
2008-02-07 15:14:24 +07:00
|
|
|
#include <linux/smp.h>
|
2008-02-07 15:13:53 +07:00
|
|
|
#include <linux/page-flags.h>
|
2008-02-07 15:13:56 +07:00
|
|
|
#include <linux/backing-dev.h>
|
2008-02-07 15:13:53 +07:00
|
|
|
#include <linux/bit_spinlock.h>
|
|
|
|
#include <linux/rcupdate.h>
|
2009-04-03 06:57:39 +07:00
|
|
|
#include <linux/limits.h>
|
2011-05-27 03:00:52 +07:00
|
|
|
#include <linux/export.h>
|
2009-01-08 09:08:00 +07:00
|
|
|
#include <linux/mutex.h>
|
2009-09-24 05:56:37 +07:00
|
|
|
#include <linux/rbtree.h>
|
2008-04-29 15:00:19 +07:00
|
|
|
#include <linux/slab.h>
|
2008-02-07 15:13:56 +07:00
|
|
|
#include <linux/swap.h>
|
2010-03-11 06:22:17 +07:00
|
|
|
#include <linux/swapops.h>
|
2008-02-07 15:13:56 +07:00
|
|
|
#include <linux/spinlock.h>
|
2010-03-11 06:22:24 +07:00
|
|
|
#include <linux/eventfd.h>
|
|
|
|
#include <linux/sort.h>
|
2008-02-07 15:13:56 +07:00
|
|
|
#include <linux/fs.h>
|
2008-02-07 15:14:25 +07:00
|
|
|
#include <linux/seq_file.h>
|
2008-04-29 15:00:24 +07:00
|
|
|
#include <linux/vmalloc.h>
|
2008-10-19 10:26:14 +07:00
|
|
|
#include <linux/mm_inline.h>
|
2008-10-19 10:28:16 +07:00
|
|
|
#include <linux/page_cgroup.h>
|
2009-12-16 07:47:08 +07:00
|
|
|
#include <linux/cpu.h>
|
2010-08-11 08:03:00 +07:00
|
|
|
#include <linux/oom.h>
|
memcg: synchronized LRU
A big patch for changing memcg's LRU semantics.
Now,
- page_cgroup is linked to mem_cgroup's its own LRU (per zone).
- LRU of page_cgroup is not synchronous with global LRU.
- page and page_cgroup is one-to-one and statically allocated.
- To find page_cgroup is on what LRU, you have to check pc->mem_cgroup as
- lru = page_cgroup_zoneinfo(pc, nid_of_pc, zid_of_pc);
- SwapCache is handled.
And, when we handle LRU list of page_cgroup, we do following.
pc = lookup_page_cgroup(page);
lock_page_cgroup(pc); .....................(1)
mz = page_cgroup_zoneinfo(pc);
spin_lock(&mz->lru_lock);
.....add to LRU
spin_unlock(&mz->lru_lock);
unlock_page_cgroup(pc);
But (1) is spin_lock and we have to be afraid of dead-lock with zone->lru_lock.
So, trylock() is used at (1), now. Without (1), we can't trust "mz" is correct.
This is a trial to remove this dirty nesting of locks.
This patch changes mz->lru_lock to be zone->lru_lock.
Then, above sequence will be written as
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
mem_cgroup_add/remove/etc_lru() {
pc = lookup_page_cgroup(page);
mz = page_cgroup_zoneinfo(pc);
if (PageCgroupUsed(pc)) {
....add to LRU
}
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
This is much simpler.
(*) We're safe even if we don't take lock_page_cgroup(pc). Because..
1. When pc->mem_cgroup can be modified.
- at charge.
- at account_move().
2. at charge
the PCG_USED bit is not set before pc->mem_cgroup is fixed.
3. at account_move()
the page is isolated and not on LRU.
Pros.
- easy for maintenance.
- memcg can make use of laziness of pagevec.
- we don't have to duplicated LRU/Active/Unevictable bit in page_cgroup.
- LRU status of memcg will be synchronized with global LRU's one.
- # of locks are reduced.
- account_move() is simplified very much.
Cons.
- may increase cost of LRU rotation.
(no impact if memcg is not configured.)
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:08:01 +07:00
|
|
|
#include "internal.h"
|
2011-12-12 04:47:04 +07:00
|
|
|
#include <net/sock.h>
|
2012-10-09 06:33:10 +07:00
|
|
|
#include <net/ip.h>
|
2011-12-12 04:47:04 +07:00
|
|
|
#include <net/tcp_memcontrol.h>
|
2008-02-07 15:13:50 +07:00
|
|
|
|
2008-02-07 15:13:59 +07:00
|
|
|
#include <asm/uaccess.h>
|
|
|
|
|
2010-08-10 07:19:57 +07:00
|
|
|
#include <trace/events/vmscan.h>
|
|
|
|
|
2008-07-25 15:47:08 +07:00
|
|
|
struct cgroup_subsys mem_cgroup_subsys __read_mostly;
|
|
|
|
#define MEM_CGROUP_RECLAIM_RETRIES 5
|
2012-05-30 05:06:55 +07:00
|
|
|
static struct mem_cgroup *root_mem_cgroup __read_mostly;
|
2008-02-07 15:13:50 +07:00
|
|
|
|
2012-08-01 06:43:02 +07:00
|
|
|
#ifdef CONFIG_MEMCG_SWAP
|
2009-06-18 06:27:15 +07:00
|
|
|
/* Turned on only when memory cgroup is enabled && really_do_swap_account = 1 */
|
2009-01-08 09:07:57 +07:00
|
|
|
int do_swap_account __read_mostly;
|
2010-11-25 03:57:08 +07:00
|
|
|
|
|
|
|
/* for remember boot option*/
|
2012-08-01 06:43:02 +07:00
|
|
|
#ifdef CONFIG_MEMCG_SWAP_ENABLED
|
2010-11-25 03:57:08 +07:00
|
|
|
static int really_do_swap_account __initdata = 1;
|
|
|
|
#else
|
|
|
|
static int really_do_swap_account __initdata = 0;
|
|
|
|
#endif
|
|
|
|
|
2009-01-08 09:07:57 +07:00
|
|
|
#else
|
2012-05-30 05:06:56 +07:00
|
|
|
#define do_swap_account 0
|
2009-01-08 09:07:57 +07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2008-02-07 15:14:24 +07:00
|
|
|
/*
|
|
|
|
* Statistics for memory cgroup.
|
|
|
|
*/
|
|
|
|
enum mem_cgroup_stat_index {
|
|
|
|
/*
|
|
|
|
* For MEM_CONTAINER_TYPE_ALL, usage = pagecache + rss.
|
|
|
|
*/
|
|
|
|
MEM_CGROUP_STAT_CACHE, /* # of pages charged as cache */
|
2009-06-18 06:26:34 +07:00
|
|
|
MEM_CGROUP_STAT_RSS, /* # of pages charged as anon rss */
|
2009-12-16 07:47:09 +07:00
|
|
|
MEM_CGROUP_STAT_FILE_MAPPED, /* # of pages charged as file rss */
|
2012-08-01 06:41:38 +07:00
|
|
|
MEM_CGROUP_STAT_SWAP, /* # of pages, swapped out */
|
2008-02-07 15:14:24 +07:00
|
|
|
MEM_CGROUP_STAT_NSTATS,
|
|
|
|
};
|
|
|
|
|
2012-05-30 05:07:08 +07:00
|
|
|
static const char * const mem_cgroup_stat_names[] = {
|
|
|
|
"cache",
|
|
|
|
"rss",
|
|
|
|
"mapped_file",
|
|
|
|
"swap",
|
|
|
|
};
|
|
|
|
|
2011-03-24 06:42:37 +07:00
|
|
|
enum mem_cgroup_events_index {
|
|
|
|
MEM_CGROUP_EVENTS_PGPGIN, /* # of pages paged in */
|
|
|
|
MEM_CGROUP_EVENTS_PGPGOUT, /* # of pages paged out */
|
2011-05-27 06:25:38 +07:00
|
|
|
MEM_CGROUP_EVENTS_PGFAULT, /* # of page-faults */
|
|
|
|
MEM_CGROUP_EVENTS_PGMAJFAULT, /* # of major page-faults */
|
2011-03-24 06:42:37 +07:00
|
|
|
MEM_CGROUP_EVENTS_NSTATS,
|
|
|
|
};
|
2012-05-30 05:07:08 +07:00
|
|
|
|
|
|
|
static const char * const mem_cgroup_events_names[] = {
|
|
|
|
"pgpgin",
|
|
|
|
"pgpgout",
|
|
|
|
"pgfault",
|
|
|
|
"pgmajfault",
|
|
|
|
};
|
|
|
|
|
2011-03-24 06:42:38 +07:00
|
|
|
/*
|
|
|
|
* Per memcg event counter is incremented at every pagein/pageout. With THP,
|
|
|
|
* it will be incremated by the number of pages. This counter is used for
|
|
|
|
* for trigger some periodic events. This is straightforward and better
|
|
|
|
* than using jiffies etc. to handle periodic memcg event.
|
|
|
|
*/
|
|
|
|
enum mem_cgroup_events_target {
|
|
|
|
MEM_CGROUP_TARGET_THRESH,
|
|
|
|
MEM_CGROUP_TARGET_SOFTLIMIT,
|
2011-07-09 05:39:43 +07:00
|
|
|
MEM_CGROUP_TARGET_NUMAINFO,
|
2011-03-24 06:42:38 +07:00
|
|
|
MEM_CGROUP_NTARGETS,
|
|
|
|
};
|
2012-05-30 05:06:56 +07:00
|
|
|
#define THRESHOLDS_EVENTS_TARGET 128
|
|
|
|
#define SOFTLIMIT_EVENTS_TARGET 1024
|
|
|
|
#define NUMAINFO_EVENTS_TARGET 1024
|
2011-03-24 06:42:37 +07:00
|
|
|
|
2008-02-07 15:14:24 +07:00
|
|
|
struct mem_cgroup_stat_cpu {
|
2011-03-24 06:42:38 +07:00
|
|
|
long count[MEM_CGROUP_STAT_NSTATS];
|
2011-03-24 06:42:37 +07:00
|
|
|
unsigned long events[MEM_CGROUP_EVENTS_NSTATS];
|
2012-05-30 05:07:07 +07:00
|
|
|
unsigned long nr_page_events;
|
2011-03-24 06:42:38 +07:00
|
|
|
unsigned long targets[MEM_CGROUP_NTARGETS];
|
2008-02-07 15:14:24 +07:00
|
|
|
};
|
|
|
|
|
mm: memcg: per-priority per-zone hierarchy scan generations
Memory cgroup limit reclaim currently picks one memory cgroup out of the
target hierarchy, remembers it as the last scanned child, and reclaims
all zones in it with decreasing priority levels.
The new hierarchy reclaim code will pick memory cgroups from the same
hierarchy concurrently from different zones and priority levels, it
becomes necessary that hierarchy roots not only remember the last
scanned child, but do so for each zone and priority level.
Until now, we reclaimed memcgs like this:
mem = mem_cgroup_iter(root)
for each priority level:
for each zone in zonelist:
reclaim(mem, zone)
But subsequent patches will move the memcg iteration inside the loop
over the zones:
for each priority level:
for each zone in zonelist:
mem = mem_cgroup_iter(root)
reclaim(mem, zone)
And to keep with the original scan order - memcg -> priority -> zone -
the last scanned memcg has to be remembered per zone and per priority
level.
Furthermore, global reclaim will be switched to the hierarchy walk as
well. Different from limit reclaim, which can just recheck the limit
after some reclaim progress, its target is to scan all memcgs for the
desired zone pages, proportional to the memcg size, and so reliably
detecting a full hierarchy round-trip will become crucial.
Currently, the code relies on one reclaimer encountering the same memcg
twice, but that is error-prone with concurrent reclaimers. Instead, use
a generation counter that is increased every time the child with the
highest ID has been visited, so that reclaimers can stop when the
generation changes.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:55 +07:00
|
|
|
struct mem_cgroup_reclaim_iter {
|
|
|
|
/* css_id of the last scanned hierarchy member */
|
|
|
|
int position;
|
|
|
|
/* scan generation, increased every round-trip */
|
|
|
|
unsigned int generation;
|
|
|
|
};
|
|
|
|
|
2008-02-07 15:14:31 +07:00
|
|
|
/*
|
|
|
|
* per-zone information in memory controller.
|
|
|
|
*/
|
|
|
|
struct mem_cgroup_per_zone {
|
2012-01-13 08:18:10 +07:00
|
|
|
struct lruvec lruvec;
|
2012-03-22 06:34:19 +07:00
|
|
|
unsigned long lru_size[NR_LRU_LISTS];
|
2009-01-08 09:08:20 +07:00
|
|
|
|
mm: memcg: per-priority per-zone hierarchy scan generations
Memory cgroup limit reclaim currently picks one memory cgroup out of the
target hierarchy, remembers it as the last scanned child, and reclaims
all zones in it with decreasing priority levels.
The new hierarchy reclaim code will pick memory cgroups from the same
hierarchy concurrently from different zones and priority levels, it
becomes necessary that hierarchy roots not only remember the last
scanned child, but do so for each zone and priority level.
Until now, we reclaimed memcgs like this:
mem = mem_cgroup_iter(root)
for each priority level:
for each zone in zonelist:
reclaim(mem, zone)
But subsequent patches will move the memcg iteration inside the loop
over the zones:
for each priority level:
for each zone in zonelist:
mem = mem_cgroup_iter(root)
reclaim(mem, zone)
And to keep with the original scan order - memcg -> priority -> zone -
the last scanned memcg has to be remembered per zone and per priority
level.
Furthermore, global reclaim will be switched to the hierarchy walk as
well. Different from limit reclaim, which can just recheck the limit
after some reclaim progress, its target is to scan all memcgs for the
desired zone pages, proportional to the memcg size, and so reliably
detecting a full hierarchy round-trip will become crucial.
Currently, the code relies on one reclaimer encountering the same memcg
twice, but that is error-prone with concurrent reclaimers. Instead, use
a generation counter that is increased every time the child with the
highest ID has been visited, so that reclaimers can stop when the
generation changes.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:55 +07:00
|
|
|
struct mem_cgroup_reclaim_iter reclaim_iter[DEF_PRIORITY + 1];
|
|
|
|
|
2009-09-24 05:56:37 +07:00
|
|
|
struct rb_node tree_node; /* RB tree node */
|
|
|
|
unsigned long long usage_in_excess;/* Set to the value by which */
|
|
|
|
/* the soft limit is exceeded*/
|
|
|
|
bool on_tree;
|
2012-03-22 06:34:18 +07:00
|
|
|
struct mem_cgroup *memcg; /* Back pointer, we cannot */
|
2009-09-24 05:56:39 +07:00
|
|
|
/* use container_of */
|
2008-02-07 15:14:31 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
struct mem_cgroup_per_node {
|
|
|
|
struct mem_cgroup_per_zone zoneinfo[MAX_NR_ZONES];
|
|
|
|
};
|
|
|
|
|
|
|
|
struct mem_cgroup_lru_info {
|
|
|
|
struct mem_cgroup_per_node *nodeinfo[MAX_NUMNODES];
|
|
|
|
};
|
|
|
|
|
2009-09-24 05:56:37 +07:00
|
|
|
/*
|
|
|
|
* Cgroups above their limits are maintained in a RB-Tree, independent of
|
|
|
|
* their hierarchy representation
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct mem_cgroup_tree_per_zone {
|
|
|
|
struct rb_root rb_root;
|
|
|
|
spinlock_t lock;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct mem_cgroup_tree_per_node {
|
|
|
|
struct mem_cgroup_tree_per_zone rb_tree_per_zone[MAX_NR_ZONES];
|
|
|
|
};
|
|
|
|
|
|
|
|
struct mem_cgroup_tree {
|
|
|
|
struct mem_cgroup_tree_per_node *rb_tree_per_node[MAX_NUMNODES];
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct mem_cgroup_tree soft_limit_tree __read_mostly;
|
|
|
|
|
2010-03-11 06:22:24 +07:00
|
|
|
struct mem_cgroup_threshold {
|
|
|
|
struct eventfd_ctx *eventfd;
|
|
|
|
u64 threshold;
|
|
|
|
};
|
|
|
|
|
2010-05-27 04:42:36 +07:00
|
|
|
/* For threshold */
|
2010-03-11 06:22:24 +07:00
|
|
|
struct mem_cgroup_threshold_ary {
|
2012-05-30 05:06:57 +07:00
|
|
|
/* An array index points to threshold just below or equal to usage. */
|
2010-05-27 04:42:42 +07:00
|
|
|
int current_threshold;
|
2010-03-11 06:22:24 +07:00
|
|
|
/* Size of entries[] */
|
|
|
|
unsigned int size;
|
|
|
|
/* Array of thresholds */
|
|
|
|
struct mem_cgroup_threshold entries[0];
|
|
|
|
};
|
2010-05-27 04:42:47 +07:00
|
|
|
|
|
|
|
struct mem_cgroup_thresholds {
|
|
|
|
/* Primary thresholds array */
|
|
|
|
struct mem_cgroup_threshold_ary *primary;
|
|
|
|
/*
|
|
|
|
* Spare threshold array.
|
|
|
|
* This is needed to make mem_cgroup_unregister_event() "never fail".
|
|
|
|
* It must be able to store at least primary->size - 1 entries.
|
|
|
|
*/
|
|
|
|
struct mem_cgroup_threshold_ary *spare;
|
|
|
|
};
|
|
|
|
|
2010-05-27 04:42:36 +07:00
|
|
|
/* for OOM */
|
|
|
|
struct mem_cgroup_eventfd_list {
|
|
|
|
struct list_head list;
|
|
|
|
struct eventfd_ctx *eventfd;
|
|
|
|
};
|
2010-03-11 06:22:24 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static void mem_cgroup_threshold(struct mem_cgroup *memcg);
|
|
|
|
static void mem_cgroup_oom_notify(struct mem_cgroup *memcg);
|
2010-03-11 06:22:24 +07:00
|
|
|
|
2008-02-07 15:13:50 +07:00
|
|
|
/*
|
|
|
|
* 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,
|
|
|
|
* to help the administrator determine what knobs to tune.
|
|
|
|
*
|
|
|
|
* TODO: Add a water mark for the memory controller. Reclaim will begin when
|
2008-02-07 15:13:53 +07:00
|
|
|
* we hit the water mark. May be even add a low water mark, such that
|
|
|
|
* no reclaim occurs from a cgroup at it's low water mark, this is
|
|
|
|
* a feature that will be implemented much later in the future.
|
2008-02-07 15:13:50 +07:00
|
|
|
*/
|
|
|
|
struct mem_cgroup {
|
|
|
|
struct cgroup_subsys_state css;
|
|
|
|
/*
|
|
|
|
* the counter to account for memory usage
|
|
|
|
*/
|
|
|
|
struct res_counter res;
|
memcg: free mem_cgroup by RCU to fix oops
After fixing the GPF in mem_cgroup_lru_del_list(), three times one
machine running a similar load (moving and removing memcgs while
swapping) has oopsed in mem_cgroup_zone_nr_lru_pages(), when retrieving
memcg zone numbers for get_scan_count() for shrink_mem_cgroup_zone():
this is where a struct mem_cgroup is first accessed after being chosen
by mem_cgroup_iter().
Just what protects a struct mem_cgroup from being freed, in between
mem_cgroup_iter()'s css_get_next() and its css_tryget()? css_tryget()
fails once css->refcnt is zero with CSS_REMOVED set in flags, yes: but
what if that memory is freed and reused for something else, which sets
"refcnt" non-zero? Hmm, and scope for an indefinite freeze if refcnt is
left at zero but flags are cleared.
It's tempting to move the css_tryget() into css_get_next(), to make it
really "get" the css, but I don't think that actually solves anything:
the same difficulty in moving from css_id found to stable css remains.
But we already have rcu_read_lock() around the two, so it's easily fixed
if __mem_cgroup_free() just uses kfree_rcu() to free mem_cgroup.
However, a big struct mem_cgroup is allocated with vzalloc() instead of
kzalloc(), and we're not allowed to vfree() at interrupt time: there
doesn't appear to be a general vfree_rcu() to help with this, so roll
our own using schedule_work(). The compiler decently removes
vfree_work() and vfree_rcu() when the config doesn't need them.
Signed-off-by: Hugh Dickins <hughd@google.com>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Cc: Konstantin Khlebnikov <khlebnikov@openvz.org>
Cc: Tejun Heo <tj@kernel.org>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-16 05:17:07 +07:00
|
|
|
|
|
|
|
union {
|
|
|
|
/*
|
|
|
|
* the counter to account for mem+swap usage.
|
|
|
|
*/
|
|
|
|
struct res_counter memsw;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* rcu_freeing is used only when freeing struct mem_cgroup,
|
|
|
|
* so put it into a union to avoid wasting more memory.
|
|
|
|
* It must be disjoint from the css field. It could be
|
|
|
|
* in a union with the res field, but res plays a much
|
|
|
|
* larger part in mem_cgroup life than memsw, and might
|
|
|
|
* be of interest, even at time of free, when debugging.
|
|
|
|
* So share rcu_head with the less interesting memsw.
|
|
|
|
*/
|
|
|
|
struct rcu_head rcu_freeing;
|
|
|
|
/*
|
2012-05-30 05:07:10 +07:00
|
|
|
* We also need some space for a worker in deferred freeing.
|
|
|
|
* By the time we call it, rcu_freeing is no longer in use.
|
memcg: free mem_cgroup by RCU to fix oops
After fixing the GPF in mem_cgroup_lru_del_list(), three times one
machine running a similar load (moving and removing memcgs while
swapping) has oopsed in mem_cgroup_zone_nr_lru_pages(), when retrieving
memcg zone numbers for get_scan_count() for shrink_mem_cgroup_zone():
this is where a struct mem_cgroup is first accessed after being chosen
by mem_cgroup_iter().
Just what protects a struct mem_cgroup from being freed, in between
mem_cgroup_iter()'s css_get_next() and its css_tryget()? css_tryget()
fails once css->refcnt is zero with CSS_REMOVED set in flags, yes: but
what if that memory is freed and reused for something else, which sets
"refcnt" non-zero? Hmm, and scope for an indefinite freeze if refcnt is
left at zero but flags are cleared.
It's tempting to move the css_tryget() into css_get_next(), to make it
really "get" the css, but I don't think that actually solves anything:
the same difficulty in moving from css_id found to stable css remains.
But we already have rcu_read_lock() around the two, so it's easily fixed
if __mem_cgroup_free() just uses kfree_rcu() to free mem_cgroup.
However, a big struct mem_cgroup is allocated with vzalloc() instead of
kzalloc(), and we're not allowed to vfree() at interrupt time: there
doesn't appear to be a general vfree_rcu() to help with this, so roll
our own using schedule_work(). The compiler decently removes
vfree_work() and vfree_rcu() when the config doesn't need them.
Signed-off-by: Hugh Dickins <hughd@google.com>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Cc: Konstantin Khlebnikov <khlebnikov@openvz.org>
Cc: Tejun Heo <tj@kernel.org>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-16 05:17:07 +07:00
|
|
|
*/
|
|
|
|
struct work_struct work_freeing;
|
|
|
|
};
|
|
|
|
|
2008-02-07 15:13:51 +07:00
|
|
|
/*
|
|
|
|
* Per cgroup active and inactive list, similar to the
|
|
|
|
* per zone LRU lists.
|
|
|
|
*/
|
2008-02-07 15:14:31 +07:00
|
|
|
struct mem_cgroup_lru_info info;
|
2011-05-27 06:25:33 +07:00
|
|
|
int last_scanned_node;
|
|
|
|
#if MAX_NUMNODES > 1
|
|
|
|
nodemask_t scan_nodes;
|
2011-07-09 05:39:43 +07:00
|
|
|
atomic_t numainfo_events;
|
|
|
|
atomic_t numainfo_updating;
|
2011-05-27 06:25:33 +07:00
|
|
|
#endif
|
2009-01-08 09:08:07 +07:00
|
|
|
/*
|
|
|
|
* Should the accounting and control be hierarchical, per subtree?
|
|
|
|
*/
|
|
|
|
bool use_hierarchy;
|
2011-07-27 06:08:23 +07:00
|
|
|
|
|
|
|
bool oom_lock;
|
|
|
|
atomic_t under_oom;
|
|
|
|
|
2009-01-08 09:08:00 +07:00
|
|
|
atomic_t refcnt;
|
2009-01-08 09:08:18 +07:00
|
|
|
|
2011-07-27 06:08:21 +07:00
|
|
|
int swappiness;
|
2010-05-27 04:42:37 +07:00
|
|
|
/* OOM-Killer disable */
|
|
|
|
int oom_kill_disable;
|
2009-01-08 09:08:24 +07:00
|
|
|
|
2009-06-18 06:27:19 +07:00
|
|
|
/* set when res.limit == memsw.limit */
|
|
|
|
bool memsw_is_minimum;
|
|
|
|
|
2010-03-11 06:22:24 +07:00
|
|
|
/* protect arrays of thresholds */
|
|
|
|
struct mutex thresholds_lock;
|
|
|
|
|
|
|
|
/* thresholds for memory usage. RCU-protected */
|
2010-05-27 04:42:47 +07:00
|
|
|
struct mem_cgroup_thresholds thresholds;
|
2010-05-27 04:42:46 +07:00
|
|
|
|
2010-03-11 06:22:24 +07:00
|
|
|
/* thresholds for mem+swap usage. RCU-protected */
|
2010-05-27 04:42:47 +07:00
|
|
|
struct mem_cgroup_thresholds memsw_thresholds;
|
2010-05-27 04:42:46 +07:00
|
|
|
|
2010-05-27 04:42:36 +07:00
|
|
|
/* For oom notifier event fd */
|
|
|
|
struct list_head oom_notify;
|
2011-09-15 06:21:58 +07:00
|
|
|
|
2010-03-11 06:22:13 +07:00
|
|
|
/*
|
|
|
|
* Should we move charges of a task when a task is moved into this
|
|
|
|
* mem_cgroup ? And what type of charges should we move ?
|
|
|
|
*/
|
|
|
|
unsigned long move_charge_at_immigrate;
|
2012-03-22 06:34:23 +07:00
|
|
|
/*
|
|
|
|
* set > 0 if pages under this cgroup are moving to other cgroup.
|
|
|
|
*/
|
|
|
|
atomic_t moving_account;
|
2012-03-22 06:34:24 +07:00
|
|
|
/* taken only while moving_account > 0 */
|
|
|
|
spinlock_t move_lock;
|
2008-02-07 15:14:24 +07:00
|
|
|
/*
|
memcg: use generic percpu instead of private implementation
When per-cpu counter for memcg was implemneted, dynamic percpu allocator
was not very good. But now, we have good one and useful macros. This
patch replaces memcg's private percpu counter implementation with generic
dynamic percpu allocator.
The benefits are
- We can remove private implementation.
- The counters will be NUMA-aware. (Current one is not...)
- This patch makes sizeof struct mem_cgroup smaller. Then,
struct mem_cgroup may be fit in page size on small config.
- About basic performance aspects, see below.
[Before]
# size mm/memcontrol.o
text data bss dec hex filename
24373 2528 4132 31033 7939 mm/memcontrol.o
[page-fault-throuput test on 8cpu/SMP in root cgroup]
# /root/bin/perf stat -a -e page-faults,cache-misses --repeat 5 ./multi-fault-fork 8
Performance counter stats for './multi-fault-fork 8' (5 runs):
45878618 page-faults ( +- 0.110% )
602635826 cache-misses ( +- 0.105% )
61.005373262 seconds time elapsed ( +- 0.004% )
Then cache-miss/page fault = 13.14
[After]
#size mm/memcontrol.o
text data bss dec hex filename
23913 2528 4132 30573 776d mm/memcontrol.o
# /root/bin/perf stat -a -e page-faults,cache-misses --repeat 5 ./multi-fault-fork 8
Performance counter stats for './multi-fault-fork 8' (5 runs):
48179400 page-faults ( +- 0.271% )
588628407 cache-misses ( +- 0.136% )
61.004615021 seconds time elapsed ( +- 0.004% )
Then cache-miss/page fault = 12.22
Text size is reduced.
This performance improvement is not big and will be invisible in real world
applications. But this result shows this patch has some good effect even
on (small) SMP.
Here is a test program I used.
1. fork() processes on each cpus.
2. do page fault repeatedly on each process.
3. after 60secs, kill all childredn and exit.
(3 is necessary for getting stable data, this is improvement from previous one.)
#define _GNU_SOURCE
#include <stdio.h>
#include <sched.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
/*
* For avoiding contention in page table lock, FAULT area is
* sparse. If FAULT_LENGTH is too large for your cpus, decrease it.
*/
#define FAULT_LENGTH (2 * 1024 * 1024)
#define PAGE_SIZE 4096
#define MAXNUM (128)
void alarm_handler(int sig)
{
}
void *worker(int cpu, int ppid)
{
void *start, *end;
char *c;
cpu_set_t set;
int i;
CPU_ZERO(&set);
CPU_SET(cpu, &set);
sched_setaffinity(0, sizeof(set), &set);
start = mmap(NULL, FAULT_LENGTH, PROT_READ|PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
if (start == MAP_FAILED) {
perror("mmap");
exit(1);
}
end = start + FAULT_LENGTH;
pause();
//fprintf(stderr, "run%d", cpu);
while (1) {
for (c = (char*)start; (void *)c < end; c += PAGE_SIZE)
*c = 0;
madvise(start, FAULT_LENGTH, MADV_DONTNEED);
}
return NULL;
}
int main(int argc, char *argv[])
{
int num, i, ret, pid, status;
int pids[MAXNUM];
if (argc < 2)
return 0;
setpgid(0, 0);
signal(SIGALRM, alarm_handler);
num = atoi(argv[1]);
pid = getpid();
for (i = 0; i < num; ++i) {
ret = fork();
if (!ret) {
worker(i, pid);
exit(0);
}
pids[i] = ret;
}
sleep(1);
kill(-pid, SIGALRM);
sleep(60);
for (i = 0; i < num; i++)
kill(pids[i], SIGKILL);
for (i = 0; i < num; i++)
waitpid(pids[i], &status, 0);
return 0;
}
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-03-11 06:22:29 +07:00
|
|
|
* percpu counter.
|
2008-02-07 15:14:24 +07:00
|
|
|
*/
|
2012-05-30 05:06:56 +07:00
|
|
|
struct mem_cgroup_stat_cpu __percpu *stat;
|
2010-10-28 05:33:42 +07:00
|
|
|
/*
|
|
|
|
* 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;
|
2011-12-12 04:47:04 +07:00
|
|
|
|
2012-10-09 06:33:10 +07:00
|
|
|
#if defined(CONFIG_MEMCG_KMEM) && defined(CONFIG_INET)
|
2011-12-12 04:47:04 +07:00
|
|
|
struct tcp_memcontrol tcp_mem;
|
|
|
|
#endif
|
2008-02-07 15:13:50 +07:00
|
|
|
};
|
|
|
|
|
2010-03-11 06:22:13 +07:00
|
|
|
/* Stuffs for move charges at task migration. */
|
|
|
|
/*
|
|
|
|
* Types of charges to be moved. "move_charge_at_immitgrate" is treated as a
|
|
|
|
* left-shifted bitmap of these types.
|
|
|
|
*/
|
|
|
|
enum move_type {
|
2010-03-11 06:22:14 +07:00
|
|
|
MOVE_CHARGE_TYPE_ANON, /* private anonymous page and swap of it */
|
2010-05-27 04:42:39 +07:00
|
|
|
MOVE_CHARGE_TYPE_FILE, /* file page(including tmpfs) and swap of it */
|
2010-03-11 06:22:13 +07:00
|
|
|
NR_MOVE_TYPE,
|
|
|
|
};
|
|
|
|
|
2010-03-11 06:22:14 +07:00
|
|
|
/* "mc" and its members are protected by cgroup_mutex */
|
|
|
|
static struct move_charge_struct {
|
2010-11-25 03:57:06 +07:00
|
|
|
spinlock_t lock; /* for from, to */
|
2010-03-11 06:22:14 +07:00
|
|
|
struct mem_cgroup *from;
|
|
|
|
struct mem_cgroup *to;
|
|
|
|
unsigned long precharge;
|
2010-03-11 06:22:15 +07:00
|
|
|
unsigned long moved_charge;
|
2010-03-11 06:22:18 +07:00
|
|
|
unsigned long moved_swap;
|
2010-03-11 06:22:16 +07:00
|
|
|
struct task_struct *moving_task; /* a task moving charges */
|
|
|
|
wait_queue_head_t waitq; /* a waitq for other context */
|
|
|
|
} mc = {
|
2010-08-11 08:02:58 +07:00
|
|
|
.lock = __SPIN_LOCK_UNLOCKED(mc.lock),
|
2010-03-11 06:22:16 +07:00
|
|
|
.waitq = __WAIT_QUEUE_HEAD_INITIALIZER(mc.waitq),
|
|
|
|
};
|
2010-03-11 06:22:14 +07:00
|
|
|
|
2010-05-27 04:42:38 +07:00
|
|
|
static bool move_anon(void)
|
|
|
|
{
|
|
|
|
return test_bit(MOVE_CHARGE_TYPE_ANON,
|
|
|
|
&mc.to->move_charge_at_immigrate);
|
|
|
|
}
|
|
|
|
|
2010-05-27 04:42:39 +07:00
|
|
|
static bool move_file(void)
|
|
|
|
{
|
|
|
|
return test_bit(MOVE_CHARGE_TYPE_FILE,
|
|
|
|
&mc.to->move_charge_at_immigrate);
|
|
|
|
}
|
|
|
|
|
2009-09-24 05:56:39 +07:00
|
|
|
/*
|
|
|
|
* Maximum loops in mem_cgroup_hierarchical_reclaim(), used for soft
|
|
|
|
* limit reclaim to prevent infinite loops, if they ever occur.
|
|
|
|
*/
|
2012-05-30 05:06:56 +07:00
|
|
|
#define MEM_CGROUP_MAX_RECLAIM_LOOPS 100
|
|
|
|
#define MEM_CGROUP_MAX_SOFT_LIMIT_RECLAIM_LOOPS 2
|
2009-09-24 05:56:39 +07:00
|
|
|
|
2008-02-07 15:14:17 +07:00
|
|
|
enum charge_type {
|
|
|
|
MEM_CGROUP_CHARGE_TYPE_CACHE = 0,
|
2012-08-01 06:41:40 +07:00
|
|
|
MEM_CGROUP_CHARGE_TYPE_ANON,
|
2009-01-08 09:07:56 +07:00
|
|
|
MEM_CGROUP_CHARGE_TYPE_SWAPOUT, /* for accounting swapcache */
|
2009-06-18 06:27:17 +07:00
|
|
|
MEM_CGROUP_CHARGE_TYPE_DROP, /* a page was unused swap cache */
|
2008-10-19 10:28:11 +07:00
|
|
|
NR_CHARGE_TYPE,
|
|
|
|
};
|
|
|
|
|
2009-01-08 09:08:00 +07:00
|
|
|
/* for encoding cft->private value on file */
|
2011-12-22 08:02:27 +07:00
|
|
|
#define _MEM (0)
|
|
|
|
#define _MEMSWAP (1)
|
|
|
|
#define _OOM_TYPE (2)
|
2012-05-30 05:06:56 +07:00
|
|
|
#define MEMFILE_PRIVATE(x, val) ((x) << 16 | (val))
|
|
|
|
#define MEMFILE_TYPE(val) ((val) >> 16 & 0xffff)
|
2009-01-08 09:08:00 +07:00
|
|
|
#define MEMFILE_ATTR(val) ((val) & 0xffff)
|
2010-05-27 04:42:36 +07:00
|
|
|
/* Used for OOM nofiier */
|
|
|
|
#define OOM_CONTROL (0)
|
2009-01-08 09:08:00 +07:00
|
|
|
|
2009-09-24 05:56:38 +07:00
|
|
|
/*
|
|
|
|
* Reclaim flags for mem_cgroup_hierarchical_reclaim
|
|
|
|
*/
|
|
|
|
#define MEM_CGROUP_RECLAIM_NOSWAP_BIT 0x0
|
|
|
|
#define MEM_CGROUP_RECLAIM_NOSWAP (1 << MEM_CGROUP_RECLAIM_NOSWAP_BIT)
|
|
|
|
#define MEM_CGROUP_RECLAIM_SHRINK_BIT 0x1
|
|
|
|
#define MEM_CGROUP_RECLAIM_SHRINK (1 << MEM_CGROUP_RECLAIM_SHRINK_BIT)
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static void mem_cgroup_get(struct mem_cgroup *memcg);
|
|
|
|
static void mem_cgroup_put(struct mem_cgroup *memcg);
|
2011-12-12 04:47:03 +07:00
|
|
|
|
2012-08-01 06:46:01 +07:00
|
|
|
static inline
|
|
|
|
struct mem_cgroup *mem_cgroup_from_css(struct cgroup_subsys_state *s)
|
|
|
|
{
|
|
|
|
return container_of(s, struct mem_cgroup, css);
|
|
|
|
}
|
|
|
|
|
2012-10-09 06:33:13 +07:00
|
|
|
static inline bool mem_cgroup_is_root(struct mem_cgroup *memcg)
|
|
|
|
{
|
|
|
|
return (memcg == root_mem_cgroup);
|
|
|
|
}
|
|
|
|
|
2011-12-12 04:47:03 +07:00
|
|
|
/* Writing them here to avoid exposing memcg's inner layout */
|
2012-10-09 06:33:10 +07:00
|
|
|
#if defined(CONFIG_INET) && defined(CONFIG_MEMCG_KMEM)
|
2011-12-12 04:47:03 +07:00
|
|
|
|
|
|
|
void sock_update_memcg(struct sock *sk)
|
|
|
|
{
|
2012-01-20 11:57:14 +07:00
|
|
|
if (mem_cgroup_sockets_enabled) {
|
2011-12-12 04:47:03 +07:00
|
|
|
struct mem_cgroup *memcg;
|
memcg: decrement static keys at real destroy time
We call the destroy function when a cgroup starts to be removed, such as
by a rmdir event.
However, because of our reference counters, some objects are still
inflight. Right now, we are decrementing the static_keys at destroy()
time, meaning that if we get rid of the last static_key reference, some
objects will still have charges, but the code to properly uncharge them
won't be run.
This becomes a problem specially if it is ever enabled again, because now
new charges will be added to the staled charges making keeping it pretty
much impossible.
We just need to be careful with the static branch activation: since there
is no particular preferred order of their activation, we need to make sure
that we only start using it after all call sites are active. This is
achieved by having a per-memcg flag that is only updated after
static_key_slow_inc() returns. At this time, we are sure all sites are
active.
This is made per-memcg, not global, for a reason: it also has the effect
of making socket accounting more consistent. The first memcg to be
limited will trigger static_key() activation, therefore, accounting. But
all the others will then be accounted no matter what. After this patch,
only limited memcgs will have its sockets accounted.
[akpm@linux-foundation.org: move enum sock_flag_bits into sock.h,
document enum sock_flag_bits,
convert memcg_proto_active() and memcg_proto_activated() to test_bit(),
redo tcp_update_limit() comment to 80 cols]
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>
Cc: Michal Hocko <mhocko@suse.cz>
Acked-by: David Miller <davem@davemloft.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-05-30 05:07:11 +07:00
|
|
|
struct cg_proto *cg_proto;
|
2011-12-12 04:47:03 +07:00
|
|
|
|
|
|
|
BUG_ON(!sk->sk_prot->proto_cgroup);
|
|
|
|
|
2012-01-06 03:16:39 +07:00
|
|
|
/* Socket cloning can throw us here with sk_cgrp already
|
|
|
|
* filled. It won't however, necessarily happen from
|
|
|
|
* process context. So the test for root memcg given
|
|
|
|
* the current task's memcg won't help us in this case.
|
|
|
|
*
|
|
|
|
* Respecting the original socket's memcg is a better
|
|
|
|
* decision in this case.
|
|
|
|
*/
|
|
|
|
if (sk->sk_cgrp) {
|
|
|
|
BUG_ON(mem_cgroup_is_root(sk->sk_cgrp->memcg));
|
|
|
|
mem_cgroup_get(sk->sk_cgrp->memcg);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-12-12 04:47:03 +07:00
|
|
|
rcu_read_lock();
|
|
|
|
memcg = mem_cgroup_from_task(current);
|
memcg: decrement static keys at real destroy time
We call the destroy function when a cgroup starts to be removed, such as
by a rmdir event.
However, because of our reference counters, some objects are still
inflight. Right now, we are decrementing the static_keys at destroy()
time, meaning that if we get rid of the last static_key reference, some
objects will still have charges, but the code to properly uncharge them
won't be run.
This becomes a problem specially if it is ever enabled again, because now
new charges will be added to the staled charges making keeping it pretty
much impossible.
We just need to be careful with the static branch activation: since there
is no particular preferred order of their activation, we need to make sure
that we only start using it after all call sites are active. This is
achieved by having a per-memcg flag that is only updated after
static_key_slow_inc() returns. At this time, we are sure all sites are
active.
This is made per-memcg, not global, for a reason: it also has the effect
of making socket accounting more consistent. The first memcg to be
limited will trigger static_key() activation, therefore, accounting. But
all the others will then be accounted no matter what. After this patch,
only limited memcgs will have its sockets accounted.
[akpm@linux-foundation.org: move enum sock_flag_bits into sock.h,
document enum sock_flag_bits,
convert memcg_proto_active() and memcg_proto_activated() to test_bit(),
redo tcp_update_limit() comment to 80 cols]
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>
Cc: Michal Hocko <mhocko@suse.cz>
Acked-by: David Miller <davem@davemloft.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-05-30 05:07:11 +07:00
|
|
|
cg_proto = sk->sk_prot->proto_cgroup(memcg);
|
|
|
|
if (!mem_cgroup_is_root(memcg) && memcg_proto_active(cg_proto)) {
|
2011-12-12 04:47:03 +07:00
|
|
|
mem_cgroup_get(memcg);
|
memcg: decrement static keys at real destroy time
We call the destroy function when a cgroup starts to be removed, such as
by a rmdir event.
However, because of our reference counters, some objects are still
inflight. Right now, we are decrementing the static_keys at destroy()
time, meaning that if we get rid of the last static_key reference, some
objects will still have charges, but the code to properly uncharge them
won't be run.
This becomes a problem specially if it is ever enabled again, because now
new charges will be added to the staled charges making keeping it pretty
much impossible.
We just need to be careful with the static branch activation: since there
is no particular preferred order of their activation, we need to make sure
that we only start using it after all call sites are active. This is
achieved by having a per-memcg flag that is only updated after
static_key_slow_inc() returns. At this time, we are sure all sites are
active.
This is made per-memcg, not global, for a reason: it also has the effect
of making socket accounting more consistent. The first memcg to be
limited will trigger static_key() activation, therefore, accounting. But
all the others will then be accounted no matter what. After this patch,
only limited memcgs will have its sockets accounted.
[akpm@linux-foundation.org: move enum sock_flag_bits into sock.h,
document enum sock_flag_bits,
convert memcg_proto_active() and memcg_proto_activated() to test_bit(),
redo tcp_update_limit() comment to 80 cols]
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>
Cc: Michal Hocko <mhocko@suse.cz>
Acked-by: David Miller <davem@davemloft.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-05-30 05:07:11 +07:00
|
|
|
sk->sk_cgrp = cg_proto;
|
2011-12-12 04:47:03 +07:00
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(sock_update_memcg);
|
|
|
|
|
|
|
|
void sock_release_memcg(struct sock *sk)
|
|
|
|
{
|
2012-01-20 11:57:14 +07:00
|
|
|
if (mem_cgroup_sockets_enabled && sk->sk_cgrp) {
|
2011-12-12 04:47:03 +07:00
|
|
|
struct mem_cgroup *memcg;
|
|
|
|
WARN_ON(!sk->sk_cgrp->memcg);
|
|
|
|
memcg = sk->sk_cgrp->memcg;
|
|
|
|
mem_cgroup_put(memcg);
|
|
|
|
}
|
|
|
|
}
|
2011-12-12 04:47:04 +07:00
|
|
|
|
|
|
|
struct cg_proto *tcp_proto_cgroup(struct mem_cgroup *memcg)
|
|
|
|
{
|
|
|
|
if (!memcg || mem_cgroup_is_root(memcg))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return &memcg->tcp_mem.cg_proto;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(tcp_proto_cgroup);
|
2011-12-12 04:47:03 +07:00
|
|
|
|
memcg: decrement static keys at real destroy time
We call the destroy function when a cgroup starts to be removed, such as
by a rmdir event.
However, because of our reference counters, some objects are still
inflight. Right now, we are decrementing the static_keys at destroy()
time, meaning that if we get rid of the last static_key reference, some
objects will still have charges, but the code to properly uncharge them
won't be run.
This becomes a problem specially if it is ever enabled again, because now
new charges will be added to the staled charges making keeping it pretty
much impossible.
We just need to be careful with the static branch activation: since there
is no particular preferred order of their activation, we need to make sure
that we only start using it after all call sites are active. This is
achieved by having a per-memcg flag that is only updated after
static_key_slow_inc() returns. At this time, we are sure all sites are
active.
This is made per-memcg, not global, for a reason: it also has the effect
of making socket accounting more consistent. The first memcg to be
limited will trigger static_key() activation, therefore, accounting. But
all the others will then be accounted no matter what. After this patch,
only limited memcgs will have its sockets accounted.
[akpm@linux-foundation.org: move enum sock_flag_bits into sock.h,
document enum sock_flag_bits,
convert memcg_proto_active() and memcg_proto_activated() to test_bit(),
redo tcp_update_limit() comment to 80 cols]
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>
Cc: Michal Hocko <mhocko@suse.cz>
Acked-by: David Miller <davem@davemloft.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-05-30 05:07:11 +07:00
|
|
|
static void disarm_sock_keys(struct mem_cgroup *memcg)
|
|
|
|
{
|
|
|
|
if (!memcg_proto_activated(&memcg->tcp_mem.cg_proto))
|
|
|
|
return;
|
|
|
|
static_key_slow_dec(&memcg_socket_limit_enabled);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static void disarm_sock_keys(struct mem_cgroup *memcg)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static void drain_all_stock_async(struct mem_cgroup *memcg);
|
2009-01-08 09:08:00 +07:00
|
|
|
|
2009-09-24 05:56:37 +07:00
|
|
|
static struct mem_cgroup_per_zone *
|
2011-11-03 03:38:15 +07:00
|
|
|
mem_cgroup_zoneinfo(struct mem_cgroup *memcg, int nid, int zid)
|
2009-09-24 05:56:37 +07:00
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
return &memcg->info.nodeinfo[nid]->zoneinfo[zid];
|
2009-09-24 05:56:37 +07:00
|
|
|
}
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
struct cgroup_subsys_state *mem_cgroup_css(struct mem_cgroup *memcg)
|
2009-12-16 18:19:59 +07:00
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
return &memcg->css;
|
2009-12-16 18:19:59 +07:00
|
|
|
}
|
|
|
|
|
2009-09-24 05:56:37 +07:00
|
|
|
static struct mem_cgroup_per_zone *
|
2011-11-03 03:38:15 +07:00
|
|
|
page_cgroup_zoneinfo(struct mem_cgroup *memcg, struct page *page)
|
2009-09-24 05:56:37 +07:00
|
|
|
{
|
2011-03-24 06:42:27 +07:00
|
|
|
int nid = page_to_nid(page);
|
|
|
|
int zid = page_zonenum(page);
|
2009-09-24 05:56:37 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
return mem_cgroup_zoneinfo(memcg, nid, zid);
|
2009-09-24 05:56:37 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct mem_cgroup_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_tree_per_zone *
|
|
|
|
soft_limit_tree_from_page(struct page *page)
|
|
|
|
{
|
|
|
|
int nid = page_to_nid(page);
|
|
|
|
int zid = page_zonenum(page);
|
|
|
|
|
|
|
|
return &soft_limit_tree.rb_tree_per_node[nid]->rb_tree_per_zone[zid];
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2011-11-03 03:38:15 +07:00
|
|
|
__mem_cgroup_insert_exceeded(struct mem_cgroup *memcg,
|
2009-09-24 05:56:37 +07:00
|
|
|
struct mem_cgroup_per_zone *mz,
|
2009-10-02 05:44:12 +07:00
|
|
|
struct mem_cgroup_tree_per_zone *mctz,
|
|
|
|
unsigned long long new_usage_in_excess)
|
2009-09-24 05:56:37 +07:00
|
|
|
{
|
|
|
|
struct rb_node **p = &mctz->rb_root.rb_node;
|
|
|
|
struct rb_node *parent = NULL;
|
|
|
|
struct mem_cgroup_per_zone *mz_node;
|
|
|
|
|
|
|
|
if (mz->on_tree)
|
|
|
|
return;
|
|
|
|
|
2009-10-02 05:44:12 +07:00
|
|
|
mz->usage_in_excess = new_usage_in_excess;
|
|
|
|
if (!mz->usage_in_excess)
|
|
|
|
return;
|
2009-09-24 05:56:37 +07:00
|
|
|
while (*p) {
|
|
|
|
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, &mctz->rb_root);
|
|
|
|
mz->on_tree = true;
|
2009-09-24 05:56:39 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2011-11-03 03:38:15 +07:00
|
|
|
__mem_cgroup_remove_exceeded(struct mem_cgroup *memcg,
|
2009-09-24 05:56:39 +07:00
|
|
|
struct mem_cgroup_per_zone *mz,
|
|
|
|
struct mem_cgroup_tree_per_zone *mctz)
|
|
|
|
{
|
|
|
|
if (!mz->on_tree)
|
|
|
|
return;
|
|
|
|
rb_erase(&mz->tree_node, &mctz->rb_root);
|
|
|
|
mz->on_tree = false;
|
|
|
|
}
|
|
|
|
|
2009-09-24 05:56:37 +07:00
|
|
|
static void
|
2011-11-03 03:38:15 +07:00
|
|
|
mem_cgroup_remove_exceeded(struct mem_cgroup *memcg,
|
2009-09-24 05:56:37 +07:00
|
|
|
struct mem_cgroup_per_zone *mz,
|
|
|
|
struct mem_cgroup_tree_per_zone *mctz)
|
|
|
|
{
|
|
|
|
spin_lock(&mctz->lock);
|
2011-11-03 03:38:15 +07:00
|
|
|
__mem_cgroup_remove_exceeded(memcg, mz, mctz);
|
2009-09-24 05:56:37 +07:00
|
|
|
spin_unlock(&mctz->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static void mem_cgroup_update_tree(struct mem_cgroup *memcg, struct page *page)
|
2009-09-24 05:56:37 +07:00
|
|
|
{
|
2009-10-02 05:44:12 +07:00
|
|
|
unsigned long long excess;
|
2009-09-24 05:56:37 +07:00
|
|
|
struct mem_cgroup_per_zone *mz;
|
|
|
|
struct mem_cgroup_tree_per_zone *mctz;
|
2009-10-02 05:44:11 +07:00
|
|
|
int nid = page_to_nid(page);
|
|
|
|
int zid = page_zonenum(page);
|
2009-09-24 05:56:37 +07:00
|
|
|
mctz = soft_limit_tree_from_page(page);
|
|
|
|
|
|
|
|
/*
|
2009-10-02 05:44:11 +07:00
|
|
|
* Necessary to update all ancestors when hierarchy is used.
|
|
|
|
* because their event counter is not touched.
|
2009-09-24 05:56:37 +07:00
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
for (; memcg; memcg = parent_mem_cgroup(memcg)) {
|
|
|
|
mz = mem_cgroup_zoneinfo(memcg, nid, zid);
|
|
|
|
excess = res_counter_soft_limit_excess(&memcg->res);
|
2009-10-02 05:44:11 +07:00
|
|
|
/*
|
|
|
|
* We have to update the tree if mz is on RB-tree or
|
|
|
|
* mem is over its softlimit.
|
|
|
|
*/
|
2009-10-02 05:44:12 +07:00
|
|
|
if (excess || mz->on_tree) {
|
2009-10-02 05:44:11 +07:00
|
|
|
spin_lock(&mctz->lock);
|
|
|
|
/* if on-tree, remove it */
|
|
|
|
if (mz->on_tree)
|
2011-11-03 03:38:15 +07:00
|
|
|
__mem_cgroup_remove_exceeded(memcg, mz, mctz);
|
2009-10-02 05:44:11 +07:00
|
|
|
/*
|
2009-10-02 05:44:12 +07:00
|
|
|
* Insert again. mz->usage_in_excess will be updated.
|
|
|
|
* If excess is 0, no tree ops.
|
2009-10-02 05:44:11 +07:00
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
__mem_cgroup_insert_exceeded(memcg, mz, mctz, excess);
|
2009-10-02 05:44:11 +07:00
|
|
|
spin_unlock(&mctz->lock);
|
|
|
|
}
|
2009-09-24 05:56:37 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static void mem_cgroup_remove_from_trees(struct mem_cgroup *memcg)
|
2009-09-24 05:56:37 +07:00
|
|
|
{
|
|
|
|
int node, zone;
|
|
|
|
struct mem_cgroup_per_zone *mz;
|
|
|
|
struct mem_cgroup_tree_per_zone *mctz;
|
|
|
|
|
2012-01-13 08:19:04 +07:00
|
|
|
for_each_node(node) {
|
2009-09-24 05:56:37 +07:00
|
|
|
for (zone = 0; zone < MAX_NR_ZONES; zone++) {
|
2011-11-03 03:38:15 +07:00
|
|
|
mz = mem_cgroup_zoneinfo(memcg, node, zone);
|
2009-09-24 05:56:37 +07:00
|
|
|
mctz = soft_limit_tree_node_zone(node, zone);
|
2011-11-03 03:38:15 +07:00
|
|
|
mem_cgroup_remove_exceeded(memcg, mz, mctz);
|
2009-09-24 05:56:37 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-09-24 05:56:39 +07:00
|
|
|
static struct mem_cgroup_per_zone *
|
|
|
|
__mem_cgroup_largest_soft_limit_node(struct mem_cgroup_tree_per_zone *mctz)
|
|
|
|
{
|
|
|
|
struct rb_node *rightmost = NULL;
|
2009-10-02 05:44:08 +07:00
|
|
|
struct mem_cgroup_per_zone *mz;
|
2009-09-24 05:56:39 +07:00
|
|
|
|
|
|
|
retry:
|
2009-10-02 05:44:08 +07:00
|
|
|
mz = NULL;
|
2009-09-24 05:56:39 +07:00
|
|
|
rightmost = rb_last(&mctz->rb_root);
|
|
|
|
if (!rightmost)
|
|
|
|
goto done; /* Nothing to reclaim from */
|
|
|
|
|
|
|
|
mz = rb_entry(rightmost, struct mem_cgroup_per_zone, tree_node);
|
|
|
|
/*
|
|
|
|
* Remove the node now but someone else can add it back,
|
|
|
|
* we will to add it back at the end of reclaim to its correct
|
|
|
|
* position in the tree.
|
|
|
|
*/
|
2012-03-22 06:34:18 +07:00
|
|
|
__mem_cgroup_remove_exceeded(mz->memcg, mz, mctz);
|
|
|
|
if (!res_counter_soft_limit_excess(&mz->memcg->res) ||
|
|
|
|
!css_tryget(&mz->memcg->css))
|
2009-09-24 05:56:39 +07:00
|
|
|
goto retry;
|
|
|
|
done:
|
|
|
|
return mz;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct mem_cgroup_per_zone *
|
|
|
|
mem_cgroup_largest_soft_limit_node(struct mem_cgroup_tree_per_zone *mctz)
|
|
|
|
{
|
|
|
|
struct mem_cgroup_per_zone *mz;
|
|
|
|
|
|
|
|
spin_lock(&mctz->lock);
|
|
|
|
mz = __mem_cgroup_largest_soft_limit_node(mctz);
|
|
|
|
spin_unlock(&mctz->lock);
|
|
|
|
return mz;
|
|
|
|
}
|
|
|
|
|
2010-10-28 05:33:42 +07:00
|
|
|
/*
|
|
|
|
* Implementation Note: reading percpu statistics for memcg.
|
|
|
|
*
|
|
|
|
* Both of vmstat[] and percpu_counter has threshold and do periodic
|
|
|
|
* synchronization to implement "quick" read. There are trade-off between
|
|
|
|
* reading cost and precision of value. Then, we may have a chance to implement
|
|
|
|
* a periodic synchronizion of counter in memcg's counter.
|
|
|
|
*
|
|
|
|
* But this _read() function is used for user interface now. The user accounts
|
|
|
|
* memory usage by memory cgroup and he _always_ requires exact value because
|
|
|
|
* he accounts memory. Even if we provide quick-and-fuzzy read, we always
|
|
|
|
* have to visit all online cpus and make sum. So, for now, unnecessary
|
|
|
|
* synchronization is not implemented. (just implemented for cpu hotplug)
|
|
|
|
*
|
|
|
|
* If there are kernel internal actions which can make use of some not-exact
|
|
|
|
* value, and reading all cpu value can be performance bottleneck in some
|
|
|
|
* common workload, threashold and synchonization as vmstat[] should be
|
|
|
|
* implemented.
|
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
static long mem_cgroup_read_stat(struct mem_cgroup *memcg,
|
2011-03-24 06:42:38 +07:00
|
|
|
enum mem_cgroup_stat_index idx)
|
memcg: use generic percpu instead of private implementation
When per-cpu counter for memcg was implemneted, dynamic percpu allocator
was not very good. But now, we have good one and useful macros. This
patch replaces memcg's private percpu counter implementation with generic
dynamic percpu allocator.
The benefits are
- We can remove private implementation.
- The counters will be NUMA-aware. (Current one is not...)
- This patch makes sizeof struct mem_cgroup smaller. Then,
struct mem_cgroup may be fit in page size on small config.
- About basic performance aspects, see below.
[Before]
# size mm/memcontrol.o
text data bss dec hex filename
24373 2528 4132 31033 7939 mm/memcontrol.o
[page-fault-throuput test on 8cpu/SMP in root cgroup]
# /root/bin/perf stat -a -e page-faults,cache-misses --repeat 5 ./multi-fault-fork 8
Performance counter stats for './multi-fault-fork 8' (5 runs):
45878618 page-faults ( +- 0.110% )
602635826 cache-misses ( +- 0.105% )
61.005373262 seconds time elapsed ( +- 0.004% )
Then cache-miss/page fault = 13.14
[After]
#size mm/memcontrol.o
text data bss dec hex filename
23913 2528 4132 30573 776d mm/memcontrol.o
# /root/bin/perf stat -a -e page-faults,cache-misses --repeat 5 ./multi-fault-fork 8
Performance counter stats for './multi-fault-fork 8' (5 runs):
48179400 page-faults ( +- 0.271% )
588628407 cache-misses ( +- 0.136% )
61.004615021 seconds time elapsed ( +- 0.004% )
Then cache-miss/page fault = 12.22
Text size is reduced.
This performance improvement is not big and will be invisible in real world
applications. But this result shows this patch has some good effect even
on (small) SMP.
Here is a test program I used.
1. fork() processes on each cpus.
2. do page fault repeatedly on each process.
3. after 60secs, kill all childredn and exit.
(3 is necessary for getting stable data, this is improvement from previous one.)
#define _GNU_SOURCE
#include <stdio.h>
#include <sched.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
/*
* For avoiding contention in page table lock, FAULT area is
* sparse. If FAULT_LENGTH is too large for your cpus, decrease it.
*/
#define FAULT_LENGTH (2 * 1024 * 1024)
#define PAGE_SIZE 4096
#define MAXNUM (128)
void alarm_handler(int sig)
{
}
void *worker(int cpu, int ppid)
{
void *start, *end;
char *c;
cpu_set_t set;
int i;
CPU_ZERO(&set);
CPU_SET(cpu, &set);
sched_setaffinity(0, sizeof(set), &set);
start = mmap(NULL, FAULT_LENGTH, PROT_READ|PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
if (start == MAP_FAILED) {
perror("mmap");
exit(1);
}
end = start + FAULT_LENGTH;
pause();
//fprintf(stderr, "run%d", cpu);
while (1) {
for (c = (char*)start; (void *)c < end; c += PAGE_SIZE)
*c = 0;
madvise(start, FAULT_LENGTH, MADV_DONTNEED);
}
return NULL;
}
int main(int argc, char *argv[])
{
int num, i, ret, pid, status;
int pids[MAXNUM];
if (argc < 2)
return 0;
setpgid(0, 0);
signal(SIGALRM, alarm_handler);
num = atoi(argv[1]);
pid = getpid();
for (i = 0; i < num; ++i) {
ret = fork();
if (!ret) {
worker(i, pid);
exit(0);
}
pids[i] = ret;
}
sleep(1);
kill(-pid, SIGALRM);
sleep(60);
for (i = 0; i < num; i++)
kill(pids[i], SIGKILL);
for (i = 0; i < num; i++)
waitpid(pids[i], &status, 0);
return 0;
}
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-03-11 06:22:29 +07:00
|
|
|
{
|
2011-03-24 06:42:38 +07:00
|
|
|
long val = 0;
|
memcg: use generic percpu instead of private implementation
When per-cpu counter for memcg was implemneted, dynamic percpu allocator
was not very good. But now, we have good one and useful macros. This
patch replaces memcg's private percpu counter implementation with generic
dynamic percpu allocator.
The benefits are
- We can remove private implementation.
- The counters will be NUMA-aware. (Current one is not...)
- This patch makes sizeof struct mem_cgroup smaller. Then,
struct mem_cgroup may be fit in page size on small config.
- About basic performance aspects, see below.
[Before]
# size mm/memcontrol.o
text data bss dec hex filename
24373 2528 4132 31033 7939 mm/memcontrol.o
[page-fault-throuput test on 8cpu/SMP in root cgroup]
# /root/bin/perf stat -a -e page-faults,cache-misses --repeat 5 ./multi-fault-fork 8
Performance counter stats for './multi-fault-fork 8' (5 runs):
45878618 page-faults ( +- 0.110% )
602635826 cache-misses ( +- 0.105% )
61.005373262 seconds time elapsed ( +- 0.004% )
Then cache-miss/page fault = 13.14
[After]
#size mm/memcontrol.o
text data bss dec hex filename
23913 2528 4132 30573 776d mm/memcontrol.o
# /root/bin/perf stat -a -e page-faults,cache-misses --repeat 5 ./multi-fault-fork 8
Performance counter stats for './multi-fault-fork 8' (5 runs):
48179400 page-faults ( +- 0.271% )
588628407 cache-misses ( +- 0.136% )
61.004615021 seconds time elapsed ( +- 0.004% )
Then cache-miss/page fault = 12.22
Text size is reduced.
This performance improvement is not big and will be invisible in real world
applications. But this result shows this patch has some good effect even
on (small) SMP.
Here is a test program I used.
1. fork() processes on each cpus.
2. do page fault repeatedly on each process.
3. after 60secs, kill all childredn and exit.
(3 is necessary for getting stable data, this is improvement from previous one.)
#define _GNU_SOURCE
#include <stdio.h>
#include <sched.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
/*
* For avoiding contention in page table lock, FAULT area is
* sparse. If FAULT_LENGTH is too large for your cpus, decrease it.
*/
#define FAULT_LENGTH (2 * 1024 * 1024)
#define PAGE_SIZE 4096
#define MAXNUM (128)
void alarm_handler(int sig)
{
}
void *worker(int cpu, int ppid)
{
void *start, *end;
char *c;
cpu_set_t set;
int i;
CPU_ZERO(&set);
CPU_SET(cpu, &set);
sched_setaffinity(0, sizeof(set), &set);
start = mmap(NULL, FAULT_LENGTH, PROT_READ|PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
if (start == MAP_FAILED) {
perror("mmap");
exit(1);
}
end = start + FAULT_LENGTH;
pause();
//fprintf(stderr, "run%d", cpu);
while (1) {
for (c = (char*)start; (void *)c < end; c += PAGE_SIZE)
*c = 0;
madvise(start, FAULT_LENGTH, MADV_DONTNEED);
}
return NULL;
}
int main(int argc, char *argv[])
{
int num, i, ret, pid, status;
int pids[MAXNUM];
if (argc < 2)
return 0;
setpgid(0, 0);
signal(SIGALRM, alarm_handler);
num = atoi(argv[1]);
pid = getpid();
for (i = 0; i < num; ++i) {
ret = fork();
if (!ret) {
worker(i, pid);
exit(0);
}
pids[i] = ret;
}
sleep(1);
kill(-pid, SIGALRM);
sleep(60);
for (i = 0; i < num; i++)
kill(pids[i], SIGKILL);
for (i = 0; i < num; i++)
waitpid(pids[i], &status, 0);
return 0;
}
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-03-11 06:22:29 +07:00
|
|
|
int cpu;
|
|
|
|
|
2010-10-28 05:33:42 +07:00
|
|
|
get_online_cpus();
|
|
|
|
for_each_online_cpu(cpu)
|
2011-11-03 03:38:15 +07:00
|
|
|
val += per_cpu(memcg->stat->count[idx], cpu);
|
2010-10-28 05:33:42 +07:00
|
|
|
#ifdef CONFIG_HOTPLUG_CPU
|
2011-11-03 03:38:15 +07:00
|
|
|
spin_lock(&memcg->pcp_counter_lock);
|
|
|
|
val += memcg->nocpu_base.count[idx];
|
|
|
|
spin_unlock(&memcg->pcp_counter_lock);
|
2010-10-28 05:33:42 +07:00
|
|
|
#endif
|
|
|
|
put_online_cpus();
|
memcg: use generic percpu instead of private implementation
When per-cpu counter for memcg was implemneted, dynamic percpu allocator
was not very good. But now, we have good one and useful macros. This
patch replaces memcg's private percpu counter implementation with generic
dynamic percpu allocator.
The benefits are
- We can remove private implementation.
- The counters will be NUMA-aware. (Current one is not...)
- This patch makes sizeof struct mem_cgroup smaller. Then,
struct mem_cgroup may be fit in page size on small config.
- About basic performance aspects, see below.
[Before]
# size mm/memcontrol.o
text data bss dec hex filename
24373 2528 4132 31033 7939 mm/memcontrol.o
[page-fault-throuput test on 8cpu/SMP in root cgroup]
# /root/bin/perf stat -a -e page-faults,cache-misses --repeat 5 ./multi-fault-fork 8
Performance counter stats for './multi-fault-fork 8' (5 runs):
45878618 page-faults ( +- 0.110% )
602635826 cache-misses ( +- 0.105% )
61.005373262 seconds time elapsed ( +- 0.004% )
Then cache-miss/page fault = 13.14
[After]
#size mm/memcontrol.o
text data bss dec hex filename
23913 2528 4132 30573 776d mm/memcontrol.o
# /root/bin/perf stat -a -e page-faults,cache-misses --repeat 5 ./multi-fault-fork 8
Performance counter stats for './multi-fault-fork 8' (5 runs):
48179400 page-faults ( +- 0.271% )
588628407 cache-misses ( +- 0.136% )
61.004615021 seconds time elapsed ( +- 0.004% )
Then cache-miss/page fault = 12.22
Text size is reduced.
This performance improvement is not big and will be invisible in real world
applications. But this result shows this patch has some good effect even
on (small) SMP.
Here is a test program I used.
1. fork() processes on each cpus.
2. do page fault repeatedly on each process.
3. after 60secs, kill all childredn and exit.
(3 is necessary for getting stable data, this is improvement from previous one.)
#define _GNU_SOURCE
#include <stdio.h>
#include <sched.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
/*
* For avoiding contention in page table lock, FAULT area is
* sparse. If FAULT_LENGTH is too large for your cpus, decrease it.
*/
#define FAULT_LENGTH (2 * 1024 * 1024)
#define PAGE_SIZE 4096
#define MAXNUM (128)
void alarm_handler(int sig)
{
}
void *worker(int cpu, int ppid)
{
void *start, *end;
char *c;
cpu_set_t set;
int i;
CPU_ZERO(&set);
CPU_SET(cpu, &set);
sched_setaffinity(0, sizeof(set), &set);
start = mmap(NULL, FAULT_LENGTH, PROT_READ|PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
if (start == MAP_FAILED) {
perror("mmap");
exit(1);
}
end = start + FAULT_LENGTH;
pause();
//fprintf(stderr, "run%d", cpu);
while (1) {
for (c = (char*)start; (void *)c < end; c += PAGE_SIZE)
*c = 0;
madvise(start, FAULT_LENGTH, MADV_DONTNEED);
}
return NULL;
}
int main(int argc, char *argv[])
{
int num, i, ret, pid, status;
int pids[MAXNUM];
if (argc < 2)
return 0;
setpgid(0, 0);
signal(SIGALRM, alarm_handler);
num = atoi(argv[1]);
pid = getpid();
for (i = 0; i < num; ++i) {
ret = fork();
if (!ret) {
worker(i, pid);
exit(0);
}
pids[i] = ret;
}
sleep(1);
kill(-pid, SIGALRM);
sleep(60);
for (i = 0; i < num; i++)
kill(pids[i], SIGKILL);
for (i = 0; i < num; i++)
waitpid(pids[i], &status, 0);
return 0;
}
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-03-11 06:22:29 +07:00
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static void mem_cgroup_swap_statistics(struct mem_cgroup *memcg,
|
2009-09-24 05:56:42 +07:00
|
|
|
bool charge)
|
|
|
|
{
|
|
|
|
int val = (charge) ? 1 : -1;
|
2012-08-01 06:41:38 +07:00
|
|
|
this_cpu_add(memcg->stat->count[MEM_CGROUP_STAT_SWAP], val);
|
2009-09-24 05:56:42 +07:00
|
|
|
}
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static unsigned long mem_cgroup_read_events(struct mem_cgroup *memcg,
|
2011-03-24 06:42:37 +07:00
|
|
|
enum mem_cgroup_events_index idx)
|
|
|
|
{
|
|
|
|
unsigned long val = 0;
|
|
|
|
int cpu;
|
|
|
|
|
|
|
|
for_each_online_cpu(cpu)
|
2011-11-03 03:38:15 +07:00
|
|
|
val += per_cpu(memcg->stat->events[idx], cpu);
|
2011-03-24 06:42:37 +07:00
|
|
|
#ifdef CONFIG_HOTPLUG_CPU
|
2011-11-03 03:38:15 +07:00
|
|
|
spin_lock(&memcg->pcp_counter_lock);
|
|
|
|
val += memcg->nocpu_base.events[idx];
|
|
|
|
spin_unlock(&memcg->pcp_counter_lock);
|
2011-03-24 06:42:37 +07:00
|
|
|
#endif
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static void mem_cgroup_charge_statistics(struct mem_cgroup *memcg,
|
2012-03-22 06:34:22 +07:00
|
|
|
bool anon, int nr_pages)
|
2008-02-07 15:14:24 +07:00
|
|
|
{
|
memcg: use generic percpu instead of private implementation
When per-cpu counter for memcg was implemneted, dynamic percpu allocator
was not very good. But now, we have good one and useful macros. This
patch replaces memcg's private percpu counter implementation with generic
dynamic percpu allocator.
The benefits are
- We can remove private implementation.
- The counters will be NUMA-aware. (Current one is not...)
- This patch makes sizeof struct mem_cgroup smaller. Then,
struct mem_cgroup may be fit in page size on small config.
- About basic performance aspects, see below.
[Before]
# size mm/memcontrol.o
text data bss dec hex filename
24373 2528 4132 31033 7939 mm/memcontrol.o
[page-fault-throuput test on 8cpu/SMP in root cgroup]
# /root/bin/perf stat -a -e page-faults,cache-misses --repeat 5 ./multi-fault-fork 8
Performance counter stats for './multi-fault-fork 8' (5 runs):
45878618 page-faults ( +- 0.110% )
602635826 cache-misses ( +- 0.105% )
61.005373262 seconds time elapsed ( +- 0.004% )
Then cache-miss/page fault = 13.14
[After]
#size mm/memcontrol.o
text data bss dec hex filename
23913 2528 4132 30573 776d mm/memcontrol.o
# /root/bin/perf stat -a -e page-faults,cache-misses --repeat 5 ./multi-fault-fork 8
Performance counter stats for './multi-fault-fork 8' (5 runs):
48179400 page-faults ( +- 0.271% )
588628407 cache-misses ( +- 0.136% )
61.004615021 seconds time elapsed ( +- 0.004% )
Then cache-miss/page fault = 12.22
Text size is reduced.
This performance improvement is not big and will be invisible in real world
applications. But this result shows this patch has some good effect even
on (small) SMP.
Here is a test program I used.
1. fork() processes on each cpus.
2. do page fault repeatedly on each process.
3. after 60secs, kill all childredn and exit.
(3 is necessary for getting stable data, this is improvement from previous one.)
#define _GNU_SOURCE
#include <stdio.h>
#include <sched.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
/*
* For avoiding contention in page table lock, FAULT area is
* sparse. If FAULT_LENGTH is too large for your cpus, decrease it.
*/
#define FAULT_LENGTH (2 * 1024 * 1024)
#define PAGE_SIZE 4096
#define MAXNUM (128)
void alarm_handler(int sig)
{
}
void *worker(int cpu, int ppid)
{
void *start, *end;
char *c;
cpu_set_t set;
int i;
CPU_ZERO(&set);
CPU_SET(cpu, &set);
sched_setaffinity(0, sizeof(set), &set);
start = mmap(NULL, FAULT_LENGTH, PROT_READ|PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
if (start == MAP_FAILED) {
perror("mmap");
exit(1);
}
end = start + FAULT_LENGTH;
pause();
//fprintf(stderr, "run%d", cpu);
while (1) {
for (c = (char*)start; (void *)c < end; c += PAGE_SIZE)
*c = 0;
madvise(start, FAULT_LENGTH, MADV_DONTNEED);
}
return NULL;
}
int main(int argc, char *argv[])
{
int num, i, ret, pid, status;
int pids[MAXNUM];
if (argc < 2)
return 0;
setpgid(0, 0);
signal(SIGALRM, alarm_handler);
num = atoi(argv[1]);
pid = getpid();
for (i = 0; i < num; ++i) {
ret = fork();
if (!ret) {
worker(i, pid);
exit(0);
}
pids[i] = ret;
}
sleep(1);
kill(-pid, SIGALRM);
sleep(60);
for (i = 0; i < num; i++)
kill(pids[i], SIGKILL);
for (i = 0; i < num; i++)
waitpid(pids[i], &status, 0);
return 0;
}
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-03-11 06:22:29 +07:00
|
|
|
preempt_disable();
|
|
|
|
|
2012-03-22 06:34:22 +07:00
|
|
|
/*
|
|
|
|
* Here, RSS means 'mapped anon' and anon's SwapCache. Shmem/tmpfs is
|
|
|
|
* counted as CACHE even if it's on ANON LRU.
|
|
|
|
*/
|
|
|
|
if (anon)
|
|
|
|
__this_cpu_add(memcg->stat->count[MEM_CGROUP_STAT_RSS],
|
2011-11-03 03:38:15 +07:00
|
|
|
nr_pages);
|
2008-02-07 15:14:24 +07:00
|
|
|
else
|
2012-03-22 06:34:22 +07:00
|
|
|
__this_cpu_add(memcg->stat->count[MEM_CGROUP_STAT_CACHE],
|
2011-11-03 03:38:15 +07:00
|
|
|
nr_pages);
|
2008-05-01 18:35:12 +07:00
|
|
|
|
2011-01-21 05:44:23 +07:00
|
|
|
/* pagein of a big page is an event. So, ignore page size */
|
|
|
|
if (nr_pages > 0)
|
2011-11-03 03:38:15 +07:00
|
|
|
__this_cpu_inc(memcg->stat->events[MEM_CGROUP_EVENTS_PGPGIN]);
|
2011-02-02 06:52:45 +07:00
|
|
|
else {
|
2011-11-03 03:38:15 +07:00
|
|
|
__this_cpu_inc(memcg->stat->events[MEM_CGROUP_EVENTS_PGPGOUT]);
|
2011-02-02 06:52:45 +07:00
|
|
|
nr_pages = -nr_pages; /* for event */
|
|
|
|
}
|
2011-01-21 05:44:23 +07:00
|
|
|
|
2012-05-30 05:07:07 +07:00
|
|
|
__this_cpu_add(memcg->stat->nr_page_events, nr_pages);
|
2010-03-11 06:22:24 +07:00
|
|
|
|
memcg: use generic percpu instead of private implementation
When per-cpu counter for memcg was implemneted, dynamic percpu allocator
was not very good. But now, we have good one and useful macros. This
patch replaces memcg's private percpu counter implementation with generic
dynamic percpu allocator.
The benefits are
- We can remove private implementation.
- The counters will be NUMA-aware. (Current one is not...)
- This patch makes sizeof struct mem_cgroup smaller. Then,
struct mem_cgroup may be fit in page size on small config.
- About basic performance aspects, see below.
[Before]
# size mm/memcontrol.o
text data bss dec hex filename
24373 2528 4132 31033 7939 mm/memcontrol.o
[page-fault-throuput test on 8cpu/SMP in root cgroup]
# /root/bin/perf stat -a -e page-faults,cache-misses --repeat 5 ./multi-fault-fork 8
Performance counter stats for './multi-fault-fork 8' (5 runs):
45878618 page-faults ( +- 0.110% )
602635826 cache-misses ( +- 0.105% )
61.005373262 seconds time elapsed ( +- 0.004% )
Then cache-miss/page fault = 13.14
[After]
#size mm/memcontrol.o
text data bss dec hex filename
23913 2528 4132 30573 776d mm/memcontrol.o
# /root/bin/perf stat -a -e page-faults,cache-misses --repeat 5 ./multi-fault-fork 8
Performance counter stats for './multi-fault-fork 8' (5 runs):
48179400 page-faults ( +- 0.271% )
588628407 cache-misses ( +- 0.136% )
61.004615021 seconds time elapsed ( +- 0.004% )
Then cache-miss/page fault = 12.22
Text size is reduced.
This performance improvement is not big and will be invisible in real world
applications. But this result shows this patch has some good effect even
on (small) SMP.
Here is a test program I used.
1. fork() processes on each cpus.
2. do page fault repeatedly on each process.
3. after 60secs, kill all childredn and exit.
(3 is necessary for getting stable data, this is improvement from previous one.)
#define _GNU_SOURCE
#include <stdio.h>
#include <sched.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
/*
* For avoiding contention in page table lock, FAULT area is
* sparse. If FAULT_LENGTH is too large for your cpus, decrease it.
*/
#define FAULT_LENGTH (2 * 1024 * 1024)
#define PAGE_SIZE 4096
#define MAXNUM (128)
void alarm_handler(int sig)
{
}
void *worker(int cpu, int ppid)
{
void *start, *end;
char *c;
cpu_set_t set;
int i;
CPU_ZERO(&set);
CPU_SET(cpu, &set);
sched_setaffinity(0, sizeof(set), &set);
start = mmap(NULL, FAULT_LENGTH, PROT_READ|PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
if (start == MAP_FAILED) {
perror("mmap");
exit(1);
}
end = start + FAULT_LENGTH;
pause();
//fprintf(stderr, "run%d", cpu);
while (1) {
for (c = (char*)start; (void *)c < end; c += PAGE_SIZE)
*c = 0;
madvise(start, FAULT_LENGTH, MADV_DONTNEED);
}
return NULL;
}
int main(int argc, char *argv[])
{
int num, i, ret, pid, status;
int pids[MAXNUM];
if (argc < 2)
return 0;
setpgid(0, 0);
signal(SIGALRM, alarm_handler);
num = atoi(argv[1]);
pid = getpid();
for (i = 0; i < num; ++i) {
ret = fork();
if (!ret) {
worker(i, pid);
exit(0);
}
pids[i] = ret;
}
sleep(1);
kill(-pid, SIGALRM);
sleep(60);
for (i = 0; i < num; i++)
kill(pids[i], SIGKILL);
for (i = 0; i < num; i++)
waitpid(pids[i], &status, 0);
return 0;
}
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-03-11 06:22:29 +07:00
|
|
|
preempt_enable();
|
2008-02-07 15:14:31 +07:00
|
|
|
}
|
|
|
|
|
memcg: consolidate memory cgroup lru stat functions
In mm/memcontrol.c, there are many lru stat functions as..
mem_cgroup_zone_nr_lru_pages
mem_cgroup_node_nr_file_lru_pages
mem_cgroup_nr_file_lru_pages
mem_cgroup_node_nr_anon_lru_pages
mem_cgroup_nr_anon_lru_pages
mem_cgroup_node_nr_unevictable_lru_pages
mem_cgroup_nr_unevictable_lru_pages
mem_cgroup_node_nr_lru_pages
mem_cgroup_nr_lru_pages
mem_cgroup_get_local_zonestat
Some of them are under #ifdef MAX_NUMNODES >1 and others are not.
This seems bad. This patch consolidates all functions into
mem_cgroup_zone_nr_lru_pages()
mem_cgroup_node_nr_lru_pages()
mem_cgroup_nr_lru_pages()
For these functions, "which LRU?" information is passed by a mask.
example:
mem_cgroup_nr_lru_pages(mem, BIT(LRU_ACTIVE_ANON))
And I added some macro as ALL_LRU, ALL_LRU_FILE, ALL_LRU_ANON.
example:
mem_cgroup_nr_lru_pages(mem, ALL_LRU)
BTW, considering layout of NUMA memory placement of counters, this patch seems
to be better.
Now, when we gather all LRU information, we scan in following orer
for_each_lru -> for_each_node -> for_each_zone.
This means we'll touch cache lines in different node in turn.
After patch, we'll scan
for_each_node -> for_each_zone -> for_each_lru(mask)
Then, we'll gather information in the same cacheline at once.
[akpm@linux-foundation.org: fix warnigns, build error]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-07-27 06:08:22 +07:00
|
|
|
unsigned long
|
2012-05-30 05:07:08 +07:00
|
|
|
mem_cgroup_get_lru_size(struct lruvec *lruvec, enum lru_list lru)
|
2012-05-30 05:07:00 +07:00
|
|
|
{
|
|
|
|
struct mem_cgroup_per_zone *mz;
|
|
|
|
|
|
|
|
mz = container_of(lruvec, struct mem_cgroup_per_zone, lruvec);
|
|
|
|
return mz->lru_size[lru];
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned long
|
2011-11-03 03:38:15 +07:00
|
|
|
mem_cgroup_zone_nr_lru_pages(struct mem_cgroup *memcg, int nid, int zid,
|
memcg: consolidate memory cgroup lru stat functions
In mm/memcontrol.c, there are many lru stat functions as..
mem_cgroup_zone_nr_lru_pages
mem_cgroup_node_nr_file_lru_pages
mem_cgroup_nr_file_lru_pages
mem_cgroup_node_nr_anon_lru_pages
mem_cgroup_nr_anon_lru_pages
mem_cgroup_node_nr_unevictable_lru_pages
mem_cgroup_nr_unevictable_lru_pages
mem_cgroup_node_nr_lru_pages
mem_cgroup_nr_lru_pages
mem_cgroup_get_local_zonestat
Some of them are under #ifdef MAX_NUMNODES >1 and others are not.
This seems bad. This patch consolidates all functions into
mem_cgroup_zone_nr_lru_pages()
mem_cgroup_node_nr_lru_pages()
mem_cgroup_nr_lru_pages()
For these functions, "which LRU?" information is passed by a mask.
example:
mem_cgroup_nr_lru_pages(mem, BIT(LRU_ACTIVE_ANON))
And I added some macro as ALL_LRU, ALL_LRU_FILE, ALL_LRU_ANON.
example:
mem_cgroup_nr_lru_pages(mem, ALL_LRU)
BTW, considering layout of NUMA memory placement of counters, this patch seems
to be better.
Now, when we gather all LRU information, we scan in following orer
for_each_lru -> for_each_node -> for_each_zone.
This means we'll touch cache lines in different node in turn.
After patch, we'll scan
for_each_node -> for_each_zone -> for_each_lru(mask)
Then, we'll gather information in the same cacheline at once.
[akpm@linux-foundation.org: fix warnigns, build error]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-07-27 06:08:22 +07:00
|
|
|
unsigned int lru_mask)
|
2011-05-27 06:25:33 +07:00
|
|
|
{
|
|
|
|
struct mem_cgroup_per_zone *mz;
|
2012-03-22 06:34:19 +07:00
|
|
|
enum lru_list lru;
|
memcg: consolidate memory cgroup lru stat functions
In mm/memcontrol.c, there are many lru stat functions as..
mem_cgroup_zone_nr_lru_pages
mem_cgroup_node_nr_file_lru_pages
mem_cgroup_nr_file_lru_pages
mem_cgroup_node_nr_anon_lru_pages
mem_cgroup_nr_anon_lru_pages
mem_cgroup_node_nr_unevictable_lru_pages
mem_cgroup_nr_unevictable_lru_pages
mem_cgroup_node_nr_lru_pages
mem_cgroup_nr_lru_pages
mem_cgroup_get_local_zonestat
Some of them are under #ifdef MAX_NUMNODES >1 and others are not.
This seems bad. This patch consolidates all functions into
mem_cgroup_zone_nr_lru_pages()
mem_cgroup_node_nr_lru_pages()
mem_cgroup_nr_lru_pages()
For these functions, "which LRU?" information is passed by a mask.
example:
mem_cgroup_nr_lru_pages(mem, BIT(LRU_ACTIVE_ANON))
And I added some macro as ALL_LRU, ALL_LRU_FILE, ALL_LRU_ANON.
example:
mem_cgroup_nr_lru_pages(mem, ALL_LRU)
BTW, considering layout of NUMA memory placement of counters, this patch seems
to be better.
Now, when we gather all LRU information, we scan in following orer
for_each_lru -> for_each_node -> for_each_zone.
This means we'll touch cache lines in different node in turn.
After patch, we'll scan
for_each_node -> for_each_zone -> for_each_lru(mask)
Then, we'll gather information in the same cacheline at once.
[akpm@linux-foundation.org: fix warnigns, build error]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-07-27 06:08:22 +07:00
|
|
|
unsigned long ret = 0;
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
mz = mem_cgroup_zoneinfo(memcg, nid, zid);
|
memcg: consolidate memory cgroup lru stat functions
In mm/memcontrol.c, there are many lru stat functions as..
mem_cgroup_zone_nr_lru_pages
mem_cgroup_node_nr_file_lru_pages
mem_cgroup_nr_file_lru_pages
mem_cgroup_node_nr_anon_lru_pages
mem_cgroup_nr_anon_lru_pages
mem_cgroup_node_nr_unevictable_lru_pages
mem_cgroup_nr_unevictable_lru_pages
mem_cgroup_node_nr_lru_pages
mem_cgroup_nr_lru_pages
mem_cgroup_get_local_zonestat
Some of them are under #ifdef MAX_NUMNODES >1 and others are not.
This seems bad. This patch consolidates all functions into
mem_cgroup_zone_nr_lru_pages()
mem_cgroup_node_nr_lru_pages()
mem_cgroup_nr_lru_pages()
For these functions, "which LRU?" information is passed by a mask.
example:
mem_cgroup_nr_lru_pages(mem, BIT(LRU_ACTIVE_ANON))
And I added some macro as ALL_LRU, ALL_LRU_FILE, ALL_LRU_ANON.
example:
mem_cgroup_nr_lru_pages(mem, ALL_LRU)
BTW, considering layout of NUMA memory placement of counters, this patch seems
to be better.
Now, when we gather all LRU information, we scan in following orer
for_each_lru -> for_each_node -> for_each_zone.
This means we'll touch cache lines in different node in turn.
After patch, we'll scan
for_each_node -> for_each_zone -> for_each_lru(mask)
Then, we'll gather information in the same cacheline at once.
[akpm@linux-foundation.org: fix warnigns, build error]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-07-27 06:08:22 +07:00
|
|
|
|
2012-03-22 06:34:19 +07:00
|
|
|
for_each_lru(lru) {
|
|
|
|
if (BIT(lru) & lru_mask)
|
|
|
|
ret += mz->lru_size[lru];
|
memcg: consolidate memory cgroup lru stat functions
In mm/memcontrol.c, there are many lru stat functions as..
mem_cgroup_zone_nr_lru_pages
mem_cgroup_node_nr_file_lru_pages
mem_cgroup_nr_file_lru_pages
mem_cgroup_node_nr_anon_lru_pages
mem_cgroup_nr_anon_lru_pages
mem_cgroup_node_nr_unevictable_lru_pages
mem_cgroup_nr_unevictable_lru_pages
mem_cgroup_node_nr_lru_pages
mem_cgroup_nr_lru_pages
mem_cgroup_get_local_zonestat
Some of them are under #ifdef MAX_NUMNODES >1 and others are not.
This seems bad. This patch consolidates all functions into
mem_cgroup_zone_nr_lru_pages()
mem_cgroup_node_nr_lru_pages()
mem_cgroup_nr_lru_pages()
For these functions, "which LRU?" information is passed by a mask.
example:
mem_cgroup_nr_lru_pages(mem, BIT(LRU_ACTIVE_ANON))
And I added some macro as ALL_LRU, ALL_LRU_FILE, ALL_LRU_ANON.
example:
mem_cgroup_nr_lru_pages(mem, ALL_LRU)
BTW, considering layout of NUMA memory placement of counters, this patch seems
to be better.
Now, when we gather all LRU information, we scan in following orer
for_each_lru -> for_each_node -> for_each_zone.
This means we'll touch cache lines in different node in turn.
After patch, we'll scan
for_each_node -> for_each_zone -> for_each_lru(mask)
Then, we'll gather information in the same cacheline at once.
[akpm@linux-foundation.org: fix warnigns, build error]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-07-27 06:08:22 +07:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned long
|
2011-11-03 03:38:15 +07:00
|
|
|
mem_cgroup_node_nr_lru_pages(struct mem_cgroup *memcg,
|
memcg: consolidate memory cgroup lru stat functions
In mm/memcontrol.c, there are many lru stat functions as..
mem_cgroup_zone_nr_lru_pages
mem_cgroup_node_nr_file_lru_pages
mem_cgroup_nr_file_lru_pages
mem_cgroup_node_nr_anon_lru_pages
mem_cgroup_nr_anon_lru_pages
mem_cgroup_node_nr_unevictable_lru_pages
mem_cgroup_nr_unevictable_lru_pages
mem_cgroup_node_nr_lru_pages
mem_cgroup_nr_lru_pages
mem_cgroup_get_local_zonestat
Some of them are under #ifdef MAX_NUMNODES >1 and others are not.
This seems bad. This patch consolidates all functions into
mem_cgroup_zone_nr_lru_pages()
mem_cgroup_node_nr_lru_pages()
mem_cgroup_nr_lru_pages()
For these functions, "which LRU?" information is passed by a mask.
example:
mem_cgroup_nr_lru_pages(mem, BIT(LRU_ACTIVE_ANON))
And I added some macro as ALL_LRU, ALL_LRU_FILE, ALL_LRU_ANON.
example:
mem_cgroup_nr_lru_pages(mem, ALL_LRU)
BTW, considering layout of NUMA memory placement of counters, this patch seems
to be better.
Now, when we gather all LRU information, we scan in following orer
for_each_lru -> for_each_node -> for_each_zone.
This means we'll touch cache lines in different node in turn.
After patch, we'll scan
for_each_node -> for_each_zone -> for_each_lru(mask)
Then, we'll gather information in the same cacheline at once.
[akpm@linux-foundation.org: fix warnigns, build error]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-07-27 06:08:22 +07:00
|
|
|
int nid, unsigned int lru_mask)
|
|
|
|
{
|
2011-05-27 06:25:33 +07:00
|
|
|
u64 total = 0;
|
|
|
|
int zid;
|
|
|
|
|
memcg: consolidate memory cgroup lru stat functions
In mm/memcontrol.c, there are many lru stat functions as..
mem_cgroup_zone_nr_lru_pages
mem_cgroup_node_nr_file_lru_pages
mem_cgroup_nr_file_lru_pages
mem_cgroup_node_nr_anon_lru_pages
mem_cgroup_nr_anon_lru_pages
mem_cgroup_node_nr_unevictable_lru_pages
mem_cgroup_nr_unevictable_lru_pages
mem_cgroup_node_nr_lru_pages
mem_cgroup_nr_lru_pages
mem_cgroup_get_local_zonestat
Some of them are under #ifdef MAX_NUMNODES >1 and others are not.
This seems bad. This patch consolidates all functions into
mem_cgroup_zone_nr_lru_pages()
mem_cgroup_node_nr_lru_pages()
mem_cgroup_nr_lru_pages()
For these functions, "which LRU?" information is passed by a mask.
example:
mem_cgroup_nr_lru_pages(mem, BIT(LRU_ACTIVE_ANON))
And I added some macro as ALL_LRU, ALL_LRU_FILE, ALL_LRU_ANON.
example:
mem_cgroup_nr_lru_pages(mem, ALL_LRU)
BTW, considering layout of NUMA memory placement of counters, this patch seems
to be better.
Now, when we gather all LRU information, we scan in following orer
for_each_lru -> for_each_node -> for_each_zone.
This means we'll touch cache lines in different node in turn.
After patch, we'll scan
for_each_node -> for_each_zone -> for_each_lru(mask)
Then, we'll gather information in the same cacheline at once.
[akpm@linux-foundation.org: fix warnigns, build error]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-07-27 06:08:22 +07:00
|
|
|
for (zid = 0; zid < MAX_NR_ZONES; zid++)
|
2011-11-03 03:38:15 +07:00
|
|
|
total += mem_cgroup_zone_nr_lru_pages(memcg,
|
|
|
|
nid, zid, lru_mask);
|
memcg: consolidate memory cgroup lru stat functions
In mm/memcontrol.c, there are many lru stat functions as..
mem_cgroup_zone_nr_lru_pages
mem_cgroup_node_nr_file_lru_pages
mem_cgroup_nr_file_lru_pages
mem_cgroup_node_nr_anon_lru_pages
mem_cgroup_nr_anon_lru_pages
mem_cgroup_node_nr_unevictable_lru_pages
mem_cgroup_nr_unevictable_lru_pages
mem_cgroup_node_nr_lru_pages
mem_cgroup_nr_lru_pages
mem_cgroup_get_local_zonestat
Some of them are under #ifdef MAX_NUMNODES >1 and others are not.
This seems bad. This patch consolidates all functions into
mem_cgroup_zone_nr_lru_pages()
mem_cgroup_node_nr_lru_pages()
mem_cgroup_nr_lru_pages()
For these functions, "which LRU?" information is passed by a mask.
example:
mem_cgroup_nr_lru_pages(mem, BIT(LRU_ACTIVE_ANON))
And I added some macro as ALL_LRU, ALL_LRU_FILE, ALL_LRU_ANON.
example:
mem_cgroup_nr_lru_pages(mem, ALL_LRU)
BTW, considering layout of NUMA memory placement of counters, this patch seems
to be better.
Now, when we gather all LRU information, we scan in following orer
for_each_lru -> for_each_node -> for_each_zone.
This means we'll touch cache lines in different node in turn.
After patch, we'll scan
for_each_node -> for_each_zone -> for_each_lru(mask)
Then, we'll gather information in the same cacheline at once.
[akpm@linux-foundation.org: fix warnigns, build error]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-07-27 06:08:22 +07:00
|
|
|
|
2011-05-27 06:25:33 +07:00
|
|
|
return total;
|
|
|
|
}
|
memcg: consolidate memory cgroup lru stat functions
In mm/memcontrol.c, there are many lru stat functions as..
mem_cgroup_zone_nr_lru_pages
mem_cgroup_node_nr_file_lru_pages
mem_cgroup_nr_file_lru_pages
mem_cgroup_node_nr_anon_lru_pages
mem_cgroup_nr_anon_lru_pages
mem_cgroup_node_nr_unevictable_lru_pages
mem_cgroup_nr_unevictable_lru_pages
mem_cgroup_node_nr_lru_pages
mem_cgroup_nr_lru_pages
mem_cgroup_get_local_zonestat
Some of them are under #ifdef MAX_NUMNODES >1 and others are not.
This seems bad. This patch consolidates all functions into
mem_cgroup_zone_nr_lru_pages()
mem_cgroup_node_nr_lru_pages()
mem_cgroup_nr_lru_pages()
For these functions, "which LRU?" information is passed by a mask.
example:
mem_cgroup_nr_lru_pages(mem, BIT(LRU_ACTIVE_ANON))
And I added some macro as ALL_LRU, ALL_LRU_FILE, ALL_LRU_ANON.
example:
mem_cgroup_nr_lru_pages(mem, ALL_LRU)
BTW, considering layout of NUMA memory placement of counters, this patch seems
to be better.
Now, when we gather all LRU information, we scan in following orer
for_each_lru -> for_each_node -> for_each_zone.
This means we'll touch cache lines in different node in turn.
After patch, we'll scan
for_each_node -> for_each_zone -> for_each_lru(mask)
Then, we'll gather information in the same cacheline at once.
[akpm@linux-foundation.org: fix warnigns, build error]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-07-27 06:08:22 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static unsigned long mem_cgroup_nr_lru_pages(struct mem_cgroup *memcg,
|
memcg: consolidate memory cgroup lru stat functions
In mm/memcontrol.c, there are many lru stat functions as..
mem_cgroup_zone_nr_lru_pages
mem_cgroup_node_nr_file_lru_pages
mem_cgroup_nr_file_lru_pages
mem_cgroup_node_nr_anon_lru_pages
mem_cgroup_nr_anon_lru_pages
mem_cgroup_node_nr_unevictable_lru_pages
mem_cgroup_nr_unevictable_lru_pages
mem_cgroup_node_nr_lru_pages
mem_cgroup_nr_lru_pages
mem_cgroup_get_local_zonestat
Some of them are under #ifdef MAX_NUMNODES >1 and others are not.
This seems bad. This patch consolidates all functions into
mem_cgroup_zone_nr_lru_pages()
mem_cgroup_node_nr_lru_pages()
mem_cgroup_nr_lru_pages()
For these functions, "which LRU?" information is passed by a mask.
example:
mem_cgroup_nr_lru_pages(mem, BIT(LRU_ACTIVE_ANON))
And I added some macro as ALL_LRU, ALL_LRU_FILE, ALL_LRU_ANON.
example:
mem_cgroup_nr_lru_pages(mem, ALL_LRU)
BTW, considering layout of NUMA memory placement of counters, this patch seems
to be better.
Now, when we gather all LRU information, we scan in following orer
for_each_lru -> for_each_node -> for_each_zone.
This means we'll touch cache lines in different node in turn.
After patch, we'll scan
for_each_node -> for_each_zone -> for_each_lru(mask)
Then, we'll gather information in the same cacheline at once.
[akpm@linux-foundation.org: fix warnigns, build error]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-07-27 06:08:22 +07:00
|
|
|
unsigned int lru_mask)
|
2008-02-07 15:14:31 +07:00
|
|
|
{
|
2011-05-27 06:25:33 +07:00
|
|
|
int nid;
|
2008-02-07 15:14:31 +07:00
|
|
|
u64 total = 0;
|
|
|
|
|
memcg: consolidate memory cgroup lru stat functions
In mm/memcontrol.c, there are many lru stat functions as..
mem_cgroup_zone_nr_lru_pages
mem_cgroup_node_nr_file_lru_pages
mem_cgroup_nr_file_lru_pages
mem_cgroup_node_nr_anon_lru_pages
mem_cgroup_nr_anon_lru_pages
mem_cgroup_node_nr_unevictable_lru_pages
mem_cgroup_nr_unevictable_lru_pages
mem_cgroup_node_nr_lru_pages
mem_cgroup_nr_lru_pages
mem_cgroup_get_local_zonestat
Some of them are under #ifdef MAX_NUMNODES >1 and others are not.
This seems bad. This patch consolidates all functions into
mem_cgroup_zone_nr_lru_pages()
mem_cgroup_node_nr_lru_pages()
mem_cgroup_nr_lru_pages()
For these functions, "which LRU?" information is passed by a mask.
example:
mem_cgroup_nr_lru_pages(mem, BIT(LRU_ACTIVE_ANON))
And I added some macro as ALL_LRU, ALL_LRU_FILE, ALL_LRU_ANON.
example:
mem_cgroup_nr_lru_pages(mem, ALL_LRU)
BTW, considering layout of NUMA memory placement of counters, this patch seems
to be better.
Now, when we gather all LRU information, we scan in following orer
for_each_lru -> for_each_node -> for_each_zone.
This means we'll touch cache lines in different node in turn.
After patch, we'll scan
for_each_node -> for_each_zone -> for_each_lru(mask)
Then, we'll gather information in the same cacheline at once.
[akpm@linux-foundation.org: fix warnigns, build error]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-07-27 06:08:22 +07:00
|
|
|
for_each_node_state(nid, N_HIGH_MEMORY)
|
2011-11-03 03:38:15 +07:00
|
|
|
total += mem_cgroup_node_nr_lru_pages(memcg, nid, lru_mask);
|
2008-02-07 15:14:31 +07:00
|
|
|
return total;
|
2008-02-07 15:14:24 +07:00
|
|
|
}
|
|
|
|
|
2012-01-13 08:18:23 +07:00
|
|
|
static bool mem_cgroup_event_ratelimit(struct mem_cgroup *memcg,
|
|
|
|
enum mem_cgroup_events_target target)
|
2011-03-24 06:42:38 +07:00
|
|
|
{
|
|
|
|
unsigned long val, next;
|
|
|
|
|
2012-05-30 05:07:07 +07:00
|
|
|
val = __this_cpu_read(memcg->stat->nr_page_events);
|
2011-11-03 03:38:33 +07:00
|
|
|
next = __this_cpu_read(memcg->stat->targets[target]);
|
2011-03-24 06:42:38 +07:00
|
|
|
/* from time_after() in jiffies.h */
|
2012-01-13 08:18:23 +07:00
|
|
|
if ((long)next - (long)val < 0) {
|
|
|
|
switch (target) {
|
|
|
|
case MEM_CGROUP_TARGET_THRESH:
|
|
|
|
next = val + THRESHOLDS_EVENTS_TARGET;
|
|
|
|
break;
|
|
|
|
case MEM_CGROUP_TARGET_SOFTLIMIT:
|
|
|
|
next = val + SOFTLIMIT_EVENTS_TARGET;
|
|
|
|
break;
|
|
|
|
case MEM_CGROUP_TARGET_NUMAINFO:
|
|
|
|
next = val + NUMAINFO_EVENTS_TARGET;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
__this_cpu_write(memcg->stat->targets[target], next);
|
|
|
|
return true;
|
2011-03-24 06:42:38 +07:00
|
|
|
}
|
2012-01-13 08:18:23 +07:00
|
|
|
return false;
|
2010-03-11 06:22:31 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check events in order.
|
|
|
|
*
|
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
static void memcg_check_events(struct mem_cgroup *memcg, struct page *page)
|
2010-03-11 06:22:31 +07:00
|
|
|
{
|
2011-11-03 03:38:33 +07:00
|
|
|
preempt_disable();
|
2010-03-11 06:22:31 +07:00
|
|
|
/* threshold event is triggered in finer grain than soft limit */
|
2012-01-13 08:18:23 +07:00
|
|
|
if (unlikely(mem_cgroup_event_ratelimit(memcg,
|
|
|
|
MEM_CGROUP_TARGET_THRESH))) {
|
2012-02-04 06:37:14 +07:00
|
|
|
bool do_softlimit;
|
|
|
|
bool do_numainfo __maybe_unused;
|
2012-01-13 08:18:23 +07:00
|
|
|
|
|
|
|
do_softlimit = mem_cgroup_event_ratelimit(memcg,
|
|
|
|
MEM_CGROUP_TARGET_SOFTLIMIT);
|
|
|
|
#if MAX_NUMNODES > 1
|
|
|
|
do_numainfo = mem_cgroup_event_ratelimit(memcg,
|
|
|
|
MEM_CGROUP_TARGET_NUMAINFO);
|
|
|
|
#endif
|
|
|
|
preempt_enable();
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
mem_cgroup_threshold(memcg);
|
2012-01-13 08:18:23 +07:00
|
|
|
if (unlikely(do_softlimit))
|
2011-11-03 03:38:15 +07:00
|
|
|
mem_cgroup_update_tree(memcg, page);
|
2011-07-09 05:39:43 +07:00
|
|
|
#if MAX_NUMNODES > 1
|
2012-01-13 08:18:23 +07:00
|
|
|
if (unlikely(do_numainfo))
|
2011-11-03 03:38:15 +07:00
|
|
|
atomic_inc(&memcg->numainfo_events);
|
2011-07-09 05:39:43 +07:00
|
|
|
#endif
|
2012-01-13 08:18:23 +07:00
|
|
|
} else
|
|
|
|
preempt_enable();
|
2010-03-11 06:22:31 +07:00
|
|
|
}
|
|
|
|
|
2011-12-12 04:47:04 +07:00
|
|
|
struct mem_cgroup *mem_cgroup_from_cont(struct cgroup *cont)
|
2008-02-07 15:13:50 +07:00
|
|
|
{
|
2012-08-01 06:46:01 +07:00
|
|
|
return mem_cgroup_from_css(
|
|
|
|
cgroup_subsys_state(cont, mem_cgroup_subsys_id));
|
2008-02-07 15:13:50 +07:00
|
|
|
}
|
|
|
|
|
cgroups: add an owner to the mm_struct
Remove the mem_cgroup member from mm_struct and instead adds an owner.
This approach was suggested by Paul Menage. The advantage of this approach
is that, once the mm->owner is known, using the subsystem id, the cgroup
can be determined. It also allows several control groups that are
virtually grouped by mm_struct, to exist independent of the memory
controller i.e., without adding mem_cgroup's for each controller, to
mm_struct.
A new config option CONFIG_MM_OWNER is added and the memory resource
controller selects this config option.
This patch also adds cgroup callbacks to notify subsystems when mm->owner
changes. The mm_cgroup_changed callback is called with the task_lock() of
the new task held and is called just prior to changing the mm->owner.
I am indebted to Paul Menage for the several reviews of this patchset and
helping me make it lighter and simpler.
This patch was tested on a powerpc box, it was compiled with both the
MM_OWNER config turned on and off.
After the thread group leader exits, it's moved to init_css_state by
cgroup_exit(), thus all future charges from runnings threads would be
redirected to the init_css_set's subsystem.
Signed-off-by: Balbir Singh <balbir@linux.vnet.ibm.com>
Cc: Pavel Emelianov <xemul@openvz.org>
Cc: Hugh Dickins <hugh@veritas.com>
Cc: Sudhir Kumar <skumar@linux.vnet.ibm.com>
Cc: YAMAMOTO Takashi <yamamoto@valinux.co.jp>
Cc: Hirokazu Takahashi <taka@valinux.co.jp>
Cc: David Rientjes <rientjes@google.com>,
Cc: Balbir Singh <balbir@linux.vnet.ibm.com>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Pekka Enberg <penberg@cs.helsinki.fi>
Reviewed-by: Paul Menage <menage@google.com>
Cc: Oleg Nesterov <oleg@tv-sign.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-04-29 15:00:16 +07:00
|
|
|
struct mem_cgroup *mem_cgroup_from_task(struct task_struct *p)
|
2008-02-07 15:13:51 +07:00
|
|
|
{
|
mm owner: fix race between swapoff and exit
There's a race between mm->owner assignment and swapoff, more easily
seen when task slab poisoning is turned on. The condition occurs when
try_to_unuse() runs in parallel with an exiting task. A similar race
can occur with callers of get_task_mm(), such as /proc/<pid>/<mmstats>
or ptrace or page migration.
CPU0 CPU1
try_to_unuse
looks at mm = task0->mm
increments mm->mm_users
task 0 exits
mm->owner needs to be updated, but no
new owner is found (mm_users > 1, but
no other task has task->mm = task0->mm)
mm_update_next_owner() leaves
mmput(mm) decrements mm->mm_users
task0 freed
dereferencing mm->owner fails
The fix is to notify the subsystem via mm_owner_changed callback(),
if no new owner is found, by specifying the new task as NULL.
Jiri Slaby:
mm->owner was set to NULL prior to calling cgroup_mm_owner_callbacks(), but
must be set after that, so as not to pass NULL as old owner causing oops.
Daisuke Nishimura:
mm_update_next_owner() may set mm->owner to NULL, but mem_cgroup_from_task()
and its callers need to take account of this situation to avoid oops.
Hugh Dickins:
Lockdep warning and hang below exec_mmap() when testing these patches.
exit_mm() up_reads mmap_sem before calling mm_update_next_owner(),
so exec_mmap() now needs to do the same. And with that repositioning,
there's now no point in mm_need_new_owner() allowing for NULL mm.
Reported-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Balbir Singh <balbir@linux.vnet.ibm.com>
Signed-off-by: Jiri Slaby <jirislaby@gmail.com>
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: Hugh Dickins <hugh@veritas.com>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-09-29 05:09:31 +07:00
|
|
|
/*
|
|
|
|
* mm_update_next_owner() may clear mm->owner to NULL
|
|
|
|
* if it races with swapoff, page migration, etc.
|
|
|
|
* So this can be called with p == NULL.
|
|
|
|
*/
|
|
|
|
if (unlikely(!p))
|
|
|
|
return NULL;
|
|
|
|
|
2012-08-01 06:46:01 +07:00
|
|
|
return mem_cgroup_from_css(task_subsys_state(p, mem_cgroup_subsys_id));
|
2008-02-07 15:13:51 +07:00
|
|
|
}
|
|
|
|
|
2011-06-16 05:08:13 +07:00
|
|
|
struct mem_cgroup *try_get_mem_cgroup_from_mm(struct mm_struct *mm)
|
2009-01-08 09:08:33 +07:00
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
struct mem_cgroup *memcg = NULL;
|
memcg: fix OOM killer under memcg
This patch tries to fix OOM Killer problems caused by hierarchy.
Now, memcg itself has OOM KILL function (in oom_kill.c) and tries to
kill a task in memcg.
But, when hierarchy is used, it's broken and correct task cannot
be killed. For example, in following cgroup
/groupA/ hierarchy=1, limit=1G,
01 nolimit
02 nolimit
All tasks' memory usage under /groupA, /groupA/01, groupA/02 is limited to
groupA's 1Gbytes but OOM Killer just kills tasks in groupA.
This patch provides makes the bad process be selected from all tasks
under hierarchy. BTW, currently, oom_jiffies is updated against groupA
in above case. oom_jiffies of tree should be updated.
To see how oom_jiffies is used, please check mem_cgroup_oom_called()
callers.
[akpm@linux-foundation.org: build fix]
[akpm@linux-foundation.org: const fix]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Paul Menage <menage@google.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: David Rientjes <rientjes@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-04-03 06:57:38 +07:00
|
|
|
|
|
|
|
if (!mm)
|
|
|
|
return NULL;
|
2009-01-08 09:08:33 +07:00
|
|
|
/*
|
|
|
|
* Because we have no locks, mm->owner's may be being moved to other
|
|
|
|
* cgroup. We use css_tryget() here even if this looks
|
|
|
|
* pessimistic (rather than adding locks here).
|
|
|
|
*/
|
|
|
|
rcu_read_lock();
|
|
|
|
do {
|
2011-11-03 03:38:15 +07:00
|
|
|
memcg = mem_cgroup_from_task(rcu_dereference(mm->owner));
|
|
|
|
if (unlikely(!memcg))
|
2009-01-08 09:08:33 +07:00
|
|
|
break;
|
2011-11-03 03:38:15 +07:00
|
|
|
} while (!css_tryget(&memcg->css));
|
2009-01-08 09:08:33 +07:00
|
|
|
rcu_read_unlock();
|
2011-11-03 03:38:15 +07:00
|
|
|
return memcg;
|
2009-01-08 09:08:33 +07:00
|
|
|
}
|
|
|
|
|
2012-01-13 08:17:59 +07:00
|
|
|
/**
|
|
|
|
* mem_cgroup_iter - iterate over memory cgroup hierarchy
|
|
|
|
* @root: hierarchy root
|
|
|
|
* @prev: previously returned memcg, NULL on first invocation
|
|
|
|
* @reclaim: cookie for shared reclaim walks, NULL for full walks
|
|
|
|
*
|
|
|
|
* Returns references to children of the hierarchy below @root, or
|
|
|
|
* @root itself, or %NULL after a full round-trip.
|
|
|
|
*
|
|
|
|
* Caller must pass the return value in @prev on subsequent
|
|
|
|
* invocations for reference counting, or use mem_cgroup_iter_break()
|
|
|
|
* to cancel a hierarchy walk before the round-trip is complete.
|
|
|
|
*
|
|
|
|
* Reclaimers can specify a zone and a priority level in @reclaim to
|
|
|
|
* divide up the memcgs in the hierarchy among all concurrent
|
|
|
|
* reclaimers operating on the same zone and priority.
|
|
|
|
*/
|
|
|
|
struct mem_cgroup *mem_cgroup_iter(struct mem_cgroup *root,
|
|
|
|
struct mem_cgroup *prev,
|
|
|
|
struct mem_cgroup_reclaim_cookie *reclaim)
|
2009-04-03 06:57:35 +07:00
|
|
|
{
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
struct mem_cgroup *memcg = NULL;
|
|
|
|
int id = 0;
|
2010-10-28 05:33:42 +07:00
|
|
|
|
2012-01-13 08:17:59 +07:00
|
|
|
if (mem_cgroup_disabled())
|
|
|
|
return NULL;
|
|
|
|
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
if (!root)
|
|
|
|
root = root_mem_cgroup;
|
2010-10-28 05:33:41 +07:00
|
|
|
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
if (prev && !reclaim)
|
|
|
|
id = css_id(&prev->css);
|
2009-04-03 06:57:35 +07:00
|
|
|
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
if (prev && prev != root)
|
|
|
|
css_put(&prev->css);
|
2009-04-03 06:57:35 +07:00
|
|
|
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
if (!root->use_hierarchy && root != root_mem_cgroup) {
|
|
|
|
if (prev)
|
|
|
|
return NULL;
|
|
|
|
return root;
|
|
|
|
}
|
2009-04-03 06:57:35 +07:00
|
|
|
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
while (!memcg) {
|
mm: memcg: per-priority per-zone hierarchy scan generations
Memory cgroup limit reclaim currently picks one memory cgroup out of the
target hierarchy, remembers it as the last scanned child, and reclaims
all zones in it with decreasing priority levels.
The new hierarchy reclaim code will pick memory cgroups from the same
hierarchy concurrently from different zones and priority levels, it
becomes necessary that hierarchy roots not only remember the last
scanned child, but do so for each zone and priority level.
Until now, we reclaimed memcgs like this:
mem = mem_cgroup_iter(root)
for each priority level:
for each zone in zonelist:
reclaim(mem, zone)
But subsequent patches will move the memcg iteration inside the loop
over the zones:
for each priority level:
for each zone in zonelist:
mem = mem_cgroup_iter(root)
reclaim(mem, zone)
And to keep with the original scan order - memcg -> priority -> zone -
the last scanned memcg has to be remembered per zone and per priority
level.
Furthermore, global reclaim will be switched to the hierarchy walk as
well. Different from limit reclaim, which can just recheck the limit
after some reclaim progress, its target is to scan all memcgs for the
desired zone pages, proportional to the memcg size, and so reliably
detecting a full hierarchy round-trip will become crucial.
Currently, the code relies on one reclaimer encountering the same memcg
twice, but that is error-prone with concurrent reclaimers. Instead, use
a generation counter that is increased every time the child with the
highest ID has been visited, so that reclaimers can stop when the
generation changes.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:55 +07:00
|
|
|
struct mem_cgroup_reclaim_iter *uninitialized_var(iter);
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
struct cgroup_subsys_state *css;
|
2010-10-28 05:33:42 +07:00
|
|
|
|
mm: memcg: per-priority per-zone hierarchy scan generations
Memory cgroup limit reclaim currently picks one memory cgroup out of the
target hierarchy, remembers it as the last scanned child, and reclaims
all zones in it with decreasing priority levels.
The new hierarchy reclaim code will pick memory cgroups from the same
hierarchy concurrently from different zones and priority levels, it
becomes necessary that hierarchy roots not only remember the last
scanned child, but do so for each zone and priority level.
Until now, we reclaimed memcgs like this:
mem = mem_cgroup_iter(root)
for each priority level:
for each zone in zonelist:
reclaim(mem, zone)
But subsequent patches will move the memcg iteration inside the loop
over the zones:
for each priority level:
for each zone in zonelist:
mem = mem_cgroup_iter(root)
reclaim(mem, zone)
And to keep with the original scan order - memcg -> priority -> zone -
the last scanned memcg has to be remembered per zone and per priority
level.
Furthermore, global reclaim will be switched to the hierarchy walk as
well. Different from limit reclaim, which can just recheck the limit
after some reclaim progress, its target is to scan all memcgs for the
desired zone pages, proportional to the memcg size, and so reliably
detecting a full hierarchy round-trip will become crucial.
Currently, the code relies on one reclaimer encountering the same memcg
twice, but that is error-prone with concurrent reclaimers. Instead, use
a generation counter that is increased every time the child with the
highest ID has been visited, so that reclaimers can stop when the
generation changes.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:55 +07:00
|
|
|
if (reclaim) {
|
|
|
|
int nid = zone_to_nid(reclaim->zone);
|
|
|
|
int zid = zone_idx(reclaim->zone);
|
|
|
|
struct mem_cgroup_per_zone *mz;
|
|
|
|
|
|
|
|
mz = mem_cgroup_zoneinfo(root, nid, zid);
|
|
|
|
iter = &mz->reclaim_iter[reclaim->priority];
|
|
|
|
if (prev && reclaim->generation != iter->generation)
|
|
|
|
return NULL;
|
|
|
|
id = iter->position;
|
|
|
|
}
|
2010-10-28 05:33:41 +07:00
|
|
|
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
rcu_read_lock();
|
|
|
|
css = css_get_next(&mem_cgroup_subsys, id + 1, &root->css, &id);
|
|
|
|
if (css) {
|
|
|
|
if (css == &root->css || css_tryget(css))
|
2012-08-01 06:46:01 +07:00
|
|
|
memcg = mem_cgroup_from_css(css);
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
} else
|
|
|
|
id = 0;
|
2009-04-03 06:57:35 +07:00
|
|
|
rcu_read_unlock();
|
|
|
|
|
mm: memcg: per-priority per-zone hierarchy scan generations
Memory cgroup limit reclaim currently picks one memory cgroup out of the
target hierarchy, remembers it as the last scanned child, and reclaims
all zones in it with decreasing priority levels.
The new hierarchy reclaim code will pick memory cgroups from the same
hierarchy concurrently from different zones and priority levels, it
becomes necessary that hierarchy roots not only remember the last
scanned child, but do so for each zone and priority level.
Until now, we reclaimed memcgs like this:
mem = mem_cgroup_iter(root)
for each priority level:
for each zone in zonelist:
reclaim(mem, zone)
But subsequent patches will move the memcg iteration inside the loop
over the zones:
for each priority level:
for each zone in zonelist:
mem = mem_cgroup_iter(root)
reclaim(mem, zone)
And to keep with the original scan order - memcg -> priority -> zone -
the last scanned memcg has to be remembered per zone and per priority
level.
Furthermore, global reclaim will be switched to the hierarchy walk as
well. Different from limit reclaim, which can just recheck the limit
after some reclaim progress, its target is to scan all memcgs for the
desired zone pages, proportional to the memcg size, and so reliably
detecting a full hierarchy round-trip will become crucial.
Currently, the code relies on one reclaimer encountering the same memcg
twice, but that is error-prone with concurrent reclaimers. Instead, use
a generation counter that is increased every time the child with the
highest ID has been visited, so that reclaimers can stop when the
generation changes.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:55 +07:00
|
|
|
if (reclaim) {
|
|
|
|
iter->position = id;
|
|
|
|
if (!css)
|
|
|
|
iter->generation++;
|
|
|
|
else if (!prev && memcg)
|
|
|
|
reclaim->generation = iter->generation;
|
|
|
|
}
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
|
|
|
|
if (prev && !css)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return memcg;
|
2009-04-03 06:57:35 +07:00
|
|
|
}
|
2010-10-28 05:33:41 +07:00
|
|
|
|
2012-01-13 08:17:59 +07:00
|
|
|
/**
|
|
|
|
* mem_cgroup_iter_break - abort a hierarchy walk prematurely
|
|
|
|
* @root: hierarchy root
|
|
|
|
* @prev: last visited hierarchy member as returned by mem_cgroup_iter()
|
|
|
|
*/
|
|
|
|
void mem_cgroup_iter_break(struct mem_cgroup *root,
|
|
|
|
struct mem_cgroup *prev)
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
{
|
|
|
|
if (!root)
|
|
|
|
root = root_mem_cgroup;
|
|
|
|
if (prev && prev != root)
|
|
|
|
css_put(&prev->css);
|
|
|
|
}
|
2010-10-28 05:33:41 +07:00
|
|
|
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
/*
|
|
|
|
* Iteration constructs for visiting all cgroups (under a tree). If
|
|
|
|
* loops are exited prematurely (break), mem_cgroup_iter_break() must
|
|
|
|
* be used for reference counting.
|
|
|
|
*/
|
|
|
|
#define for_each_mem_cgroup_tree(iter, root) \
|
mm: memcg: per-priority per-zone hierarchy scan generations
Memory cgroup limit reclaim currently picks one memory cgroup out of the
target hierarchy, remembers it as the last scanned child, and reclaims
all zones in it with decreasing priority levels.
The new hierarchy reclaim code will pick memory cgroups from the same
hierarchy concurrently from different zones and priority levels, it
becomes necessary that hierarchy roots not only remember the last
scanned child, but do so for each zone and priority level.
Until now, we reclaimed memcgs like this:
mem = mem_cgroup_iter(root)
for each priority level:
for each zone in zonelist:
reclaim(mem, zone)
But subsequent patches will move the memcg iteration inside the loop
over the zones:
for each priority level:
for each zone in zonelist:
mem = mem_cgroup_iter(root)
reclaim(mem, zone)
And to keep with the original scan order - memcg -> priority -> zone -
the last scanned memcg has to be remembered per zone and per priority
level.
Furthermore, global reclaim will be switched to the hierarchy walk as
well. Different from limit reclaim, which can just recheck the limit
after some reclaim progress, its target is to scan all memcgs for the
desired zone pages, proportional to the memcg size, and so reliably
detecting a full hierarchy round-trip will become crucial.
Currently, the code relies on one reclaimer encountering the same memcg
twice, but that is error-prone with concurrent reclaimers. Instead, use
a generation counter that is increased every time the child with the
highest ID has been visited, so that reclaimers can stop when the
generation changes.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:55 +07:00
|
|
|
for (iter = mem_cgroup_iter(root, NULL, NULL); \
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
iter != NULL; \
|
mm: memcg: per-priority per-zone hierarchy scan generations
Memory cgroup limit reclaim currently picks one memory cgroup out of the
target hierarchy, remembers it as the last scanned child, and reclaims
all zones in it with decreasing priority levels.
The new hierarchy reclaim code will pick memory cgroups from the same
hierarchy concurrently from different zones and priority levels, it
becomes necessary that hierarchy roots not only remember the last
scanned child, but do so for each zone and priority level.
Until now, we reclaimed memcgs like this:
mem = mem_cgroup_iter(root)
for each priority level:
for each zone in zonelist:
reclaim(mem, zone)
But subsequent patches will move the memcg iteration inside the loop
over the zones:
for each priority level:
for each zone in zonelist:
mem = mem_cgroup_iter(root)
reclaim(mem, zone)
And to keep with the original scan order - memcg -> priority -> zone -
the last scanned memcg has to be remembered per zone and per priority
level.
Furthermore, global reclaim will be switched to the hierarchy walk as
well. Different from limit reclaim, which can just recheck the limit
after some reclaim progress, its target is to scan all memcgs for the
desired zone pages, proportional to the memcg size, and so reliably
detecting a full hierarchy round-trip will become crucial.
Currently, the code relies on one reclaimer encountering the same memcg
twice, but that is error-prone with concurrent reclaimers. Instead, use
a generation counter that is increased every time the child with the
highest ID has been visited, so that reclaimers can stop when the
generation changes.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:55 +07:00
|
|
|
iter = mem_cgroup_iter(root, iter, NULL))
|
2010-10-28 05:33:42 +07:00
|
|
|
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
#define for_each_mem_cgroup(iter) \
|
mm: memcg: per-priority per-zone hierarchy scan generations
Memory cgroup limit reclaim currently picks one memory cgroup out of the
target hierarchy, remembers it as the last scanned child, and reclaims
all zones in it with decreasing priority levels.
The new hierarchy reclaim code will pick memory cgroups from the same
hierarchy concurrently from different zones and priority levels, it
becomes necessary that hierarchy roots not only remember the last
scanned child, but do so for each zone and priority level.
Until now, we reclaimed memcgs like this:
mem = mem_cgroup_iter(root)
for each priority level:
for each zone in zonelist:
reclaim(mem, zone)
But subsequent patches will move the memcg iteration inside the loop
over the zones:
for each priority level:
for each zone in zonelist:
mem = mem_cgroup_iter(root)
reclaim(mem, zone)
And to keep with the original scan order - memcg -> priority -> zone -
the last scanned memcg has to be remembered per zone and per priority
level.
Furthermore, global reclaim will be switched to the hierarchy walk as
well. Different from limit reclaim, which can just recheck the limit
after some reclaim progress, its target is to scan all memcgs for the
desired zone pages, proportional to the memcg size, and so reliably
detecting a full hierarchy round-trip will become crucial.
Currently, the code relies on one reclaimer encountering the same memcg
twice, but that is error-prone with concurrent reclaimers. Instead, use
a generation counter that is increased every time the child with the
highest ID has been visited, so that reclaimers can stop when the
generation changes.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:55 +07:00
|
|
|
for (iter = mem_cgroup_iter(NULL, NULL, NULL); \
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
iter != NULL; \
|
mm: memcg: per-priority per-zone hierarchy scan generations
Memory cgroup limit reclaim currently picks one memory cgroup out of the
target hierarchy, remembers it as the last scanned child, and reclaims
all zones in it with decreasing priority levels.
The new hierarchy reclaim code will pick memory cgroups from the same
hierarchy concurrently from different zones and priority levels, it
becomes necessary that hierarchy roots not only remember the last
scanned child, but do so for each zone and priority level.
Until now, we reclaimed memcgs like this:
mem = mem_cgroup_iter(root)
for each priority level:
for each zone in zonelist:
reclaim(mem, zone)
But subsequent patches will move the memcg iteration inside the loop
over the zones:
for each priority level:
for each zone in zonelist:
mem = mem_cgroup_iter(root)
reclaim(mem, zone)
And to keep with the original scan order - memcg -> priority -> zone -
the last scanned memcg has to be remembered per zone and per priority
level.
Furthermore, global reclaim will be switched to the hierarchy walk as
well. Different from limit reclaim, which can just recheck the limit
after some reclaim progress, its target is to scan all memcgs for the
desired zone pages, proportional to the memcg size, and so reliably
detecting a full hierarchy round-trip will become crucial.
Currently, the code relies on one reclaimer encountering the same memcg
twice, but that is error-prone with concurrent reclaimers. Instead, use
a generation counter that is increased every time the child with the
highest ID has been visited, so that reclaimers can stop when the
generation changes.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:55 +07:00
|
|
|
iter = mem_cgroup_iter(NULL, iter, NULL))
|
2009-04-03 06:57:35 +07:00
|
|
|
|
2011-05-27 06:25:38 +07:00
|
|
|
void mem_cgroup_count_vm_event(struct mm_struct *mm, enum vm_event_item idx)
|
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
struct mem_cgroup *memcg;
|
2011-05-27 06:25:38 +07:00
|
|
|
|
|
|
|
if (!mm)
|
|
|
|
return;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
2011-11-03 03:38:15 +07:00
|
|
|
memcg = mem_cgroup_from_task(rcu_dereference(mm->owner));
|
|
|
|
if (unlikely(!memcg))
|
2011-05-27 06:25:38 +07:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
switch (idx) {
|
|
|
|
case PGFAULT:
|
2012-01-13 08:18:35 +07:00
|
|
|
this_cpu_inc(memcg->stat->events[MEM_CGROUP_EVENTS_PGFAULT]);
|
|
|
|
break;
|
|
|
|
case PGMAJFAULT:
|
|
|
|
this_cpu_inc(memcg->stat->events[MEM_CGROUP_EVENTS_PGMAJFAULT]);
|
2011-05-27 06:25:38 +07:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
BUG();
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
rcu_read_unlock();
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(mem_cgroup_count_vm_event);
|
|
|
|
|
2012-01-13 08:18:15 +07:00
|
|
|
/**
|
|
|
|
* mem_cgroup_zone_lruvec - get the lru list vector for a zone and memcg
|
|
|
|
* @zone: zone of the wanted lruvec
|
2012-05-30 05:07:09 +07:00
|
|
|
* @memcg: memcg of the wanted lruvec
|
2012-01-13 08:18:15 +07:00
|
|
|
*
|
|
|
|
* Returns the lru list vector holding pages for the given @zone and
|
|
|
|
* @mem. This can be the global zone lruvec, if the memory controller
|
|
|
|
* is disabled.
|
|
|
|
*/
|
|
|
|
struct lruvec *mem_cgroup_zone_lruvec(struct zone *zone,
|
|
|
|
struct mem_cgroup *memcg)
|
|
|
|
{
|
|
|
|
struct mem_cgroup_per_zone *mz;
|
memcg: fix hotplugged memory zone oops
When MEMCG is configured on (even when it's disabled by boot option),
when adding or removing a page to/from its lru list, the zone pointer
used for stats updates is nowadays taken from the struct lruvec. (On
many configurations, calculating zone from page is slower.)
But we have no code to update all the lruvecs (per zone, per memcg) when
a memory node is hotadded. Here's an extract from the oops which
results when running numactl to bind a program to a newly onlined node:
BUG: unable to handle kernel NULL pointer dereference at 0000000000000f60
IP: __mod_zone_page_state+0x9/0x60
Pid: 1219, comm: numactl Not tainted 3.6.0-rc5+ #180 Bochs Bochs
Process numactl (pid: 1219, threadinfo ffff880039abc000, task ffff8800383c4ce0)
Call Trace:
__pagevec_lru_add_fn+0xdf/0x140
pagevec_lru_move_fn+0xb1/0x100
__pagevec_lru_add+0x1c/0x30
lru_add_drain_cpu+0xa3/0x130
lru_add_drain+0x2f/0x40
...
The natural solution might be to use a memcg callback whenever memory is
hotadded; but that solution has not been scoped out, and it happens that
we do have an easy location at which to update lruvec->zone. The lruvec
pointer is discovered either by mem_cgroup_zone_lruvec() or by
mem_cgroup_page_lruvec(), and both of those do know the right zone.
So check and set lruvec->zone in those; and remove the inadequate
attempt to set lruvec->zone from lruvec_init(), which is called before
NODE_DATA(node) has been allocated in such cases.
Ah, there was one exceptionr. For no particularly good reason,
mem_cgroup_force_empty_list() has its own code for deciding lruvec.
Change it to use the standard mem_cgroup_zone_lruvec() and
mem_cgroup_get_lru_size() too. In fact it was already safe against such
an oops (the lru lists in danger could only be empty), but we're better
proofed against future changes this way.
I've marked this for stable (3.6) since we introduced the problem in 3.5
(now closed to stable); but I have no idea if this is the only fix
needed to get memory hotadd working with memcg in 3.6, and received no
answer when I enquired twice before.
Reported-by: Tang Chen <tangchen@cn.fujitsu.com>
Signed-off-by: Hugh Dickins <hughd@google.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Konstantin Khlebnikov <khlebnikov@openvz.org>
Cc: Wen Congyang <wency@cn.fujitsu.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-11-17 05:14:54 +07:00
|
|
|
struct lruvec *lruvec;
|
2012-01-13 08:18:15 +07:00
|
|
|
|
memcg: fix hotplugged memory zone oops
When MEMCG is configured on (even when it's disabled by boot option),
when adding or removing a page to/from its lru list, the zone pointer
used for stats updates is nowadays taken from the struct lruvec. (On
many configurations, calculating zone from page is slower.)
But we have no code to update all the lruvecs (per zone, per memcg) when
a memory node is hotadded. Here's an extract from the oops which
results when running numactl to bind a program to a newly onlined node:
BUG: unable to handle kernel NULL pointer dereference at 0000000000000f60
IP: __mod_zone_page_state+0x9/0x60
Pid: 1219, comm: numactl Not tainted 3.6.0-rc5+ #180 Bochs Bochs
Process numactl (pid: 1219, threadinfo ffff880039abc000, task ffff8800383c4ce0)
Call Trace:
__pagevec_lru_add_fn+0xdf/0x140
pagevec_lru_move_fn+0xb1/0x100
__pagevec_lru_add+0x1c/0x30
lru_add_drain_cpu+0xa3/0x130
lru_add_drain+0x2f/0x40
...
The natural solution might be to use a memcg callback whenever memory is
hotadded; but that solution has not been scoped out, and it happens that
we do have an easy location at which to update lruvec->zone. The lruvec
pointer is discovered either by mem_cgroup_zone_lruvec() or by
mem_cgroup_page_lruvec(), and both of those do know the right zone.
So check and set lruvec->zone in those; and remove the inadequate
attempt to set lruvec->zone from lruvec_init(), which is called before
NODE_DATA(node) has been allocated in such cases.
Ah, there was one exceptionr. For no particularly good reason,
mem_cgroup_force_empty_list() has its own code for deciding lruvec.
Change it to use the standard mem_cgroup_zone_lruvec() and
mem_cgroup_get_lru_size() too. In fact it was already safe against such
an oops (the lru lists in danger could only be empty), but we're better
proofed against future changes this way.
I've marked this for stable (3.6) since we introduced the problem in 3.5
(now closed to stable); but I have no idea if this is the only fix
needed to get memory hotadd working with memcg in 3.6, and received no
answer when I enquired twice before.
Reported-by: Tang Chen <tangchen@cn.fujitsu.com>
Signed-off-by: Hugh Dickins <hughd@google.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Konstantin Khlebnikov <khlebnikov@openvz.org>
Cc: Wen Congyang <wency@cn.fujitsu.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-11-17 05:14:54 +07:00
|
|
|
if (mem_cgroup_disabled()) {
|
|
|
|
lruvec = &zone->lruvec;
|
|
|
|
goto out;
|
|
|
|
}
|
2012-01-13 08:18:15 +07:00
|
|
|
|
|
|
|
mz = mem_cgroup_zoneinfo(memcg, zone_to_nid(zone), zone_idx(zone));
|
memcg: fix hotplugged memory zone oops
When MEMCG is configured on (even when it's disabled by boot option),
when adding or removing a page to/from its lru list, the zone pointer
used for stats updates is nowadays taken from the struct lruvec. (On
many configurations, calculating zone from page is slower.)
But we have no code to update all the lruvecs (per zone, per memcg) when
a memory node is hotadded. Here's an extract from the oops which
results when running numactl to bind a program to a newly onlined node:
BUG: unable to handle kernel NULL pointer dereference at 0000000000000f60
IP: __mod_zone_page_state+0x9/0x60
Pid: 1219, comm: numactl Not tainted 3.6.0-rc5+ #180 Bochs Bochs
Process numactl (pid: 1219, threadinfo ffff880039abc000, task ffff8800383c4ce0)
Call Trace:
__pagevec_lru_add_fn+0xdf/0x140
pagevec_lru_move_fn+0xb1/0x100
__pagevec_lru_add+0x1c/0x30
lru_add_drain_cpu+0xa3/0x130
lru_add_drain+0x2f/0x40
...
The natural solution might be to use a memcg callback whenever memory is
hotadded; but that solution has not been scoped out, and it happens that
we do have an easy location at which to update lruvec->zone. The lruvec
pointer is discovered either by mem_cgroup_zone_lruvec() or by
mem_cgroup_page_lruvec(), and both of those do know the right zone.
So check and set lruvec->zone in those; and remove the inadequate
attempt to set lruvec->zone from lruvec_init(), which is called before
NODE_DATA(node) has been allocated in such cases.
Ah, there was one exceptionr. For no particularly good reason,
mem_cgroup_force_empty_list() has its own code for deciding lruvec.
Change it to use the standard mem_cgroup_zone_lruvec() and
mem_cgroup_get_lru_size() too. In fact it was already safe against such
an oops (the lru lists in danger could only be empty), but we're better
proofed against future changes this way.
I've marked this for stable (3.6) since we introduced the problem in 3.5
(now closed to stable); but I have no idea if this is the only fix
needed to get memory hotadd working with memcg in 3.6, and received no
answer when I enquired twice before.
Reported-by: Tang Chen <tangchen@cn.fujitsu.com>
Signed-off-by: Hugh Dickins <hughd@google.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Konstantin Khlebnikov <khlebnikov@openvz.org>
Cc: Wen Congyang <wency@cn.fujitsu.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-11-17 05:14:54 +07:00
|
|
|
lruvec = &mz->lruvec;
|
|
|
|
out:
|
|
|
|
/*
|
|
|
|
* Since a node can be onlined after the mem_cgroup was created,
|
|
|
|
* we have to be prepared to initialize lruvec->zone here;
|
|
|
|
* and if offlined then reonlined, we need to reinitialize it.
|
|
|
|
*/
|
|
|
|
if (unlikely(lruvec->zone != zone))
|
|
|
|
lruvec->zone = zone;
|
|
|
|
return lruvec;
|
2012-01-13 08:18:15 +07:00
|
|
|
}
|
|
|
|
|
memcg: synchronized LRU
A big patch for changing memcg's LRU semantics.
Now,
- page_cgroup is linked to mem_cgroup's its own LRU (per zone).
- LRU of page_cgroup is not synchronous with global LRU.
- page and page_cgroup is one-to-one and statically allocated.
- To find page_cgroup is on what LRU, you have to check pc->mem_cgroup as
- lru = page_cgroup_zoneinfo(pc, nid_of_pc, zid_of_pc);
- SwapCache is handled.
And, when we handle LRU list of page_cgroup, we do following.
pc = lookup_page_cgroup(page);
lock_page_cgroup(pc); .....................(1)
mz = page_cgroup_zoneinfo(pc);
spin_lock(&mz->lru_lock);
.....add to LRU
spin_unlock(&mz->lru_lock);
unlock_page_cgroup(pc);
But (1) is spin_lock and we have to be afraid of dead-lock with zone->lru_lock.
So, trylock() is used at (1), now. Without (1), we can't trust "mz" is correct.
This is a trial to remove this dirty nesting of locks.
This patch changes mz->lru_lock to be zone->lru_lock.
Then, above sequence will be written as
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
mem_cgroup_add/remove/etc_lru() {
pc = lookup_page_cgroup(page);
mz = page_cgroup_zoneinfo(pc);
if (PageCgroupUsed(pc)) {
....add to LRU
}
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
This is much simpler.
(*) We're safe even if we don't take lock_page_cgroup(pc). Because..
1. When pc->mem_cgroup can be modified.
- at charge.
- at account_move().
2. at charge
the PCG_USED bit is not set before pc->mem_cgroup is fixed.
3. at account_move()
the page is isolated and not on LRU.
Pros.
- easy for maintenance.
- memcg can make use of laziness of pagevec.
- we don't have to duplicated LRU/Active/Unevictable bit in page_cgroup.
- LRU status of memcg will be synchronized with global LRU's one.
- # of locks are reduced.
- account_move() is simplified very much.
Cons.
- may increase cost of LRU rotation.
(no impact if memcg is not configured.)
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:08:01 +07:00
|
|
|
/*
|
|
|
|
* Following LRU functions are allowed to be used without PCG_LOCK.
|
|
|
|
* Operations are called by routine of global LRU independently from memcg.
|
|
|
|
* What we have to take care of here is validness of pc->mem_cgroup.
|
|
|
|
*
|
|
|
|
* Changes to pc->mem_cgroup happens when
|
|
|
|
* 1. charge
|
|
|
|
* 2. moving account
|
|
|
|
* In typical case, "charge" is done before add-to-lru. Exception is SwapCache.
|
|
|
|
* It is added to LRU before charge.
|
|
|
|
* If PCG_USED bit is not set, page_cgroup is not added to this private LRU.
|
|
|
|
* When moving account, the page is not on LRU. It's isolated.
|
|
|
|
*/
|
2008-10-19 10:26:32 +07:00
|
|
|
|
2012-01-13 08:18:15 +07:00
|
|
|
/**
|
2012-05-30 05:07:09 +07:00
|
|
|
* mem_cgroup_page_lruvec - return lruvec for adding an lru page
|
2012-01-13 08:18:15 +07:00
|
|
|
* @page: the page
|
2012-05-30 05:07:09 +07:00
|
|
|
* @zone: zone of the page
|
2012-01-13 08:18:15 +07:00
|
|
|
*/
|
2012-05-30 05:07:09 +07:00
|
|
|
struct lruvec *mem_cgroup_page_lruvec(struct page *page, struct zone *zone)
|
memcg: synchronized LRU
A big patch for changing memcg's LRU semantics.
Now,
- page_cgroup is linked to mem_cgroup's its own LRU (per zone).
- LRU of page_cgroup is not synchronous with global LRU.
- page and page_cgroup is one-to-one and statically allocated.
- To find page_cgroup is on what LRU, you have to check pc->mem_cgroup as
- lru = page_cgroup_zoneinfo(pc, nid_of_pc, zid_of_pc);
- SwapCache is handled.
And, when we handle LRU list of page_cgroup, we do following.
pc = lookup_page_cgroup(page);
lock_page_cgroup(pc); .....................(1)
mz = page_cgroup_zoneinfo(pc);
spin_lock(&mz->lru_lock);
.....add to LRU
spin_unlock(&mz->lru_lock);
unlock_page_cgroup(pc);
But (1) is spin_lock and we have to be afraid of dead-lock with zone->lru_lock.
So, trylock() is used at (1), now. Without (1), we can't trust "mz" is correct.
This is a trial to remove this dirty nesting of locks.
This patch changes mz->lru_lock to be zone->lru_lock.
Then, above sequence will be written as
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
mem_cgroup_add/remove/etc_lru() {
pc = lookup_page_cgroup(page);
mz = page_cgroup_zoneinfo(pc);
if (PageCgroupUsed(pc)) {
....add to LRU
}
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
This is much simpler.
(*) We're safe even if we don't take lock_page_cgroup(pc). Because..
1. When pc->mem_cgroup can be modified.
- at charge.
- at account_move().
2. at charge
the PCG_USED bit is not set before pc->mem_cgroup is fixed.
3. at account_move()
the page is isolated and not on LRU.
Pros.
- easy for maintenance.
- memcg can make use of laziness of pagevec.
- we don't have to duplicated LRU/Active/Unevictable bit in page_cgroup.
- LRU status of memcg will be synchronized with global LRU's one.
- # of locks are reduced.
- account_move() is simplified very much.
Cons.
- may increase cost of LRU rotation.
(no impact if memcg is not configured.)
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:08:01 +07:00
|
|
|
{
|
|
|
|
struct mem_cgroup_per_zone *mz;
|
2012-01-13 08:18:15 +07:00
|
|
|
struct mem_cgroup *memcg;
|
|
|
|
struct page_cgroup *pc;
|
memcg: fix hotplugged memory zone oops
When MEMCG is configured on (even when it's disabled by boot option),
when adding or removing a page to/from its lru list, the zone pointer
used for stats updates is nowadays taken from the struct lruvec. (On
many configurations, calculating zone from page is slower.)
But we have no code to update all the lruvecs (per zone, per memcg) when
a memory node is hotadded. Here's an extract from the oops which
results when running numactl to bind a program to a newly onlined node:
BUG: unable to handle kernel NULL pointer dereference at 0000000000000f60
IP: __mod_zone_page_state+0x9/0x60
Pid: 1219, comm: numactl Not tainted 3.6.0-rc5+ #180 Bochs Bochs
Process numactl (pid: 1219, threadinfo ffff880039abc000, task ffff8800383c4ce0)
Call Trace:
__pagevec_lru_add_fn+0xdf/0x140
pagevec_lru_move_fn+0xb1/0x100
__pagevec_lru_add+0x1c/0x30
lru_add_drain_cpu+0xa3/0x130
lru_add_drain+0x2f/0x40
...
The natural solution might be to use a memcg callback whenever memory is
hotadded; but that solution has not been scoped out, and it happens that
we do have an easy location at which to update lruvec->zone. The lruvec
pointer is discovered either by mem_cgroup_zone_lruvec() or by
mem_cgroup_page_lruvec(), and both of those do know the right zone.
So check and set lruvec->zone in those; and remove the inadequate
attempt to set lruvec->zone from lruvec_init(), which is called before
NODE_DATA(node) has been allocated in such cases.
Ah, there was one exceptionr. For no particularly good reason,
mem_cgroup_force_empty_list() has its own code for deciding lruvec.
Change it to use the standard mem_cgroup_zone_lruvec() and
mem_cgroup_get_lru_size() too. In fact it was already safe against such
an oops (the lru lists in danger could only be empty), but we're better
proofed against future changes this way.
I've marked this for stable (3.6) since we introduced the problem in 3.5
(now closed to stable); but I have no idea if this is the only fix
needed to get memory hotadd working with memcg in 3.6, and received no
answer when I enquired twice before.
Reported-by: Tang Chen <tangchen@cn.fujitsu.com>
Signed-off-by: Hugh Dickins <hughd@google.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Konstantin Khlebnikov <khlebnikov@openvz.org>
Cc: Wen Congyang <wency@cn.fujitsu.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-11-17 05:14:54 +07:00
|
|
|
struct lruvec *lruvec;
|
2008-02-07 15:14:31 +07:00
|
|
|
|
memcg: fix hotplugged memory zone oops
When MEMCG is configured on (even when it's disabled by boot option),
when adding or removing a page to/from its lru list, the zone pointer
used for stats updates is nowadays taken from the struct lruvec. (On
many configurations, calculating zone from page is slower.)
But we have no code to update all the lruvecs (per zone, per memcg) when
a memory node is hotadded. Here's an extract from the oops which
results when running numactl to bind a program to a newly onlined node:
BUG: unable to handle kernel NULL pointer dereference at 0000000000000f60
IP: __mod_zone_page_state+0x9/0x60
Pid: 1219, comm: numactl Not tainted 3.6.0-rc5+ #180 Bochs Bochs
Process numactl (pid: 1219, threadinfo ffff880039abc000, task ffff8800383c4ce0)
Call Trace:
__pagevec_lru_add_fn+0xdf/0x140
pagevec_lru_move_fn+0xb1/0x100
__pagevec_lru_add+0x1c/0x30
lru_add_drain_cpu+0xa3/0x130
lru_add_drain+0x2f/0x40
...
The natural solution might be to use a memcg callback whenever memory is
hotadded; but that solution has not been scoped out, and it happens that
we do have an easy location at which to update lruvec->zone. The lruvec
pointer is discovered either by mem_cgroup_zone_lruvec() or by
mem_cgroup_page_lruvec(), and both of those do know the right zone.
So check and set lruvec->zone in those; and remove the inadequate
attempt to set lruvec->zone from lruvec_init(), which is called before
NODE_DATA(node) has been allocated in such cases.
Ah, there was one exceptionr. For no particularly good reason,
mem_cgroup_force_empty_list() has its own code for deciding lruvec.
Change it to use the standard mem_cgroup_zone_lruvec() and
mem_cgroup_get_lru_size() too. In fact it was already safe against such
an oops (the lru lists in danger could only be empty), but we're better
proofed against future changes this way.
I've marked this for stable (3.6) since we introduced the problem in 3.5
(now closed to stable); but I have no idea if this is the only fix
needed to get memory hotadd working with memcg in 3.6, and received no
answer when I enquired twice before.
Reported-by: Tang Chen <tangchen@cn.fujitsu.com>
Signed-off-by: Hugh Dickins <hughd@google.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Konstantin Khlebnikov <khlebnikov@openvz.org>
Cc: Wen Congyang <wency@cn.fujitsu.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-11-17 05:14:54 +07:00
|
|
|
if (mem_cgroup_disabled()) {
|
|
|
|
lruvec = &zone->lruvec;
|
|
|
|
goto out;
|
|
|
|
}
|
2012-01-13 08:18:15 +07:00
|
|
|
|
memcg: synchronized LRU
A big patch for changing memcg's LRU semantics.
Now,
- page_cgroup is linked to mem_cgroup's its own LRU (per zone).
- LRU of page_cgroup is not synchronous with global LRU.
- page and page_cgroup is one-to-one and statically allocated.
- To find page_cgroup is on what LRU, you have to check pc->mem_cgroup as
- lru = page_cgroup_zoneinfo(pc, nid_of_pc, zid_of_pc);
- SwapCache is handled.
And, when we handle LRU list of page_cgroup, we do following.
pc = lookup_page_cgroup(page);
lock_page_cgroup(pc); .....................(1)
mz = page_cgroup_zoneinfo(pc);
spin_lock(&mz->lru_lock);
.....add to LRU
spin_unlock(&mz->lru_lock);
unlock_page_cgroup(pc);
But (1) is spin_lock and we have to be afraid of dead-lock with zone->lru_lock.
So, trylock() is used at (1), now. Without (1), we can't trust "mz" is correct.
This is a trial to remove this dirty nesting of locks.
This patch changes mz->lru_lock to be zone->lru_lock.
Then, above sequence will be written as
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
mem_cgroup_add/remove/etc_lru() {
pc = lookup_page_cgroup(page);
mz = page_cgroup_zoneinfo(pc);
if (PageCgroupUsed(pc)) {
....add to LRU
}
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
This is much simpler.
(*) We're safe even if we don't take lock_page_cgroup(pc). Because..
1. When pc->mem_cgroup can be modified.
- at charge.
- at account_move().
2. at charge
the PCG_USED bit is not set before pc->mem_cgroup is fixed.
3. at account_move()
the page is isolated and not on LRU.
Pros.
- easy for maintenance.
- memcg can make use of laziness of pagevec.
- we don't have to duplicated LRU/Active/Unevictable bit in page_cgroup.
- LRU status of memcg will be synchronized with global LRU's one.
- # of locks are reduced.
- account_move() is simplified very much.
Cons.
- may increase cost of LRU rotation.
(no impact if memcg is not configured.)
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:08:01 +07:00
|
|
|
pc = lookup_page_cgroup(page);
|
2012-01-13 08:19:01 +07:00
|
|
|
memcg = pc->mem_cgroup;
|
memcg: fix GPF when cgroup removal races with last exit
When moving tasks from old memcg (with move_charge_at_immigrate on new
memcg), followed by removal of old memcg, hit General Protection Fault in
mem_cgroup_lru_del_list() (called from release_pages called from
free_pages_and_swap_cache from tlb_flush_mmu from tlb_finish_mmu from
exit_mmap from mmput from exit_mm from do_exit).
Somewhat reproducible, takes a few hours: the old struct mem_cgroup has
been freed and poisoned by SLAB_DEBUG, but mem_cgroup_lru_del_list() is
still trying to update its stats, and take page off lru before freeing.
A task, or a charge, or a page on lru: each secures a memcg against
removal. In this case, the last task has been moved out of the old memcg,
and it is exiting: anonymous pages are uncharged one by one from the
memcg, as they are zapped from its pagetables, so the charge gets down to
0; but the pages themselves are queued in an mmu_gather for freeing.
Most of those pages will be on lru (and force_empty is careful to
lru_add_drain_all, to add pages from pagevec to lru first), but not
necessarily all: perhaps some have been isolated for page reclaim, perhaps
some isolated for other reasons. So, force_empty may find no task, no
charge and no page on lru, and let the removal proceed.
There would still be no problem if these pages were immediately freed; but
typically (and the put_page_testzero protocol demands it) they have to be
added back to lru before they are found freeable, then removed from lru
and freed. We don't see the issue when adding, because the
mem_cgroup_iter() loops keep their own reference to the memcg being
scanned; but when it comes to mem_cgroup_lru_del_list().
I believe this was not an issue in v3.2: there, PageCgroupAcctLRU and
PageCgroupUsed flags were used (like a trick with mirrors) to deflect view
of pc->mem_cgroup to the stable root_mem_cgroup when neither set.
38c5d72f3ebe ("memcg: simplify LRU handling by new rule") mercifully
removed those convolutions, but left this General Protection Fault.
But it's surprisingly easy to restore the old behaviour: just check
PageCgroupUsed in mem_cgroup_lru_add_list() (which decides on which lruvec
to add), and reset pc to root_mem_cgroup if page is uncharged. A risky
change? just going back to how it worked before; testing, and an audit of
uses of pc->mem_cgroup, show no problem.
And there's a nice bonus: with mem_cgroup_lru_add_list() itself making
sure that an uncharged page goes to root lru, mem_cgroup_reset_owner() no
longer has any purpose, and we can safely revert 4e5f01c2b9b9 ("memcg:
clear pc->mem_cgroup if necessary").
Calling update_page_reclaim_stat() after add_page_to_lru_list() in swap.c
is not strictly necessary: the lru_lock there, with RCU before memcg
structures are freed, makes mem_cgroup_get_reclaim_stat_from_page safe
without that; but it seems cleaner to rely on one dependency less.
Signed-off-by: Hugh Dickins <hughd@google.com>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Konstantin Khlebnikov <khlebnikov@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-06 05:59:18 +07:00
|
|
|
|
|
|
|
/*
|
2012-05-30 05:07:09 +07:00
|
|
|
* Surreptitiously switch any uncharged offlist page to root:
|
memcg: fix GPF when cgroup removal races with last exit
When moving tasks from old memcg (with move_charge_at_immigrate on new
memcg), followed by removal of old memcg, hit General Protection Fault in
mem_cgroup_lru_del_list() (called from release_pages called from
free_pages_and_swap_cache from tlb_flush_mmu from tlb_finish_mmu from
exit_mmap from mmput from exit_mm from do_exit).
Somewhat reproducible, takes a few hours: the old struct mem_cgroup has
been freed and poisoned by SLAB_DEBUG, but mem_cgroup_lru_del_list() is
still trying to update its stats, and take page off lru before freeing.
A task, or a charge, or a page on lru: each secures a memcg against
removal. In this case, the last task has been moved out of the old memcg,
and it is exiting: anonymous pages are uncharged one by one from the
memcg, as they are zapped from its pagetables, so the charge gets down to
0; but the pages themselves are queued in an mmu_gather for freeing.
Most of those pages will be on lru (and force_empty is careful to
lru_add_drain_all, to add pages from pagevec to lru first), but not
necessarily all: perhaps some have been isolated for page reclaim, perhaps
some isolated for other reasons. So, force_empty may find no task, no
charge and no page on lru, and let the removal proceed.
There would still be no problem if these pages were immediately freed; but
typically (and the put_page_testzero protocol demands it) they have to be
added back to lru before they are found freeable, then removed from lru
and freed. We don't see the issue when adding, because the
mem_cgroup_iter() loops keep their own reference to the memcg being
scanned; but when it comes to mem_cgroup_lru_del_list().
I believe this was not an issue in v3.2: there, PageCgroupAcctLRU and
PageCgroupUsed flags were used (like a trick with mirrors) to deflect view
of pc->mem_cgroup to the stable root_mem_cgroup when neither set.
38c5d72f3ebe ("memcg: simplify LRU handling by new rule") mercifully
removed those convolutions, but left this General Protection Fault.
But it's surprisingly easy to restore the old behaviour: just check
PageCgroupUsed in mem_cgroup_lru_add_list() (which decides on which lruvec
to add), and reset pc to root_mem_cgroup if page is uncharged. A risky
change? just going back to how it worked before; testing, and an audit of
uses of pc->mem_cgroup, show no problem.
And there's a nice bonus: with mem_cgroup_lru_add_list() itself making
sure that an uncharged page goes to root lru, mem_cgroup_reset_owner() no
longer has any purpose, and we can safely revert 4e5f01c2b9b9 ("memcg:
clear pc->mem_cgroup if necessary").
Calling update_page_reclaim_stat() after add_page_to_lru_list() in swap.c
is not strictly necessary: the lru_lock there, with RCU before memcg
structures are freed, makes mem_cgroup_get_reclaim_stat_from_page safe
without that; but it seems cleaner to rely on one dependency less.
Signed-off-by: Hugh Dickins <hughd@google.com>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Konstantin Khlebnikov <khlebnikov@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-06 05:59:18 +07:00
|
|
|
* an uncharged page off lru does nothing to secure
|
|
|
|
* its former mem_cgroup from sudden removal.
|
|
|
|
*
|
|
|
|
* Our caller holds lru_lock, and PageCgroupUsed is updated
|
|
|
|
* under page_cgroup lock: between them, they make all uses
|
|
|
|
* of pc->mem_cgroup safe.
|
|
|
|
*/
|
2012-05-30 05:07:09 +07:00
|
|
|
if (!PageLRU(page) && !PageCgroupUsed(pc) && memcg != root_mem_cgroup)
|
memcg: fix GPF when cgroup removal races with last exit
When moving tasks from old memcg (with move_charge_at_immigrate on new
memcg), followed by removal of old memcg, hit General Protection Fault in
mem_cgroup_lru_del_list() (called from release_pages called from
free_pages_and_swap_cache from tlb_flush_mmu from tlb_finish_mmu from
exit_mmap from mmput from exit_mm from do_exit).
Somewhat reproducible, takes a few hours: the old struct mem_cgroup has
been freed and poisoned by SLAB_DEBUG, but mem_cgroup_lru_del_list() is
still trying to update its stats, and take page off lru before freeing.
A task, or a charge, or a page on lru: each secures a memcg against
removal. In this case, the last task has been moved out of the old memcg,
and it is exiting: anonymous pages are uncharged one by one from the
memcg, as they are zapped from its pagetables, so the charge gets down to
0; but the pages themselves are queued in an mmu_gather for freeing.
Most of those pages will be on lru (and force_empty is careful to
lru_add_drain_all, to add pages from pagevec to lru first), but not
necessarily all: perhaps some have been isolated for page reclaim, perhaps
some isolated for other reasons. So, force_empty may find no task, no
charge and no page on lru, and let the removal proceed.
There would still be no problem if these pages were immediately freed; but
typically (and the put_page_testzero protocol demands it) they have to be
added back to lru before they are found freeable, then removed from lru
and freed. We don't see the issue when adding, because the
mem_cgroup_iter() loops keep their own reference to the memcg being
scanned; but when it comes to mem_cgroup_lru_del_list().
I believe this was not an issue in v3.2: there, PageCgroupAcctLRU and
PageCgroupUsed flags were used (like a trick with mirrors) to deflect view
of pc->mem_cgroup to the stable root_mem_cgroup when neither set.
38c5d72f3ebe ("memcg: simplify LRU handling by new rule") mercifully
removed those convolutions, but left this General Protection Fault.
But it's surprisingly easy to restore the old behaviour: just check
PageCgroupUsed in mem_cgroup_lru_add_list() (which decides on which lruvec
to add), and reset pc to root_mem_cgroup if page is uncharged. A risky
change? just going back to how it worked before; testing, and an audit of
uses of pc->mem_cgroup, show no problem.
And there's a nice bonus: with mem_cgroup_lru_add_list() itself making
sure that an uncharged page goes to root lru, mem_cgroup_reset_owner() no
longer has any purpose, and we can safely revert 4e5f01c2b9b9 ("memcg:
clear pc->mem_cgroup if necessary").
Calling update_page_reclaim_stat() after add_page_to_lru_list() in swap.c
is not strictly necessary: the lru_lock there, with RCU before memcg
structures are freed, makes mem_cgroup_get_reclaim_stat_from_page safe
without that; but it seems cleaner to rely on one dependency less.
Signed-off-by: Hugh Dickins <hughd@google.com>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Konstantin Khlebnikov <khlebnikov@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-06 05:59:18 +07:00
|
|
|
pc->mem_cgroup = memcg = root_mem_cgroup;
|
|
|
|
|
2012-01-13 08:18:15 +07:00
|
|
|
mz = page_cgroup_zoneinfo(memcg, page);
|
memcg: fix hotplugged memory zone oops
When MEMCG is configured on (even when it's disabled by boot option),
when adding or removing a page to/from its lru list, the zone pointer
used for stats updates is nowadays taken from the struct lruvec. (On
many configurations, calculating zone from page is slower.)
But we have no code to update all the lruvecs (per zone, per memcg) when
a memory node is hotadded. Here's an extract from the oops which
results when running numactl to bind a program to a newly onlined node:
BUG: unable to handle kernel NULL pointer dereference at 0000000000000f60
IP: __mod_zone_page_state+0x9/0x60
Pid: 1219, comm: numactl Not tainted 3.6.0-rc5+ #180 Bochs Bochs
Process numactl (pid: 1219, threadinfo ffff880039abc000, task ffff8800383c4ce0)
Call Trace:
__pagevec_lru_add_fn+0xdf/0x140
pagevec_lru_move_fn+0xb1/0x100
__pagevec_lru_add+0x1c/0x30
lru_add_drain_cpu+0xa3/0x130
lru_add_drain+0x2f/0x40
...
The natural solution might be to use a memcg callback whenever memory is
hotadded; but that solution has not been scoped out, and it happens that
we do have an easy location at which to update lruvec->zone. The lruvec
pointer is discovered either by mem_cgroup_zone_lruvec() or by
mem_cgroup_page_lruvec(), and both of those do know the right zone.
So check and set lruvec->zone in those; and remove the inadequate
attempt to set lruvec->zone from lruvec_init(), which is called before
NODE_DATA(node) has been allocated in such cases.
Ah, there was one exceptionr. For no particularly good reason,
mem_cgroup_force_empty_list() has its own code for deciding lruvec.
Change it to use the standard mem_cgroup_zone_lruvec() and
mem_cgroup_get_lru_size() too. In fact it was already safe against such
an oops (the lru lists in danger could only be empty), but we're better
proofed against future changes this way.
I've marked this for stable (3.6) since we introduced the problem in 3.5
(now closed to stable); but I have no idea if this is the only fix
needed to get memory hotadd working with memcg in 3.6, and received no
answer when I enquired twice before.
Reported-by: Tang Chen <tangchen@cn.fujitsu.com>
Signed-off-by: Hugh Dickins <hughd@google.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Konstantin Khlebnikov <khlebnikov@openvz.org>
Cc: Wen Congyang <wency@cn.fujitsu.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-11-17 05:14:54 +07:00
|
|
|
lruvec = &mz->lruvec;
|
|
|
|
out:
|
|
|
|
/*
|
|
|
|
* Since a node can be onlined after the mem_cgroup was created,
|
|
|
|
* we have to be prepared to initialize lruvec->zone here;
|
|
|
|
* and if offlined then reonlined, we need to reinitialize it.
|
|
|
|
*/
|
|
|
|
if (unlikely(lruvec->zone != zone))
|
|
|
|
lruvec->zone = zone;
|
|
|
|
return lruvec;
|
memcg: synchronized LRU
A big patch for changing memcg's LRU semantics.
Now,
- page_cgroup is linked to mem_cgroup's its own LRU (per zone).
- LRU of page_cgroup is not synchronous with global LRU.
- page and page_cgroup is one-to-one and statically allocated.
- To find page_cgroup is on what LRU, you have to check pc->mem_cgroup as
- lru = page_cgroup_zoneinfo(pc, nid_of_pc, zid_of_pc);
- SwapCache is handled.
And, when we handle LRU list of page_cgroup, we do following.
pc = lookup_page_cgroup(page);
lock_page_cgroup(pc); .....................(1)
mz = page_cgroup_zoneinfo(pc);
spin_lock(&mz->lru_lock);
.....add to LRU
spin_unlock(&mz->lru_lock);
unlock_page_cgroup(pc);
But (1) is spin_lock and we have to be afraid of dead-lock with zone->lru_lock.
So, trylock() is used at (1), now. Without (1), we can't trust "mz" is correct.
This is a trial to remove this dirty nesting of locks.
This patch changes mz->lru_lock to be zone->lru_lock.
Then, above sequence will be written as
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
mem_cgroup_add/remove/etc_lru() {
pc = lookup_page_cgroup(page);
mz = page_cgroup_zoneinfo(pc);
if (PageCgroupUsed(pc)) {
....add to LRU
}
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
This is much simpler.
(*) We're safe even if we don't take lock_page_cgroup(pc). Because..
1. When pc->mem_cgroup can be modified.
- at charge.
- at account_move().
2. at charge
the PCG_USED bit is not set before pc->mem_cgroup is fixed.
3. at account_move()
the page is isolated and not on LRU.
Pros.
- easy for maintenance.
- memcg can make use of laziness of pagevec.
- we don't have to duplicated LRU/Active/Unevictable bit in page_cgroup.
- LRU status of memcg will be synchronized with global LRU's one.
- # of locks are reduced.
- account_move() is simplified very much.
Cons.
- may increase cost of LRU rotation.
(no impact if memcg is not configured.)
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:08:01 +07:00
|
|
|
}
|
2008-10-19 10:26:14 +07:00
|
|
|
|
2012-01-13 08:18:15 +07:00
|
|
|
/**
|
2012-05-30 05:07:09 +07:00
|
|
|
* mem_cgroup_update_lru_size - account for adding or removing an lru page
|
|
|
|
* @lruvec: mem_cgroup per zone lru vector
|
|
|
|
* @lru: index of lru list the page is sitting on
|
|
|
|
* @nr_pages: positive when adding or negative when removing
|
2012-01-13 08:18:15 +07:00
|
|
|
*
|
2012-05-30 05:07:09 +07:00
|
|
|
* This function must be called when a page is added to or removed from an
|
|
|
|
* lru list.
|
2011-03-23 06:32:53 +07:00
|
|
|
*/
|
2012-05-30 05:07:09 +07:00
|
|
|
void mem_cgroup_update_lru_size(struct lruvec *lruvec, enum lru_list lru,
|
|
|
|
int nr_pages)
|
2011-03-23 06:32:53 +07:00
|
|
|
{
|
|
|
|
struct mem_cgroup_per_zone *mz;
|
2012-05-30 05:07:09 +07:00
|
|
|
unsigned long *lru_size;
|
2011-03-23 06:32:53 +07:00
|
|
|
|
|
|
|
if (mem_cgroup_disabled())
|
|
|
|
return;
|
|
|
|
|
2012-05-30 05:07:09 +07:00
|
|
|
mz = container_of(lruvec, struct mem_cgroup_per_zone, lruvec);
|
|
|
|
lru_size = mz->lru_size + lru;
|
|
|
|
*lru_size += nr_pages;
|
|
|
|
VM_BUG_ON((long)(*lru_size) < 0);
|
memcg: synchronized LRU
A big patch for changing memcg's LRU semantics.
Now,
- page_cgroup is linked to mem_cgroup's its own LRU (per zone).
- LRU of page_cgroup is not synchronous with global LRU.
- page and page_cgroup is one-to-one and statically allocated.
- To find page_cgroup is on what LRU, you have to check pc->mem_cgroup as
- lru = page_cgroup_zoneinfo(pc, nid_of_pc, zid_of_pc);
- SwapCache is handled.
And, when we handle LRU list of page_cgroup, we do following.
pc = lookup_page_cgroup(page);
lock_page_cgroup(pc); .....................(1)
mz = page_cgroup_zoneinfo(pc);
spin_lock(&mz->lru_lock);
.....add to LRU
spin_unlock(&mz->lru_lock);
unlock_page_cgroup(pc);
But (1) is spin_lock and we have to be afraid of dead-lock with zone->lru_lock.
So, trylock() is used at (1), now. Without (1), we can't trust "mz" is correct.
This is a trial to remove this dirty nesting of locks.
This patch changes mz->lru_lock to be zone->lru_lock.
Then, above sequence will be written as
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
mem_cgroup_add/remove/etc_lru() {
pc = lookup_page_cgroup(page);
mz = page_cgroup_zoneinfo(pc);
if (PageCgroupUsed(pc)) {
....add to LRU
}
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
This is much simpler.
(*) We're safe even if we don't take lock_page_cgroup(pc). Because..
1. When pc->mem_cgroup can be modified.
- at charge.
- at account_move().
2. at charge
the PCG_USED bit is not set before pc->mem_cgroup is fixed.
3. at account_move()
the page is isolated and not on LRU.
Pros.
- easy for maintenance.
- memcg can make use of laziness of pagevec.
- we don't have to duplicated LRU/Active/Unevictable bit in page_cgroup.
- LRU status of memcg will be synchronized with global LRU's one.
- # of locks are reduced.
- account_move() is simplified very much.
Cons.
- may increase cost of LRU rotation.
(no impact if memcg is not configured.)
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:08:01 +07:00
|
|
|
}
|
2009-01-08 09:08:34 +07:00
|
|
|
|
2011-07-27 06:08:29 +07:00
|
|
|
/*
|
2011-11-03 03:38:15 +07:00
|
|
|
* Checks whether given mem is same or in the root_mem_cgroup's
|
2011-07-27 06:08:29 +07:00
|
|
|
* hierarchy subtree
|
|
|
|
*/
|
2012-05-30 05:06:25 +07:00
|
|
|
bool __mem_cgroup_same_or_subtree(const struct mem_cgroup *root_memcg,
|
|
|
|
struct mem_cgroup *memcg)
|
2011-07-27 06:08:29 +07:00
|
|
|
{
|
2012-05-30 05:06:24 +07:00
|
|
|
if (root_memcg == memcg)
|
|
|
|
return true;
|
2012-06-21 02:52:58 +07:00
|
|
|
if (!root_memcg->use_hierarchy || !memcg)
|
2012-05-30 05:06:24 +07:00
|
|
|
return false;
|
2012-05-30 05:06:25 +07:00
|
|
|
return css_is_ancestor(&memcg->css, &root_memcg->css);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool mem_cgroup_same_or_subtree(const struct mem_cgroup *root_memcg,
|
|
|
|
struct mem_cgroup *memcg)
|
|
|
|
{
|
|
|
|
bool ret;
|
|
|
|
|
2012-05-30 05:06:24 +07:00
|
|
|
rcu_read_lock();
|
2012-05-30 05:06:25 +07:00
|
|
|
ret = __mem_cgroup_same_or_subtree(root_memcg, memcg);
|
2012-05-30 05:06:24 +07:00
|
|
|
rcu_read_unlock();
|
|
|
|
return ret;
|
2011-07-27 06:08:29 +07:00
|
|
|
}
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
int task_in_mem_cgroup(struct task_struct *task, const struct mem_cgroup *memcg)
|
2008-02-07 15:14:06 +07:00
|
|
|
{
|
|
|
|
int ret;
|
memcg: fix OOM killer under memcg
This patch tries to fix OOM Killer problems caused by hierarchy.
Now, memcg itself has OOM KILL function (in oom_kill.c) and tries to
kill a task in memcg.
But, when hierarchy is used, it's broken and correct task cannot
be killed. For example, in following cgroup
/groupA/ hierarchy=1, limit=1G,
01 nolimit
02 nolimit
All tasks' memory usage under /groupA, /groupA/01, groupA/02 is limited to
groupA's 1Gbytes but OOM Killer just kills tasks in groupA.
This patch provides makes the bad process be selected from all tasks
under hierarchy. BTW, currently, oom_jiffies is updated against groupA
in above case. oom_jiffies of tree should be updated.
To see how oom_jiffies is used, please check mem_cgroup_oom_called()
callers.
[akpm@linux-foundation.org: build fix]
[akpm@linux-foundation.org: const fix]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Paul Menage <menage@google.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: David Rientjes <rientjes@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-04-03 06:57:38 +07:00
|
|
|
struct mem_cgroup *curr = NULL;
|
2010-08-11 08:03:00 +07:00
|
|
|
struct task_struct *p;
|
2008-02-07 15:14:06 +07:00
|
|
|
|
2010-08-11 08:03:00 +07:00
|
|
|
p = find_lock_task_mm(task);
|
oom, memcg: fix exclusion of memcg threads after they have detached their mm
The oom killer relies on logic that identifies threads that have already
been oom killed when scanning the tasklist and, if found, deferring
until such threads have exited. This is done by checking for any
candidate threads that have the TIF_MEMDIE bit set.
For memcg ooms, candidate threads are first found by calling
task_in_mem_cgroup() since the oom killer should not defer if there's an
oom killed thread in another memcg.
Unfortunately, task_in_mem_cgroup() excludes threads if they have
detached their mm in the process of exiting so TIF_MEMDIE is never
detected for such conditions. This is different for global, mempolicy,
and cpuset oom conditions where a detached mm is only excluded after
checking for TIF_MEMDIE and deferring, if necessary, in
select_bad_process().
The fix is to return true if a task has a detached mm but is still in
the memcg or its hierarchy that is currently oom. This will allow the
oom killer to appropriately defer rather than kill unnecessarily or, in
the worst case, panic the machine if nothing else is available to kill.
Signed-off-by: David Rientjes <rientjes@google.com>
Acked-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Balbir Singh <bsingharora@gmail.com>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Michal Hocko <mhocko@suse.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:18:52 +07:00
|
|
|
if (p) {
|
|
|
|
curr = try_get_mem_cgroup_from_mm(p->mm);
|
|
|
|
task_unlock(p);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* All threads may have already detached their mm's, but the oom
|
|
|
|
* killer still needs to detect if they have already been oom
|
|
|
|
* killed to prevent needlessly killing additional tasks.
|
|
|
|
*/
|
|
|
|
task_lock(task);
|
|
|
|
curr = mem_cgroup_from_task(task);
|
|
|
|
if (curr)
|
|
|
|
css_get(&curr->css);
|
|
|
|
task_unlock(task);
|
|
|
|
}
|
memcg: fix OOM killer under memcg
This patch tries to fix OOM Killer problems caused by hierarchy.
Now, memcg itself has OOM KILL function (in oom_kill.c) and tries to
kill a task in memcg.
But, when hierarchy is used, it's broken and correct task cannot
be killed. For example, in following cgroup
/groupA/ hierarchy=1, limit=1G,
01 nolimit
02 nolimit
All tasks' memory usage under /groupA, /groupA/01, groupA/02 is limited to
groupA's 1Gbytes but OOM Killer just kills tasks in groupA.
This patch provides makes the bad process be selected from all tasks
under hierarchy. BTW, currently, oom_jiffies is updated against groupA
in above case. oom_jiffies of tree should be updated.
To see how oom_jiffies is used, please check mem_cgroup_oom_called()
callers.
[akpm@linux-foundation.org: build fix]
[akpm@linux-foundation.org: const fix]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Paul Menage <menage@google.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: David Rientjes <rientjes@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-04-03 06:57:38 +07:00
|
|
|
if (!curr)
|
|
|
|
return 0;
|
2009-12-16 07:47:12 +07:00
|
|
|
/*
|
2011-11-03 03:38:15 +07:00
|
|
|
* We should check use_hierarchy of "memcg" not "curr". Because checking
|
2009-12-16 07:47:12 +07:00
|
|
|
* use_hierarchy of "curr" here make this function true if hierarchy is
|
2011-11-03 03:38:15 +07:00
|
|
|
* enabled in "curr" and "curr" is a child of "memcg" in *cgroup*
|
|
|
|
* hierarchy(even if use_hierarchy is disabled in "memcg").
|
2009-12-16 07:47:12 +07:00
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
ret = mem_cgroup_same_or_subtree(memcg, curr);
|
memcg: fix OOM killer under memcg
This patch tries to fix OOM Killer problems caused by hierarchy.
Now, memcg itself has OOM KILL function (in oom_kill.c) and tries to
kill a task in memcg.
But, when hierarchy is used, it's broken and correct task cannot
be killed. For example, in following cgroup
/groupA/ hierarchy=1, limit=1G,
01 nolimit
02 nolimit
All tasks' memory usage under /groupA, /groupA/01, groupA/02 is limited to
groupA's 1Gbytes but OOM Killer just kills tasks in groupA.
This patch provides makes the bad process be selected from all tasks
under hierarchy. BTW, currently, oom_jiffies is updated against groupA
in above case. oom_jiffies of tree should be updated.
To see how oom_jiffies is used, please check mem_cgroup_oom_called()
callers.
[akpm@linux-foundation.org: build fix]
[akpm@linux-foundation.org: const fix]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Paul Menage <menage@google.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: David Rientjes <rientjes@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-04-03 06:57:38 +07:00
|
|
|
css_put(&curr->css);
|
2008-02-07 15:14:06 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-05-30 05:07:00 +07:00
|
|
|
int mem_cgroup_inactive_anon_is_low(struct lruvec *lruvec)
|
2009-01-08 09:08:18 +07:00
|
|
|
{
|
2011-11-03 03:38:23 +07:00
|
|
|
unsigned long inactive_ratio;
|
2009-01-08 09:08:18 +07:00
|
|
|
unsigned long inactive;
|
2011-11-03 03:38:23 +07:00
|
|
|
unsigned long active;
|
2009-01-08 09:08:25 +07:00
|
|
|
unsigned long gb;
|
2009-01-08 09:08:18 +07:00
|
|
|
|
2012-05-30 05:07:08 +07:00
|
|
|
inactive = mem_cgroup_get_lru_size(lruvec, LRU_INACTIVE_ANON);
|
|
|
|
active = mem_cgroup_get_lru_size(lruvec, LRU_ACTIVE_ANON);
|
2009-01-08 09:08:18 +07:00
|
|
|
|
2009-01-08 09:08:25 +07:00
|
|
|
gb = (inactive + active) >> (30 - PAGE_SHIFT);
|
|
|
|
if (gb)
|
|
|
|
inactive_ratio = int_sqrt(10 * gb);
|
|
|
|
else
|
|
|
|
inactive_ratio = 1;
|
|
|
|
|
2011-11-03 03:38:23 +07:00
|
|
|
return inactive * inactive_ratio < active;
|
2009-01-08 09:08:18 +07:00
|
|
|
}
|
|
|
|
|
2012-05-30 05:07:00 +07:00
|
|
|
int mem_cgroup_inactive_file_is_low(struct lruvec *lruvec)
|
2009-06-17 05:32:28 +07:00
|
|
|
{
|
|
|
|
unsigned long active;
|
|
|
|
unsigned long inactive;
|
|
|
|
|
2012-05-30 05:07:08 +07:00
|
|
|
inactive = mem_cgroup_get_lru_size(lruvec, LRU_INACTIVE_FILE);
|
|
|
|
active = mem_cgroup_get_lru_size(lruvec, LRU_ACTIVE_FILE);
|
2009-06-17 05:32:28 +07:00
|
|
|
|
|
|
|
return (active > inactive);
|
|
|
|
}
|
|
|
|
|
2009-01-08 09:08:06 +07:00
|
|
|
#define mem_cgroup_from_res_counter(counter, member) \
|
|
|
|
container_of(counter, struct mem_cgroup, member)
|
|
|
|
|
2011-02-02 06:52:43 +07:00
|
|
|
/**
|
2011-03-24 06:42:21 +07:00
|
|
|
* mem_cgroup_margin - calculate chargeable space of a memory cgroup
|
2012-06-21 02:53:01 +07:00
|
|
|
* @memcg: the memory cgroup
|
2011-02-02 06:52:43 +07:00
|
|
|
*
|
2011-03-24 06:42:21 +07:00
|
|
|
* Returns the maximum amount of memory @mem can be charged with, in
|
2011-03-24 06:42:36 +07:00
|
|
|
* pages.
|
2011-02-02 06:52:43 +07:00
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
static unsigned long mem_cgroup_margin(struct mem_cgroup *memcg)
|
2011-02-02 06:52:43 +07:00
|
|
|
{
|
2011-03-24 06:42:21 +07:00
|
|
|
unsigned long long margin;
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
margin = res_counter_margin(&memcg->res);
|
2011-03-24 06:42:21 +07:00
|
|
|
if (do_swap_account)
|
2011-11-03 03:38:15 +07:00
|
|
|
margin = min(margin, res_counter_margin(&memcg->memsw));
|
2011-03-24 06:42:36 +07:00
|
|
|
return margin >> PAGE_SHIFT;
|
2011-02-02 06:52:43 +07:00
|
|
|
}
|
|
|
|
|
2011-07-27 06:08:21 +07:00
|
|
|
int mem_cgroup_swappiness(struct mem_cgroup *memcg)
|
2009-01-08 09:08:24 +07:00
|
|
|
{
|
|
|
|
struct cgroup *cgrp = memcg->css.cgroup;
|
|
|
|
|
|
|
|
/* root ? */
|
|
|
|
if (cgrp->parent == NULL)
|
|
|
|
return vm_swappiness;
|
|
|
|
|
2011-03-24 06:42:32 +07:00
|
|
|
return memcg->swappiness;
|
2009-01-08 09:08:24 +07:00
|
|
|
}
|
|
|
|
|
2012-03-22 06:34:23 +07:00
|
|
|
/*
|
|
|
|
* memcg->moving_account is used for checking possibility that some thread is
|
|
|
|
* calling move_account(). When a thread on CPU-A starts moving pages under
|
|
|
|
* a memcg, other threads should check memcg->moving_account under
|
|
|
|
* rcu_read_lock(), like this:
|
|
|
|
*
|
|
|
|
* CPU-A CPU-B
|
|
|
|
* rcu_read_lock()
|
|
|
|
* memcg->moving_account+1 if (memcg->mocing_account)
|
|
|
|
* take heavy locks.
|
|
|
|
* synchronize_rcu() update something.
|
|
|
|
* rcu_read_unlock()
|
|
|
|
* start move here.
|
|
|
|
*/
|
2012-03-22 06:34:26 +07:00
|
|
|
|
|
|
|
/* for quick checking without looking up memcg */
|
|
|
|
atomic_t memcg_moving __read_mostly;
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static void mem_cgroup_start_move(struct mem_cgroup *memcg)
|
memcg: avoid lock in updating file_mapped (Was fix race in file_mapped accouting flag management
At accounting file events per memory cgroup, we need to find memory cgroup
via page_cgroup->mem_cgroup. Now, we use lock_page_cgroup() for guarantee
pc->mem_cgroup is not overwritten while we make use of it.
But, considering the context which page-cgroup for files are accessed,
we can use alternative light-weight mutual execusion in the most case.
At handling file-caches, the only race we have to take care of is "moving"
account, IOW, overwriting page_cgroup->mem_cgroup. (See comment in the
patch)
Unlike charge/uncharge, "move" happens not so frequently. It happens only when
rmdir() and task-moving (with a special settings.)
This patch adds a race-checker for file-cache-status accounting v.s. account
moving. The new per-cpu-per-memcg counter MEM_CGROUP_ON_MOVE is added.
The routine for account move
1. Increment it before start moving
2. Call synchronize_rcu()
3. Decrement it after the end of moving.
By this, file-status-counting routine can check it needs to call
lock_page_cgroup(). In most case, I doesn't need to call it.
Following is a perf data of a process which mmap()/munmap 32MB of file cache
in a minute.
Before patch:
28.25% mmap mmap [.] main
22.64% mmap [kernel.kallsyms] [k] page_fault
9.96% mmap [kernel.kallsyms] [k] mem_cgroup_update_file_mapped
3.67% mmap [kernel.kallsyms] [k] filemap_fault
3.50% mmap [kernel.kallsyms] [k] unmap_vmas
2.99% mmap [kernel.kallsyms] [k] __do_fault
2.76% mmap [kernel.kallsyms] [k] find_get_page
After patch:
30.00% mmap mmap [.] main
23.78% mmap [kernel.kallsyms] [k] page_fault
5.52% mmap [kernel.kallsyms] [k] mem_cgroup_update_file_mapped
3.81% mmap [kernel.kallsyms] [k] unmap_vmas
3.26% mmap [kernel.kallsyms] [k] find_get_page
3.18% mmap [kernel.kallsyms] [k] __do_fault
3.03% mmap [kernel.kallsyms] [k] filemap_fault
2.40% mmap [kernel.kallsyms] [k] handle_mm_fault
2.40% mmap [kernel.kallsyms] [k] do_page_fault
This patch reduces memcg's cost to some extent.
(mem_cgroup_update_file_mapped is called by both of map/unmap)
Note: It seems some more improvements are required..but no idea.
maybe removing set/unset flag is required.
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Greg Thelen <gthelen@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-10-28 05:33:40 +07:00
|
|
|
{
|
2012-03-22 06:34:26 +07:00
|
|
|
atomic_inc(&memcg_moving);
|
2012-03-22 06:34:23 +07:00
|
|
|
atomic_inc(&memcg->moving_account);
|
memcg: avoid lock in updating file_mapped (Was fix race in file_mapped accouting flag management
At accounting file events per memory cgroup, we need to find memory cgroup
via page_cgroup->mem_cgroup. Now, we use lock_page_cgroup() for guarantee
pc->mem_cgroup is not overwritten while we make use of it.
But, considering the context which page-cgroup for files are accessed,
we can use alternative light-weight mutual execusion in the most case.
At handling file-caches, the only race we have to take care of is "moving"
account, IOW, overwriting page_cgroup->mem_cgroup. (See comment in the
patch)
Unlike charge/uncharge, "move" happens not so frequently. It happens only when
rmdir() and task-moving (with a special settings.)
This patch adds a race-checker for file-cache-status accounting v.s. account
moving. The new per-cpu-per-memcg counter MEM_CGROUP_ON_MOVE is added.
The routine for account move
1. Increment it before start moving
2. Call synchronize_rcu()
3. Decrement it after the end of moving.
By this, file-status-counting routine can check it needs to call
lock_page_cgroup(). In most case, I doesn't need to call it.
Following is a perf data of a process which mmap()/munmap 32MB of file cache
in a minute.
Before patch:
28.25% mmap mmap [.] main
22.64% mmap [kernel.kallsyms] [k] page_fault
9.96% mmap [kernel.kallsyms] [k] mem_cgroup_update_file_mapped
3.67% mmap [kernel.kallsyms] [k] filemap_fault
3.50% mmap [kernel.kallsyms] [k] unmap_vmas
2.99% mmap [kernel.kallsyms] [k] __do_fault
2.76% mmap [kernel.kallsyms] [k] find_get_page
After patch:
30.00% mmap mmap [.] main
23.78% mmap [kernel.kallsyms] [k] page_fault
5.52% mmap [kernel.kallsyms] [k] mem_cgroup_update_file_mapped
3.81% mmap [kernel.kallsyms] [k] unmap_vmas
3.26% mmap [kernel.kallsyms] [k] find_get_page
3.18% mmap [kernel.kallsyms] [k] __do_fault
3.03% mmap [kernel.kallsyms] [k] filemap_fault
2.40% mmap [kernel.kallsyms] [k] handle_mm_fault
2.40% mmap [kernel.kallsyms] [k] do_page_fault
This patch reduces memcg's cost to some extent.
(mem_cgroup_update_file_mapped is called by both of map/unmap)
Note: It seems some more improvements are required..but no idea.
maybe removing set/unset flag is required.
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Greg Thelen <gthelen@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-10-28 05:33:40 +07:00
|
|
|
synchronize_rcu();
|
|
|
|
}
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static void mem_cgroup_end_move(struct mem_cgroup *memcg)
|
memcg: avoid lock in updating file_mapped (Was fix race in file_mapped accouting flag management
At accounting file events per memory cgroup, we need to find memory cgroup
via page_cgroup->mem_cgroup. Now, we use lock_page_cgroup() for guarantee
pc->mem_cgroup is not overwritten while we make use of it.
But, considering the context which page-cgroup for files are accessed,
we can use alternative light-weight mutual execusion in the most case.
At handling file-caches, the only race we have to take care of is "moving"
account, IOW, overwriting page_cgroup->mem_cgroup. (See comment in the
patch)
Unlike charge/uncharge, "move" happens not so frequently. It happens only when
rmdir() and task-moving (with a special settings.)
This patch adds a race-checker for file-cache-status accounting v.s. account
moving. The new per-cpu-per-memcg counter MEM_CGROUP_ON_MOVE is added.
The routine for account move
1. Increment it before start moving
2. Call synchronize_rcu()
3. Decrement it after the end of moving.
By this, file-status-counting routine can check it needs to call
lock_page_cgroup(). In most case, I doesn't need to call it.
Following is a perf data of a process which mmap()/munmap 32MB of file cache
in a minute.
Before patch:
28.25% mmap mmap [.] main
22.64% mmap [kernel.kallsyms] [k] page_fault
9.96% mmap [kernel.kallsyms] [k] mem_cgroup_update_file_mapped
3.67% mmap [kernel.kallsyms] [k] filemap_fault
3.50% mmap [kernel.kallsyms] [k] unmap_vmas
2.99% mmap [kernel.kallsyms] [k] __do_fault
2.76% mmap [kernel.kallsyms] [k] find_get_page
After patch:
30.00% mmap mmap [.] main
23.78% mmap [kernel.kallsyms] [k] page_fault
5.52% mmap [kernel.kallsyms] [k] mem_cgroup_update_file_mapped
3.81% mmap [kernel.kallsyms] [k] unmap_vmas
3.26% mmap [kernel.kallsyms] [k] find_get_page
3.18% mmap [kernel.kallsyms] [k] __do_fault
3.03% mmap [kernel.kallsyms] [k] filemap_fault
2.40% mmap [kernel.kallsyms] [k] handle_mm_fault
2.40% mmap [kernel.kallsyms] [k] do_page_fault
This patch reduces memcg's cost to some extent.
(mem_cgroup_update_file_mapped is called by both of map/unmap)
Note: It seems some more improvements are required..but no idea.
maybe removing set/unset flag is required.
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Greg Thelen <gthelen@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-10-28 05:33:40 +07:00
|
|
|
{
|
2012-03-22 06:34:23 +07:00
|
|
|
/*
|
|
|
|
* Now, mem_cgroup_clear_mc() may call this function with NULL.
|
|
|
|
* We check NULL in callee rather than caller.
|
|
|
|
*/
|
2012-03-22 06:34:26 +07:00
|
|
|
if (memcg) {
|
|
|
|
atomic_dec(&memcg_moving);
|
2012-03-22 06:34:23 +07:00
|
|
|
atomic_dec(&memcg->moving_account);
|
2012-03-22 06:34:26 +07:00
|
|
|
}
|
memcg: avoid lock in updating file_mapped (Was fix race in file_mapped accouting flag management
At accounting file events per memory cgroup, we need to find memory cgroup
via page_cgroup->mem_cgroup. Now, we use lock_page_cgroup() for guarantee
pc->mem_cgroup is not overwritten while we make use of it.
But, considering the context which page-cgroup for files are accessed,
we can use alternative light-weight mutual execusion in the most case.
At handling file-caches, the only race we have to take care of is "moving"
account, IOW, overwriting page_cgroup->mem_cgroup. (See comment in the
patch)
Unlike charge/uncharge, "move" happens not so frequently. It happens only when
rmdir() and task-moving (with a special settings.)
This patch adds a race-checker for file-cache-status accounting v.s. account
moving. The new per-cpu-per-memcg counter MEM_CGROUP_ON_MOVE is added.
The routine for account move
1. Increment it before start moving
2. Call synchronize_rcu()
3. Decrement it after the end of moving.
By this, file-status-counting routine can check it needs to call
lock_page_cgroup(). In most case, I doesn't need to call it.
Following is a perf data of a process which mmap()/munmap 32MB of file cache
in a minute.
Before patch:
28.25% mmap mmap [.] main
22.64% mmap [kernel.kallsyms] [k] page_fault
9.96% mmap [kernel.kallsyms] [k] mem_cgroup_update_file_mapped
3.67% mmap [kernel.kallsyms] [k] filemap_fault
3.50% mmap [kernel.kallsyms] [k] unmap_vmas
2.99% mmap [kernel.kallsyms] [k] __do_fault
2.76% mmap [kernel.kallsyms] [k] find_get_page
After patch:
30.00% mmap mmap [.] main
23.78% mmap [kernel.kallsyms] [k] page_fault
5.52% mmap [kernel.kallsyms] [k] mem_cgroup_update_file_mapped
3.81% mmap [kernel.kallsyms] [k] unmap_vmas
3.26% mmap [kernel.kallsyms] [k] find_get_page
3.18% mmap [kernel.kallsyms] [k] __do_fault
3.03% mmap [kernel.kallsyms] [k] filemap_fault
2.40% mmap [kernel.kallsyms] [k] handle_mm_fault
2.40% mmap [kernel.kallsyms] [k] do_page_fault
This patch reduces memcg's cost to some extent.
(mem_cgroup_update_file_mapped is called by both of map/unmap)
Note: It seems some more improvements are required..but no idea.
maybe removing set/unset flag is required.
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Greg Thelen <gthelen@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-10-28 05:33:40 +07:00
|
|
|
}
|
2012-03-22 06:34:23 +07:00
|
|
|
|
memcg: avoid lock in updating file_mapped (Was fix race in file_mapped accouting flag management
At accounting file events per memory cgroup, we need to find memory cgroup
via page_cgroup->mem_cgroup. Now, we use lock_page_cgroup() for guarantee
pc->mem_cgroup is not overwritten while we make use of it.
But, considering the context which page-cgroup for files are accessed,
we can use alternative light-weight mutual execusion in the most case.
At handling file-caches, the only race we have to take care of is "moving"
account, IOW, overwriting page_cgroup->mem_cgroup. (See comment in the
patch)
Unlike charge/uncharge, "move" happens not so frequently. It happens only when
rmdir() and task-moving (with a special settings.)
This patch adds a race-checker for file-cache-status accounting v.s. account
moving. The new per-cpu-per-memcg counter MEM_CGROUP_ON_MOVE is added.
The routine for account move
1. Increment it before start moving
2. Call synchronize_rcu()
3. Decrement it after the end of moving.
By this, file-status-counting routine can check it needs to call
lock_page_cgroup(). In most case, I doesn't need to call it.
Following is a perf data of a process which mmap()/munmap 32MB of file cache
in a minute.
Before patch:
28.25% mmap mmap [.] main
22.64% mmap [kernel.kallsyms] [k] page_fault
9.96% mmap [kernel.kallsyms] [k] mem_cgroup_update_file_mapped
3.67% mmap [kernel.kallsyms] [k] filemap_fault
3.50% mmap [kernel.kallsyms] [k] unmap_vmas
2.99% mmap [kernel.kallsyms] [k] __do_fault
2.76% mmap [kernel.kallsyms] [k] find_get_page
After patch:
30.00% mmap mmap [.] main
23.78% mmap [kernel.kallsyms] [k] page_fault
5.52% mmap [kernel.kallsyms] [k] mem_cgroup_update_file_mapped
3.81% mmap [kernel.kallsyms] [k] unmap_vmas
3.26% mmap [kernel.kallsyms] [k] find_get_page
3.18% mmap [kernel.kallsyms] [k] __do_fault
3.03% mmap [kernel.kallsyms] [k] filemap_fault
2.40% mmap [kernel.kallsyms] [k] handle_mm_fault
2.40% mmap [kernel.kallsyms] [k] do_page_fault
This patch reduces memcg's cost to some extent.
(mem_cgroup_update_file_mapped is called by both of map/unmap)
Note: It seems some more improvements are required..but no idea.
maybe removing set/unset flag is required.
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Greg Thelen <gthelen@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-10-28 05:33:40 +07:00
|
|
|
/*
|
|
|
|
* 2 routines for checking "mem" is under move_account() or not.
|
|
|
|
*
|
2012-03-22 06:34:26 +07:00
|
|
|
* mem_cgroup_stolen() - checking whether a cgroup is mc.from or not. This
|
|
|
|
* is used for avoiding races in accounting. If true,
|
memcg: avoid lock in updating file_mapped (Was fix race in file_mapped accouting flag management
At accounting file events per memory cgroup, we need to find memory cgroup
via page_cgroup->mem_cgroup. Now, we use lock_page_cgroup() for guarantee
pc->mem_cgroup is not overwritten while we make use of it.
But, considering the context which page-cgroup for files are accessed,
we can use alternative light-weight mutual execusion in the most case.
At handling file-caches, the only race we have to take care of is "moving"
account, IOW, overwriting page_cgroup->mem_cgroup. (See comment in the
patch)
Unlike charge/uncharge, "move" happens not so frequently. It happens only when
rmdir() and task-moving (with a special settings.)
This patch adds a race-checker for file-cache-status accounting v.s. account
moving. The new per-cpu-per-memcg counter MEM_CGROUP_ON_MOVE is added.
The routine for account move
1. Increment it before start moving
2. Call synchronize_rcu()
3. Decrement it after the end of moving.
By this, file-status-counting routine can check it needs to call
lock_page_cgroup(). In most case, I doesn't need to call it.
Following is a perf data of a process which mmap()/munmap 32MB of file cache
in a minute.
Before patch:
28.25% mmap mmap [.] main
22.64% mmap [kernel.kallsyms] [k] page_fault
9.96% mmap [kernel.kallsyms] [k] mem_cgroup_update_file_mapped
3.67% mmap [kernel.kallsyms] [k] filemap_fault
3.50% mmap [kernel.kallsyms] [k] unmap_vmas
2.99% mmap [kernel.kallsyms] [k] __do_fault
2.76% mmap [kernel.kallsyms] [k] find_get_page
After patch:
30.00% mmap mmap [.] main
23.78% mmap [kernel.kallsyms] [k] page_fault
5.52% mmap [kernel.kallsyms] [k] mem_cgroup_update_file_mapped
3.81% mmap [kernel.kallsyms] [k] unmap_vmas
3.26% mmap [kernel.kallsyms] [k] find_get_page
3.18% mmap [kernel.kallsyms] [k] __do_fault
3.03% mmap [kernel.kallsyms] [k] filemap_fault
2.40% mmap [kernel.kallsyms] [k] handle_mm_fault
2.40% mmap [kernel.kallsyms] [k] do_page_fault
This patch reduces memcg's cost to some extent.
(mem_cgroup_update_file_mapped is called by both of map/unmap)
Note: It seems some more improvements are required..but no idea.
maybe removing set/unset flag is required.
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Greg Thelen <gthelen@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-10-28 05:33:40 +07:00
|
|
|
* pc->mem_cgroup may be overwritten.
|
|
|
|
*
|
|
|
|
* mem_cgroup_under_move() - checking a cgroup is mc.from or mc.to or
|
|
|
|
* under hierarchy of moving cgroups. This is for
|
|
|
|
* waiting at hith-memory prressure caused by "move".
|
|
|
|
*/
|
|
|
|
|
2012-03-22 06:34:26 +07:00
|
|
|
static bool mem_cgroup_stolen(struct mem_cgroup *memcg)
|
memcg: avoid lock in updating file_mapped (Was fix race in file_mapped accouting flag management
At accounting file events per memory cgroup, we need to find memory cgroup
via page_cgroup->mem_cgroup. Now, we use lock_page_cgroup() for guarantee
pc->mem_cgroup is not overwritten while we make use of it.
But, considering the context which page-cgroup for files are accessed,
we can use alternative light-weight mutual execusion in the most case.
At handling file-caches, the only race we have to take care of is "moving"
account, IOW, overwriting page_cgroup->mem_cgroup. (See comment in the
patch)
Unlike charge/uncharge, "move" happens not so frequently. It happens only when
rmdir() and task-moving (with a special settings.)
This patch adds a race-checker for file-cache-status accounting v.s. account
moving. The new per-cpu-per-memcg counter MEM_CGROUP_ON_MOVE is added.
The routine for account move
1. Increment it before start moving
2. Call synchronize_rcu()
3. Decrement it after the end of moving.
By this, file-status-counting routine can check it needs to call
lock_page_cgroup(). In most case, I doesn't need to call it.
Following is a perf data of a process which mmap()/munmap 32MB of file cache
in a minute.
Before patch:
28.25% mmap mmap [.] main
22.64% mmap [kernel.kallsyms] [k] page_fault
9.96% mmap [kernel.kallsyms] [k] mem_cgroup_update_file_mapped
3.67% mmap [kernel.kallsyms] [k] filemap_fault
3.50% mmap [kernel.kallsyms] [k] unmap_vmas
2.99% mmap [kernel.kallsyms] [k] __do_fault
2.76% mmap [kernel.kallsyms] [k] find_get_page
After patch:
30.00% mmap mmap [.] main
23.78% mmap [kernel.kallsyms] [k] page_fault
5.52% mmap [kernel.kallsyms] [k] mem_cgroup_update_file_mapped
3.81% mmap [kernel.kallsyms] [k] unmap_vmas
3.26% mmap [kernel.kallsyms] [k] find_get_page
3.18% mmap [kernel.kallsyms] [k] __do_fault
3.03% mmap [kernel.kallsyms] [k] filemap_fault
2.40% mmap [kernel.kallsyms] [k] handle_mm_fault
2.40% mmap [kernel.kallsyms] [k] do_page_fault
This patch reduces memcg's cost to some extent.
(mem_cgroup_update_file_mapped is called by both of map/unmap)
Note: It seems some more improvements are required..but no idea.
maybe removing set/unset flag is required.
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Greg Thelen <gthelen@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-10-28 05:33:40 +07:00
|
|
|
{
|
|
|
|
VM_BUG_ON(!rcu_read_lock_held());
|
2012-03-22 06:34:23 +07:00
|
|
|
return atomic_read(&memcg->moving_account) > 0;
|
memcg: avoid lock in updating file_mapped (Was fix race in file_mapped accouting flag management
At accounting file events per memory cgroup, we need to find memory cgroup
via page_cgroup->mem_cgroup. Now, we use lock_page_cgroup() for guarantee
pc->mem_cgroup is not overwritten while we make use of it.
But, considering the context which page-cgroup for files are accessed,
we can use alternative light-weight mutual execusion in the most case.
At handling file-caches, the only race we have to take care of is "moving"
account, IOW, overwriting page_cgroup->mem_cgroup. (See comment in the
patch)
Unlike charge/uncharge, "move" happens not so frequently. It happens only when
rmdir() and task-moving (with a special settings.)
This patch adds a race-checker for file-cache-status accounting v.s. account
moving. The new per-cpu-per-memcg counter MEM_CGROUP_ON_MOVE is added.
The routine for account move
1. Increment it before start moving
2. Call synchronize_rcu()
3. Decrement it after the end of moving.
By this, file-status-counting routine can check it needs to call
lock_page_cgroup(). In most case, I doesn't need to call it.
Following is a perf data of a process which mmap()/munmap 32MB of file cache
in a minute.
Before patch:
28.25% mmap mmap [.] main
22.64% mmap [kernel.kallsyms] [k] page_fault
9.96% mmap [kernel.kallsyms] [k] mem_cgroup_update_file_mapped
3.67% mmap [kernel.kallsyms] [k] filemap_fault
3.50% mmap [kernel.kallsyms] [k] unmap_vmas
2.99% mmap [kernel.kallsyms] [k] __do_fault
2.76% mmap [kernel.kallsyms] [k] find_get_page
After patch:
30.00% mmap mmap [.] main
23.78% mmap [kernel.kallsyms] [k] page_fault
5.52% mmap [kernel.kallsyms] [k] mem_cgroup_update_file_mapped
3.81% mmap [kernel.kallsyms] [k] unmap_vmas
3.26% mmap [kernel.kallsyms] [k] find_get_page
3.18% mmap [kernel.kallsyms] [k] __do_fault
3.03% mmap [kernel.kallsyms] [k] filemap_fault
2.40% mmap [kernel.kallsyms] [k] handle_mm_fault
2.40% mmap [kernel.kallsyms] [k] do_page_fault
This patch reduces memcg's cost to some extent.
(mem_cgroup_update_file_mapped is called by both of map/unmap)
Note: It seems some more improvements are required..but no idea.
maybe removing set/unset flag is required.
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Greg Thelen <gthelen@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-10-28 05:33:40 +07:00
|
|
|
}
|
2010-08-11 08:02:57 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static bool mem_cgroup_under_move(struct mem_cgroup *memcg)
|
2010-08-11 08:02:57 +07:00
|
|
|
{
|
2010-08-11 08:02:58 +07:00
|
|
|
struct mem_cgroup *from;
|
|
|
|
struct mem_cgroup *to;
|
2010-08-11 08:02:57 +07:00
|
|
|
bool ret = false;
|
2010-08-11 08:02:58 +07:00
|
|
|
/*
|
|
|
|
* Unlike task_move routines, we access mc.to, mc.from not under
|
|
|
|
* mutual exclusion by cgroup_mutex. Here, we take spinlock instead.
|
|
|
|
*/
|
|
|
|
spin_lock(&mc.lock);
|
|
|
|
from = mc.from;
|
|
|
|
to = mc.to;
|
|
|
|
if (!from)
|
|
|
|
goto unlock;
|
2011-07-27 06:08:29 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
ret = mem_cgroup_same_or_subtree(memcg, from)
|
|
|
|
|| mem_cgroup_same_or_subtree(memcg, to);
|
2010-08-11 08:02:58 +07:00
|
|
|
unlock:
|
|
|
|
spin_unlock(&mc.lock);
|
2010-08-11 08:02:57 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static bool mem_cgroup_wait_acct_move(struct mem_cgroup *memcg)
|
2010-08-11 08:02:57 +07:00
|
|
|
{
|
|
|
|
if (mc.moving_task && current != mc.moving_task) {
|
2011-11-03 03:38:15 +07:00
|
|
|
if (mem_cgroup_under_move(memcg)) {
|
2010-08-11 08:02:57 +07:00
|
|
|
DEFINE_WAIT(wait);
|
|
|
|
prepare_to_wait(&mc.waitq, &wait, TASK_INTERRUPTIBLE);
|
|
|
|
/* moving charge context might have finished. */
|
|
|
|
if (mc.moving_task)
|
|
|
|
schedule();
|
|
|
|
finish_wait(&mc.waitq, &wait);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-03-22 06:34:24 +07:00
|
|
|
/*
|
|
|
|
* Take this lock when
|
|
|
|
* - a code tries to modify page's memcg while it's USED.
|
|
|
|
* - a code tries to modify page state accounting in a memcg.
|
2012-03-22 06:34:26 +07:00
|
|
|
* see mem_cgroup_stolen(), too.
|
2012-03-22 06:34:24 +07:00
|
|
|
*/
|
|
|
|
static void move_lock_mem_cgroup(struct mem_cgroup *memcg,
|
|
|
|
unsigned long *flags)
|
|
|
|
{
|
|
|
|
spin_lock_irqsave(&memcg->move_lock, *flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void move_unlock_mem_cgroup(struct mem_cgroup *memcg,
|
|
|
|
unsigned long *flags)
|
|
|
|
{
|
|
|
|
spin_unlock_irqrestore(&memcg->move_lock, *flags);
|
|
|
|
}
|
|
|
|
|
2009-04-03 06:57:39 +07:00
|
|
|
/**
|
2010-03-11 06:22:25 +07:00
|
|
|
* mem_cgroup_print_oom_info: Called from OOM with tasklist_lock held in read mode.
|
2009-04-03 06:57:39 +07:00
|
|
|
* @memcg: The memory cgroup that went over limit
|
|
|
|
* @p: Task that is going to be killed
|
|
|
|
*
|
|
|
|
* NOTE: @memcg and @p's mem_cgroup can be different when hierarchy is
|
|
|
|
* enabled
|
|
|
|
*/
|
|
|
|
void mem_cgroup_print_oom_info(struct mem_cgroup *memcg, struct task_struct *p)
|
|
|
|
{
|
|
|
|
struct cgroup *task_cgrp;
|
|
|
|
struct cgroup *mem_cgrp;
|
|
|
|
/*
|
|
|
|
* Need a buffer in BSS, can't rely on allocations. The code relies
|
|
|
|
* on the assumption that OOM is serialized for memory controller.
|
|
|
|
* If this assumption is broken, revisit this code.
|
|
|
|
*/
|
|
|
|
static char memcg_name[PATH_MAX];
|
|
|
|
int ret;
|
|
|
|
|
2009-12-16 07:47:12 +07:00
|
|
|
if (!memcg || !p)
|
2009-04-03 06:57:39 +07:00
|
|
|
return;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
|
|
|
|
mem_cgrp = memcg->css.cgroup;
|
|
|
|
task_cgrp = task_cgroup(p, mem_cgroup_subsys_id);
|
|
|
|
|
|
|
|
ret = cgroup_path(task_cgrp, memcg_name, PATH_MAX);
|
|
|
|
if (ret < 0) {
|
|
|
|
/*
|
|
|
|
* Unfortunately, we are unable to convert to a useful name
|
|
|
|
* But we'll still print out the usage information
|
|
|
|
*/
|
|
|
|
rcu_read_unlock();
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
printk(KERN_INFO "Task in %s killed", memcg_name);
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
ret = cgroup_path(mem_cgrp, memcg_name, PATH_MAX);
|
|
|
|
if (ret < 0) {
|
|
|
|
rcu_read_unlock();
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Continues from above, so we don't need an KERN_ level
|
|
|
|
*/
|
|
|
|
printk(KERN_CONT " as a result of limit of %s\n", memcg_name);
|
|
|
|
done:
|
|
|
|
|
|
|
|
printk(KERN_INFO "memory: usage %llukB, limit %llukB, failcnt %llu\n",
|
|
|
|
res_counter_read_u64(&memcg->res, RES_USAGE) >> 10,
|
|
|
|
res_counter_read_u64(&memcg->res, RES_LIMIT) >> 10,
|
|
|
|
res_counter_read_u64(&memcg->res, RES_FAILCNT));
|
|
|
|
printk(KERN_INFO "memory+swap: usage %llukB, limit %llukB, "
|
|
|
|
"failcnt %llu\n",
|
|
|
|
res_counter_read_u64(&memcg->memsw, RES_USAGE) >> 10,
|
|
|
|
res_counter_read_u64(&memcg->memsw, RES_LIMIT) >> 10,
|
|
|
|
res_counter_read_u64(&memcg->memsw, RES_FAILCNT));
|
|
|
|
}
|
|
|
|
|
2009-04-03 06:57:36 +07:00
|
|
|
/*
|
|
|
|
* This function returns the number of memcg under hierarchy tree. Returns
|
|
|
|
* 1(self count) if no children.
|
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
static int mem_cgroup_count_children(struct mem_cgroup *memcg)
|
2009-04-03 06:57:36 +07:00
|
|
|
{
|
|
|
|
int num = 0;
|
2010-10-28 05:33:41 +07:00
|
|
|
struct mem_cgroup *iter;
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
for_each_mem_cgroup_tree(iter, memcg)
|
2010-10-28 05:33:41 +07:00
|
|
|
num++;
|
2009-04-03 06:57:36 +07:00
|
|
|
return num;
|
|
|
|
}
|
|
|
|
|
oom: badness heuristic rewrite
This a complete rewrite of the oom killer's badness() heuristic which is
used to determine which task to kill in oom conditions. The goal is to
make it as simple and predictable as possible so the results are better
understood and we end up killing the task which will lead to the most
memory freeing while still respecting the fine-tuning from userspace.
Instead of basing the heuristic on mm->total_vm for each task, the task's
rss and swap space is used instead. This is a better indication of the
amount of memory that will be freeable if the oom killed task is chosen
and subsequently exits. This helps specifically in cases where KDE or
GNOME is chosen for oom kill on desktop systems instead of a memory
hogging task.
The baseline for the heuristic is a proportion of memory that each task is
currently using in memory plus swap compared to the amount of "allowable"
memory. "Allowable," in this sense, means the system-wide resources for
unconstrained oom conditions, the set of mempolicy nodes, the mems
attached to current's cpuset, or a memory controller's limit. The
proportion is given on a scale of 0 (never kill) to 1000 (always kill),
roughly meaning that if a task has a badness() score of 500 that the task
consumes approximately 50% of allowable memory resident in RAM or in swap
space.
The proportion is always relative to the amount of "allowable" memory and
not the total amount of RAM systemwide so that mempolicies and cpusets may
operate in isolation; they shall not need to know the true size of the
machine on which they are running if they are bound to a specific set of
nodes or mems, respectively.
Root tasks are given 3% extra memory just like __vm_enough_memory()
provides in LSMs. In the event of two tasks consuming similar amounts of
memory, it is generally better to save root's task.
Because of the change in the badness() heuristic's baseline, it is also
necessary to introduce a new user interface to tune it. It's not possible
to redefine the meaning of /proc/pid/oom_adj with a new scale since the
ABI cannot be changed for backward compatability. Instead, a new tunable,
/proc/pid/oom_score_adj, is added that ranges from -1000 to +1000. It may
be used to polarize the heuristic such that certain tasks are never
considered for oom kill while others may always be considered. The value
is added directly into the badness() score so a value of -500, for
example, means to discount 50% of its memory consumption in comparison to
other tasks either on the system, bound to the mempolicy, in the cpuset,
or sharing the same memory controller.
/proc/pid/oom_adj is changed so that its meaning is rescaled into the
units used by /proc/pid/oom_score_adj, and vice versa. Changing one of
these per-task tunables will rescale the value of the other to an
equivalent meaning. Although /proc/pid/oom_adj was originally defined as
a bitshift on the badness score, it now shares the same linear growth as
/proc/pid/oom_score_adj but with different granularity. This is required
so the ABI is not broken with userspace applications and allows oom_adj to
be deprecated for future removal.
Signed-off-by: David Rientjes <rientjes@google.com>
Cc: Nick Piggin <npiggin@suse.de>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-10 07:19:46 +07:00
|
|
|
/*
|
|
|
|
* Return the memory (and swap, if configured) limit for a memcg.
|
|
|
|
*/
|
mm, memcg: introduce own oom handler to iterate only over its own threads
The global oom killer is serialized by the per-zonelist
try_set_zonelist_oom() which is used in the page allocator. Concurrent
oom kills are thus a rare event and only occur in systems using
mempolicies and with a large number of nodes.
Memory controller oom kills, however, can frequently be concurrent since
there is no serialization once the oom killer is called for oom conditions
in several different memcgs in parallel.
This creates a massive contention on tasklist_lock since the oom killer
requires the readside for the tasklist iteration. If several memcgs are
calling the oom killer, this lock can be held for a substantial amount of
time, especially if threads continue to enter it as other threads are
exiting.
Since the exit path grabs the writeside of the lock with irqs disabled in
a few different places, this can cause a soft lockup on cpus as a result
of tasklist_lock starvation.
The kernel lacks unfair writelocks, and successful calls to the oom killer
usually result in at least one thread entering the exit path, so an
alternative solution is needed.
This patch introduces a seperate oom handler for memcgs so that they do
not require tasklist_lock for as much time. Instead, it iterates only
over the threads attached to the oom memcg and grabs a reference to the
selected thread before calling oom_kill_process() to ensure it doesn't
prematurely exit.
This still requires tasklist_lock for the tasklist dump, iterating
children of the selected process, and killing all other threads on the
system sharing the same memory as the selected victim. So while this
isn't a complete solution to tasklist_lock starvation, it significantly
reduces the amount of time that it is held.
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Michal Hocko <mhocko@suse.cz>
Signed-off-by: David Rientjes <rientjes@google.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Reviewed-by: Sha Zhengju <handai.szj@taobao.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-08-01 06:43:44 +07:00
|
|
|
static u64 mem_cgroup_get_limit(struct mem_cgroup *memcg)
|
oom: badness heuristic rewrite
This a complete rewrite of the oom killer's badness() heuristic which is
used to determine which task to kill in oom conditions. The goal is to
make it as simple and predictable as possible so the results are better
understood and we end up killing the task which will lead to the most
memory freeing while still respecting the fine-tuning from userspace.
Instead of basing the heuristic on mm->total_vm for each task, the task's
rss and swap space is used instead. This is a better indication of the
amount of memory that will be freeable if the oom killed task is chosen
and subsequently exits. This helps specifically in cases where KDE or
GNOME is chosen for oom kill on desktop systems instead of a memory
hogging task.
The baseline for the heuristic is a proportion of memory that each task is
currently using in memory plus swap compared to the amount of "allowable"
memory. "Allowable," in this sense, means the system-wide resources for
unconstrained oom conditions, the set of mempolicy nodes, the mems
attached to current's cpuset, or a memory controller's limit. The
proportion is given on a scale of 0 (never kill) to 1000 (always kill),
roughly meaning that if a task has a badness() score of 500 that the task
consumes approximately 50% of allowable memory resident in RAM or in swap
space.
The proportion is always relative to the amount of "allowable" memory and
not the total amount of RAM systemwide so that mempolicies and cpusets may
operate in isolation; they shall not need to know the true size of the
machine on which they are running if they are bound to a specific set of
nodes or mems, respectively.
Root tasks are given 3% extra memory just like __vm_enough_memory()
provides in LSMs. In the event of two tasks consuming similar amounts of
memory, it is generally better to save root's task.
Because of the change in the badness() heuristic's baseline, it is also
necessary to introduce a new user interface to tune it. It's not possible
to redefine the meaning of /proc/pid/oom_adj with a new scale since the
ABI cannot be changed for backward compatability. Instead, a new tunable,
/proc/pid/oom_score_adj, is added that ranges from -1000 to +1000. It may
be used to polarize the heuristic such that certain tasks are never
considered for oom kill while others may always be considered. The value
is added directly into the badness() score so a value of -500, for
example, means to discount 50% of its memory consumption in comparison to
other tasks either on the system, bound to the mempolicy, in the cpuset,
or sharing the same memory controller.
/proc/pid/oom_adj is changed so that its meaning is rescaled into the
units used by /proc/pid/oom_score_adj, and vice versa. Changing one of
these per-task tunables will rescale the value of the other to an
equivalent meaning. Although /proc/pid/oom_adj was originally defined as
a bitshift on the badness score, it now shares the same linear growth as
/proc/pid/oom_score_adj but with different granularity. This is required
so the ABI is not broken with userspace applications and allows oom_adj to
be deprecated for future removal.
Signed-off-by: David Rientjes <rientjes@google.com>
Cc: Nick Piggin <npiggin@suse.de>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-10 07:19:46 +07:00
|
|
|
{
|
|
|
|
u64 limit;
|
|
|
|
|
2011-01-14 06:47:39 +07:00
|
|
|
limit = res_counter_read_u64(&memcg->res, RES_LIMIT);
|
|
|
|
|
oom: badness heuristic rewrite
This a complete rewrite of the oom killer's badness() heuristic which is
used to determine which task to kill in oom conditions. The goal is to
make it as simple and predictable as possible so the results are better
understood and we end up killing the task which will lead to the most
memory freeing while still respecting the fine-tuning from userspace.
Instead of basing the heuristic on mm->total_vm for each task, the task's
rss and swap space is used instead. This is a better indication of the
amount of memory that will be freeable if the oom killed task is chosen
and subsequently exits. This helps specifically in cases where KDE or
GNOME is chosen for oom kill on desktop systems instead of a memory
hogging task.
The baseline for the heuristic is a proportion of memory that each task is
currently using in memory plus swap compared to the amount of "allowable"
memory. "Allowable," in this sense, means the system-wide resources for
unconstrained oom conditions, the set of mempolicy nodes, the mems
attached to current's cpuset, or a memory controller's limit. The
proportion is given on a scale of 0 (never kill) to 1000 (always kill),
roughly meaning that if a task has a badness() score of 500 that the task
consumes approximately 50% of allowable memory resident in RAM or in swap
space.
The proportion is always relative to the amount of "allowable" memory and
not the total amount of RAM systemwide so that mempolicies and cpusets may
operate in isolation; they shall not need to know the true size of the
machine on which they are running if they are bound to a specific set of
nodes or mems, respectively.
Root tasks are given 3% extra memory just like __vm_enough_memory()
provides in LSMs. In the event of two tasks consuming similar amounts of
memory, it is generally better to save root's task.
Because of the change in the badness() heuristic's baseline, it is also
necessary to introduce a new user interface to tune it. It's not possible
to redefine the meaning of /proc/pid/oom_adj with a new scale since the
ABI cannot be changed for backward compatability. Instead, a new tunable,
/proc/pid/oom_score_adj, is added that ranges from -1000 to +1000. It may
be used to polarize the heuristic such that certain tasks are never
considered for oom kill while others may always be considered. The value
is added directly into the badness() score so a value of -500, for
example, means to discount 50% of its memory consumption in comparison to
other tasks either on the system, bound to the mempolicy, in the cpuset,
or sharing the same memory controller.
/proc/pid/oom_adj is changed so that its meaning is rescaled into the
units used by /proc/pid/oom_score_adj, and vice versa. Changing one of
these per-task tunables will rescale the value of the other to an
equivalent meaning. Although /proc/pid/oom_adj was originally defined as
a bitshift on the badness score, it now shares the same linear growth as
/proc/pid/oom_score_adj but with different granularity. This is required
so the ABI is not broken with userspace applications and allows oom_adj to
be deprecated for future removal.
Signed-off-by: David Rientjes <rientjes@google.com>
Cc: Nick Piggin <npiggin@suse.de>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-10 07:19:46 +07:00
|
|
|
/*
|
2012-11-17 05:14:49 +07:00
|
|
|
* Do not consider swap space if we cannot swap due to swappiness
|
oom: badness heuristic rewrite
This a complete rewrite of the oom killer's badness() heuristic which is
used to determine which task to kill in oom conditions. The goal is to
make it as simple and predictable as possible so the results are better
understood and we end up killing the task which will lead to the most
memory freeing while still respecting the fine-tuning from userspace.
Instead of basing the heuristic on mm->total_vm for each task, the task's
rss and swap space is used instead. This is a better indication of the
amount of memory that will be freeable if the oom killed task is chosen
and subsequently exits. This helps specifically in cases where KDE or
GNOME is chosen for oom kill on desktop systems instead of a memory
hogging task.
The baseline for the heuristic is a proportion of memory that each task is
currently using in memory plus swap compared to the amount of "allowable"
memory. "Allowable," in this sense, means the system-wide resources for
unconstrained oom conditions, the set of mempolicy nodes, the mems
attached to current's cpuset, or a memory controller's limit. The
proportion is given on a scale of 0 (never kill) to 1000 (always kill),
roughly meaning that if a task has a badness() score of 500 that the task
consumes approximately 50% of allowable memory resident in RAM or in swap
space.
The proportion is always relative to the amount of "allowable" memory and
not the total amount of RAM systemwide so that mempolicies and cpusets may
operate in isolation; they shall not need to know the true size of the
machine on which they are running if they are bound to a specific set of
nodes or mems, respectively.
Root tasks are given 3% extra memory just like __vm_enough_memory()
provides in LSMs. In the event of two tasks consuming similar amounts of
memory, it is generally better to save root's task.
Because of the change in the badness() heuristic's baseline, it is also
necessary to introduce a new user interface to tune it. It's not possible
to redefine the meaning of /proc/pid/oom_adj with a new scale since the
ABI cannot be changed for backward compatability. Instead, a new tunable,
/proc/pid/oom_score_adj, is added that ranges from -1000 to +1000. It may
be used to polarize the heuristic such that certain tasks are never
considered for oom kill while others may always be considered. The value
is added directly into the badness() score so a value of -500, for
example, means to discount 50% of its memory consumption in comparison to
other tasks either on the system, bound to the mempolicy, in the cpuset,
or sharing the same memory controller.
/proc/pid/oom_adj is changed so that its meaning is rescaled into the
units used by /proc/pid/oom_score_adj, and vice versa. Changing one of
these per-task tunables will rescale the value of the other to an
equivalent meaning. Although /proc/pid/oom_adj was originally defined as
a bitshift on the badness score, it now shares the same linear growth as
/proc/pid/oom_score_adj but with different granularity. This is required
so the ABI is not broken with userspace applications and allows oom_adj to
be deprecated for future removal.
Signed-off-by: David Rientjes <rientjes@google.com>
Cc: Nick Piggin <npiggin@suse.de>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-10 07:19:46 +07:00
|
|
|
*/
|
2012-11-17 05:14:49 +07:00
|
|
|
if (mem_cgroup_swappiness(memcg)) {
|
|
|
|
u64 memsw;
|
|
|
|
|
|
|
|
limit += total_swap_pages << PAGE_SHIFT;
|
|
|
|
memsw = res_counter_read_u64(&memcg->memsw, RES_LIMIT);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If memsw is finite and limits the amount of swap space
|
|
|
|
* available to this memcg, return that limit.
|
|
|
|
*/
|
|
|
|
limit = min(limit, memsw);
|
|
|
|
}
|
|
|
|
|
|
|
|
return limit;
|
oom: badness heuristic rewrite
This a complete rewrite of the oom killer's badness() heuristic which is
used to determine which task to kill in oom conditions. The goal is to
make it as simple and predictable as possible so the results are better
understood and we end up killing the task which will lead to the most
memory freeing while still respecting the fine-tuning from userspace.
Instead of basing the heuristic on mm->total_vm for each task, the task's
rss and swap space is used instead. This is a better indication of the
amount of memory that will be freeable if the oom killed task is chosen
and subsequently exits. This helps specifically in cases where KDE or
GNOME is chosen for oom kill on desktop systems instead of a memory
hogging task.
The baseline for the heuristic is a proportion of memory that each task is
currently using in memory plus swap compared to the amount of "allowable"
memory. "Allowable," in this sense, means the system-wide resources for
unconstrained oom conditions, the set of mempolicy nodes, the mems
attached to current's cpuset, or a memory controller's limit. The
proportion is given on a scale of 0 (never kill) to 1000 (always kill),
roughly meaning that if a task has a badness() score of 500 that the task
consumes approximately 50% of allowable memory resident in RAM or in swap
space.
The proportion is always relative to the amount of "allowable" memory and
not the total amount of RAM systemwide so that mempolicies and cpusets may
operate in isolation; they shall not need to know the true size of the
machine on which they are running if they are bound to a specific set of
nodes or mems, respectively.
Root tasks are given 3% extra memory just like __vm_enough_memory()
provides in LSMs. In the event of two tasks consuming similar amounts of
memory, it is generally better to save root's task.
Because of the change in the badness() heuristic's baseline, it is also
necessary to introduce a new user interface to tune it. It's not possible
to redefine the meaning of /proc/pid/oom_adj with a new scale since the
ABI cannot be changed for backward compatability. Instead, a new tunable,
/proc/pid/oom_score_adj, is added that ranges from -1000 to +1000. It may
be used to polarize the heuristic such that certain tasks are never
considered for oom kill while others may always be considered. The value
is added directly into the badness() score so a value of -500, for
example, means to discount 50% of its memory consumption in comparison to
other tasks either on the system, bound to the mempolicy, in the cpuset,
or sharing the same memory controller.
/proc/pid/oom_adj is changed so that its meaning is rescaled into the
units used by /proc/pid/oom_score_adj, and vice versa. Changing one of
these per-task tunables will rescale the value of the other to an
equivalent meaning. Although /proc/pid/oom_adj was originally defined as
a bitshift on the badness score, it now shares the same linear growth as
/proc/pid/oom_score_adj but with different granularity. This is required
so the ABI is not broken with userspace applications and allows oom_adj to
be deprecated for future removal.
Signed-off-by: David Rientjes <rientjes@google.com>
Cc: Nick Piggin <npiggin@suse.de>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-10 07:19:46 +07:00
|
|
|
}
|
|
|
|
|
2012-08-01 06:43:48 +07:00
|
|
|
void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
|
|
|
|
int order)
|
mm, memcg: introduce own oom handler to iterate only over its own threads
The global oom killer is serialized by the per-zonelist
try_set_zonelist_oom() which is used in the page allocator. Concurrent
oom kills are thus a rare event and only occur in systems using
mempolicies and with a large number of nodes.
Memory controller oom kills, however, can frequently be concurrent since
there is no serialization once the oom killer is called for oom conditions
in several different memcgs in parallel.
This creates a massive contention on tasklist_lock since the oom killer
requires the readside for the tasklist iteration. If several memcgs are
calling the oom killer, this lock can be held for a substantial amount of
time, especially if threads continue to enter it as other threads are
exiting.
Since the exit path grabs the writeside of the lock with irqs disabled in
a few different places, this can cause a soft lockup on cpus as a result
of tasklist_lock starvation.
The kernel lacks unfair writelocks, and successful calls to the oom killer
usually result in at least one thread entering the exit path, so an
alternative solution is needed.
This patch introduces a seperate oom handler for memcgs so that they do
not require tasklist_lock for as much time. Instead, it iterates only
over the threads attached to the oom memcg and grabs a reference to the
selected thread before calling oom_kill_process() to ensure it doesn't
prematurely exit.
This still requires tasklist_lock for the tasklist dump, iterating
children of the selected process, and killing all other threads on the
system sharing the same memory as the selected victim. So while this
isn't a complete solution to tasklist_lock starvation, it significantly
reduces the amount of time that it is held.
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Michal Hocko <mhocko@suse.cz>
Signed-off-by: David Rientjes <rientjes@google.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Reviewed-by: Sha Zhengju <handai.szj@taobao.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-08-01 06:43:44 +07:00
|
|
|
{
|
|
|
|
struct mem_cgroup *iter;
|
|
|
|
unsigned long chosen_points = 0;
|
|
|
|
unsigned long totalpages;
|
|
|
|
unsigned int points = 0;
|
|
|
|
struct task_struct *chosen = NULL;
|
|
|
|
|
2012-08-01 06:43:48 +07:00
|
|
|
/*
|
|
|
|
* If current has a pending SIGKILL, then automatically select it. The
|
|
|
|
* goal is to allow it to allocate so that it may quickly exit and free
|
|
|
|
* its memory.
|
|
|
|
*/
|
|
|
|
if (fatal_signal_pending(current)) {
|
|
|
|
set_thread_flag(TIF_MEMDIE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
check_panic_on_oom(CONSTRAINT_MEMCG, gfp_mask, order, NULL);
|
mm, memcg: introduce own oom handler to iterate only over its own threads
The global oom killer is serialized by the per-zonelist
try_set_zonelist_oom() which is used in the page allocator. Concurrent
oom kills are thus a rare event and only occur in systems using
mempolicies and with a large number of nodes.
Memory controller oom kills, however, can frequently be concurrent since
there is no serialization once the oom killer is called for oom conditions
in several different memcgs in parallel.
This creates a massive contention on tasklist_lock since the oom killer
requires the readside for the tasklist iteration. If several memcgs are
calling the oom killer, this lock can be held for a substantial amount of
time, especially if threads continue to enter it as other threads are
exiting.
Since the exit path grabs the writeside of the lock with irqs disabled in
a few different places, this can cause a soft lockup on cpus as a result
of tasklist_lock starvation.
The kernel lacks unfair writelocks, and successful calls to the oom killer
usually result in at least one thread entering the exit path, so an
alternative solution is needed.
This patch introduces a seperate oom handler for memcgs so that they do
not require tasklist_lock for as much time. Instead, it iterates only
over the threads attached to the oom memcg and grabs a reference to the
selected thread before calling oom_kill_process() to ensure it doesn't
prematurely exit.
This still requires tasklist_lock for the tasklist dump, iterating
children of the selected process, and killing all other threads on the
system sharing the same memory as the selected victim. So while this
isn't a complete solution to tasklist_lock starvation, it significantly
reduces the amount of time that it is held.
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Michal Hocko <mhocko@suse.cz>
Signed-off-by: David Rientjes <rientjes@google.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Reviewed-by: Sha Zhengju <handai.szj@taobao.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-08-01 06:43:44 +07:00
|
|
|
totalpages = mem_cgroup_get_limit(memcg) >> PAGE_SHIFT ? : 1;
|
|
|
|
for_each_mem_cgroup_tree(iter, memcg) {
|
|
|
|
struct cgroup *cgroup = iter->css.cgroup;
|
|
|
|
struct cgroup_iter it;
|
|
|
|
struct task_struct *task;
|
|
|
|
|
|
|
|
cgroup_iter_start(cgroup, &it);
|
|
|
|
while ((task = cgroup_iter_next(cgroup, &it))) {
|
|
|
|
switch (oom_scan_process_thread(task, totalpages, NULL,
|
|
|
|
false)) {
|
|
|
|
case OOM_SCAN_SELECT:
|
|
|
|
if (chosen)
|
|
|
|
put_task_struct(chosen);
|
|
|
|
chosen = task;
|
|
|
|
chosen_points = ULONG_MAX;
|
|
|
|
get_task_struct(chosen);
|
|
|
|
/* fall through */
|
|
|
|
case OOM_SCAN_CONTINUE:
|
|
|
|
continue;
|
|
|
|
case OOM_SCAN_ABORT:
|
|
|
|
cgroup_iter_end(cgroup, &it);
|
|
|
|
mem_cgroup_iter_break(memcg, iter);
|
|
|
|
if (chosen)
|
|
|
|
put_task_struct(chosen);
|
|
|
|
return;
|
|
|
|
case OOM_SCAN_OK:
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
points = oom_badness(task, memcg, NULL, totalpages);
|
|
|
|
if (points > chosen_points) {
|
|
|
|
if (chosen)
|
|
|
|
put_task_struct(chosen);
|
|
|
|
chosen = task;
|
|
|
|
chosen_points = points;
|
|
|
|
get_task_struct(chosen);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cgroup_iter_end(cgroup, &it);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!chosen)
|
|
|
|
return;
|
|
|
|
points = chosen_points * 1000 / totalpages;
|
|
|
|
oom_kill_process(chosen, gfp_mask, order, points, totalpages, memcg,
|
|
|
|
NULL, "Memory cgroup out of memory");
|
|
|
|
}
|
|
|
|
|
2012-01-13 08:17:59 +07:00
|
|
|
static unsigned long mem_cgroup_reclaim(struct mem_cgroup *memcg,
|
|
|
|
gfp_t gfp_mask,
|
|
|
|
unsigned long flags)
|
|
|
|
{
|
|
|
|
unsigned long total = 0;
|
|
|
|
bool noswap = false;
|
|
|
|
int loop;
|
|
|
|
|
|
|
|
if (flags & MEM_CGROUP_RECLAIM_NOSWAP)
|
|
|
|
noswap = true;
|
|
|
|
if (!(flags & MEM_CGROUP_RECLAIM_SHRINK) && memcg->memsw_is_minimum)
|
|
|
|
noswap = true;
|
|
|
|
|
|
|
|
for (loop = 0; loop < MEM_CGROUP_MAX_RECLAIM_LOOPS; loop++) {
|
|
|
|
if (loop)
|
|
|
|
drain_all_stock_async(memcg);
|
|
|
|
total += try_to_free_mem_cgroup_pages(memcg, gfp_mask, noswap);
|
|
|
|
/*
|
|
|
|
* Allow limit shrinkers, which are triggered directly
|
|
|
|
* by userspace, to catch signals and stop reclaim
|
|
|
|
* after minimal progress, regardless of the margin.
|
|
|
|
*/
|
|
|
|
if (total && (flags & MEM_CGROUP_RECLAIM_SHRINK))
|
|
|
|
break;
|
|
|
|
if (mem_cgroup_margin(memcg))
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
* If nothing was reclaimed after two attempts, there
|
|
|
|
* may be no reclaimable pages in this hierarchy.
|
|
|
|
*/
|
|
|
|
if (loop && !total)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
2011-07-09 05:39:42 +07:00
|
|
|
/**
|
|
|
|
* test_mem_cgroup_node_reclaimable
|
2012-06-21 02:53:01 +07:00
|
|
|
* @memcg: the target memcg
|
2011-07-09 05:39:42 +07:00
|
|
|
* @nid: the node ID to be checked.
|
|
|
|
* @noswap : specify true here if the user wants flle only information.
|
|
|
|
*
|
|
|
|
* This function returns whether the specified memcg contains any
|
|
|
|
* reclaimable pages on a node. Returns true if there are any reclaimable
|
|
|
|
* pages in the node.
|
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
static bool test_mem_cgroup_node_reclaimable(struct mem_cgroup *memcg,
|
2011-07-09 05:39:42 +07:00
|
|
|
int nid, bool noswap)
|
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
if (mem_cgroup_node_nr_lru_pages(memcg, nid, LRU_ALL_FILE))
|
2011-07-09 05:39:42 +07:00
|
|
|
return true;
|
|
|
|
if (noswap || !total_swap_pages)
|
|
|
|
return false;
|
2011-11-03 03:38:15 +07:00
|
|
|
if (mem_cgroup_node_nr_lru_pages(memcg, nid, LRU_ALL_ANON))
|
2011-07-09 05:39:42 +07:00
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
2011-05-27 06:25:33 +07:00
|
|
|
#if MAX_NUMNODES > 1
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Always updating the nodemask is not very good - even if we have an empty
|
|
|
|
* list or the wrong list here, we can start from some node and traverse all
|
|
|
|
* nodes based on the zonelist. So update the list loosely once per 10 secs.
|
|
|
|
*
|
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
static void mem_cgroup_may_update_nodemask(struct mem_cgroup *memcg)
|
2011-05-27 06:25:33 +07:00
|
|
|
{
|
|
|
|
int nid;
|
2011-07-09 05:39:43 +07:00
|
|
|
/*
|
|
|
|
* numainfo_events > 0 means there was at least NUMAINFO_EVENTS_TARGET
|
|
|
|
* pagein/pageout changes since the last update.
|
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
if (!atomic_read(&memcg->numainfo_events))
|
2011-07-09 05:39:43 +07:00
|
|
|
return;
|
2011-11-03 03:38:15 +07:00
|
|
|
if (atomic_inc_return(&memcg->numainfo_updating) > 1)
|
2011-05-27 06:25:33 +07:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* make a nodemask where this memcg uses memory from */
|
2011-11-03 03:38:15 +07:00
|
|
|
memcg->scan_nodes = node_states[N_HIGH_MEMORY];
|
2011-05-27 06:25:33 +07:00
|
|
|
|
|
|
|
for_each_node_mask(nid, node_states[N_HIGH_MEMORY]) {
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
if (!test_mem_cgroup_node_reclaimable(memcg, nid, false))
|
|
|
|
node_clear(nid, memcg->scan_nodes);
|
2011-05-27 06:25:33 +07:00
|
|
|
}
|
2011-07-09 05:39:43 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
atomic_set(&memcg->numainfo_events, 0);
|
|
|
|
atomic_set(&memcg->numainfo_updating, 0);
|
2011-05-27 06:25:33 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Selecting a node where we start reclaim from. Because what we need is just
|
|
|
|
* reducing usage counter, start from anywhere is O,K. Considering
|
|
|
|
* memory reclaim from current node, there are pros. and cons.
|
|
|
|
*
|
|
|
|
* Freeing memory from current node means freeing memory from a node which
|
|
|
|
* we'll use or we've used. So, it may make LRU bad. And if several threads
|
|
|
|
* hit limits, it will see a contention on a node. But freeing from remote
|
|
|
|
* node means more costs for memory reclaim because of memory latency.
|
|
|
|
*
|
|
|
|
* Now, we use round-robin. Better algorithm is welcomed.
|
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
int mem_cgroup_select_victim_node(struct mem_cgroup *memcg)
|
2011-05-27 06:25:33 +07:00
|
|
|
{
|
|
|
|
int node;
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
mem_cgroup_may_update_nodemask(memcg);
|
|
|
|
node = memcg->last_scanned_node;
|
2011-05-27 06:25:33 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
node = next_node(node, memcg->scan_nodes);
|
2011-05-27 06:25:33 +07:00
|
|
|
if (node == MAX_NUMNODES)
|
2011-11-03 03:38:15 +07:00
|
|
|
node = first_node(memcg->scan_nodes);
|
2011-05-27 06:25:33 +07:00
|
|
|
/*
|
|
|
|
* We call this when we hit limit, not when pages are added to LRU.
|
|
|
|
* No LRU may hold pages because all pages are UNEVICTABLE or
|
|
|
|
* memcg is too small and all pages are not on LRU. In that case,
|
|
|
|
* we use curret node.
|
|
|
|
*/
|
|
|
|
if (unlikely(node == MAX_NUMNODES))
|
|
|
|
node = numa_node_id();
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
memcg->last_scanned_node = node;
|
2011-05-27 06:25:33 +07:00
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2011-07-09 05:39:42 +07:00
|
|
|
/*
|
|
|
|
* Check all nodes whether it contains reclaimable pages or not.
|
|
|
|
* For quick scan, we make use of scan_nodes. This will allow us to skip
|
|
|
|
* unused nodes. But scan_nodes is lazily updated and may not cotain
|
|
|
|
* enough new information. We need to do double check.
|
|
|
|
*/
|
2012-05-30 05:06:55 +07:00
|
|
|
static bool mem_cgroup_reclaimable(struct mem_cgroup *memcg, bool noswap)
|
2011-07-09 05:39:42 +07:00
|
|
|
{
|
|
|
|
int nid;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* quick check...making use of scan_node.
|
|
|
|
* We can skip unused nodes.
|
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
if (!nodes_empty(memcg->scan_nodes)) {
|
|
|
|
for (nid = first_node(memcg->scan_nodes);
|
2011-07-09 05:39:42 +07:00
|
|
|
nid < MAX_NUMNODES;
|
2011-11-03 03:38:15 +07:00
|
|
|
nid = next_node(nid, memcg->scan_nodes)) {
|
2011-07-09 05:39:42 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
if (test_mem_cgroup_node_reclaimable(memcg, nid, noswap))
|
2011-07-09 05:39:42 +07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Check rest of nodes.
|
|
|
|
*/
|
|
|
|
for_each_node_state(nid, N_HIGH_MEMORY) {
|
2011-11-03 03:38:15 +07:00
|
|
|
if (node_isset(nid, memcg->scan_nodes))
|
2011-07-09 05:39:42 +07:00
|
|
|
continue;
|
2011-11-03 03:38:15 +07:00
|
|
|
if (test_mem_cgroup_node_reclaimable(memcg, nid, noswap))
|
2011-07-09 05:39:42 +07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-05-27 06:25:33 +07:00
|
|
|
#else
|
2011-11-03 03:38:15 +07:00
|
|
|
int mem_cgroup_select_victim_node(struct mem_cgroup *memcg)
|
2011-05-27 06:25:33 +07:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2011-07-09 05:39:42 +07:00
|
|
|
|
2012-05-30 05:06:55 +07:00
|
|
|
static bool mem_cgroup_reclaimable(struct mem_cgroup *memcg, bool noswap)
|
2011-07-09 05:39:42 +07:00
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
return test_mem_cgroup_node_reclaimable(memcg, 0, noswap);
|
2011-07-09 05:39:42 +07:00
|
|
|
}
|
2011-05-27 06:25:33 +07:00
|
|
|
#endif
|
|
|
|
|
2012-01-13 08:17:59 +07:00
|
|
|
static int mem_cgroup_soft_reclaim(struct mem_cgroup *root_memcg,
|
|
|
|
struct zone *zone,
|
|
|
|
gfp_t gfp_mask,
|
|
|
|
unsigned long *total_scanned)
|
2009-01-08 09:08:06 +07:00
|
|
|
{
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
struct mem_cgroup *victim = NULL;
|
2012-01-13 08:17:59 +07:00
|
|
|
int total = 0;
|
2009-04-03 06:57:33 +07:00
|
|
|
int loop = 0;
|
2011-03-24 06:42:21 +07:00
|
|
|
unsigned long excess;
|
2011-09-15 06:21:58 +07:00
|
|
|
unsigned long nr_scanned;
|
mm: memcg: per-priority per-zone hierarchy scan generations
Memory cgroup limit reclaim currently picks one memory cgroup out of the
target hierarchy, remembers it as the last scanned child, and reclaims
all zones in it with decreasing priority levels.
The new hierarchy reclaim code will pick memory cgroups from the same
hierarchy concurrently from different zones and priority levels, it
becomes necessary that hierarchy roots not only remember the last
scanned child, but do so for each zone and priority level.
Until now, we reclaimed memcgs like this:
mem = mem_cgroup_iter(root)
for each priority level:
for each zone in zonelist:
reclaim(mem, zone)
But subsequent patches will move the memcg iteration inside the loop
over the zones:
for each priority level:
for each zone in zonelist:
mem = mem_cgroup_iter(root)
reclaim(mem, zone)
And to keep with the original scan order - memcg -> priority -> zone -
the last scanned memcg has to be remembered per zone and per priority
level.
Furthermore, global reclaim will be switched to the hierarchy walk as
well. Different from limit reclaim, which can just recheck the limit
after some reclaim progress, its target is to scan all memcgs for the
desired zone pages, proportional to the memcg size, and so reliably
detecting a full hierarchy round-trip will become crucial.
Currently, the code relies on one reclaimer encountering the same memcg
twice, but that is error-prone with concurrent reclaimers. Instead, use
a generation counter that is increased every time the child with the
highest ID has been visited, so that reclaimers can stop when the
generation changes.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:55 +07:00
|
|
|
struct mem_cgroup_reclaim_cookie reclaim = {
|
|
|
|
.zone = zone,
|
|
|
|
.priority = 0,
|
|
|
|
};
|
2011-03-24 06:42:21 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
excess = res_counter_soft_limit_excess(&root_memcg->res) >> PAGE_SHIFT;
|
2009-04-03 06:57:33 +07:00
|
|
|
|
2009-09-24 05:56:39 +07:00
|
|
|
while (1) {
|
mm: memcg: per-priority per-zone hierarchy scan generations
Memory cgroup limit reclaim currently picks one memory cgroup out of the
target hierarchy, remembers it as the last scanned child, and reclaims
all zones in it with decreasing priority levels.
The new hierarchy reclaim code will pick memory cgroups from the same
hierarchy concurrently from different zones and priority levels, it
becomes necessary that hierarchy roots not only remember the last
scanned child, but do so for each zone and priority level.
Until now, we reclaimed memcgs like this:
mem = mem_cgroup_iter(root)
for each priority level:
for each zone in zonelist:
reclaim(mem, zone)
But subsequent patches will move the memcg iteration inside the loop
over the zones:
for each priority level:
for each zone in zonelist:
mem = mem_cgroup_iter(root)
reclaim(mem, zone)
And to keep with the original scan order - memcg -> priority -> zone -
the last scanned memcg has to be remembered per zone and per priority
level.
Furthermore, global reclaim will be switched to the hierarchy walk as
well. Different from limit reclaim, which can just recheck the limit
after some reclaim progress, its target is to scan all memcgs for the
desired zone pages, proportional to the memcg size, and so reliably
detecting a full hierarchy round-trip will become crucial.
Currently, the code relies on one reclaimer encountering the same memcg
twice, but that is error-prone with concurrent reclaimers. Instead, use
a generation counter that is increased every time the child with the
highest ID has been visited, so that reclaimers can stop when the
generation changes.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:55 +07:00
|
|
|
victim = mem_cgroup_iter(root_memcg, victim, &reclaim);
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
if (!victim) {
|
2009-04-03 06:57:33 +07:00
|
|
|
loop++;
|
2009-09-24 05:56:39 +07:00
|
|
|
if (loop >= 2) {
|
|
|
|
/*
|
|
|
|
* If we have not been able to reclaim
|
|
|
|
* anything, it might because there are
|
|
|
|
* no reclaimable pages under this hierarchy
|
|
|
|
*/
|
2012-01-13 08:17:59 +07:00
|
|
|
if (!total)
|
2009-09-24 05:56:39 +07:00
|
|
|
break;
|
|
|
|
/*
|
2011-03-31 08:57:33 +07:00
|
|
|
* We want to do more targeted reclaim.
|
2009-09-24 05:56:39 +07:00
|
|
|
* excess >> 2 is not to excessive so as to
|
|
|
|
* reclaim too much, nor too less that we keep
|
|
|
|
* coming back to reclaim from this cgroup
|
|
|
|
*/
|
|
|
|
if (total >= (excess >> 2) ||
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
(loop > MEM_CGROUP_MAX_RECLAIM_LOOPS))
|
2009-09-24 05:56:39 +07:00
|
|
|
break;
|
|
|
|
}
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
continue;
|
2009-09-24 05:56:39 +07:00
|
|
|
}
|
2012-01-13 08:17:59 +07:00
|
|
|
if (!mem_cgroup_reclaimable(victim, false))
|
2009-01-08 09:08:06 +07:00
|
|
|
continue;
|
2012-01-13 08:17:59 +07:00
|
|
|
total += mem_cgroup_shrink_node_zone(victim, gfp_mask, false,
|
|
|
|
zone, &nr_scanned);
|
|
|
|
*total_scanned += nr_scanned;
|
|
|
|
if (!res_counter_soft_limit_excess(&root_memcg->res))
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
break;
|
2009-01-08 09:08:06 +07:00
|
|
|
}
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
mem_cgroup_iter_break(root_memcg, victim);
|
2009-04-03 06:57:33 +07:00
|
|
|
return total;
|
2009-01-08 09:08:06 +07:00
|
|
|
}
|
|
|
|
|
2010-03-11 06:22:39 +07:00
|
|
|
/*
|
|
|
|
* Check OOM-Killer is already running under our hierarchy.
|
|
|
|
* If someone is running, return false.
|
2011-07-27 06:08:24 +07:00
|
|
|
* Has to be called with memcg_oom_lock
|
2010-03-11 06:22:39 +07:00
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
static bool mem_cgroup_oom_lock(struct mem_cgroup *memcg)
|
2010-03-11 06:22:39 +07:00
|
|
|
{
|
2011-07-27 06:08:23 +07:00
|
|
|
struct mem_cgroup *iter, *failed = NULL;
|
2009-01-08 09:08:08 +07:00
|
|
|
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
for_each_mem_cgroup_tree(iter, memcg) {
|
2011-08-26 05:59:16 +07:00
|
|
|
if (iter->oom_lock) {
|
2011-07-27 06:08:23 +07:00
|
|
|
/*
|
|
|
|
* this subtree of our hierarchy is already locked
|
|
|
|
* so we cannot give a lock.
|
|
|
|
*/
|
|
|
|
failed = iter;
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
mem_cgroup_iter_break(memcg, iter);
|
|
|
|
break;
|
2011-08-26 05:59:16 +07:00
|
|
|
} else
|
|
|
|
iter->oom_lock = true;
|
2010-10-28 05:33:41 +07:00
|
|
|
}
|
2010-03-11 06:22:39 +07:00
|
|
|
|
2011-07-27 06:08:23 +07:00
|
|
|
if (!failed)
|
2011-08-26 05:59:16 +07:00
|
|
|
return true;
|
2011-07-27 06:08:23 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* OK, we failed to lock the whole subtree so we have to clean up
|
|
|
|
* what we set up to the failing subtree
|
|
|
|
*/
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
for_each_mem_cgroup_tree(iter, memcg) {
|
2011-07-27 06:08:23 +07:00
|
|
|
if (iter == failed) {
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
mem_cgroup_iter_break(memcg, iter);
|
|
|
|
break;
|
2011-07-27 06:08:23 +07:00
|
|
|
}
|
|
|
|
iter->oom_lock = false;
|
|
|
|
}
|
2011-08-26 05:59:16 +07:00
|
|
|
return false;
|
2009-01-08 09:08:08 +07:00
|
|
|
}
|
memcg: fix OOM killer under memcg
This patch tries to fix OOM Killer problems caused by hierarchy.
Now, memcg itself has OOM KILL function (in oom_kill.c) and tries to
kill a task in memcg.
But, when hierarchy is used, it's broken and correct task cannot
be killed. For example, in following cgroup
/groupA/ hierarchy=1, limit=1G,
01 nolimit
02 nolimit
All tasks' memory usage under /groupA, /groupA/01, groupA/02 is limited to
groupA's 1Gbytes but OOM Killer just kills tasks in groupA.
This patch provides makes the bad process be selected from all tasks
under hierarchy. BTW, currently, oom_jiffies is updated against groupA
in above case. oom_jiffies of tree should be updated.
To see how oom_jiffies is used, please check mem_cgroup_oom_called()
callers.
[akpm@linux-foundation.org: build fix]
[akpm@linux-foundation.org: const fix]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Paul Menage <menage@google.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: David Rientjes <rientjes@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-04-03 06:57:38 +07:00
|
|
|
|
2011-07-27 06:08:23 +07:00
|
|
|
/*
|
2011-07-27 06:08:24 +07:00
|
|
|
* Has to be called with memcg_oom_lock
|
2011-07-27 06:08:23 +07:00
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
static int mem_cgroup_oom_unlock(struct mem_cgroup *memcg)
|
memcg: fix OOM killer under memcg
This patch tries to fix OOM Killer problems caused by hierarchy.
Now, memcg itself has OOM KILL function (in oom_kill.c) and tries to
kill a task in memcg.
But, when hierarchy is used, it's broken and correct task cannot
be killed. For example, in following cgroup
/groupA/ hierarchy=1, limit=1G,
01 nolimit
02 nolimit
All tasks' memory usage under /groupA, /groupA/01, groupA/02 is limited to
groupA's 1Gbytes but OOM Killer just kills tasks in groupA.
This patch provides makes the bad process be selected from all tasks
under hierarchy. BTW, currently, oom_jiffies is updated against groupA
in above case. oom_jiffies of tree should be updated.
To see how oom_jiffies is used, please check mem_cgroup_oom_called()
callers.
[akpm@linux-foundation.org: build fix]
[akpm@linux-foundation.org: const fix]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Paul Menage <menage@google.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: David Rientjes <rientjes@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-04-03 06:57:38 +07:00
|
|
|
{
|
2010-10-28 05:33:41 +07:00
|
|
|
struct mem_cgroup *iter;
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
for_each_mem_cgroup_tree(iter, memcg)
|
2011-07-27 06:08:23 +07:00
|
|
|
iter->oom_lock = false;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static void mem_cgroup_mark_under_oom(struct mem_cgroup *memcg)
|
2011-07-27 06:08:23 +07:00
|
|
|
{
|
|
|
|
struct mem_cgroup *iter;
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
for_each_mem_cgroup_tree(iter, memcg)
|
2011-07-27 06:08:23 +07:00
|
|
|
atomic_inc(&iter->under_oom);
|
|
|
|
}
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static void mem_cgroup_unmark_under_oom(struct mem_cgroup *memcg)
|
2011-07-27 06:08:23 +07:00
|
|
|
{
|
|
|
|
struct mem_cgroup *iter;
|
|
|
|
|
2010-03-11 06:22:39 +07:00
|
|
|
/*
|
|
|
|
* When a new child is created while the hierarchy is under oom,
|
|
|
|
* mem_cgroup_oom_lock() may not be called. We have to use
|
|
|
|
* atomic_add_unless() here.
|
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
for_each_mem_cgroup_tree(iter, memcg)
|
2011-07-27 06:08:23 +07:00
|
|
|
atomic_add_unless(&iter->under_oom, -1, 0);
|
memcg: fix OOM killer under memcg
This patch tries to fix OOM Killer problems caused by hierarchy.
Now, memcg itself has OOM KILL function (in oom_kill.c) and tries to
kill a task in memcg.
But, when hierarchy is used, it's broken and correct task cannot
be killed. For example, in following cgroup
/groupA/ hierarchy=1, limit=1G,
01 nolimit
02 nolimit
All tasks' memory usage under /groupA, /groupA/01, groupA/02 is limited to
groupA's 1Gbytes but OOM Killer just kills tasks in groupA.
This patch provides makes the bad process be selected from all tasks
under hierarchy. BTW, currently, oom_jiffies is updated against groupA
in above case. oom_jiffies of tree should be updated.
To see how oom_jiffies is used, please check mem_cgroup_oom_called()
callers.
[akpm@linux-foundation.org: build fix]
[akpm@linux-foundation.org: const fix]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Paul Menage <menage@google.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: David Rientjes <rientjes@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-04-03 06:57:38 +07:00
|
|
|
}
|
|
|
|
|
2011-07-27 06:08:24 +07:00
|
|
|
static DEFINE_SPINLOCK(memcg_oom_lock);
|
2010-03-11 06:22:39 +07:00
|
|
|
static DECLARE_WAIT_QUEUE_HEAD(memcg_oom_waitq);
|
|
|
|
|
2010-05-27 04:42:36 +07:00
|
|
|
struct oom_wait_info {
|
2012-03-22 06:34:18 +07:00
|
|
|
struct mem_cgroup *memcg;
|
2010-05-27 04:42:36 +07:00
|
|
|
wait_queue_t wait;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int memcg_oom_wake_function(wait_queue_t *wait,
|
|
|
|
unsigned mode, int sync, void *arg)
|
|
|
|
{
|
2012-03-22 06:34:18 +07:00
|
|
|
struct mem_cgroup *wake_memcg = (struct mem_cgroup *)arg;
|
|
|
|
struct mem_cgroup *oom_wait_memcg;
|
2010-05-27 04:42:36 +07:00
|
|
|
struct oom_wait_info *oom_wait_info;
|
|
|
|
|
|
|
|
oom_wait_info = container_of(wait, struct oom_wait_info, wait);
|
2012-03-22 06:34:18 +07:00
|
|
|
oom_wait_memcg = oom_wait_info->memcg;
|
2010-05-27 04:42:36 +07:00
|
|
|
|
|
|
|
/*
|
2012-03-22 06:34:18 +07:00
|
|
|
* Both of oom_wait_info->memcg and wake_memcg are stable under us.
|
2010-05-27 04:42:36 +07:00
|
|
|
* Then we can use css_is_ancestor without taking care of RCU.
|
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
if (!mem_cgroup_same_or_subtree(oom_wait_memcg, wake_memcg)
|
|
|
|
&& !mem_cgroup_same_or_subtree(wake_memcg, oom_wait_memcg))
|
2010-05-27 04:42:36 +07:00
|
|
|
return 0;
|
|
|
|
return autoremove_wake_function(wait, mode, sync, arg);
|
|
|
|
}
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static void memcg_wakeup_oom(struct mem_cgroup *memcg)
|
2010-05-27 04:42:36 +07:00
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
/* for filtering, pass "memcg" as argument. */
|
|
|
|
__wake_up(&memcg_oom_waitq, TASK_NORMAL, 0, memcg);
|
2010-05-27 04:42:36 +07:00
|
|
|
}
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static void memcg_oom_recover(struct mem_cgroup *memcg)
|
2010-05-27 04:42:37 +07:00
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
if (memcg && atomic_read(&memcg->under_oom))
|
|
|
|
memcg_wakeup_oom(memcg);
|
2010-05-27 04:42:37 +07:00
|
|
|
}
|
|
|
|
|
2010-03-11 06:22:39 +07:00
|
|
|
/*
|
|
|
|
* try to call OOM killer. returns false if we should exit memory-reclaim loop.
|
|
|
|
*/
|
2012-05-30 05:06:55 +07:00
|
|
|
static bool mem_cgroup_handle_oom(struct mem_cgroup *memcg, gfp_t mask,
|
|
|
|
int order)
|
memcg: fix OOM killer under memcg
This patch tries to fix OOM Killer problems caused by hierarchy.
Now, memcg itself has OOM KILL function (in oom_kill.c) and tries to
kill a task in memcg.
But, when hierarchy is used, it's broken and correct task cannot
be killed. For example, in following cgroup
/groupA/ hierarchy=1, limit=1G,
01 nolimit
02 nolimit
All tasks' memory usage under /groupA, /groupA/01, groupA/02 is limited to
groupA's 1Gbytes but OOM Killer just kills tasks in groupA.
This patch provides makes the bad process be selected from all tasks
under hierarchy. BTW, currently, oom_jiffies is updated against groupA
in above case. oom_jiffies of tree should be updated.
To see how oom_jiffies is used, please check mem_cgroup_oom_called()
callers.
[akpm@linux-foundation.org: build fix]
[akpm@linux-foundation.org: const fix]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Paul Menage <menage@google.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: David Rientjes <rientjes@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-04-03 06:57:38 +07:00
|
|
|
{
|
2010-05-27 04:42:36 +07:00
|
|
|
struct oom_wait_info owait;
|
2010-05-27 04:42:37 +07:00
|
|
|
bool locked, need_to_kill;
|
2010-03-11 06:22:39 +07:00
|
|
|
|
2012-03-22 06:34:18 +07:00
|
|
|
owait.memcg = memcg;
|
2010-05-27 04:42:36 +07:00
|
|
|
owait.wait.flags = 0;
|
|
|
|
owait.wait.func = memcg_oom_wake_function;
|
|
|
|
owait.wait.private = current;
|
|
|
|
INIT_LIST_HEAD(&owait.wait.task_list);
|
2010-05-27 04:42:37 +07:00
|
|
|
need_to_kill = true;
|
2011-11-03 03:38:15 +07:00
|
|
|
mem_cgroup_mark_under_oom(memcg);
|
2011-07-27 06:08:23 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
/* At first, try to OOM lock hierarchy under memcg.*/
|
2011-07-27 06:08:24 +07:00
|
|
|
spin_lock(&memcg_oom_lock);
|
2011-11-03 03:38:15 +07:00
|
|
|
locked = mem_cgroup_oom_lock(memcg);
|
2010-03-11 06:22:39 +07:00
|
|
|
/*
|
|
|
|
* Even if signal_pending(), we can't quit charge() loop without
|
|
|
|
* accounting. So, UNINTERRUPTIBLE is appropriate. But SIGKILL
|
|
|
|
* under OOM is always welcomed, use TASK_KILLABLE here.
|
|
|
|
*/
|
2010-05-27 04:42:37 +07:00
|
|
|
prepare_to_wait(&memcg_oom_waitq, &owait.wait, TASK_KILLABLE);
|
2011-11-03 03:38:15 +07:00
|
|
|
if (!locked || memcg->oom_kill_disable)
|
2010-05-27 04:42:37 +07:00
|
|
|
need_to_kill = false;
|
|
|
|
if (locked)
|
2011-11-03 03:38:15 +07:00
|
|
|
mem_cgroup_oom_notify(memcg);
|
2011-07-27 06:08:24 +07:00
|
|
|
spin_unlock(&memcg_oom_lock);
|
2010-03-11 06:22:39 +07:00
|
|
|
|
2010-05-27 04:42:37 +07:00
|
|
|
if (need_to_kill) {
|
|
|
|
finish_wait(&memcg_oom_waitq, &owait.wait);
|
2012-03-22 06:34:10 +07:00
|
|
|
mem_cgroup_out_of_memory(memcg, mask, order);
|
2010-05-27 04:42:37 +07:00
|
|
|
} else {
|
2010-03-11 06:22:39 +07:00
|
|
|
schedule();
|
2010-05-27 04:42:36 +07:00
|
|
|
finish_wait(&memcg_oom_waitq, &owait.wait);
|
2010-03-11 06:22:39 +07:00
|
|
|
}
|
2011-07-27 06:08:24 +07:00
|
|
|
spin_lock(&memcg_oom_lock);
|
2011-07-27 06:08:23 +07:00
|
|
|
if (locked)
|
2011-11-03 03:38:15 +07:00
|
|
|
mem_cgroup_oom_unlock(memcg);
|
|
|
|
memcg_wakeup_oom(memcg);
|
2011-07-27 06:08:24 +07:00
|
|
|
spin_unlock(&memcg_oom_lock);
|
2010-03-11 06:22:39 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
mem_cgroup_unmark_under_oom(memcg);
|
2011-07-27 06:08:23 +07:00
|
|
|
|
2010-03-11 06:22:39 +07:00
|
|
|
if (test_thread_flag(TIF_MEMDIE) || fatal_signal_pending(current))
|
|
|
|
return false;
|
|
|
|
/* Give chance to dying process */
|
2011-11-03 03:38:18 +07:00
|
|
|
schedule_timeout_uninterruptible(1);
|
2010-03-11 06:22:39 +07:00
|
|
|
return true;
|
memcg: fix OOM killer under memcg
This patch tries to fix OOM Killer problems caused by hierarchy.
Now, memcg itself has OOM KILL function (in oom_kill.c) and tries to
kill a task in memcg.
But, when hierarchy is used, it's broken and correct task cannot
be killed. For example, in following cgroup
/groupA/ hierarchy=1, limit=1G,
01 nolimit
02 nolimit
All tasks' memory usage under /groupA, /groupA/01, groupA/02 is limited to
groupA's 1Gbytes but OOM Killer just kills tasks in groupA.
This patch provides makes the bad process be selected from all tasks
under hierarchy. BTW, currently, oom_jiffies is updated against groupA
in above case. oom_jiffies of tree should be updated.
To see how oom_jiffies is used, please check mem_cgroup_oom_called()
callers.
[akpm@linux-foundation.org: build fix]
[akpm@linux-foundation.org: const fix]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Paul Menage <menage@google.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: David Rientjes <rientjes@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-04-03 06:57:38 +07:00
|
|
|
}
|
|
|
|
|
2009-06-18 06:26:34 +07:00
|
|
|
/*
|
|
|
|
* Currently used to update mapped file statistics, but the routine can be
|
|
|
|
* generalized to update other statistics as well.
|
memcg: avoid lock in updating file_mapped (Was fix race in file_mapped accouting flag management
At accounting file events per memory cgroup, we need to find memory cgroup
via page_cgroup->mem_cgroup. Now, we use lock_page_cgroup() for guarantee
pc->mem_cgroup is not overwritten while we make use of it.
But, considering the context which page-cgroup for files are accessed,
we can use alternative light-weight mutual execusion in the most case.
At handling file-caches, the only race we have to take care of is "moving"
account, IOW, overwriting page_cgroup->mem_cgroup. (See comment in the
patch)
Unlike charge/uncharge, "move" happens not so frequently. It happens only when
rmdir() and task-moving (with a special settings.)
This patch adds a race-checker for file-cache-status accounting v.s. account
moving. The new per-cpu-per-memcg counter MEM_CGROUP_ON_MOVE is added.
The routine for account move
1. Increment it before start moving
2. Call synchronize_rcu()
3. Decrement it after the end of moving.
By this, file-status-counting routine can check it needs to call
lock_page_cgroup(). In most case, I doesn't need to call it.
Following is a perf data of a process which mmap()/munmap 32MB of file cache
in a minute.
Before patch:
28.25% mmap mmap [.] main
22.64% mmap [kernel.kallsyms] [k] page_fault
9.96% mmap [kernel.kallsyms] [k] mem_cgroup_update_file_mapped
3.67% mmap [kernel.kallsyms] [k] filemap_fault
3.50% mmap [kernel.kallsyms] [k] unmap_vmas
2.99% mmap [kernel.kallsyms] [k] __do_fault
2.76% mmap [kernel.kallsyms] [k] find_get_page
After patch:
30.00% mmap mmap [.] main
23.78% mmap [kernel.kallsyms] [k] page_fault
5.52% mmap [kernel.kallsyms] [k] mem_cgroup_update_file_mapped
3.81% mmap [kernel.kallsyms] [k] unmap_vmas
3.26% mmap [kernel.kallsyms] [k] find_get_page
3.18% mmap [kernel.kallsyms] [k] __do_fault
3.03% mmap [kernel.kallsyms] [k] filemap_fault
2.40% mmap [kernel.kallsyms] [k] handle_mm_fault
2.40% mmap [kernel.kallsyms] [k] do_page_fault
This patch reduces memcg's cost to some extent.
(mem_cgroup_update_file_mapped is called by both of map/unmap)
Note: It seems some more improvements are required..but no idea.
maybe removing set/unset flag is required.
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Greg Thelen <gthelen@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-10-28 05:33:40 +07:00
|
|
|
*
|
|
|
|
* Notes: Race condition
|
|
|
|
*
|
|
|
|
* We usually use page_cgroup_lock() for accessing page_cgroup member but
|
|
|
|
* it tends to be costly. But considering some conditions, we doesn't need
|
|
|
|
* to do so _always_.
|
|
|
|
*
|
|
|
|
* Considering "charge", lock_page_cgroup() is not required because all
|
|
|
|
* file-stat operations happen after a page is attached to radix-tree. There
|
|
|
|
* are no race with "charge".
|
|
|
|
*
|
|
|
|
* Considering "uncharge", we know that memcg doesn't clear pc->mem_cgroup
|
|
|
|
* at "uncharge" intentionally. So, we always see valid pc->mem_cgroup even
|
|
|
|
* if there are race with "uncharge". Statistics itself is properly handled
|
|
|
|
* by flags.
|
|
|
|
*
|
|
|
|
* Considering "move", this is an only case we see a race. To make the race
|
2012-03-22 06:34:23 +07:00
|
|
|
* small, we check mm->moving_account and detect there are possibility of race
|
|
|
|
* If there is, we take a lock.
|
2009-06-18 06:26:34 +07:00
|
|
|
*/
|
2010-10-28 05:33:43 +07:00
|
|
|
|
memcg: use new logic for page stat accounting
Now, page-stat-per-memcg is recorded into per page_cgroup flag by
duplicating page's status into the flag. The reason is that memcg has a
feature to move a page from a group to another group and we have race
between "move" and "page stat accounting",
Under current logic, assume CPU-A and CPU-B. CPU-A does "move" and CPU-B
does "page stat accounting".
When CPU-A goes 1st,
CPU-A CPU-B
update "struct page" info.
move_lock_mem_cgroup(memcg)
see pc->flags
copy page stat to new group
overwrite pc->mem_cgroup.
move_unlock_mem_cgroup(memcg)
move_lock_mem_cgroup(mem)
set pc->flags
update page stat accounting
move_unlock_mem_cgroup(mem)
stat accounting is guarded by move_lock_mem_cgroup() and "move" logic
(CPU-A) doesn't see changes in "struct page" information.
But it's costly to have the same information both in 'struct page' and
'struct page_cgroup'. And, there is a potential problem.
For example, assume we have PG_dirty accounting in memcg.
PG_..is a flag for struct page.
PCG_ is a flag for struct page_cgroup.
(This is just an example. The same problem can be found in any
kind of page stat accounting.)
CPU-A CPU-B
TestSet PG_dirty
(delay) TestClear PG_dirty
if (TestClear(PCG_dirty))
memcg->nr_dirty--
if (TestSet(PCG_dirty))
memcg->nr_dirty++
Here, memcg->nr_dirty = +1, this is wrong. This race was reported by Greg
Thelen <gthelen@google.com>. Now, only FILE_MAPPED is supported but
fortunately, it's serialized by page table lock and this is not real bug,
_now_,
If this potential problem is caused by having duplicated information in
struct page and struct page_cgroup, we may be able to fix this by using
original 'struct page' information. But we'll have a problem in "move
account"
Assume we use only PG_dirty.
CPU-A CPU-B
TestSet PG_dirty
(delay) move_lock_mem_cgroup()
if (PageDirty(page))
new_memcg->nr_dirty++
pc->mem_cgroup = new_memcg;
move_unlock_mem_cgroup()
move_lock_mem_cgroup()
memcg = pc->mem_cgroup
new_memcg->nr_dirty++
accounting information may be double-counted. This was original reason to
have PCG_xxx flags but it seems PCG_xxx has another problem.
I think we need a bigger lock as
move_lock_mem_cgroup(page)
TestSetPageDirty(page)
update page stats (without any checks)
move_unlock_mem_cgroup(page)
This fixes both of problems and we don't have to duplicate page flag into
page_cgroup. Please note: move_lock_mem_cgroup() is held only when there
are possibility of "account move" under the system. So, in most path,
status update will go without atomic locks.
This patch introduces mem_cgroup_begin_update_page_stat() and
mem_cgroup_end_update_page_stat() both should be called at modifying
'struct page' information if memcg takes care of it. as
mem_cgroup_begin_update_page_stat()
modify page information
mem_cgroup_update_page_stat()
=> never check any 'struct page' info, just update counters.
mem_cgroup_end_update_page_stat().
This patch is slow because we need to call begin_update_page_stat()/
end_update_page_stat() regardless of accounted will be changed or not. A
following patch adds an easy optimization and reduces the cost.
[akpm@linux-foundation.org: s/lock/locked/]
[hughd@google.com: fix deadlock by avoiding stat lock when anon]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Greg Thelen <gthelen@google.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-22 06:34:25 +07:00
|
|
|
void __mem_cgroup_begin_update_page_stat(struct page *page,
|
|
|
|
bool *locked, unsigned long *flags)
|
|
|
|
{
|
|
|
|
struct mem_cgroup *memcg;
|
|
|
|
struct page_cgroup *pc;
|
|
|
|
|
|
|
|
pc = lookup_page_cgroup(page);
|
|
|
|
again:
|
|
|
|
memcg = pc->mem_cgroup;
|
|
|
|
if (unlikely(!memcg || !PageCgroupUsed(pc)))
|
|
|
|
return;
|
|
|
|
/*
|
|
|
|
* If this memory cgroup is not under account moving, we don't
|
2012-08-01 06:43:26 +07:00
|
|
|
* need to take move_lock_mem_cgroup(). Because we already hold
|
memcg: use new logic for page stat accounting
Now, page-stat-per-memcg is recorded into per page_cgroup flag by
duplicating page's status into the flag. The reason is that memcg has a
feature to move a page from a group to another group and we have race
between "move" and "page stat accounting",
Under current logic, assume CPU-A and CPU-B. CPU-A does "move" and CPU-B
does "page stat accounting".
When CPU-A goes 1st,
CPU-A CPU-B
update "struct page" info.
move_lock_mem_cgroup(memcg)
see pc->flags
copy page stat to new group
overwrite pc->mem_cgroup.
move_unlock_mem_cgroup(memcg)
move_lock_mem_cgroup(mem)
set pc->flags
update page stat accounting
move_unlock_mem_cgroup(mem)
stat accounting is guarded by move_lock_mem_cgroup() and "move" logic
(CPU-A) doesn't see changes in "struct page" information.
But it's costly to have the same information both in 'struct page' and
'struct page_cgroup'. And, there is a potential problem.
For example, assume we have PG_dirty accounting in memcg.
PG_..is a flag for struct page.
PCG_ is a flag for struct page_cgroup.
(This is just an example. The same problem can be found in any
kind of page stat accounting.)
CPU-A CPU-B
TestSet PG_dirty
(delay) TestClear PG_dirty
if (TestClear(PCG_dirty))
memcg->nr_dirty--
if (TestSet(PCG_dirty))
memcg->nr_dirty++
Here, memcg->nr_dirty = +1, this is wrong. This race was reported by Greg
Thelen <gthelen@google.com>. Now, only FILE_MAPPED is supported but
fortunately, it's serialized by page table lock and this is not real bug,
_now_,
If this potential problem is caused by having duplicated information in
struct page and struct page_cgroup, we may be able to fix this by using
original 'struct page' information. But we'll have a problem in "move
account"
Assume we use only PG_dirty.
CPU-A CPU-B
TestSet PG_dirty
(delay) move_lock_mem_cgroup()
if (PageDirty(page))
new_memcg->nr_dirty++
pc->mem_cgroup = new_memcg;
move_unlock_mem_cgroup()
move_lock_mem_cgroup()
memcg = pc->mem_cgroup
new_memcg->nr_dirty++
accounting information may be double-counted. This was original reason to
have PCG_xxx flags but it seems PCG_xxx has another problem.
I think we need a bigger lock as
move_lock_mem_cgroup(page)
TestSetPageDirty(page)
update page stats (without any checks)
move_unlock_mem_cgroup(page)
This fixes both of problems and we don't have to duplicate page flag into
page_cgroup. Please note: move_lock_mem_cgroup() is held only when there
are possibility of "account move" under the system. So, in most path,
status update will go without atomic locks.
This patch introduces mem_cgroup_begin_update_page_stat() and
mem_cgroup_end_update_page_stat() both should be called at modifying
'struct page' information if memcg takes care of it. as
mem_cgroup_begin_update_page_stat()
modify page information
mem_cgroup_update_page_stat()
=> never check any 'struct page' info, just update counters.
mem_cgroup_end_update_page_stat().
This patch is slow because we need to call begin_update_page_stat()/
end_update_page_stat() regardless of accounted will be changed or not. A
following patch adds an easy optimization and reduces the cost.
[akpm@linux-foundation.org: s/lock/locked/]
[hughd@google.com: fix deadlock by avoiding stat lock when anon]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Greg Thelen <gthelen@google.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-22 06:34:25 +07:00
|
|
|
* rcu_read_lock(), any calls to move_account will be delayed until
|
2012-03-22 06:34:26 +07:00
|
|
|
* rcu_read_unlock() if mem_cgroup_stolen() == true.
|
memcg: use new logic for page stat accounting
Now, page-stat-per-memcg is recorded into per page_cgroup flag by
duplicating page's status into the flag. The reason is that memcg has a
feature to move a page from a group to another group and we have race
between "move" and "page stat accounting",
Under current logic, assume CPU-A and CPU-B. CPU-A does "move" and CPU-B
does "page stat accounting".
When CPU-A goes 1st,
CPU-A CPU-B
update "struct page" info.
move_lock_mem_cgroup(memcg)
see pc->flags
copy page stat to new group
overwrite pc->mem_cgroup.
move_unlock_mem_cgroup(memcg)
move_lock_mem_cgroup(mem)
set pc->flags
update page stat accounting
move_unlock_mem_cgroup(mem)
stat accounting is guarded by move_lock_mem_cgroup() and "move" logic
(CPU-A) doesn't see changes in "struct page" information.
But it's costly to have the same information both in 'struct page' and
'struct page_cgroup'. And, there is a potential problem.
For example, assume we have PG_dirty accounting in memcg.
PG_..is a flag for struct page.
PCG_ is a flag for struct page_cgroup.
(This is just an example. The same problem can be found in any
kind of page stat accounting.)
CPU-A CPU-B
TestSet PG_dirty
(delay) TestClear PG_dirty
if (TestClear(PCG_dirty))
memcg->nr_dirty--
if (TestSet(PCG_dirty))
memcg->nr_dirty++
Here, memcg->nr_dirty = +1, this is wrong. This race was reported by Greg
Thelen <gthelen@google.com>. Now, only FILE_MAPPED is supported but
fortunately, it's serialized by page table lock and this is not real bug,
_now_,
If this potential problem is caused by having duplicated information in
struct page and struct page_cgroup, we may be able to fix this by using
original 'struct page' information. But we'll have a problem in "move
account"
Assume we use only PG_dirty.
CPU-A CPU-B
TestSet PG_dirty
(delay) move_lock_mem_cgroup()
if (PageDirty(page))
new_memcg->nr_dirty++
pc->mem_cgroup = new_memcg;
move_unlock_mem_cgroup()
move_lock_mem_cgroup()
memcg = pc->mem_cgroup
new_memcg->nr_dirty++
accounting information may be double-counted. This was original reason to
have PCG_xxx flags but it seems PCG_xxx has another problem.
I think we need a bigger lock as
move_lock_mem_cgroup(page)
TestSetPageDirty(page)
update page stats (without any checks)
move_unlock_mem_cgroup(page)
This fixes both of problems and we don't have to duplicate page flag into
page_cgroup. Please note: move_lock_mem_cgroup() is held only when there
are possibility of "account move" under the system. So, in most path,
status update will go without atomic locks.
This patch introduces mem_cgroup_begin_update_page_stat() and
mem_cgroup_end_update_page_stat() both should be called at modifying
'struct page' information if memcg takes care of it. as
mem_cgroup_begin_update_page_stat()
modify page information
mem_cgroup_update_page_stat()
=> never check any 'struct page' info, just update counters.
mem_cgroup_end_update_page_stat().
This patch is slow because we need to call begin_update_page_stat()/
end_update_page_stat() regardless of accounted will be changed or not. A
following patch adds an easy optimization and reduces the cost.
[akpm@linux-foundation.org: s/lock/locked/]
[hughd@google.com: fix deadlock by avoiding stat lock when anon]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Greg Thelen <gthelen@google.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-22 06:34:25 +07:00
|
|
|
*/
|
2012-03-22 06:34:26 +07:00
|
|
|
if (!mem_cgroup_stolen(memcg))
|
memcg: use new logic for page stat accounting
Now, page-stat-per-memcg is recorded into per page_cgroup flag by
duplicating page's status into the flag. The reason is that memcg has a
feature to move a page from a group to another group and we have race
between "move" and "page stat accounting",
Under current logic, assume CPU-A and CPU-B. CPU-A does "move" and CPU-B
does "page stat accounting".
When CPU-A goes 1st,
CPU-A CPU-B
update "struct page" info.
move_lock_mem_cgroup(memcg)
see pc->flags
copy page stat to new group
overwrite pc->mem_cgroup.
move_unlock_mem_cgroup(memcg)
move_lock_mem_cgroup(mem)
set pc->flags
update page stat accounting
move_unlock_mem_cgroup(mem)
stat accounting is guarded by move_lock_mem_cgroup() and "move" logic
(CPU-A) doesn't see changes in "struct page" information.
But it's costly to have the same information both in 'struct page' and
'struct page_cgroup'. And, there is a potential problem.
For example, assume we have PG_dirty accounting in memcg.
PG_..is a flag for struct page.
PCG_ is a flag for struct page_cgroup.
(This is just an example. The same problem can be found in any
kind of page stat accounting.)
CPU-A CPU-B
TestSet PG_dirty
(delay) TestClear PG_dirty
if (TestClear(PCG_dirty))
memcg->nr_dirty--
if (TestSet(PCG_dirty))
memcg->nr_dirty++
Here, memcg->nr_dirty = +1, this is wrong. This race was reported by Greg
Thelen <gthelen@google.com>. Now, only FILE_MAPPED is supported but
fortunately, it's serialized by page table lock and this is not real bug,
_now_,
If this potential problem is caused by having duplicated information in
struct page and struct page_cgroup, we may be able to fix this by using
original 'struct page' information. But we'll have a problem in "move
account"
Assume we use only PG_dirty.
CPU-A CPU-B
TestSet PG_dirty
(delay) move_lock_mem_cgroup()
if (PageDirty(page))
new_memcg->nr_dirty++
pc->mem_cgroup = new_memcg;
move_unlock_mem_cgroup()
move_lock_mem_cgroup()
memcg = pc->mem_cgroup
new_memcg->nr_dirty++
accounting information may be double-counted. This was original reason to
have PCG_xxx flags but it seems PCG_xxx has another problem.
I think we need a bigger lock as
move_lock_mem_cgroup(page)
TestSetPageDirty(page)
update page stats (without any checks)
move_unlock_mem_cgroup(page)
This fixes both of problems and we don't have to duplicate page flag into
page_cgroup. Please note: move_lock_mem_cgroup() is held only when there
are possibility of "account move" under the system. So, in most path,
status update will go without atomic locks.
This patch introduces mem_cgroup_begin_update_page_stat() and
mem_cgroup_end_update_page_stat() both should be called at modifying
'struct page' information if memcg takes care of it. as
mem_cgroup_begin_update_page_stat()
modify page information
mem_cgroup_update_page_stat()
=> never check any 'struct page' info, just update counters.
mem_cgroup_end_update_page_stat().
This patch is slow because we need to call begin_update_page_stat()/
end_update_page_stat() regardless of accounted will be changed or not. A
following patch adds an easy optimization and reduces the cost.
[akpm@linux-foundation.org: s/lock/locked/]
[hughd@google.com: fix deadlock by avoiding stat lock when anon]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Greg Thelen <gthelen@google.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-22 06:34:25 +07:00
|
|
|
return;
|
|
|
|
|
|
|
|
move_lock_mem_cgroup(memcg, flags);
|
|
|
|
if (memcg != pc->mem_cgroup || !PageCgroupUsed(pc)) {
|
|
|
|
move_unlock_mem_cgroup(memcg, flags);
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
*locked = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void __mem_cgroup_end_update_page_stat(struct page *page, unsigned long *flags)
|
|
|
|
{
|
|
|
|
struct page_cgroup *pc = lookup_page_cgroup(page);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* It's guaranteed that pc->mem_cgroup never changes while
|
|
|
|
* lock is held because a routine modifies pc->mem_cgroup
|
2012-08-01 06:43:26 +07:00
|
|
|
* should take move_lock_mem_cgroup().
|
memcg: use new logic for page stat accounting
Now, page-stat-per-memcg is recorded into per page_cgroup flag by
duplicating page's status into the flag. The reason is that memcg has a
feature to move a page from a group to another group and we have race
between "move" and "page stat accounting",
Under current logic, assume CPU-A and CPU-B. CPU-A does "move" and CPU-B
does "page stat accounting".
When CPU-A goes 1st,
CPU-A CPU-B
update "struct page" info.
move_lock_mem_cgroup(memcg)
see pc->flags
copy page stat to new group
overwrite pc->mem_cgroup.
move_unlock_mem_cgroup(memcg)
move_lock_mem_cgroup(mem)
set pc->flags
update page stat accounting
move_unlock_mem_cgroup(mem)
stat accounting is guarded by move_lock_mem_cgroup() and "move" logic
(CPU-A) doesn't see changes in "struct page" information.
But it's costly to have the same information both in 'struct page' and
'struct page_cgroup'. And, there is a potential problem.
For example, assume we have PG_dirty accounting in memcg.
PG_..is a flag for struct page.
PCG_ is a flag for struct page_cgroup.
(This is just an example. The same problem can be found in any
kind of page stat accounting.)
CPU-A CPU-B
TestSet PG_dirty
(delay) TestClear PG_dirty
if (TestClear(PCG_dirty))
memcg->nr_dirty--
if (TestSet(PCG_dirty))
memcg->nr_dirty++
Here, memcg->nr_dirty = +1, this is wrong. This race was reported by Greg
Thelen <gthelen@google.com>. Now, only FILE_MAPPED is supported but
fortunately, it's serialized by page table lock and this is not real bug,
_now_,
If this potential problem is caused by having duplicated information in
struct page and struct page_cgroup, we may be able to fix this by using
original 'struct page' information. But we'll have a problem in "move
account"
Assume we use only PG_dirty.
CPU-A CPU-B
TestSet PG_dirty
(delay) move_lock_mem_cgroup()
if (PageDirty(page))
new_memcg->nr_dirty++
pc->mem_cgroup = new_memcg;
move_unlock_mem_cgroup()
move_lock_mem_cgroup()
memcg = pc->mem_cgroup
new_memcg->nr_dirty++
accounting information may be double-counted. This was original reason to
have PCG_xxx flags but it seems PCG_xxx has another problem.
I think we need a bigger lock as
move_lock_mem_cgroup(page)
TestSetPageDirty(page)
update page stats (without any checks)
move_unlock_mem_cgroup(page)
This fixes both of problems and we don't have to duplicate page flag into
page_cgroup. Please note: move_lock_mem_cgroup() is held only when there
are possibility of "account move" under the system. So, in most path,
status update will go without atomic locks.
This patch introduces mem_cgroup_begin_update_page_stat() and
mem_cgroup_end_update_page_stat() both should be called at modifying
'struct page' information if memcg takes care of it. as
mem_cgroup_begin_update_page_stat()
modify page information
mem_cgroup_update_page_stat()
=> never check any 'struct page' info, just update counters.
mem_cgroup_end_update_page_stat().
This patch is slow because we need to call begin_update_page_stat()/
end_update_page_stat() regardless of accounted will be changed or not. A
following patch adds an easy optimization and reduces the cost.
[akpm@linux-foundation.org: s/lock/locked/]
[hughd@google.com: fix deadlock by avoiding stat lock when anon]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Greg Thelen <gthelen@google.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-22 06:34:25 +07:00
|
|
|
*/
|
|
|
|
move_unlock_mem_cgroup(pc->mem_cgroup, flags);
|
|
|
|
}
|
|
|
|
|
2011-01-14 06:47:37 +07:00
|
|
|
void mem_cgroup_update_page_stat(struct page *page,
|
|
|
|
enum mem_cgroup_page_stat_item idx, int val)
|
2009-06-18 06:26:34 +07:00
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
struct mem_cgroup *memcg;
|
memcg: avoid lock in updating file_mapped (Was fix race in file_mapped accouting flag management
At accounting file events per memory cgroup, we need to find memory cgroup
via page_cgroup->mem_cgroup. Now, we use lock_page_cgroup() for guarantee
pc->mem_cgroup is not overwritten while we make use of it.
But, considering the context which page-cgroup for files are accessed,
we can use alternative light-weight mutual execusion in the most case.
At handling file-caches, the only race we have to take care of is "moving"
account, IOW, overwriting page_cgroup->mem_cgroup. (See comment in the
patch)
Unlike charge/uncharge, "move" happens not so frequently. It happens only when
rmdir() and task-moving (with a special settings.)
This patch adds a race-checker for file-cache-status accounting v.s. account
moving. The new per-cpu-per-memcg counter MEM_CGROUP_ON_MOVE is added.
The routine for account move
1. Increment it before start moving
2. Call synchronize_rcu()
3. Decrement it after the end of moving.
By this, file-status-counting routine can check it needs to call
lock_page_cgroup(). In most case, I doesn't need to call it.
Following is a perf data of a process which mmap()/munmap 32MB of file cache
in a minute.
Before patch:
28.25% mmap mmap [.] main
22.64% mmap [kernel.kallsyms] [k] page_fault
9.96% mmap [kernel.kallsyms] [k] mem_cgroup_update_file_mapped
3.67% mmap [kernel.kallsyms] [k] filemap_fault
3.50% mmap [kernel.kallsyms] [k] unmap_vmas
2.99% mmap [kernel.kallsyms] [k] __do_fault
2.76% mmap [kernel.kallsyms] [k] find_get_page
After patch:
30.00% mmap mmap [.] main
23.78% mmap [kernel.kallsyms] [k] page_fault
5.52% mmap [kernel.kallsyms] [k] mem_cgroup_update_file_mapped
3.81% mmap [kernel.kallsyms] [k] unmap_vmas
3.26% mmap [kernel.kallsyms] [k] find_get_page
3.18% mmap [kernel.kallsyms] [k] __do_fault
3.03% mmap [kernel.kallsyms] [k] filemap_fault
2.40% mmap [kernel.kallsyms] [k] handle_mm_fault
2.40% mmap [kernel.kallsyms] [k] do_page_fault
This patch reduces memcg's cost to some extent.
(mem_cgroup_update_file_mapped is called by both of map/unmap)
Note: It seems some more improvements are required..but no idea.
maybe removing set/unset flag is required.
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Greg Thelen <gthelen@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-10-28 05:33:40 +07:00
|
|
|
struct page_cgroup *pc = lookup_page_cgroup(page);
|
2011-01-14 06:47:38 +07:00
|
|
|
unsigned long uninitialized_var(flags);
|
2009-06-18 06:26:34 +07:00
|
|
|
|
2012-01-13 08:18:38 +07:00
|
|
|
if (mem_cgroup_disabled())
|
2009-06-18 06:26:34 +07:00
|
|
|
return;
|
memcg: use new logic for page stat accounting
Now, page-stat-per-memcg is recorded into per page_cgroup flag by
duplicating page's status into the flag. The reason is that memcg has a
feature to move a page from a group to another group and we have race
between "move" and "page stat accounting",
Under current logic, assume CPU-A and CPU-B. CPU-A does "move" and CPU-B
does "page stat accounting".
When CPU-A goes 1st,
CPU-A CPU-B
update "struct page" info.
move_lock_mem_cgroup(memcg)
see pc->flags
copy page stat to new group
overwrite pc->mem_cgroup.
move_unlock_mem_cgroup(memcg)
move_lock_mem_cgroup(mem)
set pc->flags
update page stat accounting
move_unlock_mem_cgroup(mem)
stat accounting is guarded by move_lock_mem_cgroup() and "move" logic
(CPU-A) doesn't see changes in "struct page" information.
But it's costly to have the same information both in 'struct page' and
'struct page_cgroup'. And, there is a potential problem.
For example, assume we have PG_dirty accounting in memcg.
PG_..is a flag for struct page.
PCG_ is a flag for struct page_cgroup.
(This is just an example. The same problem can be found in any
kind of page stat accounting.)
CPU-A CPU-B
TestSet PG_dirty
(delay) TestClear PG_dirty
if (TestClear(PCG_dirty))
memcg->nr_dirty--
if (TestSet(PCG_dirty))
memcg->nr_dirty++
Here, memcg->nr_dirty = +1, this is wrong. This race was reported by Greg
Thelen <gthelen@google.com>. Now, only FILE_MAPPED is supported but
fortunately, it's serialized by page table lock and this is not real bug,
_now_,
If this potential problem is caused by having duplicated information in
struct page and struct page_cgroup, we may be able to fix this by using
original 'struct page' information. But we'll have a problem in "move
account"
Assume we use only PG_dirty.
CPU-A CPU-B
TestSet PG_dirty
(delay) move_lock_mem_cgroup()
if (PageDirty(page))
new_memcg->nr_dirty++
pc->mem_cgroup = new_memcg;
move_unlock_mem_cgroup()
move_lock_mem_cgroup()
memcg = pc->mem_cgroup
new_memcg->nr_dirty++
accounting information may be double-counted. This was original reason to
have PCG_xxx flags but it seems PCG_xxx has another problem.
I think we need a bigger lock as
move_lock_mem_cgroup(page)
TestSetPageDirty(page)
update page stats (without any checks)
move_unlock_mem_cgroup(page)
This fixes both of problems and we don't have to duplicate page flag into
page_cgroup. Please note: move_lock_mem_cgroup() is held only when there
are possibility of "account move" under the system. So, in most path,
status update will go without atomic locks.
This patch introduces mem_cgroup_begin_update_page_stat() and
mem_cgroup_end_update_page_stat() both should be called at modifying
'struct page' information if memcg takes care of it. as
mem_cgroup_begin_update_page_stat()
modify page information
mem_cgroup_update_page_stat()
=> never check any 'struct page' info, just update counters.
mem_cgroup_end_update_page_stat().
This patch is slow because we need to call begin_update_page_stat()/
end_update_page_stat() regardless of accounted will be changed or not. A
following patch adds an easy optimization and reduces the cost.
[akpm@linux-foundation.org: s/lock/locked/]
[hughd@google.com: fix deadlock by avoiding stat lock when anon]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Greg Thelen <gthelen@google.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-22 06:34:25 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
memcg = pc->mem_cgroup;
|
|
|
|
if (unlikely(!memcg || !PageCgroupUsed(pc)))
|
memcg: use new logic for page stat accounting
Now, page-stat-per-memcg is recorded into per page_cgroup flag by
duplicating page's status into the flag. The reason is that memcg has a
feature to move a page from a group to another group and we have race
between "move" and "page stat accounting",
Under current logic, assume CPU-A and CPU-B. CPU-A does "move" and CPU-B
does "page stat accounting".
When CPU-A goes 1st,
CPU-A CPU-B
update "struct page" info.
move_lock_mem_cgroup(memcg)
see pc->flags
copy page stat to new group
overwrite pc->mem_cgroup.
move_unlock_mem_cgroup(memcg)
move_lock_mem_cgroup(mem)
set pc->flags
update page stat accounting
move_unlock_mem_cgroup(mem)
stat accounting is guarded by move_lock_mem_cgroup() and "move" logic
(CPU-A) doesn't see changes in "struct page" information.
But it's costly to have the same information both in 'struct page' and
'struct page_cgroup'. And, there is a potential problem.
For example, assume we have PG_dirty accounting in memcg.
PG_..is a flag for struct page.
PCG_ is a flag for struct page_cgroup.
(This is just an example. The same problem can be found in any
kind of page stat accounting.)
CPU-A CPU-B
TestSet PG_dirty
(delay) TestClear PG_dirty
if (TestClear(PCG_dirty))
memcg->nr_dirty--
if (TestSet(PCG_dirty))
memcg->nr_dirty++
Here, memcg->nr_dirty = +1, this is wrong. This race was reported by Greg
Thelen <gthelen@google.com>. Now, only FILE_MAPPED is supported but
fortunately, it's serialized by page table lock and this is not real bug,
_now_,
If this potential problem is caused by having duplicated information in
struct page and struct page_cgroup, we may be able to fix this by using
original 'struct page' information. But we'll have a problem in "move
account"
Assume we use only PG_dirty.
CPU-A CPU-B
TestSet PG_dirty
(delay) move_lock_mem_cgroup()
if (PageDirty(page))
new_memcg->nr_dirty++
pc->mem_cgroup = new_memcg;
move_unlock_mem_cgroup()
move_lock_mem_cgroup()
memcg = pc->mem_cgroup
new_memcg->nr_dirty++
accounting information may be double-counted. This was original reason to
have PCG_xxx flags but it seems PCG_xxx has another problem.
I think we need a bigger lock as
move_lock_mem_cgroup(page)
TestSetPageDirty(page)
update page stats (without any checks)
move_unlock_mem_cgroup(page)
This fixes both of problems and we don't have to duplicate page flag into
page_cgroup. Please note: move_lock_mem_cgroup() is held only when there
are possibility of "account move" under the system. So, in most path,
status update will go without atomic locks.
This patch introduces mem_cgroup_begin_update_page_stat() and
mem_cgroup_end_update_page_stat() both should be called at modifying
'struct page' information if memcg takes care of it. as
mem_cgroup_begin_update_page_stat()
modify page information
mem_cgroup_update_page_stat()
=> never check any 'struct page' info, just update counters.
mem_cgroup_end_update_page_stat().
This patch is slow because we need to call begin_update_page_stat()/
end_update_page_stat() regardless of accounted will be changed or not. A
following patch adds an easy optimization and reduces the cost.
[akpm@linux-foundation.org: s/lock/locked/]
[hughd@google.com: fix deadlock by avoiding stat lock when anon]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Greg Thelen <gthelen@google.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-22 06:34:25 +07:00
|
|
|
return;
|
2010-10-28 05:33:43 +07:00
|
|
|
|
|
|
|
switch (idx) {
|
2011-01-14 06:47:37 +07:00
|
|
|
case MEMCG_NR_FILE_MAPPED:
|
|
|
|
idx = MEM_CGROUP_STAT_FILE_MAPPED;
|
2010-10-28 05:33:43 +07:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
BUG();
|
2010-04-07 04:35:05 +07:00
|
|
|
}
|
2009-06-18 06:26:34 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
this_cpu_add(memcg->stat->count[idx], val);
|
2009-06-18 06:26:34 +07:00
|
|
|
}
|
2010-10-28 05:33:43 +07:00
|
|
|
|
2009-12-16 07:47:08 +07:00
|
|
|
/*
|
|
|
|
* size of first charge trial. "32" comes from vmscan.c's magic value.
|
|
|
|
* TODO: maybe necessary to use big numbers in big irons.
|
|
|
|
*/
|
2011-03-24 06:42:36 +07:00
|
|
|
#define CHARGE_BATCH 32U
|
2009-12-16 07:47:08 +07:00
|
|
|
struct memcg_stock_pcp {
|
|
|
|
struct mem_cgroup *cached; /* this never be root cgroup */
|
2011-03-24 06:42:34 +07:00
|
|
|
unsigned int nr_pages;
|
2009-12-16 07:47:08 +07:00
|
|
|
struct work_struct work;
|
2011-06-16 05:08:45 +07:00
|
|
|
unsigned long flags;
|
2012-05-30 05:06:56 +07:00
|
|
|
#define FLUSHING_CACHED_CHARGE 0
|
2009-12-16 07:47:08 +07:00
|
|
|
};
|
|
|
|
static DEFINE_PER_CPU(struct memcg_stock_pcp, memcg_stock);
|
2011-08-09 16:56:26 +07:00
|
|
|
static DEFINE_MUTEX(percpu_charge_mutex);
|
2009-12-16 07:47:08 +07:00
|
|
|
|
|
|
|
/*
|
2011-03-24 06:42:34 +07:00
|
|
|
* Try to consume stocked charge on this cpu. If success, one page is consumed
|
2009-12-16 07:47:08 +07:00
|
|
|
* from local stock and true is returned. If the stock is 0 or charges from a
|
|
|
|
* cgroup which is not current target, returns false. This stock will be
|
|
|
|
* refilled.
|
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
static bool consume_stock(struct mem_cgroup *memcg)
|
2009-12-16 07:47:08 +07:00
|
|
|
{
|
|
|
|
struct memcg_stock_pcp *stock;
|
|
|
|
bool ret = true;
|
|
|
|
|
|
|
|
stock = &get_cpu_var(memcg_stock);
|
2011-11-03 03:38:15 +07:00
|
|
|
if (memcg == stock->cached && stock->nr_pages)
|
2011-03-24 06:42:34 +07:00
|
|
|
stock->nr_pages--;
|
2009-12-16 07:47:08 +07:00
|
|
|
else /* need to call res_counter_charge */
|
|
|
|
ret = false;
|
|
|
|
put_cpu_var(memcg_stock);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Returns stocks cached in percpu to res_counter and reset cached information.
|
|
|
|
*/
|
|
|
|
static void drain_stock(struct memcg_stock_pcp *stock)
|
|
|
|
{
|
|
|
|
struct mem_cgroup *old = stock->cached;
|
|
|
|
|
2011-03-24 06:42:34 +07:00
|
|
|
if (stock->nr_pages) {
|
|
|
|
unsigned long bytes = stock->nr_pages * PAGE_SIZE;
|
|
|
|
|
|
|
|
res_counter_uncharge(&old->res, bytes);
|
2009-12-16 07:47:08 +07:00
|
|
|
if (do_swap_account)
|
2011-03-24 06:42:34 +07:00
|
|
|
res_counter_uncharge(&old->memsw, bytes);
|
|
|
|
stock->nr_pages = 0;
|
2009-12-16 07:47:08 +07:00
|
|
|
}
|
|
|
|
stock->cached = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This must be called under preempt disabled or must be called by
|
|
|
|
* a thread which is pinned to local cpu.
|
|
|
|
*/
|
|
|
|
static void drain_local_stock(struct work_struct *dummy)
|
|
|
|
{
|
|
|
|
struct memcg_stock_pcp *stock = &__get_cpu_var(memcg_stock);
|
|
|
|
drain_stock(stock);
|
2011-06-16 05:08:45 +07:00
|
|
|
clear_bit(FLUSHING_CACHED_CHARGE, &stock->flags);
|
2009-12-16 07:47:08 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Cache charges(val) which is from res_counter, to local per_cpu area.
|
2010-03-15 21:27:28 +07:00
|
|
|
* This will be consumed by consume_stock() function, later.
|
2009-12-16 07:47:08 +07:00
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
static void refill_stock(struct mem_cgroup *memcg, unsigned int nr_pages)
|
2009-12-16 07:47:08 +07:00
|
|
|
{
|
|
|
|
struct memcg_stock_pcp *stock = &get_cpu_var(memcg_stock);
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
if (stock->cached != memcg) { /* reset if necessary */
|
2009-12-16 07:47:08 +07:00
|
|
|
drain_stock(stock);
|
2011-11-03 03:38:15 +07:00
|
|
|
stock->cached = memcg;
|
2009-12-16 07:47:08 +07:00
|
|
|
}
|
2011-03-24 06:42:34 +07:00
|
|
|
stock->nr_pages += nr_pages;
|
2009-12-16 07:47:08 +07:00
|
|
|
put_cpu_var(memcg_stock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2011-11-03 03:38:15 +07:00
|
|
|
* Drains all per-CPU charge caches for given root_memcg resp. subtree
|
2011-07-27 06:08:28 +07:00
|
|
|
* of the hierarchy under it. sync flag says whether we should block
|
|
|
|
* until the work is done.
|
2009-12-16 07:47:08 +07:00
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
static void drain_all_stock(struct mem_cgroup *root_memcg, bool sync)
|
2009-12-16 07:47:08 +07:00
|
|
|
{
|
2011-06-16 05:08:45 +07:00
|
|
|
int cpu, curcpu;
|
2011-07-27 06:08:28 +07:00
|
|
|
|
2009-12-16 07:47:08 +07:00
|
|
|
/* Notify other cpus that system-wide "drain" is running */
|
|
|
|
get_online_cpus();
|
2011-08-26 05:59:07 +07:00
|
|
|
curcpu = get_cpu();
|
2009-12-16 07:47:08 +07:00
|
|
|
for_each_online_cpu(cpu) {
|
|
|
|
struct memcg_stock_pcp *stock = &per_cpu(memcg_stock, cpu);
|
2011-11-03 03:38:15 +07:00
|
|
|
struct mem_cgroup *memcg;
|
2011-06-16 05:08:45 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
memcg = stock->cached;
|
|
|
|
if (!memcg || !stock->nr_pages)
|
2011-06-16 05:08:45 +07:00
|
|
|
continue;
|
2011-11-03 03:38:15 +07:00
|
|
|
if (!mem_cgroup_same_or_subtree(root_memcg, memcg))
|
2011-07-27 06:08:29 +07:00
|
|
|
continue;
|
2011-07-27 06:08:27 +07:00
|
|
|
if (!test_and_set_bit(FLUSHING_CACHED_CHARGE, &stock->flags)) {
|
|
|
|
if (cpu == curcpu)
|
|
|
|
drain_local_stock(&stock->work);
|
|
|
|
else
|
|
|
|
schedule_work_on(cpu, &stock->work);
|
|
|
|
}
|
2009-12-16 07:47:08 +07:00
|
|
|
}
|
2011-08-26 05:59:07 +07:00
|
|
|
put_cpu();
|
2011-07-27 06:08:28 +07:00
|
|
|
|
|
|
|
if (!sync)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
for_each_online_cpu(cpu) {
|
|
|
|
struct memcg_stock_pcp *stock = &per_cpu(memcg_stock, cpu);
|
2011-08-09 16:56:26 +07:00
|
|
|
if (test_bit(FLUSHING_CACHED_CHARGE, &stock->flags))
|
2011-07-27 06:08:28 +07:00
|
|
|
flush_work(&stock->work);
|
|
|
|
}
|
|
|
|
out:
|
2009-12-16 07:47:08 +07:00
|
|
|
put_online_cpus();
|
2011-07-27 06:08:28 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Tries to drain stocked charges in other cpus. This function is asynchronous
|
|
|
|
* and just put a work per cpu for draining localy on each cpu. Caller can
|
|
|
|
* expects some charges will be back to res_counter later but cannot wait for
|
|
|
|
* it.
|
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
static void drain_all_stock_async(struct mem_cgroup *root_memcg)
|
2011-07-27 06:08:28 +07:00
|
|
|
{
|
2011-08-09 16:56:26 +07:00
|
|
|
/*
|
|
|
|
* If someone calls draining, avoid adding more kworker runs.
|
|
|
|
*/
|
|
|
|
if (!mutex_trylock(&percpu_charge_mutex))
|
|
|
|
return;
|
2011-11-03 03:38:15 +07:00
|
|
|
drain_all_stock(root_memcg, false);
|
2011-08-09 16:56:26 +07:00
|
|
|
mutex_unlock(&percpu_charge_mutex);
|
2009-12-16 07:47:08 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* This is a synchronous drain interface. */
|
2011-11-03 03:38:15 +07:00
|
|
|
static void drain_all_stock_sync(struct mem_cgroup *root_memcg)
|
2009-12-16 07:47:08 +07:00
|
|
|
{
|
|
|
|
/* called when force_empty is called */
|
2011-08-09 16:56:26 +07:00
|
|
|
mutex_lock(&percpu_charge_mutex);
|
2011-11-03 03:38:15 +07:00
|
|
|
drain_all_stock(root_memcg, true);
|
2011-08-09 16:56:26 +07:00
|
|
|
mutex_unlock(&percpu_charge_mutex);
|
2009-12-16 07:47:08 +07:00
|
|
|
}
|
|
|
|
|
2010-10-28 05:33:42 +07:00
|
|
|
/*
|
|
|
|
* This function drains percpu counter value from DEAD cpu and
|
|
|
|
* move it to local cpu. Note that this function can be preempted.
|
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
static void mem_cgroup_drain_pcp_counter(struct mem_cgroup *memcg, int cpu)
|
2010-10-28 05:33:42 +07:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
spin_lock(&memcg->pcp_counter_lock);
|
2012-05-30 05:07:05 +07:00
|
|
|
for (i = 0; i < MEM_CGROUP_STAT_NSTATS; i++) {
|
2011-11-03 03:38:15 +07:00
|
|
|
long x = per_cpu(memcg->stat->count[i], cpu);
|
2010-10-28 05:33:42 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
per_cpu(memcg->stat->count[i], cpu) = 0;
|
|
|
|
memcg->nocpu_base.count[i] += x;
|
2010-10-28 05:33:42 +07:00
|
|
|
}
|
2011-03-24 06:42:37 +07:00
|
|
|
for (i = 0; i < MEM_CGROUP_EVENTS_NSTATS; i++) {
|
2011-11-03 03:38:15 +07:00
|
|
|
unsigned long x = per_cpu(memcg->stat->events[i], cpu);
|
2011-03-24 06:42:37 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
per_cpu(memcg->stat->events[i], cpu) = 0;
|
|
|
|
memcg->nocpu_base.events[i] += x;
|
2011-03-24 06:42:37 +07:00
|
|
|
}
|
2011-11-03 03:38:15 +07:00
|
|
|
spin_unlock(&memcg->pcp_counter_lock);
|
2010-10-28 05:33:42 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int __cpuinit memcg_cpu_hotplug_callback(struct notifier_block *nb,
|
2009-12-16 07:47:08 +07:00
|
|
|
unsigned long action,
|
|
|
|
void *hcpu)
|
|
|
|
{
|
|
|
|
int cpu = (unsigned long)hcpu;
|
|
|
|
struct memcg_stock_pcp *stock;
|
2010-10-28 05:33:42 +07:00
|
|
|
struct mem_cgroup *iter;
|
2009-12-16 07:47:08 +07:00
|
|
|
|
2012-03-22 06:34:23 +07:00
|
|
|
if (action == CPU_ONLINE)
|
2010-10-28 05:33:42 +07:00
|
|
|
return NOTIFY_OK;
|
|
|
|
|
2012-04-13 02:49:11 +07:00
|
|
|
if (action != CPU_DEAD && action != CPU_DEAD_FROZEN)
|
2009-12-16 07:47:08 +07:00
|
|
|
return NOTIFY_OK;
|
2010-10-28 05:33:42 +07:00
|
|
|
|
mm: memcg: consolidate hierarchy iteration primitives
The memcg naturalization series:
Memory control groups are currently bolted onto the side of
traditional memory management in places where better integration would
be preferrable. To reclaim memory, for example, memory control groups
maintain their own LRU list and reclaim strategy aside from the global
per-zone LRU list reclaim. But an extra list head for each existing
page frame is expensive and maintaining it requires additional code.
This patchset disables the global per-zone LRU lists on memory cgroup
configurations and converts all its users to operate on the per-memory
cgroup lists instead. As LRU pages are then exclusively on one list,
this saves two list pointers for each page frame in the system:
page_cgroup array size with 4G physical memory
vanilla: allocated 31457280 bytes of page_cgroup
patched: allocated 15728640 bytes of page_cgroup
At the same time, system performance for various workloads is
unaffected:
100G sparse file cat, 4G physical memory, 10 runs, to test for code
bloat in the traditional LRU handling and kswapd & direct reclaim
paths, without/with the memory controller configured in
vanilla: 71.603(0.207) seconds
patched: 71.640(0.156) seconds
vanilla: 79.558(0.288) seconds
patched: 77.233(0.147) seconds
100G sparse file cat in 1G memory cgroup, 10 runs, to test for code
bloat in the traditional memory cgroup LRU handling and reclaim path
vanilla: 96.844(0.281) seconds
patched: 94.454(0.311) seconds
4 unlimited memcgs running kbuild -j32 each, 4G physical memory, 500M
swap on SSD, 10 runs, to test for regressions in kswapd & direct
reclaim using per-memcg LRU lists with multiple memcgs and multiple
allocators within each memcg
vanilla: 717.722(1.440) seconds [ 69720.100(11600.835) majfaults ]
patched: 714.106(2.313) seconds [ 71109.300(14886.186) majfaults ]
16 unlimited memcgs running kbuild, 1900M hierarchical limit, 500M
swap on SSD, 10 runs, to test for regressions in hierarchical memcg
setups
vanilla: 2742.058(1.992) seconds [ 26479.600(1736.737) majfaults ]
patched: 2743.267(1.214) seconds [ 27240.700(1076.063) majfaults ]
This patch:
There are currently two different implementations of iterating over a
memory cgroup hierarchy tree.
Consolidate them into one worker function and base the convenience
looping-macros on top of it.
Signed-off-by: Johannes Weiner <jweiner@redhat.com>
Reviewed-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Reviewed-by: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Ying Han <yinghan@google.com>
Cc: Greg Thelen <gthelen@google.com>
Cc: Michel Lespinasse <walken@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Minchan Kim <minchan.kim@gmail.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:48 +07:00
|
|
|
for_each_mem_cgroup(iter)
|
2010-10-28 05:33:42 +07:00
|
|
|
mem_cgroup_drain_pcp_counter(iter, cpu);
|
|
|
|
|
2009-12-16 07:47:08 +07:00
|
|
|
stock = &per_cpu(memcg_stock, cpu);
|
|
|
|
drain_stock(stock);
|
|
|
|
return NOTIFY_OK;
|
|
|
|
}
|
|
|
|
|
2010-08-11 08:02:57 +07:00
|
|
|
|
|
|
|
/* See __mem_cgroup_try_charge() for details */
|
|
|
|
enum {
|
|
|
|
CHARGE_OK, /* success */
|
|
|
|
CHARGE_RETRY, /* need to retry but retry is not bad */
|
|
|
|
CHARGE_NOMEM, /* we can't do more. return -ENOMEM */
|
|
|
|
CHARGE_WOULDBLOCK, /* GFP_WAIT wasn't set and no enough res. */
|
|
|
|
CHARGE_OOM_DIE, /* the current is killed because of OOM */
|
|
|
|
};
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static int mem_cgroup_do_charge(struct mem_cgroup *memcg, gfp_t gfp_mask,
|
2011-03-24 06:42:36 +07:00
|
|
|
unsigned int nr_pages, bool oom_check)
|
2010-08-11 08:02:57 +07:00
|
|
|
{
|
2011-03-24 06:42:36 +07:00
|
|
|
unsigned long csize = nr_pages * PAGE_SIZE;
|
2010-08-11 08:02:57 +07:00
|
|
|
struct mem_cgroup *mem_over_limit;
|
|
|
|
struct res_counter *fail_res;
|
|
|
|
unsigned long flags = 0;
|
|
|
|
int ret;
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
ret = res_counter_charge(&memcg->res, csize, &fail_res);
|
2010-08-11 08:02:57 +07:00
|
|
|
|
|
|
|
if (likely(!ret)) {
|
|
|
|
if (!do_swap_account)
|
|
|
|
return CHARGE_OK;
|
2011-11-03 03:38:15 +07:00
|
|
|
ret = res_counter_charge(&memcg->memsw, csize, &fail_res);
|
2010-08-11 08:02:57 +07:00
|
|
|
if (likely(!ret))
|
|
|
|
return CHARGE_OK;
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
res_counter_uncharge(&memcg->res, csize);
|
2010-08-11 08:02:57 +07:00
|
|
|
mem_over_limit = mem_cgroup_from_res_counter(fail_res, memsw);
|
|
|
|
flags |= MEM_CGROUP_RECLAIM_NOSWAP;
|
|
|
|
} else
|
|
|
|
mem_over_limit = mem_cgroup_from_res_counter(fail_res, res);
|
2011-02-02 06:52:42 +07:00
|
|
|
/*
|
2011-03-24 06:42:36 +07:00
|
|
|
* nr_pages can be either a huge page (HPAGE_PMD_NR), a batch
|
|
|
|
* of regular pages (CHARGE_BATCH), or a single regular page (1).
|
2011-02-02 06:52:42 +07:00
|
|
|
*
|
|
|
|
* Never reclaim on behalf of optional batching, retry with a
|
|
|
|
* single page instead.
|
|
|
|
*/
|
2011-03-24 06:42:36 +07:00
|
|
|
if (nr_pages == CHARGE_BATCH)
|
2010-08-11 08:02:57 +07:00
|
|
|
return CHARGE_RETRY;
|
|
|
|
|
|
|
|
if (!(gfp_mask & __GFP_WAIT))
|
|
|
|
return CHARGE_WOULDBLOCK;
|
|
|
|
|
2012-01-13 08:17:59 +07:00
|
|
|
ret = mem_cgroup_reclaim(mem_over_limit, gfp_mask, flags);
|
2011-03-24 06:42:36 +07:00
|
|
|
if (mem_cgroup_margin(mem_over_limit) >= nr_pages)
|
2011-02-02 06:52:43 +07:00
|
|
|
return CHARGE_RETRY;
|
2010-08-11 08:02:57 +07:00
|
|
|
/*
|
2011-02-02 06:52:43 +07:00
|
|
|
* Even though the limit is exceeded at this point, reclaim
|
|
|
|
* may have been able to free some pages. Retry the charge
|
|
|
|
* before killing the task.
|
|
|
|
*
|
|
|
|
* Only for regular pages, though: huge pages are rather
|
|
|
|
* unlikely to succeed so close to the limit, and we fall back
|
|
|
|
* to regular pages anyway in case of failure.
|
2010-08-11 08:02:57 +07:00
|
|
|
*/
|
2011-03-24 06:42:36 +07:00
|
|
|
if (nr_pages == 1 && ret)
|
2010-08-11 08:02:57 +07:00
|
|
|
return CHARGE_RETRY;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* At task move, charge accounts can be doubly counted. So, it's
|
|
|
|
* better to wait until the end of task_move if something is going on.
|
|
|
|
*/
|
|
|
|
if (mem_cgroup_wait_acct_move(mem_over_limit))
|
|
|
|
return CHARGE_RETRY;
|
|
|
|
|
|
|
|
/* If we don't need to call oom-killer at el, return immediately */
|
|
|
|
if (!oom_check)
|
|
|
|
return CHARGE_NOMEM;
|
|
|
|
/* check OOM */
|
2012-03-22 06:34:10 +07:00
|
|
|
if (!mem_cgroup_handle_oom(mem_over_limit, gfp_mask, get_order(csize)))
|
2010-08-11 08:02:57 +07:00
|
|
|
return CHARGE_OOM_DIE;
|
|
|
|
|
|
|
|
return CHARGE_RETRY;
|
|
|
|
}
|
|
|
|
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
/*
|
2012-01-13 08:19:01 +07:00
|
|
|
* __mem_cgroup_try_charge() does
|
|
|
|
* 1. detect memcg to be charged against from passed *mm and *ptr,
|
|
|
|
* 2. update res_counter
|
|
|
|
* 3. call memory reclaim if necessary.
|
|
|
|
*
|
|
|
|
* In some special case, if the task is fatal, fatal_signal_pending() or
|
|
|
|
* has TIF_MEMDIE, this function returns -EINTR while writing root_mem_cgroup
|
|
|
|
* to *ptr. There are two reasons for this. 1: fatal threads should quit as soon
|
|
|
|
* as possible without any hazards. 2: all pages should have a valid
|
|
|
|
* pc->mem_cgroup. If mm is NULL and the caller doesn't pass a valid memcg
|
|
|
|
* pointer, that is treated as a charge to root_mem_cgroup.
|
|
|
|
*
|
|
|
|
* So __mem_cgroup_try_charge() will return
|
|
|
|
* 0 ... on success, filling *ptr with a valid memcg pointer.
|
|
|
|
* -ENOMEM ... charge failure because of resource limits.
|
|
|
|
* -EINTR ... if thread is fatal. *ptr is filled with root_mem_cgroup.
|
|
|
|
*
|
|
|
|
* Unlike the exported interface, an "oom" parameter is added. if oom==true,
|
|
|
|
* the oom-killer can be invoked.
|
2008-02-07 15:13:53 +07:00
|
|
|
*/
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
static int __mem_cgroup_try_charge(struct mm_struct *mm,
|
2011-01-14 06:46:56 +07:00
|
|
|
gfp_t gfp_mask,
|
2011-03-24 06:42:36 +07:00
|
|
|
unsigned int nr_pages,
|
2011-11-03 03:38:15 +07:00
|
|
|
struct mem_cgroup **ptr,
|
2011-03-24 06:42:36 +07:00
|
|
|
bool oom)
|
2008-02-07 15:13:53 +07:00
|
|
|
{
|
2011-03-24 06:42:36 +07:00
|
|
|
unsigned int batch = max(CHARGE_BATCH, nr_pages);
|
2010-08-11 08:02:57 +07:00
|
|
|
int nr_oom_retries = MEM_CGROUP_RECLAIM_RETRIES;
|
2011-11-03 03:38:15 +07:00
|
|
|
struct mem_cgroup *memcg = NULL;
|
2010-08-11 08:02:57 +07:00
|
|
|
int ret;
|
2009-01-08 09:08:08 +07:00
|
|
|
|
2010-03-11 06:22:39 +07:00
|
|
|
/*
|
|
|
|
* Unlike gloval-vm's OOM-kill, we're not in memory shortage
|
|
|
|
* in system level. So, allow to go ahead dying process in addition to
|
|
|
|
* MEMDIE process.
|
|
|
|
*/
|
|
|
|
if (unlikely(test_thread_flag(TIF_MEMDIE)
|
|
|
|
|| fatal_signal_pending(current)))
|
|
|
|
goto bypass;
|
2009-01-08 09:08:08 +07:00
|
|
|
|
2008-02-07 15:13:53 +07:00
|
|
|
/*
|
2008-02-07 15:14:19 +07:00
|
|
|
* We always charge the cgroup the mm_struct belongs to.
|
|
|
|
* The mm_struct's mem_cgroup changes on task migration if the
|
2008-02-07 15:13:53 +07:00
|
|
|
* thread group leader migrates. It's possible that mm is not
|
2012-08-01 06:45:40 +07:00
|
|
|
* set, if so charge the root memcg (happens for pagecache usage).
|
2008-02-07 15:13:53 +07:00
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
if (!*ptr && !mm)
|
2012-01-13 08:19:01 +07:00
|
|
|
*ptr = root_mem_cgroup;
|
memcg: avoid css_get()
Now, memory cgroup increments css(cgroup subsys state)'s reference count
per a charged page. And the reference count is kept until the page is
uncharged. But this has 2 bad effect.
1. Because css_get/put calls atomic_inc()/dec, heavy call of them
on large smp will not scale well.
2. Because css's refcnt cannot be in a state as "ready-to-release",
cgroup's notify_on_release handler can't work with memcg.
3. css's refcnt is atomic_t, it means smaller than 32bit. Maybe too small.
This has been a problem since the 1st merge of memcg.
This is a trial to remove css's refcnt per a page. Even if we remove
refcnt, pre_destroy() does enough synchronization as
- check res->usage == 0.
- check no pages on LRU.
This patch removes css's refcnt per page. Even after this patch, at the
1st look, it seems css_get() is still called in try_charge().
But the logic is.
- If a memcg of mm->owner is cached one, consume_stock() will work.
At success, return immediately.
- If consume_stock returns false, css_get() is called and go to
slow path which may be blocked. At the end of slow path,
css_put() is called and restart from the start if necessary.
So, in the fast path, we don't call css_get() and can avoid access to
shared counter. This patch can make the most possible case fast.
Here is a result of multi-threaded page fault benchmark.
[Before]
25.32% multi-fault-all [kernel.kallsyms] [k] clear_page_c
9.30% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
8.02% multi-fault-all [kernel.kallsyms] [k] try_get_mem_cgroup_from_mm <=====(*)
7.83% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
5.38% multi-fault-all [kernel.kallsyms] [k] __css_put
5.29% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
4.92% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
4.24% multi-fault-all [kernel.kallsyms] [k] up_read
3.53% multi-fault-all [kernel.kallsyms] [k] css_put
2.11% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
1.76% multi-fault-all [kernel.kallsyms] [k] __rmqueue
1.64% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
[After]
28.41% multi-fault-all [kernel.kallsyms] [k] clear_page_c
10.08% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
9.58% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
9.38% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
5.86% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
5.65% multi-fault-all [kernel.kallsyms] [k] up_read
2.82% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
2.64% multi-fault-all [kernel.kallsyms] [k] mem_cgroup_add_lru_list
2.48% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
Then, 8.02% of try_get_mem_cgroup_from_mm() disappears because this patch
removes css_tryget() in it. (But yes, this is an extreme case.)
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-11 08:03:02 +07:00
|
|
|
again:
|
2011-11-03 03:38:15 +07:00
|
|
|
if (*ptr) { /* css should be a valid one */
|
|
|
|
memcg = *ptr;
|
|
|
|
VM_BUG_ON(css_is_removed(&memcg->css));
|
|
|
|
if (mem_cgroup_is_root(memcg))
|
memcg: avoid css_get()
Now, memory cgroup increments css(cgroup subsys state)'s reference count
per a charged page. And the reference count is kept until the page is
uncharged. But this has 2 bad effect.
1. Because css_get/put calls atomic_inc()/dec, heavy call of them
on large smp will not scale well.
2. Because css's refcnt cannot be in a state as "ready-to-release",
cgroup's notify_on_release handler can't work with memcg.
3. css's refcnt is atomic_t, it means smaller than 32bit. Maybe too small.
This has been a problem since the 1st merge of memcg.
This is a trial to remove css's refcnt per a page. Even if we remove
refcnt, pre_destroy() does enough synchronization as
- check res->usage == 0.
- check no pages on LRU.
This patch removes css's refcnt per page. Even after this patch, at the
1st look, it seems css_get() is still called in try_charge().
But the logic is.
- If a memcg of mm->owner is cached one, consume_stock() will work.
At success, return immediately.
- If consume_stock returns false, css_get() is called and go to
slow path which may be blocked. At the end of slow path,
css_put() is called and restart from the start if necessary.
So, in the fast path, we don't call css_get() and can avoid access to
shared counter. This patch can make the most possible case fast.
Here is a result of multi-threaded page fault benchmark.
[Before]
25.32% multi-fault-all [kernel.kallsyms] [k] clear_page_c
9.30% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
8.02% multi-fault-all [kernel.kallsyms] [k] try_get_mem_cgroup_from_mm <=====(*)
7.83% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
5.38% multi-fault-all [kernel.kallsyms] [k] __css_put
5.29% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
4.92% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
4.24% multi-fault-all [kernel.kallsyms] [k] up_read
3.53% multi-fault-all [kernel.kallsyms] [k] css_put
2.11% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
1.76% multi-fault-all [kernel.kallsyms] [k] __rmqueue
1.64% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
[After]
28.41% multi-fault-all [kernel.kallsyms] [k] clear_page_c
10.08% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
9.58% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
9.38% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
5.86% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
5.65% multi-fault-all [kernel.kallsyms] [k] up_read
2.82% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
2.64% multi-fault-all [kernel.kallsyms] [k] mem_cgroup_add_lru_list
2.48% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
Then, 8.02% of try_get_mem_cgroup_from_mm() disappears because this patch
removes css_tryget() in it. (But yes, this is an extreme case.)
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-11 08:03:02 +07:00
|
|
|
goto done;
|
2011-11-03 03:38:15 +07:00
|
|
|
if (nr_pages == 1 && consume_stock(memcg))
|
memcg: avoid css_get()
Now, memory cgroup increments css(cgroup subsys state)'s reference count
per a charged page. And the reference count is kept until the page is
uncharged. But this has 2 bad effect.
1. Because css_get/put calls atomic_inc()/dec, heavy call of them
on large smp will not scale well.
2. Because css's refcnt cannot be in a state as "ready-to-release",
cgroup's notify_on_release handler can't work with memcg.
3. css's refcnt is atomic_t, it means smaller than 32bit. Maybe too small.
This has been a problem since the 1st merge of memcg.
This is a trial to remove css's refcnt per a page. Even if we remove
refcnt, pre_destroy() does enough synchronization as
- check res->usage == 0.
- check no pages on LRU.
This patch removes css's refcnt per page. Even after this patch, at the
1st look, it seems css_get() is still called in try_charge().
But the logic is.
- If a memcg of mm->owner is cached one, consume_stock() will work.
At success, return immediately.
- If consume_stock returns false, css_get() is called and go to
slow path which may be blocked. At the end of slow path,
css_put() is called and restart from the start if necessary.
So, in the fast path, we don't call css_get() and can avoid access to
shared counter. This patch can make the most possible case fast.
Here is a result of multi-threaded page fault benchmark.
[Before]
25.32% multi-fault-all [kernel.kallsyms] [k] clear_page_c
9.30% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
8.02% multi-fault-all [kernel.kallsyms] [k] try_get_mem_cgroup_from_mm <=====(*)
7.83% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
5.38% multi-fault-all [kernel.kallsyms] [k] __css_put
5.29% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
4.92% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
4.24% multi-fault-all [kernel.kallsyms] [k] up_read
3.53% multi-fault-all [kernel.kallsyms] [k] css_put
2.11% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
1.76% multi-fault-all [kernel.kallsyms] [k] __rmqueue
1.64% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
[After]
28.41% multi-fault-all [kernel.kallsyms] [k] clear_page_c
10.08% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
9.58% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
9.38% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
5.86% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
5.65% multi-fault-all [kernel.kallsyms] [k] up_read
2.82% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
2.64% multi-fault-all [kernel.kallsyms] [k] mem_cgroup_add_lru_list
2.48% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
Then, 8.02% of try_get_mem_cgroup_from_mm() disappears because this patch
removes css_tryget() in it. (But yes, this is an extreme case.)
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-11 08:03:02 +07:00
|
|
|
goto done;
|
2011-11-03 03:38:15 +07:00
|
|
|
css_get(&memcg->css);
|
2010-08-11 08:02:57 +07:00
|
|
|
} else {
|
memcg: avoid css_get()
Now, memory cgroup increments css(cgroup subsys state)'s reference count
per a charged page. And the reference count is kept until the page is
uncharged. But this has 2 bad effect.
1. Because css_get/put calls atomic_inc()/dec, heavy call of them
on large smp will not scale well.
2. Because css's refcnt cannot be in a state as "ready-to-release",
cgroup's notify_on_release handler can't work with memcg.
3. css's refcnt is atomic_t, it means smaller than 32bit. Maybe too small.
This has been a problem since the 1st merge of memcg.
This is a trial to remove css's refcnt per a page. Even if we remove
refcnt, pre_destroy() does enough synchronization as
- check res->usage == 0.
- check no pages on LRU.
This patch removes css's refcnt per page. Even after this patch, at the
1st look, it seems css_get() is still called in try_charge().
But the logic is.
- If a memcg of mm->owner is cached one, consume_stock() will work.
At success, return immediately.
- If consume_stock returns false, css_get() is called and go to
slow path which may be blocked. At the end of slow path,
css_put() is called and restart from the start if necessary.
So, in the fast path, we don't call css_get() and can avoid access to
shared counter. This patch can make the most possible case fast.
Here is a result of multi-threaded page fault benchmark.
[Before]
25.32% multi-fault-all [kernel.kallsyms] [k] clear_page_c
9.30% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
8.02% multi-fault-all [kernel.kallsyms] [k] try_get_mem_cgroup_from_mm <=====(*)
7.83% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
5.38% multi-fault-all [kernel.kallsyms] [k] __css_put
5.29% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
4.92% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
4.24% multi-fault-all [kernel.kallsyms] [k] up_read
3.53% multi-fault-all [kernel.kallsyms] [k] css_put
2.11% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
1.76% multi-fault-all [kernel.kallsyms] [k] __rmqueue
1.64% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
[After]
28.41% multi-fault-all [kernel.kallsyms] [k] clear_page_c
10.08% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
9.58% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
9.38% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
5.86% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
5.65% multi-fault-all [kernel.kallsyms] [k] up_read
2.82% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
2.64% multi-fault-all [kernel.kallsyms] [k] mem_cgroup_add_lru_list
2.48% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
Then, 8.02% of try_get_mem_cgroup_from_mm() disappears because this patch
removes css_tryget() in it. (But yes, this is an extreme case.)
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-11 08:03:02 +07:00
|
|
|
struct task_struct *p;
|
2009-01-08 09:08:33 +07:00
|
|
|
|
memcg: avoid css_get()
Now, memory cgroup increments css(cgroup subsys state)'s reference count
per a charged page. And the reference count is kept until the page is
uncharged. But this has 2 bad effect.
1. Because css_get/put calls atomic_inc()/dec, heavy call of them
on large smp will not scale well.
2. Because css's refcnt cannot be in a state as "ready-to-release",
cgroup's notify_on_release handler can't work with memcg.
3. css's refcnt is atomic_t, it means smaller than 32bit. Maybe too small.
This has been a problem since the 1st merge of memcg.
This is a trial to remove css's refcnt per a page. Even if we remove
refcnt, pre_destroy() does enough synchronization as
- check res->usage == 0.
- check no pages on LRU.
This patch removes css's refcnt per page. Even after this patch, at the
1st look, it seems css_get() is still called in try_charge().
But the logic is.
- If a memcg of mm->owner is cached one, consume_stock() will work.
At success, return immediately.
- If consume_stock returns false, css_get() is called and go to
slow path which may be blocked. At the end of slow path,
css_put() is called and restart from the start if necessary.
So, in the fast path, we don't call css_get() and can avoid access to
shared counter. This patch can make the most possible case fast.
Here is a result of multi-threaded page fault benchmark.
[Before]
25.32% multi-fault-all [kernel.kallsyms] [k] clear_page_c
9.30% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
8.02% multi-fault-all [kernel.kallsyms] [k] try_get_mem_cgroup_from_mm <=====(*)
7.83% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
5.38% multi-fault-all [kernel.kallsyms] [k] __css_put
5.29% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
4.92% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
4.24% multi-fault-all [kernel.kallsyms] [k] up_read
3.53% multi-fault-all [kernel.kallsyms] [k] css_put
2.11% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
1.76% multi-fault-all [kernel.kallsyms] [k] __rmqueue
1.64% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
[After]
28.41% multi-fault-all [kernel.kallsyms] [k] clear_page_c
10.08% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
9.58% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
9.38% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
5.86% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
5.65% multi-fault-all [kernel.kallsyms] [k] up_read
2.82% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
2.64% multi-fault-all [kernel.kallsyms] [k] mem_cgroup_add_lru_list
2.48% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
Then, 8.02% of try_get_mem_cgroup_from_mm() disappears because this patch
removes css_tryget() in it. (But yes, this is an extreme case.)
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-11 08:03:02 +07:00
|
|
|
rcu_read_lock();
|
|
|
|
p = rcu_dereference(mm->owner);
|
|
|
|
/*
|
2010-12-30 05:07:11 +07:00
|
|
|
* Because we don't have task_lock(), "p" can exit.
|
2011-11-03 03:38:15 +07:00
|
|
|
* In that case, "memcg" can point to root or p can be NULL with
|
2010-12-30 05:07:11 +07:00
|
|
|
* race with swapoff. Then, we have small risk of mis-accouning.
|
|
|
|
* But such kind of mis-account by race always happens because
|
|
|
|
* we don't have cgroup_mutex(). It's overkill and we allo that
|
|
|
|
* small race, here.
|
|
|
|
* (*) swapoff at el will charge against mm-struct not against
|
|
|
|
* task-struct. So, mm->owner can be NULL.
|
memcg: avoid css_get()
Now, memory cgroup increments css(cgroup subsys state)'s reference count
per a charged page. And the reference count is kept until the page is
uncharged. But this has 2 bad effect.
1. Because css_get/put calls atomic_inc()/dec, heavy call of them
on large smp will not scale well.
2. Because css's refcnt cannot be in a state as "ready-to-release",
cgroup's notify_on_release handler can't work with memcg.
3. css's refcnt is atomic_t, it means smaller than 32bit. Maybe too small.
This has been a problem since the 1st merge of memcg.
This is a trial to remove css's refcnt per a page. Even if we remove
refcnt, pre_destroy() does enough synchronization as
- check res->usage == 0.
- check no pages on LRU.
This patch removes css's refcnt per page. Even after this patch, at the
1st look, it seems css_get() is still called in try_charge().
But the logic is.
- If a memcg of mm->owner is cached one, consume_stock() will work.
At success, return immediately.
- If consume_stock returns false, css_get() is called and go to
slow path which may be blocked. At the end of slow path,
css_put() is called and restart from the start if necessary.
So, in the fast path, we don't call css_get() and can avoid access to
shared counter. This patch can make the most possible case fast.
Here is a result of multi-threaded page fault benchmark.
[Before]
25.32% multi-fault-all [kernel.kallsyms] [k] clear_page_c
9.30% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
8.02% multi-fault-all [kernel.kallsyms] [k] try_get_mem_cgroup_from_mm <=====(*)
7.83% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
5.38% multi-fault-all [kernel.kallsyms] [k] __css_put
5.29% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
4.92% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
4.24% multi-fault-all [kernel.kallsyms] [k] up_read
3.53% multi-fault-all [kernel.kallsyms] [k] css_put
2.11% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
1.76% multi-fault-all [kernel.kallsyms] [k] __rmqueue
1.64% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
[After]
28.41% multi-fault-all [kernel.kallsyms] [k] clear_page_c
10.08% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
9.58% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
9.38% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
5.86% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
5.65% multi-fault-all [kernel.kallsyms] [k] up_read
2.82% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
2.64% multi-fault-all [kernel.kallsyms] [k] mem_cgroup_add_lru_list
2.48% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
Then, 8.02% of try_get_mem_cgroup_from_mm() disappears because this patch
removes css_tryget() in it. (But yes, this is an extreme case.)
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-11 08:03:02 +07:00
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
memcg = mem_cgroup_from_task(p);
|
2012-01-13 08:19:01 +07:00
|
|
|
if (!memcg)
|
|
|
|
memcg = root_mem_cgroup;
|
|
|
|
if (mem_cgroup_is_root(memcg)) {
|
memcg: avoid css_get()
Now, memory cgroup increments css(cgroup subsys state)'s reference count
per a charged page. And the reference count is kept until the page is
uncharged. But this has 2 bad effect.
1. Because css_get/put calls atomic_inc()/dec, heavy call of them
on large smp will not scale well.
2. Because css's refcnt cannot be in a state as "ready-to-release",
cgroup's notify_on_release handler can't work with memcg.
3. css's refcnt is atomic_t, it means smaller than 32bit. Maybe too small.
This has been a problem since the 1st merge of memcg.
This is a trial to remove css's refcnt per a page. Even if we remove
refcnt, pre_destroy() does enough synchronization as
- check res->usage == 0.
- check no pages on LRU.
This patch removes css's refcnt per page. Even after this patch, at the
1st look, it seems css_get() is still called in try_charge().
But the logic is.
- If a memcg of mm->owner is cached one, consume_stock() will work.
At success, return immediately.
- If consume_stock returns false, css_get() is called and go to
slow path which may be blocked. At the end of slow path,
css_put() is called and restart from the start if necessary.
So, in the fast path, we don't call css_get() and can avoid access to
shared counter. This patch can make the most possible case fast.
Here is a result of multi-threaded page fault benchmark.
[Before]
25.32% multi-fault-all [kernel.kallsyms] [k] clear_page_c
9.30% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
8.02% multi-fault-all [kernel.kallsyms] [k] try_get_mem_cgroup_from_mm <=====(*)
7.83% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
5.38% multi-fault-all [kernel.kallsyms] [k] __css_put
5.29% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
4.92% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
4.24% multi-fault-all [kernel.kallsyms] [k] up_read
3.53% multi-fault-all [kernel.kallsyms] [k] css_put
2.11% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
1.76% multi-fault-all [kernel.kallsyms] [k] __rmqueue
1.64% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
[After]
28.41% multi-fault-all [kernel.kallsyms] [k] clear_page_c
10.08% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
9.58% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
9.38% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
5.86% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
5.65% multi-fault-all [kernel.kallsyms] [k] up_read
2.82% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
2.64% multi-fault-all [kernel.kallsyms] [k] mem_cgroup_add_lru_list
2.48% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
Then, 8.02% of try_get_mem_cgroup_from_mm() disappears because this patch
removes css_tryget() in it. (But yes, this is an extreme case.)
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-11 08:03:02 +07:00
|
|
|
rcu_read_unlock();
|
|
|
|
goto done;
|
|
|
|
}
|
2011-11-03 03:38:15 +07:00
|
|
|
if (nr_pages == 1 && consume_stock(memcg)) {
|
memcg: avoid css_get()
Now, memory cgroup increments css(cgroup subsys state)'s reference count
per a charged page. And the reference count is kept until the page is
uncharged. But this has 2 bad effect.
1. Because css_get/put calls atomic_inc()/dec, heavy call of them
on large smp will not scale well.
2. Because css's refcnt cannot be in a state as "ready-to-release",
cgroup's notify_on_release handler can't work with memcg.
3. css's refcnt is atomic_t, it means smaller than 32bit. Maybe too small.
This has been a problem since the 1st merge of memcg.
This is a trial to remove css's refcnt per a page. Even if we remove
refcnt, pre_destroy() does enough synchronization as
- check res->usage == 0.
- check no pages on LRU.
This patch removes css's refcnt per page. Even after this patch, at the
1st look, it seems css_get() is still called in try_charge().
But the logic is.
- If a memcg of mm->owner is cached one, consume_stock() will work.
At success, return immediately.
- If consume_stock returns false, css_get() is called and go to
slow path which may be blocked. At the end of slow path,
css_put() is called and restart from the start if necessary.
So, in the fast path, we don't call css_get() and can avoid access to
shared counter. This patch can make the most possible case fast.
Here is a result of multi-threaded page fault benchmark.
[Before]
25.32% multi-fault-all [kernel.kallsyms] [k] clear_page_c
9.30% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
8.02% multi-fault-all [kernel.kallsyms] [k] try_get_mem_cgroup_from_mm <=====(*)
7.83% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
5.38% multi-fault-all [kernel.kallsyms] [k] __css_put
5.29% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
4.92% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
4.24% multi-fault-all [kernel.kallsyms] [k] up_read
3.53% multi-fault-all [kernel.kallsyms] [k] css_put
2.11% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
1.76% multi-fault-all [kernel.kallsyms] [k] __rmqueue
1.64% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
[After]
28.41% multi-fault-all [kernel.kallsyms] [k] clear_page_c
10.08% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
9.58% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
9.38% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
5.86% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
5.65% multi-fault-all [kernel.kallsyms] [k] up_read
2.82% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
2.64% multi-fault-all [kernel.kallsyms] [k] mem_cgroup_add_lru_list
2.48% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
Then, 8.02% of try_get_mem_cgroup_from_mm() disappears because this patch
removes css_tryget() in it. (But yes, this is an extreme case.)
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-11 08:03:02 +07:00
|
|
|
/*
|
|
|
|
* It seems dagerous to access memcg without css_get().
|
|
|
|
* But considering how consume_stok works, it's not
|
|
|
|
* necessary. If consume_stock success, some charges
|
|
|
|
* from this memcg are cached on this cpu. So, we
|
|
|
|
* don't need to call css_get()/css_tryget() before
|
|
|
|
* calling consume_stock().
|
|
|
|
*/
|
|
|
|
rcu_read_unlock();
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
/* after here, we may be blocked. we need to get refcnt */
|
2011-11-03 03:38:15 +07:00
|
|
|
if (!css_tryget(&memcg->css)) {
|
memcg: avoid css_get()
Now, memory cgroup increments css(cgroup subsys state)'s reference count
per a charged page. And the reference count is kept until the page is
uncharged. But this has 2 bad effect.
1. Because css_get/put calls atomic_inc()/dec, heavy call of them
on large smp will not scale well.
2. Because css's refcnt cannot be in a state as "ready-to-release",
cgroup's notify_on_release handler can't work with memcg.
3. css's refcnt is atomic_t, it means smaller than 32bit. Maybe too small.
This has been a problem since the 1st merge of memcg.
This is a trial to remove css's refcnt per a page. Even if we remove
refcnt, pre_destroy() does enough synchronization as
- check res->usage == 0.
- check no pages on LRU.
This patch removes css's refcnt per page. Even after this patch, at the
1st look, it seems css_get() is still called in try_charge().
But the logic is.
- If a memcg of mm->owner is cached one, consume_stock() will work.
At success, return immediately.
- If consume_stock returns false, css_get() is called and go to
slow path which may be blocked. At the end of slow path,
css_put() is called and restart from the start if necessary.
So, in the fast path, we don't call css_get() and can avoid access to
shared counter. This patch can make the most possible case fast.
Here is a result of multi-threaded page fault benchmark.
[Before]
25.32% multi-fault-all [kernel.kallsyms] [k] clear_page_c
9.30% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
8.02% multi-fault-all [kernel.kallsyms] [k] try_get_mem_cgroup_from_mm <=====(*)
7.83% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
5.38% multi-fault-all [kernel.kallsyms] [k] __css_put
5.29% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
4.92% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
4.24% multi-fault-all [kernel.kallsyms] [k] up_read
3.53% multi-fault-all [kernel.kallsyms] [k] css_put
2.11% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
1.76% multi-fault-all [kernel.kallsyms] [k] __rmqueue
1.64% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
[After]
28.41% multi-fault-all [kernel.kallsyms] [k] clear_page_c
10.08% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
9.58% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
9.38% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
5.86% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
5.65% multi-fault-all [kernel.kallsyms] [k] up_read
2.82% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
2.64% multi-fault-all [kernel.kallsyms] [k] mem_cgroup_add_lru_list
2.48% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
Then, 8.02% of try_get_mem_cgroup_from_mm() disappears because this patch
removes css_tryget() in it. (But yes, this is an extreme case.)
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-11 08:03:02 +07:00
|
|
|
rcu_read_unlock();
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
|
|
}
|
2008-02-07 15:13:53 +07:00
|
|
|
|
2010-08-11 08:02:57 +07:00
|
|
|
do {
|
|
|
|
bool oom_check;
|
2009-01-08 09:07:48 +07:00
|
|
|
|
2010-08-11 08:02:57 +07:00
|
|
|
/* If killed, bypass charge */
|
memcg: avoid css_get()
Now, memory cgroup increments css(cgroup subsys state)'s reference count
per a charged page. And the reference count is kept until the page is
uncharged. But this has 2 bad effect.
1. Because css_get/put calls atomic_inc()/dec, heavy call of them
on large smp will not scale well.
2. Because css's refcnt cannot be in a state as "ready-to-release",
cgroup's notify_on_release handler can't work with memcg.
3. css's refcnt is atomic_t, it means smaller than 32bit. Maybe too small.
This has been a problem since the 1st merge of memcg.
This is a trial to remove css's refcnt per a page. Even if we remove
refcnt, pre_destroy() does enough synchronization as
- check res->usage == 0.
- check no pages on LRU.
This patch removes css's refcnt per page. Even after this patch, at the
1st look, it seems css_get() is still called in try_charge().
But the logic is.
- If a memcg of mm->owner is cached one, consume_stock() will work.
At success, return immediately.
- If consume_stock returns false, css_get() is called and go to
slow path which may be blocked. At the end of slow path,
css_put() is called and restart from the start if necessary.
So, in the fast path, we don't call css_get() and can avoid access to
shared counter. This patch can make the most possible case fast.
Here is a result of multi-threaded page fault benchmark.
[Before]
25.32% multi-fault-all [kernel.kallsyms] [k] clear_page_c
9.30% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
8.02% multi-fault-all [kernel.kallsyms] [k] try_get_mem_cgroup_from_mm <=====(*)
7.83% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
5.38% multi-fault-all [kernel.kallsyms] [k] __css_put
5.29% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
4.92% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
4.24% multi-fault-all [kernel.kallsyms] [k] up_read
3.53% multi-fault-all [kernel.kallsyms] [k] css_put
2.11% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
1.76% multi-fault-all [kernel.kallsyms] [k] __rmqueue
1.64% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
[After]
28.41% multi-fault-all [kernel.kallsyms] [k] clear_page_c
10.08% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
9.58% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
9.38% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
5.86% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
5.65% multi-fault-all [kernel.kallsyms] [k] up_read
2.82% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
2.64% multi-fault-all [kernel.kallsyms] [k] mem_cgroup_add_lru_list
2.48% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
Then, 8.02% of try_get_mem_cgroup_from_mm() disappears because this patch
removes css_tryget() in it. (But yes, this is an extreme case.)
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-11 08:03:02 +07:00
|
|
|
if (fatal_signal_pending(current)) {
|
2011-11-03 03:38:15 +07:00
|
|
|
css_put(&memcg->css);
|
2010-08-11 08:02:57 +07:00
|
|
|
goto bypass;
|
memcg: avoid css_get()
Now, memory cgroup increments css(cgroup subsys state)'s reference count
per a charged page. And the reference count is kept until the page is
uncharged. But this has 2 bad effect.
1. Because css_get/put calls atomic_inc()/dec, heavy call of them
on large smp will not scale well.
2. Because css's refcnt cannot be in a state as "ready-to-release",
cgroup's notify_on_release handler can't work with memcg.
3. css's refcnt is atomic_t, it means smaller than 32bit. Maybe too small.
This has been a problem since the 1st merge of memcg.
This is a trial to remove css's refcnt per a page. Even if we remove
refcnt, pre_destroy() does enough synchronization as
- check res->usage == 0.
- check no pages on LRU.
This patch removes css's refcnt per page. Even after this patch, at the
1st look, it seems css_get() is still called in try_charge().
But the logic is.
- If a memcg of mm->owner is cached one, consume_stock() will work.
At success, return immediately.
- If consume_stock returns false, css_get() is called and go to
slow path which may be blocked. At the end of slow path,
css_put() is called and restart from the start if necessary.
So, in the fast path, we don't call css_get() and can avoid access to
shared counter. This patch can make the most possible case fast.
Here is a result of multi-threaded page fault benchmark.
[Before]
25.32% multi-fault-all [kernel.kallsyms] [k] clear_page_c
9.30% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
8.02% multi-fault-all [kernel.kallsyms] [k] try_get_mem_cgroup_from_mm <=====(*)
7.83% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
5.38% multi-fault-all [kernel.kallsyms] [k] __css_put
5.29% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
4.92% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
4.24% multi-fault-all [kernel.kallsyms] [k] up_read
3.53% multi-fault-all [kernel.kallsyms] [k] css_put
2.11% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
1.76% multi-fault-all [kernel.kallsyms] [k] __rmqueue
1.64% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
[After]
28.41% multi-fault-all [kernel.kallsyms] [k] clear_page_c
10.08% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
9.58% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
9.38% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
5.86% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
5.65% multi-fault-all [kernel.kallsyms] [k] up_read
2.82% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
2.64% multi-fault-all [kernel.kallsyms] [k] mem_cgroup_add_lru_list
2.48% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
Then, 8.02% of try_get_mem_cgroup_from_mm() disappears because this patch
removes css_tryget() in it. (But yes, this is an extreme case.)
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-11 08:03:02 +07:00
|
|
|
}
|
2009-01-08 09:08:06 +07:00
|
|
|
|
2010-08-11 08:02:57 +07:00
|
|
|
oom_check = false;
|
|
|
|
if (oom && !nr_oom_retries) {
|
|
|
|
oom_check = true;
|
|
|
|
nr_oom_retries = MEM_CGROUP_RECLAIM_RETRIES;
|
2009-12-16 07:47:08 +07:00
|
|
|
}
|
2008-02-07 15:13:56 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
ret = mem_cgroup_do_charge(memcg, gfp_mask, batch, oom_check);
|
2010-08-11 08:02:57 +07:00
|
|
|
switch (ret) {
|
|
|
|
case CHARGE_OK:
|
|
|
|
break;
|
|
|
|
case CHARGE_RETRY: /* not in OOM situation but retry */
|
2011-03-24 06:42:36 +07:00
|
|
|
batch = nr_pages;
|
2011-11-03 03:38:15 +07:00
|
|
|
css_put(&memcg->css);
|
|
|
|
memcg = NULL;
|
memcg: avoid css_get()
Now, memory cgroup increments css(cgroup subsys state)'s reference count
per a charged page. And the reference count is kept until the page is
uncharged. But this has 2 bad effect.
1. Because css_get/put calls atomic_inc()/dec, heavy call of them
on large smp will not scale well.
2. Because css's refcnt cannot be in a state as "ready-to-release",
cgroup's notify_on_release handler can't work with memcg.
3. css's refcnt is atomic_t, it means smaller than 32bit. Maybe too small.
This has been a problem since the 1st merge of memcg.
This is a trial to remove css's refcnt per a page. Even if we remove
refcnt, pre_destroy() does enough synchronization as
- check res->usage == 0.
- check no pages on LRU.
This patch removes css's refcnt per page. Even after this patch, at the
1st look, it seems css_get() is still called in try_charge().
But the logic is.
- If a memcg of mm->owner is cached one, consume_stock() will work.
At success, return immediately.
- If consume_stock returns false, css_get() is called and go to
slow path which may be blocked. At the end of slow path,
css_put() is called and restart from the start if necessary.
So, in the fast path, we don't call css_get() and can avoid access to
shared counter. This patch can make the most possible case fast.
Here is a result of multi-threaded page fault benchmark.
[Before]
25.32% multi-fault-all [kernel.kallsyms] [k] clear_page_c
9.30% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
8.02% multi-fault-all [kernel.kallsyms] [k] try_get_mem_cgroup_from_mm <=====(*)
7.83% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
5.38% multi-fault-all [kernel.kallsyms] [k] __css_put
5.29% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
4.92% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
4.24% multi-fault-all [kernel.kallsyms] [k] up_read
3.53% multi-fault-all [kernel.kallsyms] [k] css_put
2.11% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
1.76% multi-fault-all [kernel.kallsyms] [k] __rmqueue
1.64% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
[After]
28.41% multi-fault-all [kernel.kallsyms] [k] clear_page_c
10.08% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
9.58% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
9.38% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
5.86% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
5.65% multi-fault-all [kernel.kallsyms] [k] up_read
2.82% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
2.64% multi-fault-all [kernel.kallsyms] [k] mem_cgroup_add_lru_list
2.48% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
Then, 8.02% of try_get_mem_cgroup_from_mm() disappears because this patch
removes css_tryget() in it. (But yes, this is an extreme case.)
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-11 08:03:02 +07:00
|
|
|
goto again;
|
2010-08-11 08:02:57 +07:00
|
|
|
case CHARGE_WOULDBLOCK: /* !__GFP_WAIT */
|
2011-11-03 03:38:15 +07:00
|
|
|
css_put(&memcg->css);
|
2010-08-11 08:02:57 +07:00
|
|
|
goto nomem;
|
|
|
|
case CHARGE_NOMEM: /* OOM routine works */
|
memcg: avoid css_get()
Now, memory cgroup increments css(cgroup subsys state)'s reference count
per a charged page. And the reference count is kept until the page is
uncharged. But this has 2 bad effect.
1. Because css_get/put calls atomic_inc()/dec, heavy call of them
on large smp will not scale well.
2. Because css's refcnt cannot be in a state as "ready-to-release",
cgroup's notify_on_release handler can't work with memcg.
3. css's refcnt is atomic_t, it means smaller than 32bit. Maybe too small.
This has been a problem since the 1st merge of memcg.
This is a trial to remove css's refcnt per a page. Even if we remove
refcnt, pre_destroy() does enough synchronization as
- check res->usage == 0.
- check no pages on LRU.
This patch removes css's refcnt per page. Even after this patch, at the
1st look, it seems css_get() is still called in try_charge().
But the logic is.
- If a memcg of mm->owner is cached one, consume_stock() will work.
At success, return immediately.
- If consume_stock returns false, css_get() is called and go to
slow path which may be blocked. At the end of slow path,
css_put() is called and restart from the start if necessary.
So, in the fast path, we don't call css_get() and can avoid access to
shared counter. This patch can make the most possible case fast.
Here is a result of multi-threaded page fault benchmark.
[Before]
25.32% multi-fault-all [kernel.kallsyms] [k] clear_page_c
9.30% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
8.02% multi-fault-all [kernel.kallsyms] [k] try_get_mem_cgroup_from_mm <=====(*)
7.83% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
5.38% multi-fault-all [kernel.kallsyms] [k] __css_put
5.29% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
4.92% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
4.24% multi-fault-all [kernel.kallsyms] [k] up_read
3.53% multi-fault-all [kernel.kallsyms] [k] css_put
2.11% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
1.76% multi-fault-all [kernel.kallsyms] [k] __rmqueue
1.64% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
[After]
28.41% multi-fault-all [kernel.kallsyms] [k] clear_page_c
10.08% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
9.58% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
9.38% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
5.86% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
5.65% multi-fault-all [kernel.kallsyms] [k] up_read
2.82% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
2.64% multi-fault-all [kernel.kallsyms] [k] mem_cgroup_add_lru_list
2.48% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
Then, 8.02% of try_get_mem_cgroup_from_mm() disappears because this patch
removes css_tryget() in it. (But yes, this is an extreme case.)
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-11 08:03:02 +07:00
|
|
|
if (!oom) {
|
2011-11-03 03:38:15 +07:00
|
|
|
css_put(&memcg->css);
|
2010-03-11 06:22:39 +07:00
|
|
|
goto nomem;
|
memcg: avoid css_get()
Now, memory cgroup increments css(cgroup subsys state)'s reference count
per a charged page. And the reference count is kept until the page is
uncharged. But this has 2 bad effect.
1. Because css_get/put calls atomic_inc()/dec, heavy call of them
on large smp will not scale well.
2. Because css's refcnt cannot be in a state as "ready-to-release",
cgroup's notify_on_release handler can't work with memcg.
3. css's refcnt is atomic_t, it means smaller than 32bit. Maybe too small.
This has been a problem since the 1st merge of memcg.
This is a trial to remove css's refcnt per a page. Even if we remove
refcnt, pre_destroy() does enough synchronization as
- check res->usage == 0.
- check no pages on LRU.
This patch removes css's refcnt per page. Even after this patch, at the
1st look, it seems css_get() is still called in try_charge().
But the logic is.
- If a memcg of mm->owner is cached one, consume_stock() will work.
At success, return immediately.
- If consume_stock returns false, css_get() is called and go to
slow path which may be blocked. At the end of slow path,
css_put() is called and restart from the start if necessary.
So, in the fast path, we don't call css_get() and can avoid access to
shared counter. This patch can make the most possible case fast.
Here is a result of multi-threaded page fault benchmark.
[Before]
25.32% multi-fault-all [kernel.kallsyms] [k] clear_page_c
9.30% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
8.02% multi-fault-all [kernel.kallsyms] [k] try_get_mem_cgroup_from_mm <=====(*)
7.83% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
5.38% multi-fault-all [kernel.kallsyms] [k] __css_put
5.29% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
4.92% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
4.24% multi-fault-all [kernel.kallsyms] [k] up_read
3.53% multi-fault-all [kernel.kallsyms] [k] css_put
2.11% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
1.76% multi-fault-all [kernel.kallsyms] [k] __rmqueue
1.64% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
[After]
28.41% multi-fault-all [kernel.kallsyms] [k] clear_page_c
10.08% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
9.58% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
9.38% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
5.86% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
5.65% multi-fault-all [kernel.kallsyms] [k] up_read
2.82% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
2.64% multi-fault-all [kernel.kallsyms] [k] mem_cgroup_add_lru_list
2.48% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
Then, 8.02% of try_get_mem_cgroup_from_mm() disappears because this patch
removes css_tryget() in it. (But yes, this is an extreme case.)
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-11 08:03:02 +07:00
|
|
|
}
|
2010-08-11 08:02:57 +07:00
|
|
|
/* If oom, we never return -ENOMEM */
|
|
|
|
nr_oom_retries--;
|
|
|
|
break;
|
|
|
|
case CHARGE_OOM_DIE: /* Killed by OOM Killer */
|
2011-11-03 03:38:15 +07:00
|
|
|
css_put(&memcg->css);
|
2010-03-11 06:22:39 +07:00
|
|
|
goto bypass;
|
2008-02-07 15:13:56 +07:00
|
|
|
}
|
2010-08-11 08:02:57 +07:00
|
|
|
} while (ret != CHARGE_OK);
|
|
|
|
|
2011-03-24 06:42:36 +07:00
|
|
|
if (batch > nr_pages)
|
2011-11-03 03:38:15 +07:00
|
|
|
refill_stock(memcg, batch - nr_pages);
|
|
|
|
css_put(&memcg->css);
|
2009-09-24 05:56:42 +07:00
|
|
|
done:
|
2011-11-03 03:38:15 +07:00
|
|
|
*ptr = memcg;
|
2009-01-08 09:07:48 +07:00
|
|
|
return 0;
|
|
|
|
nomem:
|
2011-11-03 03:38:15 +07:00
|
|
|
*ptr = NULL;
|
2009-01-08 09:07:48 +07:00
|
|
|
return -ENOMEM;
|
2010-03-11 06:22:39 +07:00
|
|
|
bypass:
|
2012-01-13 08:19:01 +07:00
|
|
|
*ptr = root_mem_cgroup;
|
|
|
|
return -EINTR;
|
2009-01-08 09:07:48 +07:00
|
|
|
}
|
2008-02-07 15:13:53 +07:00
|
|
|
|
2009-12-16 07:47:10 +07:00
|
|
|
/*
|
|
|
|
* Somemtimes we have to undo a charge we got by try_charge().
|
|
|
|
* This function is for that and do uncharge, put css's refcnt.
|
|
|
|
* gotten by try_charge().
|
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
static void __mem_cgroup_cancel_charge(struct mem_cgroup *memcg,
|
2011-03-24 06:42:33 +07:00
|
|
|
unsigned int nr_pages)
|
2009-12-16 07:47:10 +07:00
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
if (!mem_cgroup_is_root(memcg)) {
|
2011-03-24 06:42:33 +07:00
|
|
|
unsigned long bytes = nr_pages * PAGE_SIZE;
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
res_counter_uncharge(&memcg->res, bytes);
|
2009-12-16 07:47:10 +07:00
|
|
|
if (do_swap_account)
|
2011-11-03 03:38:15 +07:00
|
|
|
res_counter_uncharge(&memcg->memsw, bytes);
|
2009-12-16 07:47:10 +07:00
|
|
|
}
|
2010-03-11 06:22:15 +07:00
|
|
|
}
|
|
|
|
|
2012-05-30 05:07:03 +07:00
|
|
|
/*
|
|
|
|
* Cancel chrages in this cgroup....doesn't propagate to parent cgroup.
|
|
|
|
* This is useful when moving usage to parent cgroup.
|
|
|
|
*/
|
|
|
|
static void __mem_cgroup_cancel_local_charge(struct mem_cgroup *memcg,
|
|
|
|
unsigned int nr_pages)
|
|
|
|
{
|
|
|
|
unsigned long bytes = nr_pages * PAGE_SIZE;
|
|
|
|
|
|
|
|
if (mem_cgroup_is_root(memcg))
|
|
|
|
return;
|
|
|
|
|
|
|
|
res_counter_uncharge_until(&memcg->res, memcg->res.parent, bytes);
|
|
|
|
if (do_swap_account)
|
|
|
|
res_counter_uncharge_until(&memcg->memsw,
|
|
|
|
memcg->memsw.parent, bytes);
|
|
|
|
}
|
|
|
|
|
2009-04-03 06:57:45 +07:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
* it's concern. (dropping refcnt from swap can be called against removed
|
|
|
|
* memcg.)
|
|
|
|
*/
|
|
|
|
static struct mem_cgroup *mem_cgroup_lookup(unsigned short id)
|
|
|
|
{
|
|
|
|
struct cgroup_subsys_state *css;
|
|
|
|
|
|
|
|
/* ID 0 is unused ID */
|
|
|
|
if (!id)
|
|
|
|
return NULL;
|
|
|
|
css = css_lookup(&mem_cgroup_subsys, id);
|
|
|
|
if (!css)
|
|
|
|
return NULL;
|
2012-08-01 06:46:01 +07:00
|
|
|
return mem_cgroup_from_css(css);
|
2009-04-03 06:57:45 +07:00
|
|
|
}
|
|
|
|
|
2009-12-16 18:19:59 +07:00
|
|
|
struct mem_cgroup *try_get_mem_cgroup_from_page(struct page *page)
|
2009-01-08 09:08:35 +07:00
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
struct mem_cgroup *memcg = NULL;
|
2009-04-03 06:57:43 +07:00
|
|
|
struct page_cgroup *pc;
|
2009-04-03 06:57:45 +07:00
|
|
|
unsigned short id;
|
2009-01-08 09:08:35 +07:00
|
|
|
swp_entry_t ent;
|
|
|
|
|
2009-04-03 06:57:43 +07:00
|
|
|
VM_BUG_ON(!PageLocked(page));
|
|
|
|
|
|
|
|
pc = lookup_page_cgroup(page);
|
2009-05-01 05:08:11 +07:00
|
|
|
lock_page_cgroup(pc);
|
2009-04-03 06:57:45 +07:00
|
|
|
if (PageCgroupUsed(pc)) {
|
2011-11-03 03:38:15 +07:00
|
|
|
memcg = pc->mem_cgroup;
|
|
|
|
if (memcg && !css_tryget(&memcg->css))
|
|
|
|
memcg = NULL;
|
2009-12-16 18:19:59 +07:00
|
|
|
} else if (PageSwapCache(page)) {
|
2009-04-03 06:57:43 +07:00
|
|
|
ent.val = page_private(page);
|
2012-01-13 08:18:48 +07:00
|
|
|
id = lookup_swap_cgroup_id(ent);
|
2009-04-03 06:57:45 +07:00
|
|
|
rcu_read_lock();
|
2011-11-03 03:38:15 +07:00
|
|
|
memcg = mem_cgroup_lookup(id);
|
|
|
|
if (memcg && !css_tryget(&memcg->css))
|
|
|
|
memcg = NULL;
|
2009-04-03 06:57:45 +07:00
|
|
|
rcu_read_unlock();
|
2009-04-03 06:57:43 +07:00
|
|
|
}
|
2009-05-01 05:08:11 +07:00
|
|
|
unlock_page_cgroup(pc);
|
2011-11-03 03:38:15 +07:00
|
|
|
return memcg;
|
2009-01-08 09:08:35 +07:00
|
|
|
}
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static void __mem_cgroup_commit_charge(struct mem_cgroup *memcg,
|
2011-03-24 06:42:29 +07:00
|
|
|
struct page *page,
|
2011-03-24 06:42:36 +07:00
|
|
|
unsigned int nr_pages,
|
2012-03-06 05:59:16 +07:00
|
|
|
enum charge_type ctype,
|
|
|
|
bool lrucare)
|
2009-01-08 09:07:48 +07:00
|
|
|
{
|
2012-04-25 01:22:33 +07:00
|
|
|
struct page_cgroup *pc = lookup_page_cgroup(page);
|
2012-03-06 05:59:16 +07:00
|
|
|
struct zone *uninitialized_var(zone);
|
2012-05-30 05:07:09 +07:00
|
|
|
struct lruvec *lruvec;
|
2012-03-06 05:59:16 +07:00
|
|
|
bool was_on_lru = false;
|
2012-03-22 06:34:22 +07:00
|
|
|
bool anon;
|
2012-03-06 05:59:16 +07:00
|
|
|
|
2011-01-21 05:44:24 +07:00
|
|
|
lock_page_cgroup(pc);
|
2012-08-01 06:45:47 +07:00
|
|
|
VM_BUG_ON(PageCgroupUsed(pc));
|
2011-01-21 05:44:24 +07:00
|
|
|
/*
|
|
|
|
* we don't need page_cgroup_lock about tail pages, becase they are not
|
|
|
|
* accessed by any other context at this point.
|
|
|
|
*/
|
2012-03-06 05:59:16 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* In some cases, SwapCache and FUSE(splice_buf->radixtree), the page
|
|
|
|
* may already be on some other mem_cgroup's LRU. Take care of it.
|
|
|
|
*/
|
|
|
|
if (lrucare) {
|
|
|
|
zone = page_zone(page);
|
|
|
|
spin_lock_irq(&zone->lru_lock);
|
|
|
|
if (PageLRU(page)) {
|
2012-05-30 05:07:09 +07:00
|
|
|
lruvec = mem_cgroup_zone_lruvec(zone, pc->mem_cgroup);
|
2012-03-06 05:59:16 +07:00
|
|
|
ClearPageLRU(page);
|
2012-05-30 05:07:09 +07:00
|
|
|
del_page_from_lru_list(page, lruvec, page_lru(page));
|
2012-03-06 05:59:16 +07:00
|
|
|
was_on_lru = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
pc->mem_cgroup = memcg;
|
2009-09-24 05:56:33 +07:00
|
|
|
/*
|
|
|
|
* We access a page_cgroup asynchronously without lock_page_cgroup().
|
|
|
|
* Especially when a page_cgroup is taken from a page, pc->mem_cgroup
|
|
|
|
* is accessed after testing USED bit. To make pc->mem_cgroup visible
|
|
|
|
* before USED bit, we need memory barrier here.
|
|
|
|
* See mem_cgroup_add_lru_list(), etc.
|
|
|
|
*/
|
memcg: synchronized LRU
A big patch for changing memcg's LRU semantics.
Now,
- page_cgroup is linked to mem_cgroup's its own LRU (per zone).
- LRU of page_cgroup is not synchronous with global LRU.
- page and page_cgroup is one-to-one and statically allocated.
- To find page_cgroup is on what LRU, you have to check pc->mem_cgroup as
- lru = page_cgroup_zoneinfo(pc, nid_of_pc, zid_of_pc);
- SwapCache is handled.
And, when we handle LRU list of page_cgroup, we do following.
pc = lookup_page_cgroup(page);
lock_page_cgroup(pc); .....................(1)
mz = page_cgroup_zoneinfo(pc);
spin_lock(&mz->lru_lock);
.....add to LRU
spin_unlock(&mz->lru_lock);
unlock_page_cgroup(pc);
But (1) is spin_lock and we have to be afraid of dead-lock with zone->lru_lock.
So, trylock() is used at (1), now. Without (1), we can't trust "mz" is correct.
This is a trial to remove this dirty nesting of locks.
This patch changes mz->lru_lock to be zone->lru_lock.
Then, above sequence will be written as
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
mem_cgroup_add/remove/etc_lru() {
pc = lookup_page_cgroup(page);
mz = page_cgroup_zoneinfo(pc);
if (PageCgroupUsed(pc)) {
....add to LRU
}
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
This is much simpler.
(*) We're safe even if we don't take lock_page_cgroup(pc). Because..
1. When pc->mem_cgroup can be modified.
- at charge.
- at account_move().
2. at charge
the PCG_USED bit is not set before pc->mem_cgroup is fixed.
3. at account_move()
the page is isolated and not on LRU.
Pros.
- easy for maintenance.
- memcg can make use of laziness of pagevec.
- we don't have to duplicated LRU/Active/Unevictable bit in page_cgroup.
- LRU status of memcg will be synchronized with global LRU's one.
- # of locks are reduced.
- account_move() is simplified very much.
Cons.
- may increase cost of LRU rotation.
(no impact if memcg is not configured.)
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:08:01 +07:00
|
|
|
smp_wmb();
|
2012-03-22 06:34:22 +07:00
|
|
|
SetPageCgroupUsed(pc);
|
2008-02-07 15:14:19 +07:00
|
|
|
|
2012-03-06 05:59:16 +07:00
|
|
|
if (lrucare) {
|
|
|
|
if (was_on_lru) {
|
2012-05-30 05:07:09 +07:00
|
|
|
lruvec = mem_cgroup_zone_lruvec(zone, pc->mem_cgroup);
|
2012-03-06 05:59:16 +07:00
|
|
|
VM_BUG_ON(PageLRU(page));
|
|
|
|
SetPageLRU(page);
|
2012-05-30 05:07:09 +07:00
|
|
|
add_page_to_lru_list(page, lruvec, page_lru(page));
|
2012-03-06 05:59:16 +07:00
|
|
|
}
|
|
|
|
spin_unlock_irq(&zone->lru_lock);
|
|
|
|
}
|
|
|
|
|
2012-08-01 06:41:40 +07:00
|
|
|
if (ctype == MEM_CGROUP_CHARGE_TYPE_ANON)
|
2012-03-22 06:34:22 +07:00
|
|
|
anon = true;
|
|
|
|
else
|
|
|
|
anon = false;
|
|
|
|
|
|
|
|
mem_cgroup_charge_statistics(memcg, anon, nr_pages);
|
2008-10-19 10:28:16 +07:00
|
|
|
unlock_page_cgroup(pc);
|
2012-03-06 05:59:16 +07:00
|
|
|
|
2010-03-11 06:22:30 +07:00
|
|
|
/*
|
|
|
|
* "charge_statistics" updated event counter. Then, check it.
|
|
|
|
* Insert ancestor (and ancestor's ancestors), to softlimit RB-tree.
|
|
|
|
* if they exceeds softlimit.
|
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
memcg_check_events(memcg, page);
|
2009-01-08 09:07:48 +07:00
|
|
|
}
|
2008-02-07 15:13:56 +07:00
|
|
|
|
2011-01-21 05:44:24 +07:00
|
|
|
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
|
|
|
|
2012-05-30 05:06:56 +07:00
|
|
|
#define PCGF_NOCOPY_AT_SPLIT (1 << PCG_LOCK | 1 << PCG_MIGRATION)
|
2011-01-21 05:44:24 +07:00
|
|
|
/*
|
|
|
|
* Because tail pages are not marked as "used", set it. We're under
|
2012-01-13 08:18:20 +07:00
|
|
|
* zone->lru_lock, 'splitting on pmd' and compound_lock.
|
|
|
|
* charge/uncharge will be never happen and move_account() is done under
|
|
|
|
* compound_lock(), so we don't have to take care of races.
|
2011-01-21 05:44:24 +07:00
|
|
|
*/
|
2012-01-13 08:18:20 +07:00
|
|
|
void mem_cgroup_split_huge_fixup(struct page *head)
|
2011-01-21 05:44:24 +07:00
|
|
|
{
|
|
|
|
struct page_cgroup *head_pc = lookup_page_cgroup(head);
|
2012-01-13 08:18:20 +07:00
|
|
|
struct page_cgroup *pc;
|
|
|
|
int i;
|
2011-01-21 05:44:24 +07:00
|
|
|
|
2011-01-26 06:07:28 +07:00
|
|
|
if (mem_cgroup_disabled())
|
|
|
|
return;
|
2012-01-13 08:18:20 +07:00
|
|
|
for (i = 1; i < HPAGE_PMD_NR; i++) {
|
|
|
|
pc = head_pc + i;
|
|
|
|
pc->mem_cgroup = head_pc->mem_cgroup;
|
|
|
|
smp_wmb();/* see __commit_charge() */
|
|
|
|
pc->flags = head_pc->flags & ~PCGF_NOCOPY_AT_SPLIT;
|
|
|
|
}
|
2011-01-21 05:44:24 +07:00
|
|
|
}
|
2012-01-13 08:19:52 +07:00
|
|
|
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
|
2011-01-21 05:44:24 +07:00
|
|
|
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
/**
|
2011-03-24 06:42:28 +07:00
|
|
|
* mem_cgroup_move_account - move account of the page
|
2011-03-24 06:42:29 +07:00
|
|
|
* @page: the page
|
2011-03-24 06:42:36 +07:00
|
|
|
* @nr_pages: number of regular pages (>1 for huge pages)
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
* @pc: page_cgroup of the page.
|
|
|
|
* @from: mem_cgroup which the page is moved from.
|
|
|
|
* @to: mem_cgroup which the page is moved to. @from != @to.
|
|
|
|
*
|
|
|
|
* The caller must confirm following.
|
memcg: synchronized LRU
A big patch for changing memcg's LRU semantics.
Now,
- page_cgroup is linked to mem_cgroup's its own LRU (per zone).
- LRU of page_cgroup is not synchronous with global LRU.
- page and page_cgroup is one-to-one and statically allocated.
- To find page_cgroup is on what LRU, you have to check pc->mem_cgroup as
- lru = page_cgroup_zoneinfo(pc, nid_of_pc, zid_of_pc);
- SwapCache is handled.
And, when we handle LRU list of page_cgroup, we do following.
pc = lookup_page_cgroup(page);
lock_page_cgroup(pc); .....................(1)
mz = page_cgroup_zoneinfo(pc);
spin_lock(&mz->lru_lock);
.....add to LRU
spin_unlock(&mz->lru_lock);
unlock_page_cgroup(pc);
But (1) is spin_lock and we have to be afraid of dead-lock with zone->lru_lock.
So, trylock() is used at (1), now. Without (1), we can't trust "mz" is correct.
This is a trial to remove this dirty nesting of locks.
This patch changes mz->lru_lock to be zone->lru_lock.
Then, above sequence will be written as
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
mem_cgroup_add/remove/etc_lru() {
pc = lookup_page_cgroup(page);
mz = page_cgroup_zoneinfo(pc);
if (PageCgroupUsed(pc)) {
....add to LRU
}
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
This is much simpler.
(*) We're safe even if we don't take lock_page_cgroup(pc). Because..
1. When pc->mem_cgroup can be modified.
- at charge.
- at account_move().
2. at charge
the PCG_USED bit is not set before pc->mem_cgroup is fixed.
3. at account_move()
the page is isolated and not on LRU.
Pros.
- easy for maintenance.
- memcg can make use of laziness of pagevec.
- we don't have to duplicated LRU/Active/Unevictable bit in page_cgroup.
- LRU status of memcg will be synchronized with global LRU's one.
- # of locks are reduced.
- account_move() is simplified very much.
Cons.
- may increase cost of LRU rotation.
(no impact if memcg is not configured.)
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:08:01 +07:00
|
|
|
* - page is not on LRU (isolate_page() is useful.)
|
2011-03-24 06:42:36 +07:00
|
|
|
* - compound_lock is held when nr_pages > 1
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
*
|
2012-05-30 05:07:04 +07:00
|
|
|
* This function doesn't do "charge" to new cgroup and doesn't do "uncharge"
|
|
|
|
* from old cgroup.
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
*/
|
2011-03-24 06:42:36 +07:00
|
|
|
static int mem_cgroup_move_account(struct page *page,
|
|
|
|
unsigned int nr_pages,
|
|
|
|
struct page_cgroup *pc,
|
|
|
|
struct mem_cgroup *from,
|
2012-05-30 05:07:04 +07:00
|
|
|
struct mem_cgroup *to)
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
{
|
2011-03-24 06:42:28 +07:00
|
|
|
unsigned long flags;
|
|
|
|
int ret;
|
2012-03-22 06:34:22 +07:00
|
|
|
bool anon = PageAnon(page);
|
2011-01-21 05:44:25 +07:00
|
|
|
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
VM_BUG_ON(from == to);
|
2011-03-24 06:42:29 +07:00
|
|
|
VM_BUG_ON(PageLRU(page));
|
2011-03-24 06:42:28 +07:00
|
|
|
/*
|
|
|
|
* The page is isolated from LRU. So, collapse function
|
|
|
|
* will not handle this page. But page splitting can happen.
|
|
|
|
* Do this check under compound_page_lock(). The caller should
|
|
|
|
* hold it.
|
|
|
|
*/
|
|
|
|
ret = -EBUSY;
|
2011-03-24 06:42:36 +07:00
|
|
|
if (nr_pages > 1 && !PageTransHuge(page))
|
2011-03-24 06:42:28 +07:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
lock_page_cgroup(pc);
|
|
|
|
|
|
|
|
ret = -EINVAL;
|
|
|
|
if (!PageCgroupUsed(pc) || pc->mem_cgroup != from)
|
|
|
|
goto unlock;
|
|
|
|
|
2012-03-22 06:34:24 +07:00
|
|
|
move_lock_mem_cgroup(from, &flags);
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
|
2012-03-22 06:34:25 +07:00
|
|
|
if (!anon && page_mapped(page)) {
|
memcg: use generic percpu instead of private implementation
When per-cpu counter for memcg was implemneted, dynamic percpu allocator
was not very good. But now, we have good one and useful macros. This
patch replaces memcg's private percpu counter implementation with generic
dynamic percpu allocator.
The benefits are
- We can remove private implementation.
- The counters will be NUMA-aware. (Current one is not...)
- This patch makes sizeof struct mem_cgroup smaller. Then,
struct mem_cgroup may be fit in page size on small config.
- About basic performance aspects, see below.
[Before]
# size mm/memcontrol.o
text data bss dec hex filename
24373 2528 4132 31033 7939 mm/memcontrol.o
[page-fault-throuput test on 8cpu/SMP in root cgroup]
# /root/bin/perf stat -a -e page-faults,cache-misses --repeat 5 ./multi-fault-fork 8
Performance counter stats for './multi-fault-fork 8' (5 runs):
45878618 page-faults ( +- 0.110% )
602635826 cache-misses ( +- 0.105% )
61.005373262 seconds time elapsed ( +- 0.004% )
Then cache-miss/page fault = 13.14
[After]
#size mm/memcontrol.o
text data bss dec hex filename
23913 2528 4132 30573 776d mm/memcontrol.o
# /root/bin/perf stat -a -e page-faults,cache-misses --repeat 5 ./multi-fault-fork 8
Performance counter stats for './multi-fault-fork 8' (5 runs):
48179400 page-faults ( +- 0.271% )
588628407 cache-misses ( +- 0.136% )
61.004615021 seconds time elapsed ( +- 0.004% )
Then cache-miss/page fault = 12.22
Text size is reduced.
This performance improvement is not big and will be invisible in real world
applications. But this result shows this patch has some good effect even
on (small) SMP.
Here is a test program I used.
1. fork() processes on each cpus.
2. do page fault repeatedly on each process.
3. after 60secs, kill all childredn and exit.
(3 is necessary for getting stable data, this is improvement from previous one.)
#define _GNU_SOURCE
#include <stdio.h>
#include <sched.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
/*
* For avoiding contention in page table lock, FAULT area is
* sparse. If FAULT_LENGTH is too large for your cpus, decrease it.
*/
#define FAULT_LENGTH (2 * 1024 * 1024)
#define PAGE_SIZE 4096
#define MAXNUM (128)
void alarm_handler(int sig)
{
}
void *worker(int cpu, int ppid)
{
void *start, *end;
char *c;
cpu_set_t set;
int i;
CPU_ZERO(&set);
CPU_SET(cpu, &set);
sched_setaffinity(0, sizeof(set), &set);
start = mmap(NULL, FAULT_LENGTH, PROT_READ|PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
if (start == MAP_FAILED) {
perror("mmap");
exit(1);
}
end = start + FAULT_LENGTH;
pause();
//fprintf(stderr, "run%d", cpu);
while (1) {
for (c = (char*)start; (void *)c < end; c += PAGE_SIZE)
*c = 0;
madvise(start, FAULT_LENGTH, MADV_DONTNEED);
}
return NULL;
}
int main(int argc, char *argv[])
{
int num, i, ret, pid, status;
int pids[MAXNUM];
if (argc < 2)
return 0;
setpgid(0, 0);
signal(SIGALRM, alarm_handler);
num = atoi(argv[1]);
pid = getpid();
for (i = 0; i < num; ++i) {
ret = fork();
if (!ret) {
worker(i, pid);
exit(0);
}
pids[i] = ret;
}
sleep(1);
kill(-pid, SIGALRM);
sleep(60);
for (i = 0; i < num; i++)
kill(pids[i], SIGKILL);
for (i = 0; i < num; i++)
waitpid(pids[i], &status, 0);
return 0;
}
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-03-11 06:22:29 +07:00
|
|
|
/* Update mapped_file data for mem_cgroup */
|
|
|
|
preempt_disable();
|
|
|
|
__this_cpu_dec(from->stat->count[MEM_CGROUP_STAT_FILE_MAPPED]);
|
|
|
|
__this_cpu_inc(to->stat->count[MEM_CGROUP_STAT_FILE_MAPPED]);
|
|
|
|
preempt_enable();
|
2009-06-18 06:26:34 +07:00
|
|
|
}
|
2012-03-22 06:34:22 +07:00
|
|
|
mem_cgroup_charge_statistics(from, anon, -nr_pages);
|
2009-06-18 06:26:34 +07:00
|
|
|
|
2010-03-11 06:22:15 +07:00
|
|
|
/* caller should have done css_get */
|
memcg: synchronized LRU
A big patch for changing memcg's LRU semantics.
Now,
- page_cgroup is linked to mem_cgroup's its own LRU (per zone).
- LRU of page_cgroup is not synchronous with global LRU.
- page and page_cgroup is one-to-one and statically allocated.
- To find page_cgroup is on what LRU, you have to check pc->mem_cgroup as
- lru = page_cgroup_zoneinfo(pc, nid_of_pc, zid_of_pc);
- SwapCache is handled.
And, when we handle LRU list of page_cgroup, we do following.
pc = lookup_page_cgroup(page);
lock_page_cgroup(pc); .....................(1)
mz = page_cgroup_zoneinfo(pc);
spin_lock(&mz->lru_lock);
.....add to LRU
spin_unlock(&mz->lru_lock);
unlock_page_cgroup(pc);
But (1) is spin_lock and we have to be afraid of dead-lock with zone->lru_lock.
So, trylock() is used at (1), now. Without (1), we can't trust "mz" is correct.
This is a trial to remove this dirty nesting of locks.
This patch changes mz->lru_lock to be zone->lru_lock.
Then, above sequence will be written as
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
mem_cgroup_add/remove/etc_lru() {
pc = lookup_page_cgroup(page);
mz = page_cgroup_zoneinfo(pc);
if (PageCgroupUsed(pc)) {
....add to LRU
}
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
This is much simpler.
(*) We're safe even if we don't take lock_page_cgroup(pc). Because..
1. When pc->mem_cgroup can be modified.
- at charge.
- at account_move().
2. at charge
the PCG_USED bit is not set before pc->mem_cgroup is fixed.
3. at account_move()
the page is isolated and not on LRU.
Pros.
- easy for maintenance.
- memcg can make use of laziness of pagevec.
- we don't have to duplicated LRU/Active/Unevictable bit in page_cgroup.
- LRU status of memcg will be synchronized with global LRU's one.
- # of locks are reduced.
- account_move() is simplified very much.
Cons.
- may increase cost of LRU rotation.
(no impact if memcg is not configured.)
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:08:01 +07:00
|
|
|
pc->mem_cgroup = to;
|
2012-03-22 06:34:22 +07:00
|
|
|
mem_cgroup_charge_statistics(to, anon, nr_pages);
|
2009-07-30 05:04:06 +07:00
|
|
|
/*
|
|
|
|
* We charges against "to" which may not have any tasks. Then, "to"
|
|
|
|
* can be under rmdir(). But in current implementation, caller of
|
2010-03-11 06:22:14 +07:00
|
|
|
* this function is just force_empty() and move charge, so it's
|
2011-03-31 08:57:33 +07:00
|
|
|
* guaranteed that "to" is never removed. So, we don't check rmdir
|
2010-03-11 06:22:14 +07:00
|
|
|
* status here.
|
2009-07-30 05:04:06 +07:00
|
|
|
*/
|
2012-03-22 06:34:24 +07:00
|
|
|
move_unlock_mem_cgroup(from, &flags);
|
2011-03-24 06:42:28 +07:00
|
|
|
ret = 0;
|
|
|
|
unlock:
|
2009-12-16 07:47:11 +07:00
|
|
|
unlock_page_cgroup(pc);
|
2010-03-11 06:22:31 +07:00
|
|
|
/*
|
|
|
|
* check events
|
|
|
|
*/
|
2011-03-24 06:42:29 +07:00
|
|
|
memcg_check_events(to, page);
|
|
|
|
memcg_check_events(from, page);
|
2011-03-24 06:42:28 +07:00
|
|
|
out:
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* move charges to its parent.
|
|
|
|
*/
|
|
|
|
|
2011-03-24 06:42:29 +07:00
|
|
|
static int mem_cgroup_move_parent(struct page *page,
|
|
|
|
struct page_cgroup *pc,
|
2012-08-01 06:42:45 +07:00
|
|
|
struct mem_cgroup *child)
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
{
|
|
|
|
struct mem_cgroup *parent;
|
2011-03-24 06:42:36 +07:00
|
|
|
unsigned int nr_pages;
|
2011-03-24 06:42:39 +07:00
|
|
|
unsigned long uninitialized_var(flags);
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Is ROOT ? */
|
2012-05-30 05:07:04 +07:00
|
|
|
if (mem_cgroup_is_root(child))
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
return -EINVAL;
|
|
|
|
|
2009-12-16 07:47:11 +07:00
|
|
|
ret = -EBUSY;
|
|
|
|
if (!get_page_unless_zero(page))
|
|
|
|
goto out;
|
|
|
|
if (isolate_lru_page(page))
|
|
|
|
goto put;
|
2011-01-26 06:07:29 +07:00
|
|
|
|
2011-03-24 06:42:36 +07:00
|
|
|
nr_pages = hpage_nr_pages(page);
|
memcg: synchronized LRU
A big patch for changing memcg's LRU semantics.
Now,
- page_cgroup is linked to mem_cgroup's its own LRU (per zone).
- LRU of page_cgroup is not synchronous with global LRU.
- page and page_cgroup is one-to-one and statically allocated.
- To find page_cgroup is on what LRU, you have to check pc->mem_cgroup as
- lru = page_cgroup_zoneinfo(pc, nid_of_pc, zid_of_pc);
- SwapCache is handled.
And, when we handle LRU list of page_cgroup, we do following.
pc = lookup_page_cgroup(page);
lock_page_cgroup(pc); .....................(1)
mz = page_cgroup_zoneinfo(pc);
spin_lock(&mz->lru_lock);
.....add to LRU
spin_unlock(&mz->lru_lock);
unlock_page_cgroup(pc);
But (1) is spin_lock and we have to be afraid of dead-lock with zone->lru_lock.
So, trylock() is used at (1), now. Without (1), we can't trust "mz" is correct.
This is a trial to remove this dirty nesting of locks.
This patch changes mz->lru_lock to be zone->lru_lock.
Then, above sequence will be written as
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
mem_cgroup_add/remove/etc_lru() {
pc = lookup_page_cgroup(page);
mz = page_cgroup_zoneinfo(pc);
if (PageCgroupUsed(pc)) {
....add to LRU
}
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
This is much simpler.
(*) We're safe even if we don't take lock_page_cgroup(pc). Because..
1. When pc->mem_cgroup can be modified.
- at charge.
- at account_move().
2. at charge
the PCG_USED bit is not set before pc->mem_cgroup is fixed.
3. at account_move()
the page is isolated and not on LRU.
Pros.
- easy for maintenance.
- memcg can make use of laziness of pagevec.
- we don't have to duplicated LRU/Active/Unevictable bit in page_cgroup.
- LRU status of memcg will be synchronized with global LRU's one.
- # of locks are reduced.
- account_move() is simplified very much.
Cons.
- may increase cost of LRU rotation.
(no impact if memcg is not configured.)
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:08:01 +07:00
|
|
|
|
2012-05-30 05:07:04 +07:00
|
|
|
parent = parent_mem_cgroup(child);
|
|
|
|
/*
|
|
|
|
* If no parent, move charges to root cgroup.
|
|
|
|
*/
|
|
|
|
if (!parent)
|
|
|
|
parent = root_mem_cgroup;
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
|
2011-03-24 06:42:36 +07:00
|
|
|
if (nr_pages > 1)
|
2011-01-21 05:44:25 +07:00
|
|
|
flags = compound_lock_irqsave(page);
|
|
|
|
|
2012-05-30 05:07:04 +07:00
|
|
|
ret = mem_cgroup_move_account(page, nr_pages,
|
2012-05-30 05:07:04 +07:00
|
|
|
pc, child, parent);
|
2012-05-30 05:07:04 +07:00
|
|
|
if (!ret)
|
|
|
|
__mem_cgroup_cancel_local_charge(child, nr_pages);
|
2011-01-26 06:07:24 +07:00
|
|
|
|
2011-03-24 06:42:36 +07:00
|
|
|
if (nr_pages > 1)
|
2011-01-21 05:44:25 +07:00
|
|
|
compound_unlock_irqrestore(page, flags);
|
memcg: synchronized LRU
A big patch for changing memcg's LRU semantics.
Now,
- page_cgroup is linked to mem_cgroup's its own LRU (per zone).
- LRU of page_cgroup is not synchronous with global LRU.
- page and page_cgroup is one-to-one and statically allocated.
- To find page_cgroup is on what LRU, you have to check pc->mem_cgroup as
- lru = page_cgroup_zoneinfo(pc, nid_of_pc, zid_of_pc);
- SwapCache is handled.
And, when we handle LRU list of page_cgroup, we do following.
pc = lookup_page_cgroup(page);
lock_page_cgroup(pc); .....................(1)
mz = page_cgroup_zoneinfo(pc);
spin_lock(&mz->lru_lock);
.....add to LRU
spin_unlock(&mz->lru_lock);
unlock_page_cgroup(pc);
But (1) is spin_lock and we have to be afraid of dead-lock with zone->lru_lock.
So, trylock() is used at (1), now. Without (1), we can't trust "mz" is correct.
This is a trial to remove this dirty nesting of locks.
This patch changes mz->lru_lock to be zone->lru_lock.
Then, above sequence will be written as
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
mem_cgroup_add/remove/etc_lru() {
pc = lookup_page_cgroup(page);
mz = page_cgroup_zoneinfo(pc);
if (PageCgroupUsed(pc)) {
....add to LRU
}
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
This is much simpler.
(*) We're safe even if we don't take lock_page_cgroup(pc). Because..
1. When pc->mem_cgroup can be modified.
- at charge.
- at account_move().
2. at charge
the PCG_USED bit is not set before pc->mem_cgroup is fixed.
3. at account_move()
the page is isolated and not on LRU.
Pros.
- easy for maintenance.
- memcg can make use of laziness of pagevec.
- we don't have to duplicated LRU/Active/Unevictable bit in page_cgroup.
- LRU status of memcg will be synchronized with global LRU's one.
- # of locks are reduced.
- account_move() is simplified very much.
Cons.
- may increase cost of LRU rotation.
(no impact if memcg is not configured.)
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:08:01 +07:00
|
|
|
putback_lru_page(page);
|
2009-12-16 07:47:11 +07:00
|
|
|
put:
|
2009-01-16 04:51:12 +07:00
|
|
|
put_page(page);
|
2009-12-16 07:47:11 +07:00
|
|
|
out:
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-01-08 09:07:48 +07:00
|
|
|
/*
|
|
|
|
* Charge the memory controller for page usage.
|
|
|
|
* Return
|
|
|
|
* 0 if the charge was successful
|
|
|
|
* < 0 if the cgroup is over its limit
|
|
|
|
*/
|
|
|
|
static int mem_cgroup_charge_common(struct page *page, struct mm_struct *mm,
|
2010-08-11 08:02:59 +07:00
|
|
|
gfp_t gfp_mask, enum charge_type ctype)
|
2009-01-08 09:07:48 +07:00
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
struct mem_cgroup *memcg = NULL;
|
2011-03-24 06:42:36 +07:00
|
|
|
unsigned int nr_pages = 1;
|
2011-02-02 06:52:44 +07:00
|
|
|
bool oom = true;
|
2009-01-08 09:07:48 +07:00
|
|
|
int ret;
|
2011-01-14 06:46:56 +07:00
|
|
|
|
2011-01-14 06:47:16 +07:00
|
|
|
if (PageTransHuge(page)) {
|
2011-03-24 06:42:36 +07:00
|
|
|
nr_pages <<= compound_order(page);
|
2011-01-14 06:47:16 +07:00
|
|
|
VM_BUG_ON(!PageTransHuge(page));
|
2011-02-02 06:52:44 +07:00
|
|
|
/*
|
|
|
|
* Never OOM-kill a process for a huge page. The
|
|
|
|
* fault handler will fall back to regular pages.
|
|
|
|
*/
|
|
|
|
oom = false;
|
2011-01-14 06:47:16 +07:00
|
|
|
}
|
2009-01-08 09:07:48 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
ret = __mem_cgroup_try_charge(mm, gfp_mask, nr_pages, &memcg, oom);
|
2012-01-13 08:19:01 +07:00
|
|
|
if (ret == -ENOMEM)
|
2009-01-08 09:07:48 +07:00
|
|
|
return ret;
|
2012-04-25 01:22:33 +07:00
|
|
|
__mem_cgroup_commit_charge(memcg, page, nr_pages, ctype, false);
|
2008-02-07 15:13:53 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-01-08 09:07:48 +07:00
|
|
|
int mem_cgroup_newpage_charge(struct page *page,
|
|
|
|
struct mm_struct *mm, gfp_t gfp_mask)
|
2008-02-07 15:14:17 +07:00
|
|
|
{
|
2009-01-08 09:08:02 +07:00
|
|
|
if (mem_cgroup_disabled())
|
2008-07-25 15:47:18 +07:00
|
|
|
return 0;
|
2012-01-13 08:18:43 +07:00
|
|
|
VM_BUG_ON(page_mapped(page));
|
|
|
|
VM_BUG_ON(page->mapping && !PageAnon(page));
|
|
|
|
VM_BUG_ON(!mm);
|
2008-02-07 15:14:17 +07:00
|
|
|
return mem_cgroup_charge_common(page, mm, gfp_mask,
|
2012-08-01 06:41:40 +07:00
|
|
|
MEM_CGROUP_CHARGE_TYPE_ANON);
|
2008-02-07 15:14:17 +07:00
|
|
|
}
|
|
|
|
|
2009-01-08 09:08:33 +07:00
|
|
|
/*
|
|
|
|
* While swap-in, try_charge -> commit or cancel, the page is locked.
|
|
|
|
* And when try_charge() successfully returns, one refcnt to memcg without
|
2009-10-07 20:21:09 +07:00
|
|
|
* struct page_cgroup is acquired. This refcnt will be consumed by
|
2009-01-08 09:08:33 +07:00
|
|
|
* "commit()" or removed by "cancel()"
|
|
|
|
*/
|
2012-08-01 06:45:43 +07:00
|
|
|
static int __mem_cgroup_try_charge_swapin(struct mm_struct *mm,
|
|
|
|
struct page *page,
|
|
|
|
gfp_t mask,
|
|
|
|
struct mem_cgroup **memcgp)
|
2009-01-08 09:08:00 +07:00
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
struct mem_cgroup *memcg;
|
2012-08-01 06:45:47 +07:00
|
|
|
struct page_cgroup *pc;
|
2009-01-08 09:08:33 +07:00
|
|
|
int ret;
|
2009-01-08 09:08:00 +07:00
|
|
|
|
2012-08-01 06:45:47 +07:00
|
|
|
pc = lookup_page_cgroup(page);
|
|
|
|
/*
|
|
|
|
* Every swap fault against a single page tries to charge the
|
|
|
|
* page, bail as early as possible. shmem_unuse() encounters
|
|
|
|
* already charged pages, too. The USED bit is protected by
|
|
|
|
* the page lock, which serializes swap cache removal, which
|
|
|
|
* in turn serializes uncharging.
|
|
|
|
*/
|
|
|
|
if (PageCgroupUsed(pc))
|
|
|
|
return 0;
|
2009-01-08 09:08:00 +07:00
|
|
|
if (!do_swap_account)
|
|
|
|
goto charge_cur_mm;
|
2011-11-03 03:38:15 +07:00
|
|
|
memcg = try_get_mem_cgroup_from_page(page);
|
|
|
|
if (!memcg)
|
2009-01-08 09:08:33 +07:00
|
|
|
goto charge_cur_mm;
|
2012-01-13 08:18:32 +07:00
|
|
|
*memcgp = memcg;
|
|
|
|
ret = __mem_cgroup_try_charge(NULL, mask, 1, memcgp, true);
|
2011-11-03 03:38:15 +07:00
|
|
|
css_put(&memcg->css);
|
2012-01-13 08:19:01 +07:00
|
|
|
if (ret == -EINTR)
|
|
|
|
ret = 0;
|
2009-01-08 09:08:33 +07:00
|
|
|
return ret;
|
2009-01-08 09:08:00 +07:00
|
|
|
charge_cur_mm:
|
2012-01-13 08:19:01 +07:00
|
|
|
ret = __mem_cgroup_try_charge(mm, mask, 1, memcgp, true);
|
|
|
|
if (ret == -EINTR)
|
|
|
|
ret = 0;
|
|
|
|
return ret;
|
2009-01-08 09:08:00 +07:00
|
|
|
}
|
|
|
|
|
2012-08-01 06:45:43 +07:00
|
|
|
int mem_cgroup_try_charge_swapin(struct mm_struct *mm, struct page *page,
|
|
|
|
gfp_t gfp_mask, struct mem_cgroup **memcgp)
|
|
|
|
{
|
|
|
|
*memcgp = NULL;
|
|
|
|
if (mem_cgroup_disabled())
|
|
|
|
return 0;
|
2012-08-01 06:45:50 +07:00
|
|
|
/*
|
|
|
|
* A racing thread's fault, or swapoff, may have already
|
|
|
|
* updated the pte, and even removed page from swap cache: in
|
|
|
|
* those cases unuse_pte()'s pte_same() test will fail; but
|
|
|
|
* there's also a KSM case which does need to charge the page.
|
|
|
|
*/
|
|
|
|
if (!PageSwapCache(page)) {
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = __mem_cgroup_try_charge(mm, gfp_mask, 1, memcgp, true);
|
|
|
|
if (ret == -EINTR)
|
|
|
|
ret = 0;
|
|
|
|
return ret;
|
|
|
|
}
|
2012-08-01 06:45:43 +07:00
|
|
|
return __mem_cgroup_try_charge_swapin(mm, page, gfp_mask, memcgp);
|
|
|
|
}
|
|
|
|
|
2012-08-01 06:45:36 +07:00
|
|
|
void mem_cgroup_cancel_charge_swapin(struct mem_cgroup *memcg)
|
|
|
|
{
|
|
|
|
if (mem_cgroup_disabled())
|
|
|
|
return;
|
|
|
|
if (!memcg)
|
|
|
|
return;
|
|
|
|
__mem_cgroup_cancel_charge(memcg, 1);
|
|
|
|
}
|
|
|
|
|
2009-04-03 06:57:48 +07:00
|
|
|
static void
|
2012-01-13 08:18:32 +07:00
|
|
|
__mem_cgroup_commit_charge_swapin(struct page *page, struct mem_cgroup *memcg,
|
2009-04-03 06:57:48 +07:00
|
|
|
enum charge_type ctype)
|
2009-01-08 09:07:48 +07:00
|
|
|
{
|
2009-01-08 09:08:02 +07:00
|
|
|
if (mem_cgroup_disabled())
|
2009-01-08 09:07:48 +07:00
|
|
|
return;
|
2012-01-13 08:18:32 +07:00
|
|
|
if (!memcg)
|
2009-01-08 09:07:48 +07:00
|
|
|
return;
|
2012-01-13 08:18:32 +07:00
|
|
|
cgroup_exclude_rmdir(&memcg->css);
|
2011-03-24 06:42:42 +07:00
|
|
|
|
2012-04-25 01:22:33 +07:00
|
|
|
__mem_cgroup_commit_charge(memcg, page, 1, ctype, true);
|
2009-01-08 09:08:00 +07:00
|
|
|
/*
|
|
|
|
* Now swap is on-memory. This means this page may be
|
|
|
|
* counted both as mem and swap....double count.
|
2009-01-08 09:08:31 +07:00
|
|
|
* Fix it by uncharging from memsw. Basically, this SwapCache is stable
|
|
|
|
* under lock_page(). But in do_swap_page()::memory.c, reuse_swap_page()
|
|
|
|
* may call delete_from_swap_cache() before reach here.
|
2009-01-08 09:08:00 +07:00
|
|
|
*/
|
2009-01-08 09:08:31 +07:00
|
|
|
if (do_swap_account && PageSwapCache(page)) {
|
2009-01-08 09:08:00 +07:00
|
|
|
swp_entry_t ent = {.val = page_private(page)};
|
2012-05-30 05:06:52 +07:00
|
|
|
mem_cgroup_uncharge_swap(ent);
|
2009-01-08 09:08:00 +07:00
|
|
|
}
|
2009-07-30 05:04:06 +07:00
|
|
|
/*
|
|
|
|
* At swapin, we may charge account against cgroup which has no tasks.
|
|
|
|
* So, rmdir()->pre_destroy() can be called while we do this charge.
|
|
|
|
* In that case, we need to call pre_destroy() again. check it here.
|
|
|
|
*/
|
2012-01-13 08:18:32 +07:00
|
|
|
cgroup_release_and_wakeup_rmdir(&memcg->css);
|
2009-01-08 09:07:48 +07:00
|
|
|
}
|
|
|
|
|
2012-01-13 08:18:32 +07:00
|
|
|
void mem_cgroup_commit_charge_swapin(struct page *page,
|
|
|
|
struct mem_cgroup *memcg)
|
2009-04-03 06:57:48 +07:00
|
|
|
{
|
2012-01-13 08:18:32 +07:00
|
|
|
__mem_cgroup_commit_charge_swapin(page, memcg,
|
2012-08-01 06:41:40 +07:00
|
|
|
MEM_CGROUP_CHARGE_TYPE_ANON);
|
2009-04-03 06:57:48 +07:00
|
|
|
}
|
|
|
|
|
2012-08-01 06:45:36 +07:00
|
|
|
int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm,
|
|
|
|
gfp_t gfp_mask)
|
2009-01-08 09:07:48 +07:00
|
|
|
{
|
2012-08-01 06:45:36 +07:00
|
|
|
struct mem_cgroup *memcg = NULL;
|
|
|
|
enum charge_type type = MEM_CGROUP_CHARGE_TYPE_CACHE;
|
|
|
|
int ret;
|
|
|
|
|
2009-01-08 09:08:02 +07:00
|
|
|
if (mem_cgroup_disabled())
|
2012-08-01 06:45:36 +07:00
|
|
|
return 0;
|
|
|
|
if (PageCompound(page))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!PageSwapCache(page))
|
|
|
|
ret = mem_cgroup_charge_common(page, mm, gfp_mask, type);
|
|
|
|
else { /* page is swapcache/shmem */
|
2012-08-01 06:45:43 +07:00
|
|
|
ret = __mem_cgroup_try_charge_swapin(mm, page,
|
|
|
|
gfp_mask, &memcg);
|
2012-08-01 06:45:36 +07:00
|
|
|
if (!ret)
|
|
|
|
__mem_cgroup_commit_charge_swapin(page, memcg, type);
|
|
|
|
}
|
|
|
|
return ret;
|
2009-01-08 09:07:48 +07:00
|
|
|
}
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static void mem_cgroup_do_uncharge(struct mem_cgroup *memcg,
|
2011-03-24 06:42:36 +07:00
|
|
|
unsigned int nr_pages,
|
|
|
|
const enum charge_type ctype)
|
2009-12-16 07:47:03 +07:00
|
|
|
{
|
|
|
|
struct memcg_batch_info *batch = NULL;
|
|
|
|
bool uncharge_memsw = true;
|
2011-03-24 06:42:36 +07:00
|
|
|
|
2009-12-16 07:47:03 +07:00
|
|
|
/* If swapout, usage of swap doesn't decrease */
|
|
|
|
if (!do_swap_account || ctype == MEM_CGROUP_CHARGE_TYPE_SWAPOUT)
|
|
|
|
uncharge_memsw = false;
|
|
|
|
|
|
|
|
batch = ¤t->memcg_batch;
|
|
|
|
/*
|
|
|
|
* In usual, we do css_get() when we remember memcg pointer.
|
|
|
|
* But in this case, we keep res->usage until end of a series of
|
|
|
|
* uncharges. Then, it's ok to ignore memcg's refcnt.
|
|
|
|
*/
|
|
|
|
if (!batch->memcg)
|
2011-11-03 03:38:15 +07:00
|
|
|
batch->memcg = memcg;
|
2010-05-27 04:42:37 +07:00
|
|
|
/*
|
|
|
|
* do_batch > 0 when unmapping pages or inode invalidate/truncate.
|
2011-03-31 08:57:33 +07:00
|
|
|
* In those cases, all pages freed continuously can be expected to be in
|
2010-05-27 04:42:37 +07:00
|
|
|
* the same cgroup and we have chance to coalesce uncharges.
|
|
|
|
* But we do uncharge one by one if this is killed by OOM(TIF_MEMDIE)
|
|
|
|
* because we want to do uncharge as soon as possible.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!batch->do_batch || test_thread_flag(TIF_MEMDIE))
|
|
|
|
goto direct_uncharge;
|
|
|
|
|
2011-03-24 06:42:36 +07:00
|
|
|
if (nr_pages > 1)
|
2011-01-14 06:46:56 +07:00
|
|
|
goto direct_uncharge;
|
|
|
|
|
2009-12-16 07:47:03 +07:00
|
|
|
/*
|
|
|
|
* In typical case, batch->memcg == mem. This means we can
|
|
|
|
* merge a series of uncharges to an uncharge of res_counter.
|
|
|
|
* If not, we uncharge res_counter ony by one.
|
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
if (batch->memcg != memcg)
|
2009-12-16 07:47:03 +07:00
|
|
|
goto direct_uncharge;
|
|
|
|
/* remember freed charge and uncharge it later */
|
2011-03-24 06:42:35 +07:00
|
|
|
batch->nr_pages++;
|
2009-12-16 07:47:03 +07:00
|
|
|
if (uncharge_memsw)
|
2011-03-24 06:42:35 +07:00
|
|
|
batch->memsw_nr_pages++;
|
2009-12-16 07:47:03 +07:00
|
|
|
return;
|
|
|
|
direct_uncharge:
|
2011-11-03 03:38:15 +07:00
|
|
|
res_counter_uncharge(&memcg->res, nr_pages * PAGE_SIZE);
|
2009-12-16 07:47:03 +07:00
|
|
|
if (uncharge_memsw)
|
2011-11-03 03:38:15 +07:00
|
|
|
res_counter_uncharge(&memcg->memsw, nr_pages * PAGE_SIZE);
|
|
|
|
if (unlikely(batch->memcg != memcg))
|
|
|
|
memcg_oom_recover(memcg);
|
2009-12-16 07:47:03 +07:00
|
|
|
}
|
2009-01-08 09:07:48 +07:00
|
|
|
|
2008-02-07 15:13:53 +07:00
|
|
|
/*
|
memcg: remove refcnt from page_cgroup
memcg: performance improvements
Patch Description
1/5 ... remove refcnt fron page_cgroup patch (shmem handling is fixed)
2/5 ... swapcache handling patch
3/5 ... add helper function for shmem's memory reclaim patch
4/5 ... optimize by likely/unlikely ppatch
5/5 ... remove redundunt check patch (shmem handling is fixed.)
Unix bench result.
== 2.6.26-rc2-mm1 + memory resource controller
Execl Throughput 2915.4 lps (29.6 secs, 3 samples)
C Compiler Throughput 1019.3 lpm (60.0 secs, 3 samples)
Shell Scripts (1 concurrent) 5796.0 lpm (60.0 secs, 3 samples)
Shell Scripts (8 concurrent) 1097.7 lpm (60.0 secs, 3 samples)
Shell Scripts (16 concurrent) 565.3 lpm (60.0 secs, 3 samples)
File Read 1024 bufsize 2000 maxblocks 1022128.0 KBps (30.0 secs, 3 samples)
File Write 1024 bufsize 2000 maxblocks 544057.0 KBps (30.0 secs, 3 samples)
File Copy 1024 bufsize 2000 maxblocks 346481.0 KBps (30.0 secs, 3 samples)
File Read 256 bufsize 500 maxblocks 319325.0 KBps (30.0 secs, 3 samples)
File Write 256 bufsize 500 maxblocks 148788.0 KBps (30.0 secs, 3 samples)
File Copy 256 bufsize 500 maxblocks 99051.0 KBps (30.0 secs, 3 samples)
File Read 4096 bufsize 8000 maxblocks 2058917.0 KBps (30.0 secs, 3 samples)
File Write 4096 bufsize 8000 maxblocks 1606109.0 KBps (30.0 secs, 3 samples)
File Copy 4096 bufsize 8000 maxblocks 854789.0 KBps (30.0 secs, 3 samples)
Dc: sqrt(2) to 99 decimal places 126145.2 lpm (30.0 secs, 3 samples)
INDEX VALUES
TEST BASELINE RESULT INDEX
Execl Throughput 43.0 2915.4 678.0
File Copy 1024 bufsize 2000 maxblocks 3960.0 346481.0 875.0
File Copy 256 bufsize 500 maxblocks 1655.0 99051.0 598.5
File Copy 4096 bufsize 8000 maxblocks 5800.0 854789.0 1473.8
Shell Scripts (8 concurrent) 6.0 1097.7 1829.5
=========
FINAL SCORE 991.3
== 2.6.26-rc2-mm1 + this set ==
Execl Throughput 3012.9 lps (29.9 secs, 3 samples)
C Compiler Throughput 981.0 lpm (60.0 secs, 3 samples)
Shell Scripts (1 concurrent) 5872.0 lpm (60.0 secs, 3 samples)
Shell Scripts (8 concurrent) 1120.3 lpm (60.0 secs, 3 samples)
Shell Scripts (16 concurrent) 578.0 lpm (60.0 secs, 3 samples)
File Read 1024 bufsize 2000 maxblocks 1003993.0 KBps (30.0 secs, 3 samples)
File Write 1024 bufsize 2000 maxblocks 550452.0 KBps (30.0 secs, 3 samples)
File Copy 1024 bufsize 2000 maxblocks 347159.0 KBps (30.0 secs, 3 samples)
File Read 256 bufsize 500 maxblocks 314644.0 KBps (30.0 secs, 3 samples)
File Write 256 bufsize 500 maxblocks 151852.0 KBps (30.0 secs, 3 samples)
File Copy 256 bufsize 500 maxblocks 101000.0 KBps (30.0 secs, 3 samples)
File Read 4096 bufsize 8000 maxblocks 2033256.0 KBps (30.0 secs, 3 samples)
File Write 4096 bufsize 8000 maxblocks 1611814.0 KBps (30.0 secs, 3 samples)
File Copy 4096 bufsize 8000 maxblocks 847979.0 KBps (30.0 secs, 3 samples)
Dc: sqrt(2) to 99 decimal places 128148.7 lpm (30.0 secs, 3 samples)
INDEX VALUES
TEST BASELINE RESULT INDEX
Execl Throughput 43.0 3012.9 700.7
File Copy 1024 bufsize 2000 maxblocks 3960.0 347159.0 876.7
File Copy 256 bufsize 500 maxblocks 1655.0 101000.0 610.3
File Copy 4096 bufsize 8000 maxblocks 5800.0 847979.0 1462.0
Shell Scripts (8 concurrent) 6.0 1120.3 1867.2
=========
FINAL SCORE 1004.6
This patch:
Remove refcnt from page_cgroup().
After this,
* A page is charged only when !page_mapped() && no page_cgroup is assigned.
* Anon page is newly mapped.
* File page is added to mapping->tree.
* A page is uncharged only when
* Anon page is fully unmapped.
* File page is removed from LRU.
There is no change in behavior from user's view.
This patch also removes unnecessary calls in rmap.c which was used only for
refcnt mangement.
[akpm@linux-foundation.org: fix warning]
[hugh@veritas.com: fix shmem_unuse_inode charging]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Hugh Dickins <hugh@veritas.com>
Cc: YAMAMOTO Takashi <yamamoto@valinux.co.jp>
Cc: Paul Menage <menage@google.com>
Cc: David Rientjes <rientjes@google.com>
Signed-off-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-07-25 15:47:14 +07:00
|
|
|
* uncharge if !page_mapped(page)
|
2008-02-07 15:13:53 +07:00
|
|
|
*/
|
2009-01-08 09:08:00 +07:00
|
|
|
static struct mem_cgroup *
|
2012-08-01 06:45:25 +07:00
|
|
|
__mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype,
|
|
|
|
bool end_migration)
|
2008-02-07 15:13:53 +07:00
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
struct mem_cgroup *memcg = NULL;
|
2011-03-24 06:42:36 +07:00
|
|
|
unsigned int nr_pages = 1;
|
|
|
|
struct page_cgroup *pc;
|
2012-03-22 06:34:22 +07:00
|
|
|
bool anon;
|
2008-02-07 15:13:53 +07:00
|
|
|
|
2009-01-08 09:08:02 +07:00
|
|
|
if (mem_cgroup_disabled())
|
2009-01-08 09:08:00 +07:00
|
|
|
return NULL;
|
2008-04-05 04:29:59 +07:00
|
|
|
|
2012-08-01 06:45:31 +07:00
|
|
|
VM_BUG_ON(PageSwapCache(page));
|
2009-01-08 09:07:56 +07:00
|
|
|
|
2011-01-14 06:47:16 +07:00
|
|
|
if (PageTransHuge(page)) {
|
2011-03-24 06:42:36 +07:00
|
|
|
nr_pages <<= compound_order(page);
|
2011-01-14 06:47:16 +07:00
|
|
|
VM_BUG_ON(!PageTransHuge(page));
|
|
|
|
}
|
2008-02-07 15:13:59 +07:00
|
|
|
/*
|
2008-02-07 15:14:41 +07:00
|
|
|
* Check if our page_cgroup is valid
|
2008-02-07 15:13:59 +07:00
|
|
|
*/
|
2008-10-19 10:28:16 +07:00
|
|
|
pc = lookup_page_cgroup(page);
|
2012-01-13 08:18:38 +07:00
|
|
|
if (unlikely(!PageCgroupUsed(pc)))
|
2009-01-08 09:08:00 +07:00
|
|
|
return NULL;
|
2008-03-05 05:29:11 +07:00
|
|
|
|
2008-10-19 10:28:16 +07:00
|
|
|
lock_page_cgroup(pc);
|
2009-01-08 09:07:56 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
memcg = pc->mem_cgroup;
|
2009-01-08 09:08:00 +07:00
|
|
|
|
2009-01-08 09:07:56 +07:00
|
|
|
if (!PageCgroupUsed(pc))
|
|
|
|
goto unlock_out;
|
|
|
|
|
2012-03-22 06:34:22 +07:00
|
|
|
anon = PageAnon(page);
|
|
|
|
|
2009-01-08 09:07:56 +07:00
|
|
|
switch (ctype) {
|
2012-08-01 06:41:40 +07:00
|
|
|
case MEM_CGROUP_CHARGE_TYPE_ANON:
|
2012-03-22 06:34:25 +07:00
|
|
|
/*
|
|
|
|
* Generally PageAnon tells if it's the anon statistics to be
|
|
|
|
* updated; but sometimes e.g. mem_cgroup_uncharge_page() is
|
|
|
|
* used before page reached the stage of being marked PageAnon.
|
|
|
|
*/
|
2012-03-22 06:34:22 +07:00
|
|
|
anon = true;
|
|
|
|
/* fallthrough */
|
2009-06-18 06:27:17 +07:00
|
|
|
case MEM_CGROUP_CHARGE_TYPE_DROP:
|
memcg: fix mis-accounting of file mapped racy with migration
FILE_MAPPED per memcg of migrated file cache is not properly updated,
because our hook in page_add_file_rmap() can't know to which memcg
FILE_MAPPED should be counted.
Basically, this patch is for fixing the bug but includes some big changes
to fix up other messes.
Now, at migrating mapped file, events happen in following sequence.
1. allocate a new page.
2. get memcg of an old page.
3. charge ageinst a new page before migration. But at this point,
no changes to new page's page_cgroup, no commit for the charge.
(IOW, PCG_USED bit is not set.)
4. page migration replaces radix-tree, old-page and new-page.
5. page migration remaps the new page if the old page was mapped.
6. Here, the new page is unlocked.
7. memcg commits the charge for newpage, Mark the new page's page_cgroup
as PCG_USED.
Because "commit" happens after page-remap, we can count FILE_MAPPED
at "5", because we should avoid to trust page_cgroup->mem_cgroup.
if PCG_USED bit is unset.
(Note: memcg's LRU removal code does that but LRU-isolation logic is used
for helping it. When we overwrite page_cgroup->mem_cgroup, page_cgroup is
not on LRU or page_cgroup->mem_cgroup is NULL.)
We can lose file_mapped accounting information at 5 because FILE_MAPPED
is updated only when mapcount changes 0->1. So we should catch it.
BTW, historically, above implemntation comes from migration-failure
of anonymous page. Because we charge both of old page and new page
with mapcount=0, we can't catch
- the page is really freed before remap.
- migration fails but it's freed before remap
or .....corner cases.
New migration sequence with memcg is:
1. allocate a new page.
2. mark PageCgroupMigration to the old page.
3. charge against a new page onto the old page's memcg. (here, new page's pc
is marked as PageCgroupUsed.)
4. page migration replaces radix-tree, page table, etc...
5. At remapping, new page's page_cgroup is now makrked as "USED"
We can catch 0->1 event and FILE_MAPPED will be properly updated.
And we can catch SWAPOUT event after unlock this and freeing this
page by unmap() can be caught.
7. Clear PageCgroupMigration of the old page.
So, FILE_MAPPED will be correctly updated.
Then, for what MIGRATION flag is ?
Without it, at migration failure, we may have to charge old page again
because it may be fully unmapped. "charge" means that we have to dive into
memory reclaim or something complated. So, it's better to avoid
charge it again. Before this patch, __commit_charge() was working for
both of the old/new page and fixed up all. But this technique has some
racy condtion around FILE_MAPPED and SWAPOUT etc...
Now, the kernel use MIGRATION flag and don't uncharge old page until
the end of migration.
I hope this change will make memcg's page migration much simpler. This
page migration has caused several troubles. Worth to add a flag for
simplification.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Reported-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Christoph Lameter <cl@linux-foundation.org>
Cc: "Kirill A. Shutemov" <kirill@shutemov.name>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-05-27 04:42:46 +07:00
|
|
|
/* See mem_cgroup_prepare_migration() */
|
2012-08-01 06:45:25 +07:00
|
|
|
if (page_mapped(page))
|
|
|
|
goto unlock_out;
|
|
|
|
/*
|
|
|
|
* Pages under migration may not be uncharged. But
|
|
|
|
* end_migration() /must/ be the one uncharging the
|
|
|
|
* unused post-migration page and so it has to call
|
|
|
|
* here with the migration bit still set. See the
|
|
|
|
* res_counter handling below.
|
|
|
|
*/
|
|
|
|
if (!end_migration && PageCgroupMigration(pc))
|
2009-01-08 09:07:56 +07:00
|
|
|
goto unlock_out;
|
|
|
|
break;
|
|
|
|
case MEM_CGROUP_CHARGE_TYPE_SWAPOUT:
|
|
|
|
if (!PageAnon(page)) { /* Shared memory */
|
|
|
|
if (page->mapping && !page_is_file_cache(page))
|
|
|
|
goto unlock_out;
|
|
|
|
} else if (page_mapped(page)) /* Anon */
|
|
|
|
goto unlock_out;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2008-10-19 10:28:16 +07:00
|
|
|
}
|
2009-01-08 09:07:56 +07:00
|
|
|
|
2012-03-22 06:34:22 +07:00
|
|
|
mem_cgroup_charge_statistics(memcg, anon, -nr_pages);
|
2009-04-03 06:57:33 +07:00
|
|
|
|
2008-10-19 10:28:16 +07:00
|
|
|
ClearPageCgroupUsed(pc);
|
2009-01-08 09:08:34 +07:00
|
|
|
/*
|
|
|
|
* pc->mem_cgroup is not cleared here. It will be accessed when it's
|
|
|
|
* freed from LRU. This is safe because uncharged page is expected not
|
|
|
|
* to be reused (freed soon). Exception is SwapCache, it's handled by
|
|
|
|
* special functions.
|
|
|
|
*/
|
2008-03-05 05:29:11 +07:00
|
|
|
|
2008-10-19 10:28:16 +07:00
|
|
|
unlock_page_cgroup(pc);
|
memcg: avoid css_get()
Now, memory cgroup increments css(cgroup subsys state)'s reference count
per a charged page. And the reference count is kept until the page is
uncharged. But this has 2 bad effect.
1. Because css_get/put calls atomic_inc()/dec, heavy call of them
on large smp will not scale well.
2. Because css's refcnt cannot be in a state as "ready-to-release",
cgroup's notify_on_release handler can't work with memcg.
3. css's refcnt is atomic_t, it means smaller than 32bit. Maybe too small.
This has been a problem since the 1st merge of memcg.
This is a trial to remove css's refcnt per a page. Even if we remove
refcnt, pre_destroy() does enough synchronization as
- check res->usage == 0.
- check no pages on LRU.
This patch removes css's refcnt per page. Even after this patch, at the
1st look, it seems css_get() is still called in try_charge().
But the logic is.
- If a memcg of mm->owner is cached one, consume_stock() will work.
At success, return immediately.
- If consume_stock returns false, css_get() is called and go to
slow path which may be blocked. At the end of slow path,
css_put() is called and restart from the start if necessary.
So, in the fast path, we don't call css_get() and can avoid access to
shared counter. This patch can make the most possible case fast.
Here is a result of multi-threaded page fault benchmark.
[Before]
25.32% multi-fault-all [kernel.kallsyms] [k] clear_page_c
9.30% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
8.02% multi-fault-all [kernel.kallsyms] [k] try_get_mem_cgroup_from_mm <=====(*)
7.83% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
5.38% multi-fault-all [kernel.kallsyms] [k] __css_put
5.29% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
4.92% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
4.24% multi-fault-all [kernel.kallsyms] [k] up_read
3.53% multi-fault-all [kernel.kallsyms] [k] css_put
2.11% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
1.76% multi-fault-all [kernel.kallsyms] [k] __rmqueue
1.64% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
[After]
28.41% multi-fault-all [kernel.kallsyms] [k] clear_page_c
10.08% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
9.58% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
9.38% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
5.86% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
5.65% multi-fault-all [kernel.kallsyms] [k] up_read
2.82% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
2.64% multi-fault-all [kernel.kallsyms] [k] mem_cgroup_add_lru_list
2.48% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
Then, 8.02% of try_get_mem_cgroup_from_mm() disappears because this patch
removes css_tryget() in it. (But yes, this is an extreme case.)
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-11 08:03:02 +07:00
|
|
|
/*
|
2011-11-03 03:38:15 +07:00
|
|
|
* even after unlock, we have memcg->res.usage here and this memcg
|
memcg: avoid css_get()
Now, memory cgroup increments css(cgroup subsys state)'s reference count
per a charged page. And the reference count is kept until the page is
uncharged. But this has 2 bad effect.
1. Because css_get/put calls atomic_inc()/dec, heavy call of them
on large smp will not scale well.
2. Because css's refcnt cannot be in a state as "ready-to-release",
cgroup's notify_on_release handler can't work with memcg.
3. css's refcnt is atomic_t, it means smaller than 32bit. Maybe too small.
This has been a problem since the 1st merge of memcg.
This is a trial to remove css's refcnt per a page. Even if we remove
refcnt, pre_destroy() does enough synchronization as
- check res->usage == 0.
- check no pages on LRU.
This patch removes css's refcnt per page. Even after this patch, at the
1st look, it seems css_get() is still called in try_charge().
But the logic is.
- If a memcg of mm->owner is cached one, consume_stock() will work.
At success, return immediately.
- If consume_stock returns false, css_get() is called and go to
slow path which may be blocked. At the end of slow path,
css_put() is called and restart from the start if necessary.
So, in the fast path, we don't call css_get() and can avoid access to
shared counter. This patch can make the most possible case fast.
Here is a result of multi-threaded page fault benchmark.
[Before]
25.32% multi-fault-all [kernel.kallsyms] [k] clear_page_c
9.30% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
8.02% multi-fault-all [kernel.kallsyms] [k] try_get_mem_cgroup_from_mm <=====(*)
7.83% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
5.38% multi-fault-all [kernel.kallsyms] [k] __css_put
5.29% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
4.92% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
4.24% multi-fault-all [kernel.kallsyms] [k] up_read
3.53% multi-fault-all [kernel.kallsyms] [k] css_put
2.11% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
1.76% multi-fault-all [kernel.kallsyms] [k] __rmqueue
1.64% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
[After]
28.41% multi-fault-all [kernel.kallsyms] [k] clear_page_c
10.08% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
9.58% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
9.38% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
5.86% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
5.65% multi-fault-all [kernel.kallsyms] [k] up_read
2.82% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
2.64% multi-fault-all [kernel.kallsyms] [k] mem_cgroup_add_lru_list
2.48% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
Then, 8.02% of try_get_mem_cgroup_from_mm() disappears because this patch
removes css_tryget() in it. (But yes, this is an extreme case.)
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-11 08:03:02 +07:00
|
|
|
* will never be freed.
|
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
memcg_check_events(memcg, page);
|
memcg: avoid css_get()
Now, memory cgroup increments css(cgroup subsys state)'s reference count
per a charged page. And the reference count is kept until the page is
uncharged. But this has 2 bad effect.
1. Because css_get/put calls atomic_inc()/dec, heavy call of them
on large smp will not scale well.
2. Because css's refcnt cannot be in a state as "ready-to-release",
cgroup's notify_on_release handler can't work with memcg.
3. css's refcnt is atomic_t, it means smaller than 32bit. Maybe too small.
This has been a problem since the 1st merge of memcg.
This is a trial to remove css's refcnt per a page. Even if we remove
refcnt, pre_destroy() does enough synchronization as
- check res->usage == 0.
- check no pages on LRU.
This patch removes css's refcnt per page. Even after this patch, at the
1st look, it seems css_get() is still called in try_charge().
But the logic is.
- If a memcg of mm->owner is cached one, consume_stock() will work.
At success, return immediately.
- If consume_stock returns false, css_get() is called and go to
slow path which may be blocked. At the end of slow path,
css_put() is called and restart from the start if necessary.
So, in the fast path, we don't call css_get() and can avoid access to
shared counter. This patch can make the most possible case fast.
Here is a result of multi-threaded page fault benchmark.
[Before]
25.32% multi-fault-all [kernel.kallsyms] [k] clear_page_c
9.30% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
8.02% multi-fault-all [kernel.kallsyms] [k] try_get_mem_cgroup_from_mm <=====(*)
7.83% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
5.38% multi-fault-all [kernel.kallsyms] [k] __css_put
5.29% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
4.92% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
4.24% multi-fault-all [kernel.kallsyms] [k] up_read
3.53% multi-fault-all [kernel.kallsyms] [k] css_put
2.11% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
1.76% multi-fault-all [kernel.kallsyms] [k] __rmqueue
1.64% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
[After]
28.41% multi-fault-all [kernel.kallsyms] [k] clear_page_c
10.08% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
9.58% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
9.38% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
5.86% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
5.65% multi-fault-all [kernel.kallsyms] [k] up_read
2.82% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
2.64% multi-fault-all [kernel.kallsyms] [k] mem_cgroup_add_lru_list
2.48% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
Then, 8.02% of try_get_mem_cgroup_from_mm() disappears because this patch
removes css_tryget() in it. (But yes, this is an extreme case.)
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-11 08:03:02 +07:00
|
|
|
if (do_swap_account && ctype == MEM_CGROUP_CHARGE_TYPE_SWAPOUT) {
|
2011-11-03 03:38:15 +07:00
|
|
|
mem_cgroup_swap_statistics(memcg, true);
|
|
|
|
mem_cgroup_get(memcg);
|
memcg: avoid css_get()
Now, memory cgroup increments css(cgroup subsys state)'s reference count
per a charged page. And the reference count is kept until the page is
uncharged. But this has 2 bad effect.
1. Because css_get/put calls atomic_inc()/dec, heavy call of them
on large smp will not scale well.
2. Because css's refcnt cannot be in a state as "ready-to-release",
cgroup's notify_on_release handler can't work with memcg.
3. css's refcnt is atomic_t, it means smaller than 32bit. Maybe too small.
This has been a problem since the 1st merge of memcg.
This is a trial to remove css's refcnt per a page. Even if we remove
refcnt, pre_destroy() does enough synchronization as
- check res->usage == 0.
- check no pages on LRU.
This patch removes css's refcnt per page. Even after this patch, at the
1st look, it seems css_get() is still called in try_charge().
But the logic is.
- If a memcg of mm->owner is cached one, consume_stock() will work.
At success, return immediately.
- If consume_stock returns false, css_get() is called and go to
slow path which may be blocked. At the end of slow path,
css_put() is called and restart from the start if necessary.
So, in the fast path, we don't call css_get() and can avoid access to
shared counter. This patch can make the most possible case fast.
Here is a result of multi-threaded page fault benchmark.
[Before]
25.32% multi-fault-all [kernel.kallsyms] [k] clear_page_c
9.30% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
8.02% multi-fault-all [kernel.kallsyms] [k] try_get_mem_cgroup_from_mm <=====(*)
7.83% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
5.38% multi-fault-all [kernel.kallsyms] [k] __css_put
5.29% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
4.92% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
4.24% multi-fault-all [kernel.kallsyms] [k] up_read
3.53% multi-fault-all [kernel.kallsyms] [k] css_put
2.11% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
1.76% multi-fault-all [kernel.kallsyms] [k] __rmqueue
1.64% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
[After]
28.41% multi-fault-all [kernel.kallsyms] [k] clear_page_c
10.08% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
9.58% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
9.38% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
5.86% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
5.65% multi-fault-all [kernel.kallsyms] [k] up_read
2.82% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
2.64% multi-fault-all [kernel.kallsyms] [k] mem_cgroup_add_lru_list
2.48% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
Then, 8.02% of try_get_mem_cgroup_from_mm() disappears because this patch
removes css_tryget() in it. (But yes, this is an extreme case.)
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-11 08:03:02 +07:00
|
|
|
}
|
2012-08-01 06:45:25 +07:00
|
|
|
/*
|
|
|
|
* Migration does not charge the res_counter for the
|
|
|
|
* replacement page, so leave it alone when phasing out the
|
|
|
|
* page that is unused after the migration.
|
|
|
|
*/
|
|
|
|
if (!end_migration && !mem_cgroup_is_root(memcg))
|
2011-11-03 03:38:15 +07:00
|
|
|
mem_cgroup_do_uncharge(memcg, nr_pages, ctype);
|
2008-02-07 15:14:31 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
return memcg;
|
2009-01-08 09:07:56 +07:00
|
|
|
|
|
|
|
unlock_out:
|
|
|
|
unlock_page_cgroup(pc);
|
2009-01-08 09:08:00 +07:00
|
|
|
return NULL;
|
2008-02-07 15:14:41 +07:00
|
|
|
}
|
|
|
|
|
memcg: remove refcnt from page_cgroup
memcg: performance improvements
Patch Description
1/5 ... remove refcnt fron page_cgroup patch (shmem handling is fixed)
2/5 ... swapcache handling patch
3/5 ... add helper function for shmem's memory reclaim patch
4/5 ... optimize by likely/unlikely ppatch
5/5 ... remove redundunt check patch (shmem handling is fixed.)
Unix bench result.
== 2.6.26-rc2-mm1 + memory resource controller
Execl Throughput 2915.4 lps (29.6 secs, 3 samples)
C Compiler Throughput 1019.3 lpm (60.0 secs, 3 samples)
Shell Scripts (1 concurrent) 5796.0 lpm (60.0 secs, 3 samples)
Shell Scripts (8 concurrent) 1097.7 lpm (60.0 secs, 3 samples)
Shell Scripts (16 concurrent) 565.3 lpm (60.0 secs, 3 samples)
File Read 1024 bufsize 2000 maxblocks 1022128.0 KBps (30.0 secs, 3 samples)
File Write 1024 bufsize 2000 maxblocks 544057.0 KBps (30.0 secs, 3 samples)
File Copy 1024 bufsize 2000 maxblocks 346481.0 KBps (30.0 secs, 3 samples)
File Read 256 bufsize 500 maxblocks 319325.0 KBps (30.0 secs, 3 samples)
File Write 256 bufsize 500 maxblocks 148788.0 KBps (30.0 secs, 3 samples)
File Copy 256 bufsize 500 maxblocks 99051.0 KBps (30.0 secs, 3 samples)
File Read 4096 bufsize 8000 maxblocks 2058917.0 KBps (30.0 secs, 3 samples)
File Write 4096 bufsize 8000 maxblocks 1606109.0 KBps (30.0 secs, 3 samples)
File Copy 4096 bufsize 8000 maxblocks 854789.0 KBps (30.0 secs, 3 samples)
Dc: sqrt(2) to 99 decimal places 126145.2 lpm (30.0 secs, 3 samples)
INDEX VALUES
TEST BASELINE RESULT INDEX
Execl Throughput 43.0 2915.4 678.0
File Copy 1024 bufsize 2000 maxblocks 3960.0 346481.0 875.0
File Copy 256 bufsize 500 maxblocks 1655.0 99051.0 598.5
File Copy 4096 bufsize 8000 maxblocks 5800.0 854789.0 1473.8
Shell Scripts (8 concurrent) 6.0 1097.7 1829.5
=========
FINAL SCORE 991.3
== 2.6.26-rc2-mm1 + this set ==
Execl Throughput 3012.9 lps (29.9 secs, 3 samples)
C Compiler Throughput 981.0 lpm (60.0 secs, 3 samples)
Shell Scripts (1 concurrent) 5872.0 lpm (60.0 secs, 3 samples)
Shell Scripts (8 concurrent) 1120.3 lpm (60.0 secs, 3 samples)
Shell Scripts (16 concurrent) 578.0 lpm (60.0 secs, 3 samples)
File Read 1024 bufsize 2000 maxblocks 1003993.0 KBps (30.0 secs, 3 samples)
File Write 1024 bufsize 2000 maxblocks 550452.0 KBps (30.0 secs, 3 samples)
File Copy 1024 bufsize 2000 maxblocks 347159.0 KBps (30.0 secs, 3 samples)
File Read 256 bufsize 500 maxblocks 314644.0 KBps (30.0 secs, 3 samples)
File Write 256 bufsize 500 maxblocks 151852.0 KBps (30.0 secs, 3 samples)
File Copy 256 bufsize 500 maxblocks 101000.0 KBps (30.0 secs, 3 samples)
File Read 4096 bufsize 8000 maxblocks 2033256.0 KBps (30.0 secs, 3 samples)
File Write 4096 bufsize 8000 maxblocks 1611814.0 KBps (30.0 secs, 3 samples)
File Copy 4096 bufsize 8000 maxblocks 847979.0 KBps (30.0 secs, 3 samples)
Dc: sqrt(2) to 99 decimal places 128148.7 lpm (30.0 secs, 3 samples)
INDEX VALUES
TEST BASELINE RESULT INDEX
Execl Throughput 43.0 3012.9 700.7
File Copy 1024 bufsize 2000 maxblocks 3960.0 347159.0 876.7
File Copy 256 bufsize 500 maxblocks 1655.0 101000.0 610.3
File Copy 4096 bufsize 8000 maxblocks 5800.0 847979.0 1462.0
Shell Scripts (8 concurrent) 6.0 1120.3 1867.2
=========
FINAL SCORE 1004.6
This patch:
Remove refcnt from page_cgroup().
After this,
* A page is charged only when !page_mapped() && no page_cgroup is assigned.
* Anon page is newly mapped.
* File page is added to mapping->tree.
* A page is uncharged only when
* Anon page is fully unmapped.
* File page is removed from LRU.
There is no change in behavior from user's view.
This patch also removes unnecessary calls in rmap.c which was used only for
refcnt mangement.
[akpm@linux-foundation.org: fix warning]
[hugh@veritas.com: fix shmem_unuse_inode charging]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Hugh Dickins <hugh@veritas.com>
Cc: YAMAMOTO Takashi <yamamoto@valinux.co.jp>
Cc: Paul Menage <menage@google.com>
Cc: David Rientjes <rientjes@google.com>
Signed-off-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-07-25 15:47:14 +07:00
|
|
|
void mem_cgroup_uncharge_page(struct page *page)
|
|
|
|
{
|
2008-10-19 10:28:16 +07:00
|
|
|
/* early check. */
|
|
|
|
if (page_mapped(page))
|
|
|
|
return;
|
2012-01-13 08:18:45 +07:00
|
|
|
VM_BUG_ON(page->mapping && !PageAnon(page));
|
2012-08-01 06:45:31 +07:00
|
|
|
if (PageSwapCache(page))
|
|
|
|
return;
|
2012-08-01 06:45:25 +07:00
|
|
|
__mem_cgroup_uncharge_common(page, MEM_CGROUP_CHARGE_TYPE_ANON, false);
|
memcg: remove refcnt from page_cgroup
memcg: performance improvements
Patch Description
1/5 ... remove refcnt fron page_cgroup patch (shmem handling is fixed)
2/5 ... swapcache handling patch
3/5 ... add helper function for shmem's memory reclaim patch
4/5 ... optimize by likely/unlikely ppatch
5/5 ... remove redundunt check patch (shmem handling is fixed.)
Unix bench result.
== 2.6.26-rc2-mm1 + memory resource controller
Execl Throughput 2915.4 lps (29.6 secs, 3 samples)
C Compiler Throughput 1019.3 lpm (60.0 secs, 3 samples)
Shell Scripts (1 concurrent) 5796.0 lpm (60.0 secs, 3 samples)
Shell Scripts (8 concurrent) 1097.7 lpm (60.0 secs, 3 samples)
Shell Scripts (16 concurrent) 565.3 lpm (60.0 secs, 3 samples)
File Read 1024 bufsize 2000 maxblocks 1022128.0 KBps (30.0 secs, 3 samples)
File Write 1024 bufsize 2000 maxblocks 544057.0 KBps (30.0 secs, 3 samples)
File Copy 1024 bufsize 2000 maxblocks 346481.0 KBps (30.0 secs, 3 samples)
File Read 256 bufsize 500 maxblocks 319325.0 KBps (30.0 secs, 3 samples)
File Write 256 bufsize 500 maxblocks 148788.0 KBps (30.0 secs, 3 samples)
File Copy 256 bufsize 500 maxblocks 99051.0 KBps (30.0 secs, 3 samples)
File Read 4096 bufsize 8000 maxblocks 2058917.0 KBps (30.0 secs, 3 samples)
File Write 4096 bufsize 8000 maxblocks 1606109.0 KBps (30.0 secs, 3 samples)
File Copy 4096 bufsize 8000 maxblocks 854789.0 KBps (30.0 secs, 3 samples)
Dc: sqrt(2) to 99 decimal places 126145.2 lpm (30.0 secs, 3 samples)
INDEX VALUES
TEST BASELINE RESULT INDEX
Execl Throughput 43.0 2915.4 678.0
File Copy 1024 bufsize 2000 maxblocks 3960.0 346481.0 875.0
File Copy 256 bufsize 500 maxblocks 1655.0 99051.0 598.5
File Copy 4096 bufsize 8000 maxblocks 5800.0 854789.0 1473.8
Shell Scripts (8 concurrent) 6.0 1097.7 1829.5
=========
FINAL SCORE 991.3
== 2.6.26-rc2-mm1 + this set ==
Execl Throughput 3012.9 lps (29.9 secs, 3 samples)
C Compiler Throughput 981.0 lpm (60.0 secs, 3 samples)
Shell Scripts (1 concurrent) 5872.0 lpm (60.0 secs, 3 samples)
Shell Scripts (8 concurrent) 1120.3 lpm (60.0 secs, 3 samples)
Shell Scripts (16 concurrent) 578.0 lpm (60.0 secs, 3 samples)
File Read 1024 bufsize 2000 maxblocks 1003993.0 KBps (30.0 secs, 3 samples)
File Write 1024 bufsize 2000 maxblocks 550452.0 KBps (30.0 secs, 3 samples)
File Copy 1024 bufsize 2000 maxblocks 347159.0 KBps (30.0 secs, 3 samples)
File Read 256 bufsize 500 maxblocks 314644.0 KBps (30.0 secs, 3 samples)
File Write 256 bufsize 500 maxblocks 151852.0 KBps (30.0 secs, 3 samples)
File Copy 256 bufsize 500 maxblocks 101000.0 KBps (30.0 secs, 3 samples)
File Read 4096 bufsize 8000 maxblocks 2033256.0 KBps (30.0 secs, 3 samples)
File Write 4096 bufsize 8000 maxblocks 1611814.0 KBps (30.0 secs, 3 samples)
File Copy 4096 bufsize 8000 maxblocks 847979.0 KBps (30.0 secs, 3 samples)
Dc: sqrt(2) to 99 decimal places 128148.7 lpm (30.0 secs, 3 samples)
INDEX VALUES
TEST BASELINE RESULT INDEX
Execl Throughput 43.0 3012.9 700.7
File Copy 1024 bufsize 2000 maxblocks 3960.0 347159.0 876.7
File Copy 256 bufsize 500 maxblocks 1655.0 101000.0 610.3
File Copy 4096 bufsize 8000 maxblocks 5800.0 847979.0 1462.0
Shell Scripts (8 concurrent) 6.0 1120.3 1867.2
=========
FINAL SCORE 1004.6
This patch:
Remove refcnt from page_cgroup().
After this,
* A page is charged only when !page_mapped() && no page_cgroup is assigned.
* Anon page is newly mapped.
* File page is added to mapping->tree.
* A page is uncharged only when
* Anon page is fully unmapped.
* File page is removed from LRU.
There is no change in behavior from user's view.
This patch also removes unnecessary calls in rmap.c which was used only for
refcnt mangement.
[akpm@linux-foundation.org: fix warning]
[hugh@veritas.com: fix shmem_unuse_inode charging]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Hugh Dickins <hugh@veritas.com>
Cc: YAMAMOTO Takashi <yamamoto@valinux.co.jp>
Cc: Paul Menage <menage@google.com>
Cc: David Rientjes <rientjes@google.com>
Signed-off-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-07-25 15:47:14 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
void mem_cgroup_uncharge_cache_page(struct page *page)
|
|
|
|
{
|
|
|
|
VM_BUG_ON(page_mapped(page));
|
2008-10-19 10:28:09 +07:00
|
|
|
VM_BUG_ON(page->mapping);
|
2012-08-01 06:45:25 +07:00
|
|
|
__mem_cgroup_uncharge_common(page, MEM_CGROUP_CHARGE_TYPE_CACHE, false);
|
memcg: remove refcnt from page_cgroup
memcg: performance improvements
Patch Description
1/5 ... remove refcnt fron page_cgroup patch (shmem handling is fixed)
2/5 ... swapcache handling patch
3/5 ... add helper function for shmem's memory reclaim patch
4/5 ... optimize by likely/unlikely ppatch
5/5 ... remove redundunt check patch (shmem handling is fixed.)
Unix bench result.
== 2.6.26-rc2-mm1 + memory resource controller
Execl Throughput 2915.4 lps (29.6 secs, 3 samples)
C Compiler Throughput 1019.3 lpm (60.0 secs, 3 samples)
Shell Scripts (1 concurrent) 5796.0 lpm (60.0 secs, 3 samples)
Shell Scripts (8 concurrent) 1097.7 lpm (60.0 secs, 3 samples)
Shell Scripts (16 concurrent) 565.3 lpm (60.0 secs, 3 samples)
File Read 1024 bufsize 2000 maxblocks 1022128.0 KBps (30.0 secs, 3 samples)
File Write 1024 bufsize 2000 maxblocks 544057.0 KBps (30.0 secs, 3 samples)
File Copy 1024 bufsize 2000 maxblocks 346481.0 KBps (30.0 secs, 3 samples)
File Read 256 bufsize 500 maxblocks 319325.0 KBps (30.0 secs, 3 samples)
File Write 256 bufsize 500 maxblocks 148788.0 KBps (30.0 secs, 3 samples)
File Copy 256 bufsize 500 maxblocks 99051.0 KBps (30.0 secs, 3 samples)
File Read 4096 bufsize 8000 maxblocks 2058917.0 KBps (30.0 secs, 3 samples)
File Write 4096 bufsize 8000 maxblocks 1606109.0 KBps (30.0 secs, 3 samples)
File Copy 4096 bufsize 8000 maxblocks 854789.0 KBps (30.0 secs, 3 samples)
Dc: sqrt(2) to 99 decimal places 126145.2 lpm (30.0 secs, 3 samples)
INDEX VALUES
TEST BASELINE RESULT INDEX
Execl Throughput 43.0 2915.4 678.0
File Copy 1024 bufsize 2000 maxblocks 3960.0 346481.0 875.0
File Copy 256 bufsize 500 maxblocks 1655.0 99051.0 598.5
File Copy 4096 bufsize 8000 maxblocks 5800.0 854789.0 1473.8
Shell Scripts (8 concurrent) 6.0 1097.7 1829.5
=========
FINAL SCORE 991.3
== 2.6.26-rc2-mm1 + this set ==
Execl Throughput 3012.9 lps (29.9 secs, 3 samples)
C Compiler Throughput 981.0 lpm (60.0 secs, 3 samples)
Shell Scripts (1 concurrent) 5872.0 lpm (60.0 secs, 3 samples)
Shell Scripts (8 concurrent) 1120.3 lpm (60.0 secs, 3 samples)
Shell Scripts (16 concurrent) 578.0 lpm (60.0 secs, 3 samples)
File Read 1024 bufsize 2000 maxblocks 1003993.0 KBps (30.0 secs, 3 samples)
File Write 1024 bufsize 2000 maxblocks 550452.0 KBps (30.0 secs, 3 samples)
File Copy 1024 bufsize 2000 maxblocks 347159.0 KBps (30.0 secs, 3 samples)
File Read 256 bufsize 500 maxblocks 314644.0 KBps (30.0 secs, 3 samples)
File Write 256 bufsize 500 maxblocks 151852.0 KBps (30.0 secs, 3 samples)
File Copy 256 bufsize 500 maxblocks 101000.0 KBps (30.0 secs, 3 samples)
File Read 4096 bufsize 8000 maxblocks 2033256.0 KBps (30.0 secs, 3 samples)
File Write 4096 bufsize 8000 maxblocks 1611814.0 KBps (30.0 secs, 3 samples)
File Copy 4096 bufsize 8000 maxblocks 847979.0 KBps (30.0 secs, 3 samples)
Dc: sqrt(2) to 99 decimal places 128148.7 lpm (30.0 secs, 3 samples)
INDEX VALUES
TEST BASELINE RESULT INDEX
Execl Throughput 43.0 3012.9 700.7
File Copy 1024 bufsize 2000 maxblocks 3960.0 347159.0 876.7
File Copy 256 bufsize 500 maxblocks 1655.0 101000.0 610.3
File Copy 4096 bufsize 8000 maxblocks 5800.0 847979.0 1462.0
Shell Scripts (8 concurrent) 6.0 1120.3 1867.2
=========
FINAL SCORE 1004.6
This patch:
Remove refcnt from page_cgroup().
After this,
* A page is charged only when !page_mapped() && no page_cgroup is assigned.
* Anon page is newly mapped.
* File page is added to mapping->tree.
* A page is uncharged only when
* Anon page is fully unmapped.
* File page is removed from LRU.
There is no change in behavior from user's view.
This patch also removes unnecessary calls in rmap.c which was used only for
refcnt mangement.
[akpm@linux-foundation.org: fix warning]
[hugh@veritas.com: fix shmem_unuse_inode charging]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Hugh Dickins <hugh@veritas.com>
Cc: YAMAMOTO Takashi <yamamoto@valinux.co.jp>
Cc: Paul Menage <menage@google.com>
Cc: David Rientjes <rientjes@google.com>
Signed-off-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-07-25 15:47:14 +07:00
|
|
|
}
|
|
|
|
|
2009-12-16 07:47:03 +07:00
|
|
|
/*
|
|
|
|
* Batch_start/batch_end is called in unmap_page_range/invlidate/trucate.
|
|
|
|
* In that cases, pages are freed continuously and we can expect pages
|
|
|
|
* are in the same memcg. All these calls itself limits the number of
|
|
|
|
* pages freed at once, then uncharge_start/end() is called properly.
|
|
|
|
* This may be called prural(2) times in a context,
|
|
|
|
*/
|
|
|
|
|
|
|
|
void mem_cgroup_uncharge_start(void)
|
|
|
|
{
|
|
|
|
current->memcg_batch.do_batch++;
|
|
|
|
/* We can do nest. */
|
|
|
|
if (current->memcg_batch.do_batch == 1) {
|
|
|
|
current->memcg_batch.memcg = NULL;
|
2011-03-24 06:42:35 +07:00
|
|
|
current->memcg_batch.nr_pages = 0;
|
|
|
|
current->memcg_batch.memsw_nr_pages = 0;
|
2009-12-16 07:47:03 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void mem_cgroup_uncharge_end(void)
|
|
|
|
{
|
|
|
|
struct memcg_batch_info *batch = ¤t->memcg_batch;
|
|
|
|
|
|
|
|
if (!batch->do_batch)
|
|
|
|
return;
|
|
|
|
|
|
|
|
batch->do_batch--;
|
|
|
|
if (batch->do_batch) /* If stacked, do nothing. */
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!batch->memcg)
|
|
|
|
return;
|
|
|
|
/*
|
|
|
|
* This "batch->memcg" is valid without any css_get/put etc...
|
|
|
|
* bacause we hide charges behind us.
|
|
|
|
*/
|
2011-03-24 06:42:35 +07:00
|
|
|
if (batch->nr_pages)
|
|
|
|
res_counter_uncharge(&batch->memcg->res,
|
|
|
|
batch->nr_pages * PAGE_SIZE);
|
|
|
|
if (batch->memsw_nr_pages)
|
|
|
|
res_counter_uncharge(&batch->memcg->memsw,
|
|
|
|
batch->memsw_nr_pages * PAGE_SIZE);
|
2010-05-27 04:42:37 +07:00
|
|
|
memcg_oom_recover(batch->memcg);
|
2009-12-16 07:47:03 +07:00
|
|
|
/* forget this pointer (for sanity check) */
|
|
|
|
batch->memcg = NULL;
|
|
|
|
}
|
|
|
|
|
2009-05-29 04:34:28 +07:00
|
|
|
#ifdef CONFIG_SWAP
|
2009-01-08 09:08:00 +07:00
|
|
|
/*
|
2009-05-29 04:34:28 +07:00
|
|
|
* called after __delete_from_swap_cache() and drop "page" account.
|
2009-01-08 09:08:00 +07:00
|
|
|
* memcg information is recorded to swap_cgroup of "ent"
|
|
|
|
*/
|
2009-06-18 06:27:17 +07:00
|
|
|
void
|
|
|
|
mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout)
|
2009-01-08 09:08:00 +07:00
|
|
|
{
|
|
|
|
struct mem_cgroup *memcg;
|
2009-06-18 06:27:17 +07:00
|
|
|
int ctype = MEM_CGROUP_CHARGE_TYPE_SWAPOUT;
|
|
|
|
|
|
|
|
if (!swapout) /* this was a swap cache but the swap is unused ! */
|
|
|
|
ctype = MEM_CGROUP_CHARGE_TYPE_DROP;
|
|
|
|
|
2012-08-01 06:45:25 +07:00
|
|
|
memcg = __mem_cgroup_uncharge_common(page, ctype, false);
|
2009-01-08 09:08:00 +07:00
|
|
|
|
memcg: avoid css_get()
Now, memory cgroup increments css(cgroup subsys state)'s reference count
per a charged page. And the reference count is kept until the page is
uncharged. But this has 2 bad effect.
1. Because css_get/put calls atomic_inc()/dec, heavy call of them
on large smp will not scale well.
2. Because css's refcnt cannot be in a state as "ready-to-release",
cgroup's notify_on_release handler can't work with memcg.
3. css's refcnt is atomic_t, it means smaller than 32bit. Maybe too small.
This has been a problem since the 1st merge of memcg.
This is a trial to remove css's refcnt per a page. Even if we remove
refcnt, pre_destroy() does enough synchronization as
- check res->usage == 0.
- check no pages on LRU.
This patch removes css's refcnt per page. Even after this patch, at the
1st look, it seems css_get() is still called in try_charge().
But the logic is.
- If a memcg of mm->owner is cached one, consume_stock() will work.
At success, return immediately.
- If consume_stock returns false, css_get() is called and go to
slow path which may be blocked. At the end of slow path,
css_put() is called and restart from the start if necessary.
So, in the fast path, we don't call css_get() and can avoid access to
shared counter. This patch can make the most possible case fast.
Here is a result of multi-threaded page fault benchmark.
[Before]
25.32% multi-fault-all [kernel.kallsyms] [k] clear_page_c
9.30% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
8.02% multi-fault-all [kernel.kallsyms] [k] try_get_mem_cgroup_from_mm <=====(*)
7.83% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
5.38% multi-fault-all [kernel.kallsyms] [k] __css_put
5.29% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
4.92% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
4.24% multi-fault-all [kernel.kallsyms] [k] up_read
3.53% multi-fault-all [kernel.kallsyms] [k] css_put
2.11% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
1.76% multi-fault-all [kernel.kallsyms] [k] __rmqueue
1.64% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
[After]
28.41% multi-fault-all [kernel.kallsyms] [k] clear_page_c
10.08% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irq
9.58% multi-fault-all [kernel.kallsyms] [k] down_read_trylock
9.38% multi-fault-all [kernel.kallsyms] [k] _raw_spin_lock_irqsave
5.86% multi-fault-all [kernel.kallsyms] [k] __alloc_pages_nodemask
5.65% multi-fault-all [kernel.kallsyms] [k] up_read
2.82% multi-fault-all [kernel.kallsyms] [k] handle_mm_fault
2.64% multi-fault-all [kernel.kallsyms] [k] mem_cgroup_add_lru_list
2.48% multi-fault-all [kernel.kallsyms] [k] __mem_cgroup_commit_charge
Then, 8.02% of try_get_mem_cgroup_from_mm() disappears because this patch
removes css_tryget() in it. (But yes, this is an extreme case.)
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-08-11 08:03:02 +07:00
|
|
|
/*
|
|
|
|
* record memcg information, if swapout && memcg != NULL,
|
|
|
|
* mem_cgroup_get() was called in uncharge().
|
|
|
|
*/
|
|
|
|
if (do_swap_account && swapout && memcg)
|
2009-04-03 06:57:45 +07:00
|
|
|
swap_cgroup_record(ent, css_id(&memcg->css));
|
2009-01-08 09:08:00 +07:00
|
|
|
}
|
2009-05-29 04:34:28 +07:00
|
|
|
#endif
|
2009-01-08 09:08:00 +07:00
|
|
|
|
2012-08-01 06:43:02 +07:00
|
|
|
#ifdef CONFIG_MEMCG_SWAP
|
2009-01-08 09:08:00 +07:00
|
|
|
/*
|
|
|
|
* called from swap_entry_free(). remove record in swap_cgroup and
|
|
|
|
* uncharge "memsw" account.
|
|
|
|
*/
|
|
|
|
void mem_cgroup_uncharge_swap(swp_entry_t ent)
|
2009-01-08 09:07:56 +07:00
|
|
|
{
|
2009-01-08 09:08:00 +07:00
|
|
|
struct mem_cgroup *memcg;
|
2009-04-03 06:57:45 +07:00
|
|
|
unsigned short id;
|
2009-01-08 09:08:00 +07:00
|
|
|
|
|
|
|
if (!do_swap_account)
|
|
|
|
return;
|
|
|
|
|
2009-04-03 06:57:45 +07:00
|
|
|
id = swap_cgroup_record(ent, 0);
|
|
|
|
rcu_read_lock();
|
|
|
|
memcg = mem_cgroup_lookup(id);
|
2009-01-08 09:08:00 +07:00
|
|
|
if (memcg) {
|
2009-04-03 06:57:45 +07:00
|
|
|
/*
|
|
|
|
* We uncharge this because swap is freed.
|
|
|
|
* This memcg can be obsolete one. We avoid calling css_tryget
|
|
|
|
*/
|
2009-09-24 05:56:42 +07:00
|
|
|
if (!mem_cgroup_is_root(memcg))
|
2009-10-02 05:44:11 +07:00
|
|
|
res_counter_uncharge(&memcg->memsw, PAGE_SIZE);
|
2009-09-24 05:56:42 +07:00
|
|
|
mem_cgroup_swap_statistics(memcg, false);
|
2009-01-08 09:08:00 +07:00
|
|
|
mem_cgroup_put(memcg);
|
|
|
|
}
|
2009-04-03 06:57:45 +07:00
|
|
|
rcu_read_unlock();
|
2009-01-08 09:07:56 +07:00
|
|
|
}
|
2010-03-11 06:22:17 +07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* mem_cgroup_move_swap_account - move swap charge and swap_cgroup's record.
|
|
|
|
* @entry: swap entry to be moved
|
|
|
|
* @from: mem_cgroup which the entry is moved from
|
|
|
|
* @to: mem_cgroup which the entry is moved to
|
|
|
|
*
|
|
|
|
* It succeeds only when the swap_cgroup's record for this entry is the same
|
|
|
|
* as the mem_cgroup's id of @from.
|
|
|
|
*
|
|
|
|
* Returns 0 on success, -EINVAL on failure.
|
|
|
|
*
|
|
|
|
* The caller must have charged to @to, IOW, called res_counter_charge() about
|
|
|
|
* both res and memsw, and called css_get().
|
|
|
|
*/
|
|
|
|
static int mem_cgroup_move_swap_account(swp_entry_t entry,
|
2012-05-30 05:06:51 +07:00
|
|
|
struct mem_cgroup *from, struct mem_cgroup *to)
|
2010-03-11 06:22:17 +07:00
|
|
|
{
|
|
|
|
unsigned short old_id, new_id;
|
|
|
|
|
|
|
|
old_id = css_id(&from->css);
|
|
|
|
new_id = css_id(&to->css);
|
|
|
|
|
|
|
|
if (swap_cgroup_cmpxchg(entry, old_id, new_id) == old_id) {
|
|
|
|
mem_cgroup_swap_statistics(from, false);
|
2010-03-11 06:22:18 +07:00
|
|
|
mem_cgroup_swap_statistics(to, true);
|
2010-03-11 06:22:17 +07:00
|
|
|
/*
|
2010-03-11 06:22:18 +07:00
|
|
|
* This function is only called from task migration context now.
|
|
|
|
* It postpones res_counter and refcount handling till the end
|
|
|
|
* of task migration(mem_cgroup_clear_mc()) for performance
|
|
|
|
* improvement. But we cannot postpone mem_cgroup_get(to)
|
|
|
|
* because if the process that has been moved to @to does
|
|
|
|
* swap-in, the refcount of @to might be decreased to 0.
|
2010-03-11 06:22:17 +07:00
|
|
|
*/
|
|
|
|
mem_cgroup_get(to);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static inline int mem_cgroup_move_swap_account(swp_entry_t entry,
|
2012-05-30 05:06:51 +07:00
|
|
|
struct mem_cgroup *from, struct mem_cgroup *to)
|
2010-03-11 06:22:17 +07:00
|
|
|
{
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2009-01-08 09:08:00 +07:00
|
|
|
#endif
|
2009-01-08 09:07:56 +07:00
|
|
|
|
2008-02-07 15:14:10 +07:00
|
|
|
/*
|
2009-01-08 09:07:50 +07:00
|
|
|
* Before starting migration, account PAGE_SIZE to mem_cgroup that the old
|
|
|
|
* page belongs to.
|
2008-02-07 15:14:10 +07:00
|
|
|
*/
|
2012-08-01 06:45:25 +07:00
|
|
|
void mem_cgroup_prepare_migration(struct page *page, struct page *newpage,
|
|
|
|
struct mem_cgroup **memcgp)
|
2008-02-07 15:14:10 +07:00
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
struct mem_cgroup *memcg = NULL;
|
2011-03-24 06:42:36 +07:00
|
|
|
struct page_cgroup *pc;
|
memcg: fix mis-accounting of file mapped racy with migration
FILE_MAPPED per memcg of migrated file cache is not properly updated,
because our hook in page_add_file_rmap() can't know to which memcg
FILE_MAPPED should be counted.
Basically, this patch is for fixing the bug but includes some big changes
to fix up other messes.
Now, at migrating mapped file, events happen in following sequence.
1. allocate a new page.
2. get memcg of an old page.
3. charge ageinst a new page before migration. But at this point,
no changes to new page's page_cgroup, no commit for the charge.
(IOW, PCG_USED bit is not set.)
4. page migration replaces radix-tree, old-page and new-page.
5. page migration remaps the new page if the old page was mapped.
6. Here, the new page is unlocked.
7. memcg commits the charge for newpage, Mark the new page's page_cgroup
as PCG_USED.
Because "commit" happens after page-remap, we can count FILE_MAPPED
at "5", because we should avoid to trust page_cgroup->mem_cgroup.
if PCG_USED bit is unset.
(Note: memcg's LRU removal code does that but LRU-isolation logic is used
for helping it. When we overwrite page_cgroup->mem_cgroup, page_cgroup is
not on LRU or page_cgroup->mem_cgroup is NULL.)
We can lose file_mapped accounting information at 5 because FILE_MAPPED
is updated only when mapcount changes 0->1. So we should catch it.
BTW, historically, above implemntation comes from migration-failure
of anonymous page. Because we charge both of old page and new page
with mapcount=0, we can't catch
- the page is really freed before remap.
- migration fails but it's freed before remap
or .....corner cases.
New migration sequence with memcg is:
1. allocate a new page.
2. mark PageCgroupMigration to the old page.
3. charge against a new page onto the old page's memcg. (here, new page's pc
is marked as PageCgroupUsed.)
4. page migration replaces radix-tree, page table, etc...
5. At remapping, new page's page_cgroup is now makrked as "USED"
We can catch 0->1 event and FILE_MAPPED will be properly updated.
And we can catch SWAPOUT event after unlock this and freeing this
page by unmap() can be caught.
7. Clear PageCgroupMigration of the old page.
So, FILE_MAPPED will be correctly updated.
Then, for what MIGRATION flag is ?
Without it, at migration failure, we may have to charge old page again
because it may be fully unmapped. "charge" means that we have to dive into
memory reclaim or something complated. So, it's better to avoid
charge it again. Before this patch, __commit_charge() was working for
both of the old/new page and fixed up all. But this technique has some
racy condtion around FILE_MAPPED and SWAPOUT etc...
Now, the kernel use MIGRATION flag and don't uncharge old page until
the end of migration.
I hope this change will make memcg's page migration much simpler. This
page migration has caused several troubles. Worth to add a flag for
simplification.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Reported-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Christoph Lameter <cl@linux-foundation.org>
Cc: "Kirill A. Shutemov" <kirill@shutemov.name>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-05-27 04:42:46 +07:00
|
|
|
enum charge_type ctype;
|
2008-03-05 05:29:09 +07:00
|
|
|
|
2012-01-13 08:18:32 +07:00
|
|
|
*memcgp = NULL;
|
2011-03-24 06:42:19 +07:00
|
|
|
|
2011-01-14 06:46:56 +07:00
|
|
|
VM_BUG_ON(PageTransHuge(page));
|
2009-01-08 09:08:02 +07:00
|
|
|
if (mem_cgroup_disabled())
|
2012-08-01 06:45:25 +07:00
|
|
|
return;
|
2008-04-05 04:29:59 +07:00
|
|
|
|
2008-10-19 10:28:16 +07:00
|
|
|
pc = lookup_page_cgroup(page);
|
|
|
|
lock_page_cgroup(pc);
|
|
|
|
if (PageCgroupUsed(pc)) {
|
2011-11-03 03:38:15 +07:00
|
|
|
memcg = pc->mem_cgroup;
|
|
|
|
css_get(&memcg->css);
|
memcg: fix mis-accounting of file mapped racy with migration
FILE_MAPPED per memcg of migrated file cache is not properly updated,
because our hook in page_add_file_rmap() can't know to which memcg
FILE_MAPPED should be counted.
Basically, this patch is for fixing the bug but includes some big changes
to fix up other messes.
Now, at migrating mapped file, events happen in following sequence.
1. allocate a new page.
2. get memcg of an old page.
3. charge ageinst a new page before migration. But at this point,
no changes to new page's page_cgroup, no commit for the charge.
(IOW, PCG_USED bit is not set.)
4. page migration replaces radix-tree, old-page and new-page.
5. page migration remaps the new page if the old page was mapped.
6. Here, the new page is unlocked.
7. memcg commits the charge for newpage, Mark the new page's page_cgroup
as PCG_USED.
Because "commit" happens after page-remap, we can count FILE_MAPPED
at "5", because we should avoid to trust page_cgroup->mem_cgroup.
if PCG_USED bit is unset.
(Note: memcg's LRU removal code does that but LRU-isolation logic is used
for helping it. When we overwrite page_cgroup->mem_cgroup, page_cgroup is
not on LRU or page_cgroup->mem_cgroup is NULL.)
We can lose file_mapped accounting information at 5 because FILE_MAPPED
is updated only when mapcount changes 0->1. So we should catch it.
BTW, historically, above implemntation comes from migration-failure
of anonymous page. Because we charge both of old page and new page
with mapcount=0, we can't catch
- the page is really freed before remap.
- migration fails but it's freed before remap
or .....corner cases.
New migration sequence with memcg is:
1. allocate a new page.
2. mark PageCgroupMigration to the old page.
3. charge against a new page onto the old page's memcg. (here, new page's pc
is marked as PageCgroupUsed.)
4. page migration replaces radix-tree, page table, etc...
5. At remapping, new page's page_cgroup is now makrked as "USED"
We can catch 0->1 event and FILE_MAPPED will be properly updated.
And we can catch SWAPOUT event after unlock this and freeing this
page by unmap() can be caught.
7. Clear PageCgroupMigration of the old page.
So, FILE_MAPPED will be correctly updated.
Then, for what MIGRATION flag is ?
Without it, at migration failure, we may have to charge old page again
because it may be fully unmapped. "charge" means that we have to dive into
memory reclaim or something complated. So, it's better to avoid
charge it again. Before this patch, __commit_charge() was working for
both of the old/new page and fixed up all. But this technique has some
racy condtion around FILE_MAPPED and SWAPOUT etc...
Now, the kernel use MIGRATION flag and don't uncharge old page until
the end of migration.
I hope this change will make memcg's page migration much simpler. This
page migration has caused several troubles. Worth to add a flag for
simplification.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Reported-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Christoph Lameter <cl@linux-foundation.org>
Cc: "Kirill A. Shutemov" <kirill@shutemov.name>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-05-27 04:42:46 +07:00
|
|
|
/*
|
|
|
|
* At migrating an anonymous page, its mapcount goes down
|
|
|
|
* to 0 and uncharge() will be called. But, even if it's fully
|
|
|
|
* unmapped, migration may fail and this page has to be
|
|
|
|
* charged again. We set MIGRATION flag here and delay uncharge
|
|
|
|
* until end_migration() is called
|
|
|
|
*
|
|
|
|
* Corner Case Thinking
|
|
|
|
* A)
|
|
|
|
* When the old page was mapped as Anon and it's unmap-and-freed
|
|
|
|
* while migration was ongoing.
|
|
|
|
* If unmap finds the old page, uncharge() of it will be delayed
|
|
|
|
* until end_migration(). If unmap finds a new page, it's
|
|
|
|
* uncharged when it make mapcount to be 1->0. If unmap code
|
|
|
|
* finds swap_migration_entry, the new page will not be mapped
|
|
|
|
* and end_migration() will find it(mapcount==0).
|
|
|
|
*
|
|
|
|
* B)
|
|
|
|
* When the old page was mapped but migraion fails, the kernel
|
|
|
|
* remaps it. A charge for it is kept by MIGRATION flag even
|
|
|
|
* if mapcount goes down to 0. We can do remap successfully
|
|
|
|
* without charging it again.
|
|
|
|
*
|
|
|
|
* C)
|
|
|
|
* The "old" page is under lock_page() until the end of
|
|
|
|
* migration, so, the old page itself will not be swapped-out.
|
|
|
|
* If the new page is swapped out before end_migraton, our
|
|
|
|
* hook to usual swap-out path will catch the event.
|
|
|
|
*/
|
|
|
|
if (PageAnon(page))
|
|
|
|
SetPageCgroupMigration(pc);
|
2008-07-25 15:47:10 +07:00
|
|
|
}
|
2008-10-19 10:28:16 +07:00
|
|
|
unlock_page_cgroup(pc);
|
memcg: fix mis-accounting of file mapped racy with migration
FILE_MAPPED per memcg of migrated file cache is not properly updated,
because our hook in page_add_file_rmap() can't know to which memcg
FILE_MAPPED should be counted.
Basically, this patch is for fixing the bug but includes some big changes
to fix up other messes.
Now, at migrating mapped file, events happen in following sequence.
1. allocate a new page.
2. get memcg of an old page.
3. charge ageinst a new page before migration. But at this point,
no changes to new page's page_cgroup, no commit for the charge.
(IOW, PCG_USED bit is not set.)
4. page migration replaces radix-tree, old-page and new-page.
5. page migration remaps the new page if the old page was mapped.
6. Here, the new page is unlocked.
7. memcg commits the charge for newpage, Mark the new page's page_cgroup
as PCG_USED.
Because "commit" happens after page-remap, we can count FILE_MAPPED
at "5", because we should avoid to trust page_cgroup->mem_cgroup.
if PCG_USED bit is unset.
(Note: memcg's LRU removal code does that but LRU-isolation logic is used
for helping it. When we overwrite page_cgroup->mem_cgroup, page_cgroup is
not on LRU or page_cgroup->mem_cgroup is NULL.)
We can lose file_mapped accounting information at 5 because FILE_MAPPED
is updated only when mapcount changes 0->1. So we should catch it.
BTW, historically, above implemntation comes from migration-failure
of anonymous page. Because we charge both of old page and new page
with mapcount=0, we can't catch
- the page is really freed before remap.
- migration fails but it's freed before remap
or .....corner cases.
New migration sequence with memcg is:
1. allocate a new page.
2. mark PageCgroupMigration to the old page.
3. charge against a new page onto the old page's memcg. (here, new page's pc
is marked as PageCgroupUsed.)
4. page migration replaces radix-tree, page table, etc...
5. At remapping, new page's page_cgroup is now makrked as "USED"
We can catch 0->1 event and FILE_MAPPED will be properly updated.
And we can catch SWAPOUT event after unlock this and freeing this
page by unmap() can be caught.
7. Clear PageCgroupMigration of the old page.
So, FILE_MAPPED will be correctly updated.
Then, for what MIGRATION flag is ?
Without it, at migration failure, we may have to charge old page again
because it may be fully unmapped. "charge" means that we have to dive into
memory reclaim or something complated. So, it's better to avoid
charge it again. Before this patch, __commit_charge() was working for
both of the old/new page and fixed up all. But this technique has some
racy condtion around FILE_MAPPED and SWAPOUT etc...
Now, the kernel use MIGRATION flag and don't uncharge old page until
the end of migration.
I hope this change will make memcg's page migration much simpler. This
page migration has caused several troubles. Worth to add a flag for
simplification.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Reported-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Christoph Lameter <cl@linux-foundation.org>
Cc: "Kirill A. Shutemov" <kirill@shutemov.name>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-05-27 04:42:46 +07:00
|
|
|
/*
|
|
|
|
* If the page is not charged at this point,
|
|
|
|
* we return here.
|
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
if (!memcg)
|
2012-08-01 06:45:25 +07:00
|
|
|
return;
|
2009-01-08 09:07:50 +07:00
|
|
|
|
2012-01-13 08:18:32 +07:00
|
|
|
*memcgp = memcg;
|
memcg: fix mis-accounting of file mapped racy with migration
FILE_MAPPED per memcg of migrated file cache is not properly updated,
because our hook in page_add_file_rmap() can't know to which memcg
FILE_MAPPED should be counted.
Basically, this patch is for fixing the bug but includes some big changes
to fix up other messes.
Now, at migrating mapped file, events happen in following sequence.
1. allocate a new page.
2. get memcg of an old page.
3. charge ageinst a new page before migration. But at this point,
no changes to new page's page_cgroup, no commit for the charge.
(IOW, PCG_USED bit is not set.)
4. page migration replaces radix-tree, old-page and new-page.
5. page migration remaps the new page if the old page was mapped.
6. Here, the new page is unlocked.
7. memcg commits the charge for newpage, Mark the new page's page_cgroup
as PCG_USED.
Because "commit" happens after page-remap, we can count FILE_MAPPED
at "5", because we should avoid to trust page_cgroup->mem_cgroup.
if PCG_USED bit is unset.
(Note: memcg's LRU removal code does that but LRU-isolation logic is used
for helping it. When we overwrite page_cgroup->mem_cgroup, page_cgroup is
not on LRU or page_cgroup->mem_cgroup is NULL.)
We can lose file_mapped accounting information at 5 because FILE_MAPPED
is updated only when mapcount changes 0->1. So we should catch it.
BTW, historically, above implemntation comes from migration-failure
of anonymous page. Because we charge both of old page and new page
with mapcount=0, we can't catch
- the page is really freed before remap.
- migration fails but it's freed before remap
or .....corner cases.
New migration sequence with memcg is:
1. allocate a new page.
2. mark PageCgroupMigration to the old page.
3. charge against a new page onto the old page's memcg. (here, new page's pc
is marked as PageCgroupUsed.)
4. page migration replaces radix-tree, page table, etc...
5. At remapping, new page's page_cgroup is now makrked as "USED"
We can catch 0->1 event and FILE_MAPPED will be properly updated.
And we can catch SWAPOUT event after unlock this and freeing this
page by unmap() can be caught.
7. Clear PageCgroupMigration of the old page.
So, FILE_MAPPED will be correctly updated.
Then, for what MIGRATION flag is ?
Without it, at migration failure, we may have to charge old page again
because it may be fully unmapped. "charge" means that we have to dive into
memory reclaim or something complated. So, it's better to avoid
charge it again. Before this patch, __commit_charge() was working for
both of the old/new page and fixed up all. But this technique has some
racy condtion around FILE_MAPPED and SWAPOUT etc...
Now, the kernel use MIGRATION flag and don't uncharge old page until
the end of migration.
I hope this change will make memcg's page migration much simpler. This
page migration has caused several troubles. Worth to add a flag for
simplification.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Reported-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Christoph Lameter <cl@linux-foundation.org>
Cc: "Kirill A. Shutemov" <kirill@shutemov.name>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-05-27 04:42:46 +07:00
|
|
|
/*
|
|
|
|
* We charge new page before it's used/mapped. So, even if unlock_page()
|
|
|
|
* is called before end_migration, we can catch all events on this new
|
|
|
|
* page. In the case new page is migrated but not remapped, new page's
|
|
|
|
* mapcount will be finally 0 and we call uncharge in end_migration().
|
|
|
|
*/
|
|
|
|
if (PageAnon(page))
|
2012-08-01 06:41:40 +07:00
|
|
|
ctype = MEM_CGROUP_CHARGE_TYPE_ANON;
|
memcg: fix mis-accounting of file mapped racy with migration
FILE_MAPPED per memcg of migrated file cache is not properly updated,
because our hook in page_add_file_rmap() can't know to which memcg
FILE_MAPPED should be counted.
Basically, this patch is for fixing the bug but includes some big changes
to fix up other messes.
Now, at migrating mapped file, events happen in following sequence.
1. allocate a new page.
2. get memcg of an old page.
3. charge ageinst a new page before migration. But at this point,
no changes to new page's page_cgroup, no commit for the charge.
(IOW, PCG_USED bit is not set.)
4. page migration replaces radix-tree, old-page and new-page.
5. page migration remaps the new page if the old page was mapped.
6. Here, the new page is unlocked.
7. memcg commits the charge for newpage, Mark the new page's page_cgroup
as PCG_USED.
Because "commit" happens after page-remap, we can count FILE_MAPPED
at "5", because we should avoid to trust page_cgroup->mem_cgroup.
if PCG_USED bit is unset.
(Note: memcg's LRU removal code does that but LRU-isolation logic is used
for helping it. When we overwrite page_cgroup->mem_cgroup, page_cgroup is
not on LRU or page_cgroup->mem_cgroup is NULL.)
We can lose file_mapped accounting information at 5 because FILE_MAPPED
is updated only when mapcount changes 0->1. So we should catch it.
BTW, historically, above implemntation comes from migration-failure
of anonymous page. Because we charge both of old page and new page
with mapcount=0, we can't catch
- the page is really freed before remap.
- migration fails but it's freed before remap
or .....corner cases.
New migration sequence with memcg is:
1. allocate a new page.
2. mark PageCgroupMigration to the old page.
3. charge against a new page onto the old page's memcg. (here, new page's pc
is marked as PageCgroupUsed.)
4. page migration replaces radix-tree, page table, etc...
5. At remapping, new page's page_cgroup is now makrked as "USED"
We can catch 0->1 event and FILE_MAPPED will be properly updated.
And we can catch SWAPOUT event after unlock this and freeing this
page by unmap() can be caught.
7. Clear PageCgroupMigration of the old page.
So, FILE_MAPPED will be correctly updated.
Then, for what MIGRATION flag is ?
Without it, at migration failure, we may have to charge old page again
because it may be fully unmapped. "charge" means that we have to dive into
memory reclaim or something complated. So, it's better to avoid
charge it again. Before this patch, __commit_charge() was working for
both of the old/new page and fixed up all. But this technique has some
racy condtion around FILE_MAPPED and SWAPOUT etc...
Now, the kernel use MIGRATION flag and don't uncharge old page until
the end of migration.
I hope this change will make memcg's page migration much simpler. This
page migration has caused several troubles. Worth to add a flag for
simplification.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Reported-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Christoph Lameter <cl@linux-foundation.org>
Cc: "Kirill A. Shutemov" <kirill@shutemov.name>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-05-27 04:42:46 +07:00
|
|
|
else
|
2012-08-01 06:45:39 +07:00
|
|
|
ctype = MEM_CGROUP_CHARGE_TYPE_CACHE;
|
2012-08-01 06:45:25 +07:00
|
|
|
/*
|
|
|
|
* The page is committed to the memcg, but it's not actually
|
|
|
|
* charged to the res_counter since we plan on replacing the
|
|
|
|
* old one and only one page is going to be left afterwards.
|
|
|
|
*/
|
2012-04-25 01:22:33 +07:00
|
|
|
__mem_cgroup_commit_charge(memcg, newpage, 1, ctype, false);
|
2008-02-07 15:14:10 +07:00
|
|
|
}
|
2008-03-05 05:29:09 +07:00
|
|
|
|
memcg: remove refcnt from page_cgroup
memcg: performance improvements
Patch Description
1/5 ... remove refcnt fron page_cgroup patch (shmem handling is fixed)
2/5 ... swapcache handling patch
3/5 ... add helper function for shmem's memory reclaim patch
4/5 ... optimize by likely/unlikely ppatch
5/5 ... remove redundunt check patch (shmem handling is fixed.)
Unix bench result.
== 2.6.26-rc2-mm1 + memory resource controller
Execl Throughput 2915.4 lps (29.6 secs, 3 samples)
C Compiler Throughput 1019.3 lpm (60.0 secs, 3 samples)
Shell Scripts (1 concurrent) 5796.0 lpm (60.0 secs, 3 samples)
Shell Scripts (8 concurrent) 1097.7 lpm (60.0 secs, 3 samples)
Shell Scripts (16 concurrent) 565.3 lpm (60.0 secs, 3 samples)
File Read 1024 bufsize 2000 maxblocks 1022128.0 KBps (30.0 secs, 3 samples)
File Write 1024 bufsize 2000 maxblocks 544057.0 KBps (30.0 secs, 3 samples)
File Copy 1024 bufsize 2000 maxblocks 346481.0 KBps (30.0 secs, 3 samples)
File Read 256 bufsize 500 maxblocks 319325.0 KBps (30.0 secs, 3 samples)
File Write 256 bufsize 500 maxblocks 148788.0 KBps (30.0 secs, 3 samples)
File Copy 256 bufsize 500 maxblocks 99051.0 KBps (30.0 secs, 3 samples)
File Read 4096 bufsize 8000 maxblocks 2058917.0 KBps (30.0 secs, 3 samples)
File Write 4096 bufsize 8000 maxblocks 1606109.0 KBps (30.0 secs, 3 samples)
File Copy 4096 bufsize 8000 maxblocks 854789.0 KBps (30.0 secs, 3 samples)
Dc: sqrt(2) to 99 decimal places 126145.2 lpm (30.0 secs, 3 samples)
INDEX VALUES
TEST BASELINE RESULT INDEX
Execl Throughput 43.0 2915.4 678.0
File Copy 1024 bufsize 2000 maxblocks 3960.0 346481.0 875.0
File Copy 256 bufsize 500 maxblocks 1655.0 99051.0 598.5
File Copy 4096 bufsize 8000 maxblocks 5800.0 854789.0 1473.8
Shell Scripts (8 concurrent) 6.0 1097.7 1829.5
=========
FINAL SCORE 991.3
== 2.6.26-rc2-mm1 + this set ==
Execl Throughput 3012.9 lps (29.9 secs, 3 samples)
C Compiler Throughput 981.0 lpm (60.0 secs, 3 samples)
Shell Scripts (1 concurrent) 5872.0 lpm (60.0 secs, 3 samples)
Shell Scripts (8 concurrent) 1120.3 lpm (60.0 secs, 3 samples)
Shell Scripts (16 concurrent) 578.0 lpm (60.0 secs, 3 samples)
File Read 1024 bufsize 2000 maxblocks 1003993.0 KBps (30.0 secs, 3 samples)
File Write 1024 bufsize 2000 maxblocks 550452.0 KBps (30.0 secs, 3 samples)
File Copy 1024 bufsize 2000 maxblocks 347159.0 KBps (30.0 secs, 3 samples)
File Read 256 bufsize 500 maxblocks 314644.0 KBps (30.0 secs, 3 samples)
File Write 256 bufsize 500 maxblocks 151852.0 KBps (30.0 secs, 3 samples)
File Copy 256 bufsize 500 maxblocks 101000.0 KBps (30.0 secs, 3 samples)
File Read 4096 bufsize 8000 maxblocks 2033256.0 KBps (30.0 secs, 3 samples)
File Write 4096 bufsize 8000 maxblocks 1611814.0 KBps (30.0 secs, 3 samples)
File Copy 4096 bufsize 8000 maxblocks 847979.0 KBps (30.0 secs, 3 samples)
Dc: sqrt(2) to 99 decimal places 128148.7 lpm (30.0 secs, 3 samples)
INDEX VALUES
TEST BASELINE RESULT INDEX
Execl Throughput 43.0 3012.9 700.7
File Copy 1024 bufsize 2000 maxblocks 3960.0 347159.0 876.7
File Copy 256 bufsize 500 maxblocks 1655.0 101000.0 610.3
File Copy 4096 bufsize 8000 maxblocks 5800.0 847979.0 1462.0
Shell Scripts (8 concurrent) 6.0 1120.3 1867.2
=========
FINAL SCORE 1004.6
This patch:
Remove refcnt from page_cgroup().
After this,
* A page is charged only when !page_mapped() && no page_cgroup is assigned.
* Anon page is newly mapped.
* File page is added to mapping->tree.
* A page is uncharged only when
* Anon page is fully unmapped.
* File page is removed from LRU.
There is no change in behavior from user's view.
This patch also removes unnecessary calls in rmap.c which was used only for
refcnt mangement.
[akpm@linux-foundation.org: fix warning]
[hugh@veritas.com: fix shmem_unuse_inode charging]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Hugh Dickins <hugh@veritas.com>
Cc: YAMAMOTO Takashi <yamamoto@valinux.co.jp>
Cc: Paul Menage <menage@google.com>
Cc: David Rientjes <rientjes@google.com>
Signed-off-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-07-25 15:47:14 +07:00
|
|
|
/* remove redundant charge if migration failed*/
|
2011-11-03 03:38:15 +07:00
|
|
|
void mem_cgroup_end_migration(struct mem_cgroup *memcg,
|
2011-01-14 06:47:43 +07:00
|
|
|
struct page *oldpage, struct page *newpage, bool migration_ok)
|
2008-02-07 15:14:10 +07:00
|
|
|
{
|
memcg: fix mis-accounting of file mapped racy with migration
FILE_MAPPED per memcg of migrated file cache is not properly updated,
because our hook in page_add_file_rmap() can't know to which memcg
FILE_MAPPED should be counted.
Basically, this patch is for fixing the bug but includes some big changes
to fix up other messes.
Now, at migrating mapped file, events happen in following sequence.
1. allocate a new page.
2. get memcg of an old page.
3. charge ageinst a new page before migration. But at this point,
no changes to new page's page_cgroup, no commit for the charge.
(IOW, PCG_USED bit is not set.)
4. page migration replaces radix-tree, old-page and new-page.
5. page migration remaps the new page if the old page was mapped.
6. Here, the new page is unlocked.
7. memcg commits the charge for newpage, Mark the new page's page_cgroup
as PCG_USED.
Because "commit" happens after page-remap, we can count FILE_MAPPED
at "5", because we should avoid to trust page_cgroup->mem_cgroup.
if PCG_USED bit is unset.
(Note: memcg's LRU removal code does that but LRU-isolation logic is used
for helping it. When we overwrite page_cgroup->mem_cgroup, page_cgroup is
not on LRU or page_cgroup->mem_cgroup is NULL.)
We can lose file_mapped accounting information at 5 because FILE_MAPPED
is updated only when mapcount changes 0->1. So we should catch it.
BTW, historically, above implemntation comes from migration-failure
of anonymous page. Because we charge both of old page and new page
with mapcount=0, we can't catch
- the page is really freed before remap.
- migration fails but it's freed before remap
or .....corner cases.
New migration sequence with memcg is:
1. allocate a new page.
2. mark PageCgroupMigration to the old page.
3. charge against a new page onto the old page's memcg. (here, new page's pc
is marked as PageCgroupUsed.)
4. page migration replaces radix-tree, page table, etc...
5. At remapping, new page's page_cgroup is now makrked as "USED"
We can catch 0->1 event and FILE_MAPPED will be properly updated.
And we can catch SWAPOUT event after unlock this and freeing this
page by unmap() can be caught.
7. Clear PageCgroupMigration of the old page.
So, FILE_MAPPED will be correctly updated.
Then, for what MIGRATION flag is ?
Without it, at migration failure, we may have to charge old page again
because it may be fully unmapped. "charge" means that we have to dive into
memory reclaim or something complated. So, it's better to avoid
charge it again. Before this patch, __commit_charge() was working for
both of the old/new page and fixed up all. But this technique has some
racy condtion around FILE_MAPPED and SWAPOUT etc...
Now, the kernel use MIGRATION flag and don't uncharge old page until
the end of migration.
I hope this change will make memcg's page migration much simpler. This
page migration has caused several troubles. Worth to add a flag for
simplification.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Reported-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Christoph Lameter <cl@linux-foundation.org>
Cc: "Kirill A. Shutemov" <kirill@shutemov.name>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-05-27 04:42:46 +07:00
|
|
|
struct page *used, *unused;
|
2009-01-08 09:07:50 +07:00
|
|
|
struct page_cgroup *pc;
|
2012-03-22 06:34:22 +07:00
|
|
|
bool anon;
|
2009-01-08 09:07:50 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
if (!memcg)
|
2009-01-08 09:07:50 +07:00
|
|
|
return;
|
memcg: fix mis-accounting of file mapped racy with migration
FILE_MAPPED per memcg of migrated file cache is not properly updated,
because our hook in page_add_file_rmap() can't know to which memcg
FILE_MAPPED should be counted.
Basically, this patch is for fixing the bug but includes some big changes
to fix up other messes.
Now, at migrating mapped file, events happen in following sequence.
1. allocate a new page.
2. get memcg of an old page.
3. charge ageinst a new page before migration. But at this point,
no changes to new page's page_cgroup, no commit for the charge.
(IOW, PCG_USED bit is not set.)
4. page migration replaces radix-tree, old-page and new-page.
5. page migration remaps the new page if the old page was mapped.
6. Here, the new page is unlocked.
7. memcg commits the charge for newpage, Mark the new page's page_cgroup
as PCG_USED.
Because "commit" happens after page-remap, we can count FILE_MAPPED
at "5", because we should avoid to trust page_cgroup->mem_cgroup.
if PCG_USED bit is unset.
(Note: memcg's LRU removal code does that but LRU-isolation logic is used
for helping it. When we overwrite page_cgroup->mem_cgroup, page_cgroup is
not on LRU or page_cgroup->mem_cgroup is NULL.)
We can lose file_mapped accounting information at 5 because FILE_MAPPED
is updated only when mapcount changes 0->1. So we should catch it.
BTW, historically, above implemntation comes from migration-failure
of anonymous page. Because we charge both of old page and new page
with mapcount=0, we can't catch
- the page is really freed before remap.
- migration fails but it's freed before remap
or .....corner cases.
New migration sequence with memcg is:
1. allocate a new page.
2. mark PageCgroupMigration to the old page.
3. charge against a new page onto the old page's memcg. (here, new page's pc
is marked as PageCgroupUsed.)
4. page migration replaces radix-tree, page table, etc...
5. At remapping, new page's page_cgroup is now makrked as "USED"
We can catch 0->1 event and FILE_MAPPED will be properly updated.
And we can catch SWAPOUT event after unlock this and freeing this
page by unmap() can be caught.
7. Clear PageCgroupMigration of the old page.
So, FILE_MAPPED will be correctly updated.
Then, for what MIGRATION flag is ?
Without it, at migration failure, we may have to charge old page again
because it may be fully unmapped. "charge" means that we have to dive into
memory reclaim or something complated. So, it's better to avoid
charge it again. Before this patch, __commit_charge() was working for
both of the old/new page and fixed up all. But this technique has some
racy condtion around FILE_MAPPED and SWAPOUT etc...
Now, the kernel use MIGRATION flag and don't uncharge old page until
the end of migration.
I hope this change will make memcg's page migration much simpler. This
page migration has caused several troubles. Worth to add a flag for
simplification.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Reported-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Christoph Lameter <cl@linux-foundation.org>
Cc: "Kirill A. Shutemov" <kirill@shutemov.name>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-05-27 04:42:46 +07:00
|
|
|
/* blocks rmdir() */
|
2011-11-03 03:38:15 +07:00
|
|
|
cgroup_exclude_rmdir(&memcg->css);
|
2011-01-14 06:47:43 +07:00
|
|
|
if (!migration_ok) {
|
memcg: fix mis-accounting of file mapped racy with migration
FILE_MAPPED per memcg of migrated file cache is not properly updated,
because our hook in page_add_file_rmap() can't know to which memcg
FILE_MAPPED should be counted.
Basically, this patch is for fixing the bug but includes some big changes
to fix up other messes.
Now, at migrating mapped file, events happen in following sequence.
1. allocate a new page.
2. get memcg of an old page.
3. charge ageinst a new page before migration. But at this point,
no changes to new page's page_cgroup, no commit for the charge.
(IOW, PCG_USED bit is not set.)
4. page migration replaces radix-tree, old-page and new-page.
5. page migration remaps the new page if the old page was mapped.
6. Here, the new page is unlocked.
7. memcg commits the charge for newpage, Mark the new page's page_cgroup
as PCG_USED.
Because "commit" happens after page-remap, we can count FILE_MAPPED
at "5", because we should avoid to trust page_cgroup->mem_cgroup.
if PCG_USED bit is unset.
(Note: memcg's LRU removal code does that but LRU-isolation logic is used
for helping it. When we overwrite page_cgroup->mem_cgroup, page_cgroup is
not on LRU or page_cgroup->mem_cgroup is NULL.)
We can lose file_mapped accounting information at 5 because FILE_MAPPED
is updated only when mapcount changes 0->1. So we should catch it.
BTW, historically, above implemntation comes from migration-failure
of anonymous page. Because we charge both of old page and new page
with mapcount=0, we can't catch
- the page is really freed before remap.
- migration fails but it's freed before remap
or .....corner cases.
New migration sequence with memcg is:
1. allocate a new page.
2. mark PageCgroupMigration to the old page.
3. charge against a new page onto the old page's memcg. (here, new page's pc
is marked as PageCgroupUsed.)
4. page migration replaces radix-tree, page table, etc...
5. At remapping, new page's page_cgroup is now makrked as "USED"
We can catch 0->1 event and FILE_MAPPED will be properly updated.
And we can catch SWAPOUT event after unlock this and freeing this
page by unmap() can be caught.
7. Clear PageCgroupMigration of the old page.
So, FILE_MAPPED will be correctly updated.
Then, for what MIGRATION flag is ?
Without it, at migration failure, we may have to charge old page again
because it may be fully unmapped. "charge" means that we have to dive into
memory reclaim or something complated. So, it's better to avoid
charge it again. Before this patch, __commit_charge() was working for
both of the old/new page and fixed up all. But this technique has some
racy condtion around FILE_MAPPED and SWAPOUT etc...
Now, the kernel use MIGRATION flag and don't uncharge old page until
the end of migration.
I hope this change will make memcg's page migration much simpler. This
page migration has caused several troubles. Worth to add a flag for
simplification.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Reported-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Christoph Lameter <cl@linux-foundation.org>
Cc: "Kirill A. Shutemov" <kirill@shutemov.name>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-05-27 04:42:46 +07:00
|
|
|
used = oldpage;
|
|
|
|
unused = newpage;
|
2009-01-08 09:07:50 +07:00
|
|
|
} else {
|
memcg: fix mis-accounting of file mapped racy with migration
FILE_MAPPED per memcg of migrated file cache is not properly updated,
because our hook in page_add_file_rmap() can't know to which memcg
FILE_MAPPED should be counted.
Basically, this patch is for fixing the bug but includes some big changes
to fix up other messes.
Now, at migrating mapped file, events happen in following sequence.
1. allocate a new page.
2. get memcg of an old page.
3. charge ageinst a new page before migration. But at this point,
no changes to new page's page_cgroup, no commit for the charge.
(IOW, PCG_USED bit is not set.)
4. page migration replaces radix-tree, old-page and new-page.
5. page migration remaps the new page if the old page was mapped.
6. Here, the new page is unlocked.
7. memcg commits the charge for newpage, Mark the new page's page_cgroup
as PCG_USED.
Because "commit" happens after page-remap, we can count FILE_MAPPED
at "5", because we should avoid to trust page_cgroup->mem_cgroup.
if PCG_USED bit is unset.
(Note: memcg's LRU removal code does that but LRU-isolation logic is used
for helping it. When we overwrite page_cgroup->mem_cgroup, page_cgroup is
not on LRU or page_cgroup->mem_cgroup is NULL.)
We can lose file_mapped accounting information at 5 because FILE_MAPPED
is updated only when mapcount changes 0->1. So we should catch it.
BTW, historically, above implemntation comes from migration-failure
of anonymous page. Because we charge both of old page and new page
with mapcount=0, we can't catch
- the page is really freed before remap.
- migration fails but it's freed before remap
or .....corner cases.
New migration sequence with memcg is:
1. allocate a new page.
2. mark PageCgroupMigration to the old page.
3. charge against a new page onto the old page's memcg. (here, new page's pc
is marked as PageCgroupUsed.)
4. page migration replaces radix-tree, page table, etc...
5. At remapping, new page's page_cgroup is now makrked as "USED"
We can catch 0->1 event and FILE_MAPPED will be properly updated.
And we can catch SWAPOUT event after unlock this and freeing this
page by unmap() can be caught.
7. Clear PageCgroupMigration of the old page.
So, FILE_MAPPED will be correctly updated.
Then, for what MIGRATION flag is ?
Without it, at migration failure, we may have to charge old page again
because it may be fully unmapped. "charge" means that we have to dive into
memory reclaim or something complated. So, it's better to avoid
charge it again. Before this patch, __commit_charge() was working for
both of the old/new page and fixed up all. But this technique has some
racy condtion around FILE_MAPPED and SWAPOUT etc...
Now, the kernel use MIGRATION flag and don't uncharge old page until
the end of migration.
I hope this change will make memcg's page migration much simpler. This
page migration has caused several troubles. Worth to add a flag for
simplification.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Reported-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Christoph Lameter <cl@linux-foundation.org>
Cc: "Kirill A. Shutemov" <kirill@shutemov.name>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-05-27 04:42:46 +07:00
|
|
|
used = newpage;
|
2009-01-08 09:07:50 +07:00
|
|
|
unused = oldpage;
|
|
|
|
}
|
2012-08-01 06:45:25 +07:00
|
|
|
anon = PageAnon(used);
|
2012-08-01 06:45:34 +07:00
|
|
|
__mem_cgroup_uncharge_common(unused,
|
|
|
|
anon ? MEM_CGROUP_CHARGE_TYPE_ANON
|
|
|
|
: MEM_CGROUP_CHARGE_TYPE_CACHE,
|
|
|
|
true);
|
2012-08-01 06:45:25 +07:00
|
|
|
css_put(&memcg->css);
|
memcg: remove refcnt from page_cgroup
memcg: performance improvements
Patch Description
1/5 ... remove refcnt fron page_cgroup patch (shmem handling is fixed)
2/5 ... swapcache handling patch
3/5 ... add helper function for shmem's memory reclaim patch
4/5 ... optimize by likely/unlikely ppatch
5/5 ... remove redundunt check patch (shmem handling is fixed.)
Unix bench result.
== 2.6.26-rc2-mm1 + memory resource controller
Execl Throughput 2915.4 lps (29.6 secs, 3 samples)
C Compiler Throughput 1019.3 lpm (60.0 secs, 3 samples)
Shell Scripts (1 concurrent) 5796.0 lpm (60.0 secs, 3 samples)
Shell Scripts (8 concurrent) 1097.7 lpm (60.0 secs, 3 samples)
Shell Scripts (16 concurrent) 565.3 lpm (60.0 secs, 3 samples)
File Read 1024 bufsize 2000 maxblocks 1022128.0 KBps (30.0 secs, 3 samples)
File Write 1024 bufsize 2000 maxblocks 544057.0 KBps (30.0 secs, 3 samples)
File Copy 1024 bufsize 2000 maxblocks 346481.0 KBps (30.0 secs, 3 samples)
File Read 256 bufsize 500 maxblocks 319325.0 KBps (30.0 secs, 3 samples)
File Write 256 bufsize 500 maxblocks 148788.0 KBps (30.0 secs, 3 samples)
File Copy 256 bufsize 500 maxblocks 99051.0 KBps (30.0 secs, 3 samples)
File Read 4096 bufsize 8000 maxblocks 2058917.0 KBps (30.0 secs, 3 samples)
File Write 4096 bufsize 8000 maxblocks 1606109.0 KBps (30.0 secs, 3 samples)
File Copy 4096 bufsize 8000 maxblocks 854789.0 KBps (30.0 secs, 3 samples)
Dc: sqrt(2) to 99 decimal places 126145.2 lpm (30.0 secs, 3 samples)
INDEX VALUES
TEST BASELINE RESULT INDEX
Execl Throughput 43.0 2915.4 678.0
File Copy 1024 bufsize 2000 maxblocks 3960.0 346481.0 875.0
File Copy 256 bufsize 500 maxblocks 1655.0 99051.0 598.5
File Copy 4096 bufsize 8000 maxblocks 5800.0 854789.0 1473.8
Shell Scripts (8 concurrent) 6.0 1097.7 1829.5
=========
FINAL SCORE 991.3
== 2.6.26-rc2-mm1 + this set ==
Execl Throughput 3012.9 lps (29.9 secs, 3 samples)
C Compiler Throughput 981.0 lpm (60.0 secs, 3 samples)
Shell Scripts (1 concurrent) 5872.0 lpm (60.0 secs, 3 samples)
Shell Scripts (8 concurrent) 1120.3 lpm (60.0 secs, 3 samples)
Shell Scripts (16 concurrent) 578.0 lpm (60.0 secs, 3 samples)
File Read 1024 bufsize 2000 maxblocks 1003993.0 KBps (30.0 secs, 3 samples)
File Write 1024 bufsize 2000 maxblocks 550452.0 KBps (30.0 secs, 3 samples)
File Copy 1024 bufsize 2000 maxblocks 347159.0 KBps (30.0 secs, 3 samples)
File Read 256 bufsize 500 maxblocks 314644.0 KBps (30.0 secs, 3 samples)
File Write 256 bufsize 500 maxblocks 151852.0 KBps (30.0 secs, 3 samples)
File Copy 256 bufsize 500 maxblocks 101000.0 KBps (30.0 secs, 3 samples)
File Read 4096 bufsize 8000 maxblocks 2033256.0 KBps (30.0 secs, 3 samples)
File Write 4096 bufsize 8000 maxblocks 1611814.0 KBps (30.0 secs, 3 samples)
File Copy 4096 bufsize 8000 maxblocks 847979.0 KBps (30.0 secs, 3 samples)
Dc: sqrt(2) to 99 decimal places 128148.7 lpm (30.0 secs, 3 samples)
INDEX VALUES
TEST BASELINE RESULT INDEX
Execl Throughput 43.0 3012.9 700.7
File Copy 1024 bufsize 2000 maxblocks 3960.0 347159.0 876.7
File Copy 256 bufsize 500 maxblocks 1655.0 101000.0 610.3
File Copy 4096 bufsize 8000 maxblocks 5800.0 847979.0 1462.0
Shell Scripts (8 concurrent) 6.0 1120.3 1867.2
=========
FINAL SCORE 1004.6
This patch:
Remove refcnt from page_cgroup().
After this,
* A page is charged only when !page_mapped() && no page_cgroup is assigned.
* Anon page is newly mapped.
* File page is added to mapping->tree.
* A page is uncharged only when
* Anon page is fully unmapped.
* File page is removed from LRU.
There is no change in behavior from user's view.
This patch also removes unnecessary calls in rmap.c which was used only for
refcnt mangement.
[akpm@linux-foundation.org: fix warning]
[hugh@veritas.com: fix shmem_unuse_inode charging]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Hugh Dickins <hugh@veritas.com>
Cc: YAMAMOTO Takashi <yamamoto@valinux.co.jp>
Cc: Paul Menage <menage@google.com>
Cc: David Rientjes <rientjes@google.com>
Signed-off-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-07-25 15:47:14 +07:00
|
|
|
/*
|
memcg: fix mis-accounting of file mapped racy with migration
FILE_MAPPED per memcg of migrated file cache is not properly updated,
because our hook in page_add_file_rmap() can't know to which memcg
FILE_MAPPED should be counted.
Basically, this patch is for fixing the bug but includes some big changes
to fix up other messes.
Now, at migrating mapped file, events happen in following sequence.
1. allocate a new page.
2. get memcg of an old page.
3. charge ageinst a new page before migration. But at this point,
no changes to new page's page_cgroup, no commit for the charge.
(IOW, PCG_USED bit is not set.)
4. page migration replaces radix-tree, old-page and new-page.
5. page migration remaps the new page if the old page was mapped.
6. Here, the new page is unlocked.
7. memcg commits the charge for newpage, Mark the new page's page_cgroup
as PCG_USED.
Because "commit" happens after page-remap, we can count FILE_MAPPED
at "5", because we should avoid to trust page_cgroup->mem_cgroup.
if PCG_USED bit is unset.
(Note: memcg's LRU removal code does that but LRU-isolation logic is used
for helping it. When we overwrite page_cgroup->mem_cgroup, page_cgroup is
not on LRU or page_cgroup->mem_cgroup is NULL.)
We can lose file_mapped accounting information at 5 because FILE_MAPPED
is updated only when mapcount changes 0->1. So we should catch it.
BTW, historically, above implemntation comes from migration-failure
of anonymous page. Because we charge both of old page and new page
with mapcount=0, we can't catch
- the page is really freed before remap.
- migration fails but it's freed before remap
or .....corner cases.
New migration sequence with memcg is:
1. allocate a new page.
2. mark PageCgroupMigration to the old page.
3. charge against a new page onto the old page's memcg. (here, new page's pc
is marked as PageCgroupUsed.)
4. page migration replaces radix-tree, page table, etc...
5. At remapping, new page's page_cgroup is now makrked as "USED"
We can catch 0->1 event and FILE_MAPPED will be properly updated.
And we can catch SWAPOUT event after unlock this and freeing this
page by unmap() can be caught.
7. Clear PageCgroupMigration of the old page.
So, FILE_MAPPED will be correctly updated.
Then, for what MIGRATION flag is ?
Without it, at migration failure, we may have to charge old page again
because it may be fully unmapped. "charge" means that we have to dive into
memory reclaim or something complated. So, it's better to avoid
charge it again. Before this patch, __commit_charge() was working for
both of the old/new page and fixed up all. But this technique has some
racy condtion around FILE_MAPPED and SWAPOUT etc...
Now, the kernel use MIGRATION flag and don't uncharge old page until
the end of migration.
I hope this change will make memcg's page migration much simpler. This
page migration has caused several troubles. Worth to add a flag for
simplification.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Reported-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Christoph Lameter <cl@linux-foundation.org>
Cc: "Kirill A. Shutemov" <kirill@shutemov.name>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-05-27 04:42:46 +07:00
|
|
|
* We disallowed uncharge of pages under migration because mapcount
|
|
|
|
* of the page goes down to zero, temporarly.
|
|
|
|
* Clear the flag and check the page should be charged.
|
2009-01-08 09:07:50 +07:00
|
|
|
*/
|
memcg: fix mis-accounting of file mapped racy with migration
FILE_MAPPED per memcg of migrated file cache is not properly updated,
because our hook in page_add_file_rmap() can't know to which memcg
FILE_MAPPED should be counted.
Basically, this patch is for fixing the bug but includes some big changes
to fix up other messes.
Now, at migrating mapped file, events happen in following sequence.
1. allocate a new page.
2. get memcg of an old page.
3. charge ageinst a new page before migration. But at this point,
no changes to new page's page_cgroup, no commit for the charge.
(IOW, PCG_USED bit is not set.)
4. page migration replaces radix-tree, old-page and new-page.
5. page migration remaps the new page if the old page was mapped.
6. Here, the new page is unlocked.
7. memcg commits the charge for newpage, Mark the new page's page_cgroup
as PCG_USED.
Because "commit" happens after page-remap, we can count FILE_MAPPED
at "5", because we should avoid to trust page_cgroup->mem_cgroup.
if PCG_USED bit is unset.
(Note: memcg's LRU removal code does that but LRU-isolation logic is used
for helping it. When we overwrite page_cgroup->mem_cgroup, page_cgroup is
not on LRU or page_cgroup->mem_cgroup is NULL.)
We can lose file_mapped accounting information at 5 because FILE_MAPPED
is updated only when mapcount changes 0->1. So we should catch it.
BTW, historically, above implemntation comes from migration-failure
of anonymous page. Because we charge both of old page and new page
with mapcount=0, we can't catch
- the page is really freed before remap.
- migration fails but it's freed before remap
or .....corner cases.
New migration sequence with memcg is:
1. allocate a new page.
2. mark PageCgroupMigration to the old page.
3. charge against a new page onto the old page's memcg. (here, new page's pc
is marked as PageCgroupUsed.)
4. page migration replaces radix-tree, page table, etc...
5. At remapping, new page's page_cgroup is now makrked as "USED"
We can catch 0->1 event and FILE_MAPPED will be properly updated.
And we can catch SWAPOUT event after unlock this and freeing this
page by unmap() can be caught.
7. Clear PageCgroupMigration of the old page.
So, FILE_MAPPED will be correctly updated.
Then, for what MIGRATION flag is ?
Without it, at migration failure, we may have to charge old page again
because it may be fully unmapped. "charge" means that we have to dive into
memory reclaim or something complated. So, it's better to avoid
charge it again. Before this patch, __commit_charge() was working for
both of the old/new page and fixed up all. But this technique has some
racy condtion around FILE_MAPPED and SWAPOUT etc...
Now, the kernel use MIGRATION flag and don't uncharge old page until
the end of migration.
I hope this change will make memcg's page migration much simpler. This
page migration has caused several troubles. Worth to add a flag for
simplification.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Reported-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Christoph Lameter <cl@linux-foundation.org>
Cc: "Kirill A. Shutemov" <kirill@shutemov.name>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-05-27 04:42:46 +07:00
|
|
|
pc = lookup_page_cgroup(oldpage);
|
|
|
|
lock_page_cgroup(pc);
|
|
|
|
ClearPageCgroupMigration(pc);
|
|
|
|
unlock_page_cgroup(pc);
|
|
|
|
|
2009-01-08 09:07:50 +07:00
|
|
|
/*
|
memcg: fix mis-accounting of file mapped racy with migration
FILE_MAPPED per memcg of migrated file cache is not properly updated,
because our hook in page_add_file_rmap() can't know to which memcg
FILE_MAPPED should be counted.
Basically, this patch is for fixing the bug but includes some big changes
to fix up other messes.
Now, at migrating mapped file, events happen in following sequence.
1. allocate a new page.
2. get memcg of an old page.
3. charge ageinst a new page before migration. But at this point,
no changes to new page's page_cgroup, no commit for the charge.
(IOW, PCG_USED bit is not set.)
4. page migration replaces radix-tree, old-page and new-page.
5. page migration remaps the new page if the old page was mapped.
6. Here, the new page is unlocked.
7. memcg commits the charge for newpage, Mark the new page's page_cgroup
as PCG_USED.
Because "commit" happens after page-remap, we can count FILE_MAPPED
at "5", because we should avoid to trust page_cgroup->mem_cgroup.
if PCG_USED bit is unset.
(Note: memcg's LRU removal code does that but LRU-isolation logic is used
for helping it. When we overwrite page_cgroup->mem_cgroup, page_cgroup is
not on LRU or page_cgroup->mem_cgroup is NULL.)
We can lose file_mapped accounting information at 5 because FILE_MAPPED
is updated only when mapcount changes 0->1. So we should catch it.
BTW, historically, above implemntation comes from migration-failure
of anonymous page. Because we charge both of old page and new page
with mapcount=0, we can't catch
- the page is really freed before remap.
- migration fails but it's freed before remap
or .....corner cases.
New migration sequence with memcg is:
1. allocate a new page.
2. mark PageCgroupMigration to the old page.
3. charge against a new page onto the old page's memcg. (here, new page's pc
is marked as PageCgroupUsed.)
4. page migration replaces radix-tree, page table, etc...
5. At remapping, new page's page_cgroup is now makrked as "USED"
We can catch 0->1 event and FILE_MAPPED will be properly updated.
And we can catch SWAPOUT event after unlock this and freeing this
page by unmap() can be caught.
7. Clear PageCgroupMigration of the old page.
So, FILE_MAPPED will be correctly updated.
Then, for what MIGRATION flag is ?
Without it, at migration failure, we may have to charge old page again
because it may be fully unmapped. "charge" means that we have to dive into
memory reclaim or something complated. So, it's better to avoid
charge it again. Before this patch, __commit_charge() was working for
both of the old/new page and fixed up all. But this technique has some
racy condtion around FILE_MAPPED and SWAPOUT etc...
Now, the kernel use MIGRATION flag and don't uncharge old page until
the end of migration.
I hope this change will make memcg's page migration much simpler. This
page migration has caused several troubles. Worth to add a flag for
simplification.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Reported-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Christoph Lameter <cl@linux-foundation.org>
Cc: "Kirill A. Shutemov" <kirill@shutemov.name>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-05-27 04:42:46 +07:00
|
|
|
* If a page is a file cache, radix-tree replacement is very atomic
|
|
|
|
* and we can skip this check. When it was an Anon page, its mapcount
|
|
|
|
* goes down to 0. But because we added MIGRATION flage, it's not
|
|
|
|
* uncharged yet. There are several case but page->mapcount check
|
|
|
|
* and USED bit check in mem_cgroup_uncharge_page() will do enough
|
|
|
|
* check. (see prepare_charge() also)
|
memcg: remove refcnt from page_cgroup
memcg: performance improvements
Patch Description
1/5 ... remove refcnt fron page_cgroup patch (shmem handling is fixed)
2/5 ... swapcache handling patch
3/5 ... add helper function for shmem's memory reclaim patch
4/5 ... optimize by likely/unlikely ppatch
5/5 ... remove redundunt check patch (shmem handling is fixed.)
Unix bench result.
== 2.6.26-rc2-mm1 + memory resource controller
Execl Throughput 2915.4 lps (29.6 secs, 3 samples)
C Compiler Throughput 1019.3 lpm (60.0 secs, 3 samples)
Shell Scripts (1 concurrent) 5796.0 lpm (60.0 secs, 3 samples)
Shell Scripts (8 concurrent) 1097.7 lpm (60.0 secs, 3 samples)
Shell Scripts (16 concurrent) 565.3 lpm (60.0 secs, 3 samples)
File Read 1024 bufsize 2000 maxblocks 1022128.0 KBps (30.0 secs, 3 samples)
File Write 1024 bufsize 2000 maxblocks 544057.0 KBps (30.0 secs, 3 samples)
File Copy 1024 bufsize 2000 maxblocks 346481.0 KBps (30.0 secs, 3 samples)
File Read 256 bufsize 500 maxblocks 319325.0 KBps (30.0 secs, 3 samples)
File Write 256 bufsize 500 maxblocks 148788.0 KBps (30.0 secs, 3 samples)
File Copy 256 bufsize 500 maxblocks 99051.0 KBps (30.0 secs, 3 samples)
File Read 4096 bufsize 8000 maxblocks 2058917.0 KBps (30.0 secs, 3 samples)
File Write 4096 bufsize 8000 maxblocks 1606109.0 KBps (30.0 secs, 3 samples)
File Copy 4096 bufsize 8000 maxblocks 854789.0 KBps (30.0 secs, 3 samples)
Dc: sqrt(2) to 99 decimal places 126145.2 lpm (30.0 secs, 3 samples)
INDEX VALUES
TEST BASELINE RESULT INDEX
Execl Throughput 43.0 2915.4 678.0
File Copy 1024 bufsize 2000 maxblocks 3960.0 346481.0 875.0
File Copy 256 bufsize 500 maxblocks 1655.0 99051.0 598.5
File Copy 4096 bufsize 8000 maxblocks 5800.0 854789.0 1473.8
Shell Scripts (8 concurrent) 6.0 1097.7 1829.5
=========
FINAL SCORE 991.3
== 2.6.26-rc2-mm1 + this set ==
Execl Throughput 3012.9 lps (29.9 secs, 3 samples)
C Compiler Throughput 981.0 lpm (60.0 secs, 3 samples)
Shell Scripts (1 concurrent) 5872.0 lpm (60.0 secs, 3 samples)
Shell Scripts (8 concurrent) 1120.3 lpm (60.0 secs, 3 samples)
Shell Scripts (16 concurrent) 578.0 lpm (60.0 secs, 3 samples)
File Read 1024 bufsize 2000 maxblocks 1003993.0 KBps (30.0 secs, 3 samples)
File Write 1024 bufsize 2000 maxblocks 550452.0 KBps (30.0 secs, 3 samples)
File Copy 1024 bufsize 2000 maxblocks 347159.0 KBps (30.0 secs, 3 samples)
File Read 256 bufsize 500 maxblocks 314644.0 KBps (30.0 secs, 3 samples)
File Write 256 bufsize 500 maxblocks 151852.0 KBps (30.0 secs, 3 samples)
File Copy 256 bufsize 500 maxblocks 101000.0 KBps (30.0 secs, 3 samples)
File Read 4096 bufsize 8000 maxblocks 2033256.0 KBps (30.0 secs, 3 samples)
File Write 4096 bufsize 8000 maxblocks 1611814.0 KBps (30.0 secs, 3 samples)
File Copy 4096 bufsize 8000 maxblocks 847979.0 KBps (30.0 secs, 3 samples)
Dc: sqrt(2) to 99 decimal places 128148.7 lpm (30.0 secs, 3 samples)
INDEX VALUES
TEST BASELINE RESULT INDEX
Execl Throughput 43.0 3012.9 700.7
File Copy 1024 bufsize 2000 maxblocks 3960.0 347159.0 876.7
File Copy 256 bufsize 500 maxblocks 1655.0 101000.0 610.3
File Copy 4096 bufsize 8000 maxblocks 5800.0 847979.0 1462.0
Shell Scripts (8 concurrent) 6.0 1120.3 1867.2
=========
FINAL SCORE 1004.6
This patch:
Remove refcnt from page_cgroup().
After this,
* A page is charged only when !page_mapped() && no page_cgroup is assigned.
* Anon page is newly mapped.
* File page is added to mapping->tree.
* A page is uncharged only when
* Anon page is fully unmapped.
* File page is removed from LRU.
There is no change in behavior from user's view.
This patch also removes unnecessary calls in rmap.c which was used only for
refcnt mangement.
[akpm@linux-foundation.org: fix warning]
[hugh@veritas.com: fix shmem_unuse_inode charging]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Hugh Dickins <hugh@veritas.com>
Cc: YAMAMOTO Takashi <yamamoto@valinux.co.jp>
Cc: Paul Menage <menage@google.com>
Cc: David Rientjes <rientjes@google.com>
Signed-off-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-07-25 15:47:14 +07:00
|
|
|
*/
|
2012-03-22 06:34:22 +07:00
|
|
|
if (anon)
|
memcg: fix mis-accounting of file mapped racy with migration
FILE_MAPPED per memcg of migrated file cache is not properly updated,
because our hook in page_add_file_rmap() can't know to which memcg
FILE_MAPPED should be counted.
Basically, this patch is for fixing the bug but includes some big changes
to fix up other messes.
Now, at migrating mapped file, events happen in following sequence.
1. allocate a new page.
2. get memcg of an old page.
3. charge ageinst a new page before migration. But at this point,
no changes to new page's page_cgroup, no commit for the charge.
(IOW, PCG_USED bit is not set.)
4. page migration replaces radix-tree, old-page and new-page.
5. page migration remaps the new page if the old page was mapped.
6. Here, the new page is unlocked.
7. memcg commits the charge for newpage, Mark the new page's page_cgroup
as PCG_USED.
Because "commit" happens after page-remap, we can count FILE_MAPPED
at "5", because we should avoid to trust page_cgroup->mem_cgroup.
if PCG_USED bit is unset.
(Note: memcg's LRU removal code does that but LRU-isolation logic is used
for helping it. When we overwrite page_cgroup->mem_cgroup, page_cgroup is
not on LRU or page_cgroup->mem_cgroup is NULL.)
We can lose file_mapped accounting information at 5 because FILE_MAPPED
is updated only when mapcount changes 0->1. So we should catch it.
BTW, historically, above implemntation comes from migration-failure
of anonymous page. Because we charge both of old page and new page
with mapcount=0, we can't catch
- the page is really freed before remap.
- migration fails but it's freed before remap
or .....corner cases.
New migration sequence with memcg is:
1. allocate a new page.
2. mark PageCgroupMigration to the old page.
3. charge against a new page onto the old page's memcg. (here, new page's pc
is marked as PageCgroupUsed.)
4. page migration replaces radix-tree, page table, etc...
5. At remapping, new page's page_cgroup is now makrked as "USED"
We can catch 0->1 event and FILE_MAPPED will be properly updated.
And we can catch SWAPOUT event after unlock this and freeing this
page by unmap() can be caught.
7. Clear PageCgroupMigration of the old page.
So, FILE_MAPPED will be correctly updated.
Then, for what MIGRATION flag is ?
Without it, at migration failure, we may have to charge old page again
because it may be fully unmapped. "charge" means that we have to dive into
memory reclaim or something complated. So, it's better to avoid
charge it again. Before this patch, __commit_charge() was working for
both of the old/new page and fixed up all. But this technique has some
racy condtion around FILE_MAPPED and SWAPOUT etc...
Now, the kernel use MIGRATION flag and don't uncharge old page until
the end of migration.
I hope this change will make memcg's page migration much simpler. This
page migration has caused several troubles. Worth to add a flag for
simplification.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Reported-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Christoph Lameter <cl@linux-foundation.org>
Cc: "Kirill A. Shutemov" <kirill@shutemov.name>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-05-27 04:42:46 +07:00
|
|
|
mem_cgroup_uncharge_page(used);
|
2009-07-30 05:04:06 +07:00
|
|
|
/*
|
memcg: fix mis-accounting of file mapped racy with migration
FILE_MAPPED per memcg of migrated file cache is not properly updated,
because our hook in page_add_file_rmap() can't know to which memcg
FILE_MAPPED should be counted.
Basically, this patch is for fixing the bug but includes some big changes
to fix up other messes.
Now, at migrating mapped file, events happen in following sequence.
1. allocate a new page.
2. get memcg of an old page.
3. charge ageinst a new page before migration. But at this point,
no changes to new page's page_cgroup, no commit for the charge.
(IOW, PCG_USED bit is not set.)
4. page migration replaces radix-tree, old-page and new-page.
5. page migration remaps the new page if the old page was mapped.
6. Here, the new page is unlocked.
7. memcg commits the charge for newpage, Mark the new page's page_cgroup
as PCG_USED.
Because "commit" happens after page-remap, we can count FILE_MAPPED
at "5", because we should avoid to trust page_cgroup->mem_cgroup.
if PCG_USED bit is unset.
(Note: memcg's LRU removal code does that but LRU-isolation logic is used
for helping it. When we overwrite page_cgroup->mem_cgroup, page_cgroup is
not on LRU or page_cgroup->mem_cgroup is NULL.)
We can lose file_mapped accounting information at 5 because FILE_MAPPED
is updated only when mapcount changes 0->1. So we should catch it.
BTW, historically, above implemntation comes from migration-failure
of anonymous page. Because we charge both of old page and new page
with mapcount=0, we can't catch
- the page is really freed before remap.
- migration fails but it's freed before remap
or .....corner cases.
New migration sequence with memcg is:
1. allocate a new page.
2. mark PageCgroupMigration to the old page.
3. charge against a new page onto the old page's memcg. (here, new page's pc
is marked as PageCgroupUsed.)
4. page migration replaces radix-tree, page table, etc...
5. At remapping, new page's page_cgroup is now makrked as "USED"
We can catch 0->1 event and FILE_MAPPED will be properly updated.
And we can catch SWAPOUT event after unlock this and freeing this
page by unmap() can be caught.
7. Clear PageCgroupMigration of the old page.
So, FILE_MAPPED will be correctly updated.
Then, for what MIGRATION flag is ?
Without it, at migration failure, we may have to charge old page again
because it may be fully unmapped. "charge" means that we have to dive into
memory reclaim or something complated. So, it's better to avoid
charge it again. Before this patch, __commit_charge() was working for
both of the old/new page and fixed up all. But this technique has some
racy condtion around FILE_MAPPED and SWAPOUT etc...
Now, the kernel use MIGRATION flag and don't uncharge old page until
the end of migration.
I hope this change will make memcg's page migration much simpler. This
page migration has caused several troubles. Worth to add a flag for
simplification.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Reported-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Christoph Lameter <cl@linux-foundation.org>
Cc: "Kirill A. Shutemov" <kirill@shutemov.name>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-05-27 04:42:46 +07:00
|
|
|
* At migration, we may charge account against cgroup which has no
|
|
|
|
* tasks.
|
2009-07-30 05:04:06 +07:00
|
|
|
* So, rmdir()->pre_destroy() can be called while we do this charge.
|
|
|
|
* In that case, we need to call pre_destroy() again. check it here.
|
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
cgroup_release_and_wakeup_rmdir(&memcg->css);
|
2008-02-07 15:14:10 +07:00
|
|
|
}
|
2008-02-07 15:13:51 +07:00
|
|
|
|
memcg: add mem_cgroup_replace_page_cache() to fix LRU issue
Commit ef6a3c6311 ("mm: add replace_page_cache_page() function") added a
function replace_page_cache_page(). This function replaces a page in the
radix-tree with a new page. WHen doing this, memory cgroup needs to fix
up the accounting information. memcg need to check PCG_USED bit etc.
In some(many?) cases, 'newpage' is on LRU before calling
replace_page_cache(). So, memcg's LRU accounting information should be
fixed, too.
This patch adds mem_cgroup_replace_page_cache() and removes the old hooks.
In that function, old pages will be unaccounted without touching
res_counter and new page will be accounted to the memcg (of old page).
WHen overwriting pc->mem_cgroup of newpage, take zone->lru_lock and avoid
races with LRU handling.
Background:
replace_page_cache_page() is called by FUSE code in its splice() handling.
Here, 'newpage' is replacing oldpage but this newpage is not a newly allocated
page and may be on LRU. LRU mis-accounting will be critical for memory cgroup
because rmdir() checks the whole LRU is empty and there is no account leak.
If a page is on the other LRU than it should be, rmdir() will fail.
This bug was added in March 2011, but no bug report yet. I guess there
are not many people who use memcg and FUSE at the same time with upstream
kernels.
The result of this bug is that admin cannot destroy a memcg because of
account leak. So, no panic, no deadlock. And, even if an active cgroup
exist, umount can succseed. So no problem at shutdown.
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Acked-by: Michal Hocko <mhocko@suse.cz>
Cc: Miklos Szeredi <mszeredi@suse.cz>
Cc: Hugh Dickins <hughd@google.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:44 +07:00
|
|
|
/*
|
|
|
|
* At replace page cache, newpage is not under any memcg but it's on
|
|
|
|
* LRU. So, this function doesn't touch res_counter but handles LRU
|
|
|
|
* in correct way. Both pages are locked so we cannot race with uncharge.
|
|
|
|
*/
|
|
|
|
void mem_cgroup_replace_page_cache(struct page *oldpage,
|
|
|
|
struct page *newpage)
|
|
|
|
{
|
shmem: replace page if mapping excludes its zone
The GMA500 GPU driver uses GEM shmem objects, but with a new twist: the
backing RAM has to be below 4GB. Not a problem while the boards
supported only 4GB: but now Intel's D2700MUD boards support 8GB, and
their GMA3600 is managed by the GMA500 driver.
shmem/tmpfs has never pretended to support hardware restrictions on the
backing memory, but it might have appeared to do so before v3.1, and
even now it works fine until a page is swapped out then back in. When
read_cache_page_gfp() supplied a freshly allocated page for copy, that
compensated for whatever choice might have been made by earlier swapin
readahead; but swapoff was likely to destroy the illusion.
We'd like to continue to support GMA500, so now add a new
shmem_should_replace_page() check on the zone when about to move a page
from swapcache to filecache (in swapin and swapoff cases), with
shmem_replace_page() to allocate and substitute a suitable page (given
gma500/gem.c's mapping_set_gfp_mask GFP_KERNEL | __GFP_DMA32).
This does involve a minor extension to mem_cgroup_replace_page_cache()
(the page may or may not have already been charged); and I've removed a
comment and call to mem_cgroup_uncharge_cache_page(), which in fact is
always a no-op while PageSwapCache.
Also removed optimization of an unlikely path in shmem_getpage_gfp(),
now that we need to check PageSwapCache more carefully (a racing caller
might already have made the copy). And at one point shmem_unuse_inode()
needs to use the hitherto private page_swapcount(), to guard against
racing with inode eviction.
It would make sense to extend shmem_should_replace_page(), to cover
cpuset and NUMA mempolicy restrictions too, but set that aside for now:
needs a cleanup of shmem mempolicy handling, and more testing, and ought
to handle swap faults in do_swap_page() as well as shmem.
Signed-off-by: Hugh Dickins <hughd@google.com>
Cc: Christoph Hellwig <hch@infradead.org>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Stephane Marchesin <marcheu@chromium.org>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: Dave Airlie <airlied@gmail.com>
Cc: Daniel Vetter <daniel@ffwll.ch>
Cc: Rob Clark <rob.clark@linaro.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-05-30 05:06:38 +07:00
|
|
|
struct mem_cgroup *memcg = NULL;
|
memcg: add mem_cgroup_replace_page_cache() to fix LRU issue
Commit ef6a3c6311 ("mm: add replace_page_cache_page() function") added a
function replace_page_cache_page(). This function replaces a page in the
radix-tree with a new page. WHen doing this, memory cgroup needs to fix
up the accounting information. memcg need to check PCG_USED bit etc.
In some(many?) cases, 'newpage' is on LRU before calling
replace_page_cache(). So, memcg's LRU accounting information should be
fixed, too.
This patch adds mem_cgroup_replace_page_cache() and removes the old hooks.
In that function, old pages will be unaccounted without touching
res_counter and new page will be accounted to the memcg (of old page).
WHen overwriting pc->mem_cgroup of newpage, take zone->lru_lock and avoid
races with LRU handling.
Background:
replace_page_cache_page() is called by FUSE code in its splice() handling.
Here, 'newpage' is replacing oldpage but this newpage is not a newly allocated
page and may be on LRU. LRU mis-accounting will be critical for memory cgroup
because rmdir() checks the whole LRU is empty and there is no account leak.
If a page is on the other LRU than it should be, rmdir() will fail.
This bug was added in March 2011, but no bug report yet. I guess there
are not many people who use memcg and FUSE at the same time with upstream
kernels.
The result of this bug is that admin cannot destroy a memcg because of
account leak. So, no panic, no deadlock. And, even if an active cgroup
exist, umount can succseed. So no problem at shutdown.
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Acked-by: Michal Hocko <mhocko@suse.cz>
Cc: Miklos Szeredi <mszeredi@suse.cz>
Cc: Hugh Dickins <hughd@google.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:44 +07:00
|
|
|
struct page_cgroup *pc;
|
|
|
|
enum charge_type type = MEM_CGROUP_CHARGE_TYPE_CACHE;
|
|
|
|
|
|
|
|
if (mem_cgroup_disabled())
|
|
|
|
return;
|
|
|
|
|
|
|
|
pc = lookup_page_cgroup(oldpage);
|
|
|
|
/* fix accounting on old pages */
|
|
|
|
lock_page_cgroup(pc);
|
shmem: replace page if mapping excludes its zone
The GMA500 GPU driver uses GEM shmem objects, but with a new twist: the
backing RAM has to be below 4GB. Not a problem while the boards
supported only 4GB: but now Intel's D2700MUD boards support 8GB, and
their GMA3600 is managed by the GMA500 driver.
shmem/tmpfs has never pretended to support hardware restrictions on the
backing memory, but it might have appeared to do so before v3.1, and
even now it works fine until a page is swapped out then back in. When
read_cache_page_gfp() supplied a freshly allocated page for copy, that
compensated for whatever choice might have been made by earlier swapin
readahead; but swapoff was likely to destroy the illusion.
We'd like to continue to support GMA500, so now add a new
shmem_should_replace_page() check on the zone when about to move a page
from swapcache to filecache (in swapin and swapoff cases), with
shmem_replace_page() to allocate and substitute a suitable page (given
gma500/gem.c's mapping_set_gfp_mask GFP_KERNEL | __GFP_DMA32).
This does involve a minor extension to mem_cgroup_replace_page_cache()
(the page may or may not have already been charged); and I've removed a
comment and call to mem_cgroup_uncharge_cache_page(), which in fact is
always a no-op while PageSwapCache.
Also removed optimization of an unlikely path in shmem_getpage_gfp(),
now that we need to check PageSwapCache more carefully (a racing caller
might already have made the copy). And at one point shmem_unuse_inode()
needs to use the hitherto private page_swapcount(), to guard against
racing with inode eviction.
It would make sense to extend shmem_should_replace_page(), to cover
cpuset and NUMA mempolicy restrictions too, but set that aside for now:
needs a cleanup of shmem mempolicy handling, and more testing, and ought
to handle swap faults in do_swap_page() as well as shmem.
Signed-off-by: Hugh Dickins <hughd@google.com>
Cc: Christoph Hellwig <hch@infradead.org>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Stephane Marchesin <marcheu@chromium.org>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: Dave Airlie <airlied@gmail.com>
Cc: Daniel Vetter <daniel@ffwll.ch>
Cc: Rob Clark <rob.clark@linaro.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-05-30 05:06:38 +07:00
|
|
|
if (PageCgroupUsed(pc)) {
|
|
|
|
memcg = pc->mem_cgroup;
|
|
|
|
mem_cgroup_charge_statistics(memcg, false, -1);
|
|
|
|
ClearPageCgroupUsed(pc);
|
|
|
|
}
|
memcg: add mem_cgroup_replace_page_cache() to fix LRU issue
Commit ef6a3c6311 ("mm: add replace_page_cache_page() function") added a
function replace_page_cache_page(). This function replaces a page in the
radix-tree with a new page. WHen doing this, memory cgroup needs to fix
up the accounting information. memcg need to check PCG_USED bit etc.
In some(many?) cases, 'newpage' is on LRU before calling
replace_page_cache(). So, memcg's LRU accounting information should be
fixed, too.
This patch adds mem_cgroup_replace_page_cache() and removes the old hooks.
In that function, old pages will be unaccounted without touching
res_counter and new page will be accounted to the memcg (of old page).
WHen overwriting pc->mem_cgroup of newpage, take zone->lru_lock and avoid
races with LRU handling.
Background:
replace_page_cache_page() is called by FUSE code in its splice() handling.
Here, 'newpage' is replacing oldpage but this newpage is not a newly allocated
page and may be on LRU. LRU mis-accounting will be critical for memory cgroup
because rmdir() checks the whole LRU is empty and there is no account leak.
If a page is on the other LRU than it should be, rmdir() will fail.
This bug was added in March 2011, but no bug report yet. I guess there
are not many people who use memcg and FUSE at the same time with upstream
kernels.
The result of this bug is that admin cannot destroy a memcg because of
account leak. So, no panic, no deadlock. And, even if an active cgroup
exist, umount can succseed. So no problem at shutdown.
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Acked-by: Michal Hocko <mhocko@suse.cz>
Cc: Miklos Szeredi <mszeredi@suse.cz>
Cc: Hugh Dickins <hughd@google.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:44 +07:00
|
|
|
unlock_page_cgroup(pc);
|
|
|
|
|
shmem: replace page if mapping excludes its zone
The GMA500 GPU driver uses GEM shmem objects, but with a new twist: the
backing RAM has to be below 4GB. Not a problem while the boards
supported only 4GB: but now Intel's D2700MUD boards support 8GB, and
their GMA3600 is managed by the GMA500 driver.
shmem/tmpfs has never pretended to support hardware restrictions on the
backing memory, but it might have appeared to do so before v3.1, and
even now it works fine until a page is swapped out then back in. When
read_cache_page_gfp() supplied a freshly allocated page for copy, that
compensated for whatever choice might have been made by earlier swapin
readahead; but swapoff was likely to destroy the illusion.
We'd like to continue to support GMA500, so now add a new
shmem_should_replace_page() check on the zone when about to move a page
from swapcache to filecache (in swapin and swapoff cases), with
shmem_replace_page() to allocate and substitute a suitable page (given
gma500/gem.c's mapping_set_gfp_mask GFP_KERNEL | __GFP_DMA32).
This does involve a minor extension to mem_cgroup_replace_page_cache()
(the page may or may not have already been charged); and I've removed a
comment and call to mem_cgroup_uncharge_cache_page(), which in fact is
always a no-op while PageSwapCache.
Also removed optimization of an unlikely path in shmem_getpage_gfp(),
now that we need to check PageSwapCache more carefully (a racing caller
might already have made the copy). And at one point shmem_unuse_inode()
needs to use the hitherto private page_swapcount(), to guard against
racing with inode eviction.
It would make sense to extend shmem_should_replace_page(), to cover
cpuset and NUMA mempolicy restrictions too, but set that aside for now:
needs a cleanup of shmem mempolicy handling, and more testing, and ought
to handle swap faults in do_swap_page() as well as shmem.
Signed-off-by: Hugh Dickins <hughd@google.com>
Cc: Christoph Hellwig <hch@infradead.org>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Stephane Marchesin <marcheu@chromium.org>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: Dave Airlie <airlied@gmail.com>
Cc: Daniel Vetter <daniel@ffwll.ch>
Cc: Rob Clark <rob.clark@linaro.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-05-30 05:06:38 +07:00
|
|
|
/*
|
|
|
|
* When called from shmem_replace_page(), in some cases the
|
|
|
|
* oldpage has already been charged, and in some cases not.
|
|
|
|
*/
|
|
|
|
if (!memcg)
|
|
|
|
return;
|
memcg: add mem_cgroup_replace_page_cache() to fix LRU issue
Commit ef6a3c6311 ("mm: add replace_page_cache_page() function") added a
function replace_page_cache_page(). This function replaces a page in the
radix-tree with a new page. WHen doing this, memory cgroup needs to fix
up the accounting information. memcg need to check PCG_USED bit etc.
In some(many?) cases, 'newpage' is on LRU before calling
replace_page_cache(). So, memcg's LRU accounting information should be
fixed, too.
This patch adds mem_cgroup_replace_page_cache() and removes the old hooks.
In that function, old pages will be unaccounted without touching
res_counter and new page will be accounted to the memcg (of old page).
WHen overwriting pc->mem_cgroup of newpage, take zone->lru_lock and avoid
races with LRU handling.
Background:
replace_page_cache_page() is called by FUSE code in its splice() handling.
Here, 'newpage' is replacing oldpage but this newpage is not a newly allocated
page and may be on LRU. LRU mis-accounting will be critical for memory cgroup
because rmdir() checks the whole LRU is empty and there is no account leak.
If a page is on the other LRU than it should be, rmdir() will fail.
This bug was added in March 2011, but no bug report yet. I guess there
are not many people who use memcg and FUSE at the same time with upstream
kernels.
The result of this bug is that admin cannot destroy a memcg because of
account leak. So, no panic, no deadlock. And, even if an active cgroup
exist, umount can succseed. So no problem at shutdown.
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Acked-by: Michal Hocko <mhocko@suse.cz>
Cc: Miklos Szeredi <mszeredi@suse.cz>
Cc: Hugh Dickins <hughd@google.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:44 +07:00
|
|
|
/*
|
|
|
|
* Even if newpage->mapping was NULL before starting replacement,
|
|
|
|
* the newpage may be on LRU(or pagevec for LRU) already. We lock
|
|
|
|
* LRU while we overwrite pc->mem_cgroup.
|
|
|
|
*/
|
2012-04-25 01:22:33 +07:00
|
|
|
__mem_cgroup_commit_charge(memcg, newpage, 1, type, true);
|
memcg: add mem_cgroup_replace_page_cache() to fix LRU issue
Commit ef6a3c6311 ("mm: add replace_page_cache_page() function") added a
function replace_page_cache_page(). This function replaces a page in the
radix-tree with a new page. WHen doing this, memory cgroup needs to fix
up the accounting information. memcg need to check PCG_USED bit etc.
In some(many?) cases, 'newpage' is on LRU before calling
replace_page_cache(). So, memcg's LRU accounting information should be
fixed, too.
This patch adds mem_cgroup_replace_page_cache() and removes the old hooks.
In that function, old pages will be unaccounted without touching
res_counter and new page will be accounted to the memcg (of old page).
WHen overwriting pc->mem_cgroup of newpage, take zone->lru_lock and avoid
races with LRU handling.
Background:
replace_page_cache_page() is called by FUSE code in its splice() handling.
Here, 'newpage' is replacing oldpage but this newpage is not a newly allocated
page and may be on LRU. LRU mis-accounting will be critical for memory cgroup
because rmdir() checks the whole LRU is empty and there is no account leak.
If a page is on the other LRU than it should be, rmdir() will fail.
This bug was added in March 2011, but no bug report yet. I guess there
are not many people who use memcg and FUSE at the same time with upstream
kernels.
The result of this bug is that admin cannot destroy a memcg because of
account leak. So, no panic, no deadlock. And, even if an active cgroup
exist, umount can succseed. So no problem at shutdown.
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Acked-by: Michal Hocko <mhocko@suse.cz>
Cc: Miklos Szeredi <mszeredi@suse.cz>
Cc: Hugh Dickins <hughd@google.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-01-13 08:17:44 +07:00
|
|
|
}
|
|
|
|
|
2011-03-24 06:42:25 +07:00
|
|
|
#ifdef CONFIG_DEBUG_VM
|
|
|
|
static struct page_cgroup *lookup_page_cgroup_used(struct page *page)
|
|
|
|
{
|
|
|
|
struct page_cgroup *pc;
|
|
|
|
|
|
|
|
pc = lookup_page_cgroup(page);
|
2012-01-13 08:18:38 +07:00
|
|
|
/*
|
|
|
|
* Can be NULL while feeding pages into the page allocator for
|
|
|
|
* the first time, i.e. during boot or memory hotplug;
|
|
|
|
* or when mem_cgroup_disabled().
|
|
|
|
*/
|
2011-03-24 06:42:25 +07:00
|
|
|
if (likely(pc) && PageCgroupUsed(pc))
|
|
|
|
return pc;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool mem_cgroup_bad_page_check(struct page *page)
|
|
|
|
{
|
|
|
|
if (mem_cgroup_disabled())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return lookup_page_cgroup_used(page) != NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void mem_cgroup_print_bad_page(struct page *page)
|
|
|
|
{
|
|
|
|
struct page_cgroup *pc;
|
|
|
|
|
|
|
|
pc = lookup_page_cgroup_used(page);
|
|
|
|
if (pc) {
|
2012-01-13 08:19:54 +07:00
|
|
|
printk(KERN_ALERT "pc:%p pc->flags:%lx pc->mem_cgroup:%p\n",
|
2011-03-24 06:42:25 +07:00
|
|
|
pc, pc->flags, pc->mem_cgroup);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-01-08 09:08:00 +07:00
|
|
|
static DEFINE_MUTEX(set_limit_mutex);
|
|
|
|
|
2009-01-07 05:39:44 +07:00
|
|
|
static int mem_cgroup_resize_limit(struct mem_cgroup *memcg,
|
2009-01-08 09:08:00 +07:00
|
|
|
unsigned long long val)
|
2008-07-25 15:47:20 +07:00
|
|
|
{
|
2009-04-03 06:57:36 +07:00
|
|
|
int retry_count;
|
2010-05-27 04:42:37 +07:00
|
|
|
u64 memswlimit, memlimit;
|
2008-07-25 15:47:20 +07:00
|
|
|
int ret = 0;
|
2009-04-03 06:57:36 +07:00
|
|
|
int children = mem_cgroup_count_children(memcg);
|
|
|
|
u64 curusage, oldusage;
|
2010-05-27 04:42:37 +07:00
|
|
|
int enlarge;
|
2009-04-03 06:57:36 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* For keeping hierarchical_reclaim simple, how long we should retry
|
|
|
|
* is depends on callers. We set our retry-count to be function
|
|
|
|
* of # of children which we should visit in this loop.
|
|
|
|
*/
|
|
|
|
retry_count = MEM_CGROUP_RECLAIM_RETRIES * children;
|
|
|
|
|
|
|
|
oldusage = res_counter_read_u64(&memcg->res, RES_USAGE);
|
2008-07-25 15:47:20 +07:00
|
|
|
|
2010-05-27 04:42:37 +07:00
|
|
|
enlarge = 0;
|
2009-01-08 09:08:00 +07:00
|
|
|
while (retry_count) {
|
2008-07-25 15:47:20 +07:00
|
|
|
if (signal_pending(current)) {
|
|
|
|
ret = -EINTR;
|
|
|
|
break;
|
|
|
|
}
|
2009-01-08 09:08:00 +07:00
|
|
|
/*
|
|
|
|
* Rather than hide all in some function, I do this in
|
|
|
|
* open coded manner. You see what this really does.
|
2012-08-01 06:43:23 +07:00
|
|
|
* We have to guarantee memcg->res.limit <= memcg->memsw.limit.
|
2009-01-08 09:08:00 +07:00
|
|
|
*/
|
|
|
|
mutex_lock(&set_limit_mutex);
|
|
|
|
memswlimit = res_counter_read_u64(&memcg->memsw, RES_LIMIT);
|
|
|
|
if (memswlimit < val) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
mutex_unlock(&set_limit_mutex);
|
2008-07-25 15:47:20 +07:00
|
|
|
break;
|
|
|
|
}
|
2010-05-27 04:42:37 +07:00
|
|
|
|
|
|
|
memlimit = res_counter_read_u64(&memcg->res, RES_LIMIT);
|
|
|
|
if (memlimit < val)
|
|
|
|
enlarge = 1;
|
|
|
|
|
2009-01-08 09:08:00 +07:00
|
|
|
ret = res_counter_set_limit(&memcg->res, val);
|
2009-06-18 06:27:19 +07:00
|
|
|
if (!ret) {
|
|
|
|
if (memswlimit == val)
|
|
|
|
memcg->memsw_is_minimum = true;
|
|
|
|
else
|
|
|
|
memcg->memsw_is_minimum = false;
|
|
|
|
}
|
2009-01-08 09:08:00 +07:00
|
|
|
mutex_unlock(&set_limit_mutex);
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
break;
|
|
|
|
|
2012-01-13 08:17:59 +07:00
|
|
|
mem_cgroup_reclaim(memcg, GFP_KERNEL,
|
|
|
|
MEM_CGROUP_RECLAIM_SHRINK);
|
2009-04-03 06:57:36 +07:00
|
|
|
curusage = res_counter_read_u64(&memcg->res, RES_USAGE);
|
|
|
|
/* Usage is reduced ? */
|
|
|
|
if (curusage >= oldusage)
|
|
|
|
retry_count--;
|
|
|
|
else
|
|
|
|
oldusage = curusage;
|
2009-01-08 09:08:00 +07:00
|
|
|
}
|
2010-05-27 04:42:37 +07:00
|
|
|
if (!ret && enlarge)
|
|
|
|
memcg_oom_recover(memcg);
|
2009-01-08 09:08:18 +07:00
|
|
|
|
2009-01-08 09:08:00 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-06-18 06:27:15 +07:00
|
|
|
static int mem_cgroup_resize_memsw_limit(struct mem_cgroup *memcg,
|
|
|
|
unsigned long long val)
|
2009-01-08 09:08:00 +07:00
|
|
|
{
|
2009-04-03 06:57:36 +07:00
|
|
|
int retry_count;
|
2010-05-27 04:42:37 +07:00
|
|
|
u64 memlimit, memswlimit, oldusage, curusage;
|
2009-04-03 06:57:36 +07:00
|
|
|
int children = mem_cgroup_count_children(memcg);
|
|
|
|
int ret = -EBUSY;
|
2010-05-27 04:42:37 +07:00
|
|
|
int enlarge = 0;
|
2009-01-08 09:08:00 +07:00
|
|
|
|
2009-04-03 06:57:36 +07:00
|
|
|
/* see mem_cgroup_resize_res_limit */
|
|
|
|
retry_count = children * MEM_CGROUP_RECLAIM_RETRIES;
|
|
|
|
oldusage = res_counter_read_u64(&memcg->memsw, RES_USAGE);
|
2009-01-08 09:08:00 +07:00
|
|
|
while (retry_count) {
|
|
|
|
if (signal_pending(current)) {
|
|
|
|
ret = -EINTR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Rather than hide all in some function, I do this in
|
|
|
|
* open coded manner. You see what this really does.
|
2012-08-01 06:43:23 +07:00
|
|
|
* We have to guarantee memcg->res.limit <= memcg->memsw.limit.
|
2009-01-08 09:08:00 +07:00
|
|
|
*/
|
|
|
|
mutex_lock(&set_limit_mutex);
|
|
|
|
memlimit = res_counter_read_u64(&memcg->res, RES_LIMIT);
|
|
|
|
if (memlimit > val) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
mutex_unlock(&set_limit_mutex);
|
|
|
|
break;
|
|
|
|
}
|
2010-05-27 04:42:37 +07:00
|
|
|
memswlimit = res_counter_read_u64(&memcg->memsw, RES_LIMIT);
|
|
|
|
if (memswlimit < val)
|
|
|
|
enlarge = 1;
|
2009-01-08 09:08:00 +07:00
|
|
|
ret = res_counter_set_limit(&memcg->memsw, val);
|
2009-06-18 06:27:19 +07:00
|
|
|
if (!ret) {
|
|
|
|
if (memlimit == val)
|
|
|
|
memcg->memsw_is_minimum = true;
|
|
|
|
else
|
|
|
|
memcg->memsw_is_minimum = false;
|
|
|
|
}
|
2009-01-08 09:08:00 +07:00
|
|
|
mutex_unlock(&set_limit_mutex);
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
break;
|
|
|
|
|
2012-01-13 08:17:59 +07:00
|
|
|
mem_cgroup_reclaim(memcg, GFP_KERNEL,
|
|
|
|
MEM_CGROUP_RECLAIM_NOSWAP |
|
|
|
|
MEM_CGROUP_RECLAIM_SHRINK);
|
2009-01-08 09:08:00 +07:00
|
|
|
curusage = res_counter_read_u64(&memcg->memsw, RES_USAGE);
|
2009-04-03 06:57:36 +07:00
|
|
|
/* Usage is reduced ? */
|
2009-01-08 09:08:00 +07:00
|
|
|
if (curusage >= oldusage)
|
2008-07-25 15:47:20 +07:00
|
|
|
retry_count--;
|
2009-04-03 06:57:36 +07:00
|
|
|
else
|
|
|
|
oldusage = curusage;
|
2008-07-25 15:47:20 +07:00
|
|
|
}
|
2010-05-27 04:42:37 +07:00
|
|
|
if (!ret && enlarge)
|
|
|
|
memcg_oom_recover(memcg);
|
2008-07-25 15:47:20 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2009-09-24 05:56:39 +07:00
|
|
|
unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order,
|
2011-05-27 06:25:25 +07:00
|
|
|
gfp_t gfp_mask,
|
|
|
|
unsigned long *total_scanned)
|
2009-09-24 05:56:39 +07:00
|
|
|
{
|
|
|
|
unsigned long nr_reclaimed = 0;
|
|
|
|
struct mem_cgroup_per_zone *mz, *next_mz = NULL;
|
|
|
|
unsigned long reclaimed;
|
|
|
|
int loop = 0;
|
|
|
|
struct mem_cgroup_tree_per_zone *mctz;
|
2009-10-02 05:44:12 +07:00
|
|
|
unsigned long long excess;
|
2011-05-27 06:25:25 +07:00
|
|
|
unsigned long nr_scanned;
|
2009-09-24 05:56:39 +07:00
|
|
|
|
|
|
|
if (order > 0)
|
|
|
|
return 0;
|
|
|
|
|
2010-08-11 08:03:05 +07:00
|
|
|
mctz = soft_limit_tree_node_zone(zone_to_nid(zone), zone_idx(zone));
|
2009-09-24 05:56:39 +07:00
|
|
|
/*
|
|
|
|
* This loop can run a while, specially if mem_cgroup's continuously
|
|
|
|
* keep exceeding their soft limit and putting the system under
|
|
|
|
* pressure
|
|
|
|
*/
|
|
|
|
do {
|
|
|
|
if (next_mz)
|
|
|
|
mz = next_mz;
|
|
|
|
else
|
|
|
|
mz = mem_cgroup_largest_soft_limit_node(mctz);
|
|
|
|
if (!mz)
|
|
|
|
break;
|
|
|
|
|
2011-05-27 06:25:25 +07:00
|
|
|
nr_scanned = 0;
|
2012-03-22 06:34:18 +07:00
|
|
|
reclaimed = mem_cgroup_soft_reclaim(mz->memcg, zone,
|
2012-01-13 08:17:59 +07:00
|
|
|
gfp_mask, &nr_scanned);
|
2009-09-24 05:56:39 +07:00
|
|
|
nr_reclaimed += reclaimed;
|
2011-05-27 06:25:25 +07:00
|
|
|
*total_scanned += nr_scanned;
|
2009-09-24 05:56:39 +07:00
|
|
|
spin_lock(&mctz->lock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we failed to reclaim anything from this memory cgroup
|
|
|
|
* it is time to move on to the next cgroup
|
|
|
|
*/
|
|
|
|
next_mz = NULL;
|
|
|
|
if (!reclaimed) {
|
|
|
|
do {
|
|
|
|
/*
|
|
|
|
* Loop until we find yet another one.
|
|
|
|
*
|
|
|
|
* By the time we get the soft_limit lock
|
|
|
|
* again, someone might have aded the
|
|
|
|
* group back on the RB tree. Iterate to
|
|
|
|
* make sure we get a different mem.
|
|
|
|
* mem_cgroup_largest_soft_limit_node returns
|
|
|
|
* NULL if no other cgroup is present on
|
|
|
|
* the tree
|
|
|
|
*/
|
|
|
|
next_mz =
|
|
|
|
__mem_cgroup_largest_soft_limit_node(mctz);
|
2011-05-27 06:25:28 +07:00
|
|
|
if (next_mz == mz)
|
2012-03-22 06:34:18 +07:00
|
|
|
css_put(&next_mz->memcg->css);
|
2011-05-27 06:25:28 +07:00
|
|
|
else /* next_mz == NULL or other memcg */
|
2009-09-24 05:56:39 +07:00
|
|
|
break;
|
|
|
|
} while (1);
|
|
|
|
}
|
2012-03-22 06:34:18 +07:00
|
|
|
__mem_cgroup_remove_exceeded(mz->memcg, mz, mctz);
|
|
|
|
excess = res_counter_soft_limit_excess(&mz->memcg->res);
|
2009-09-24 05:56:39 +07:00
|
|
|
/*
|
|
|
|
* One school of thought says that we should not add
|
|
|
|
* back the node to the tree if reclaim returns 0.
|
|
|
|
* But our reclaim could return 0, simply because due
|
|
|
|
* to priority we are exposing a smaller subset of
|
|
|
|
* memory to reclaim from. Consider this as a longer
|
|
|
|
* term TODO.
|
|
|
|
*/
|
2009-10-02 05:44:12 +07:00
|
|
|
/* If excess == 0, no tree ops */
|
2012-03-22 06:34:18 +07:00
|
|
|
__mem_cgroup_insert_exceeded(mz->memcg, mz, mctz, excess);
|
2009-09-24 05:56:39 +07:00
|
|
|
spin_unlock(&mctz->lock);
|
2012-03-22 06:34:18 +07:00
|
|
|
css_put(&mz->memcg->css);
|
2009-09-24 05:56:39 +07:00
|
|
|
loop++;
|
|
|
|
/*
|
|
|
|
* Could not reclaim anything and there are no more
|
|
|
|
* mem cgroups to try or we seem to be looping without
|
|
|
|
* reclaiming anything.
|
|
|
|
*/
|
|
|
|
if (!nr_reclaimed &&
|
|
|
|
(next_mz == NULL ||
|
|
|
|
loop > MEM_CGROUP_MAX_SOFT_LIMIT_RECLAIM_LOOPS))
|
|
|
|
break;
|
|
|
|
} while (!nr_reclaimed);
|
|
|
|
if (next_mz)
|
2012-03-22 06:34:18 +07:00
|
|
|
css_put(&next_mz->memcg->css);
|
2009-09-24 05:56:39 +07:00
|
|
|
return nr_reclaimed;
|
|
|
|
}
|
|
|
|
|
2008-02-07 15:14:16 +07:00
|
|
|
/*
|
2012-08-01 06:42:46 +07:00
|
|
|
* Traverse a specified page_cgroup list and try to drop them all. This doesn't
|
|
|
|
* reclaim the pages page themselves - it just removes the page_cgroups.
|
|
|
|
* Returns true if some page_cgroups were not freed, indicating that the caller
|
|
|
|
* must retry this operation.
|
2008-02-07 15:14:16 +07:00
|
|
|
*/
|
2012-08-01 06:42:46 +07:00
|
|
|
static bool mem_cgroup_force_empty_list(struct mem_cgroup *memcg,
|
memcg: synchronized LRU
A big patch for changing memcg's LRU semantics.
Now,
- page_cgroup is linked to mem_cgroup's its own LRU (per zone).
- LRU of page_cgroup is not synchronous with global LRU.
- page and page_cgroup is one-to-one and statically allocated.
- To find page_cgroup is on what LRU, you have to check pc->mem_cgroup as
- lru = page_cgroup_zoneinfo(pc, nid_of_pc, zid_of_pc);
- SwapCache is handled.
And, when we handle LRU list of page_cgroup, we do following.
pc = lookup_page_cgroup(page);
lock_page_cgroup(pc); .....................(1)
mz = page_cgroup_zoneinfo(pc);
spin_lock(&mz->lru_lock);
.....add to LRU
spin_unlock(&mz->lru_lock);
unlock_page_cgroup(pc);
But (1) is spin_lock and we have to be afraid of dead-lock with zone->lru_lock.
So, trylock() is used at (1), now. Without (1), we can't trust "mz" is correct.
This is a trial to remove this dirty nesting of locks.
This patch changes mz->lru_lock to be zone->lru_lock.
Then, above sequence will be written as
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
mem_cgroup_add/remove/etc_lru() {
pc = lookup_page_cgroup(page);
mz = page_cgroup_zoneinfo(pc);
if (PageCgroupUsed(pc)) {
....add to LRU
}
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
This is much simpler.
(*) We're safe even if we don't take lock_page_cgroup(pc). Because..
1. When pc->mem_cgroup can be modified.
- at charge.
- at account_move().
2. at charge
the PCG_USED bit is not set before pc->mem_cgroup is fixed.
3. at account_move()
the page is isolated and not on LRU.
Pros.
- easy for maintenance.
- memcg can make use of laziness of pagevec.
- we don't have to duplicated LRU/Active/Unevictable bit in page_cgroup.
- LRU status of memcg will be synchronized with global LRU's one.
- # of locks are reduced.
- account_move() is simplified very much.
Cons.
- may increase cost of LRU rotation.
(no impact if memcg is not configured.)
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:08:01 +07:00
|
|
|
int node, int zid, enum lru_list lru)
|
2008-02-07 15:14:16 +07:00
|
|
|
{
|
memcg: fix hotplugged memory zone oops
When MEMCG is configured on (even when it's disabled by boot option),
when adding or removing a page to/from its lru list, the zone pointer
used for stats updates is nowadays taken from the struct lruvec. (On
many configurations, calculating zone from page is slower.)
But we have no code to update all the lruvecs (per zone, per memcg) when
a memory node is hotadded. Here's an extract from the oops which
results when running numactl to bind a program to a newly onlined node:
BUG: unable to handle kernel NULL pointer dereference at 0000000000000f60
IP: __mod_zone_page_state+0x9/0x60
Pid: 1219, comm: numactl Not tainted 3.6.0-rc5+ #180 Bochs Bochs
Process numactl (pid: 1219, threadinfo ffff880039abc000, task ffff8800383c4ce0)
Call Trace:
__pagevec_lru_add_fn+0xdf/0x140
pagevec_lru_move_fn+0xb1/0x100
__pagevec_lru_add+0x1c/0x30
lru_add_drain_cpu+0xa3/0x130
lru_add_drain+0x2f/0x40
...
The natural solution might be to use a memcg callback whenever memory is
hotadded; but that solution has not been scoped out, and it happens that
we do have an easy location at which to update lruvec->zone. The lruvec
pointer is discovered either by mem_cgroup_zone_lruvec() or by
mem_cgroup_page_lruvec(), and both of those do know the right zone.
So check and set lruvec->zone in those; and remove the inadequate
attempt to set lruvec->zone from lruvec_init(), which is called before
NODE_DATA(node) has been allocated in such cases.
Ah, there was one exceptionr. For no particularly good reason,
mem_cgroup_force_empty_list() has its own code for deciding lruvec.
Change it to use the standard mem_cgroup_zone_lruvec() and
mem_cgroup_get_lru_size() too. In fact it was already safe against such
an oops (the lru lists in danger could only be empty), but we're better
proofed against future changes this way.
I've marked this for stable (3.6) since we introduced the problem in 3.5
(now closed to stable); but I have no idea if this is the only fix
needed to get memory hotadd working with memcg in 3.6, and received no
answer when I enquired twice before.
Reported-by: Tang Chen <tangchen@cn.fujitsu.com>
Signed-off-by: Hugh Dickins <hughd@google.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Konstantin Khlebnikov <khlebnikov@openvz.org>
Cc: Wen Congyang <wency@cn.fujitsu.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-11-17 05:14:54 +07:00
|
|
|
struct lruvec *lruvec;
|
memcg: synchronized LRU
A big patch for changing memcg's LRU semantics.
Now,
- page_cgroup is linked to mem_cgroup's its own LRU (per zone).
- LRU of page_cgroup is not synchronous with global LRU.
- page and page_cgroup is one-to-one and statically allocated.
- To find page_cgroup is on what LRU, you have to check pc->mem_cgroup as
- lru = page_cgroup_zoneinfo(pc, nid_of_pc, zid_of_pc);
- SwapCache is handled.
And, when we handle LRU list of page_cgroup, we do following.
pc = lookup_page_cgroup(page);
lock_page_cgroup(pc); .....................(1)
mz = page_cgroup_zoneinfo(pc);
spin_lock(&mz->lru_lock);
.....add to LRU
spin_unlock(&mz->lru_lock);
unlock_page_cgroup(pc);
But (1) is spin_lock and we have to be afraid of dead-lock with zone->lru_lock.
So, trylock() is used at (1), now. Without (1), we can't trust "mz" is correct.
This is a trial to remove this dirty nesting of locks.
This patch changes mz->lru_lock to be zone->lru_lock.
Then, above sequence will be written as
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
mem_cgroup_add/remove/etc_lru() {
pc = lookup_page_cgroup(page);
mz = page_cgroup_zoneinfo(pc);
if (PageCgroupUsed(pc)) {
....add to LRU
}
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
This is much simpler.
(*) We're safe even if we don't take lock_page_cgroup(pc). Because..
1. When pc->mem_cgroup can be modified.
- at charge.
- at account_move().
2. at charge
the PCG_USED bit is not set before pc->mem_cgroup is fixed.
3. at account_move()
the page is isolated and not on LRU.
Pros.
- easy for maintenance.
- memcg can make use of laziness of pagevec.
- we don't have to duplicated LRU/Active/Unevictable bit in page_cgroup.
- LRU status of memcg will be synchronized with global LRU's one.
- # of locks are reduced.
- account_move() is simplified very much.
Cons.
- may increase cost of LRU rotation.
(no impact if memcg is not configured.)
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:08:01 +07:00
|
|
|
unsigned long flags, loop;
|
2008-02-07 15:14:39 +07:00
|
|
|
struct list_head *list;
|
2012-01-13 08:18:15 +07:00
|
|
|
struct page *busy;
|
|
|
|
struct zone *zone;
|
2008-02-07 15:14:39 +07:00
|
|
|
|
memcg: synchronized LRU
A big patch for changing memcg's LRU semantics.
Now,
- page_cgroup is linked to mem_cgroup's its own LRU (per zone).
- LRU of page_cgroup is not synchronous with global LRU.
- page and page_cgroup is one-to-one and statically allocated.
- To find page_cgroup is on what LRU, you have to check pc->mem_cgroup as
- lru = page_cgroup_zoneinfo(pc, nid_of_pc, zid_of_pc);
- SwapCache is handled.
And, when we handle LRU list of page_cgroup, we do following.
pc = lookup_page_cgroup(page);
lock_page_cgroup(pc); .....................(1)
mz = page_cgroup_zoneinfo(pc);
spin_lock(&mz->lru_lock);
.....add to LRU
spin_unlock(&mz->lru_lock);
unlock_page_cgroup(pc);
But (1) is spin_lock and we have to be afraid of dead-lock with zone->lru_lock.
So, trylock() is used at (1), now. Without (1), we can't trust "mz" is correct.
This is a trial to remove this dirty nesting of locks.
This patch changes mz->lru_lock to be zone->lru_lock.
Then, above sequence will be written as
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
mem_cgroup_add/remove/etc_lru() {
pc = lookup_page_cgroup(page);
mz = page_cgroup_zoneinfo(pc);
if (PageCgroupUsed(pc)) {
....add to LRU
}
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
This is much simpler.
(*) We're safe even if we don't take lock_page_cgroup(pc). Because..
1. When pc->mem_cgroup can be modified.
- at charge.
- at account_move().
2. at charge
the PCG_USED bit is not set before pc->mem_cgroup is fixed.
3. at account_move()
the page is isolated and not on LRU.
Pros.
- easy for maintenance.
- memcg can make use of laziness of pagevec.
- we don't have to duplicated LRU/Active/Unevictable bit in page_cgroup.
- LRU status of memcg will be synchronized with global LRU's one.
- # of locks are reduced.
- account_move() is simplified very much.
Cons.
- may increase cost of LRU rotation.
(no impact if memcg is not configured.)
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:08:01 +07:00
|
|
|
zone = &NODE_DATA(node)->node_zones[zid];
|
memcg: fix hotplugged memory zone oops
When MEMCG is configured on (even when it's disabled by boot option),
when adding or removing a page to/from its lru list, the zone pointer
used for stats updates is nowadays taken from the struct lruvec. (On
many configurations, calculating zone from page is slower.)
But we have no code to update all the lruvecs (per zone, per memcg) when
a memory node is hotadded. Here's an extract from the oops which
results when running numactl to bind a program to a newly onlined node:
BUG: unable to handle kernel NULL pointer dereference at 0000000000000f60
IP: __mod_zone_page_state+0x9/0x60
Pid: 1219, comm: numactl Not tainted 3.6.0-rc5+ #180 Bochs Bochs
Process numactl (pid: 1219, threadinfo ffff880039abc000, task ffff8800383c4ce0)
Call Trace:
__pagevec_lru_add_fn+0xdf/0x140
pagevec_lru_move_fn+0xb1/0x100
__pagevec_lru_add+0x1c/0x30
lru_add_drain_cpu+0xa3/0x130
lru_add_drain+0x2f/0x40
...
The natural solution might be to use a memcg callback whenever memory is
hotadded; but that solution has not been scoped out, and it happens that
we do have an easy location at which to update lruvec->zone. The lruvec
pointer is discovered either by mem_cgroup_zone_lruvec() or by
mem_cgroup_page_lruvec(), and both of those do know the right zone.
So check and set lruvec->zone in those; and remove the inadequate
attempt to set lruvec->zone from lruvec_init(), which is called before
NODE_DATA(node) has been allocated in such cases.
Ah, there was one exceptionr. For no particularly good reason,
mem_cgroup_force_empty_list() has its own code for deciding lruvec.
Change it to use the standard mem_cgroup_zone_lruvec() and
mem_cgroup_get_lru_size() too. In fact it was already safe against such
an oops (the lru lists in danger could only be empty), but we're better
proofed against future changes this way.
I've marked this for stable (3.6) since we introduced the problem in 3.5
(now closed to stable); but I have no idea if this is the only fix
needed to get memory hotadd working with memcg in 3.6, and received no
answer when I enquired twice before.
Reported-by: Tang Chen <tangchen@cn.fujitsu.com>
Signed-off-by: Hugh Dickins <hughd@google.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Konstantin Khlebnikov <khlebnikov@openvz.org>
Cc: Wen Congyang <wency@cn.fujitsu.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-11-17 05:14:54 +07:00
|
|
|
lruvec = mem_cgroup_zone_lruvec(zone, memcg);
|
|
|
|
list = &lruvec->lists[lru];
|
2008-02-07 15:14:16 +07:00
|
|
|
|
memcg: fix hotplugged memory zone oops
When MEMCG is configured on (even when it's disabled by boot option),
when adding or removing a page to/from its lru list, the zone pointer
used for stats updates is nowadays taken from the struct lruvec. (On
many configurations, calculating zone from page is slower.)
But we have no code to update all the lruvecs (per zone, per memcg) when
a memory node is hotadded. Here's an extract from the oops which
results when running numactl to bind a program to a newly onlined node:
BUG: unable to handle kernel NULL pointer dereference at 0000000000000f60
IP: __mod_zone_page_state+0x9/0x60
Pid: 1219, comm: numactl Not tainted 3.6.0-rc5+ #180 Bochs Bochs
Process numactl (pid: 1219, threadinfo ffff880039abc000, task ffff8800383c4ce0)
Call Trace:
__pagevec_lru_add_fn+0xdf/0x140
pagevec_lru_move_fn+0xb1/0x100
__pagevec_lru_add+0x1c/0x30
lru_add_drain_cpu+0xa3/0x130
lru_add_drain+0x2f/0x40
...
The natural solution might be to use a memcg callback whenever memory is
hotadded; but that solution has not been scoped out, and it happens that
we do have an easy location at which to update lruvec->zone. The lruvec
pointer is discovered either by mem_cgroup_zone_lruvec() or by
mem_cgroup_page_lruvec(), and both of those do know the right zone.
So check and set lruvec->zone in those; and remove the inadequate
attempt to set lruvec->zone from lruvec_init(), which is called before
NODE_DATA(node) has been allocated in such cases.
Ah, there was one exceptionr. For no particularly good reason,
mem_cgroup_force_empty_list() has its own code for deciding lruvec.
Change it to use the standard mem_cgroup_zone_lruvec() and
mem_cgroup_get_lru_size() too. In fact it was already safe against such
an oops (the lru lists in danger could only be empty), but we're better
proofed against future changes this way.
I've marked this for stable (3.6) since we introduced the problem in 3.5
(now closed to stable); but I have no idea if this is the only fix
needed to get memory hotadd working with memcg in 3.6, and received no
answer when I enquired twice before.
Reported-by: Tang Chen <tangchen@cn.fujitsu.com>
Signed-off-by: Hugh Dickins <hughd@google.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Konstantin Khlebnikov <khlebnikov@openvz.org>
Cc: Wen Congyang <wency@cn.fujitsu.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-11-17 05:14:54 +07:00
|
|
|
loop = mem_cgroup_get_lru_size(lruvec, lru);
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
/* give some margin against EBUSY etc...*/
|
|
|
|
loop += 256;
|
|
|
|
busy = NULL;
|
|
|
|
while (loop--) {
|
2012-01-13 08:18:15 +07:00
|
|
|
struct page_cgroup *pc;
|
2011-03-24 06:42:29 +07:00
|
|
|
struct page *page;
|
|
|
|
|
memcg: synchronized LRU
A big patch for changing memcg's LRU semantics.
Now,
- page_cgroup is linked to mem_cgroup's its own LRU (per zone).
- LRU of page_cgroup is not synchronous with global LRU.
- page and page_cgroup is one-to-one and statically allocated.
- To find page_cgroup is on what LRU, you have to check pc->mem_cgroup as
- lru = page_cgroup_zoneinfo(pc, nid_of_pc, zid_of_pc);
- SwapCache is handled.
And, when we handle LRU list of page_cgroup, we do following.
pc = lookup_page_cgroup(page);
lock_page_cgroup(pc); .....................(1)
mz = page_cgroup_zoneinfo(pc);
spin_lock(&mz->lru_lock);
.....add to LRU
spin_unlock(&mz->lru_lock);
unlock_page_cgroup(pc);
But (1) is spin_lock and we have to be afraid of dead-lock with zone->lru_lock.
So, trylock() is used at (1), now. Without (1), we can't trust "mz" is correct.
This is a trial to remove this dirty nesting of locks.
This patch changes mz->lru_lock to be zone->lru_lock.
Then, above sequence will be written as
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
mem_cgroup_add/remove/etc_lru() {
pc = lookup_page_cgroup(page);
mz = page_cgroup_zoneinfo(pc);
if (PageCgroupUsed(pc)) {
....add to LRU
}
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
This is much simpler.
(*) We're safe even if we don't take lock_page_cgroup(pc). Because..
1. When pc->mem_cgroup can be modified.
- at charge.
- at account_move().
2. at charge
the PCG_USED bit is not set before pc->mem_cgroup is fixed.
3. at account_move()
the page is isolated and not on LRU.
Pros.
- easy for maintenance.
- memcg can make use of laziness of pagevec.
- we don't have to duplicated LRU/Active/Unevictable bit in page_cgroup.
- LRU status of memcg will be synchronized with global LRU's one.
- # of locks are reduced.
- account_move() is simplified very much.
Cons.
- may increase cost of LRU rotation.
(no impact if memcg is not configured.)
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:08:01 +07:00
|
|
|
spin_lock_irqsave(&zone->lru_lock, flags);
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
if (list_empty(list)) {
|
memcg: synchronized LRU
A big patch for changing memcg's LRU semantics.
Now,
- page_cgroup is linked to mem_cgroup's its own LRU (per zone).
- LRU of page_cgroup is not synchronous with global LRU.
- page and page_cgroup is one-to-one and statically allocated.
- To find page_cgroup is on what LRU, you have to check pc->mem_cgroup as
- lru = page_cgroup_zoneinfo(pc, nid_of_pc, zid_of_pc);
- SwapCache is handled.
And, when we handle LRU list of page_cgroup, we do following.
pc = lookup_page_cgroup(page);
lock_page_cgroup(pc); .....................(1)
mz = page_cgroup_zoneinfo(pc);
spin_lock(&mz->lru_lock);
.....add to LRU
spin_unlock(&mz->lru_lock);
unlock_page_cgroup(pc);
But (1) is spin_lock and we have to be afraid of dead-lock with zone->lru_lock.
So, trylock() is used at (1), now. Without (1), we can't trust "mz" is correct.
This is a trial to remove this dirty nesting of locks.
This patch changes mz->lru_lock to be zone->lru_lock.
Then, above sequence will be written as
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
mem_cgroup_add/remove/etc_lru() {
pc = lookup_page_cgroup(page);
mz = page_cgroup_zoneinfo(pc);
if (PageCgroupUsed(pc)) {
....add to LRU
}
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
This is much simpler.
(*) We're safe even if we don't take lock_page_cgroup(pc). Because..
1. When pc->mem_cgroup can be modified.
- at charge.
- at account_move().
2. at charge
the PCG_USED bit is not set before pc->mem_cgroup is fixed.
3. at account_move()
the page is isolated and not on LRU.
Pros.
- easy for maintenance.
- memcg can make use of laziness of pagevec.
- we don't have to duplicated LRU/Active/Unevictable bit in page_cgroup.
- LRU status of memcg will be synchronized with global LRU's one.
- # of locks are reduced.
- account_move() is simplified very much.
Cons.
- may increase cost of LRU rotation.
(no impact if memcg is not configured.)
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:08:01 +07:00
|
|
|
spin_unlock_irqrestore(&zone->lru_lock, flags);
|
2008-10-19 10:28:16 +07:00
|
|
|
break;
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
}
|
2012-01-13 08:18:15 +07:00
|
|
|
page = list_entry(list->prev, struct page, lru);
|
|
|
|
if (busy == page) {
|
|
|
|
list_move(&page->lru, list);
|
2010-03-06 04:42:04 +07:00
|
|
|
busy = NULL;
|
memcg: synchronized LRU
A big patch for changing memcg's LRU semantics.
Now,
- page_cgroup is linked to mem_cgroup's its own LRU (per zone).
- LRU of page_cgroup is not synchronous with global LRU.
- page and page_cgroup is one-to-one and statically allocated.
- To find page_cgroup is on what LRU, you have to check pc->mem_cgroup as
- lru = page_cgroup_zoneinfo(pc, nid_of_pc, zid_of_pc);
- SwapCache is handled.
And, when we handle LRU list of page_cgroup, we do following.
pc = lookup_page_cgroup(page);
lock_page_cgroup(pc); .....................(1)
mz = page_cgroup_zoneinfo(pc);
spin_lock(&mz->lru_lock);
.....add to LRU
spin_unlock(&mz->lru_lock);
unlock_page_cgroup(pc);
But (1) is spin_lock and we have to be afraid of dead-lock with zone->lru_lock.
So, trylock() is used at (1), now. Without (1), we can't trust "mz" is correct.
This is a trial to remove this dirty nesting of locks.
This patch changes mz->lru_lock to be zone->lru_lock.
Then, above sequence will be written as
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
mem_cgroup_add/remove/etc_lru() {
pc = lookup_page_cgroup(page);
mz = page_cgroup_zoneinfo(pc);
if (PageCgroupUsed(pc)) {
....add to LRU
}
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
This is much simpler.
(*) We're safe even if we don't take lock_page_cgroup(pc). Because..
1. When pc->mem_cgroup can be modified.
- at charge.
- at account_move().
2. at charge
the PCG_USED bit is not set before pc->mem_cgroup is fixed.
3. at account_move()
the page is isolated and not on LRU.
Pros.
- easy for maintenance.
- memcg can make use of laziness of pagevec.
- we don't have to duplicated LRU/Active/Unevictable bit in page_cgroup.
- LRU status of memcg will be synchronized with global LRU's one.
- # of locks are reduced.
- account_move() is simplified very much.
Cons.
- may increase cost of LRU rotation.
(no impact if memcg is not configured.)
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:08:01 +07:00
|
|
|
spin_unlock_irqrestore(&zone->lru_lock, flags);
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
continue;
|
|
|
|
}
|
memcg: synchronized LRU
A big patch for changing memcg's LRU semantics.
Now,
- page_cgroup is linked to mem_cgroup's its own LRU (per zone).
- LRU of page_cgroup is not synchronous with global LRU.
- page and page_cgroup is one-to-one and statically allocated.
- To find page_cgroup is on what LRU, you have to check pc->mem_cgroup as
- lru = page_cgroup_zoneinfo(pc, nid_of_pc, zid_of_pc);
- SwapCache is handled.
And, when we handle LRU list of page_cgroup, we do following.
pc = lookup_page_cgroup(page);
lock_page_cgroup(pc); .....................(1)
mz = page_cgroup_zoneinfo(pc);
spin_lock(&mz->lru_lock);
.....add to LRU
spin_unlock(&mz->lru_lock);
unlock_page_cgroup(pc);
But (1) is spin_lock and we have to be afraid of dead-lock with zone->lru_lock.
So, trylock() is used at (1), now. Without (1), we can't trust "mz" is correct.
This is a trial to remove this dirty nesting of locks.
This patch changes mz->lru_lock to be zone->lru_lock.
Then, above sequence will be written as
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
mem_cgroup_add/remove/etc_lru() {
pc = lookup_page_cgroup(page);
mz = page_cgroup_zoneinfo(pc);
if (PageCgroupUsed(pc)) {
....add to LRU
}
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
This is much simpler.
(*) We're safe even if we don't take lock_page_cgroup(pc). Because..
1. When pc->mem_cgroup can be modified.
- at charge.
- at account_move().
2. at charge
the PCG_USED bit is not set before pc->mem_cgroup is fixed.
3. at account_move()
the page is isolated and not on LRU.
Pros.
- easy for maintenance.
- memcg can make use of laziness of pagevec.
- we don't have to duplicated LRU/Active/Unevictable bit in page_cgroup.
- LRU status of memcg will be synchronized with global LRU's one.
- # of locks are reduced.
- account_move() is simplified very much.
Cons.
- may increase cost of LRU rotation.
(no impact if memcg is not configured.)
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:08:01 +07:00
|
|
|
spin_unlock_irqrestore(&zone->lru_lock, flags);
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
|
2012-01-13 08:18:15 +07:00
|
|
|
pc = lookup_page_cgroup(page);
|
2011-03-24 06:42:29 +07:00
|
|
|
|
2012-08-01 06:42:46 +07:00
|
|
|
if (mem_cgroup_move_parent(page, pc, memcg)) {
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
/* found lock contention or "pc" is obsolete. */
|
2012-01-13 08:18:15 +07:00
|
|
|
busy = page;
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
cond_resched();
|
|
|
|
} else
|
|
|
|
busy = NULL;
|
2008-02-07 15:14:16 +07:00
|
|
|
}
|
2012-08-01 06:42:46 +07:00
|
|
|
return !list_empty(list);
|
2008-02-07 15:14:16 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* make mem_cgroup's charge to be 0 if there is no task.
|
|
|
|
* This enables deleting this mem_cgroup.
|
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
static int mem_cgroup_force_empty(struct mem_cgroup *memcg, bool free_all)
|
2008-02-07 15:14:16 +07:00
|
|
|
{
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
int ret;
|
|
|
|
int node, zid, shrink;
|
|
|
|
int nr_retries = MEM_CGROUP_RECLAIM_RETRIES;
|
2011-11-03 03:38:15 +07:00
|
|
|
struct cgroup *cgrp = memcg->css.cgroup;
|
2008-03-05 05:29:09 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
css_get(&memcg->css);
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
|
|
|
|
shrink = 0;
|
2009-01-08 09:07:55 +07:00
|
|
|
/* should free all ? */
|
|
|
|
if (free_all)
|
|
|
|
goto try_to_free;
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
move_account:
|
memcg: ensure list is empty at rmdir
Current mem_cgroup_force_empty() only ensures mem->res.usage == 0 on
success. But this doesn't guarantee memcg's LRU is really empty, because
there are some cases in which !PageCgrupUsed pages exist on memcg's LRU.
For example:
- Pages can be uncharged by its owner process while they are on LRU.
- race between mem_cgroup_add_lru_list() and __mem_cgroup_uncharge_common().
So there can be a case in which the usage is zero but some of the LRUs are not empty.
OTOH, mem_cgroup_del_lru_list(), which can be called asynchronously with
rmdir, accesses the mem_cgroup, so this access can cause a problem if it
races with rmdir because the mem_cgroup might have been freed by rmdir.
Actually, I saw a bug which seems to be caused by this race.
[1530745.949906] BUG: unable to handle kernel NULL pointer dereference at 0000000000000230
[1530745.950651] IP: [<ffffffff810fbc11>] mem_cgroup_del_lru_list+0x30/0x80
[1530745.950651] PGD 3863de067 PUD 3862c7067 PMD 0
[1530745.950651] Oops: 0002 [#1] SMP
[1530745.950651] last sysfs file: /sys/devices/system/cpu/cpu7/cache/index1/shared_cpu_map
[1530745.950651] CPU 3
[1530745.950651] Modules linked in: configs ipt_REJECT xt_tcpudp iptable_filter ip_tables x_tables bridge stp nfsd nfs_acl auth_rpcgss exportfs autofs4 hidp rfcomm l2cap crc16 bluetooth lockd sunrpc ib_iser rdma_cm ib_cm iw_cm ib_sa ib_mad ib_core ib_addr iscsi_tcp bnx2i cnic uio ipv6 cxgb3i cxgb3 mdio libiscsi_tcp libiscsi scsi_transport_iscsi dm_mirror dm_multipath scsi_dh video output sbs sbshc battery ac lp kvm_intel kvm sg ide_cd_mod cdrom serio_raw tpm_tis tpm tpm_bios acpi_memhotplug button parport_pc parport rtc_cmos rtc_core rtc_lib e1000 i2c_i801 i2c_core pcspkr dm_region_hash dm_log dm_mod ata_piix libata shpchp megaraid_mbox sd_mod scsi_mod megaraid_mm ext3 jbd uhci_hcd ohci_hcd ehci_hcd [last unloaded: freq_table]
[1530745.950651] Pid: 19653, comm: shmem_test_02 Tainted: G M 2.6.32-mm1-00701-g2b04386 #3 Express5800/140Rd-4 [N8100-1065]
[1530745.950651] RIP: 0010:[<ffffffff810fbc11>] [<ffffffff810fbc11>] mem_cgroup_del_lru_list+0x30/0x80
[1530745.950651] RSP: 0018:ffff8803863ddcb8 EFLAGS: 00010002
[1530745.950651] RAX: 00000000000001e0 RBX: ffff8803abc02238 RCX: 00000000000001e0
[1530745.950651] RDX: 0000000000000000 RSI: ffff88038611a000 RDI: ffff8803abc02238
[1530745.950651] RBP: ffff8803863ddcc8 R08: 0000000000000002 R09: ffff8803a04c8643
[1530745.950651] R10: 0000000000000000 R11: ffffffff810c7333 R12: 0000000000000000
[1530745.950651] R13: ffff880000017f00 R14: 0000000000000092 R15: ffff8800179d0310
[1530745.950651] FS: 0000000000000000(0000) GS:ffff880017800000(0000) knlGS:0000000000000000
[1530745.950651] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b
[1530745.950651] CR2: 0000000000000230 CR3: 0000000379d87000 CR4: 00000000000006e0
[1530745.950651] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[1530745.950651] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
[1530745.950651] Process shmem_test_02 (pid: 19653, threadinfo ffff8803863dc000, task ffff88038612a8a0)
[1530745.950651] Stack:
[1530745.950651] ffffea00040c2fe8 0000000000000000 ffff8803863ddd98 ffffffff810c739a
[1530745.950651] <0> 00000000863ddd18 000000000000000c 0000000000000000 0000000000000000
[1530745.950651] <0> 0000000000000002 0000000000000000 ffff8803863ddd68 0000000000000046
[1530745.950651] Call Trace:
[1530745.950651] [<ffffffff810c739a>] release_pages+0x142/0x1e7
[1530745.950651] [<ffffffff810c778f>] ? pagevec_move_tail+0x6e/0x112
[1530745.950651] [<ffffffff810c781e>] pagevec_move_tail+0xfd/0x112
[1530745.950651] [<ffffffff810c78a9>] lru_add_drain+0x76/0x94
[1530745.950651] [<ffffffff810dba0c>] exit_mmap+0x6e/0x145
[1530745.950651] [<ffffffff8103f52d>] mmput+0x5e/0xcf
[1530745.950651] [<ffffffff81043ea8>] exit_mm+0x11c/0x129
[1530745.950651] [<ffffffff8108fb29>] ? audit_free+0x196/0x1c9
[1530745.950651] [<ffffffff81045353>] do_exit+0x1f5/0x6b7
[1530745.950651] [<ffffffff8106133f>] ? up_read+0x2b/0x2f
[1530745.950651] [<ffffffff8137d187>] ? lockdep_sys_exit_thunk+0x35/0x67
[1530745.950651] [<ffffffff81045898>] do_group_exit+0x83/0xb0
[1530745.950651] [<ffffffff810458dc>] sys_exit_group+0x17/0x1b
[1530745.950651] [<ffffffff81002c1b>] system_call_fastpath+0x16/0x1b
[1530745.950651] Code: 54 53 0f 1f 44 00 00 83 3d cc 29 7c 00 00 41 89 f4 75 63 eb 4e 48 83 7b 08 00 75 04 0f 0b eb fe 48 89 df e8 18 f3 ff ff 44 89 e2 <48> ff 4c d0 50 48 8b 05 2b 2d 7c 00 48 39 43 08 74 39 48 8b 4b
[1530745.950651] RIP [<ffffffff810fbc11>] mem_cgroup_del_lru_list+0x30/0x80
[1530745.950651] RSP <ffff8803863ddcb8>
[1530745.950651] CR2: 0000000000000230
[1530745.950651] ---[ end trace c3419c1bb8acc34f ]---
[1530745.950651] Fixing recursive fault but reboot is needed!
The problem here is pages on LRU may contain pointer to stale memcg. To
make res->usage to be 0, all pages on memcg must be uncharged or moved to
another(parent) memcg. Moved page_cgroup have already removed from
original LRU, but uncharged page_cgroup contains pointer to memcg withou
PCG_USED bit. (This asynchronous LRU work is for improving performance.)
If PCG_USED bit is not set, page_cgroup will never be added to memcg's
LRU. So, about pages not on LRU, they never access stale pointer. Then,
what we have to take care of is page_cgroup _on_ LRU list. This patch
fixes this problem by making mem_cgroup_force_empty() visit all LRUs
before exiting its loop and guarantee there are no pages on its LRU.
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@linux.vnet.ibm.com>
Cc: <stable@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-01-16 08:01:30 +07:00
|
|
|
do {
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
ret = -EBUSY;
|
2009-01-08 09:07:55 +07:00
|
|
|
if (cgroup_task_count(cgrp) || !list_empty(&cgrp->children))
|
|
|
|
goto out;
|
2008-10-19 10:28:16 +07:00
|
|
|
/* This is for making all *used* pages to be on LRU. */
|
|
|
|
lru_add_drain_all();
|
2011-11-03 03:38:15 +07:00
|
|
|
drain_all_stock_sync(memcg);
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
ret = 0;
|
2011-11-03 03:38:15 +07:00
|
|
|
mem_cgroup_start_move(memcg);
|
2009-01-30 05:25:17 +07:00
|
|
|
for_each_node_state(node, N_HIGH_MEMORY) {
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
for (zid = 0; !ret && zid < MAX_NR_ZONES; zid++) {
|
2012-03-22 06:34:19 +07:00
|
|
|
enum lru_list lru;
|
|
|
|
for_each_lru(lru) {
|
2011-11-03 03:38:15 +07:00
|
|
|
ret = mem_cgroup_force_empty_list(memcg,
|
2012-03-22 06:34:19 +07:00
|
|
|
node, zid, lru);
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
}
|
2008-02-07 15:14:38 +07:00
|
|
|
}
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
}
|
2011-11-03 03:38:15 +07:00
|
|
|
mem_cgroup_end_move(memcg);
|
|
|
|
memcg_oom_recover(memcg);
|
2008-10-19 10:28:16 +07:00
|
|
|
cond_resched();
|
memcg: ensure list is empty at rmdir
Current mem_cgroup_force_empty() only ensures mem->res.usage == 0 on
success. But this doesn't guarantee memcg's LRU is really empty, because
there are some cases in which !PageCgrupUsed pages exist on memcg's LRU.
For example:
- Pages can be uncharged by its owner process while they are on LRU.
- race between mem_cgroup_add_lru_list() and __mem_cgroup_uncharge_common().
So there can be a case in which the usage is zero but some of the LRUs are not empty.
OTOH, mem_cgroup_del_lru_list(), which can be called asynchronously with
rmdir, accesses the mem_cgroup, so this access can cause a problem if it
races with rmdir because the mem_cgroup might have been freed by rmdir.
Actually, I saw a bug which seems to be caused by this race.
[1530745.949906] BUG: unable to handle kernel NULL pointer dereference at 0000000000000230
[1530745.950651] IP: [<ffffffff810fbc11>] mem_cgroup_del_lru_list+0x30/0x80
[1530745.950651] PGD 3863de067 PUD 3862c7067 PMD 0
[1530745.950651] Oops: 0002 [#1] SMP
[1530745.950651] last sysfs file: /sys/devices/system/cpu/cpu7/cache/index1/shared_cpu_map
[1530745.950651] CPU 3
[1530745.950651] Modules linked in: configs ipt_REJECT xt_tcpudp iptable_filter ip_tables x_tables bridge stp nfsd nfs_acl auth_rpcgss exportfs autofs4 hidp rfcomm l2cap crc16 bluetooth lockd sunrpc ib_iser rdma_cm ib_cm iw_cm ib_sa ib_mad ib_core ib_addr iscsi_tcp bnx2i cnic uio ipv6 cxgb3i cxgb3 mdio libiscsi_tcp libiscsi scsi_transport_iscsi dm_mirror dm_multipath scsi_dh video output sbs sbshc battery ac lp kvm_intel kvm sg ide_cd_mod cdrom serio_raw tpm_tis tpm tpm_bios acpi_memhotplug button parport_pc parport rtc_cmos rtc_core rtc_lib e1000 i2c_i801 i2c_core pcspkr dm_region_hash dm_log dm_mod ata_piix libata shpchp megaraid_mbox sd_mod scsi_mod megaraid_mm ext3 jbd uhci_hcd ohci_hcd ehci_hcd [last unloaded: freq_table]
[1530745.950651] Pid: 19653, comm: shmem_test_02 Tainted: G M 2.6.32-mm1-00701-g2b04386 #3 Express5800/140Rd-4 [N8100-1065]
[1530745.950651] RIP: 0010:[<ffffffff810fbc11>] [<ffffffff810fbc11>] mem_cgroup_del_lru_list+0x30/0x80
[1530745.950651] RSP: 0018:ffff8803863ddcb8 EFLAGS: 00010002
[1530745.950651] RAX: 00000000000001e0 RBX: ffff8803abc02238 RCX: 00000000000001e0
[1530745.950651] RDX: 0000000000000000 RSI: ffff88038611a000 RDI: ffff8803abc02238
[1530745.950651] RBP: ffff8803863ddcc8 R08: 0000000000000002 R09: ffff8803a04c8643
[1530745.950651] R10: 0000000000000000 R11: ffffffff810c7333 R12: 0000000000000000
[1530745.950651] R13: ffff880000017f00 R14: 0000000000000092 R15: ffff8800179d0310
[1530745.950651] FS: 0000000000000000(0000) GS:ffff880017800000(0000) knlGS:0000000000000000
[1530745.950651] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b
[1530745.950651] CR2: 0000000000000230 CR3: 0000000379d87000 CR4: 00000000000006e0
[1530745.950651] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[1530745.950651] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
[1530745.950651] Process shmem_test_02 (pid: 19653, threadinfo ffff8803863dc000, task ffff88038612a8a0)
[1530745.950651] Stack:
[1530745.950651] ffffea00040c2fe8 0000000000000000 ffff8803863ddd98 ffffffff810c739a
[1530745.950651] <0> 00000000863ddd18 000000000000000c 0000000000000000 0000000000000000
[1530745.950651] <0> 0000000000000002 0000000000000000 ffff8803863ddd68 0000000000000046
[1530745.950651] Call Trace:
[1530745.950651] [<ffffffff810c739a>] release_pages+0x142/0x1e7
[1530745.950651] [<ffffffff810c778f>] ? pagevec_move_tail+0x6e/0x112
[1530745.950651] [<ffffffff810c781e>] pagevec_move_tail+0xfd/0x112
[1530745.950651] [<ffffffff810c78a9>] lru_add_drain+0x76/0x94
[1530745.950651] [<ffffffff810dba0c>] exit_mmap+0x6e/0x145
[1530745.950651] [<ffffffff8103f52d>] mmput+0x5e/0xcf
[1530745.950651] [<ffffffff81043ea8>] exit_mm+0x11c/0x129
[1530745.950651] [<ffffffff8108fb29>] ? audit_free+0x196/0x1c9
[1530745.950651] [<ffffffff81045353>] do_exit+0x1f5/0x6b7
[1530745.950651] [<ffffffff8106133f>] ? up_read+0x2b/0x2f
[1530745.950651] [<ffffffff8137d187>] ? lockdep_sys_exit_thunk+0x35/0x67
[1530745.950651] [<ffffffff81045898>] do_group_exit+0x83/0xb0
[1530745.950651] [<ffffffff810458dc>] sys_exit_group+0x17/0x1b
[1530745.950651] [<ffffffff81002c1b>] system_call_fastpath+0x16/0x1b
[1530745.950651] Code: 54 53 0f 1f 44 00 00 83 3d cc 29 7c 00 00 41 89 f4 75 63 eb 4e 48 83 7b 08 00 75 04 0f 0b eb fe 48 89 df e8 18 f3 ff ff 44 89 e2 <48> ff 4c d0 50 48 8b 05 2b 2d 7c 00 48 39 43 08 74 39 48 8b 4b
[1530745.950651] RIP [<ffffffff810fbc11>] mem_cgroup_del_lru_list+0x30/0x80
[1530745.950651] RSP <ffff8803863ddcb8>
[1530745.950651] CR2: 0000000000000230
[1530745.950651] ---[ end trace c3419c1bb8acc34f ]---
[1530745.950651] Fixing recursive fault but reboot is needed!
The problem here is pages on LRU may contain pointer to stale memcg. To
make res->usage to be 0, all pages on memcg must be uncharged or moved to
another(parent) memcg. Moved page_cgroup have already removed from
original LRU, but uncharged page_cgroup contains pointer to memcg withou
PCG_USED bit. (This asynchronous LRU work is for improving performance.)
If PCG_USED bit is not set, page_cgroup will never be added to memcg's
LRU. So, about pages not on LRU, they never access stale pointer. Then,
what we have to take care of is page_cgroup _on_ LRU list. This patch
fixes this problem by making mem_cgroup_force_empty() visit all LRUs
before exiting its loop and guarantee there are no pages on its LRU.
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@linux.vnet.ibm.com>
Cc: <stable@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-01-16 08:01:30 +07:00
|
|
|
/* "ret" should also be checked to ensure all lists are empty. */
|
2012-04-13 02:49:13 +07:00
|
|
|
} while (res_counter_read_u64(&memcg->res, RES_USAGE) > 0 || ret);
|
2008-02-07 15:14:16 +07:00
|
|
|
out:
|
2011-11-03 03:38:15 +07:00
|
|
|
css_put(&memcg->css);
|
2008-02-07 15:14:16 +07:00
|
|
|
return ret;
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
|
|
|
|
try_to_free:
|
2009-01-08 09:07:55 +07:00
|
|
|
/* returns EBUSY if there is a task or if we come here twice. */
|
|
|
|
if (cgroup_task_count(cgrp) || !list_empty(&cgrp->children) || shrink) {
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
ret = -EBUSY;
|
|
|
|
goto out;
|
|
|
|
}
|
2009-01-08 09:07:55 +07:00
|
|
|
/* we call try-to-free pages for make this cgroup empty */
|
|
|
|
lru_add_drain_all();
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
/* try to free all pages in this cgroup */
|
|
|
|
shrink = 1;
|
2012-04-13 02:49:13 +07:00
|
|
|
while (nr_retries && res_counter_read_u64(&memcg->res, RES_USAGE) > 0) {
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
int progress;
|
2009-01-08 09:07:55 +07:00
|
|
|
|
|
|
|
if (signal_pending(current)) {
|
|
|
|
ret = -EINTR;
|
|
|
|
goto out;
|
|
|
|
}
|
2011-11-03 03:38:15 +07:00
|
|
|
progress = try_to_free_mem_cgroup_pages(memcg, GFP_KERNEL,
|
2011-09-15 06:21:58 +07:00
|
|
|
false);
|
2009-01-08 09:07:55 +07:00
|
|
|
if (!progress) {
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
nr_retries--;
|
2009-01-08 09:07:55 +07:00
|
|
|
/* maybe some writeback is necessary */
|
2009-07-09 19:52:32 +07:00
|
|
|
congestion_wait(BLK_RW_ASYNC, HZ/10);
|
2009-01-08 09:07:55 +07:00
|
|
|
}
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
|
|
|
|
}
|
memcg: synchronized LRU
A big patch for changing memcg's LRU semantics.
Now,
- page_cgroup is linked to mem_cgroup's its own LRU (per zone).
- LRU of page_cgroup is not synchronous with global LRU.
- page and page_cgroup is one-to-one and statically allocated.
- To find page_cgroup is on what LRU, you have to check pc->mem_cgroup as
- lru = page_cgroup_zoneinfo(pc, nid_of_pc, zid_of_pc);
- SwapCache is handled.
And, when we handle LRU list of page_cgroup, we do following.
pc = lookup_page_cgroup(page);
lock_page_cgroup(pc); .....................(1)
mz = page_cgroup_zoneinfo(pc);
spin_lock(&mz->lru_lock);
.....add to LRU
spin_unlock(&mz->lru_lock);
unlock_page_cgroup(pc);
But (1) is spin_lock and we have to be afraid of dead-lock with zone->lru_lock.
So, trylock() is used at (1), now. Without (1), we can't trust "mz" is correct.
This is a trial to remove this dirty nesting of locks.
This patch changes mz->lru_lock to be zone->lru_lock.
Then, above sequence will be written as
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
mem_cgroup_add/remove/etc_lru() {
pc = lookup_page_cgroup(page);
mz = page_cgroup_zoneinfo(pc);
if (PageCgroupUsed(pc)) {
....add to LRU
}
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
This is much simpler.
(*) We're safe even if we don't take lock_page_cgroup(pc). Because..
1. When pc->mem_cgroup can be modified.
- at charge.
- at account_move().
2. at charge
the PCG_USED bit is not set before pc->mem_cgroup is fixed.
3. at account_move()
the page is isolated and not on LRU.
Pros.
- easy for maintenance.
- memcg can make use of laziness of pagevec.
- we don't have to duplicated LRU/Active/Unevictable bit in page_cgroup.
- LRU status of memcg will be synchronized with global LRU's one.
- # of locks are reduced.
- account_move() is simplified very much.
Cons.
- may increase cost of LRU rotation.
(no impact if memcg is not configured.)
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:08:01 +07:00
|
|
|
lru_add_drain();
|
memcg: move all acccounting to parent at rmdir()
This patch provides a function to move account information of a page
between mem_cgroups and rewrite force_empty to make use of this.
This moving of page_cgroup is done under
- lru_lock of source/destination mem_cgroup is held.
- lock_page_cgroup() is held.
Then, a routine which touches pc->mem_cgroup without lock_page_cgroup()
should confirm pc->mem_cgroup is still valid or not. Typical code can be
following.
(while page is not under lock_page())
mem = pc->mem_cgroup;
mz = page_cgroup_zoneinfo(pc)
spin_lock_irqsave(&mz->lru_lock);
if (pc->mem_cgroup == mem)
...../* some list handling */
spin_unlock_irqrestore(&mz->lru_lock);
Of course, better way is
lock_page_cgroup(pc);
....
unlock_page_cgroup(pc);
But you should confirm the nest of lock and avoid deadlock.
If you treats page_cgroup from mem_cgroup's LRU under mz->lru_lock,
you don't have to worry about what pc->mem_cgroup points to.
moved pages are added to head of lru, not to tail.
Expected users of this routine is:
- force_empty (rmdir)
- moving tasks between cgroup (for moving account information.)
- hierarchy (maybe useful.)
force_empty(rmdir) uses this move_account and move pages to its parent.
This "move" will not cause OOM (I added "oom" parameter to try_charge().)
If the parent is busy (not enough memory), force_empty calls try_to_free_page()
and reduce usage.
Purpose of this behavior is
- Fix "forget all" behavior of force_empty and avoid leak of accounting.
- By "moving first, free if necessary", keep pages on memory as much as
possible.
Adding a switch to change behavior of force_empty to
- free first, move if necessary
- free all, if there is mlocked/busy pages, return -EBUSY.
is under consideration. (I'll add if someone requtests.)
This patch also removes memory.force_empty file, a brutal debug-only interface.
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Paul Menage <menage@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:07:53 +07:00
|
|
|
/* try move_account...there may be some *locked* pages. */
|
memcg: ensure list is empty at rmdir
Current mem_cgroup_force_empty() only ensures mem->res.usage == 0 on
success. But this doesn't guarantee memcg's LRU is really empty, because
there are some cases in which !PageCgrupUsed pages exist on memcg's LRU.
For example:
- Pages can be uncharged by its owner process while they are on LRU.
- race between mem_cgroup_add_lru_list() and __mem_cgroup_uncharge_common().
So there can be a case in which the usage is zero but some of the LRUs are not empty.
OTOH, mem_cgroup_del_lru_list(), which can be called asynchronously with
rmdir, accesses the mem_cgroup, so this access can cause a problem if it
races with rmdir because the mem_cgroup might have been freed by rmdir.
Actually, I saw a bug which seems to be caused by this race.
[1530745.949906] BUG: unable to handle kernel NULL pointer dereference at 0000000000000230
[1530745.950651] IP: [<ffffffff810fbc11>] mem_cgroup_del_lru_list+0x30/0x80
[1530745.950651] PGD 3863de067 PUD 3862c7067 PMD 0
[1530745.950651] Oops: 0002 [#1] SMP
[1530745.950651] last sysfs file: /sys/devices/system/cpu/cpu7/cache/index1/shared_cpu_map
[1530745.950651] CPU 3
[1530745.950651] Modules linked in: configs ipt_REJECT xt_tcpudp iptable_filter ip_tables x_tables bridge stp nfsd nfs_acl auth_rpcgss exportfs autofs4 hidp rfcomm l2cap crc16 bluetooth lockd sunrpc ib_iser rdma_cm ib_cm iw_cm ib_sa ib_mad ib_core ib_addr iscsi_tcp bnx2i cnic uio ipv6 cxgb3i cxgb3 mdio libiscsi_tcp libiscsi scsi_transport_iscsi dm_mirror dm_multipath scsi_dh video output sbs sbshc battery ac lp kvm_intel kvm sg ide_cd_mod cdrom serio_raw tpm_tis tpm tpm_bios acpi_memhotplug button parport_pc parport rtc_cmos rtc_core rtc_lib e1000 i2c_i801 i2c_core pcspkr dm_region_hash dm_log dm_mod ata_piix libata shpchp megaraid_mbox sd_mod scsi_mod megaraid_mm ext3 jbd uhci_hcd ohci_hcd ehci_hcd [last unloaded: freq_table]
[1530745.950651] Pid: 19653, comm: shmem_test_02 Tainted: G M 2.6.32-mm1-00701-g2b04386 #3 Express5800/140Rd-4 [N8100-1065]
[1530745.950651] RIP: 0010:[<ffffffff810fbc11>] [<ffffffff810fbc11>] mem_cgroup_del_lru_list+0x30/0x80
[1530745.950651] RSP: 0018:ffff8803863ddcb8 EFLAGS: 00010002
[1530745.950651] RAX: 00000000000001e0 RBX: ffff8803abc02238 RCX: 00000000000001e0
[1530745.950651] RDX: 0000000000000000 RSI: ffff88038611a000 RDI: ffff8803abc02238
[1530745.950651] RBP: ffff8803863ddcc8 R08: 0000000000000002 R09: ffff8803a04c8643
[1530745.950651] R10: 0000000000000000 R11: ffffffff810c7333 R12: 0000000000000000
[1530745.950651] R13: ffff880000017f00 R14: 0000000000000092 R15: ffff8800179d0310
[1530745.950651] FS: 0000000000000000(0000) GS:ffff880017800000(0000) knlGS:0000000000000000
[1530745.950651] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b
[1530745.950651] CR2: 0000000000000230 CR3: 0000000379d87000 CR4: 00000000000006e0
[1530745.950651] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[1530745.950651] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
[1530745.950651] Process shmem_test_02 (pid: 19653, threadinfo ffff8803863dc000, task ffff88038612a8a0)
[1530745.950651] Stack:
[1530745.950651] ffffea00040c2fe8 0000000000000000 ffff8803863ddd98 ffffffff810c739a
[1530745.950651] <0> 00000000863ddd18 000000000000000c 0000000000000000 0000000000000000
[1530745.950651] <0> 0000000000000002 0000000000000000 ffff8803863ddd68 0000000000000046
[1530745.950651] Call Trace:
[1530745.950651] [<ffffffff810c739a>] release_pages+0x142/0x1e7
[1530745.950651] [<ffffffff810c778f>] ? pagevec_move_tail+0x6e/0x112
[1530745.950651] [<ffffffff810c781e>] pagevec_move_tail+0xfd/0x112
[1530745.950651] [<ffffffff810c78a9>] lru_add_drain+0x76/0x94
[1530745.950651] [<ffffffff810dba0c>] exit_mmap+0x6e/0x145
[1530745.950651] [<ffffffff8103f52d>] mmput+0x5e/0xcf
[1530745.950651] [<ffffffff81043ea8>] exit_mm+0x11c/0x129
[1530745.950651] [<ffffffff8108fb29>] ? audit_free+0x196/0x1c9
[1530745.950651] [<ffffffff81045353>] do_exit+0x1f5/0x6b7
[1530745.950651] [<ffffffff8106133f>] ? up_read+0x2b/0x2f
[1530745.950651] [<ffffffff8137d187>] ? lockdep_sys_exit_thunk+0x35/0x67
[1530745.950651] [<ffffffff81045898>] do_group_exit+0x83/0xb0
[1530745.950651] [<ffffffff810458dc>] sys_exit_group+0x17/0x1b
[1530745.950651] [<ffffffff81002c1b>] system_call_fastpath+0x16/0x1b
[1530745.950651] Code: 54 53 0f 1f 44 00 00 83 3d cc 29 7c 00 00 41 89 f4 75 63 eb 4e 48 83 7b 08 00 75 04 0f 0b eb fe 48 89 df e8 18 f3 ff ff 44 89 e2 <48> ff 4c d0 50 48 8b 05 2b 2d 7c 00 48 39 43 08 74 39 48 8b 4b
[1530745.950651] RIP [<ffffffff810fbc11>] mem_cgroup_del_lru_list+0x30/0x80
[1530745.950651] RSP <ffff8803863ddcb8>
[1530745.950651] CR2: 0000000000000230
[1530745.950651] ---[ end trace c3419c1bb8acc34f ]---
[1530745.950651] Fixing recursive fault but reboot is needed!
The problem here is pages on LRU may contain pointer to stale memcg. To
make res->usage to be 0, all pages on memcg must be uncharged or moved to
another(parent) memcg. Moved page_cgroup have already removed from
original LRU, but uncharged page_cgroup contains pointer to memcg withou
PCG_USED bit. (This asynchronous LRU work is for improving performance.)
If PCG_USED bit is not set, page_cgroup will never be added to memcg's
LRU. So, about pages not on LRU, they never access stale pointer. Then,
what we have to take care of is page_cgroup _on_ LRU list. This patch
fixes this problem by making mem_cgroup_force_empty() visit all LRUs
before exiting its loop and guarantee there are no pages on its LRU.
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@linux.vnet.ibm.com>
Cc: <stable@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-01-16 08:01:30 +07:00
|
|
|
goto move_account;
|
2008-02-07 15:14:16 +07:00
|
|
|
}
|
|
|
|
|
2012-05-30 05:06:55 +07:00
|
|
|
static int mem_cgroup_force_empty_write(struct cgroup *cont, unsigned int event)
|
2009-01-08 09:07:55 +07:00
|
|
|
{
|
|
|
|
return mem_cgroup_force_empty(mem_cgroup_from_cont(cont), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-08 09:08:07 +07:00
|
|
|
static u64 mem_cgroup_hierarchy_read(struct cgroup *cont, struct cftype *cft)
|
|
|
|
{
|
|
|
|
return mem_cgroup_from_cont(cont)->use_hierarchy;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mem_cgroup_hierarchy_write(struct cgroup *cont, struct cftype *cft,
|
|
|
|
u64 val)
|
|
|
|
{
|
|
|
|
int retval = 0;
|
2011-11-03 03:38:15 +07:00
|
|
|
struct mem_cgroup *memcg = mem_cgroup_from_cont(cont);
|
2009-01-08 09:08:07 +07:00
|
|
|
struct cgroup *parent = cont->parent;
|
2011-11-03 03:38:15 +07:00
|
|
|
struct mem_cgroup *parent_memcg = NULL;
|
2009-01-08 09:08:07 +07:00
|
|
|
|
|
|
|
if (parent)
|
2011-11-03 03:38:15 +07:00
|
|
|
parent_memcg = mem_cgroup_from_cont(parent);
|
2009-01-08 09:08:07 +07:00
|
|
|
|
|
|
|
cgroup_lock();
|
memcg: fix bad behavior in use_hierarchy file
I have an application that does the following:
* copy the state of all controllers attached to a hierarchy
* replicate it as a child of the current level.
I would expect writes to the files to mostly succeed, since they are
inheriting sane values from parents.
But that is not the case for use_hierarchy. If it is set to 0, we succeed
ok. If we're set to 1, the value of the file is automatically set to 1 in
the children, but if userspace tries to write the very same 1, it will
fail. That same situation happens if we set use_hierarchy, create a
child, and then try to write 1 again.
Now, there is no reason whatsoever for failing to write a value that is
already there. It doesn't even match the comments, that states:
/* If parent's use_hierarchy is set, we can't make any modifications
* in the child subtrees...
since we are not changing anything.
So test the new value against the one we're storing, and automatically
return 0 if we're not proposing a change.
Signed-off-by: Glauber Costa <glommer@parallels.com>
Cc: Dhaval Giani <dhaval.giani@gmail.com>
Acked-by: Michal Hocko <mhocko@suse.cz>
Cc: Kamezawa Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-08-01 06:43:07 +07:00
|
|
|
|
|
|
|
if (memcg->use_hierarchy == val)
|
|
|
|
goto out;
|
|
|
|
|
2009-01-08 09:08:07 +07:00
|
|
|
/*
|
tree-wide: fix assorted typos all over the place
That is "success", "unknown", "through", "performance", "[re|un]mapping"
, "access", "default", "reasonable", "[con]currently", "temperature"
, "channel", "[un]used", "application", "example","hierarchy", "therefore"
, "[over|under]flow", "contiguous", "threshold", "enough" and others.
Signed-off-by: André Goddard Rosa <andre.goddard@gmail.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2009-11-14 22:09:05 +07:00
|
|
|
* If parent's use_hierarchy is set, we can't make any modifications
|
2009-01-08 09:08:07 +07:00
|
|
|
* in the child subtrees. If it is unset, then the change can
|
|
|
|
* occur, provided the current cgroup has no children.
|
|
|
|
*
|
|
|
|
* For the root cgroup, parent_mem is NULL, we allow value to be
|
|
|
|
* set if there are no children.
|
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
if ((!parent_memcg || !parent_memcg->use_hierarchy) &&
|
2009-01-08 09:08:07 +07:00
|
|
|
(val == 1 || val == 0)) {
|
|
|
|
if (list_empty(&cont->children))
|
2011-11-03 03:38:15 +07:00
|
|
|
memcg->use_hierarchy = val;
|
2009-01-08 09:08:07 +07:00
|
|
|
else
|
|
|
|
retval = -EBUSY;
|
|
|
|
} else
|
|
|
|
retval = -EINVAL;
|
memcg: fix bad behavior in use_hierarchy file
I have an application that does the following:
* copy the state of all controllers attached to a hierarchy
* replicate it as a child of the current level.
I would expect writes to the files to mostly succeed, since they are
inheriting sane values from parents.
But that is not the case for use_hierarchy. If it is set to 0, we succeed
ok. If we're set to 1, the value of the file is automatically set to 1 in
the children, but if userspace tries to write the very same 1, it will
fail. That same situation happens if we set use_hierarchy, create a
child, and then try to write 1 again.
Now, there is no reason whatsoever for failing to write a value that is
already there. It doesn't even match the comments, that states:
/* If parent's use_hierarchy is set, we can't make any modifications
* in the child subtrees...
since we are not changing anything.
So test the new value against the one we're storing, and automatically
return 0 if we're not proposing a change.
Signed-off-by: Glauber Costa <glommer@parallels.com>
Cc: Dhaval Giani <dhaval.giani@gmail.com>
Acked-by: Michal Hocko <mhocko@suse.cz>
Cc: Kamezawa Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-08-01 06:43:07 +07:00
|
|
|
|
|
|
|
out:
|
2009-01-08 09:08:07 +07:00
|
|
|
cgroup_unlock();
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2009-09-24 05:56:42 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static unsigned long mem_cgroup_recursive_stat(struct mem_cgroup *memcg,
|
2011-03-24 06:42:38 +07:00
|
|
|
enum mem_cgroup_stat_index idx)
|
2009-09-24 05:56:42 +07:00
|
|
|
{
|
2010-10-28 05:33:41 +07:00
|
|
|
struct mem_cgroup *iter;
|
2011-03-24 06:42:38 +07:00
|
|
|
long val = 0;
|
2009-09-24 05:56:42 +07:00
|
|
|
|
2011-03-24 06:42:38 +07:00
|
|
|
/* Per-cpu values can be negative, use a signed accumulator */
|
2011-11-03 03:38:15 +07:00
|
|
|
for_each_mem_cgroup_tree(iter, memcg)
|
2010-10-28 05:33:41 +07:00
|
|
|
val += mem_cgroup_read_stat(iter, idx);
|
|
|
|
|
|
|
|
if (val < 0) /* race ? */
|
|
|
|
val = 0;
|
|
|
|
return val;
|
2009-09-24 05:56:42 +07:00
|
|
|
}
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static inline u64 mem_cgroup_usage(struct mem_cgroup *memcg, bool swap)
|
2010-03-11 06:22:21 +07:00
|
|
|
{
|
2010-10-28 05:33:41 +07:00
|
|
|
u64 val;
|
2010-03-11 06:22:21 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
if (!mem_cgroup_is_root(memcg)) {
|
2010-03-11 06:22:21 +07:00
|
|
|
if (!swap)
|
2011-12-22 08:02:27 +07:00
|
|
|
return res_counter_read_u64(&memcg->res, RES_USAGE);
|
2010-03-11 06:22:21 +07:00
|
|
|
else
|
2011-12-22 08:02:27 +07:00
|
|
|
return res_counter_read_u64(&memcg->memsw, RES_USAGE);
|
2010-03-11 06:22:21 +07:00
|
|
|
}
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
val = mem_cgroup_recursive_stat(memcg, MEM_CGROUP_STAT_CACHE);
|
|
|
|
val += mem_cgroup_recursive_stat(memcg, MEM_CGROUP_STAT_RSS);
|
2010-03-11 06:22:21 +07:00
|
|
|
|
2010-10-28 05:33:41 +07:00
|
|
|
if (swap)
|
2012-08-01 06:41:38 +07:00
|
|
|
val += mem_cgroup_recursive_stat(memcg, MEM_CGROUP_STAT_SWAP);
|
2010-03-11 06:22:21 +07:00
|
|
|
|
|
|
|
return val << PAGE_SHIFT;
|
|
|
|
}
|
|
|
|
|
2012-04-02 02:09:55 +07:00
|
|
|
static ssize_t mem_cgroup_read(struct cgroup *cont, struct cftype *cft,
|
|
|
|
struct file *file, char __user *buf,
|
|
|
|
size_t nbytes, loff_t *ppos)
|
2008-02-07 15:13:50 +07:00
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
struct mem_cgroup *memcg = mem_cgroup_from_cont(cont);
|
2012-04-02 02:09:55 +07:00
|
|
|
char str[64];
|
2010-03-11 06:22:21 +07:00
|
|
|
u64 val;
|
2012-04-02 02:09:55 +07:00
|
|
|
int type, name, len;
|
2009-01-08 09:08:00 +07:00
|
|
|
|
|
|
|
type = MEMFILE_TYPE(cft->private);
|
|
|
|
name = MEMFILE_ATTR(cft->private);
|
2012-04-02 02:09:55 +07:00
|
|
|
|
|
|
|
if (!do_swap_account && type == _MEMSWAP)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2009-01-08 09:08:00 +07:00
|
|
|
switch (type) {
|
|
|
|
case _MEM:
|
2010-03-11 06:22:21 +07:00
|
|
|
if (name == RES_USAGE)
|
2011-11-03 03:38:15 +07:00
|
|
|
val = mem_cgroup_usage(memcg, false);
|
2010-03-11 06:22:21 +07:00
|
|
|
else
|
2011-11-03 03:38:15 +07:00
|
|
|
val = res_counter_read_u64(&memcg->res, name);
|
2009-01-08 09:08:00 +07:00
|
|
|
break;
|
|
|
|
case _MEMSWAP:
|
2010-03-11 06:22:21 +07:00
|
|
|
if (name == RES_USAGE)
|
2011-11-03 03:38:15 +07:00
|
|
|
val = mem_cgroup_usage(memcg, true);
|
2010-03-11 06:22:21 +07:00
|
|
|
else
|
2011-11-03 03:38:15 +07:00
|
|
|
val = res_counter_read_u64(&memcg->memsw, name);
|
2009-01-08 09:08:00 +07:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
BUG();
|
|
|
|
}
|
2012-04-02 02:09:55 +07:00
|
|
|
|
|
|
|
len = scnprintf(str, sizeof(str), "%llu\n", (unsigned long long)val);
|
|
|
|
return simple_read_from_buffer(buf, nbytes, ppos, str, len);
|
2008-02-07 15:13:50 +07:00
|
|
|
}
|
2008-07-25 15:47:20 +07:00
|
|
|
/*
|
|
|
|
* The user of this function is...
|
|
|
|
* RES_LIMIT.
|
|
|
|
*/
|
2008-07-25 15:47:04 +07:00
|
|
|
static int mem_cgroup_write(struct cgroup *cont, struct cftype *cft,
|
|
|
|
const char *buffer)
|
2008-02-07 15:13:50 +07:00
|
|
|
{
|
2008-07-25 15:47:20 +07:00
|
|
|
struct mem_cgroup *memcg = mem_cgroup_from_cont(cont);
|
2009-01-08 09:08:00 +07:00
|
|
|
int type, name;
|
2008-07-25 15:47:20 +07:00
|
|
|
unsigned long long val;
|
|
|
|
int ret;
|
|
|
|
|
2009-01-08 09:08:00 +07:00
|
|
|
type = MEMFILE_TYPE(cft->private);
|
|
|
|
name = MEMFILE_ATTR(cft->private);
|
2012-04-02 02:09:55 +07:00
|
|
|
|
|
|
|
if (!do_swap_account && type == _MEMSWAP)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2009-01-08 09:08:00 +07:00
|
|
|
switch (name) {
|
2008-07-25 15:47:20 +07:00
|
|
|
case RES_LIMIT:
|
2009-09-24 05:56:32 +07:00
|
|
|
if (mem_cgroup_is_root(memcg)) { /* Can't set limit on root */
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
2008-07-25 15:47:20 +07:00
|
|
|
/* This function does all necessary parse...reuse it */
|
|
|
|
ret = res_counter_memparse_write_strategy(buffer, &val);
|
2009-01-08 09:08:00 +07:00
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
if (type == _MEM)
|
2008-07-25 15:47:20 +07:00
|
|
|
ret = mem_cgroup_resize_limit(memcg, val);
|
2009-01-08 09:08:00 +07:00
|
|
|
else
|
|
|
|
ret = mem_cgroup_resize_memsw_limit(memcg, val);
|
2008-07-25 15:47:20 +07:00
|
|
|
break;
|
2009-09-24 05:56:36 +07:00
|
|
|
case RES_SOFT_LIMIT:
|
|
|
|
ret = res_counter_memparse_write_strategy(buffer, &val);
|
|
|
|
if (ret)
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
* For memsw, soft limits are hard to implement in terms
|
|
|
|
* of semantics, for now, we support soft limits for
|
|
|
|
* control without swap
|
|
|
|
*/
|
|
|
|
if (type == _MEM)
|
|
|
|
ret = res_counter_set_soft_limit(&memcg->res, val);
|
|
|
|
else
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
2008-07-25 15:47:20 +07:00
|
|
|
default:
|
|
|
|
ret = -EINVAL; /* should be BUG() ? */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return ret;
|
2008-02-07 15:13:50 +07:00
|
|
|
}
|
|
|
|
|
2009-01-08 09:08:26 +07:00
|
|
|
static void memcg_get_hierarchical_limit(struct mem_cgroup *memcg,
|
|
|
|
unsigned long long *mem_limit, unsigned long long *memsw_limit)
|
|
|
|
{
|
|
|
|
struct cgroup *cgroup;
|
|
|
|
unsigned long long min_limit, min_memsw_limit, tmp;
|
|
|
|
|
|
|
|
min_limit = res_counter_read_u64(&memcg->res, RES_LIMIT);
|
|
|
|
min_memsw_limit = res_counter_read_u64(&memcg->memsw, RES_LIMIT);
|
|
|
|
cgroup = memcg->css.cgroup;
|
|
|
|
if (!memcg->use_hierarchy)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
while (cgroup->parent) {
|
|
|
|
cgroup = cgroup->parent;
|
|
|
|
memcg = mem_cgroup_from_cont(cgroup);
|
|
|
|
if (!memcg->use_hierarchy)
|
|
|
|
break;
|
|
|
|
tmp = res_counter_read_u64(&memcg->res, RES_LIMIT);
|
|
|
|
min_limit = min(min_limit, tmp);
|
|
|
|
tmp = res_counter_read_u64(&memcg->memsw, RES_LIMIT);
|
|
|
|
min_memsw_limit = min(min_memsw_limit, tmp);
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
*mem_limit = min_limit;
|
|
|
|
*memsw_limit = min_memsw_limit;
|
|
|
|
}
|
|
|
|
|
2008-04-29 15:00:21 +07:00
|
|
|
static int mem_cgroup_reset(struct cgroup *cont, unsigned int event)
|
2008-04-29 15:00:17 +07:00
|
|
|
{
|
2012-04-02 02:09:55 +07:00
|
|
|
struct mem_cgroup *memcg = mem_cgroup_from_cont(cont);
|
2009-01-08 09:08:00 +07:00
|
|
|
int type, name;
|
2008-04-29 15:00:17 +07:00
|
|
|
|
2009-01-08 09:08:00 +07:00
|
|
|
type = MEMFILE_TYPE(event);
|
|
|
|
name = MEMFILE_ATTR(event);
|
2012-04-02 02:09:55 +07:00
|
|
|
|
|
|
|
if (!do_swap_account && type == _MEMSWAP)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2009-01-08 09:08:00 +07:00
|
|
|
switch (name) {
|
2008-04-29 15:00:21 +07:00
|
|
|
case RES_MAX_USAGE:
|
2009-01-08 09:08:00 +07:00
|
|
|
if (type == _MEM)
|
2011-11-03 03:38:15 +07:00
|
|
|
res_counter_reset_max(&memcg->res);
|
2009-01-08 09:08:00 +07:00
|
|
|
else
|
2011-11-03 03:38:15 +07:00
|
|
|
res_counter_reset_max(&memcg->memsw);
|
2008-04-29 15:00:21 +07:00
|
|
|
break;
|
|
|
|
case RES_FAILCNT:
|
2009-01-08 09:08:00 +07:00
|
|
|
if (type == _MEM)
|
2011-11-03 03:38:15 +07:00
|
|
|
res_counter_reset_failcnt(&memcg->res);
|
2009-01-08 09:08:00 +07:00
|
|
|
else
|
2011-11-03 03:38:15 +07:00
|
|
|
res_counter_reset_failcnt(&memcg->memsw);
|
2008-04-29 15:00:21 +07:00
|
|
|
break;
|
|
|
|
}
|
2009-09-24 05:56:37 +07:00
|
|
|
|
2008-04-29 15:00:20 +07:00
|
|
|
return 0;
|
2008-04-29 15:00:17 +07:00
|
|
|
}
|
|
|
|
|
2010-03-11 06:22:13 +07:00
|
|
|
static u64 mem_cgroup_move_charge_read(struct cgroup *cgrp,
|
|
|
|
struct cftype *cft)
|
|
|
|
{
|
|
|
|
return mem_cgroup_from_cont(cgrp)->move_charge_at_immigrate;
|
|
|
|
}
|
|
|
|
|
2010-03-11 06:22:17 +07:00
|
|
|
#ifdef CONFIG_MMU
|
2010-03-11 06:22:13 +07:00
|
|
|
static int mem_cgroup_move_charge_write(struct cgroup *cgrp,
|
|
|
|
struct cftype *cft, u64 val)
|
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
struct mem_cgroup *memcg = mem_cgroup_from_cont(cgrp);
|
2010-03-11 06:22:13 +07:00
|
|
|
|
|
|
|
if (val >= (1 << NR_MOVE_TYPE))
|
|
|
|
return -EINVAL;
|
|
|
|
/*
|
|
|
|
* We check this value several times in both in can_attach() and
|
|
|
|
* attach(), so we need cgroup lock to prevent this value from being
|
|
|
|
* inconsistent.
|
|
|
|
*/
|
|
|
|
cgroup_lock();
|
2011-11-03 03:38:15 +07:00
|
|
|
memcg->move_charge_at_immigrate = val;
|
2010-03-11 06:22:13 +07:00
|
|
|
cgroup_unlock();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2010-03-11 06:22:17 +07:00
|
|
|
#else
|
|
|
|
static int mem_cgroup_move_charge_write(struct cgroup *cgrp,
|
|
|
|
struct cftype *cft, u64 val)
|
|
|
|
{
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|
|
|
|
#endif
|
2010-03-11 06:22:13 +07:00
|
|
|
|
2011-05-27 06:25:37 +07:00
|
|
|
#ifdef CONFIG_NUMA
|
2012-08-01 06:43:09 +07:00
|
|
|
static int memcg_numa_stat_show(struct cgroup *cont, struct cftype *cft,
|
2012-05-30 05:07:06 +07:00
|
|
|
struct seq_file *m)
|
2011-05-27 06:25:37 +07:00
|
|
|
{
|
|
|
|
int nid;
|
|
|
|
unsigned long total_nr, file_nr, anon_nr, unevictable_nr;
|
|
|
|
unsigned long node_nr;
|
2012-03-22 06:34:18 +07:00
|
|
|
struct mem_cgroup *memcg = mem_cgroup_from_cont(cont);
|
2011-05-27 06:25:37 +07:00
|
|
|
|
2012-03-22 06:34:18 +07:00
|
|
|
total_nr = mem_cgroup_nr_lru_pages(memcg, LRU_ALL);
|
2011-05-27 06:25:37 +07:00
|
|
|
seq_printf(m, "total=%lu", total_nr);
|
|
|
|
for_each_node_state(nid, N_HIGH_MEMORY) {
|
2012-03-22 06:34:18 +07:00
|
|
|
node_nr = mem_cgroup_node_nr_lru_pages(memcg, nid, LRU_ALL);
|
2011-05-27 06:25:37 +07:00
|
|
|
seq_printf(m, " N%d=%lu", nid, node_nr);
|
|
|
|
}
|
|
|
|
seq_putc(m, '\n');
|
|
|
|
|
2012-03-22 06:34:18 +07:00
|
|
|
file_nr = mem_cgroup_nr_lru_pages(memcg, LRU_ALL_FILE);
|
2011-05-27 06:25:37 +07:00
|
|
|
seq_printf(m, "file=%lu", file_nr);
|
|
|
|
for_each_node_state(nid, N_HIGH_MEMORY) {
|
2012-03-22 06:34:18 +07:00
|
|
|
node_nr = mem_cgroup_node_nr_lru_pages(memcg, nid,
|
memcg: consolidate memory cgroup lru stat functions
In mm/memcontrol.c, there are many lru stat functions as..
mem_cgroup_zone_nr_lru_pages
mem_cgroup_node_nr_file_lru_pages
mem_cgroup_nr_file_lru_pages
mem_cgroup_node_nr_anon_lru_pages
mem_cgroup_nr_anon_lru_pages
mem_cgroup_node_nr_unevictable_lru_pages
mem_cgroup_nr_unevictable_lru_pages
mem_cgroup_node_nr_lru_pages
mem_cgroup_nr_lru_pages
mem_cgroup_get_local_zonestat
Some of them are under #ifdef MAX_NUMNODES >1 and others are not.
This seems bad. This patch consolidates all functions into
mem_cgroup_zone_nr_lru_pages()
mem_cgroup_node_nr_lru_pages()
mem_cgroup_nr_lru_pages()
For these functions, "which LRU?" information is passed by a mask.
example:
mem_cgroup_nr_lru_pages(mem, BIT(LRU_ACTIVE_ANON))
And I added some macro as ALL_LRU, ALL_LRU_FILE, ALL_LRU_ANON.
example:
mem_cgroup_nr_lru_pages(mem, ALL_LRU)
BTW, considering layout of NUMA memory placement of counters, this patch seems
to be better.
Now, when we gather all LRU information, we scan in following orer
for_each_lru -> for_each_node -> for_each_zone.
This means we'll touch cache lines in different node in turn.
After patch, we'll scan
for_each_node -> for_each_zone -> for_each_lru(mask)
Then, we'll gather information in the same cacheline at once.
[akpm@linux-foundation.org: fix warnigns, build error]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-07-27 06:08:22 +07:00
|
|
|
LRU_ALL_FILE);
|
2011-05-27 06:25:37 +07:00
|
|
|
seq_printf(m, " N%d=%lu", nid, node_nr);
|
|
|
|
}
|
|
|
|
seq_putc(m, '\n');
|
|
|
|
|
2012-03-22 06:34:18 +07:00
|
|
|
anon_nr = mem_cgroup_nr_lru_pages(memcg, LRU_ALL_ANON);
|
2011-05-27 06:25:37 +07:00
|
|
|
seq_printf(m, "anon=%lu", anon_nr);
|
|
|
|
for_each_node_state(nid, N_HIGH_MEMORY) {
|
2012-03-22 06:34:18 +07:00
|
|
|
node_nr = mem_cgroup_node_nr_lru_pages(memcg, nid,
|
memcg: consolidate memory cgroup lru stat functions
In mm/memcontrol.c, there are many lru stat functions as..
mem_cgroup_zone_nr_lru_pages
mem_cgroup_node_nr_file_lru_pages
mem_cgroup_nr_file_lru_pages
mem_cgroup_node_nr_anon_lru_pages
mem_cgroup_nr_anon_lru_pages
mem_cgroup_node_nr_unevictable_lru_pages
mem_cgroup_nr_unevictable_lru_pages
mem_cgroup_node_nr_lru_pages
mem_cgroup_nr_lru_pages
mem_cgroup_get_local_zonestat
Some of them are under #ifdef MAX_NUMNODES >1 and others are not.
This seems bad. This patch consolidates all functions into
mem_cgroup_zone_nr_lru_pages()
mem_cgroup_node_nr_lru_pages()
mem_cgroup_nr_lru_pages()
For these functions, "which LRU?" information is passed by a mask.
example:
mem_cgroup_nr_lru_pages(mem, BIT(LRU_ACTIVE_ANON))
And I added some macro as ALL_LRU, ALL_LRU_FILE, ALL_LRU_ANON.
example:
mem_cgroup_nr_lru_pages(mem, ALL_LRU)
BTW, considering layout of NUMA memory placement of counters, this patch seems
to be better.
Now, when we gather all LRU information, we scan in following orer
for_each_lru -> for_each_node -> for_each_zone.
This means we'll touch cache lines in different node in turn.
After patch, we'll scan
for_each_node -> for_each_zone -> for_each_lru(mask)
Then, we'll gather information in the same cacheline at once.
[akpm@linux-foundation.org: fix warnigns, build error]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-07-27 06:08:22 +07:00
|
|
|
LRU_ALL_ANON);
|
2011-05-27 06:25:37 +07:00
|
|
|
seq_printf(m, " N%d=%lu", nid, node_nr);
|
|
|
|
}
|
|
|
|
seq_putc(m, '\n');
|
|
|
|
|
2012-03-22 06:34:18 +07:00
|
|
|
unevictable_nr = mem_cgroup_nr_lru_pages(memcg, BIT(LRU_UNEVICTABLE));
|
2011-05-27 06:25:37 +07:00
|
|
|
seq_printf(m, "unevictable=%lu", unevictable_nr);
|
|
|
|
for_each_node_state(nid, N_HIGH_MEMORY) {
|
2012-03-22 06:34:18 +07:00
|
|
|
node_nr = mem_cgroup_node_nr_lru_pages(memcg, nid,
|
memcg: consolidate memory cgroup lru stat functions
In mm/memcontrol.c, there are many lru stat functions as..
mem_cgroup_zone_nr_lru_pages
mem_cgroup_node_nr_file_lru_pages
mem_cgroup_nr_file_lru_pages
mem_cgroup_node_nr_anon_lru_pages
mem_cgroup_nr_anon_lru_pages
mem_cgroup_node_nr_unevictable_lru_pages
mem_cgroup_nr_unevictable_lru_pages
mem_cgroup_node_nr_lru_pages
mem_cgroup_nr_lru_pages
mem_cgroup_get_local_zonestat
Some of them are under #ifdef MAX_NUMNODES >1 and others are not.
This seems bad. This patch consolidates all functions into
mem_cgroup_zone_nr_lru_pages()
mem_cgroup_node_nr_lru_pages()
mem_cgroup_nr_lru_pages()
For these functions, "which LRU?" information is passed by a mask.
example:
mem_cgroup_nr_lru_pages(mem, BIT(LRU_ACTIVE_ANON))
And I added some macro as ALL_LRU, ALL_LRU_FILE, ALL_LRU_ANON.
example:
mem_cgroup_nr_lru_pages(mem, ALL_LRU)
BTW, considering layout of NUMA memory placement of counters, this patch seems
to be better.
Now, when we gather all LRU information, we scan in following orer
for_each_lru -> for_each_node -> for_each_zone.
This means we'll touch cache lines in different node in turn.
After patch, we'll scan
for_each_node -> for_each_zone -> for_each_lru(mask)
Then, we'll gather information in the same cacheline at once.
[akpm@linux-foundation.org: fix warnigns, build error]
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-07-27 06:08:22 +07:00
|
|
|
BIT(LRU_UNEVICTABLE));
|
2011-05-27 06:25:37 +07:00
|
|
|
seq_printf(m, " N%d=%lu", nid, node_nr);
|
|
|
|
}
|
|
|
|
seq_putc(m, '\n');
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_NUMA */
|
|
|
|
|
2012-05-30 05:07:08 +07:00
|
|
|
static const char * const mem_cgroup_lru_names[] = {
|
|
|
|
"inactive_anon",
|
|
|
|
"active_anon",
|
|
|
|
"inactive_file",
|
|
|
|
"active_file",
|
|
|
|
"unevictable",
|
|
|
|
};
|
|
|
|
|
|
|
|
static inline void mem_cgroup_lru_names_not_uptodate(void)
|
|
|
|
{
|
|
|
|
BUILD_BUG_ON(ARRAY_SIZE(mem_cgroup_lru_names) != NR_LRU_LISTS);
|
|
|
|
}
|
|
|
|
|
2012-08-01 06:43:09 +07:00
|
|
|
static int memcg_stat_show(struct cgroup *cont, struct cftype *cft,
|
2012-05-30 05:07:06 +07:00
|
|
|
struct seq_file *m)
|
2008-02-07 15:14:25 +07:00
|
|
|
{
|
2012-03-22 06:34:18 +07:00
|
|
|
struct mem_cgroup *memcg = mem_cgroup_from_cont(cont);
|
2012-05-30 05:07:08 +07:00
|
|
|
struct mem_cgroup *mi;
|
|
|
|
unsigned int i;
|
2011-05-27 06:25:37 +07:00
|
|
|
|
2012-05-30 05:07:08 +07:00
|
|
|
for (i = 0; i < MEM_CGROUP_STAT_NSTATS; i++) {
|
2012-08-01 06:41:38 +07:00
|
|
|
if (i == MEM_CGROUP_STAT_SWAP && !do_swap_account)
|
2009-09-24 05:56:43 +07:00
|
|
|
continue;
|
2012-05-30 05:07:08 +07:00
|
|
|
seq_printf(m, "%s %ld\n", mem_cgroup_stat_names[i],
|
|
|
|
mem_cgroup_read_stat(memcg, i) * PAGE_SIZE);
|
2009-09-24 05:56:43 +07:00
|
|
|
}
|
2008-10-19 10:26:40 +07:00
|
|
|
|
2012-05-30 05:07:08 +07:00
|
|
|
for (i = 0; i < MEM_CGROUP_EVENTS_NSTATS; i++)
|
|
|
|
seq_printf(m, "%s %lu\n", mem_cgroup_events_names[i],
|
|
|
|
mem_cgroup_read_events(memcg, i));
|
|
|
|
|
|
|
|
for (i = 0; i < NR_LRU_LISTS; i++)
|
|
|
|
seq_printf(m, "%s %lu\n", mem_cgroup_lru_names[i],
|
|
|
|
mem_cgroup_nr_lru_pages(memcg, BIT(i)) * PAGE_SIZE);
|
|
|
|
|
2009-04-03 06:57:35 +07:00
|
|
|
/* Hierarchical information */
|
2009-01-08 09:08:26 +07:00
|
|
|
{
|
|
|
|
unsigned long long limit, memsw_limit;
|
2012-03-22 06:34:18 +07:00
|
|
|
memcg_get_hierarchical_limit(memcg, &limit, &memsw_limit);
|
2012-05-30 05:07:06 +07:00
|
|
|
seq_printf(m, "hierarchical_memory_limit %llu\n", limit);
|
2009-01-08 09:08:26 +07:00
|
|
|
if (do_swap_account)
|
2012-05-30 05:07:06 +07:00
|
|
|
seq_printf(m, "hierarchical_memsw_limit %llu\n",
|
|
|
|
memsw_limit);
|
2009-01-08 09:08:26 +07:00
|
|
|
}
|
2009-01-08 09:08:22 +07:00
|
|
|
|
2012-05-30 05:07:08 +07:00
|
|
|
for (i = 0; i < MEM_CGROUP_STAT_NSTATS; i++) {
|
|
|
|
long long val = 0;
|
|
|
|
|
2012-08-01 06:41:38 +07:00
|
|
|
if (i == MEM_CGROUP_STAT_SWAP && !do_swap_account)
|
2009-09-24 05:56:43 +07:00
|
|
|
continue;
|
2012-05-30 05:07:08 +07:00
|
|
|
for_each_mem_cgroup_tree(mi, memcg)
|
|
|
|
val += mem_cgroup_read_stat(mi, i) * PAGE_SIZE;
|
|
|
|
seq_printf(m, "total_%s %lld\n", mem_cgroup_stat_names[i], val);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < MEM_CGROUP_EVENTS_NSTATS; i++) {
|
|
|
|
unsigned long long val = 0;
|
|
|
|
|
|
|
|
for_each_mem_cgroup_tree(mi, memcg)
|
|
|
|
val += mem_cgroup_read_events(mi, i);
|
|
|
|
seq_printf(m, "total_%s %llu\n",
|
|
|
|
mem_cgroup_events_names[i], val);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < NR_LRU_LISTS; i++) {
|
|
|
|
unsigned long long val = 0;
|
|
|
|
|
|
|
|
for_each_mem_cgroup_tree(mi, memcg)
|
|
|
|
val += mem_cgroup_nr_lru_pages(mi, BIT(i)) * PAGE_SIZE;
|
|
|
|
seq_printf(m, "total_%s %llu\n", mem_cgroup_lru_names[i], val);
|
2009-09-24 05:56:43 +07:00
|
|
|
}
|
2009-04-03 06:57:35 +07:00
|
|
|
|
2009-01-08 09:08:22 +07:00
|
|
|
#ifdef CONFIG_DEBUG_VM
|
|
|
|
{
|
|
|
|
int nid, zid;
|
|
|
|
struct mem_cgroup_per_zone *mz;
|
2012-05-30 05:06:53 +07:00
|
|
|
struct zone_reclaim_stat *rstat;
|
2009-01-08 09:08:22 +07:00
|
|
|
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++) {
|
2012-03-22 06:34:18 +07:00
|
|
|
mz = mem_cgroup_zoneinfo(memcg, nid, zid);
|
2012-05-30 05:06:53 +07:00
|
|
|
rstat = &mz->lruvec.reclaim_stat;
|
2009-01-08 09:08:22 +07:00
|
|
|
|
2012-05-30 05:06:53 +07:00
|
|
|
recent_rotated[0] += rstat->recent_rotated[0];
|
|
|
|
recent_rotated[1] += rstat->recent_rotated[1];
|
|
|
|
recent_scanned[0] += rstat->recent_scanned[0];
|
|
|
|
recent_scanned[1] += rstat->recent_scanned[1];
|
2009-01-08 09:08:22 +07:00
|
|
|
}
|
2012-05-30 05:07:06 +07:00
|
|
|
seq_printf(m, "recent_rotated_anon %lu\n", recent_rotated[0]);
|
|
|
|
seq_printf(m, "recent_rotated_file %lu\n", recent_rotated[1]);
|
|
|
|
seq_printf(m, "recent_scanned_anon %lu\n", recent_scanned[0]);
|
|
|
|
seq_printf(m, "recent_scanned_file %lu\n", recent_scanned[1]);
|
2009-01-08 09:08:22 +07:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-02-07 15:14:25 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-01-08 09:08:24 +07:00
|
|
|
static u64 mem_cgroup_swappiness_read(struct cgroup *cgrp, struct cftype *cft)
|
|
|
|
{
|
|
|
|
struct mem_cgroup *memcg = mem_cgroup_from_cont(cgrp);
|
|
|
|
|
2011-07-27 06:08:21 +07:00
|
|
|
return mem_cgroup_swappiness(memcg);
|
2009-01-08 09:08:24 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2009-01-16 04:51:26 +07:00
|
|
|
|
2009-01-08 09:08:24 +07:00
|
|
|
if (val > 100)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (cgrp->parent == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
parent = mem_cgroup_from_cont(cgrp->parent);
|
2009-01-16 04:51:26 +07:00
|
|
|
|
|
|
|
cgroup_lock();
|
|
|
|
|
2009-01-08 09:08:24 +07:00
|
|
|
/* If under hierarchy, only empty-root can set this value */
|
|
|
|
if ((parent->use_hierarchy) ||
|
2009-01-16 04:51:26 +07:00
|
|
|
(memcg->use_hierarchy && !list_empty(&cgrp->children))) {
|
|
|
|
cgroup_unlock();
|
2009-01-08 09:08:24 +07:00
|
|
|
return -EINVAL;
|
2009-01-16 04:51:26 +07:00
|
|
|
}
|
2009-01-08 09:08:24 +07:00
|
|
|
|
|
|
|
memcg->swappiness = val;
|
|
|
|
|
2009-01-16 04:51:26 +07:00
|
|
|
cgroup_unlock();
|
|
|
|
|
2009-01-08 09:08:24 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-03-11 06:22:24 +07:00
|
|
|
static void __mem_cgroup_threshold(struct mem_cgroup *memcg, bool swap)
|
|
|
|
{
|
|
|
|
struct mem_cgroup_threshold_ary *t;
|
|
|
|
u64 usage;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
if (!swap)
|
2010-05-27 04:42:47 +07:00
|
|
|
t = rcu_dereference(memcg->thresholds.primary);
|
2010-03-11 06:22:24 +07:00
|
|
|
else
|
2010-05-27 04:42:47 +07:00
|
|
|
t = rcu_dereference(memcg->memsw_thresholds.primary);
|
2010-03-11 06:22:24 +07:00
|
|
|
|
|
|
|
if (!t)
|
|
|
|
goto unlock;
|
|
|
|
|
|
|
|
usage = mem_cgroup_usage(memcg, swap);
|
|
|
|
|
|
|
|
/*
|
2012-05-30 05:06:57 +07:00
|
|
|
* current_threshold points to threshold just below or equal to usage.
|
2010-03-11 06:22:24 +07:00
|
|
|
* If it's not true, a threshold was crossed after last
|
|
|
|
* call of __mem_cgroup_threshold().
|
|
|
|
*/
|
2010-05-27 04:42:42 +07:00
|
|
|
i = t->current_threshold;
|
2010-03-11 06:22:24 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Iterate backward over array of thresholds starting from
|
|
|
|
* current_threshold and check if a threshold is crossed.
|
|
|
|
* If none of thresholds below usage is crossed, we read
|
|
|
|
* only one element of the array here.
|
|
|
|
*/
|
|
|
|
for (; i >= 0 && unlikely(t->entries[i].threshold > usage); i--)
|
|
|
|
eventfd_signal(t->entries[i].eventfd, 1);
|
|
|
|
|
|
|
|
/* i = current_threshold + 1 */
|
|
|
|
i++;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Iterate forward over array of thresholds starting from
|
|
|
|
* current_threshold+1 and check if a threshold is crossed.
|
|
|
|
* If none of thresholds above usage is crossed, we read
|
|
|
|
* only one element of the array here.
|
|
|
|
*/
|
|
|
|
for (; i < t->size && unlikely(t->entries[i].threshold <= usage); i++)
|
|
|
|
eventfd_signal(t->entries[i].eventfd, 1);
|
|
|
|
|
|
|
|
/* Update current_threshold */
|
2010-05-27 04:42:42 +07:00
|
|
|
t->current_threshold = i - 1;
|
2010-03-11 06:22:24 +07:00
|
|
|
unlock:
|
|
|
|
rcu_read_unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mem_cgroup_threshold(struct mem_cgroup *memcg)
|
|
|
|
{
|
2010-10-08 02:59:27 +07:00
|
|
|
while (memcg) {
|
|
|
|
__mem_cgroup_threshold(memcg, false);
|
|
|
|
if (do_swap_account)
|
|
|
|
__mem_cgroup_threshold(memcg, true);
|
|
|
|
|
|
|
|
memcg = parent_mem_cgroup(memcg);
|
|
|
|
}
|
2010-03-11 06:22:24 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int compare_thresholds(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
const struct mem_cgroup_threshold *_a = a;
|
|
|
|
const struct mem_cgroup_threshold *_b = b;
|
|
|
|
|
|
|
|
return _a->threshold - _b->threshold;
|
|
|
|
}
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static int mem_cgroup_oom_notify_cb(struct mem_cgroup *memcg)
|
2010-05-27 04:42:36 +07:00
|
|
|
{
|
|
|
|
struct mem_cgroup_eventfd_list *ev;
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
list_for_each_entry(ev, &memcg->oom_notify, list)
|
2010-05-27 04:42:36 +07:00
|
|
|
eventfd_signal(ev->eventfd, 1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static void mem_cgroup_oom_notify(struct mem_cgroup *memcg)
|
2010-05-27 04:42:36 +07:00
|
|
|
{
|
2010-10-28 05:33:41 +07:00
|
|
|
struct mem_cgroup *iter;
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
for_each_mem_cgroup_tree(iter, memcg)
|
2010-10-28 05:33:41 +07:00
|
|
|
mem_cgroup_oom_notify_cb(iter);
|
2010-05-27 04:42:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mem_cgroup_usage_register_event(struct cgroup *cgrp,
|
|
|
|
struct cftype *cft, struct eventfd_ctx *eventfd, const char *args)
|
2010-03-11 06:22:24 +07:00
|
|
|
{
|
|
|
|
struct mem_cgroup *memcg = mem_cgroup_from_cont(cgrp);
|
2010-05-27 04:42:47 +07:00
|
|
|
struct mem_cgroup_thresholds *thresholds;
|
|
|
|
struct mem_cgroup_threshold_ary *new;
|
2010-03-11 06:22:24 +07:00
|
|
|
int type = MEMFILE_TYPE(cft->private);
|
|
|
|
u64 threshold, usage;
|
2010-05-27 04:42:47 +07:00
|
|
|
int i, size, ret;
|
2010-03-11 06:22:24 +07:00
|
|
|
|
|
|
|
ret = res_counter_memparse_write_strategy(args, &threshold);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
mutex_lock(&memcg->thresholds_lock);
|
2010-05-27 04:42:47 +07:00
|
|
|
|
2010-03-11 06:22:24 +07:00
|
|
|
if (type == _MEM)
|
2010-05-27 04:42:47 +07:00
|
|
|
thresholds = &memcg->thresholds;
|
2010-03-11 06:22:24 +07:00
|
|
|
else if (type == _MEMSWAP)
|
2010-05-27 04:42:47 +07:00
|
|
|
thresholds = &memcg->memsw_thresholds;
|
2010-03-11 06:22:24 +07:00
|
|
|
else
|
|
|
|
BUG();
|
|
|
|
|
|
|
|
usage = mem_cgroup_usage(memcg, type == _MEMSWAP);
|
|
|
|
|
|
|
|
/* Check if a threshold crossed before adding a new one */
|
2010-05-27 04:42:47 +07:00
|
|
|
if (thresholds->primary)
|
2010-03-11 06:22:24 +07:00
|
|
|
__mem_cgroup_threshold(memcg, type == _MEMSWAP);
|
|
|
|
|
2010-05-27 04:42:47 +07:00
|
|
|
size = thresholds->primary ? thresholds->primary->size + 1 : 1;
|
2010-03-11 06:22:24 +07:00
|
|
|
|
|
|
|
/* Allocate memory for new array of thresholds */
|
2010-05-27 04:42:47 +07:00
|
|
|
new = kmalloc(sizeof(*new) + size * sizeof(struct mem_cgroup_threshold),
|
2010-03-11 06:22:24 +07:00
|
|
|
GFP_KERNEL);
|
2010-05-27 04:42:47 +07:00
|
|
|
if (!new) {
|
2010-03-11 06:22:24 +07:00
|
|
|
ret = -ENOMEM;
|
|
|
|
goto unlock;
|
|
|
|
}
|
2010-05-27 04:42:47 +07:00
|
|
|
new->size = size;
|
2010-03-11 06:22:24 +07:00
|
|
|
|
|
|
|
/* Copy thresholds (if any) to new array */
|
2010-05-27 04:42:47 +07:00
|
|
|
if (thresholds->primary) {
|
|
|
|
memcpy(new->entries, thresholds->primary->entries, (size - 1) *
|
2010-03-11 06:22:24 +07:00
|
|
|
sizeof(struct mem_cgroup_threshold));
|
2010-05-27 04:42:47 +07:00
|
|
|
}
|
|
|
|
|
2010-03-11 06:22:24 +07:00
|
|
|
/* Add new threshold */
|
2010-05-27 04:42:47 +07:00
|
|
|
new->entries[size - 1].eventfd = eventfd;
|
|
|
|
new->entries[size - 1].threshold = threshold;
|
2010-03-11 06:22:24 +07:00
|
|
|
|
|
|
|
/* Sort thresholds. Registering of new threshold isn't time-critical */
|
2010-05-27 04:42:47 +07:00
|
|
|
sort(new->entries, size, sizeof(struct mem_cgroup_threshold),
|
2010-03-11 06:22:24 +07:00
|
|
|
compare_thresholds, NULL);
|
|
|
|
|
|
|
|
/* Find current threshold */
|
2010-05-27 04:42:47 +07:00
|
|
|
new->current_threshold = -1;
|
2010-03-11 06:22:24 +07:00
|
|
|
for (i = 0; i < size; i++) {
|
2012-05-30 05:06:57 +07:00
|
|
|
if (new->entries[i].threshold <= usage) {
|
2010-03-11 06:22:24 +07:00
|
|
|
/*
|
2010-05-27 04:42:47 +07:00
|
|
|
* new->current_threshold will not be used until
|
|
|
|
* rcu_assign_pointer(), so it's safe to increment
|
2010-03-11 06:22:24 +07:00
|
|
|
* it here.
|
|
|
|
*/
|
2010-05-27 04:42:47 +07:00
|
|
|
++new->current_threshold;
|
2012-05-30 05:06:57 +07:00
|
|
|
} else
|
|
|
|
break;
|
2010-03-11 06:22:24 +07:00
|
|
|
}
|
|
|
|
|
2010-05-27 04:42:47 +07:00
|
|
|
/* Free old spare buffer and save old primary buffer as spare */
|
|
|
|
kfree(thresholds->spare);
|
|
|
|
thresholds->spare = thresholds->primary;
|
|
|
|
|
|
|
|
rcu_assign_pointer(thresholds->primary, new);
|
2010-03-11 06:22:24 +07:00
|
|
|
|
2010-05-27 04:42:46 +07:00
|
|
|
/* To be sure that nobody uses thresholds */
|
2010-03-11 06:22:24 +07:00
|
|
|
synchronize_rcu();
|
|
|
|
|
|
|
|
unlock:
|
|
|
|
mutex_unlock(&memcg->thresholds_lock);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-05-27 04:42:46 +07:00
|
|
|
static void mem_cgroup_usage_unregister_event(struct cgroup *cgrp,
|
2010-05-27 04:42:36 +07:00
|
|
|
struct cftype *cft, struct eventfd_ctx *eventfd)
|
2010-03-11 06:22:24 +07:00
|
|
|
{
|
|
|
|
struct mem_cgroup *memcg = mem_cgroup_from_cont(cgrp);
|
2010-05-27 04:42:47 +07:00
|
|
|
struct mem_cgroup_thresholds *thresholds;
|
|
|
|
struct mem_cgroup_threshold_ary *new;
|
2010-03-11 06:22:24 +07:00
|
|
|
int type = MEMFILE_TYPE(cft->private);
|
|
|
|
u64 usage;
|
2010-05-27 04:42:47 +07:00
|
|
|
int i, j, size;
|
2010-03-11 06:22:24 +07:00
|
|
|
|
|
|
|
mutex_lock(&memcg->thresholds_lock);
|
|
|
|
if (type == _MEM)
|
2010-05-27 04:42:47 +07:00
|
|
|
thresholds = &memcg->thresholds;
|
2010-03-11 06:22:24 +07:00
|
|
|
else if (type == _MEMSWAP)
|
2010-05-27 04:42:47 +07:00
|
|
|
thresholds = &memcg->memsw_thresholds;
|
2010-03-11 06:22:24 +07:00
|
|
|
else
|
|
|
|
BUG();
|
|
|
|
|
mm: memcg: Correct unregistring of events attached to the same eventfd
There is an issue when memcg unregisters events that were attached to
the same eventfd:
- On the first call mem_cgroup_usage_unregister_event() removes all
events attached to a given eventfd, and if there were no events left,
thresholds->primary would become NULL;
- Since there were several events registered, cgroups core will call
mem_cgroup_usage_unregister_event() again, but now kernel will oops,
as the function doesn't expect that threshold->primary may be NULL.
That's a good question whether mem_cgroup_usage_unregister_event()
should actually remove all events in one go, but nowadays it can't
do any better as cftype->unregister_event callback doesn't pass
any private event-associated cookie. So, let's fix the issue by
simply checking for threshold->primary.
FWIW, w/o the patch the following oops may be observed:
BUG: unable to handle kernel NULL pointer dereference at 0000000000000004
IP: [<ffffffff810be32c>] mem_cgroup_usage_unregister_event+0x9c/0x1f0
Pid: 574, comm: kworker/0:2 Not tainted 3.3.0-rc4+ #9 Bochs Bochs
RIP: 0010:[<ffffffff810be32c>] [<ffffffff810be32c>] mem_cgroup_usage_unregister_event+0x9c/0x1f0
RSP: 0018:ffff88001d0b9d60 EFLAGS: 00010246
Process kworker/0:2 (pid: 574, threadinfo ffff88001d0b8000, task ffff88001de91cc0)
Call Trace:
[<ffffffff8107092b>] cgroup_event_remove+0x2b/0x60
[<ffffffff8103db94>] process_one_work+0x174/0x450
[<ffffffff8103e413>] worker_thread+0x123/0x2d0
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Anton Vorontsov <anton.vorontsov@linaro.org>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Michal Hocko <mhocko@suse.cz>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-02-24 08:14:46 +07:00
|
|
|
if (!thresholds->primary)
|
|
|
|
goto unlock;
|
|
|
|
|
2010-03-11 06:22:24 +07:00
|
|
|
usage = mem_cgroup_usage(memcg, type == _MEMSWAP);
|
|
|
|
|
|
|
|
/* Check if a threshold crossed before removing */
|
|
|
|
__mem_cgroup_threshold(memcg, type == _MEMSWAP);
|
|
|
|
|
|
|
|
/* Calculate new number of threshold */
|
2010-05-27 04:42:47 +07:00
|
|
|
size = 0;
|
|
|
|
for (i = 0; i < thresholds->primary->size; i++) {
|
|
|
|
if (thresholds->primary->entries[i].eventfd != eventfd)
|
2010-03-11 06:22:24 +07:00
|
|
|
size++;
|
|
|
|
}
|
|
|
|
|
2010-05-27 04:42:47 +07:00
|
|
|
new = thresholds->spare;
|
2010-05-27 04:42:46 +07:00
|
|
|
|
2010-03-11 06:22:24 +07:00
|
|
|
/* Set thresholds array to NULL if we don't have thresholds */
|
|
|
|
if (!size) {
|
2010-05-27 04:42:47 +07:00
|
|
|
kfree(new);
|
|
|
|
new = NULL;
|
2010-05-27 04:42:46 +07:00
|
|
|
goto swap_buffers;
|
2010-03-11 06:22:24 +07:00
|
|
|
}
|
|
|
|
|
2010-05-27 04:42:47 +07:00
|
|
|
new->size = size;
|
2010-03-11 06:22:24 +07:00
|
|
|
|
|
|
|
/* Copy thresholds and find current threshold */
|
2010-05-27 04:42:47 +07:00
|
|
|
new->current_threshold = -1;
|
|
|
|
for (i = 0, j = 0; i < thresholds->primary->size; i++) {
|
|
|
|
if (thresholds->primary->entries[i].eventfd == eventfd)
|
2010-03-11 06:22:24 +07:00
|
|
|
continue;
|
|
|
|
|
2010-05-27 04:42:47 +07:00
|
|
|
new->entries[j] = thresholds->primary->entries[i];
|
2012-05-30 05:06:57 +07:00
|
|
|
if (new->entries[j].threshold <= usage) {
|
2010-03-11 06:22:24 +07:00
|
|
|
/*
|
2010-05-27 04:42:47 +07:00
|
|
|
* new->current_threshold will not be used
|
2010-03-11 06:22:24 +07:00
|
|
|
* until rcu_assign_pointer(), so it's safe to increment
|
|
|
|
* it here.
|
|
|
|
*/
|
2010-05-27 04:42:47 +07:00
|
|
|
++new->current_threshold;
|
2010-03-11 06:22:24 +07:00
|
|
|
}
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
|
2010-05-27 04:42:46 +07:00
|
|
|
swap_buffers:
|
2010-05-27 04:42:47 +07:00
|
|
|
/* Swap primary and spare array */
|
|
|
|
thresholds->spare = thresholds->primary;
|
2012-05-11 03:01:45 +07:00
|
|
|
/* If all events are unregistered, free the spare array */
|
|
|
|
if (!new) {
|
|
|
|
kfree(thresholds->spare);
|
|
|
|
thresholds->spare = NULL;
|
|
|
|
}
|
|
|
|
|
2010-05-27 04:42:47 +07:00
|
|
|
rcu_assign_pointer(thresholds->primary, new);
|
2010-03-11 06:22:24 +07:00
|
|
|
|
2010-05-27 04:42:46 +07:00
|
|
|
/* To be sure that nobody uses thresholds */
|
2010-03-11 06:22:24 +07:00
|
|
|
synchronize_rcu();
|
mm: memcg: Correct unregistring of events attached to the same eventfd
There is an issue when memcg unregisters events that were attached to
the same eventfd:
- On the first call mem_cgroup_usage_unregister_event() removes all
events attached to a given eventfd, and if there were no events left,
thresholds->primary would become NULL;
- Since there were several events registered, cgroups core will call
mem_cgroup_usage_unregister_event() again, but now kernel will oops,
as the function doesn't expect that threshold->primary may be NULL.
That's a good question whether mem_cgroup_usage_unregister_event()
should actually remove all events in one go, but nowadays it can't
do any better as cftype->unregister_event callback doesn't pass
any private event-associated cookie. So, let's fix the issue by
simply checking for threshold->primary.
FWIW, w/o the patch the following oops may be observed:
BUG: unable to handle kernel NULL pointer dereference at 0000000000000004
IP: [<ffffffff810be32c>] mem_cgroup_usage_unregister_event+0x9c/0x1f0
Pid: 574, comm: kworker/0:2 Not tainted 3.3.0-rc4+ #9 Bochs Bochs
RIP: 0010:[<ffffffff810be32c>] [<ffffffff810be32c>] mem_cgroup_usage_unregister_event+0x9c/0x1f0
RSP: 0018:ffff88001d0b9d60 EFLAGS: 00010246
Process kworker/0:2 (pid: 574, threadinfo ffff88001d0b8000, task ffff88001de91cc0)
Call Trace:
[<ffffffff8107092b>] cgroup_event_remove+0x2b/0x60
[<ffffffff8103db94>] process_one_work+0x174/0x450
[<ffffffff8103e413>] worker_thread+0x123/0x2d0
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Anton Vorontsov <anton.vorontsov@linaro.org>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Kirill A. Shutemov <kirill@shutemov.name>
Cc: Michal Hocko <mhocko@suse.cz>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-02-24 08:14:46 +07:00
|
|
|
unlock:
|
2010-03-11 06:22:24 +07:00
|
|
|
mutex_unlock(&memcg->thresholds_lock);
|
|
|
|
}
|
2009-01-08 09:07:55 +07:00
|
|
|
|
2010-05-27 04:42:36 +07:00
|
|
|
static int mem_cgroup_oom_register_event(struct cgroup *cgrp,
|
|
|
|
struct cftype *cft, struct eventfd_ctx *eventfd, const char *args)
|
|
|
|
{
|
|
|
|
struct mem_cgroup *memcg = mem_cgroup_from_cont(cgrp);
|
|
|
|
struct mem_cgroup_eventfd_list *event;
|
|
|
|
int type = MEMFILE_TYPE(cft->private);
|
|
|
|
|
|
|
|
BUG_ON(type != _OOM_TYPE);
|
|
|
|
event = kmalloc(sizeof(*event), GFP_KERNEL);
|
|
|
|
if (!event)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2011-07-27 06:08:24 +07:00
|
|
|
spin_lock(&memcg_oom_lock);
|
2010-05-27 04:42:36 +07:00
|
|
|
|
|
|
|
event->eventfd = eventfd;
|
|
|
|
list_add(&event->list, &memcg->oom_notify);
|
|
|
|
|
|
|
|
/* already in OOM ? */
|
2011-07-27 06:08:23 +07:00
|
|
|
if (atomic_read(&memcg->under_oom))
|
2010-05-27 04:42:36 +07:00
|
|
|
eventfd_signal(eventfd, 1);
|
2011-07-27 06:08:24 +07:00
|
|
|
spin_unlock(&memcg_oom_lock);
|
2010-05-27 04:42:36 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-05-27 04:42:46 +07:00
|
|
|
static void mem_cgroup_oom_unregister_event(struct cgroup *cgrp,
|
2010-05-27 04:42:36 +07:00
|
|
|
struct cftype *cft, struct eventfd_ctx *eventfd)
|
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
struct mem_cgroup *memcg = mem_cgroup_from_cont(cgrp);
|
2010-05-27 04:42:36 +07:00
|
|
|
struct mem_cgroup_eventfd_list *ev, *tmp;
|
|
|
|
int type = MEMFILE_TYPE(cft->private);
|
|
|
|
|
|
|
|
BUG_ON(type != _OOM_TYPE);
|
|
|
|
|
2011-07-27 06:08:24 +07:00
|
|
|
spin_lock(&memcg_oom_lock);
|
2010-05-27 04:42:36 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
list_for_each_entry_safe(ev, tmp, &memcg->oom_notify, list) {
|
2010-05-27 04:42:36 +07:00
|
|
|
if (ev->eventfd == eventfd) {
|
|
|
|
list_del(&ev->list);
|
|
|
|
kfree(ev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-27 06:08:24 +07:00
|
|
|
spin_unlock(&memcg_oom_lock);
|
2010-05-27 04:42:36 +07:00
|
|
|
}
|
|
|
|
|
2010-05-27 04:42:37 +07:00
|
|
|
static int mem_cgroup_oom_control_read(struct cgroup *cgrp,
|
|
|
|
struct cftype *cft, struct cgroup_map_cb *cb)
|
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
struct mem_cgroup *memcg = mem_cgroup_from_cont(cgrp);
|
2010-05-27 04:42:37 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
cb->fill(cb, "oom_kill_disable", memcg->oom_kill_disable);
|
2010-05-27 04:42:37 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
if (atomic_read(&memcg->under_oom))
|
2010-05-27 04:42:37 +07:00
|
|
|
cb->fill(cb, "under_oom", 1);
|
|
|
|
else
|
|
|
|
cb->fill(cb, "under_oom", 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mem_cgroup_oom_control_write(struct cgroup *cgrp,
|
|
|
|
struct cftype *cft, u64 val)
|
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
struct mem_cgroup *memcg = mem_cgroup_from_cont(cgrp);
|
2010-05-27 04:42:37 +07:00
|
|
|
struct mem_cgroup *parent;
|
|
|
|
|
|
|
|
/* cannot set to root cgroup and only 0 and 1 are allowed */
|
|
|
|
if (!cgrp->parent || !((val == 0) || (val == 1)))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
parent = mem_cgroup_from_cont(cgrp->parent);
|
|
|
|
|
|
|
|
cgroup_lock();
|
|
|
|
/* oom-kill-disable is a flag for subhierarchy. */
|
|
|
|
if ((parent->use_hierarchy) ||
|
2011-11-03 03:38:15 +07:00
|
|
|
(memcg->use_hierarchy && !list_empty(&cgrp->children))) {
|
2010-05-27 04:42:37 +07:00
|
|
|
cgroup_unlock();
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2011-11-03 03:38:15 +07:00
|
|
|
memcg->oom_kill_disable = val;
|
2010-06-30 05:05:18 +07:00
|
|
|
if (!val)
|
2011-11-03 03:38:15 +07:00
|
|
|
memcg_oom_recover(memcg);
|
2010-05-27 04:42:37 +07:00
|
|
|
cgroup_unlock();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-08-01 06:43:02 +07:00
|
|
|
#ifdef CONFIG_MEMCG_KMEM
|
2012-04-10 05:36:34 +07:00
|
|
|
static int memcg_init_kmem(struct mem_cgroup *memcg, struct cgroup_subsys *ss)
|
2011-12-12 04:47:01 +07:00
|
|
|
{
|
2012-04-10 05:36:33 +07:00
|
|
|
return mem_cgroup_sockets_init(memcg, ss);
|
2011-12-12 04:47:01 +07:00
|
|
|
};
|
|
|
|
|
2012-04-10 05:36:33 +07:00
|
|
|
static void kmem_cgroup_destroy(struct mem_cgroup *memcg)
|
2011-12-12 04:47:04 +07:00
|
|
|
{
|
2012-04-10 05:36:33 +07:00
|
|
|
mem_cgroup_sockets_destroy(memcg);
|
2011-12-12 04:47:04 +07:00
|
|
|
}
|
2011-12-12 04:47:01 +07:00
|
|
|
#else
|
2012-04-10 05:36:34 +07:00
|
|
|
static int memcg_init_kmem(struct mem_cgroup *memcg, struct cgroup_subsys *ss)
|
2011-12-12 04:47:01 +07:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2011-12-12 04:47:04 +07:00
|
|
|
|
2012-04-10 05:36:33 +07:00
|
|
|
static void kmem_cgroup_destroy(struct mem_cgroup *memcg)
|
2011-12-12 04:47:04 +07:00
|
|
|
{
|
|
|
|
}
|
2011-12-12 04:47:01 +07:00
|
|
|
#endif
|
|
|
|
|
2008-02-07 15:13:50 +07:00
|
|
|
static struct cftype mem_cgroup_files[] = {
|
|
|
|
{
|
2008-02-07 15:13:57 +07:00
|
|
|
.name = "usage_in_bytes",
|
2009-01-08 09:08:00 +07:00
|
|
|
.private = MEMFILE_PRIVATE(_MEM, RES_USAGE),
|
2012-04-02 02:09:55 +07:00
|
|
|
.read = mem_cgroup_read,
|
2010-05-27 04:42:36 +07:00
|
|
|
.register_event = mem_cgroup_usage_register_event,
|
|
|
|
.unregister_event = mem_cgroup_usage_unregister_event,
|
2008-02-07 15:13:50 +07:00
|
|
|
},
|
2008-04-29 15:00:17 +07:00
|
|
|
{
|
|
|
|
.name = "max_usage_in_bytes",
|
2009-01-08 09:08:00 +07:00
|
|
|
.private = MEMFILE_PRIVATE(_MEM, RES_MAX_USAGE),
|
2008-04-29 15:00:21 +07:00
|
|
|
.trigger = mem_cgroup_reset,
|
2012-04-02 02:09:55 +07:00
|
|
|
.read = mem_cgroup_read,
|
2008-04-29 15:00:17 +07:00
|
|
|
},
|
2008-02-07 15:13:50 +07:00
|
|
|
{
|
2008-02-07 15:13:57 +07:00
|
|
|
.name = "limit_in_bytes",
|
2009-01-08 09:08:00 +07:00
|
|
|
.private = MEMFILE_PRIVATE(_MEM, RES_LIMIT),
|
2008-07-25 15:47:04 +07:00
|
|
|
.write_string = mem_cgroup_write,
|
2012-04-02 02:09:55 +07:00
|
|
|
.read = mem_cgroup_read,
|
2008-02-07 15:13:50 +07:00
|
|
|
},
|
2009-09-24 05:56:36 +07:00
|
|
|
{
|
|
|
|
.name = "soft_limit_in_bytes",
|
|
|
|
.private = MEMFILE_PRIVATE(_MEM, RES_SOFT_LIMIT),
|
|
|
|
.write_string = mem_cgroup_write,
|
2012-04-02 02:09:55 +07:00
|
|
|
.read = mem_cgroup_read,
|
2009-09-24 05:56:36 +07:00
|
|
|
},
|
2008-02-07 15:13:50 +07:00
|
|
|
{
|
|
|
|
.name = "failcnt",
|
2009-01-08 09:08:00 +07:00
|
|
|
.private = MEMFILE_PRIVATE(_MEM, RES_FAILCNT),
|
2008-04-29 15:00:21 +07:00
|
|
|
.trigger = mem_cgroup_reset,
|
2012-04-02 02:09:55 +07:00
|
|
|
.read = mem_cgroup_read,
|
2008-02-07 15:13:50 +07:00
|
|
|
},
|
2008-02-07 15:14:25 +07:00
|
|
|
{
|
|
|
|
.name = "stat",
|
2012-08-01 06:43:09 +07:00
|
|
|
.read_seq_string = memcg_stat_show,
|
2008-02-07 15:14:25 +07:00
|
|
|
},
|
2009-01-08 09:07:55 +07:00
|
|
|
{
|
|
|
|
.name = "force_empty",
|
|
|
|
.trigger = mem_cgroup_force_empty_write,
|
|
|
|
},
|
2009-01-08 09:08:07 +07:00
|
|
|
{
|
|
|
|
.name = "use_hierarchy",
|
|
|
|
.write_u64 = mem_cgroup_hierarchy_write,
|
|
|
|
.read_u64 = mem_cgroup_hierarchy_read,
|
|
|
|
},
|
2009-01-08 09:08:24 +07:00
|
|
|
{
|
|
|
|
.name = "swappiness",
|
|
|
|
.read_u64 = mem_cgroup_swappiness_read,
|
|
|
|
.write_u64 = mem_cgroup_swappiness_write,
|
|
|
|
},
|
2010-03-11 06:22:13 +07:00
|
|
|
{
|
|
|
|
.name = "move_charge_at_immigrate",
|
|
|
|
.read_u64 = mem_cgroup_move_charge_read,
|
|
|
|
.write_u64 = mem_cgroup_move_charge_write,
|
|
|
|
},
|
2010-05-27 04:42:36 +07:00
|
|
|
{
|
|
|
|
.name = "oom_control",
|
2010-05-27 04:42:37 +07:00
|
|
|
.read_map = mem_cgroup_oom_control_read,
|
|
|
|
.write_u64 = mem_cgroup_oom_control_write,
|
2010-05-27 04:42:36 +07:00
|
|
|
.register_event = mem_cgroup_oom_register_event,
|
|
|
|
.unregister_event = mem_cgroup_oom_unregister_event,
|
|
|
|
.private = MEMFILE_PRIVATE(_OOM_TYPE, OOM_CONTROL),
|
|
|
|
},
|
2011-05-27 06:25:37 +07:00
|
|
|
#ifdef CONFIG_NUMA
|
|
|
|
{
|
|
|
|
.name = "numa_stat",
|
2012-08-01 06:43:09 +07:00
|
|
|
.read_seq_string = memcg_numa_stat_show,
|
2011-05-27 06:25:37 +07:00
|
|
|
},
|
|
|
|
#endif
|
2012-08-01 06:43:02 +07:00
|
|
|
#ifdef CONFIG_MEMCG_SWAP
|
2009-01-08 09:08:00 +07:00
|
|
|
{
|
|
|
|
.name = "memsw.usage_in_bytes",
|
|
|
|
.private = MEMFILE_PRIVATE(_MEMSWAP, RES_USAGE),
|
2012-04-02 02:09:55 +07:00
|
|
|
.read = mem_cgroup_read,
|
2010-05-27 04:42:36 +07:00
|
|
|
.register_event = mem_cgroup_usage_register_event,
|
|
|
|
.unregister_event = mem_cgroup_usage_unregister_event,
|
2009-01-08 09:08:00 +07:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "memsw.max_usage_in_bytes",
|
|
|
|
.private = MEMFILE_PRIVATE(_MEMSWAP, RES_MAX_USAGE),
|
|
|
|
.trigger = mem_cgroup_reset,
|
2012-04-02 02:09:55 +07:00
|
|
|
.read = mem_cgroup_read,
|
2009-01-08 09:08:00 +07:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "memsw.limit_in_bytes",
|
|
|
|
.private = MEMFILE_PRIVATE(_MEMSWAP, RES_LIMIT),
|
|
|
|
.write_string = mem_cgroup_write,
|
2012-04-02 02:09:55 +07:00
|
|
|
.read = mem_cgroup_read,
|
2009-01-08 09:08:00 +07:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.name = "memsw.failcnt",
|
|
|
|
.private = MEMFILE_PRIVATE(_MEMSWAP, RES_FAILCNT),
|
|
|
|
.trigger = mem_cgroup_reset,
|
2012-04-02 02:09:55 +07:00
|
|
|
.read = mem_cgroup_read,
|
2009-01-08 09:08:00 +07:00
|
|
|
},
|
|
|
|
#endif
|
2012-04-02 02:09:55 +07:00
|
|
|
{ }, /* terminate */
|
2012-04-02 02:09:55 +07:00
|
|
|
};
|
2009-01-08 09:08:00 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static int alloc_mem_cgroup_per_zone_info(struct mem_cgroup *memcg, int node)
|
2008-02-07 15:14:31 +07:00
|
|
|
{
|
|
|
|
struct mem_cgroup_per_node *pn;
|
2008-02-07 15:14:38 +07:00
|
|
|
struct mem_cgroup_per_zone *mz;
|
2008-04-09 07:41:54 +07:00
|
|
|
int zone, tmp = node;
|
2008-02-07 15:14:38 +07:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2008-04-09 07:41:54 +07:00
|
|
|
if (!node_state(node, N_NORMAL_MEMORY))
|
|
|
|
tmp = -1;
|
2011-01-14 06:47:42 +07:00
|
|
|
pn = kzalloc_node(sizeof(*pn), GFP_KERNEL, tmp);
|
2008-02-07 15:14:31 +07:00
|
|
|
if (!pn)
|
|
|
|
return 1;
|
2008-02-07 15:14:38 +07:00
|
|
|
|
|
|
|
for (zone = 0; zone < MAX_NR_ZONES; zone++) {
|
|
|
|
mz = &pn->zoneinfo[zone];
|
memcg: fix hotplugged memory zone oops
When MEMCG is configured on (even when it's disabled by boot option),
when adding or removing a page to/from its lru list, the zone pointer
used for stats updates is nowadays taken from the struct lruvec. (On
many configurations, calculating zone from page is slower.)
But we have no code to update all the lruvecs (per zone, per memcg) when
a memory node is hotadded. Here's an extract from the oops which
results when running numactl to bind a program to a newly onlined node:
BUG: unable to handle kernel NULL pointer dereference at 0000000000000f60
IP: __mod_zone_page_state+0x9/0x60
Pid: 1219, comm: numactl Not tainted 3.6.0-rc5+ #180 Bochs Bochs
Process numactl (pid: 1219, threadinfo ffff880039abc000, task ffff8800383c4ce0)
Call Trace:
__pagevec_lru_add_fn+0xdf/0x140
pagevec_lru_move_fn+0xb1/0x100
__pagevec_lru_add+0x1c/0x30
lru_add_drain_cpu+0xa3/0x130
lru_add_drain+0x2f/0x40
...
The natural solution might be to use a memcg callback whenever memory is
hotadded; but that solution has not been scoped out, and it happens that
we do have an easy location at which to update lruvec->zone. The lruvec
pointer is discovered either by mem_cgroup_zone_lruvec() or by
mem_cgroup_page_lruvec(), and both of those do know the right zone.
So check and set lruvec->zone in those; and remove the inadequate
attempt to set lruvec->zone from lruvec_init(), which is called before
NODE_DATA(node) has been allocated in such cases.
Ah, there was one exceptionr. For no particularly good reason,
mem_cgroup_force_empty_list() has its own code for deciding lruvec.
Change it to use the standard mem_cgroup_zone_lruvec() and
mem_cgroup_get_lru_size() too. In fact it was already safe against such
an oops (the lru lists in danger could only be empty), but we're better
proofed against future changes this way.
I've marked this for stable (3.6) since we introduced the problem in 3.5
(now closed to stable); but I have no idea if this is the only fix
needed to get memory hotadd working with memcg in 3.6, and received no
answer when I enquired twice before.
Reported-by: Tang Chen <tangchen@cn.fujitsu.com>
Signed-off-by: Hugh Dickins <hughd@google.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Konstantin Khlebnikov <khlebnikov@openvz.org>
Cc: Wen Congyang <wency@cn.fujitsu.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-11-17 05:14:54 +07:00
|
|
|
lruvec_init(&mz->lruvec);
|
2009-09-24 05:56:37 +07:00
|
|
|
mz->usage_in_excess = 0;
|
2009-09-24 05:56:39 +07:00
|
|
|
mz->on_tree = false;
|
2012-03-22 06:34:18 +07:00
|
|
|
mz->memcg = memcg;
|
2008-02-07 15:14:38 +07:00
|
|
|
}
|
2011-11-03 03:38:21 +07:00
|
|
|
memcg->info.nodeinfo[node] = pn;
|
2008-02-07 15:14:31 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static void free_mem_cgroup_per_zone_info(struct mem_cgroup *memcg, int node)
|
2008-02-07 15:14:38 +07:00
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
kfree(memcg->info.nodeinfo[node]);
|
2008-02-07 15:14:38 +07:00
|
|
|
}
|
|
|
|
|
2008-04-29 15:00:24 +07:00
|
|
|
static struct mem_cgroup *mem_cgroup_alloc(void)
|
|
|
|
{
|
2012-03-22 06:34:18 +07:00
|
|
|
struct mem_cgroup *memcg;
|
memcg: use generic percpu instead of private implementation
When per-cpu counter for memcg was implemneted, dynamic percpu allocator
was not very good. But now, we have good one and useful macros. This
patch replaces memcg's private percpu counter implementation with generic
dynamic percpu allocator.
The benefits are
- We can remove private implementation.
- The counters will be NUMA-aware. (Current one is not...)
- This patch makes sizeof struct mem_cgroup smaller. Then,
struct mem_cgroup may be fit in page size on small config.
- About basic performance aspects, see below.
[Before]
# size mm/memcontrol.o
text data bss dec hex filename
24373 2528 4132 31033 7939 mm/memcontrol.o
[page-fault-throuput test on 8cpu/SMP in root cgroup]
# /root/bin/perf stat -a -e page-faults,cache-misses --repeat 5 ./multi-fault-fork 8
Performance counter stats for './multi-fault-fork 8' (5 runs):
45878618 page-faults ( +- 0.110% )
602635826 cache-misses ( +- 0.105% )
61.005373262 seconds time elapsed ( +- 0.004% )
Then cache-miss/page fault = 13.14
[After]
#size mm/memcontrol.o
text data bss dec hex filename
23913 2528 4132 30573 776d mm/memcontrol.o
# /root/bin/perf stat -a -e page-faults,cache-misses --repeat 5 ./multi-fault-fork 8
Performance counter stats for './multi-fault-fork 8' (5 runs):
48179400 page-faults ( +- 0.271% )
588628407 cache-misses ( +- 0.136% )
61.004615021 seconds time elapsed ( +- 0.004% )
Then cache-miss/page fault = 12.22
Text size is reduced.
This performance improvement is not big and will be invisible in real world
applications. But this result shows this patch has some good effect even
on (small) SMP.
Here is a test program I used.
1. fork() processes on each cpus.
2. do page fault repeatedly on each process.
3. after 60secs, kill all childredn and exit.
(3 is necessary for getting stable data, this is improvement from previous one.)
#define _GNU_SOURCE
#include <stdio.h>
#include <sched.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
/*
* For avoiding contention in page table lock, FAULT area is
* sparse. If FAULT_LENGTH is too large for your cpus, decrease it.
*/
#define FAULT_LENGTH (2 * 1024 * 1024)
#define PAGE_SIZE 4096
#define MAXNUM (128)
void alarm_handler(int sig)
{
}
void *worker(int cpu, int ppid)
{
void *start, *end;
char *c;
cpu_set_t set;
int i;
CPU_ZERO(&set);
CPU_SET(cpu, &set);
sched_setaffinity(0, sizeof(set), &set);
start = mmap(NULL, FAULT_LENGTH, PROT_READ|PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
if (start == MAP_FAILED) {
perror("mmap");
exit(1);
}
end = start + FAULT_LENGTH;
pause();
//fprintf(stderr, "run%d", cpu);
while (1) {
for (c = (char*)start; (void *)c < end; c += PAGE_SIZE)
*c = 0;
madvise(start, FAULT_LENGTH, MADV_DONTNEED);
}
return NULL;
}
int main(int argc, char *argv[])
{
int num, i, ret, pid, status;
int pids[MAXNUM];
if (argc < 2)
return 0;
setpgid(0, 0);
signal(SIGALRM, alarm_handler);
num = atoi(argv[1]);
pid = getpid();
for (i = 0; i < num; ++i) {
ret = fork();
if (!ret) {
worker(i, pid);
exit(0);
}
pids[i] = ret;
}
sleep(1);
kill(-pid, SIGALRM);
sleep(60);
for (i = 0; i < num; i++)
kill(pids[i], SIGKILL);
for (i = 0; i < num; i++)
waitpid(pids[i], &status, 0);
return 0;
}
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-03-11 06:22:29 +07:00
|
|
|
int size = sizeof(struct mem_cgroup);
|
2008-04-29 15:00:24 +07:00
|
|
|
|
memcg: use generic percpu instead of private implementation
When per-cpu counter for memcg was implemneted, dynamic percpu allocator
was not very good. But now, we have good one and useful macros. This
patch replaces memcg's private percpu counter implementation with generic
dynamic percpu allocator.
The benefits are
- We can remove private implementation.
- The counters will be NUMA-aware. (Current one is not...)
- This patch makes sizeof struct mem_cgroup smaller. Then,
struct mem_cgroup may be fit in page size on small config.
- About basic performance aspects, see below.
[Before]
# size mm/memcontrol.o
text data bss dec hex filename
24373 2528 4132 31033 7939 mm/memcontrol.o
[page-fault-throuput test on 8cpu/SMP in root cgroup]
# /root/bin/perf stat -a -e page-faults,cache-misses --repeat 5 ./multi-fault-fork 8
Performance counter stats for './multi-fault-fork 8' (5 runs):
45878618 page-faults ( +- 0.110% )
602635826 cache-misses ( +- 0.105% )
61.005373262 seconds time elapsed ( +- 0.004% )
Then cache-miss/page fault = 13.14
[After]
#size mm/memcontrol.o
text data bss dec hex filename
23913 2528 4132 30573 776d mm/memcontrol.o
# /root/bin/perf stat -a -e page-faults,cache-misses --repeat 5 ./multi-fault-fork 8
Performance counter stats for './multi-fault-fork 8' (5 runs):
48179400 page-faults ( +- 0.271% )
588628407 cache-misses ( +- 0.136% )
61.004615021 seconds time elapsed ( +- 0.004% )
Then cache-miss/page fault = 12.22
Text size is reduced.
This performance improvement is not big and will be invisible in real world
applications. But this result shows this patch has some good effect even
on (small) SMP.
Here is a test program I used.
1. fork() processes on each cpus.
2. do page fault repeatedly on each process.
3. after 60secs, kill all childredn and exit.
(3 is necessary for getting stable data, this is improvement from previous one.)
#define _GNU_SOURCE
#include <stdio.h>
#include <sched.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
/*
* For avoiding contention in page table lock, FAULT area is
* sparse. If FAULT_LENGTH is too large for your cpus, decrease it.
*/
#define FAULT_LENGTH (2 * 1024 * 1024)
#define PAGE_SIZE 4096
#define MAXNUM (128)
void alarm_handler(int sig)
{
}
void *worker(int cpu, int ppid)
{
void *start, *end;
char *c;
cpu_set_t set;
int i;
CPU_ZERO(&set);
CPU_SET(cpu, &set);
sched_setaffinity(0, sizeof(set), &set);
start = mmap(NULL, FAULT_LENGTH, PROT_READ|PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
if (start == MAP_FAILED) {
perror("mmap");
exit(1);
}
end = start + FAULT_LENGTH;
pause();
//fprintf(stderr, "run%d", cpu);
while (1) {
for (c = (char*)start; (void *)c < end; c += PAGE_SIZE)
*c = 0;
madvise(start, FAULT_LENGTH, MADV_DONTNEED);
}
return NULL;
}
int main(int argc, char *argv[])
{
int num, i, ret, pid, status;
int pids[MAXNUM];
if (argc < 2)
return 0;
setpgid(0, 0);
signal(SIGALRM, alarm_handler);
num = atoi(argv[1]);
pid = getpid();
for (i = 0; i < num; ++i) {
ret = fork();
if (!ret) {
worker(i, pid);
exit(0);
}
pids[i] = ret;
}
sleep(1);
kill(-pid, SIGALRM);
sleep(60);
for (i = 0; i < num; i++)
kill(pids[i], SIGKILL);
for (i = 0; i < num; i++)
waitpid(pids[i], &status, 0);
return 0;
}
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-03-11 06:22:29 +07:00
|
|
|
/* Can be very big if MAX_NUMNODES is very big */
|
2009-01-08 09:07:53 +07:00
|
|
|
if (size < PAGE_SIZE)
|
2012-03-22 06:34:18 +07:00
|
|
|
memcg = kzalloc(size, GFP_KERNEL);
|
2008-04-29 15:00:24 +07:00
|
|
|
else
|
2012-03-22 06:34:18 +07:00
|
|
|
memcg = vzalloc(size);
|
2008-04-29 15:00:24 +07:00
|
|
|
|
2012-03-22 06:34:18 +07:00
|
|
|
if (!memcg)
|
2010-03-24 03:35:12 +07:00
|
|
|
return NULL;
|
|
|
|
|
2012-03-22 06:34:18 +07:00
|
|
|
memcg->stat = alloc_percpu(struct mem_cgroup_stat_cpu);
|
|
|
|
if (!memcg->stat)
|
2010-11-12 05:05:12 +07:00
|
|
|
goto out_free;
|
2012-03-22 06:34:18 +07:00
|
|
|
spin_lock_init(&memcg->pcp_counter_lock);
|
|
|
|
return memcg;
|
2010-11-12 05:05:12 +07:00
|
|
|
|
|
|
|
out_free:
|
|
|
|
if (size < PAGE_SIZE)
|
2012-03-22 06:34:18 +07:00
|
|
|
kfree(memcg);
|
2010-11-12 05:05:12 +07:00
|
|
|
else
|
2012-03-22 06:34:18 +07:00
|
|
|
vfree(memcg);
|
2010-11-12 05:05:12 +07:00
|
|
|
return NULL;
|
2008-04-29 15:00:24 +07:00
|
|
|
}
|
|
|
|
|
memcg: free mem_cgroup by RCU to fix oops
After fixing the GPF in mem_cgroup_lru_del_list(), three times one
machine running a similar load (moving and removing memcgs while
swapping) has oopsed in mem_cgroup_zone_nr_lru_pages(), when retrieving
memcg zone numbers for get_scan_count() for shrink_mem_cgroup_zone():
this is where a struct mem_cgroup is first accessed after being chosen
by mem_cgroup_iter().
Just what protects a struct mem_cgroup from being freed, in between
mem_cgroup_iter()'s css_get_next() and its css_tryget()? css_tryget()
fails once css->refcnt is zero with CSS_REMOVED set in flags, yes: but
what if that memory is freed and reused for something else, which sets
"refcnt" non-zero? Hmm, and scope for an indefinite freeze if refcnt is
left at zero but flags are cleared.
It's tempting to move the css_tryget() into css_get_next(), to make it
really "get" the css, but I don't think that actually solves anything:
the same difficulty in moving from css_id found to stable css remains.
But we already have rcu_read_lock() around the two, so it's easily fixed
if __mem_cgroup_free() just uses kfree_rcu() to free mem_cgroup.
However, a big struct mem_cgroup is allocated with vzalloc() instead of
kzalloc(), and we're not allowed to vfree() at interrupt time: there
doesn't appear to be a general vfree_rcu() to help with this, so roll
our own using schedule_work(). The compiler decently removes
vfree_work() and vfree_rcu() when the config doesn't need them.
Signed-off-by: Hugh Dickins <hughd@google.com>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Cc: Konstantin Khlebnikov <khlebnikov@openvz.org>
Cc: Tejun Heo <tj@kernel.org>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-16 05:17:07 +07:00
|
|
|
/*
|
2012-05-30 05:07:10 +07:00
|
|
|
* Helpers for freeing a kmalloc()ed/vzalloc()ed mem_cgroup by RCU,
|
memcg: free mem_cgroup by RCU to fix oops
After fixing the GPF in mem_cgroup_lru_del_list(), three times one
machine running a similar load (moving and removing memcgs while
swapping) has oopsed in mem_cgroup_zone_nr_lru_pages(), when retrieving
memcg zone numbers for get_scan_count() for shrink_mem_cgroup_zone():
this is where a struct mem_cgroup is first accessed after being chosen
by mem_cgroup_iter().
Just what protects a struct mem_cgroup from being freed, in between
mem_cgroup_iter()'s css_get_next() and its css_tryget()? css_tryget()
fails once css->refcnt is zero with CSS_REMOVED set in flags, yes: but
what if that memory is freed and reused for something else, which sets
"refcnt" non-zero? Hmm, and scope for an indefinite freeze if refcnt is
left at zero but flags are cleared.
It's tempting to move the css_tryget() into css_get_next(), to make it
really "get" the css, but I don't think that actually solves anything:
the same difficulty in moving from css_id found to stable css remains.
But we already have rcu_read_lock() around the two, so it's easily fixed
if __mem_cgroup_free() just uses kfree_rcu() to free mem_cgroup.
However, a big struct mem_cgroup is allocated with vzalloc() instead of
kzalloc(), and we're not allowed to vfree() at interrupt time: there
doesn't appear to be a general vfree_rcu() to help with this, so roll
our own using schedule_work(). The compiler decently removes
vfree_work() and vfree_rcu() when the config doesn't need them.
Signed-off-by: Hugh Dickins <hughd@google.com>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Cc: Konstantin Khlebnikov <khlebnikov@openvz.org>
Cc: Tejun Heo <tj@kernel.org>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-16 05:17:07 +07:00
|
|
|
* but in process context. The work_freeing structure is overlaid
|
|
|
|
* on the rcu_freeing structure, which itself is overlaid on memsw.
|
|
|
|
*/
|
2012-05-30 05:07:10 +07:00
|
|
|
static void free_work(struct work_struct *work)
|
memcg: free mem_cgroup by RCU to fix oops
After fixing the GPF in mem_cgroup_lru_del_list(), three times one
machine running a similar load (moving and removing memcgs while
swapping) has oopsed in mem_cgroup_zone_nr_lru_pages(), when retrieving
memcg zone numbers for get_scan_count() for shrink_mem_cgroup_zone():
this is where a struct mem_cgroup is first accessed after being chosen
by mem_cgroup_iter().
Just what protects a struct mem_cgroup from being freed, in between
mem_cgroup_iter()'s css_get_next() and its css_tryget()? css_tryget()
fails once css->refcnt is zero with CSS_REMOVED set in flags, yes: but
what if that memory is freed and reused for something else, which sets
"refcnt" non-zero? Hmm, and scope for an indefinite freeze if refcnt is
left at zero but flags are cleared.
It's tempting to move the css_tryget() into css_get_next(), to make it
really "get" the css, but I don't think that actually solves anything:
the same difficulty in moving from css_id found to stable css remains.
But we already have rcu_read_lock() around the two, so it's easily fixed
if __mem_cgroup_free() just uses kfree_rcu() to free mem_cgroup.
However, a big struct mem_cgroup is allocated with vzalloc() instead of
kzalloc(), and we're not allowed to vfree() at interrupt time: there
doesn't appear to be a general vfree_rcu() to help with this, so roll
our own using schedule_work(). The compiler decently removes
vfree_work() and vfree_rcu() when the config doesn't need them.
Signed-off-by: Hugh Dickins <hughd@google.com>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Cc: Konstantin Khlebnikov <khlebnikov@openvz.org>
Cc: Tejun Heo <tj@kernel.org>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-16 05:17:07 +07:00
|
|
|
{
|
|
|
|
struct mem_cgroup *memcg;
|
2012-05-30 05:07:10 +07:00
|
|
|
int size = sizeof(struct mem_cgroup);
|
memcg: free mem_cgroup by RCU to fix oops
After fixing the GPF in mem_cgroup_lru_del_list(), three times one
machine running a similar load (moving and removing memcgs while
swapping) has oopsed in mem_cgroup_zone_nr_lru_pages(), when retrieving
memcg zone numbers for get_scan_count() for shrink_mem_cgroup_zone():
this is where a struct mem_cgroup is first accessed after being chosen
by mem_cgroup_iter().
Just what protects a struct mem_cgroup from being freed, in between
mem_cgroup_iter()'s css_get_next() and its css_tryget()? css_tryget()
fails once css->refcnt is zero with CSS_REMOVED set in flags, yes: but
what if that memory is freed and reused for something else, which sets
"refcnt" non-zero? Hmm, and scope for an indefinite freeze if refcnt is
left at zero but flags are cleared.
It's tempting to move the css_tryget() into css_get_next(), to make it
really "get" the css, but I don't think that actually solves anything:
the same difficulty in moving from css_id found to stable css remains.
But we already have rcu_read_lock() around the two, so it's easily fixed
if __mem_cgroup_free() just uses kfree_rcu() to free mem_cgroup.
However, a big struct mem_cgroup is allocated with vzalloc() instead of
kzalloc(), and we're not allowed to vfree() at interrupt time: there
doesn't appear to be a general vfree_rcu() to help with this, so roll
our own using schedule_work(). The compiler decently removes
vfree_work() and vfree_rcu() when the config doesn't need them.
Signed-off-by: Hugh Dickins <hughd@google.com>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Cc: Konstantin Khlebnikov <khlebnikov@openvz.org>
Cc: Tejun Heo <tj@kernel.org>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-16 05:17:07 +07:00
|
|
|
|
|
|
|
memcg = container_of(work, struct mem_cgroup, work_freeing);
|
memcg: decrement static keys at real destroy time
We call the destroy function when a cgroup starts to be removed, such as
by a rmdir event.
However, because of our reference counters, some objects are still
inflight. Right now, we are decrementing the static_keys at destroy()
time, meaning that if we get rid of the last static_key reference, some
objects will still have charges, but the code to properly uncharge them
won't be run.
This becomes a problem specially if it is ever enabled again, because now
new charges will be added to the staled charges making keeping it pretty
much impossible.
We just need to be careful with the static branch activation: since there
is no particular preferred order of their activation, we need to make sure
that we only start using it after all call sites are active. This is
achieved by having a per-memcg flag that is only updated after
static_key_slow_inc() returns. At this time, we are sure all sites are
active.
This is made per-memcg, not global, for a reason: it also has the effect
of making socket accounting more consistent. The first memcg to be
limited will trigger static_key() activation, therefore, accounting. But
all the others will then be accounted no matter what. After this patch,
only limited memcgs will have its sockets accounted.
[akpm@linux-foundation.org: move enum sock_flag_bits into sock.h,
document enum sock_flag_bits,
convert memcg_proto_active() and memcg_proto_activated() to test_bit(),
redo tcp_update_limit() comment to 80 cols]
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>
Cc: Michal Hocko <mhocko@suse.cz>
Acked-by: David Miller <davem@davemloft.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-05-30 05:07:11 +07:00
|
|
|
/*
|
|
|
|
* We need to make sure that (at least for now), the jump label
|
|
|
|
* destruction code runs outside of the cgroup lock. This is because
|
|
|
|
* get_online_cpus(), which is called from the static_branch update,
|
|
|
|
* can't be called inside the cgroup_lock. cpusets are the ones
|
|
|
|
* enforcing this dependency, so if they ever change, we might as well.
|
|
|
|
*
|
|
|
|
* schedule_work() will guarantee this happens. Be careful if you need
|
|
|
|
* to move this code around, and make sure it is outside
|
|
|
|
* the cgroup_lock.
|
|
|
|
*/
|
|
|
|
disarm_sock_keys(memcg);
|
2012-05-30 05:07:10 +07:00
|
|
|
if (size < PAGE_SIZE)
|
|
|
|
kfree(memcg);
|
|
|
|
else
|
|
|
|
vfree(memcg);
|
memcg: free mem_cgroup by RCU to fix oops
After fixing the GPF in mem_cgroup_lru_del_list(), three times one
machine running a similar load (moving and removing memcgs while
swapping) has oopsed in mem_cgroup_zone_nr_lru_pages(), when retrieving
memcg zone numbers for get_scan_count() for shrink_mem_cgroup_zone():
this is where a struct mem_cgroup is first accessed after being chosen
by mem_cgroup_iter().
Just what protects a struct mem_cgroup from being freed, in between
mem_cgroup_iter()'s css_get_next() and its css_tryget()? css_tryget()
fails once css->refcnt is zero with CSS_REMOVED set in flags, yes: but
what if that memory is freed and reused for something else, which sets
"refcnt" non-zero? Hmm, and scope for an indefinite freeze if refcnt is
left at zero but flags are cleared.
It's tempting to move the css_tryget() into css_get_next(), to make it
really "get" the css, but I don't think that actually solves anything:
the same difficulty in moving from css_id found to stable css remains.
But we already have rcu_read_lock() around the two, so it's easily fixed
if __mem_cgroup_free() just uses kfree_rcu() to free mem_cgroup.
However, a big struct mem_cgroup is allocated with vzalloc() instead of
kzalloc(), and we're not allowed to vfree() at interrupt time: there
doesn't appear to be a general vfree_rcu() to help with this, so roll
our own using schedule_work(). The compiler decently removes
vfree_work() and vfree_rcu() when the config doesn't need them.
Signed-off-by: Hugh Dickins <hughd@google.com>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Cc: Konstantin Khlebnikov <khlebnikov@openvz.org>
Cc: Tejun Heo <tj@kernel.org>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-16 05:17:07 +07:00
|
|
|
}
|
2012-05-30 05:07:10 +07:00
|
|
|
|
|
|
|
static void free_rcu(struct rcu_head *rcu_head)
|
memcg: free mem_cgroup by RCU to fix oops
After fixing the GPF in mem_cgroup_lru_del_list(), three times one
machine running a similar load (moving and removing memcgs while
swapping) has oopsed in mem_cgroup_zone_nr_lru_pages(), when retrieving
memcg zone numbers for get_scan_count() for shrink_mem_cgroup_zone():
this is where a struct mem_cgroup is first accessed after being chosen
by mem_cgroup_iter().
Just what protects a struct mem_cgroup from being freed, in between
mem_cgroup_iter()'s css_get_next() and its css_tryget()? css_tryget()
fails once css->refcnt is zero with CSS_REMOVED set in flags, yes: but
what if that memory is freed and reused for something else, which sets
"refcnt" non-zero? Hmm, and scope for an indefinite freeze if refcnt is
left at zero but flags are cleared.
It's tempting to move the css_tryget() into css_get_next(), to make it
really "get" the css, but I don't think that actually solves anything:
the same difficulty in moving from css_id found to stable css remains.
But we already have rcu_read_lock() around the two, so it's easily fixed
if __mem_cgroup_free() just uses kfree_rcu() to free mem_cgroup.
However, a big struct mem_cgroup is allocated with vzalloc() instead of
kzalloc(), and we're not allowed to vfree() at interrupt time: there
doesn't appear to be a general vfree_rcu() to help with this, so roll
our own using schedule_work(). The compiler decently removes
vfree_work() and vfree_rcu() when the config doesn't need them.
Signed-off-by: Hugh Dickins <hughd@google.com>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Cc: Konstantin Khlebnikov <khlebnikov@openvz.org>
Cc: Tejun Heo <tj@kernel.org>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-16 05:17:07 +07:00
|
|
|
{
|
|
|
|
struct mem_cgroup *memcg;
|
|
|
|
|
|
|
|
memcg = container_of(rcu_head, struct mem_cgroup, rcu_freeing);
|
2012-05-30 05:07:10 +07:00
|
|
|
INIT_WORK(&memcg->work_freeing, free_work);
|
memcg: free mem_cgroup by RCU to fix oops
After fixing the GPF in mem_cgroup_lru_del_list(), three times one
machine running a similar load (moving and removing memcgs while
swapping) has oopsed in mem_cgroup_zone_nr_lru_pages(), when retrieving
memcg zone numbers for get_scan_count() for shrink_mem_cgroup_zone():
this is where a struct mem_cgroup is first accessed after being chosen
by mem_cgroup_iter().
Just what protects a struct mem_cgroup from being freed, in between
mem_cgroup_iter()'s css_get_next() and its css_tryget()? css_tryget()
fails once css->refcnt is zero with CSS_REMOVED set in flags, yes: but
what if that memory is freed and reused for something else, which sets
"refcnt" non-zero? Hmm, and scope for an indefinite freeze if refcnt is
left at zero but flags are cleared.
It's tempting to move the css_tryget() into css_get_next(), to make it
really "get" the css, but I don't think that actually solves anything:
the same difficulty in moving from css_id found to stable css remains.
But we already have rcu_read_lock() around the two, so it's easily fixed
if __mem_cgroup_free() just uses kfree_rcu() to free mem_cgroup.
However, a big struct mem_cgroup is allocated with vzalloc() instead of
kzalloc(), and we're not allowed to vfree() at interrupt time: there
doesn't appear to be a general vfree_rcu() to help with this, so roll
our own using schedule_work(). The compiler decently removes
vfree_work() and vfree_rcu() when the config doesn't need them.
Signed-off-by: Hugh Dickins <hughd@google.com>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Cc: Konstantin Khlebnikov <khlebnikov@openvz.org>
Cc: Tejun Heo <tj@kernel.org>
Cc: Ying Han <yinghan@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-16 05:17:07 +07:00
|
|
|
schedule_work(&memcg->work_freeing);
|
|
|
|
}
|
|
|
|
|
2009-01-08 09:08:00 +07:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static void __mem_cgroup_free(struct mem_cgroup *memcg)
|
2008-04-29 15:00:24 +07:00
|
|
|
{
|
memcg: synchronized LRU
A big patch for changing memcg's LRU semantics.
Now,
- page_cgroup is linked to mem_cgroup's its own LRU (per zone).
- LRU of page_cgroup is not synchronous with global LRU.
- page and page_cgroup is one-to-one and statically allocated.
- To find page_cgroup is on what LRU, you have to check pc->mem_cgroup as
- lru = page_cgroup_zoneinfo(pc, nid_of_pc, zid_of_pc);
- SwapCache is handled.
And, when we handle LRU list of page_cgroup, we do following.
pc = lookup_page_cgroup(page);
lock_page_cgroup(pc); .....................(1)
mz = page_cgroup_zoneinfo(pc);
spin_lock(&mz->lru_lock);
.....add to LRU
spin_unlock(&mz->lru_lock);
unlock_page_cgroup(pc);
But (1) is spin_lock and we have to be afraid of dead-lock with zone->lru_lock.
So, trylock() is used at (1), now. Without (1), we can't trust "mz" is correct.
This is a trial to remove this dirty nesting of locks.
This patch changes mz->lru_lock to be zone->lru_lock.
Then, above sequence will be written as
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
mem_cgroup_add/remove/etc_lru() {
pc = lookup_page_cgroup(page);
mz = page_cgroup_zoneinfo(pc);
if (PageCgroupUsed(pc)) {
....add to LRU
}
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
This is much simpler.
(*) We're safe even if we don't take lock_page_cgroup(pc). Because..
1. When pc->mem_cgroup can be modified.
- at charge.
- at account_move().
2. at charge
the PCG_USED bit is not set before pc->mem_cgroup is fixed.
3. at account_move()
the page is isolated and not on LRU.
Pros.
- easy for maintenance.
- memcg can make use of laziness of pagevec.
- we don't have to duplicated LRU/Active/Unevictable bit in page_cgroup.
- LRU status of memcg will be synchronized with global LRU's one.
- # of locks are reduced.
- account_move() is simplified very much.
Cons.
- may increase cost of LRU rotation.
(no impact if memcg is not configured.)
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:08:01 +07:00
|
|
|
int node;
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
mem_cgroup_remove_from_trees(memcg);
|
|
|
|
free_css_id(&mem_cgroup_subsys, &memcg->css);
|
2009-04-03 06:57:33 +07:00
|
|
|
|
2012-01-13 08:19:04 +07:00
|
|
|
for_each_node(node)
|
2011-11-03 03:38:15 +07:00
|
|
|
free_mem_cgroup_per_zone_info(memcg, node);
|
memcg: synchronized LRU
A big patch for changing memcg's LRU semantics.
Now,
- page_cgroup is linked to mem_cgroup's its own LRU (per zone).
- LRU of page_cgroup is not synchronous with global LRU.
- page and page_cgroup is one-to-one and statically allocated.
- To find page_cgroup is on what LRU, you have to check pc->mem_cgroup as
- lru = page_cgroup_zoneinfo(pc, nid_of_pc, zid_of_pc);
- SwapCache is handled.
And, when we handle LRU list of page_cgroup, we do following.
pc = lookup_page_cgroup(page);
lock_page_cgroup(pc); .....................(1)
mz = page_cgroup_zoneinfo(pc);
spin_lock(&mz->lru_lock);
.....add to LRU
spin_unlock(&mz->lru_lock);
unlock_page_cgroup(pc);
But (1) is spin_lock and we have to be afraid of dead-lock with zone->lru_lock.
So, trylock() is used at (1), now. Without (1), we can't trust "mz" is correct.
This is a trial to remove this dirty nesting of locks.
This patch changes mz->lru_lock to be zone->lru_lock.
Then, above sequence will be written as
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
mem_cgroup_add/remove/etc_lru() {
pc = lookup_page_cgroup(page);
mz = page_cgroup_zoneinfo(pc);
if (PageCgroupUsed(pc)) {
....add to LRU
}
spin_lock(&zone->lru_lock); # in vmscan.c or swap.c via global LRU
This is much simpler.
(*) We're safe even if we don't take lock_page_cgroup(pc). Because..
1. When pc->mem_cgroup can be modified.
- at charge.
- at account_move().
2. at charge
the PCG_USED bit is not set before pc->mem_cgroup is fixed.
3. at account_move()
the page is isolated and not on LRU.
Pros.
- easy for maintenance.
- memcg can make use of laziness of pagevec.
- we don't have to duplicated LRU/Active/Unevictable bit in page_cgroup.
- LRU status of memcg will be synchronized with global LRU's one.
- # of locks are reduced.
- account_move() is simplified very much.
Cons.
- may increase cost of LRU rotation.
(no impact if memcg is not configured.)
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Li Zefan <lizf@cn.fujitsu.com>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Pavel Emelyanov <xemul@openvz.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-01-08 09:08:01 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
free_percpu(memcg->stat);
|
2012-05-30 05:07:10 +07:00
|
|
|
call_rcu(&memcg->rcu_freeing, free_rcu);
|
2008-04-29 15:00:24 +07:00
|
|
|
}
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static void mem_cgroup_get(struct mem_cgroup *memcg)
|
2009-01-08 09:08:00 +07:00
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
atomic_inc(&memcg->refcnt);
|
2009-01-08 09:08:00 +07:00
|
|
|
}
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static void __mem_cgroup_put(struct mem_cgroup *memcg, int count)
|
2009-01-08 09:08:00 +07:00
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
if (atomic_sub_and_test(count, &memcg->refcnt)) {
|
|
|
|
struct mem_cgroup *parent = parent_mem_cgroup(memcg);
|
|
|
|
__mem_cgroup_free(memcg);
|
2009-01-30 05:25:11 +07:00
|
|
|
if (parent)
|
|
|
|
mem_cgroup_put(parent);
|
|
|
|
}
|
2009-01-08 09:08:00 +07:00
|
|
|
}
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
static void mem_cgroup_put(struct mem_cgroup *memcg)
|
2010-03-11 06:22:18 +07:00
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
__mem_cgroup_put(memcg, 1);
|
2010-03-11 06:22:18 +07:00
|
|
|
}
|
|
|
|
|
2009-01-30 05:25:11 +07:00
|
|
|
/*
|
|
|
|
* Returns the parent mem_cgroup in memcgroup hierarchy with hierarchy enabled.
|
|
|
|
*/
|
2011-12-12 04:47:03 +07:00
|
|
|
struct mem_cgroup *parent_mem_cgroup(struct mem_cgroup *memcg)
|
2009-01-30 05:25:11 +07:00
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
if (!memcg->res.parent)
|
2009-01-30 05:25:11 +07:00
|
|
|
return NULL;
|
2011-11-03 03:38:15 +07:00
|
|
|
return mem_cgroup_from_res_counter(memcg->res.parent, res);
|
2009-01-30 05:25:11 +07:00
|
|
|
}
|
2011-12-12 04:47:03 +07:00
|
|
|
EXPORT_SYMBOL(parent_mem_cgroup);
|
2008-04-29 15:00:24 +07:00
|
|
|
|
2012-08-01 06:43:02 +07:00
|
|
|
#ifdef CONFIG_MEMCG_SWAP
|
2009-01-08 09:07:57 +07:00
|
|
|
static void __init enable_swap_cgroup(void)
|
|
|
|
{
|
2009-01-08 09:08:02 +07:00
|
|
|
if (!mem_cgroup_disabled() && really_do_swap_account)
|
2009-01-08 09:07:57 +07:00
|
|
|
do_swap_account = 1;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static void __init enable_swap_cgroup(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-09-24 05:56:37 +07:00
|
|
|
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;
|
|
|
|
|
2012-01-13 08:19:04 +07:00
|
|
|
for_each_node(node) {
|
2009-09-24 05:56:37 +07:00
|
|
|
tmp = node;
|
|
|
|
if (!node_state(node, N_NORMAL_MEMORY))
|
|
|
|
tmp = -1;
|
|
|
|
rtpn = kzalloc_node(sizeof(*rtpn), GFP_KERNEL, tmp);
|
|
|
|
if (!rtpn)
|
2012-01-13 08:18:50 +07:00
|
|
|
goto err_cleanup;
|
2009-09-24 05:56:37 +07:00
|
|
|
|
|
|
|
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;
|
2012-01-13 08:18:50 +07:00
|
|
|
|
|
|
|
err_cleanup:
|
2012-01-13 08:19:04 +07:00
|
|
|
for_each_node(node) {
|
2012-01-13 08:18:50 +07:00
|
|
|
if (!soft_limit_tree.rb_tree_per_node[node])
|
|
|
|
break;
|
|
|
|
kfree(soft_limit_tree.rb_tree_per_node[node]);
|
|
|
|
soft_limit_tree.rb_tree_per_node[node] = NULL;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
|
2009-09-24 05:56:37 +07:00
|
|
|
}
|
|
|
|
|
2009-01-16 04:51:25 +07:00
|
|
|
static struct cgroup_subsys_state * __ref
|
2012-01-31 12:47:36 +07:00
|
|
|
mem_cgroup_create(struct cgroup *cont)
|
2008-02-07 15:13:50 +07:00
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
struct mem_cgroup *memcg, *parent;
|
2009-04-03 06:57:33 +07:00
|
|
|
long error = -ENOMEM;
|
2008-02-07 15:14:31 +07:00
|
|
|
int node;
|
2008-02-07 15:13:50 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
memcg = mem_cgroup_alloc();
|
|
|
|
if (!memcg)
|
2009-04-03 06:57:33 +07:00
|
|
|
return ERR_PTR(error);
|
2008-02-07 15:13:51 +07:00
|
|
|
|
2012-01-13 08:19:04 +07:00
|
|
|
for_each_node(node)
|
2011-11-03 03:38:15 +07:00
|
|
|
if (alloc_mem_cgroup_per_zone_info(memcg, node))
|
2008-02-07 15:14:31 +07:00
|
|
|
goto free_out;
|
2009-09-24 05:56:37 +07:00
|
|
|
|
2009-01-08 09:07:57 +07:00
|
|
|
/* root ? */
|
2009-01-08 09:08:05 +07:00
|
|
|
if (cont->parent == NULL) {
|
2009-12-16 07:47:08 +07:00
|
|
|
int cpu;
|
2009-01-08 09:07:57 +07:00
|
|
|
enable_swap_cgroup();
|
2009-01-08 09:08:05 +07:00
|
|
|
parent = NULL;
|
2009-09-24 05:56:37 +07:00
|
|
|
if (mem_cgroup_soft_limit_tree_init())
|
|
|
|
goto free_out;
|
2011-12-20 08:11:57 +07:00
|
|
|
root_mem_cgroup = memcg;
|
2009-12-16 07:47:08 +07:00
|
|
|
for_each_possible_cpu(cpu) {
|
|
|
|
struct memcg_stock_pcp *stock =
|
|
|
|
&per_cpu(memcg_stock, cpu);
|
|
|
|
INIT_WORK(&stock->work, drain_local_stock);
|
|
|
|
}
|
2010-10-28 05:33:42 +07:00
|
|
|
hotcpu_notifier(memcg_cpu_hotplug_callback, 0);
|
2009-01-08 09:08:07 +07:00
|
|
|
} else {
|
2009-01-08 09:08:05 +07:00
|
|
|
parent = mem_cgroup_from_cont(cont->parent);
|
2011-11-03 03:38:15 +07:00
|
|
|
memcg->use_hierarchy = parent->use_hierarchy;
|
|
|
|
memcg->oom_kill_disable = parent->oom_kill_disable;
|
2009-01-08 09:08:07 +07:00
|
|
|
}
|
2009-01-08 09:08:05 +07:00
|
|
|
|
2009-01-08 09:08:07 +07:00
|
|
|
if (parent && parent->use_hierarchy) {
|
2011-11-03 03:38:15 +07:00
|
|
|
res_counter_init(&memcg->res, &parent->res);
|
|
|
|
res_counter_init(&memcg->memsw, &parent->memsw);
|
2009-01-30 05:25:11 +07:00
|
|
|
/*
|
|
|
|
* 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);
|
2009-01-08 09:08:07 +07:00
|
|
|
} else {
|
2011-11-03 03:38:15 +07:00
|
|
|
res_counter_init(&memcg->res, NULL);
|
|
|
|
res_counter_init(&memcg->memsw, NULL);
|
2012-09-14 02:20:58 +07:00
|
|
|
/*
|
|
|
|
* Deeper hierachy with use_hierarchy == false doesn't make
|
|
|
|
* much sense so let cgroup subsystem know about this
|
|
|
|
* unfortunate state in our controller.
|
|
|
|
*/
|
|
|
|
if (parent && parent != root_mem_cgroup)
|
|
|
|
mem_cgroup_subsys.broken_hierarchy = true;
|
2009-01-08 09:08:07 +07:00
|
|
|
}
|
2011-11-03 03:38:15 +07:00
|
|
|
memcg->last_scanned_node = MAX_NUMNODES;
|
|
|
|
INIT_LIST_HEAD(&memcg->oom_notify);
|
2009-01-08 09:08:06 +07:00
|
|
|
|
2009-01-08 09:08:24 +07:00
|
|
|
if (parent)
|
2011-11-03 03:38:15 +07:00
|
|
|
memcg->swappiness = mem_cgroup_swappiness(parent);
|
|
|
|
atomic_set(&memcg->refcnt, 1);
|
|
|
|
memcg->move_charge_at_immigrate = 0;
|
|
|
|
mutex_init(&memcg->thresholds_lock);
|
2012-03-22 06:34:24 +07:00
|
|
|
spin_lock_init(&memcg->move_lock);
|
2012-04-10 05:36:34 +07:00
|
|
|
|
|
|
|
error = memcg_init_kmem(memcg, &mem_cgroup_subsys);
|
|
|
|
if (error) {
|
|
|
|
/*
|
|
|
|
* We call put now because our (and parent's) refcnts
|
|
|
|
* are already in place. mem_cgroup_put() will internally
|
|
|
|
* call __mem_cgroup_free, so return directly
|
|
|
|
*/
|
|
|
|
mem_cgroup_put(memcg);
|
|
|
|
return ERR_PTR(error);
|
|
|
|
}
|
2011-11-03 03:38:15 +07:00
|
|
|
return &memcg->css;
|
2008-02-07 15:14:31 +07:00
|
|
|
free_out:
|
2011-11-03 03:38:15 +07:00
|
|
|
__mem_cgroup_free(memcg);
|
2009-04-03 06:57:33 +07:00
|
|
|
return ERR_PTR(error);
|
2008-02-07 15:13:50 +07:00
|
|
|
}
|
|
|
|
|
2012-01-31 12:47:36 +07:00
|
|
|
static int mem_cgroup_pre_destroy(struct cgroup *cont)
|
2008-02-07 15:14:28 +07:00
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
struct mem_cgroup *memcg = mem_cgroup_from_cont(cont);
|
2009-04-03 06:57:26 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
return mem_cgroup_force_empty(memcg, false);
|
2008-02-07 15:14:28 +07:00
|
|
|
}
|
|
|
|
|
2012-01-31 12:47:36 +07:00
|
|
|
static void mem_cgroup_destroy(struct cgroup *cont)
|
2008-02-07 15:13:50 +07:00
|
|
|
{
|
2011-11-03 03:38:15 +07:00
|
|
|
struct mem_cgroup *memcg = mem_cgroup_from_cont(cont);
|
2009-01-16 04:51:13 +07:00
|
|
|
|
2012-04-10 05:36:33 +07:00
|
|
|
kmem_cgroup_destroy(memcg);
|
2011-12-12 04:47:04 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
mem_cgroup_put(memcg);
|
2008-02-07 15:13:50 +07:00
|
|
|
}
|
|
|
|
|
2010-03-11 06:22:17 +07:00
|
|
|
#ifdef CONFIG_MMU
|
2010-03-11 06:22:13 +07:00
|
|
|
/* Handlers for move charge at task migration. */
|
2010-03-11 06:22:15 +07:00
|
|
|
#define PRECHARGE_COUNT_AT_ONCE 256
|
|
|
|
static int mem_cgroup_do_precharge(unsigned long count)
|
2010-03-11 06:22:13 +07:00
|
|
|
{
|
2010-03-11 06:22:15 +07:00
|
|
|
int ret = 0;
|
|
|
|
int batch_count = PRECHARGE_COUNT_AT_ONCE;
|
2011-11-03 03:38:15 +07:00
|
|
|
struct mem_cgroup *memcg = mc.to;
|
2010-03-11 06:22:14 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
if (mem_cgroup_is_root(memcg)) {
|
2010-03-11 06:22:15 +07:00
|
|
|
mc.precharge += count;
|
|
|
|
/* we don't need css_get for root */
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
/* try to charge at once */
|
|
|
|
if (count > 1) {
|
|
|
|
struct res_counter *dummy;
|
|
|
|
/*
|
2011-11-03 03:38:15 +07:00
|
|
|
* "memcg" cannot be under rmdir() because we've already checked
|
2010-03-11 06:22:15 +07:00
|
|
|
* by cgroup_lock_live_cgroup() that it is not removed and we
|
|
|
|
* are still under the same cgroup_mutex. So we can postpone
|
|
|
|
* css_get().
|
|
|
|
*/
|
2011-11-03 03:38:15 +07:00
|
|
|
if (res_counter_charge(&memcg->res, PAGE_SIZE * count, &dummy))
|
2010-03-11 06:22:15 +07:00
|
|
|
goto one_by_one;
|
2011-11-03 03:38:15 +07:00
|
|
|
if (do_swap_account && res_counter_charge(&memcg->memsw,
|
2010-03-11 06:22:15 +07:00
|
|
|
PAGE_SIZE * count, &dummy)) {
|
2011-11-03 03:38:15 +07:00
|
|
|
res_counter_uncharge(&memcg->res, PAGE_SIZE * count);
|
2010-03-11 06:22:15 +07:00
|
|
|
goto one_by_one;
|
|
|
|
}
|
|
|
|
mc.precharge += count;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
one_by_one:
|
|
|
|
/* fall back to one by one charge */
|
|
|
|
while (count--) {
|
|
|
|
if (signal_pending(current)) {
|
|
|
|
ret = -EINTR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!batch_count--) {
|
|
|
|
batch_count = PRECHARGE_COUNT_AT_ONCE;
|
|
|
|
cond_resched();
|
|
|
|
}
|
2011-11-03 03:38:15 +07:00
|
|
|
ret = __mem_cgroup_try_charge(NULL,
|
|
|
|
GFP_KERNEL, 1, &memcg, false);
|
2012-01-13 08:19:01 +07:00
|
|
|
if (ret)
|
2010-03-11 06:22:15 +07:00
|
|
|
/* mem_cgroup_clear_mc() will do uncharge later */
|
2012-01-13 08:19:01 +07:00
|
|
|
return ret;
|
2010-03-11 06:22:15 +07:00
|
|
|
mc.precharge++;
|
|
|
|
}
|
2010-03-11 06:22:14 +07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2012-03-22 06:34:27 +07:00
|
|
|
* get_mctgt_type - get target type of moving charge
|
2010-03-11 06:22:14 +07:00
|
|
|
* @vma: the vma the pte to be checked belongs
|
|
|
|
* @addr: the address corresponding to the pte to be checked
|
|
|
|
* @ptent: the pte to be checked
|
2010-03-11 06:22:17 +07:00
|
|
|
* @target: the pointer the target page or swap ent will be stored(can be NULL)
|
2010-03-11 06:22:14 +07:00
|
|
|
*
|
|
|
|
* Returns
|
|
|
|
* 0(MC_TARGET_NONE): if the pte is not a target for move charge.
|
|
|
|
* 1(MC_TARGET_PAGE): if the page corresponding to this pte is a target for
|
|
|
|
* move charge. if @target is not NULL, the page is stored in target->page
|
|
|
|
* with extra refcnt got(Callers should handle it).
|
2010-03-11 06:22:17 +07:00
|
|
|
* 2(MC_TARGET_SWAP): if the swap entry corresponding to this pte is a
|
|
|
|
* target for charge migration. if @target is not NULL, the entry is stored
|
|
|
|
* in target->ent.
|
2010-03-11 06:22:14 +07:00
|
|
|
*
|
|
|
|
* Called with pte lock held.
|
|
|
|
*/
|
|
|
|
union mc_target {
|
|
|
|
struct page *page;
|
2010-03-11 06:22:17 +07:00
|
|
|
swp_entry_t ent;
|
2010-03-11 06:22:14 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
enum mc_target_type {
|
2012-03-22 06:34:27 +07:00
|
|
|
MC_TARGET_NONE = 0,
|
2010-03-11 06:22:14 +07:00
|
|
|
MC_TARGET_PAGE,
|
2010-03-11 06:22:17 +07:00
|
|
|
MC_TARGET_SWAP,
|
2010-03-11 06:22:14 +07:00
|
|
|
};
|
|
|
|
|
2010-05-27 04:42:38 +07:00
|
|
|
static struct page *mc_handle_present_pte(struct vm_area_struct *vma,
|
|
|
|
unsigned long addr, pte_t ptent)
|
2010-03-11 06:22:14 +07:00
|
|
|
{
|
2010-05-27 04:42:38 +07:00
|
|
|
struct page *page = vm_normal_page(vma, addr, ptent);
|
2010-03-11 06:22:14 +07:00
|
|
|
|
2010-05-27 04:42:38 +07:00
|
|
|
if (!page || !page_mapped(page))
|
|
|
|
return NULL;
|
|
|
|
if (PageAnon(page)) {
|
|
|
|
/* we don't move shared anon */
|
memcg: fix/change behavior of shared anon at moving task
This patch changes memcg's behavior at task_move().
At task_move(), the kernel scans a task's page table and move the changes
for mapped pages from source cgroup to target cgroup. There has been a
bug at handling shared anonymous pages for a long time.
Before patch:
- The spec says 'shared anonymous pages are not moved.'
- The implementation was 'shared anonymoys pages may be moved'.
If page_mapcount <=2, shared anonymous pages's charge were moved.
After patch:
- The spec says 'all anonymous pages are moved'.
- The implementation is 'all anonymous pages are moved'.
Considering usage of memcg, this will not affect user's experience.
'shared anonymous' pages only exists between a tree of processes which
don't do exec(). Moving one of process without exec() seems not sane.
For example, libcgroup will not be affected by this change. (Anyway, no
one noticed the implementation for a long time...)
Below is a discussion log:
- current spec/implementation are complex
- Now, shared file caches are moved
- It adds unclear check as page_mapcount(). To do correct check,
we should check swap users, etc.
- No one notice this implementation behavior. So, no one get benefit
from the design.
- In general, once task is moved to a cgroup for running, it will not
be moved....
- Finally, we have control knob as memory.move_charge_at_immigrate.
Here is a patch to allow moving shared pages, completely. This makes
memcg simpler and fix current broken code.
Suggested-by: Hugh Dickins <hughd@google.com>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Michal Hocko <mhocko@suse.cz>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Cc: Glauber Costa <glommer@parallels.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-05-30 05:06:51 +07:00
|
|
|
if (!move_anon())
|
2010-05-27 04:42:38 +07:00
|
|
|
return NULL;
|
2010-05-27 04:42:39 +07:00
|
|
|
} else if (!move_file())
|
|
|
|
/* we ignore mapcount for file pages */
|
2010-05-27 04:42:38 +07:00
|
|
|
return NULL;
|
|
|
|
if (!get_page_unless_zero(page))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return page;
|
|
|
|
}
|
|
|
|
|
memcg: fix/change behavior of shared anon at moving task
This patch changes memcg's behavior at task_move().
At task_move(), the kernel scans a task's page table and move the changes
for mapped pages from source cgroup to target cgroup. There has been a
bug at handling shared anonymous pages for a long time.
Before patch:
- The spec says 'shared anonymous pages are not moved.'
- The implementation was 'shared anonymoys pages may be moved'.
If page_mapcount <=2, shared anonymous pages's charge were moved.
After patch:
- The spec says 'all anonymous pages are moved'.
- The implementation is 'all anonymous pages are moved'.
Considering usage of memcg, this will not affect user's experience.
'shared anonymous' pages only exists between a tree of processes which
don't do exec(). Moving one of process without exec() seems not sane.
For example, libcgroup will not be affected by this change. (Anyway, no
one noticed the implementation for a long time...)
Below is a discussion log:
- current spec/implementation are complex
- Now, shared file caches are moved
- It adds unclear check as page_mapcount(). To do correct check,
we should check swap users, etc.
- No one notice this implementation behavior. So, no one get benefit
from the design.
- In general, once task is moved to a cgroup for running, it will not
be moved....
- Finally, we have control knob as memory.move_charge_at_immigrate.
Here is a patch to allow moving shared pages, completely. This makes
memcg simpler and fix current broken code.
Suggested-by: Hugh Dickins <hughd@google.com>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Michal Hocko <mhocko@suse.cz>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Cc: Glauber Costa <glommer@parallels.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-05-30 05:06:51 +07:00
|
|
|
#ifdef CONFIG_SWAP
|
2010-05-27 04:42:38 +07:00
|
|
|
static struct page *mc_handle_swap_pte(struct vm_area_struct *vma,
|
|
|
|
unsigned long addr, pte_t ptent, swp_entry_t *entry)
|
|
|
|
{
|
|
|
|
struct page *page = NULL;
|
|
|
|
swp_entry_t ent = pte_to_swp_entry(ptent);
|
|
|
|
|
|
|
|
if (!move_anon() || non_swap_entry(ent))
|
|
|
|
return NULL;
|
memcg: fix/change behavior of shared anon at moving task
This patch changes memcg's behavior at task_move().
At task_move(), the kernel scans a task's page table and move the changes
for mapped pages from source cgroup to target cgroup. There has been a
bug at handling shared anonymous pages for a long time.
Before patch:
- The spec says 'shared anonymous pages are not moved.'
- The implementation was 'shared anonymoys pages may be moved'.
If page_mapcount <=2, shared anonymous pages's charge were moved.
After patch:
- The spec says 'all anonymous pages are moved'.
- The implementation is 'all anonymous pages are moved'.
Considering usage of memcg, this will not affect user's experience.
'shared anonymous' pages only exists between a tree of processes which
don't do exec(). Moving one of process without exec() seems not sane.
For example, libcgroup will not be affected by this change. (Anyway, no
one noticed the implementation for a long time...)
Below is a discussion log:
- current spec/implementation are complex
- Now, shared file caches are moved
- It adds unclear check as page_mapcount(). To do correct check,
we should check swap users, etc.
- No one notice this implementation behavior. So, no one get benefit
from the design.
- In general, once task is moved to a cgroup for running, it will not
be moved....
- Finally, we have control knob as memory.move_charge_at_immigrate.
Here is a patch to allow moving shared pages, completely. This makes
memcg simpler and fix current broken code.
Suggested-by: Hugh Dickins <hughd@google.com>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Michal Hocko <mhocko@suse.cz>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Cc: Glauber Costa <glommer@parallels.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-05-30 05:06:51 +07:00
|
|
|
/*
|
|
|
|
* Because lookup_swap_cache() updates some statistics counter,
|
|
|
|
* we call find_get_page() with swapper_space directly.
|
|
|
|
*/
|
|
|
|
page = find_get_page(&swapper_space, ent.val);
|
2010-05-27 04:42:38 +07:00
|
|
|
if (do_swap_account)
|
|
|
|
entry->val = ent.val;
|
|
|
|
|
|
|
|
return page;
|
|
|
|
}
|
memcg: fix/change behavior of shared anon at moving task
This patch changes memcg's behavior at task_move().
At task_move(), the kernel scans a task's page table and move the changes
for mapped pages from source cgroup to target cgroup. There has been a
bug at handling shared anonymous pages for a long time.
Before patch:
- The spec says 'shared anonymous pages are not moved.'
- The implementation was 'shared anonymoys pages may be moved'.
If page_mapcount <=2, shared anonymous pages's charge were moved.
After patch:
- The spec says 'all anonymous pages are moved'.
- The implementation is 'all anonymous pages are moved'.
Considering usage of memcg, this will not affect user's experience.
'shared anonymous' pages only exists between a tree of processes which
don't do exec(). Moving one of process without exec() seems not sane.
For example, libcgroup will not be affected by this change. (Anyway, no
one noticed the implementation for a long time...)
Below is a discussion log:
- current spec/implementation are complex
- Now, shared file caches are moved
- It adds unclear check as page_mapcount(). To do correct check,
we should check swap users, etc.
- No one notice this implementation behavior. So, no one get benefit
from the design.
- In general, once task is moved to a cgroup for running, it will not
be moved....
- Finally, we have control knob as memory.move_charge_at_immigrate.
Here is a patch to allow moving shared pages, completely. This makes
memcg simpler and fix current broken code.
Suggested-by: Hugh Dickins <hughd@google.com>
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Acked-by: Michal Hocko <mhocko@suse.cz>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Cc: Glauber Costa <glommer@parallels.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-05-30 05:06:51 +07:00
|
|
|
#else
|
|
|
|
static struct page *mc_handle_swap_pte(struct vm_area_struct *vma,
|
|
|
|
unsigned long addr, pte_t ptent, swp_entry_t *entry)
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#endif
|
2010-05-27 04:42:38 +07:00
|
|
|
|
2010-05-27 04:42:39 +07:00
|
|
|
static struct page *mc_handle_file_pte(struct vm_area_struct *vma,
|
|
|
|
unsigned long addr, pte_t ptent, swp_entry_t *entry)
|
|
|
|
{
|
|
|
|
struct page *page = NULL;
|
|
|
|
struct address_space *mapping;
|
|
|
|
pgoff_t pgoff;
|
|
|
|
|
|
|
|
if (!vma->vm_file) /* anonymous vma */
|
|
|
|
return NULL;
|
|
|
|
if (!move_file())
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
mapping = vma->vm_file->f_mapping;
|
|
|
|
if (pte_none(ptent))
|
|
|
|
pgoff = linear_page_index(vma, addr);
|
|
|
|
else /* pte_file(ptent) is true */
|
|
|
|
pgoff = pte_to_pgoff(ptent);
|
|
|
|
|
|
|
|
/* page is moved even if it's not RSS of this task(page-faulted). */
|
2011-08-04 06:21:24 +07:00
|
|
|
page = find_get_page(mapping, pgoff);
|
|
|
|
|
|
|
|
#ifdef CONFIG_SWAP
|
|
|
|
/* shmem/tmpfs may report page out on swap: account for that too. */
|
|
|
|
if (radix_tree_exceptional_entry(page)) {
|
|
|
|
swp_entry_t swap = radix_to_swp_entry(page);
|
2010-05-27 04:42:39 +07:00
|
|
|
if (do_swap_account)
|
2011-08-04 06:21:24 +07:00
|
|
|
*entry = swap;
|
|
|
|
page = find_get_page(&swapper_space, swap.val);
|
2010-05-27 04:42:39 +07:00
|
|
|
}
|
2011-08-04 06:21:24 +07:00
|
|
|
#endif
|
2010-05-27 04:42:39 +07:00
|
|
|
return page;
|
|
|
|
}
|
|
|
|
|
2012-03-22 06:34:27 +07:00
|
|
|
static enum mc_target_type get_mctgt_type(struct vm_area_struct *vma,
|
2010-05-27 04:42:38 +07:00
|
|
|
unsigned long addr, pte_t ptent, union mc_target *target)
|
|
|
|
{
|
|
|
|
struct page *page = NULL;
|
|
|
|
struct page_cgroup *pc;
|
2012-03-22 06:34:27 +07:00
|
|
|
enum mc_target_type ret = MC_TARGET_NONE;
|
2010-05-27 04:42:38 +07:00
|
|
|
swp_entry_t ent = { .val = 0 };
|
|
|
|
|
|
|
|
if (pte_present(ptent))
|
|
|
|
page = mc_handle_present_pte(vma, addr, ptent);
|
|
|
|
else if (is_swap_pte(ptent))
|
|
|
|
page = mc_handle_swap_pte(vma, addr, ptent, &ent);
|
2010-05-27 04:42:39 +07:00
|
|
|
else if (pte_none(ptent) || pte_file(ptent))
|
|
|
|
page = mc_handle_file_pte(vma, addr, ptent, &ent);
|
2010-05-27 04:42:38 +07:00
|
|
|
|
|
|
|
if (!page && !ent.val)
|
2012-03-22 06:34:27 +07:00
|
|
|
return ret;
|
2010-03-11 06:22:17 +07:00
|
|
|
if (page) {
|
|
|
|
pc = lookup_page_cgroup(page);
|
|
|
|
/*
|
|
|
|
* Do only loose check w/o page_cgroup lock.
|
|
|
|
* mem_cgroup_move_account() checks the pc is valid or not under
|
|
|
|
* the lock.
|
|
|
|
*/
|
|
|
|
if (PageCgroupUsed(pc) && pc->mem_cgroup == mc.from) {
|
|
|
|
ret = MC_TARGET_PAGE;
|
|
|
|
if (target)
|
|
|
|
target->page = page;
|
|
|
|
}
|
|
|
|
if (!ret || !target)
|
|
|
|
put_page(page);
|
|
|
|
}
|
2010-05-27 04:42:38 +07:00
|
|
|
/* There is a swap entry and a page doesn't exist or isn't charged */
|
|
|
|
if (ent.val && !ret &&
|
2012-01-13 08:18:48 +07:00
|
|
|
css_id(&mc.from->css) == lookup_swap_cgroup_id(ent)) {
|
2010-05-12 04:06:58 +07:00
|
|
|
ret = MC_TARGET_SWAP;
|
|
|
|
if (target)
|
|
|
|
target->ent = ent;
|
2010-03-11 06:22:14 +07:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-03-22 06:34:28 +07:00
|
|
|
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
|
|
|
/*
|
|
|
|
* We don't consider swapping or file mapped pages because THP does not
|
|
|
|
* support them for now.
|
|
|
|
* Caller should make sure that pmd_trans_huge(pmd) is true.
|
|
|
|
*/
|
|
|
|
static enum mc_target_type get_mctgt_type_thp(struct vm_area_struct *vma,
|
|
|
|
unsigned long addr, pmd_t pmd, union mc_target *target)
|
|
|
|
{
|
|
|
|
struct page *page = NULL;
|
|
|
|
struct page_cgroup *pc;
|
|
|
|
enum mc_target_type ret = MC_TARGET_NONE;
|
|
|
|
|
|
|
|
page = pmd_page(pmd);
|
|
|
|
VM_BUG_ON(!page || !PageHead(page));
|
|
|
|
if (!move_anon())
|
|
|
|
return ret;
|
|
|
|
pc = lookup_page_cgroup(page);
|
|
|
|
if (PageCgroupUsed(pc) && pc->mem_cgroup == mc.from) {
|
|
|
|
ret = MC_TARGET_PAGE;
|
|
|
|
if (target) {
|
|
|
|
get_page(page);
|
|
|
|
target->page = page;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
static inline enum mc_target_type get_mctgt_type_thp(struct vm_area_struct *vma,
|
|
|
|
unsigned long addr, pmd_t pmd, union mc_target *target)
|
|
|
|
{
|
|
|
|
return MC_TARGET_NONE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2010-03-11 06:22:14 +07:00
|
|
|
static int mem_cgroup_count_precharge_pte_range(pmd_t *pmd,
|
|
|
|
unsigned long addr, unsigned long end,
|
|
|
|
struct mm_walk *walk)
|
|
|
|
{
|
|
|
|
struct vm_area_struct *vma = walk->private;
|
|
|
|
pte_t *pte;
|
|
|
|
spinlock_t *ptl;
|
|
|
|
|
2012-03-22 06:34:28 +07:00
|
|
|
if (pmd_trans_huge_lock(pmd, vma) == 1) {
|
|
|
|
if (get_mctgt_type_thp(vma, addr, *pmd, NULL) == MC_TARGET_PAGE)
|
|
|
|
mc.precharge += HPAGE_PMD_NR;
|
|
|
|
spin_unlock(&vma->vm_mm->page_table_lock);
|
mm: thp: fix pmd_bad() triggering in code paths holding mmap_sem read mode
In some cases it may happen that pmd_none_or_clear_bad() is called with
the mmap_sem hold in read mode. In those cases the huge page faults can
allocate hugepmds under pmd_none_or_clear_bad() and that can trigger a
false positive from pmd_bad() that will not like to see a pmd
materializing as trans huge.
It's not khugepaged causing the problem, khugepaged holds the mmap_sem
in write mode (and all those sites must hold the mmap_sem in read mode
to prevent pagetables to go away from under them, during code review it
seems vm86 mode on 32bit kernels requires that too unless it's
restricted to 1 thread per process or UP builds). The race is only with
the huge pagefaults that can convert a pmd_none() into a
pmd_trans_huge().
Effectively all these pmd_none_or_clear_bad() sites running with
mmap_sem in read mode are somewhat speculative with the page faults, and
the result is always undefined when they run simultaneously. This is
probably why it wasn't common to run into this. For example if the
madvise(MADV_DONTNEED) runs zap_page_range() shortly before the page
fault, the hugepage will not be zapped, if the page fault runs first it
will be zapped.
Altering pmd_bad() not to error out if it finds hugepmds won't be enough
to fix this, because zap_pmd_range would then proceed to call
zap_pte_range (which would be incorrect if the pmd become a
pmd_trans_huge()).
The simplest way to fix this is to read the pmd in the local stack
(regardless of what we read, no need of actual CPU barriers, only
compiler barrier needed), and be sure it is not changing under the code
that computes its value. Even if the real pmd is changing under the
value we hold on the stack, we don't care. If we actually end up in
zap_pte_range it means the pmd was not none already and it was not huge,
and it can't become huge from under us (khugepaged locking explained
above).
All we need is to enforce that there is no way anymore that in a code
path like below, pmd_trans_huge can be false, but pmd_none_or_clear_bad
can run into a hugepmd. The overhead of a barrier() is just a compiler
tweak and should not be measurable (I only added it for THP builds). I
don't exclude different compiler versions may have prevented the race
too by caching the value of *pmd on the stack (that hasn't been
verified, but it wouldn't be impossible considering
pmd_none_or_clear_bad, pmd_bad, pmd_trans_huge, pmd_none are all inlines
and there's no external function called in between pmd_trans_huge and
pmd_none_or_clear_bad).
if (pmd_trans_huge(*pmd)) {
if (next-addr != HPAGE_PMD_SIZE) {
VM_BUG_ON(!rwsem_is_locked(&tlb->mm->mmap_sem));
split_huge_page_pmd(vma->vm_mm, pmd);
} else if (zap_huge_pmd(tlb, vma, pmd, addr))
continue;
/* fall through */
}
if (pmd_none_or_clear_bad(pmd))
Because this race condition could be exercised without special
privileges this was reported in CVE-2012-1179.
The race was identified and fully explained by Ulrich who debugged it.
I'm quoting his accurate explanation below, for reference.
====== start quote =======
mapcount 0 page_mapcount 1
kernel BUG at mm/huge_memory.c:1384!
At some point prior to the panic, a "bad pmd ..." message similar to the
following is logged on the console:
mm/memory.c:145: bad pmd ffff8800376e1f98(80000000314000e7).
The "bad pmd ..." message is logged by pmd_clear_bad() before it clears
the page's PMD table entry.
143 void pmd_clear_bad(pmd_t *pmd)
144 {
-> 145 pmd_ERROR(*pmd);
146 pmd_clear(pmd);
147 }
After the PMD table entry has been cleared, there is an inconsistency
between the actual number of PMD table entries that are mapping the page
and the page's map count (_mapcount field in struct page). When the page
is subsequently reclaimed, __split_huge_page() detects this inconsistency.
1381 if (mapcount != page_mapcount(page))
1382 printk(KERN_ERR "mapcount %d page_mapcount %d\n",
1383 mapcount, page_mapcount(page));
-> 1384 BUG_ON(mapcount != page_mapcount(page));
The root cause of the problem is a race of two threads in a multithreaded
process. Thread B incurs a page fault on a virtual address that has never
been accessed (PMD entry is zero) while Thread A is executing an madvise()
system call on a virtual address within the same 2 MB (huge page) range.
virtual address space
.---------------------.
| |
| |
.-|---------------------|
| | |
| | |<-- B(fault)
| | |
2 MB | |/////////////////////|-.
huge < |/////////////////////| > A(range)
page | |/////////////////////|-'
| | |
| | |
'-|---------------------|
| |
| |
'---------------------'
- Thread A is executing an madvise(..., MADV_DONTNEED) system call
on the virtual address range "A(range)" shown in the picture.
sys_madvise
// Acquire the semaphore in shared mode.
down_read(¤t->mm->mmap_sem)
...
madvise_vma
switch (behavior)
case MADV_DONTNEED:
madvise_dontneed
zap_page_range
unmap_vmas
unmap_page_range
zap_pud_range
zap_pmd_range
//
// Assume that this huge page has never been accessed.
// I.e. content of the PMD entry is zero (not mapped).
//
if (pmd_trans_huge(*pmd)) {
// We don't get here due to the above assumption.
}
//
// Assume that Thread B incurred a page fault and
.---------> // sneaks in here as shown below.
| //
| if (pmd_none_or_clear_bad(pmd))
| {
| if (unlikely(pmd_bad(*pmd)))
| pmd_clear_bad
| {
| pmd_ERROR
| // Log "bad pmd ..." message here.
| pmd_clear
| // Clear the page's PMD entry.
| // Thread B incremented the map count
| // in page_add_new_anon_rmap(), but
| // now the page is no longer mapped
| // by a PMD entry (-> inconsistency).
| }
| }
|
v
- Thread B is handling a page fault on virtual address "B(fault)" shown
in the picture.
...
do_page_fault
__do_page_fault
// Acquire the semaphore in shared mode.
down_read_trylock(&mm->mmap_sem)
...
handle_mm_fault
if (pmd_none(*pmd) && transparent_hugepage_enabled(vma))
// We get here due to the above assumption (PMD entry is zero).
do_huge_pmd_anonymous_page
alloc_hugepage_vma
// Allocate a new transparent huge page here.
...
__do_huge_pmd_anonymous_page
...
spin_lock(&mm->page_table_lock)
...
page_add_new_anon_rmap
// Here we increment the page's map count (starts at -1).
atomic_set(&page->_mapcount, 0)
set_pmd_at
// Here we set the page's PMD entry which will be cleared
// when Thread A calls pmd_clear_bad().
...
spin_unlock(&mm->page_table_lock)
The mmap_sem does not prevent the race because both threads are acquiring
it in shared mode (down_read). Thread B holds the page_table_lock while
the page's map count and PMD table entry are updated. However, Thread A
does not synchronize on that lock.
====== end quote =======
[akpm@linux-foundation.org: checkpatch fixes]
Reported-by: Ulrich Obergfell <uobergfe@redhat.com>
Signed-off-by: Andrea Arcangeli <aarcange@redhat.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Cc: Mel Gorman <mgorman@suse.de>
Cc: Hugh Dickins <hughd@google.com>
Cc: Dave Jones <davej@redhat.com>
Acked-by: Larry Woodman <lwoodman@redhat.com>
Acked-by: Rik van Riel <riel@redhat.com>
Cc: <stable@vger.kernel.org> [2.6.38+]
Cc: Mark Salter <msalter@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-22 06:33:42 +07:00
|
|
|
return 0;
|
2012-03-22 06:34:28 +07:00
|
|
|
}
|
2011-03-23 06:32:56 +07:00
|
|
|
|
2012-03-29 04:42:40 +07:00
|
|
|
if (pmd_trans_unstable(pmd))
|
|
|
|
return 0;
|
2010-03-11 06:22:14 +07:00
|
|
|
pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
|
|
|
|
for (; addr != end; pte++, addr += PAGE_SIZE)
|
2012-03-22 06:34:27 +07:00
|
|
|
if (get_mctgt_type(vma, addr, *pte, NULL))
|
2010-03-11 06:22:14 +07:00
|
|
|
mc.precharge++; /* increment precharge temporarily */
|
|
|
|
pte_unmap_unlock(pte - 1, ptl);
|
|
|
|
cond_resched();
|
|
|
|
|
2010-03-11 06:22:13 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-03-11 06:22:14 +07:00
|
|
|
static unsigned long mem_cgroup_count_precharge(struct mm_struct *mm)
|
|
|
|
{
|
|
|
|
unsigned long precharge;
|
|
|
|
struct vm_area_struct *vma;
|
|
|
|
|
2011-01-14 06:47:41 +07:00
|
|
|
down_read(&mm->mmap_sem);
|
2010-03-11 06:22:14 +07:00
|
|
|
for (vma = mm->mmap; vma; vma = vma->vm_next) {
|
|
|
|
struct mm_walk mem_cgroup_count_precharge_walk = {
|
|
|
|
.pmd_entry = mem_cgroup_count_precharge_pte_range,
|
|
|
|
.mm = mm,
|
|
|
|
.private = vma,
|
|
|
|
};
|
|
|
|
if (is_vm_hugetlb_page(vma))
|
|
|
|
continue;
|
|
|
|
walk_page_range(vma->vm_start, vma->vm_end,
|
|
|
|
&mem_cgroup_count_precharge_walk);
|
|
|
|
}
|
2011-01-14 06:47:41 +07:00
|
|
|
up_read(&mm->mmap_sem);
|
2010-03-11 06:22:14 +07:00
|
|
|
|
|
|
|
precharge = mc.precharge;
|
|
|
|
mc.precharge = 0;
|
|
|
|
|
|
|
|
return precharge;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mem_cgroup_precharge_mc(struct mm_struct *mm)
|
|
|
|
{
|
2011-01-14 06:47:41 +07:00
|
|
|
unsigned long precharge = mem_cgroup_count_precharge(mm);
|
|
|
|
|
|
|
|
VM_BUG_ON(mc.moving_task);
|
|
|
|
mc.moving_task = current;
|
|
|
|
return mem_cgroup_do_precharge(precharge);
|
2010-03-11 06:22:14 +07:00
|
|
|
}
|
|
|
|
|
2011-01-14 06:47:41 +07:00
|
|
|
/* cancels all extra charges on mc.from and mc.to, and wakes up all waiters. */
|
|
|
|
static void __mem_cgroup_clear_mc(void)
|
2010-03-11 06:22:14 +07:00
|
|
|
{
|
2010-08-11 08:02:58 +07:00
|
|
|
struct mem_cgroup *from = mc.from;
|
|
|
|
struct mem_cgroup *to = mc.to;
|
|
|
|
|
2010-03-11 06:22:14 +07:00
|
|
|
/* we must uncharge all the leftover precharges from mc.to */
|
2010-03-11 06:22:15 +07:00
|
|
|
if (mc.precharge) {
|
|
|
|
__mem_cgroup_cancel_charge(mc.to, mc.precharge);
|
|
|
|
mc.precharge = 0;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* we didn't uncharge from mc.from at mem_cgroup_move_account(), so
|
|
|
|
* we must uncharge here.
|
|
|
|
*/
|
|
|
|
if (mc.moved_charge) {
|
|
|
|
__mem_cgroup_cancel_charge(mc.from, mc.moved_charge);
|
|
|
|
mc.moved_charge = 0;
|
2010-03-11 06:22:14 +07:00
|
|
|
}
|
2010-03-11 06:22:18 +07:00
|
|
|
/* we must fixup refcnts and charges */
|
|
|
|
if (mc.moved_swap) {
|
|
|
|
/* uncharge swap account from the old cgroup */
|
|
|
|
if (!mem_cgroup_is_root(mc.from))
|
|
|
|
res_counter_uncharge(&mc.from->memsw,
|
|
|
|
PAGE_SIZE * mc.moved_swap);
|
|
|
|
__mem_cgroup_put(mc.from, mc.moved_swap);
|
|
|
|
|
|
|
|
if (!mem_cgroup_is_root(mc.to)) {
|
|
|
|
/*
|
|
|
|
* we charged both to->res and to->memsw, so we should
|
|
|
|
* uncharge to->res.
|
|
|
|
*/
|
|
|
|
res_counter_uncharge(&mc.to->res,
|
|
|
|
PAGE_SIZE * mc.moved_swap);
|
|
|
|
}
|
|
|
|
/* we've already done mem_cgroup_get(mc.to) */
|
|
|
|
mc.moved_swap = 0;
|
|
|
|
}
|
2011-01-14 06:47:41 +07:00
|
|
|
memcg_oom_recover(from);
|
|
|
|
memcg_oom_recover(to);
|
|
|
|
wake_up_all(&mc.waitq);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mem_cgroup_clear_mc(void)
|
|
|
|
{
|
|
|
|
struct mem_cgroup *from = mc.from;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* we must clear moving_task before waking up waiters at the end of
|
|
|
|
* task migration.
|
|
|
|
*/
|
|
|
|
mc.moving_task = NULL;
|
|
|
|
__mem_cgroup_clear_mc();
|
2010-08-11 08:02:58 +07:00
|
|
|
spin_lock(&mc.lock);
|
2010-03-11 06:22:14 +07:00
|
|
|
mc.from = NULL;
|
|
|
|
mc.to = NULL;
|
2010-08-11 08:02:58 +07:00
|
|
|
spin_unlock(&mc.lock);
|
memcg: avoid lock in updating file_mapped (Was fix race in file_mapped accouting flag management
At accounting file events per memory cgroup, we need to find memory cgroup
via page_cgroup->mem_cgroup. Now, we use lock_page_cgroup() for guarantee
pc->mem_cgroup is not overwritten while we make use of it.
But, considering the context which page-cgroup for files are accessed,
we can use alternative light-weight mutual execusion in the most case.
At handling file-caches, the only race we have to take care of is "moving"
account, IOW, overwriting page_cgroup->mem_cgroup. (See comment in the
patch)
Unlike charge/uncharge, "move" happens not so frequently. It happens only when
rmdir() and task-moving (with a special settings.)
This patch adds a race-checker for file-cache-status accounting v.s. account
moving. The new per-cpu-per-memcg counter MEM_CGROUP_ON_MOVE is added.
The routine for account move
1. Increment it before start moving
2. Call synchronize_rcu()
3. Decrement it after the end of moving.
By this, file-status-counting routine can check it needs to call
lock_page_cgroup(). In most case, I doesn't need to call it.
Following is a perf data of a process which mmap()/munmap 32MB of file cache
in a minute.
Before patch:
28.25% mmap mmap [.] main
22.64% mmap [kernel.kallsyms] [k] page_fault
9.96% mmap [kernel.kallsyms] [k] mem_cgroup_update_file_mapped
3.67% mmap [kernel.kallsyms] [k] filemap_fault
3.50% mmap [kernel.kallsyms] [k] unmap_vmas
2.99% mmap [kernel.kallsyms] [k] __do_fault
2.76% mmap [kernel.kallsyms] [k] find_get_page
After patch:
30.00% mmap mmap [.] main
23.78% mmap [kernel.kallsyms] [k] page_fault
5.52% mmap [kernel.kallsyms] [k] mem_cgroup_update_file_mapped
3.81% mmap [kernel.kallsyms] [k] unmap_vmas
3.26% mmap [kernel.kallsyms] [k] find_get_page
3.18% mmap [kernel.kallsyms] [k] __do_fault
3.03% mmap [kernel.kallsyms] [k] filemap_fault
2.40% mmap [kernel.kallsyms] [k] handle_mm_fault
2.40% mmap [kernel.kallsyms] [k] do_page_fault
This patch reduces memcg's cost to some extent.
(mem_cgroup_update_file_mapped is called by both of map/unmap)
Note: It seems some more improvements are required..but no idea.
maybe removing set/unset flag is required.
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Greg Thelen <gthelen@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-10-28 05:33:40 +07:00
|
|
|
mem_cgroup_end_move(from);
|
2010-03-11 06:22:14 +07:00
|
|
|
}
|
|
|
|
|
2012-01-31 12:47:36 +07:00
|
|
|
static int mem_cgroup_can_attach(struct cgroup *cgroup,
|
|
|
|
struct cgroup_taskset *tset)
|
2010-03-11 06:22:13 +07:00
|
|
|
{
|
2011-12-13 09:12:21 +07:00
|
|
|
struct task_struct *p = cgroup_taskset_first(tset);
|
2010-03-11 06:22:13 +07:00
|
|
|
int ret = 0;
|
2011-11-03 03:38:15 +07:00
|
|
|
struct mem_cgroup *memcg = mem_cgroup_from_cont(cgroup);
|
2010-03-11 06:22:13 +07:00
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
if (memcg->move_charge_at_immigrate) {
|
2010-03-11 06:22:13 +07:00
|
|
|
struct mm_struct *mm;
|
|
|
|
struct mem_cgroup *from = mem_cgroup_from_task(p);
|
|
|
|
|
2011-11-03 03:38:15 +07:00
|
|
|
VM_BUG_ON(from == memcg);
|
2010-03-11 06:22:13 +07:00
|
|
|
|
|
|
|
mm = get_task_mm(p);
|
|
|
|
if (!mm)
|
|
|
|
return 0;
|
|
|
|
/* We move charges only when we move a owner of the mm */
|
2010-03-11 06:22:14 +07:00
|
|
|
if (mm->owner == p) {
|
|
|
|
VM_BUG_ON(mc.from);
|
|
|
|
VM_BUG_ON(mc.to);
|
|
|
|
VM_BUG_ON(mc.precharge);
|
2010-03-11 06:22:15 +07:00
|
|
|
VM_BUG_ON(mc.moved_charge);
|
2010-03-11 06:22:18 +07:00
|
|
|
VM_BUG_ON(mc.moved_swap);
|
memcg: avoid lock in updating file_mapped (Was fix race in file_mapped accouting flag management
At accounting file events per memory cgroup, we need to find memory cgroup
via page_cgroup->mem_cgroup. Now, we use lock_page_cgroup() for guarantee
pc->mem_cgroup is not overwritten while we make use of it.
But, considering the context which page-cgroup for files are accessed,
we can use alternative light-weight mutual execusion in the most case.
At handling file-caches, the only race we have to take care of is "moving"
account, IOW, overwriting page_cgroup->mem_cgroup. (See comment in the
patch)
Unlike charge/uncharge, "move" happens not so frequently. It happens only when
rmdir() and task-moving (with a special settings.)
This patch adds a race-checker for file-cache-status accounting v.s. account
moving. The new per-cpu-per-memcg counter MEM_CGROUP_ON_MOVE is added.
The routine for account move
1. Increment it before start moving
2. Call synchronize_rcu()
3. Decrement it after the end of moving.
By this, file-status-counting routine can check it needs to call
lock_page_cgroup(). In most case, I doesn't need to call it.
Following is a perf data of a process which mmap()/munmap 32MB of file cache
in a minute.
Before patch:
28.25% mmap mmap [.] main
22.64% mmap [kernel.kallsyms] [k] page_fault
9.96% mmap [kernel.kallsyms] [k] mem_cgroup_update_file_mapped
3.67% mmap [kernel.kallsyms] [k] filemap_fault
3.50% mmap [kernel.kallsyms] [k] unmap_vmas
2.99% mmap [kernel.kallsyms] [k] __do_fault
2.76% mmap [kernel.kallsyms] [k] find_get_page
After patch:
30.00% mmap mmap [.] main
23.78% mmap [kernel.kallsyms] [k] page_fault
5.52% mmap [kernel.kallsyms] [k] mem_cgroup_update_file_mapped
3.81% mmap [kernel.kallsyms] [k] unmap_vmas
3.26% mmap [kernel.kallsyms] [k] find_get_page
3.18% mmap [kernel.kallsyms] [k] __do_fault
3.03% mmap [kernel.kallsyms] [k] filemap_fault
2.40% mmap [kernel.kallsyms] [k] handle_mm_fault
2.40% mmap [kernel.kallsyms] [k] do_page_fault
This patch reduces memcg's cost to some extent.
(mem_cgroup_update_file_mapped is called by both of map/unmap)
Note: It seems some more improvements are required..but no idea.
maybe removing set/unset flag is required.
Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Cc: Balbir Singh <balbir@in.ibm.com>
Cc: Greg Thelen <gthelen@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2010-10-28 05:33:40 +07:00
|
|
|
mem_cgroup_start_move(from);
|
2010-08-11 08:02:58 +07:00
|
|
|
spin_lock(&mc.lock);
|
2010-03-11 06:22:14 +07:00
|
|
|
mc.from = from;
|
2011-11-03 03:38:15 +07:00
|
|
|
mc.to = memcg;
|
2010-08-11 08:02:58 +07:00
|
|
|
spin_unlock(&mc.lock);
|
2011-01-14 06:47:41 +07:00
|
|
|
/* We set mc.moving_task later */
|
2010-03-11 06:22:14 +07:00
|
|
|
|
|
|
|
ret = mem_cgroup_precharge_mc(mm);
|
|
|
|
if (ret)
|
|
|
|
mem_cgroup_clear_mc();
|
2011-01-14 06:47:41 +07:00
|
|
|
}
|
|
|
|
mmput(mm);
|
2010-03-11 06:22:13 +07:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-01-31 12:47:36 +07:00
|
|
|
static void mem_cgroup_cancel_attach(struct cgroup *cgroup,
|
|
|
|
struct cgroup_taskset *tset)
|
2010-03-11 06:22:13 +07:00
|
|
|
{
|
2010-03-11 06:22:14 +07:00
|
|
|
mem_cgroup_clear_mc();
|
2010-03-11 06:22:13 +07:00
|
|
|
}
|
|
|
|
|
2010-03-11 06:22:14 +07:00
|
|
|
static int mem_cgroup_move_charge_pte_range(pmd_t *pmd,
|
|
|
|
unsigned long addr, unsigned long end,
|
|
|
|
struct mm_walk *walk)
|
2010-03-11 06:22:13 +07:00
|
|
|
{
|
2010-03-11 06:22:14 +07:00
|
|
|
int ret = 0;
|
|
|
|
struct vm_area_struct *vma = walk->private;
|
|
|
|
pte_t *pte;
|
|
|
|
spinlock_t *ptl;
|
2012-03-22 06:34:28 +07:00
|
|
|
enum mc_target_type target_type;
|
|
|
|
union mc_target target;
|
|
|
|
struct page *page;
|
|
|
|
struct page_cgroup *pc;
|
2010-03-11 06:22:14 +07:00
|
|
|
|
2012-03-22 06:34:28 +07:00
|
|
|
/*
|
|
|
|
* We don't take compound_lock() here but no race with splitting thp
|
|
|
|
* happens because:
|
|
|
|
* - if pmd_trans_huge_lock() returns 1, the relevant thp is not
|
|
|
|
* under splitting, which means there's no concurrent thp split,
|
|
|
|
* - if another thread runs into split_huge_page() just after we
|
|
|
|
* entered this if-block, the thread must wait for page table lock
|
|
|
|
* to be unlocked in __split_huge_page_splitting(), where the main
|
|
|
|
* part of thp split is not executed yet.
|
|
|
|
*/
|
|
|
|
if (pmd_trans_huge_lock(pmd, vma) == 1) {
|
2012-05-19 01:28:34 +07:00
|
|
|
if (mc.precharge < HPAGE_PMD_NR) {
|
2012-03-22 06:34:28 +07:00
|
|
|
spin_unlock(&vma->vm_mm->page_table_lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
target_type = get_mctgt_type_thp(vma, addr, *pmd, &target);
|
|
|
|
if (target_type == MC_TARGET_PAGE) {
|
|
|
|
page = target.page;
|
|
|
|
if (!isolate_lru_page(page)) {
|
|
|
|
pc = lookup_page_cgroup(page);
|
|
|
|
if (!mem_cgroup_move_account(page, HPAGE_PMD_NR,
|
2012-05-30 05:07:04 +07:00
|
|
|
pc, mc.from, mc.to)) {
|
2012-03-22 06:34:28 +07:00
|
|
|
mc.precharge -= HPAGE_PMD_NR;
|
|
|
|
mc.moved_charge += HPAGE_PMD_NR;
|
|
|
|
}
|
|
|
|
putback_lru_page(page);
|
|
|
|
}
|
|
|
|
put_page(page);
|
|
|
|
}
|
|
|
|
spin_unlock(&vma->vm_mm->page_table_lock);
|
mm: thp: fix pmd_bad() triggering in code paths holding mmap_sem read mode
In some cases it may happen that pmd_none_or_clear_bad() is called with
the mmap_sem hold in read mode. In those cases the huge page faults can
allocate hugepmds under pmd_none_or_clear_bad() and that can trigger a
false positive from pmd_bad() that will not like to see a pmd
materializing as trans huge.
It's not khugepaged causing the problem, khugepaged holds the mmap_sem
in write mode (and all those sites must hold the mmap_sem in read mode
to prevent pagetables to go away from under them, during code review it
seems vm86 mode on 32bit kernels requires that too unless it's
restricted to 1 thread per process or UP builds). The race is only with
the huge pagefaults that can convert a pmd_none() into a
pmd_trans_huge().
Effectively all these pmd_none_or_clear_bad() sites running with
mmap_sem in read mode are somewhat speculative with the page faults, and
the result is always undefined when they run simultaneously. This is
probably why it wasn't common to run into this. For example if the
madvise(MADV_DONTNEED) runs zap_page_range() shortly before the page
fault, the hugepage will not be zapped, if the page fault runs first it
will be zapped.
Altering pmd_bad() not to error out if it finds hugepmds won't be enough
to fix this, because zap_pmd_range would then proceed to call
zap_pte_range (which would be incorrect if the pmd become a
pmd_trans_huge()).
The simplest way to fix this is to read the pmd in the local stack
(regardless of what we read, no need of actual CPU barriers, only
compiler barrier needed), and be sure it is not changing under the code
that computes its value. Even if the real pmd is changing under the
value we hold on the stack, we don't care. If we actually end up in
zap_pte_range it means the pmd was not none already and it was not huge,
and it can't become huge from under us (khugepaged locking explained
above).
All we need is to enforce that there is no way anymore that in a code
path like below, pmd_trans_huge can be false, but pmd_none_or_clear_bad
can run into a hugepmd. The overhead of a barrier() is just a compiler
tweak and should not be measurable (I only added it for THP builds). I
don't exclude different compiler versions may have prevented the race
too by caching the value of *pmd on the stack (that hasn't been
verified, but it wouldn't be impossible considering
pmd_none_or_clear_bad, pmd_bad, pmd_trans_huge, pmd_none are all inlines
and there's no external function called in between pmd_trans_huge and
pmd_none_or_clear_bad).
if (pmd_trans_huge(*pmd)) {
if (next-addr != HPAGE_PMD_SIZE) {
VM_BUG_ON(!rwsem_is_locked(&tlb->mm->mmap_sem));
split_huge_page_pmd(vma->vm_mm, pmd);
} else if (zap_huge_pmd(tlb, vma, pmd, addr))
continue;
/* fall through */
}
if (pmd_none_or_clear_bad(pmd))
Because this race condition could be exercised without special
privileges this was reported in CVE-2012-1179.
The race was identified and fully explained by Ulrich who debugged it.
I'm quoting his accurate explanation below, for reference.
====== start quote =======
mapcount 0 page_mapcount 1
kernel BUG at mm/huge_memory.c:1384!
At some point prior to the panic, a "bad pmd ..." message similar to the
following is logged on the console:
mm/memory.c:145: bad pmd ffff8800376e1f98(80000000314000e7).
The "bad pmd ..." message is logged by pmd_clear_bad() before it clears
the page's PMD table entry.
143 void pmd_clear_bad(pmd_t *pmd)
144 {
-> 145 pmd_ERROR(*pmd);
146 pmd_clear(pmd);
147 }
After the PMD table entry has been cleared, there is an inconsistency
between the actual number of PMD table entries that are mapping the page
and the page's map count (_mapcount field in struct page). When the page
is subsequently reclaimed, __split_huge_page() detects this inconsistency.
1381 if (mapcount != page_mapcount(page))
1382 printk(KERN_ERR "mapcount %d page_mapcount %d\n",
1383 mapcount, page_mapcount(page));
-> 1384 BUG_ON(mapcount != page_mapcount(page));
The root cause of the problem is a race of two threads in a multithreaded
process. Thread B incurs a page fault on a virtual address that has never
been accessed (PMD entry is zero) while Thread A is executing an madvise()
system call on a virtual address within the same 2 MB (huge page) range.
virtual address space
.---------------------.
| |
| |
.-|---------------------|
| | |
| | |<-- B(fault)
| | |
2 MB | |/////////////////////|-.
huge < |/////////////////////| > A(range)
page | |/////////////////////|-'
| | |
| | |
'-|---------------------|
| |
| |
'---------------------'
- Thread A is executing an madvise(..., MADV_DONTNEED) system call
on the virtual address range "A(range)" shown in the picture.
sys_madvise
// Acquire the semaphore in shared mode.
down_read(¤t->mm->mmap_sem)
...
madvise_vma
switch (behavior)
case MADV_DONTNEED:
madvise_dontneed
zap_page_range
unmap_vmas
unmap_page_range
zap_pud_range
zap_pmd_range
//
// Assume that this huge page has never been accessed.
// I.e. content of the PMD entry is zero (not mapped).
//
if (pmd_trans_huge(*pmd)) {
// We don't get here due to the above assumption.
}
//
// Assume that Thread B incurred a page fault and
.---------> // sneaks in here as shown below.
| //
| if (pmd_none_or_clear_bad(pmd))
| {
| if (unlikely(pmd_bad(*pmd)))
| pmd_clear_bad
| {
| pmd_ERROR
| // Log "bad pmd ..." message here.
| pmd_clear
| // Clear the page's PMD entry.
| // Thread B incremented the map count
| // in page_add_new_anon_rmap(), but
| // now the page is no longer mapped
| // by a PMD entry (-> inconsistency).
| }
| }
|
v
- Thread B is handling a page fault on virtual address "B(fault)" shown
in the picture.
...
do_page_fault
__do_page_fault
// Acquire the semaphore in shared mode.
down_read_trylock(&mm->mmap_sem)
...
handle_mm_fault
if (pmd_none(*pmd) && transparent_hugepage_enabled(vma))
// We get here due to the above assumption (PMD entry is zero).
do_huge_pmd_anonymous_page
alloc_hugepage_vma
// Allocate a new transparent huge page here.
...
__do_huge_pmd_anonymous_page
...
spin_lock(&mm->page_table_lock)
...
page_add_new_anon_rmap
// Here we increment the page's map count (starts at -1).
atomic_set(&page->_mapcount, 0)
set_pmd_at
// Here we set the page's PMD entry which will be cleared
// when Thread A calls pmd_clear_bad().
...
spin_unlock(&mm->page_table_lock)
The mmap_sem does not prevent the race because both threads are acquiring
it in shared mode (down_read). Thread B holds the page_table_lock while
the page's map count and PMD table entry are updated. However, Thread A
does not synchronize on that lock.
====== end quote =======
[akpm@linux-foundation.org: checkpatch fixes]
Reported-by: Ulrich Obergfell <uobergfe@redhat.com>
Signed-off-by: Andrea Arcangeli <aarcange@redhat.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Cc: Mel Gorman <mgorman@suse.de>
Cc: Hugh Dickins <hughd@google.com>
Cc: Dave Jones <davej@redhat.com>
Acked-by: Larry Woodman <lwoodman@redhat.com>
Acked-by: Rik van Riel <riel@redhat.com>
Cc: <stable@vger.kernel.org> [2.6.38+]
Cc: Mark Salter <msalter@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-03-22 06:33:42 +07:00
|
|
|
return 0;
|
2012-03-22 06:34:28 +07:00
|
|
|
}
|
|
|
|
|
2012-03-29 04:42:40 +07:00
|
|
|
if (pmd_trans_unstable(pmd))
|
|
|
|
return 0;
|
2010-03-11 06:22:14 +07:00
|
|
|
retry:
|
|
|
|
pte = pte_offset_map_lock(vma->vm_mm, pmd, addr, &ptl);
|
|
|
|
for (; addr != end; addr += PAGE_SIZE) {
|
|
|
|
pte_t ptent = *(pte++);
|
2010-03-11 06:22:17 +07:00
|
|
|
swp_entry_t ent;
|
2010-03-11 06:22:14 +07:00
|
|
|
|
|
|
|
if (!mc.precharge)
|
|
|
|
break;
|
|
|
|
|
2012-03-22 06:34:27 +07:00
|
|
|
switch (get_mctgt_type(vma, addr, ptent, &target)) {
|
2010-03-11 06:22:14 +07:00
|
|
|
case MC_TARGET_PAGE:
|
|
|
|
page = target.page;
|
|
|
|
if (isolate_lru_page(page))
|
|
|
|
goto put;
|
|
|
|
pc = lookup_page_cgroup(page);
|
2011-03-24 06:42:36 +07:00
|
|
|
if (!mem_cgroup_move_account(page, 1, pc,
|
2012-05-30 05:07:04 +07:00
|
|
|
mc.from, mc.to)) {
|
2010-03-11 06:22:14 +07:00
|
|
|
mc.precharge--;
|
2010-03-11 06:22:15 +07:00
|
|
|
/* we uncharge from mc.from later. */
|
|
|
|
mc.moved_charge++;
|
2010-03-11 06:22:14 +07:00
|
|
|
}
|
|
|
|
putback_lru_page(page);
|
2012-03-22 06:34:27 +07:00
|
|
|
put: /* get_mctgt_type() gets the page */
|
2010-03-11 06:22:14 +07:00
|
|
|
put_page(page);
|
|
|
|
break;
|
2010-03-11 06:22:17 +07:00
|
|
|
case MC_TARGET_SWAP:
|
|
|
|
ent = target.ent;
|
2012-05-30 05:06:51 +07:00
|
|
|
if (!mem_cgroup_move_swap_account(ent, mc.from, mc.to)) {
|
2010-03-11 06:22:17 +07:00
|
|
|
mc.precharge--;
|
2010-03-11 06:22:18 +07:00
|
|
|
/* we fixup refcnts and charges later. */
|
|
|
|
mc.moved_swap++;
|
|
|
|
}
|
2010-03-11 06:22:17 +07:00
|
|
|
break;
|
2010-03-11 06:22:14 +07:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pte_unmap_unlock(pte - 1, ptl);
|
|
|
|
cond_resched();
|
|
|
|
|
|
|
|
if (addr != end) {
|
|
|
|
/*
|
|
|
|
* We have consumed all precharges we got in can_attach().
|
|
|
|
* We try charge one by one, but don't do any additional
|
|
|
|
* charges to mc.to if we have failed in charge once in attach()
|
|
|
|
* phase.
|
|
|
|
*/
|
2010-03-11 06:22:15 +07:00
|
|
|
ret = mem_cgroup_do_precharge(1);
|
2010-03-11 06:22:14 +07:00
|
|
|
if (!ret)
|
|
|
|
goto retry;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mem_cgroup_move_charge(struct mm_struct *mm)
|
|
|
|
{
|
|
|
|
struct vm_area_struct *vma;
|
|
|
|
|
|
|
|
lru_add_drain_all();
|
2011-01-14 06:47:41 +07:00
|
|
|
retry:
|
|
|
|
if (unlikely(!down_read_trylock(&mm->mmap_sem))) {
|
|
|
|
/*
|
|
|
|
* Someone who are holding the mmap_sem might be waiting in
|
|
|
|
* waitq. So we cancel all extra charges, wake up all waiters,
|
|
|
|
* and retry. Because we cancel precharges, we might not be able
|
|
|
|
* to move enough charges, but moving charge is a best-effort
|
|
|
|
* feature anyway, so it wouldn't be a big problem.
|
|
|
|
*/
|
|
|
|
__mem_cgroup_clear_mc();
|
|
|
|
cond_resched();
|
|
|
|
goto retry;
|
|
|
|
}
|
2010-03-11 06:22:14 +07:00
|
|
|
for (vma = mm->mmap; vma; vma = vma->vm_next) {
|
|
|
|
int ret;
|
|
|
|
struct mm_walk mem_cgroup_move_charge_walk = {
|
|
|
|
.pmd_entry = mem_cgroup_move_charge_pte_range,
|
|
|
|
.mm = mm,
|
|
|
|
.private = vma,
|
|
|
|
};
|
|
|
|
if (is_vm_hugetlb_page(vma))
|
|
|
|
continue;
|
|
|
|
ret = walk_page_range(vma->vm_start, vma->vm_end,
|
|
|
|
&mem_cgroup_move_charge_walk);
|
|
|
|
if (ret)
|
|
|
|
/*
|
|
|
|
* means we have consumed all precharges and failed in
|
|
|
|
* doing additional charge. Just abandon here.
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
}
|
2011-01-14 06:47:41 +07:00
|
|
|
up_read(&mm->mmap_sem);
|
2010-03-11 06:22:13 +07:00
|
|
|
}
|
|
|
|
|
2012-01-31 12:47:36 +07:00
|
|
|
static void mem_cgroup_move_task(struct cgroup *cont,
|
|
|
|
struct cgroup_taskset *tset)
|
2008-02-07 15:13:54 +07:00
|
|
|
{
|
2011-12-13 09:12:21 +07:00
|
|
|
struct task_struct *p = cgroup_taskset_first(tset);
|
2011-06-16 05:08:13 +07:00
|
|
|
struct mm_struct *mm = get_task_mm(p);
|
2011-01-14 06:47:41 +07:00
|
|
|
|
|
|
|
if (mm) {
|
2011-06-16 05:08:13 +07:00
|
|
|
if (mc.to)
|
|
|
|
mem_cgroup_move_charge(mm);
|
2011-01-14 06:47:41 +07:00
|
|
|
mmput(mm);
|
|
|
|
}
|
2011-06-16 05:08:13 +07:00
|
|
|
if (mc.to)
|
|
|
|
mem_cgroup_clear_mc();
|
2008-02-07 15:13:54 +07:00
|
|
|
}
|
2010-03-24 03:35:11 +07:00
|
|
|
#else /* !CONFIG_MMU */
|
2012-01-31 12:47:36 +07:00
|
|
|
static int mem_cgroup_can_attach(struct cgroup *cgroup,
|
|
|
|
struct cgroup_taskset *tset)
|
2010-03-24 03:35:11 +07:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2012-01-31 12:47:36 +07:00
|
|
|
static void mem_cgroup_cancel_attach(struct cgroup *cgroup,
|
|
|
|
struct cgroup_taskset *tset)
|
2010-03-24 03:35:11 +07:00
|
|
|
{
|
|
|
|
}
|
2012-01-31 12:47:36 +07:00
|
|
|
static void mem_cgroup_move_task(struct cgroup *cont,
|
|
|
|
struct cgroup_taskset *tset)
|
2010-03-24 03:35:11 +07:00
|
|
|
{
|
|
|
|
}
|
|
|
|
#endif
|
2008-02-07 15:13:54 +07:00
|
|
|
|
2008-02-07 15:13:50 +07:00
|
|
|
struct cgroup_subsys mem_cgroup_subsys = {
|
|
|
|
.name = "memory",
|
|
|
|
.subsys_id = mem_cgroup_subsys_id,
|
|
|
|
.create = mem_cgroup_create,
|
2008-02-07 15:14:28 +07:00
|
|
|
.pre_destroy = mem_cgroup_pre_destroy,
|
2008-02-07 15:13:50 +07:00
|
|
|
.destroy = mem_cgroup_destroy,
|
2010-03-11 06:22:13 +07:00
|
|
|
.can_attach = mem_cgroup_can_attach,
|
|
|
|
.cancel_attach = mem_cgroup_cancel_attach,
|
2008-02-07 15:13:54 +07:00
|
|
|
.attach = mem_cgroup_move_task,
|
2012-04-02 02:09:55 +07:00
|
|
|
.base_cftypes = mem_cgroup_files,
|
2008-02-07 15:14:31 +07:00
|
|
|
.early_init = 0,
|
2009-04-03 06:57:33 +07:00
|
|
|
.use_id = 1,
|
cgroup: make css->refcnt clearing on cgroup removal optional
Currently, cgroup removal tries to drain all css references. If there
are active css references, the removal logic waits and retries
->pre_detroy() until either all refs drop to zero or removal is
cancelled.
This semantics is unusual and adds non-trivial complexity to cgroup
core and IMHO is fundamentally misguided in that it couples internal
implementation details (references to internal data structure) with
externally visible operation (rmdir). To userland, this is a behavior
peculiarity which is unnecessary and difficult to expect (css refs is
otherwise invisible from userland), and, to policy implementations,
this is an unnecessary restriction (e.g. blkcg wants to hold css refs
for caching purposes but can't as that becomes visible as rmdir hang).
Unfortunately, memcg currently depends on ->pre_destroy() retrials and
cgroup removal vetoing and can't be immmediately switched to the new
behavior. This patch introduces the new behavior of not waiting for
css refs to drain and maintains the old behavior for subsystems which
have __DEPRECATED_clear_css_refs set.
Once, memcg is updated, we can drop the code paths for the old
behavior as proposed in the following patch. Note that the following
patch is incorrect in that dput work item is in cgroup and may lose
some of dputs when multiples css's are released back-to-back, and
__css_put() triggers check_for_release() when refcnt reaches 0 instead
of 1; however, it shows what part can be removed.
http://thread.gmane.org/gmane.linux.kernel.containers/22559/focus=75251
Note that, in not-too-distant future, cgroup core will start emitting
warning messages for subsys which require the old behavior, so please
get moving.
Signed-off-by: Tejun Heo <tj@kernel.org>
Acked-by: Li Zefan <lizf@cn.fujitsu.com>
Cc: Vivek Goyal <vgoyal@redhat.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: Balbir Singh <bsingharora@gmail.com>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
2012-04-02 02:09:56 +07:00
|
|
|
.__DEPRECATED_clear_css_refs = true,
|
2008-02-07 15:13:50 +07:00
|
|
|
};
|
2009-01-08 09:07:57 +07:00
|
|
|
|
2012-08-01 06:43:02 +07:00
|
|
|
#ifdef CONFIG_MEMCG_SWAP
|
2010-11-25 03:57:08 +07:00
|
|
|
static int __init enable_swap_account(char *s)
|
|
|
|
{
|
|
|
|
/* consider enabled if no parameter or 1 is given */
|
2011-05-25 07:12:50 +07:00
|
|
|
if (!strcmp(s, "1"))
|
2010-11-25 03:57:08 +07:00
|
|
|
really_do_swap_account = 1;
|
2011-05-25 07:12:50 +07:00
|
|
|
else if (!strcmp(s, "0"))
|
2010-11-25 03:57:08 +07:00
|
|
|
really_do_swap_account = 0;
|
|
|
|
return 1;
|
|
|
|
}
|
2011-05-25 07:12:50 +07:00
|
|
|
__setup("swapaccount=", enable_swap_account);
|
2009-01-08 09:07:57 +07:00
|
|
|
|
|
|
|
#endif
|