summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mm/page-writeback.c44
1 files changed, 42 insertions, 2 deletions
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index f32f25092c66..cc351e6f9ed9 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -939,6 +939,43 @@ static unsigned long dirty_poll_interval(unsigned long dirty,
return 1;
}
+static unsigned long bdi_max_pause(struct backing_dev_info *bdi,
+ unsigned long bdi_dirty)
+{
+ unsigned long bw = bdi->avg_write_bandwidth;
+ unsigned long hi = ilog2(bw);
+ unsigned long lo = ilog2(bdi->dirty_ratelimit);
+ unsigned long t;
+
+ /* target for 20ms max pause on 1-dd case */
+ t = HZ / 50;
+
+ /*
+ * Scale up pause time for concurrent dirtiers in order to reduce CPU
+ * overheads.
+ *
+ * (N * 20ms) on 2^N concurrent tasks.
+ */
+ if (hi > lo)
+ t += (hi - lo) * (20 * HZ) / 1024;
+
+ /*
+ * Limit pause time for small memory systems. If sleeping for too long
+ * time, a small pool of dirty/writeback pages may go empty and disk go
+ * idle.
+ *
+ * 8 serves as the safety ratio.
+ */
+ if (bdi_dirty)
+ t = min(t, bdi_dirty * HZ / (8 * bw + 1));
+
+ /*
+ * The pause time will be settled within range (max_pause/4, max_pause).
+ * Apply a minimal value of 4 to get a non-zero max_pause/4.
+ */
+ return clamp_val(t, 4, MAX_PAUSE);
+}
+
/*
* 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
@@ -958,6 +995,7 @@ static void balance_dirty_pages(struct address_space *mapping,
unsigned long dirty_thresh;
unsigned long bdi_thresh;
long pause = 0;
+ long max_pause;
bool dirty_exceeded = false;
unsigned long task_ratelimit;
unsigned long dirty_ratelimit;
@@ -1035,18 +1073,20 @@ static void balance_dirty_pages(struct address_space *mapping,
nr_dirty, bdi_thresh, bdi_dirty,
start_time);
+ max_pause = bdi_max_pause(bdi, bdi_dirty);
+
dirty_ratelimit = bdi->dirty_ratelimit;
pos_ratio = bdi_position_ratio(bdi, dirty_thresh,
background_thresh, nr_dirty,
bdi_thresh, bdi_dirty);
if (unlikely(pos_ratio == 0)) {
- pause = MAX_PAUSE;
+ pause = max_pause;
goto pause;
}
task_ratelimit = (u64)dirty_ratelimit *
pos_ratio >> RATELIMIT_CALC_SHIFT;
pause = (HZ * pages_dirtied) / (task_ratelimit | 1);
- pause = min_t(long, pause, MAX_PAUSE);
+ pause = min(pause, max_pause);
pause:
__set_current_state(TASK_UNINTERRUPTIBLE);