dirty throttling bits for 3.3
Subsystems (8)
cover dirty throttling bits for 3.3
Hi,
I'd like to push these 5 dirty throttling improvements to linux-next, targeting for inclusion in Linux 3.3.
It's a comfortably small series for review ;) And there are no changes since the last v12 post. (I went through them once again but find nothing to change.)
[PATCH 1/5] writeback: balanced_rate cannot exceed write bandwidth [PATCH 2/5] writeback: charge leaked page dirties to active tasks [PATCH 3/5] writeback: fix dirtied pages accounting on sub-page writes [PATCH 4/5] writeback: fix dirtied pages accounting on redirty [PATCH 5/5] writeback: dirty ratelimit - think time compensation
include/linux/sched.h | 1 include/linux/writeback.h | 4 + include/trace/events/writeback.h | 14 ++++-- kernel/exit.c | 2 kernel/fork.c | 1 mm/page-writeback.c | 64 ++++++++++++++++++++++++++--- 6 files changed, 77 insertions(+), 9 deletions(-)
Thanks,
Fengguang
PATCH 1/5 writeback: balanced_rate cannot exceed write bandwidth
Add an upper limit to balanced_rate according to the below inequality. This filters out some rare but huge singular points, which at least enables more readable gnuplot figures.
When there are N dd dirtiers,
balanced_dirty_ratelimit = write_bw / N
So it holds that
balanced_dirty_ratelimit <= write_bw
--- mm/page-writeback.c | 5 +++++ 1 file changed, 5 insertions(+)
mm/page-writeback.c +5 -0
@@ -804,6 +804,11 @@ static void bdi_update_dirty_ratelimit(s*/ balanced_dirty_ratelimit =div_u64 ((u64 )task_ratelimit * write_bw, dirty_rate |1 ); +/* +* balanced_dirty_ratelimit ~= (write_bw / N) <= write_bw +*/ +if (unlikely (balanced_dirty_ratelimit > write_bw)) + balanced_dirty_ratelimit = write_bw;/* * We could safely do this and return immediately:
Jan Kara
maintainerRe: [PATCH 1/5] writeback: balanced_rate cannot exceed write bandwidth
Quoted 11 lines · Wu Fengguang
Add an upper limit to
balanced_rateaccording to the below inequality. This filters out some rare but huge singular points, which at least enables more readable gnuplot figures.When there are N dd dirtiers,
balanced_dirty_ratelimit=write_bw/ NSo it holds that
balanced_dirty_ratelimit<=write_bw
The change makes sense, but do we understand why there are such huge
singular points? Are they due to errors in estimation of bandwidth or due to errors in dirtying rate computations (e.g. due to truncates), or something else?
Honza
Quoted 21 lines
Signed-off-by Wu Fengguang <[email protected]>--- mm/page-writeback.c | 5 +++++ 1 file changed, 5 insertions(+)mm/page-writeback.c +5
-0@@ -804,6 +804,11 @@ static void bdi_update_dirty_ratelimit(s*/ balanced_dirty_ratelimit =div_u64 ((u64 )task_ratelimit * write_bw, dirty_rate |1 ); +/* +* balanced_dirty_ratelimit ~= (write_bw / N) <= write_bw +*/ +if (unlikely (balanced_dirty_ratelimit > write_bw)) + balanced_dirty_ratelimit = write_bw;/* * We could safely do this and return immediately:
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Re: [PATCH 1/5] writeback: balanced_rate cannot exceed write bandwidth
Quoted 9 lines · Jan Kara
Quoted 8 lines · Wu Fengguang
Add an upper limit to
balanced_rateaccording to the below inequality. This filters out some rare but huge singular points, which at least enables more readable gnuplot figures.When there are N dd dirtiers,
balanced_dirty_ratelimit=write_bw/ N
Quoted 7 lines
Quoted 3 lines
So it holds that
balanced_dirty_ratelimit<=write_bwThe change makes sense, but do we understand why there are such huge
singular points? Are they due to errors in estimation of bandwidth or due to errors in dirtying rate computations (e.g. due to truncates), or something else?
Good point. I'll add this to the changelog:
The singular points originate from dirty_rate in the below formular:
balanced_dirty_ratelimit = task_ratelimit * write_bw / dirty_rate
where
dirty_rate = (number of page dirties in the past 200ms) / 200ms
In the extreme case, if all dd tasks suddenly get blocked on something else and hence no pages are dirtied at all, dirty_rate will be 0 and balanced_dirty_ratelimit will be inf. This could happen in reality.
There won't be tiny singular points though, as long as the dirty pages lie inside the dirty control area (above the freerun region). Because there the dd tasks will be throttled by balanced_dirty_pages() and won't be able to suddenly dirty much more pages than average.
Thanks,
Fengguang
Jan Kara
maintainerRe: [PATCH 1/5] writeback: balanced_rate cannot exceed write bandwidth
Quoted 26 lines · Wu Fengguang
Quoted 9 lines · Jan Kara
Quoted 8 lines · Wu Fengguang
Add an upper limit to
balanced_rateaccording to the below inequality. This filters out some rare but huge singular points, which at least enables more readable gnuplot figures.When there are N dd dirtiers,
balanced_dirty_ratelimit=write_bw/ NQuoted 7 lines
Quoted 3 lines
So it holds that
balanced_dirty_ratelimit<=write_bwThe change makes sense, but do we understand why there are such huge
singular points? Are they due to errors in estimation of bandwidth or due to errors in dirtying rate computations (e.g. due to truncates), or something else?Good point. I'll add this to the changelog:
The singular points originate from
dirty_ratein the below formular:
balanced_dirty_ratelimit=task_ratelimit*write_bw/dirty_rate
where
dirty_rate= (number of page dirties in the past 200ms) / 200ms
Quoted 8 lines
In the extreme case, if all dd tasks suddenly get blocked on something else and hence no pages are dirtied at all,
dirty_ratewill be 0 andbalanced_dirty_ratelimitwill be inf. This could happen in reality.There won't be tiny singular points though, as long as the dirty pages lie inside the dirty control area (above the freerun region). Because there the dd tasks will be throttled by
balanced_dirty_pages()and won't be able to suddenly dirty much more pages than average.
OK, I see. Thanks for explanation.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Re: [PATCH 1/5] writeback: balanced_rate cannot exceed write bandwidth
Quoted 31 lines · Jan Kara
Quoted 30 lines · Wu Fengguang
Quoted 9 lines · Jan Kara
Quoted 8 lines · Wu Fengguang
Add an upper limit to
balanced_rateaccording to the below inequality. This filters out some rare but huge singular points, which at least enables more readable gnuplot figures.When there are N dd dirtiers,
balanced_dirty_ratelimit=write_bw/ NQuoted 7 lines
Quoted 3 lines
So it holds that
balanced_dirty_ratelimit<=write_bwThe change makes sense, but do we understand why there are such huge
singular points? Are they due to errors in estimation of bandwidth or due to errors in dirtying rate computations (e.g. due to truncates), or something else?Good point. I'll add this to the changelog:
The singular points originate from
dirty_ratein the below formular:
balanced_dirty_ratelimit=task_ratelimit*write_bw/dirty_rate
where
dirty_rate= (number of page dirties in the past 200ms) / 200msIn the extreme case, if all dd tasks suddenly get blocked on something else and hence no pages are dirtied at all,
dirty_ratewill be 0 andbalanced_dirty_ratelimitwill be inf. This could happen in reality.
Quoted 5 lines
Quoted 4 lines
There won't be tiny singular points though, as long as the dirty pages lie inside the dirty control area (above the freerun region). Because there the dd tasks will be throttled by
balanced_dirty_pages()and won't be able to suddenly dirty much more pages than average.OK, I see. Thanks for explanation.
I'd like to comment that these huge singular points is not a real threat, since they are _guaranteed_ to be filtered out by these lines in bdi_update_dirty_ratelimit():
* |task_ratelimit - dirty_ratelimit| is used to limit the step size
* and filter out the sigular points of balanced_dirty_ratelimit. Which
* keeps jumping around randomly and can even leap far away at times
* due to the small 200ms estimation period of dirty_rate (we want to
* keep that period small to reduce time lags).
*/
step = 0;
if (dirty < setpoint) {
x = min(bdi->balanced_dirty_ratelimit,
==> min(balanced_dirty_ratelimit, task_ratelimit));
if (dirty_ratelimit < x)
step = x - dirty_ratelimit;
} else {
x = max(bdi->balanced_dirty_ratelimit,
max(balanced_dirty_ratelimit, task_ratelimit));
if (dirty_ratelimit > x)
step = dirty_ratelimit - x;
}The caveat is, task_ratelimit which is based on the number of dirty pages will never _suddenly_ fly away like balanced_dirty_ratelimit. So any weirdly large balanced_dirty_ratelimit will be cut down to the level of task_ratelimit.
Thanks,
Fengguang
PATCH 2/5 writeback: charge leaked page dirties to active tasks
In reply to Wu FengguangIt's a years long problem that a large number of short-lived dirtiers (eg. gcc instances in a fast kernel build) may starve long-run dirtiers (eg. dd) as well as pushing the dirty pages to the global hard limit.
The solution is to charge the pages dirtied by the exited gcc to the other random dirtying tasks. It sounds not perfect, however should behave good enough in practice, seeing as that throttled tasks aren't actually running so those that are running are more likely to pick it up and get throttled, therefore promoting an equal spread.
--- include/linux/writeback.h | 2 ++ kernel/exit.c | 2 ++ mm/page-writeback.c | 12 ++++++++++++ 3 files changed, 16 insertions(+)
include/linux/writeback.h +2 -0
@@ -7,6 +7,8 @@#include <linux/sched.h> #include <linux/fs.h> +DECLARE_PER_CPU (int, dirty_leaks); +/* * The 1/4 region under the global dirty thresh is for smooth dirty throttling: *
mm/page-writeback.c +12 -0
@@ -1194,6 +1194,7 @@ void set_page_dirty_balance(struct page } static DEFINE_PER_CPU(int, bdp_ratelimits); +DEFINE_PER_CPU (int ,dirty_leaks ) =0 ;/** * balance_dirty_pages_ratelimited_nr - balance dirty memory state
@@ -1242,6 +1243,17 @@ void balance_dirty_pages_ratelimited_nr( ratelimit =0 ; } } +/* +* Pick up the dirtied pages by the exited tasks. This avoids lots of +* short-lived tasks (eg. gcc invocations in a kernel build) escaping +* the dirty throttling and livelock other long-run dirtiers. +*/ + p = &__get_cpu_var (dirty_leaks); +if (*p >0 && current->nr_dirtied < ratelimit) { + nr_pages_dirtied =min (*p, ratelimit - current->nr_dirtied); + *p -= nr_pages_dirtied; + current->nr_dirtied += nr_pages_dirtied; + }preempt_enable ();if (unlikely (current->nr_dirtied >= ratelimit))
kernel/exit.c +2 -0
@@ -1037,6 +1037,8 @@ NORET_TYPE void do_exit(long code)validate_creds_for_do_exit (tsk);preempt_disable (); +if (tsk->nr_dirtied) +__this_cpu_add (dirty_leaks, tsk->nr_dirtied);exit_rcu ();/* causes final put_task_struct in finish_task_switch(). */ tsk->state =TASK_DEAD ;
Andrew Morton
maintainerRe: [PATCH 2/5] writeback: charge leaked page dirties to active tasks
Quoted 13 lines · On Mon, 21 Nov 2011 21:03:44 +0800 Wu Fengguang <[email protected]> wrote:
It's a years long problem that a large number of short-lived dirtiers (eg. gcc instances in a fast kernel build) may starve long-run dirtiers (eg. dd) as well as pushing the dirty pages to the global hard limit.
The solution is to charge the pages dirtied by the exited gcc to the other random dirtying tasks. It sounds not perfect, however should behave good enough in practice, seeing as that throttled tasks aren't actually running so those that are running are more likely to pick it up and get throttled, therefore promoting an equal spread.
mm/page-writeback.c void set_page_dirty_balance(struct page · line 1197
Quoted 4 lines
mm/page-writeback.c +1
-0@@ -1194,6 +1194,7 @@ void set_page_dirty_balance(struct page } static DEFINE_PER_CPU(int, bdp_ratelimits); +DEFINE_PER_CPU (int ,dirty_leaks ) =0 ;
This is a poor identifier for a global symbol. Generally such symbols should at least identify what subsystem they belong to.
Also, this would be a good site at whcih to document the global symbol's role. The writeback code needs a lot of documentation. Of the design-level kind.
Quoted 23 lines
mm/page-writeback.c +11
-0/** * balance_dirty_pages_ratelimited_nr - balance dirty memory state @@ -1242,6 +1243,17 @@ void balance_dirty_pages_ratelimited_nr( ratelimit =0 ; } } +/* +* Pick up the dirtied pages by the exited tasks. This avoids lots of +* short-lived tasks (eg. gcc invocations in a kernel build) escaping +* the dirty throttling and livelock other long-run dirtiers. +*/ + p = &__get_cpu_var (dirty_leaks); +if (*p >0 && current->nr_dirtied < ratelimit) { + nr_pages_dirtied =min (*p, ratelimit - current->nr_dirtied); + *p -= nr_pages_dirtied; + current->nr_dirtied += nr_pages_dirtied; + }preempt_enable ();if (unlikely (current->nr_dirtied >= ratelimit))
kernel/exit.c NORET_TYPE void do_exit(long code) · line 1041
Quoted 5 lines
kernel/exit.c +2
-0@@ -1037,6 +1037,8 @@ NORET_TYPE void do_exit(long code)validate_creds_for_do_exit (tsk);preempt_disable (); +if (tsk->nr_dirtied) +__this_cpu_add (dirty_leaks, tsk->nr_dirtied);
Whatever problem this code is solving, it only solved it in certain cases. For example, if tasks are forking, dirtying and exiting at a rapid rate on CPU 0 then all the other CPUs don't know anything about this and we didn't fix anything.
IOW, it all seems like a half-assed bandaid.
Jan Kara
maintainerRe: [PATCH 2/5] writeback: charge leaked page dirties to active tasks
Quoted 53 lines · Andrew Morton
Quoted 13 lines · On Mon, 21 Nov 2011 21:03:44 +0800 Wu Fengguang <[email protected]> wrote:
It's a years long problem that a large number of short-lived dirtiers (eg. gcc instances in a fast kernel build) may starve long-run dirtiers (eg. dd) as well as pushing the dirty pages to the global hard limit.
The solution is to charge the pages dirtied by the exited gcc to the other random dirtying tasks. It sounds not perfect, however should behave good enough in practice, seeing as that throttled tasks aren't actually running so those that are running are more likely to pick it up and get throttled, therefore promoting an equal spread.
mm/page-writeback.c void set_page_dirty_balance(struct page · line 1197
Quoted 4 lines
mm/page-writeback.c +1
-0@@ -1194,6 +1194,7 @@ void set_page_dirty_balance(struct page } static DEFINE_PER_CPU(int, bdp_ratelimits); +DEFINE_PER_CPU (int ,dirty_leaks ) =0 ;This is a poor identifier for a global symbol. Generally such symbols should at least identify what subsystem they belong to.
Also, this would be a good site at whcih to document the global symbol's role. The writeback code needs a lot of documentation. Of the design-level kind.
Quoted 25 lines
mm/page-writeback.c +11
-0/** * balance_dirty_pages_ratelimited_nr - balance dirty memory state @@ -1242,6 +1243,17 @@ void balance_dirty_pages_ratelimited_nr( ratelimit =0 ; } } +/* +* Pick up the dirtied pages by the exited tasks. This avoids lots of +* short-lived tasks (eg. gcc invocations in a kernel build) escaping +* the dirty throttling and livelock other long-run dirtiers. +*/ + p = &__get_cpu_var (dirty_leaks); +if (*p >0 && current->nr_dirtied < ratelimit) { + nr_pages_dirtied =min (*p, ratelimit - current->nr_dirtied); + *p -= nr_pages_dirtied; + current->nr_dirtied += nr_pages_dirtied; + }preempt_enable ();if (unlikely (current->nr_dirtied >= ratelimit))kernel/exit.c +0
-0@@ -1037,6 +1037,8 @@ NORET_TYPE void do_exit(long code)validate_creds_for_do_exit (tsk);
Quoted 8 lines
kernel/exit.c NORET_TYPE void do_exit(long code) · line 1041
Quoted 3 lines
kernel/exit.c +2
-0preempt_disable (); +if (tsk->nr_dirtied) +__this_cpu_add (dirty_leaks, tsk->nr_dirtied);Whatever problem this code is solving, it only solved it in certain cases. For example, if tasks are forking, dirtying and exiting at a rapid rate on CPU 0 then all the other CPUs don't know anything about this and we didn't fix anything.
I think it will work - dirty_leaks at CPU0 will get leaked pages from the
first task when it dies, the second task will pick them up and as soon as it dies it puts all the pages back to dirty_leaks at CPU0. This goes on and on and eventually, dirty_leaks will accumulate enough pages so that a task dirtying even a single page will enter balance_dirty_pages() as if it dirtied 'ratelimit' pages.
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Re: [PATCH 2/5] writeback: charge leaked page dirties to active tasks
In reply to Andrew MortonQuoted 18 lines · Andrew Morton
Quoted 15 lines · On Mon, 21 Nov 2011 21:03:44 +0800 Wu Fengguang <[email protected]> wrote:
It's a years long problem that a large number of short-lived dirtiers (eg. gcc instances in a fast kernel build) may starve long-run dirtiers (eg. dd) as well as pushing the dirty pages to the global hard limit.
The solution is to charge the pages dirtied by the exited gcc to the other random dirtying tasks. It sounds not perfect, however should behave good enough in practice, seeing as that throttled tasks aren't actually running so those that are running are more likely to pick it up and get throttled, therefore promoting an equal spread.
mm/page-writeback.c +0
-0@@ -1194,6 +1194,7 @@ void set_page_dirty_balance(struct page }
Quoted 5 lines
mm/page-writeback.c void set_page_dirty_balance(struct page · line 1197
Quoted 2 lines
mm/page-writeback.c +1
-0static DEFINE_PER_CPU(int, bdp_ratelimits); +DEFINE_PER_CPU (int ,dirty_leaks ) =0 ;This is a poor identifier for a global symbol. Generally such symbols should at least identify what subsystem they belong to.
Yes it is, "dirty_throttle_leaks" should look better.
Quoted 3 lines
Also, this would be a good site at whcih to document the global symbol's role. The writeback code needs a lot of documentation. Of the design-level kind.
Agreed, I just added this comment:
/*
* Normal tasks are throttled by
* loop {
* dirty tsk->nr_dirtied_pause pages;
* take a snap in balance_dirty_pages();
* }
* However there is a worst case. If every task exit immediately when dirtied
* (tsk->nr_dirtied_pause - 1) pages, balance_dirty_pages() will never be
* called to throttle the page dirties. The solution is to save the not yet
* throttled page dirties in dirty_throttle_leaks on task exit and charge them
* randomly into the running tasks. This works well for the above worst case,
* as the new task will pick up and accumulate the old task's leaked dirty
* count and eventually get throttled.
*/
Thanks,
Fengguang
PATCH 3/5 writeback: fix dirtied pages accounting on sub-page writes
In reply to Wu FengguangWhen dd in 512bytes, generic_perform_write() calls balance_dirty_pages_ratelimited() 8 times for the same page, but obviously the page is only dirtied once.
Fix it by accounting nr_dirtied at page dirty time.
--- mm/page-writeback.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
mm/page-writeback.c +1 -2
@@ -1224,8 +1224,6 @@ void balance_dirty_pages_ratelimited_nr(if (bdi->dirty_exceeded) ratelimit =min (ratelimit,32 >> (PAGE_SHIFT -10 )); - current->nr_dirtied += nr_pages_dirtied; -preempt_disable ();/* * This prevents one CPU to accumulate too many dirtied pages without
@@ -1734,6 +1732,7 @@ void account_page_dirtied(struct page *p__inc_bdi_stat (mapping->backing_dev_info,BDI_RECLAIMABLE );__inc_bdi_stat (mapping->backing_dev_info,BDI_DIRTIED );task_io_account_write (PAGE_CACHE_SIZE ); + current->nr_dirtied++; } }EXPORT_SYMBOL (account_page_dirtied);
Jan Kara
maintainerRe: [PATCH 3/5] writeback: fix dirtied pages accounting on sub-page writes
Quoted 5 lines · Wu Fengguang
When dd in 512bytes,
generic_perform_write()callsbalance_dirty_pages_ratelimited()8 times for the same page, but obviously the page is only dirtied once.Fix it by accounting
nr_dirtiedat page dirty time.
Well, but after this change, the interface balance_dirty_ratelimited_nr()
is strange because the argument is only used for per-CPU ratelimiting and not for per-task ratelimiting... So if you do this switch then I'd also switch bdp_ratelimits to get consistent results and a clean interface and completely kill balance_dirty_pages_ratelimited_nr().
Honza
Quoted 27 lines
Signed-off-by Wu Fengguang <[email protected]>--- mm/page-writeback.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)mm/page-writeback.c +1
-2@@ -1224,8 +1224,6 @@ void balance_dirty_pages_ratelimited_nr(if (bdi->dirty_exceeded) ratelimit =min (ratelimit,32 >> (PAGE_SHIFT -10 )); - current->nr_dirtied += nr_pages_dirtied; -preempt_disable ();/* * This prevents one CPU to accumulate too many dirtied pages without @@ -1734,6 +1732,7 @@ void account_page_dirtied(struct page *p__inc_bdi_stat (mapping->backing_dev_info,BDI_RECLAIMABLE );__inc_bdi_stat (mapping->backing_dev_info,BDI_DIRTIED );task_io_account_write (PAGE_CACHE_SIZE ); + current->nr_dirtied++; } }EXPORT_SYMBOL (account_page_dirtied);
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Re: [PATCH 3/5] writeback: fix dirtied pages accounting on sub-page writes
Quoted 9 lines · Jan Kara
Quoted 5 lines · Wu Fengguang
When dd in 512bytes,
generic_perform_write()callsbalance_dirty_pages_ratelimited()8 times for the same page, but obviously the page is only dirtied once.Fix it by accounting
nr_dirtiedat page dirty time.Well, but after this change, the interface
balance_dirty_ratelimited_nr()
is strange because the argument is only used for per-CPU ratelimiting and not for per-task ratelimiting...
Yeah I was vaguely aware of this... and still choose to ignore this since the patchset looked already forbiddingly large at the time ;)
Quoted 3 lines
So if you do this switch then I'd also
switchbdp_ratelimitsto get consistent results and a clean interface and completely killbalance_dirty_pages_ratelimited_nr().
Following your suggestions to change ratelimiting as well :)
I'll do the interface change with a standalone patch.
Thanks,
Fengguang
---
Subject: writeback: fix dirtied pages accounting on sub-page writes
Date: Thu Apr 14 07:52:37 CST 2011
When dd in 512bytes, generic_perform_write() calls balance_dirty_pages_ratelimited() 8 times for the same page, but obviously the page is only dirtied once.
Fix it by accounting tsk->nr_dirtied and bdp_ratelimits at page dirty time.
--- mm/page-writeback.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-)
mm/page-writeback.c +5 -8
@@ -1231,8 +1231,6 @@ void balance_dirty_pages_ratelimited_nr(if (bdi->dirty_exceeded) ratelimit =min (ratelimit,32 >> (PAGE_SHIFT -10 )); - current->nr_dirtied += nr_pages_dirtied; -preempt_disable ();/* * This prevents one CPU to accumulate too many dirtied pages without
@@ -1243,12 +1241,9 @@ void balance_dirty_pages_ratelimited_nr( p = &__get_cpu_var (bdp_ratelimits);if (unlikely (current->nr_dirtied >= ratelimit)) *p =0 ; -else { - *p += nr_pages_dirtied; -if (unlikely (*p >= ratelimit_pages)) { - *p =0 ; - ratelimit =0 ; - } +else if (unlikely (*p >= ratelimit_pages)) { + *p =0 ; + ratelimit =0 ; }/* * Pick up the dirtied pages by the exited tasks. This avoids lots of
@@ -1743,6 +1738,8 @@ void account_page_dirtied(struct page *p__inc_bdi_stat (mapping->backing_dev_info,BDI_DIRTIED );task_dirty_inc (current);task_io_account_write (PAGE_CACHE_SIZE ); + current->nr_dirtied++; +__get_cpu_var (bdp_ratelimits)++; } }EXPORT_SYMBOL (account_page_dirtied);
Jan Kara
maintainerRe: [PATCH 3/5] writeback: fix dirtied pages accounting on sub-page writes
Quoted 14 lines · Wu Fengguang
Quoted 9 lines · Jan Kara
Quoted 5 lines · Wu Fengguang
When dd in 512bytes,
generic_perform_write()callsbalance_dirty_pages_ratelimited()8 times for the same page, but obviously the page is only dirtied once.Fix it by accounting
nr_dirtiedat page dirty time.Well, but after this change, the interface
balance_dirty_ratelimited_nr()
is strange because the argument is only used for per-CPU ratelimiting and not for per-task ratelimiting...Yeah I was vaguely aware of this... and still choose to ignore this since the patchset looked already forbiddingly large at the time ;)
Quoted 7 lines
Quoted 3 lines
So if you do this switch then I'd also
switchbdp_ratelimitsto get consistent results and a clean interface and completely killbalance_dirty_pages_ratelimited_nr().Following your suggestions to change ratelimiting as well :)
I'll do the interface change with a standalone patch.
OK.
Quoted 43 lines
---
Subject: writeback: fix dirtied pages accounting on sub-page writes
Date: Thu Apr 14 07:52:37 CST 2011When dd in 512bytes,
generic_perform_write()callsbalance_dirty_pages_ratelimited()8 times for the same page, but obviously the page is only dirtied once.Fix it by accounting
tsk->nr_dirtiedandbdp_ratelimitsat page dirty time.Signed-off-by Wu Fengguang <[email protected]>--- mm/page-writeback.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-)mm/page-writeback.c +3
-8@@ -1231,8 +1231,6 @@ void balance_dirty_pages_ratelimited_nr(if (bdi->dirty_exceeded) ratelimit =min (ratelimit,32 >> (PAGE_SHIFT -10 )); - current->nr_dirtied += nr_pages_dirtied; -preempt_disable ();/* * This prevents one CPU to accumulate too many dirtied pages without @@ -1243,12 +1241,9 @@ void balance_dirty_pages_ratelimited_nr( p = &__get_cpu_var (bdp_ratelimits);if (unlikely (current->nr_dirtied >= ratelimit)) *p =0 ; -else { - *p += nr_pages_dirtied; -if (unlikely (*p >= ratelimit_pages)) { - *p =0 ; - ratelimit =0 ; - } +else if (unlikely (*p >= ratelimit_pages)) { + *p =0 ; + ratelimit =0 ; }/* * Pick up the dirtied pages by the exited tasks. This avoids lots of
mm/page-writeback.c void account_page_dirtied(struct page *p · line 1742
Quoted 5 lines
mm/page-writeback.c +2
-0@@ -1743,6 +1738,8 @@ void account_page_dirtied(struct page *p__inc_bdi_stat (mapping->backing_dev_info,BDI_DIRTIED );task_dirty_inc (current);task_io_account_write (PAGE_CACHE_SIZE ); + current->nr_dirtied++; +__get_cpu_var (bdp_ratelimits)++;
I think you need preempt_disable() and preempt_enable() pair around__get_cpu_var(). Otherwise a process could get rescheduled in the middle of read-modify-write cycle...
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Re: [PATCH 3/5] writeback: fix dirtied pages accounting on sub-page writes
Quoted 9 lines
mm/page-writeback.c void account_page_dirtied(struct page *p · line 1742
Quoted 6 lines
mm/page-writeback.c +2
-0@@ -1743,6 +1738,8 @@ void account_page_dirtied(struct page *p__inc_bdi_stat (mapping->backing_dev_info,BDI_DIRTIED );task_dirty_inc (current);task_io_account_write (PAGE_CACHE_SIZE ); + current->nr_dirtied++; +__get_cpu_var (bdp_ratelimits)++;I think you need
preempt_disable()andpreempt_enable()pair around__get_cpu_var(). Otherwise a process could get rescheduled in the middle of read-modify-write cycle...
Hmm, I'm not worried about it at all, because bdp_ratelimits don't need to be accurate. In normal cases it won't even trigger one single call to balance_dirty_pages().
btw, account_page_dirtied() is called inside spinlock, will it be sufficient?
Thanks,
Fengguang
Jan Kara
maintainerRe: [PATCH 3/5] writeback: fix dirtied pages accounting on sub-page writes
Quoted 10 lines · Wu Fengguang
Quoted 9 lines
mm/page-writeback.c void account_page_dirtied(struct page *p · line 1742
Quoted 6 lines
mm/page-writeback.c +2
-0@@ -1743,6 +1738,8 @@ void account_page_dirtied(struct page *p__inc_bdi_stat (mapping->backing_dev_info,BDI_DIRTIED );task_dirty_inc (current);task_io_account_write (PAGE_CACHE_SIZE ); + current->nr_dirtied++; +__get_cpu_var (bdp_ratelimits)++;I think you need
preempt_disable()andpreempt_enable()pair around__get_cpu_var(). Otherwise a process could get rescheduled in the middle of read-modify-write cycle...
Quoted 3 lines
Hmm, I'm not worried about it at all, because
bdp_ratelimitsdon't need to be accurate. In normal cases it won't even trigger one single call tobalance_dirty_pages().
I agree regarding the accuracy. But the CPU can change when the process
is scheduled again. So you could modify counter of a CPU you are not running on. And that can cause bad things...
Quoted 2 lines
btw,
account_page_dirtied()is called inside spinlock, will it be sufficient?
Currently it is not enough in real-time kernels and when sleeping
spinlocks work gets merged it won't be enough even in standard kernels... And in kernels where spinlock means preemption is disabled preempt_enable/disable will be almost for free...
Honza
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Re: [PATCH 3/5] writeback: fix dirtied pages accounting on sub-page writes
Quoted 11 lines · Jan Kara
Quoted 10 lines · Wu Fengguang
Quoted 9 lines
mm/page-writeback.c void account_page_dirtied(struct page *p · line 1742
Quoted 6 lines
mm/page-writeback.c +2
-0@@ -1743,6 +1738,8 @@ void account_page_dirtied(struct page *p__inc_bdi_stat (mapping->backing_dev_info,BDI_DIRTIED );task_dirty_inc (current);task_io_account_write (PAGE_CACHE_SIZE ); + current->nr_dirtied++; +__get_cpu_var (bdp_ratelimits)++;I think you need
preempt_disable()andpreempt_enable()pair around__get_cpu_var(). Otherwise a process could get rescheduled in the middle of read-modify-write cycle...
Quoted 6 lines
Quoted 3 lines
Hmm, I'm not worried about it at all, because
bdp_ratelimitsdon't need to be accurate. In normal cases it won't even trigger one single call tobalance_dirty_pages().I agree regarding the accuracy. But the CPU can change when the process
is scheduled again. So you could modify counter of a CPU you are not running on. And that can cause bad things...
Will modifying another CPU's per-cpu data lead to more serious problems than inaccuracy? If not, it would be fine. bdp_ratelimits is only meant to be a coarse grained safeguard after all :-)
Quoted 6 lines
Quoted 2 lines
btw,
account_page_dirtied()is called inside spinlock, will it be sufficient?Currently it is not enough in real-time kernels and when sleeping
spinlocks work gets merged it won't be enough even in standard kernels... And in kernels where spinlock means preemption is disabled preempt_enable/disable will be almost for free...
I see, spinlock won't be a general superset of preempt_enable/disable indeed.
Thanks,
Fengguang
Re: [PATCH 3/5] writeback: fix dirtied pages accounting on sub-page writes
In reply to Jan KaraQuoted 4 lines · Jan Kara
Quoted 1 line
+ __get_cpu_var(bdp_ratelimits)++;
I think you need
preempt_disable()andpreempt_enable()pair around__get_cpu_var(). Otherwise a process could get rescheduled in the middle of read-modify-write cycle...
there's of course the this_cpu_inc(bdp_ratelimits); thing.
On x86 that'll turn into a single insn, on others it will add the required preempt_disable/enable bits.
Re: [PATCH 3/5] writeback: fix dirtied pages accounting on sub-page writes
Quoted 10 lines · Peter Zijlstra
Quoted 4 lines · Jan Kara
Quoted 1 line
+ __get_cpu_var(bdp_ratelimits)++;
I think you need
preempt_disable()andpreempt_enable()pair around__get_cpu_var(). Otherwise a process could get rescheduled in the middle of read-modify-write cycle...there's of course the
this_cpu_inc(bdp_ratelimits); thing.On x86 that'll turn into a single insn, on others it will add the required preempt_disable/enable bits.
It's good to know that. But what if we don't really care which CPU data it's increasing, and can accept losing some increases due to the resulted race condition?
Thanks,
Fengguang
Re: [PATCH 3/5] writeback: fix dirtied pages accounting on sub-page writes
Quoted 7 lines · Wu Fengguang
Quoted 6 lines · Peter Zijlstra
Quoted 4 lines · Jan Kara
Quoted 1 line
+ __get_cpu_var(bdp_ratelimits)++;
I think you need
preempt_disable()andpreempt_enable()pair around__get_cpu_var(). Otherwise a process could get rescheduled in the middle of read-modify-write cycle...
Quoted 8 lines
Quoted 4 lines
there's of course the
this_cpu_inc(bdp_ratelimits); thing.On x86 that'll turn into a single insn, on others it will add the required preempt_disable/enable bits.
It's good to know that. But what if we don't really care which CPU data it's increasing, and can accept losing some increases due to the resulted race condition?
I just added a comment for it, hope it helps :)
/*
* This is racy, however bdp_ratelimits merely serves as a
* gross safeguard. We don't really care the exact CPU it's
* charging to and the resulted inaccuracy is acceptable. */
__get_cpu_var(bdp_ratelimits)++;
Thanks,
Fengguang
Re: [PATCH 3/5] writeback: fix dirtied pages accounting on sub-page writes
Quoted 17 lines · Wu Fengguang
Quoted 7 lines · Wu Fengguang
Quoted 6 lines · Peter Zijlstra
Quoted 4 lines · Jan Kara
Quoted 1 line
+ __get_cpu_var(bdp_ratelimits)++;
I think you need
preempt_disable()andpreempt_enable()pair around__get_cpu_var(). Otherwise a process could get rescheduled in the middle of read-modify-write cycle...Quoted 8 lines
Quoted 4 lines
there's of course the
this_cpu_inc(bdp_ratelimits); thing.On x86 that'll turn into a single insn, on others it will add the required preempt_disable/enable bits.
It's good to know that. But what if we don't really care which CPU data it's increasing, and can accept losing some increases due to the resulted race condition?
Quoted 8 lines
I just added a comment for it, hope it helps :)
/*
* This is racy, howeverbdp_ratelimitsmerely serves as a
* gross safeguard. We don't really care the exact CPU it's
* charging to and the resulted inaccuracy is acceptable. */
__get_cpu_var(bdp_ratelimits)++;
Thing is, I'm not sure how much update you can effectively wreck by interleaving the RmW cycles of two CPUs like this.
Simply loosing a few increments would be fine, but what are the practical implications of actually relying on this behaviour and how do various architectures cope.
Re: [PATCH 3/5] writeback: fix dirtied pages accounting on sub-page writes
Quoted 27 lines · Peter Zijlstra
Quoted 17 lines · Wu Fengguang
Quoted 7 lines · Wu Fengguang
Quoted 6 lines · Peter Zijlstra
Quoted 4 lines · Jan Kara
Quoted 1 line
+ __get_cpu_var(bdp_ratelimits)++;
I think you need
preempt_disable()andpreempt_enable()pair around__get_cpu_var(). Otherwise a process could get rescheduled in the middle of read-modify-write cycle...Quoted 8 lines
Quoted 4 lines
there's of course the
this_cpu_inc(bdp_ratelimits); thing.On x86 that'll turn into a single insn, on others it will add the required preempt_disable/enable bits.
It's good to know that. But what if we don't really care which CPU data it's increasing, and can accept losing some increases due to the resulted race condition?
Quoted 8 lines
I just added a comment for it, hope it helps :)
/*
* This is racy, howeverbdp_ratelimitsmerely serves as a
* gross safeguard. We don't really care the exact CPU it's
* charging to and the resulted inaccuracy is acceptable. */
__get_cpu_var(bdp_ratelimits)++;
Quoted 2 lines
Thing is, I'm not sure how much update you can effectively wreck by interleaving the RmW cycles of two CPUs like this.
Yeah there is the side effect of cache bouncing, which makes it not a clear win...and pure lose on x86...
Quoted 3 lines
Simply loosing a few increments would be fine, but what are the practical implications of actually relying on this behaviour and how do various architectures cope.
OK I'll give up the weird (mis-)use of the per-cpu data structure :)
Thanks,
Fengguang
---
Subject: writeback: fix dirtied pages accounting on sub-page writes
Date: Thu Apr 14 07:52:37 CST 2011
When dd in 512bytes, generic_perform_write() calls balance_dirty_pages_ratelimited() 8 times for the same page, but obviously the page is only dirtied once.
Fix it by accounting tsk->nr_dirtied and bdp_ratelimits at page dirty time.
--- mm/page-writeback.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-)
mm/page-writeback.c +5 -8
@@ -1246,8 +1246,6 @@ void balance_dirty_pages_ratelimited_nr(if (bdi->dirty_exceeded) ratelimit =min (ratelimit,32 >> (PAGE_SHIFT -10 )); - current->nr_dirtied += nr_pages_dirtied; -preempt_disable ();/* * This prevents one CPU to accumulate too many dirtied pages without
@@ -1258,12 +1256,9 @@ void balance_dirty_pages_ratelimited_nr( p = &__get_cpu_var (bdp_ratelimits);if (unlikely (current->nr_dirtied >= ratelimit)) *p =0 ; -else { - *p += nr_pages_dirtied; -if (unlikely (*p >= ratelimit_pages)) { - *p =0 ; - ratelimit =0 ; - } +else if (unlikely (*p >= ratelimit_pages)) { + *p =0 ; + ratelimit =0 ; }/* * Pick up the dirtied pages by the exited tasks. This avoids lots of
@@ -1758,6 +1753,8 @@ void account_page_dirtied(struct page *p__inc_bdi_stat (mapping->backing_dev_info,BDI_DIRTIED );task_dirty_inc (current);task_io_account_write (PAGE_CACHE_SIZE ); + current->nr_dirtied++; +this_cpu_inc (bdp_ratelimits); } }EXPORT_SYMBOL (account_page_dirtied);
Re: [PATCH 3/5] writeback: fix dirtied pages accounting on sub-page writes
In reply to Jan KaraJan,
Quoted 15 lines · Jan Kara
Quoted 14 lines · Wu Fengguang
Quoted 9 lines · Jan Kara
Quoted 5 lines · Wu Fengguang
When dd in 512bytes,
generic_perform_write()callsbalance_dirty_pages_ratelimited()8 times for the same page, but obviously the page is only dirtied once.Fix it by accounting
nr_dirtiedat page dirty time.Well, but after this change, the interface
balance_dirty_ratelimited_nr()
is strange because the argument is only used for per-CPU ratelimiting and not for per-task ratelimiting...Yeah I was vaguely aware of this... and still choose to ignore this since the patchset looked already forbiddingly large at the time ;)
Quoted 8 lines
Quoted 7 lines
Quoted 3 lines
So if you do this switch then I'd also
switchbdp_ratelimitsto get consistent results and a clean interface and completely killbalance_dirty_pages_ratelimited_nr().Following your suggestions to change ratelimiting as well :)
I'll do the interface change with a standalone patch.
OK.
It looks worthwhile to consider a better scheme to reduce calls into balance_dirty_pages_ratelimited_nr() before doing the interface change. Currently that function is called on every page in generic_perform_write().
I have no clear idea for now, so would like to delay the interface change (after this patch).
Thanks,
Fengguang
PATCH 4/5 writeback: fix dirtied pages accounting on redirty
In reply to Wu FengguangDe-account the accumulative dirty counters on page redirty.
Page redirties (very common in ext4) will introduce mismatch between counters (a) and (b)
a) NR_DIRTIED, BDI_DIRTIED, tsk->nr_dirtied
b) NR_WRITTEN, BDI_WRITTEN
This will introduce systematic errors in balanced_rate and result in dirty page position errors (ie. the dirty pages are no longer balanced around the global/bdi setpoints).
--- include/linux/writeback.h | 2 ++ mm/page-writeback.c | 12 ++++++++++++ 2 files changed, 14 insertions(+)
mm/page-writeback.c +12 -0
@@ -1792,6 +1792,17 @@ int __set_page_dirty_nobuffers(struct pa }EXPORT_SYMBOL (__set_page_dirty_nobuffers); +void account_page_redirty (struct page *page) +{ +struct address_space *mapping = page->mapping; +if (mapping &&mapping_cap_account_dirty (mapping)) { + current->nr_dirtied--; +dec_zone_page_state (page,NR_DIRTIED ); +dec_bdi_stat (mapping->backing_dev_info,BDI_DIRTIED ); + } +} +EXPORT_SYMBOL (account_page_redirty); +/* * When a writepage implementation decides that it doesn't want to write this * page for some reason, it should redirty the locked page via
@@ -1800,6 +1811,7 @@ EXPORT_SYMBOL(__set_page_dirty_nobuffersint redirty_page_for_writepage (struct writeback_control *wbc,struct page *page) { wbc->pages_skipped++; +account_page_redirty (page);return __set_page_dirty_nobuffers (page); }EXPORT_SYMBOL (redirty_page_for_writepage);
include/linux/writeback.h +2 -0
@@ -197,6 +197,8 @@ void writeback_set_ratelimit(void);void tag_pages_for_writeback (struct address_space *mapping,pgoff_t start,pgoff_t end); +void account_page_redirty (struct page *page); +/* pdflush.c */ extern int nr_pdflush_threads;/* Global so it can be exported to sysctl read-only. */
Andrew Morton
maintainerRe: [PATCH 4/5] writeback: fix dirtied pages accounting on redirty
Quoted 19 lines · On Mon, 21 Nov 2011 21:03:46 +0800 Wu Fengguang <[email protected]> wrote:
De-account the accumulative dirty counters on page redirty.
Page redirties (very common in ext4) will introduce mismatch between counters (a) and (b)
a)
NR_DIRTIED,BDI_DIRTIED,tsk->nr_dirtied
b)NR_WRITTEN,BDI_WRITTENThis will introduce systematic errors in
balanced_rateand result in dirty page position errors (ie. the dirty pages are no longer balanced around the global/bdi setpoints)....
mm/page-writeback.c +0
-0@@ -1792,6 +1792,17 @@ int __set_page_dirty_nobuffers(struct pa }EXPORT_SYMBOL (__set_page_dirty_nobuffers);
mm/page-writeback.c int __set_page_dirty_nobuffers(struct pa · line 1803
Quoted 10 lines
mm/page-writeback.c +9
-0+void account_page_redirty (struct page *page) +{ +struct address_space *mapping = page->mapping; +if (mapping &&mapping_cap_account_dirty (mapping)) { + current->nr_dirtied--; +dec_zone_page_state (page,NR_DIRTIED ); +dec_bdi_stat (mapping->backing_dev_info,BDI_DIRTIED ); + } +}
Again, writeback doesn't seem to be the best place to be adding undocumented code.
mm/page-writeback.c int __set_page_dirty_nobuffers(struct pa · line 1804
Quoted 1 line
mm/page-writeback.c +1
-0+EXPORT_SYMBOL (account_page_redirty);
The export is unneeded?
Re: [PATCH 4/5] writeback: fix dirtied pages accounting on redirty
Quoted 23 lines · Andrew Morton
Quoted 20 lines · On Mon, 21 Nov 2011 21:03:46 +0800 Wu Fengguang <[email protected]> wrote:
De-account the accumulative dirty counters on page redirty.
Page redirties (very common in ext4) will introduce mismatch between counters (a) and (b)
a)
NR_DIRTIED,BDI_DIRTIED,tsk->nr_dirtied
b)NR_WRITTEN,BDI_WRITTENThis will introduce systematic errors in
balanced_rateand result in dirty page position errors (ie. the dirty pages are no longer balanced around the global/bdi setpoints)....
mm/page-writeback.c +0
-0@@ -1792,6 +1792,17 @@ int __set_page_dirty_nobuffers(struct pa }EXPORT_SYMBOL (__set_page_dirty_nobuffers);
Quoted 12 lines
mm/page-writeback.c int __set_page_dirty_nobuffers(struct pa · line 1803
Quoted 9 lines
mm/page-writeback.c +9
-0+void account_page_redirty (struct page *page) +{ +struct address_space *mapping = page->mapping; +if (mapping &&mapping_cap_account_dirty (mapping)) { + current->nr_dirtied--; +dec_zone_page_state (page,NR_DIRTIED ); +dec_bdi_stat (mapping->backing_dev_info,BDI_DIRTIED ); + } +}Again, writeback doesn't seem to be the best place to be adding undocumented code.
Sorry, hope this comment can explain when to call the function and why.
/*
* Call this whenever redirtying a page, to de-account the dirty counters
* (NR_DIRTIED, BDI_DIRTIED, tsk->nr_dirtied), so that they match the written
* counters (NR_WRITTEN, BDI_WRITTEN) in long term. The mismatches will lead to
* systematic errors in balanced_dirty_ratelimit and the dirty pages position
* control.
*/
Quoted 3 lines
mm/page-writeback.c int __set_page_dirty_nobuffers(struct pa · line 1804
Quoted 1 line
mm/page-writeback.c +1
-0+EXPORT_SYMBOL (account_page_redirty);The export is unneeded?
btrfs will need to call into it. Here is the patch. I'll include it in the v2 series.
Thanks,
Fengguang
---
Subject: btrfs: fix dirtied pages accounting on sub-page writes
Date: Mon Aug 08 15:19:47 CST 2011
When doing 1KB sequential writes to the same page, balance_dirty_pages_ratelimited_nr() should be called once instead of 4 times, the latter makes the dirtier tasks be throttled much too heavy.
Fix it with proper de-accounting on clear_page_dirty_for_io().
--- fs/btrfs/file.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
fs/btrfs/file.c +2 -1
@@ -1136,7 +1136,8 @@ again: GFP_NOFS); }for (i =0 ; i < num_pages; i++) { -clear_page_dirty_for_io (pages[i]); +if (clear_page_dirty_for_io (pages[i])) +account_page_redirty (pages[i]);set_page_extent_mapped (pages[i]);WARN_ON (!PageLocked (pages[i])); }
PATCH 5/5 writeback: dirty ratelimit - think time compensation
In reply to Wu FengguangCompensate the task's think time when computing the final pause time, so that ->dirty_ratelimit can be executed accurately.
think time := time spend outside of balance_dirty_pages()
In the rare case that the task slept longer than the 200ms period time (result in negative pause time), the sleep time will be compensated in the following periods, too, if it's less than 1 second.
Accumulated errors are carefully avoided as long as the max pause area is not hitted.
Pseudo code:
period = pages_dirtied / task_ratelimit;
think = jiffies - dirty_paused_when;
pause = period - think;1) normal case: period > think
pause = period - think
dirty_paused_when = jiffies + pause
nr_dirtied = 0
period time
|===============================>|
think time pause time
|===============>|==============>|
------|----------------|---------------|------------------------
dirty_paused_when jiffies
2) no pause case: period <= think
don't pause; reduce future pause time by:
dirty_paused_when += period
nr_dirtied = 0
period time
|===============================>|
think time
|===================================================>|
------|--------------------------------+-------------------|----
dirty_paused_when jiffies--- include/linux/sched.h | 1 include/trace/events/writeback.h | 14 ++++++++--- kernel/fork.c | 1 mm/page-writeback.c | 36 +++++++++++++++++++++++++---- 4 files changed, 45 insertions(+), 7 deletions(-)
include/linux/sched.h +1 -0
@@ -1527,6 +1527,7 @@ struct task_struct {*/ int nr_dirtied;int nr_dirtied_pause; +unsigned long dirty_paused_when;/* start of a write-and-pause period */ #ifdef CONFIG_LATENCYTOP int latency_record_count;
mm/page-writeback.c +32 -4
@@ -1010,6 +1010,7 @@ static void balance_dirty_pages(struct aunsigned long background_thresh;unsigned long dirty_thresh;unsigned long bdi_thresh; +long period;long pause =0 ;long uninitialized_var (max_pause );bool dirty_exceeded = false;
@@ -1020,6 +1021,8 @@ static void balance_dirty_pages(struct aunsigned long start_time = jiffies;for (;;) { +unsigned long now = jiffies; +/* * Unstable writes are a feature of certain networked * filesystems (i.e. NFS) in which data may have been
@@ -1039,8 +1042,11 @@ static void balance_dirty_pages(struct a*/ freerun =dirty_freerun_ceiling (dirty_thresh, background_thresh); -if (nr_dirty <= freerun) +if (nr_dirty <= freerun) { + current->dirty_paused_when = now; + current->nr_dirtied =0 ;break ; + }if (unlikely (!writeback_in_progress (bdi)))bdi_start_background_writeback (bdi);
@@ -1098,10 +1104,21 @@ static void balance_dirty_pages(struct a task_ratelimit = ((u64 )dirty_ratelimit * pos_ratio) >>RATELIMIT_CALC_SHIFT ;if (unlikely (task_ratelimit ==0 )) { + period = max_pause; pause = max_pause; goto pause; } - pause =HZ * pages_dirtied / task_ratelimit; + period =HZ * pages_dirtied / task_ratelimit; + pause = period; +if (current->dirty_paused_when) + pause -= now - current->dirty_paused_when; +/* +* For less than 1s think time (ext3/4 may block the dirtier +* for up to 800ms from time to time on 1-HDD; so does xfs, +* however at much less frequency), try to compensate it in +* future periods by updating the virtual time; otherwise just +* do a reset, as it may be a light dirtier. +*/ if (unlikely (pause <=0 )) { trace_balance_dirty_pages(bdi, dirty_thresh,
@@ -1112,8 +1129,16 @@ static void balance_dirty_pages(struct a dirty_ratelimit, task_ratelimit, pages_dirtied, + period, pause, start_time); +if (pause < -HZ ) { + current->dirty_paused_when = now; + current->nr_dirtied =0 ; + }else if (period) { + current->dirty_paused_when += period; + current->nr_dirtied =0 ; + } pause =1 ;/* avoid resetting nr_dirtied_pause below */ break ; }
@@ -1129,11 +1154,15 @@ pause: dirty_ratelimit, task_ratelimit, pages_dirtied, + period, pause, start_time);__set_current_state (TASK_KILLABLE );io_schedule_timeout (pause); + current->dirty_paused_when = now + pause; + current->nr_dirtied =0 ; +/* * This is typically equal to (nr_dirty < dirty_thresh) and can * also keep "1000+ dd on a slow USB stick" under control.
@@ -1148,11 +1177,10 @@ pause:if (!dirty_exceeded && bdi->dirty_exceeded) bdi->dirty_exceeded =0 ; - current->nr_dirtied =0 ;if (pause ==0 ) {/* in freerun area */ current->nr_dirtied_pause =dirty_poll_interval (nr_dirty, dirty_thresh); - }else if (pause <= max_pause /4 && + }else if (period <= max_pause /4 && pages_dirtied >= current->nr_dirtied_pause) { current->nr_dirtied_pause = clamp_val( dirty_ratelimit * (max_pause / 2) / HZ,
kernel/fork.c +1 -0
@@ -1296,6 +1296,7 @@ static struct task_struct *copy_process( p->nr_dirtied =0 ; p->nr_dirtied_pause =128 >> (PAGE_SHIFT -10 ); + p->dirty_paused_when =0 ;/* * Ok, make it visible to the rest of the system.
include/trace/events/writeback.h +11 -3
@@ -289,12 +289,13 @@ TRACE_EVENT(balance_dirty_pages,unsigned long dirty_ratelimit, unsigned long task_ratelimit, unsigned long dirtied, + unsigned long period, long pause, unsigned long start_time),TP_ARGS (bdi ,thresh ,bg_thresh ,dirty ,bdi_thresh ,bdi_dirty ,dirty_ratelimit ,task_ratelimit , - dirtied, pause, start_time), + dirtied, period, pause, start_time), TP_STRUCT__entry( __array( char, bdi, 32)
@@ -309,6 +310,8 @@ TRACE_EVENT(balance_dirty_pages, __field(unsigned int, dirtied_pause) __field(unsigned long, paused) __field( long, pause) + __field(unsigned long, period) + __field( long, think) ), TP_fast_assign(
@@ -325,6 +328,9 @@ TRACE_EVENT(balance_dirty_pages, __entry->task_ratelimit =KBps (task_ratelimit); __entry->dirtied = dirtied; __entry->dirtied_pause = current->nr_dirtied_pause; + __entry->think = current->dirty_paused_when ==0 ?0 : + (long )(jiffies - current->dirty_paused_when) *1000 /HZ ; + __entry->period = period *1000 /HZ ; __entry->pause = pause *1000 /HZ ; __entry->paused = (jiffies - start_time) *1000 /HZ ; ),
@@ -335,7 +341,7 @@ TRACE_EVENT(balance_dirty_pages,"bdi_setpoint=%lu bdi_dirty=%lu " "dirty_ratelimit=%lu task_ratelimit=%lu " "dirtied=%u dirtied_pause=%u " -"paused=%lu pause=%ld" , +"paused=%lu pause=%ld period=%lu think=%ld" , __entry->bdi, __entry->limit, __entry->setpoint,
@@ -347,7 +353,9 @@ TRACE_EVENT(balance_dirty_pages, __entry->dirtied, __entry->dirtied_pause, __entry->paused,/* ms */ - __entry->pause/* ms */ + __entry->pause,/* ms */ + __entry->period,/* ms */ + __entry->think/* ms */ ) );
Quoted 5 lines · Wu Fengguang
Hi,
I'd like to push these 5 dirty throttling improvements to linux-next, targeting for inclusion in Linux 3.3.
Quoted 8 lines
It's a comfortably small series for review ;) And there are no changes since the last v12 post. (I went through them once again but find nothing to change.)
[PATCH 1/5] writeback:
balanced_ratecannot exceed write bandwidth [PATCH 2/5] writeback: charge leaked page dirties to active tasks [PATCH 3/5] writeback: fix dirtied pages accounting on sub-page writes [PATCH 4/5] writeback: fix dirtied pages accounting on redirty [PATCH 5/5] writeback: dirty ratelimit - think time compensation
Given the updates I'm ok with this series,
Thanks!
Quoted 15 lines · Peter Zijlstra
Quoted 5 lines · Wu Fengguang
Hi,
I'd like to push these 5 dirty throttling improvements to linux-next, targeting for inclusion in Linux 3.3.
Quoted 8 lines
It's a comfortably small series for review ;) And there are no changes since the last v12 post. (I went through them once again but find nothing to change.)
[PATCH 1/5] writeback:
balanced_ratecannot exceed write bandwidth [PATCH 2/5] writeback: charge leaked page dirties to active tasks [PATCH 3/5] writeback: fix dirtied pages accounting on sub-page writes [PATCH 4/5] writeback: fix dirtied pages accounting on redirty [PATCH 5/5] writeback: dirty ratelimit - think time compensation
Quoted 5 lines
Given the updates I'm ok with this series,
Acked-by Peter Zijlstra <[email protected]>Thanks!
Thank you! I'll post the updated series with your acks :)
Thanks,
Fengguang