mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-12 05:48:40 +00:00
![Greg Thelen](/assets/img/avatar_default.png)
This patchset provides the ability for each cgroup to have independent dirty page limits. Limiting dirty memory is like fixing the max amount of dirty (hard to reclaim) page cache used by a cgroup. So, in case of multiple cgroup writers, they will not be able to consume more than their designated share of dirty pages and will be forced to perform write-out if they cross that limit. The patches are based on a series proposed by Andrea Righi in Mar 2010. Overview: - Add page_cgroup flags to record when pages are dirty, in writeback, or nfs unstable. - Extend mem_cgroup to record the total number of pages in each of the interesting dirty states (dirty, writeback, unstable_nfs). - Add dirty parameters similar to the system-wide /proc/sys/vm/dirty_* limits to mem_cgroup. The mem_cgroup dirty parameters are accessible via cgroupfs control files. - Consider both system and per-memcg dirty limits in page writeback when deciding to queue background writeback or block for foreground writeback. Known shortcomings: - When a cgroup dirty limit is exceeded, then bdi writeback is employed to writeback dirty inodes. Bdi writeback considers inodes from any cgroup, not just inodes contributing dirty pages to the cgroup exceeding its limit. - When memory.use_hierarchy is set, then dirty limits are disabled. This is a implementation detail. An enhanced implementation is needed to check the chain of parents to ensure that no dirty limit is exceeded. Performance data: - A page fault microbenchmark workload was used to measure performance, which can be called in read or write mode: f = open(foo. $cpu) truncate(f, 4096) alarm(60) while (1) { p = mmap(f, 4096) if (write) *p = 1 else x = *p munmap(p) } - The workload was called for several points in the patch series in different modes: - s_read is a single threaded reader - s_write is a single threaded writer - p_read is a 16 thread reader, each operating on a different file - p_write is a 16 thread writer, each operating on a different file - Measurements were collected on a 16 core non-numa system using "perf stat --repeat 3". The -a option was used for parallel (p_*) runs. - All numbers are page fault rate (M/sec). Higher is better. - To compare the performance of a kernel without non-memcg compare the first and last rows, neither has memcg configured. The first row does not include any of these memcg patches. - To compare the performance of using memcg dirty limits, compare the baseline (2nd row titled "w/ memcg") with the the code and memcg enabled (2nd to last row titled "all patches"). root_cgroup child_cgroup s_read s_write p_read p_write s_read s_write p_read p_write mmotm w/o memcg 0.428 0.390 0.429 0.388 mmotm w/ memcg 0.411 0.378 0.391 0.362 0.412 0.377 0.385 0.363 all patches 0.384 0.360 0.370 0.348 0.381 0.363 0.368 0.347 all patches 0.431 0.402 0.427 0.395 w/o memcg This patch: Add additional flags to page_cgroup to track dirty pages within a mem_cgroup. Signed-off-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> Signed-off-by: Andrea Righi <arighi@develer.com> Signed-off-by: Greg Thelen <gthelen@google.com> Acked-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp> Cc: Balbir Singh <balbir@linux.vnet.ibm.com> Cc: Minchan Kim <minchan.kim@gmail.com> Cc: Wu Fengguang <fengguang.wu@intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
190 lines
4.8 KiB
C
190 lines
4.8 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);
|
|
|
|
#ifdef CONFIG_SPARSEMEM
|
|
static inline void __init page_cgroup_init_flatmem(void)
|
|
{
|
|
}
|
|
extern void __init page_cgroup_init(void);
|
|
#else
|
|
void __init page_cgroup_init_flatmem(void);
|
|
static inline void __init page_cgroup_init(void)
|
|
{
|
|
}
|
|
#endif
|
|
|
|
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. */
|
|
PCG_ACCT_LRU, /* page has been accounted for */
|
|
PCG_FILE_MAPPED, /* page is accounted as "mapped" */
|
|
PCG_FILE_DIRTY, /* page is dirty */
|
|
PCG_FILE_WRITEBACK, /* page is under writeback */
|
|
PCG_FILE_UNSTABLE_NFS, /* page is NFS unstable */
|
|
PCG_MIGRATION, /* under page migration */
|
|
};
|
|
|
|
#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); }
|
|
|
|
#define TESTCLEARPCGFLAG(uname, lname) \
|
|
static inline int TestClearPageCgroup##uname(struct page_cgroup *pc) \
|
|
{ return test_and_clear_bit(PCG_##lname, &pc->flags); }
|
|
|
|
#define TESTSETPCGFLAG(uname, lname) \
|
|
static inline int TestSetPageCgroup##uname(struct page_cgroup *pc) \
|
|
{ return test_and_set_bit(PCG_##lname, &pc->flags); }
|
|
|
|
/* Cache flag is set only once (at allocation) */
|
|
TESTPCGFLAG(Cache, CACHE)
|
|
CLEARPCGFLAG(Cache, CACHE)
|
|
SETPCGFLAG(Cache, CACHE)
|
|
|
|
TESTPCGFLAG(Used, USED)
|
|
CLEARPCGFLAG(Used, USED)
|
|
SETPCGFLAG(Used, USED)
|
|
|
|
SETPCGFLAG(AcctLRU, ACCT_LRU)
|
|
CLEARPCGFLAG(AcctLRU, ACCT_LRU)
|
|
TESTPCGFLAG(AcctLRU, ACCT_LRU)
|
|
TESTCLEARPCGFLAG(AcctLRU, ACCT_LRU)
|
|
|
|
|
|
SETPCGFLAG(FileMapped, FILE_MAPPED)
|
|
CLEARPCGFLAG(FileMapped, FILE_MAPPED)
|
|
TESTPCGFLAG(FileMapped, FILE_MAPPED)
|
|
|
|
SETPCGFLAG(FileDirty, FILE_DIRTY)
|
|
CLEARPCGFLAG(FileDirty, FILE_DIRTY)
|
|
TESTPCGFLAG(FileDirty, FILE_DIRTY)
|
|
TESTCLEARPCGFLAG(FileDirty, FILE_DIRTY)
|
|
TESTSETPCGFLAG(FileDirty, FILE_DIRTY)
|
|
|
|
SETPCGFLAG(FileWriteback, FILE_WRITEBACK)
|
|
CLEARPCGFLAG(FileWriteback, FILE_WRITEBACK)
|
|
TESTPCGFLAG(FileWriteback, FILE_WRITEBACK)
|
|
|
|
SETPCGFLAG(FileUnstableNFS, FILE_UNSTABLE_NFS)
|
|
CLEARPCGFLAG(FileUnstableNFS, FILE_UNSTABLE_NFS)
|
|
TESTPCGFLAG(FileUnstableNFS, FILE_UNSTABLE_NFS)
|
|
TESTCLEARPCGFLAG(FileUnstableNFS, FILE_UNSTABLE_NFS)
|
|
TESTSETPCGFLAG(FileUnstableNFS, FILE_UNSTABLE_NFS)
|
|
|
|
SETPCGFLAG(Migration, MIGRATION)
|
|
CLEARPCGFLAG(Migration, MIGRATION)
|
|
TESTPCGFLAG(Migration, MIGRATION)
|
|
|
|
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 void unlock_page_cgroup(struct page_cgroup *pc)
|
|
{
|
|
bit_spin_unlock(PCG_LOCK, &pc->flags);
|
|
}
|
|
|
|
static inline int page_is_cgroup_locked(struct page_cgroup *pc)
|
|
{
|
|
return bit_spin_is_locked(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)
|
|
{
|
|
}
|
|
|
|
static inline void __init page_cgroup_init_flatmem(void)
|
|
{
|
|
}
|
|
|
|
#endif
|
|
|
|
#include <linux/swap.h>
|
|
|
|
#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
|
|
extern unsigned short swap_cgroup_cmpxchg(swp_entry_t ent,
|
|
unsigned short old, unsigned short new);
|
|
extern unsigned short swap_cgroup_record(swp_entry_t ent, unsigned short id);
|
|
extern unsigned short 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
|
|
|
|
static inline
|
|
unsigned short swap_cgroup_record(swp_entry_t ent, unsigned short id)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static inline
|
|
unsigned short lookup_swap_cgroup(swp_entry_t ent)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
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
|