cgroup: limit the amount of dirty file pages
Subsystems (4)
RFC PATCH 1/1 cgroup: limit the amount of dirty file pages
This is a totally experimental patch against 2.6.27-rc5-mm1.
It allows to control how much dirty file pages a cgroup can have at any given time. This feature is supposed to be strictly connected to a generic cgroup IO controller (see below).
Interface: a new entry "filedirty" is added to the file memory.stat, reporting the number of dirty file pages (in pages), and a new file memory.file_dirty_limit_in_pages is added in the cgroup filesystem to show/set the current limit.
The overall design is the following.
The dirty file pages are accounted for each cgroup using the memory controller statistics. The memory controller also allows to define an upper bound on the number of dirty file pages. When this upper bound is exceeded the tasks in the cgroup are forced to writeback dirty pages to return within the cgroup limit.
With this functionality a generic cgroup IO controller can apply any kind of limitation or shaping policy directly to the IO requests (elevator, IO scheduler, ...) and it shouldn't care about how fast the userspace apps are dirtying pages in memory generating a lot of hard/slow to reclaim pages (or even potential OOM conditions), because the apps will be actually throttled by the IO controller when they'll start to writeback pages.
[ Honestly, I don't like this implementation in memcgroup. I'm using the memcgroup statistics to account the dirty file pages, but I'm also adding a variable to struct mem_cgroup to implement the dirty file pages limit. So it seems it would be more appropriate a struct res_counter, but I can't use res_counter_[un]charge() interface, because the dirty file pages limit is a soft-limit and can be exceeded without any problem; if it's exceeded the task is simply forced to perform a writeback of the dirty pages to return back to the allowed dirty limit. Suggestions are welcome. ]
--- fs/buffer.c | 1 + fs/reiser4/as_ops.c | 4 ++- fs/reiser4/page_cache.c | 4 ++- include/linux/memcontrol.h | 12 ++++++ mm/filemap.c | 1 + mm/memcontrol.c | 83 ++++++++++++++++++++++++++++++++++++++++++++ mm/page-writeback.c | 37 +++++++++++++++++++ mm/truncate.c | 2 + 8 files changed, 142 insertions(+), 2 deletions(-)
fs/buffer.c +1 -0
@@ -718,6 +718,7 @@ static int __set_page_dirty(struct page *page,WARN_ON_ONCE (warn && !PageUptodate (page));if (mapping_cap_account_dirty (mapping)) { +mem_cgroup_charge_file_dirty (page,1 );__inc_zone_page_state (page,NR_FILE_DIRTY );__inc_bdi_stat (mapping->backing_dev_info,BDI_RECLAIMABLE );
fs/reiser4/as_ops.c +3 -1
@@ -82,9 +82,11 @@ int reiser4_set_page_dirty(struct page *page)/* check for race with truncate */ if (page->mapping) {assert ("vs-1652" , page->mapping == mapping); -if (mapping_cap_account_dirty (mapping)) +if (mapping_cap_account_dirty (mapping)) { +mem_cgroup_charge_file_dirty (page,1 );inc_zone_page_state (page,NR_FILE_DIRTY ); + }radix_tree_tag_set (&mapping->page_tree, page->index,PAGECACHE_TAG_REISER4_MOVED );
fs/reiser4/page_cache.c +3 -1
@@ -467,8 +467,10 @@ int reiser4_set_page_dirty_internal(struct page *page)BUG_ON (mapping ==NULL );if (!TestSetPageDirty (page)) { -if (mapping_cap_account_dirty (mapping)) +if (mapping_cap_account_dirty (mapping)) { +mem_cgroup_charge_file_dirty (page,1 );inc_zone_page_state (page,NR_FILE_DIRTY ); + }__mark_inode_dirty (mapping->host,I_DIRTY_PAGES ); }
include/linux/memcontrol.h +12 -0
@@ -29,6 +29,9 @@ struct mm_struct;#define page_reset_bad_cgroup (page) ((page)->page_cgroup = 0) +extern void mem_cgroup_charge_file_dirty (struct page *page,int charge); +extern s64 mem_cgroup_check_file_dirty (void ); +extern struct page_cgroup *page_get_page_cgroup (struct page *page);extern int mem_cgroup_charge (struct page *page,struct mm_struct *mm,gfp_t gfp_mask);
@@ -132,6 +135,15 @@ static inline void mem_cgroup_end_migration(struct page *page) { } +static inline void mem_cgroup_charge_file_dirty (struct page *page,int charge) +{ +} + +static inline s64 mem_cgroup_check_file_dirty (void ) +{ +return 0 ; +} +static inline int mem_cgroup_calc_mapped_ratio (struct mem_cgroup *mem) {return 0 ;
mm/filemap.c +1 -0
@@ -131,6 +131,7 @@ void __remove_from_page_cache(struct page *page)* having removed the page entirely. */ if (PageDirty (page) &&mapping_cap_account_dirty (mapping)) { +mem_cgroup_charge_file_dirty (page,-1 );dec_zone_page_state (page,NR_FILE_DIRTY );dec_bdi_stat (mapping->backing_dev_info,BDI_RECLAIMABLE ); }
mm/memcontrol.c +83 -0
131 lines not on this page · raw message
mm/page-writeback.c +37 -0
@@ -25,6 +25,7 @@#include <linux/task_io_accounting_ops.h> #include <linux/blkdev.h> #include <linux/mpage.h> +#include <linux/memcontrol.h> #include <linux/rmap.h> #include <linux/percpu.h> #include <linux/notifier.h>
@@ -412,6 +413,34 @@ get_dirty_limits(long *pbackground, long *pdirty, long *pbdi_dirty, } } +static void mem_cgroup_balance_dirty_pages (struct address_space *mapping, +long long dirty_pages) +{ +struct writeback_control wbc = { + .bdi = mapping->backing_dev_info, + .sync_mode =WB_SYNC_NONE , + .older_than_this =NULL , + .nr_to_write =0 , + .range_cyclic =1 , + }; +unsigned long write_chunk =sync_writeback_pages (); + +while (dirty_pages >0 ) { + wbc.more_io =0 ; + wbc.encountered_congestion =0 ; + wbc.nr_to_write = write_chunk; + wbc.pages_skipped =0 ; +writeback_inodes (&wbc); + dirty_pages -= write_chunk - wbc.nr_to_write; +if (wbc.nr_to_write >0 || wbc.pages_skipped >0 ) { +if (wbc.encountered_congestion || wbc.more_io) +congestion_wait (WRITE ,HZ /10 ); +else +break ; + } + } +} +/* * balance_dirty_pages() must be called by processes which are generating dirty * data. It looks at the number of dirty pages in the machine and will force
@@ -556,6 +585,12 @@ void balance_dirty_pages_ratelimited_nr(struct address_space *mapping, static DEFINE_PER_CPU(unsigned long, ratelimits) = 0;unsigned long ratelimit;unsigned long *p; +long long memcg_dirty_pages =mem_cgroup_check_file_dirty (); + +if (memcg_dirty_pages) { +mem_cgroup_balance_dirty_pages (mapping, memcg_dirty_pages); +return ; + } ratelimit = ratelimit_pages;if (mapping->backing_dev_info->dirty_exceeded)
@@ -1090,6 +1125,7 @@ int __set_page_dirty_nobuffers(struct page *page)BUG_ON (mapping2 != mapping);WARN_ON_ONCE (!PagePrivate (page) && !PageUptodate (page));if (mapping_cap_account_dirty (mapping)) { +mem_cgroup_charge_file_dirty (page,1 );__inc_zone_page_state (page,NR_FILE_DIRTY );__inc_bdi_stat (mapping->backing_dev_info,BDI_RECLAIMABLE );
@@ -1234,6 +1270,7 @@ int clear_page_dirty_for_io(struct page *page)* for more comments. */ if (TestClearPageDirty (page)) { +mem_cgroup_charge_file_dirty (page,-1 );dec_zone_page_state (page,NR_FILE_DIRTY );dec_bdi_stat (mapping->backing_dev_info,BDI_RECLAIMABLE );
mm/truncate.c +2 -0
@@ -72,7 +72,9 @@ void cancel_dirty_page(struct page *page, unsigned int account_size) {if (TestClearPageDirty (page)) {struct address_space *mapping = page->mapping; +if (mapping &&mapping_cap_account_dirty (mapping)) { +mem_cgroup_charge_file_dirty (page,-1 );dec_zone_page_state (page,NR_FILE_DIRTY );dec_bdi_stat (mapping->backing_dev_info,BDI_RECLAIMABLE );
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
----- Original Message -----
Quoted 11 lines
This is a totally experimental patch against 2.6.27-rc5-mm1.
It allows to control how much dirty file pages a cgroup can have at any given time. This feature is supposed to be strictly connected to a generic cgroup IO controller (see below).
Interface: a new entry "filedirty" is added to the file memory.stat, reporting the number of dirty file pages (in pages), and a new file memory.file_dirty_limit_in_pages is added in the cgroup filesystem to show/set the current limit.
Before staring patch review, why not dirty_ratio per memcg ? Is there difficult implementation issue ?
Thanks,
-Kame
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
----- Original Message -----
Quoted 11 lines
This is a totally experimental patch against 2.6.27-rc5-mm1.
It allows to control how much dirty file pages a cgroup can have at any given time. This feature is supposed to be strictly connected to a generic cgroup IO controller (see below).
Interface: a new entry "filedirty" is added to the file memory.stat, reporting the number of dirty file pages (in pages), and a new file memory.file_dirty_limit_in_pages is added in the cgroup filesystem to show/set the current limit.
Before staring patch review, why not dirty_ratio per memcg ? Is there difficult implementation issue ?
Thanks,
-Kame
Quoted 7 lines · [email protected] wrote:
----- Original Message -----
Quoted 6 lines
This is a totally experimental patch against 2.6.27-rc5-mm1.
It allows to control how much dirty file pages a cgroup can have at any given time. This feature is supposed to be strictly connected to a generic cgroup IO controller (see below).
Quoted 7 lines
Quoted 5 lines
Interface: a new entry "filedirty" is added to the file memory.stat, reporting the number of dirty file pages (in pages), and a new file memory.file_dirty_limit_in_pages is added in the cgroup filesystem to show/set the current limit.
Before staring patch review, why not
dirty_ratioper memcg ? Is there difficult implementation issue ?
mmmh.. maybe it's a bit more complex (would add some overhead?) to translate the limit from dirty_ratio into pages or bytes, because we need to evaluate it in function of the per-cgroup dirtyable memory (lru pages and free pages I suppose). Maybe it's enough to implement it directly in determine_dirtyable_memory().
I can try to implement it and post a new patch.
-Andrea
Quoted 7 lines · [email protected] wrote:
----- Original Message -----
Quoted 6 lines
This is a totally experimental patch against 2.6.27-rc5-mm1.
It allows to control how much dirty file pages a cgroup can have at any given time. This feature is supposed to be strictly connected to a generic cgroup IO controller (see below).
Quoted 7 lines
Quoted 5 lines
Interface: a new entry "filedirty" is added to the file memory.stat, reporting the number of dirty file pages (in pages), and a new file memory.file_dirty_limit_in_pages is added in the cgroup filesystem to show/set the current limit.
Before staring patch review, why not
dirty_ratioper memcg ? Is there difficult implementation issue ?
mmmh.. maybe it's a bit more complex (would add some overhead?) to translate the limit from dirty_ratio into pages or bytes, because we need to evaluate it in function of the per-cgroup dirtyable memory (lru pages and free pages I suppose). Maybe it's enough to implement it directly in determine_dirtyable_memory().
I can try to implement it and post a new patch.
-Andrea
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 3 lines · Andrea Righi
It allows to control how much dirty file pages a cgroup can have at any given time. This feature is supposed to be strictly connected to a generic cgroup IO controller (see below).
So, this functions similarly to our global dirty ratio? Is it just intended to keep a cgroup from wedging itself too hard with too many dirty pages, just like the global ratio?
-- Dave
Quoted 3 lines · Andrea Righi
It allows to control how much dirty file pages a cgroup can have at any given time. This feature is supposed to be strictly connected to a generic cgroup IO controller (see below).
So, this functions similarly to our global dirty ratio? Is it just intended to keep a cgroup from wedging itself too hard with too many dirty pages, just like the global ratio?
-- Dave
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 10 lines · Dave Hansen
Quoted 3 lines · Andrea Righi
It allows to control how much dirty file pages a cgroup can have at any given time. This feature is supposed to be strictly connected to a generic cgroup IO controller (see below).
So, this functions similarly to our global dirty ratio? Is it just intended to keep a cgroup from wedging itself too hard with too many dirty pages, just like the global ratio?
-- Dave
Correct, it's the same functionality provided by vm.dirty_ratio and vm.dirty_background_ratio, except that is intended to be per-cgroup.
Without this functionality, a cgroup can even dirty all its memory, allocated by the memory controller, since statistics and writeback configurations are global.
-Andrea
Quoted 10 lines · Dave Hansen
Quoted 3 lines · Andrea Righi
It allows to control how much dirty file pages a cgroup can have at any given time. This feature is supposed to be strictly connected to a generic cgroup IO controller (see below).
So, this functions similarly to our global dirty ratio? Is it just intended to keep a cgroup from wedging itself too hard with too many dirty pages, just like the global ratio?
-- Dave
Correct, it's the same functionality provided by vm.dirty_ratio and vm.dirty_background_ratio, except that is intended to be per-cgroup.
Without this functionality, a cgroup can even dirty all its memory, allocated by the memory controller, since statistics and writeback configurations are global.
-Andrea
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>