27a7faa077
For accounting swap, we need a record per swap entry, at least. This patch adds following function. - swap_cgroup_swapon() .... called from swapon - swap_cgroup_swapoff() ... called at the end of swapoff. - swap_cgroup_record() .... record information of swap entry. - swap_cgroup_lookup() .... lookup information of swap entry. This patch just implements "how to record information". No actual method for limit the usage of swap. These routine uses flat table to record and lookup. "wise" lookup system like radix-tree requires requires memory allocation at new records but swap-out is usually called under memory shortage (or memcg hits limit.) So, I used static allocation. (maybe dynamic allocation is not very hard but it adds additional memory allocation in memory shortage path.) Note1: In this, we use pointer to record information and this means 8bytes per swap entry. I think we can reduce this when we create "id of cgroup" in the range of 0-65535 or 0-255. Reported-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp> Reviewed-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp> Tested-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp> Reported-by: Hugh Dickins <hugh@veritas.com> Reported-by: Balbir Singh <balbir@linux.vnet.ibm.com> Reported-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> Cc: Pavel Emelianov <xemul@openvz.org> Cc: Li Zefan <lizf@cn.fujitsu.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
143 lines
3.4 KiB
C
143 lines
3.4 KiB
C
#ifndef __LINUX_PAGE_CGROUP_H
|
|
#define __LINUX_PAGE_CGROUP_H
|
|
|
|
#ifdef CONFIG_CGROUP_MEM_RES_CTLR
|
|
#include <linux/bit_spinlock.h>
|
|
/*
|
|
* Page Cgroup can be considered as an extended mem_map.
|
|
* A page_cgroup page is associated with every page descriptor. The
|
|
* page_cgroup helps us identify information about the cgroup
|
|
* All page cgroups are allocated at boot or memory hotplug event,
|
|
* then the page cgroup for pfn always exists.
|
|
*/
|
|
struct page_cgroup {
|
|
unsigned long flags;
|
|
struct mem_cgroup *mem_cgroup;
|
|
struct page *page;
|
|
struct list_head lru; /* per cgroup LRU list */
|
|
};
|
|
|
|
void __meminit pgdat_page_cgroup_init(struct pglist_data *pgdat);
|
|
void __init page_cgroup_init(void);
|
|
struct page_cgroup *lookup_page_cgroup(struct page *page);
|
|
|
|
enum {
|
|
/* flags for mem_cgroup */
|
|
PCG_LOCK, /* page cgroup is locked */
|
|
PCG_CACHE, /* charged as cache */
|
|
PCG_USED, /* this object is in use. */
|
|
/* flags for LRU placement */
|
|
PCG_ACTIVE, /* page is active in this cgroup */
|
|
PCG_FILE, /* page is file system backed */
|
|
PCG_UNEVICTABLE, /* page is unevictableable */
|
|
};
|
|
|
|
#define TESTPCGFLAG(uname, lname) \
|
|
static inline int PageCgroup##uname(struct page_cgroup *pc) \
|
|
{ return test_bit(PCG_##lname, &pc->flags); }
|
|
|
|
#define SETPCGFLAG(uname, lname) \
|
|
static inline void SetPageCgroup##uname(struct page_cgroup *pc)\
|
|
{ set_bit(PCG_##lname, &pc->flags); }
|
|
|
|
#define CLEARPCGFLAG(uname, lname) \
|
|
static inline void ClearPageCgroup##uname(struct page_cgroup *pc) \
|
|
{ clear_bit(PCG_##lname, &pc->flags); }
|
|
|
|
/* Cache flag is set only once (at allocation) */
|
|
TESTPCGFLAG(Cache, CACHE)
|
|
|
|
TESTPCGFLAG(Used, USED)
|
|
CLEARPCGFLAG(Used, USED)
|
|
|
|
/* LRU management flags (from global-lru definition) */
|
|
TESTPCGFLAG(File, FILE)
|
|
SETPCGFLAG(File, FILE)
|
|
CLEARPCGFLAG(File, FILE)
|
|
|
|
TESTPCGFLAG(Active, ACTIVE)
|
|
SETPCGFLAG(Active, ACTIVE)
|
|
CLEARPCGFLAG(Active, ACTIVE)
|
|
|
|
TESTPCGFLAG(Unevictable, UNEVICTABLE)
|
|
SETPCGFLAG(Unevictable, UNEVICTABLE)
|
|
CLEARPCGFLAG(Unevictable, UNEVICTABLE)
|
|
|
|
static inline int page_cgroup_nid(struct page_cgroup *pc)
|
|
{
|
|
return page_to_nid(pc->page);
|
|
}
|
|
|
|
static inline enum zone_type page_cgroup_zid(struct page_cgroup *pc)
|
|
{
|
|
return page_zonenum(pc->page);
|
|
}
|
|
|
|
static inline void lock_page_cgroup(struct page_cgroup *pc)
|
|
{
|
|
bit_spin_lock(PCG_LOCK, &pc->flags);
|
|
}
|
|
|
|
static inline int trylock_page_cgroup(struct page_cgroup *pc)
|
|
{
|
|
return bit_spin_trylock(PCG_LOCK, &pc->flags);
|
|
}
|
|
|
|
static inline void unlock_page_cgroup(struct page_cgroup *pc)
|
|
{
|
|
bit_spin_unlock(PCG_LOCK, &pc->flags);
|
|
}
|
|
|
|
#else /* CONFIG_CGROUP_MEM_RES_CTLR */
|
|
struct page_cgroup;
|
|
|
|
static inline void __meminit pgdat_page_cgroup_init(struct pglist_data *pgdat)
|
|
{
|
|
}
|
|
|
|
static inline struct page_cgroup *lookup_page_cgroup(struct page *page)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static inline void page_cgroup_init(void)
|
|
{
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
|
|
#include <linux/swap.h>
|
|
extern struct mem_cgroup *
|
|
swap_cgroup_record(swp_entry_t ent, struct mem_cgroup *mem);
|
|
extern struct mem_cgroup *lookup_swap_cgroup(swp_entry_t ent);
|
|
extern int swap_cgroup_swapon(int type, unsigned long max_pages);
|
|
extern void swap_cgroup_swapoff(int type);
|
|
#else
|
|
#include <linux/swap.h>
|
|
|
|
static inline
|
|
struct mem_cgroup *swap_cgroup_record(swp_entry_t ent, struct mem_cgroup *mem)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static inline
|
|
struct mem_cgroup *lookup_swap_cgroup(swp_entry_t ent)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
static inline int
|
|
swap_cgroup_swapon(int type, unsigned long max_pages)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static inline void swap_cgroup_swapoff(int type)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#endif
|
|
#endif
|