aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2015-05-22 18:23:22 -0400
committerJens Axboe <axboe@fb.com>2015-06-02 10:38:12 -0400
commitdcc25ae76eb7b8ff883eaaab57e30e8f2f085be3 (patch)
tree7fb1d01278ad2b16a1c21ead3e567a3bb7d00c25
parent380c27ca33ebecc9da35aa90c8b3a9154f90aac2 (diff)
writeback: move global_dirty_limit into wb_domain
This patch is a part of the series to define wb_domain which represents a domain that wb's (bdi_writeback's) belong to and are measured against each other in. This will enable IO backpressure propagation for cgroup writeback. global_dirty_limit exists to regulate the global dirty threshold which is a property of the wb_domain. This patch moves hard_dirty_limit, dirty_lock, and update_time into wb_domain. This is pure reorganization and doesn't introduce any behavioral changes. Signed-off-by: Tejun Heo <tj@kernel.org> Cc: Jens Axboe <axboe@kernel.dk> Cc: Jan Kara <jack@suse.cz> Cc: Wu Fengguang <fengguang.wu@intel.com> Cc: Greg Thelen <gthelen@google.com> Signed-off-by: Jens Axboe <axboe@fb.com>
-rw-r--r--fs/fs-writeback.c2
-rw-r--r--include/linux/writeback.h17
-rw-r--r--include/trace/events/writeback.h7
-rw-r--r--mm/page-writeback.c46
4 files changed, 44 insertions, 28 deletions
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index cd89484486f6..51c8a5b14cdf 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -887,7 +887,7 @@ static long writeback_chunk_size(struct bdi_writeback *wb,
887 pages = LONG_MAX; 887 pages = LONG_MAX;
888 else { 888 else {
889 pages = min(wb->avg_write_bandwidth / 2, 889 pages = min(wb->avg_write_bandwidth / 2,
890 global_dirty_limit / DIRTY_SCOPE); 890 global_wb_domain.dirty_limit / DIRTY_SCOPE);
891 pages = min(pages, work->nr_pages); 891 pages = min(pages, work->nr_pages);
892 pages = round_down(pages + MIN_WRITEBACK_PAGES, 892 pages = round_down(pages + MIN_WRITEBACK_PAGES,
893 MIN_WRITEBACK_PAGES); 893 MIN_WRITEBACK_PAGES);
diff --git a/include/linux/writeback.h b/include/linux/writeback.h
index 3148db1296a2..5fdd4e1805e6 100644
--- a/include/linux/writeback.h
+++ b/include/linux/writeback.h
@@ -95,6 +95,8 @@ struct writeback_control {
95 * dirtyable memory accordingly. 95 * dirtyable memory accordingly.
96 */ 96 */
97struct wb_domain { 97struct wb_domain {
98 spinlock_t lock;
99
98 /* 100 /*
99 * Scale the writeback cache size proportional to the relative 101 * Scale the writeback cache size proportional to the relative
100 * writeout speed. 102 * writeout speed.
@@ -115,6 +117,19 @@ struct wb_domain {
115 struct fprop_global completions; 117 struct fprop_global completions;
116 struct timer_list period_timer; /* timer for aging of completions */ 118 struct timer_list period_timer; /* timer for aging of completions */
117 unsigned long period_time; 119 unsigned long period_time;
120
121 /*
122 * The dirtyable memory and dirty threshold could be suddenly
123 * knocked down by a large amount (eg. on the startup of KVM in a
124 * swapless system). This may throw the system into deep dirty
125 * exceeded state and throttle heavy/light dirtiers alike. To
126 * retain good responsiveness, maintain global_dirty_limit for
127 * tracking slowly down to the knocked down dirty threshold.
128 *
129 * Both fields are protected by ->lock.
130 */
131 unsigned long dirty_limit_tstamp;
132 unsigned long dirty_limit;
118}; 133};
119 134
120/* 135/*
@@ -153,7 +168,7 @@ void throttle_vm_writeout(gfp_t gfp_mask);
153bool zone_dirty_ok(struct zone *zone); 168bool zone_dirty_ok(struct zone *zone);
154int wb_domain_init(struct wb_domain *dom, gfp_t gfp); 169int wb_domain_init(struct wb_domain *dom, gfp_t gfp);
155 170
156extern unsigned long global_dirty_limit; 171extern struct wb_domain global_wb_domain;
157 172
158/* These are exported to sysctl. */ 173/* These are exported to sysctl. */
159extern int dirty_background_ratio; 174extern int dirty_background_ratio;
diff --git a/include/trace/events/writeback.h b/include/trace/events/writeback.h
index 9b876f6cc81a..bec69995968f 100644
--- a/include/trace/events/writeback.h
+++ b/include/trace/events/writeback.h
@@ -361,7 +361,7 @@ TRACE_EVENT(global_dirty_state,
361 __entry->nr_written = global_page_state(NR_WRITTEN); 361 __entry->nr_written = global_page_state(NR_WRITTEN);
362 __entry->background_thresh = background_thresh; 362 __entry->background_thresh = background_thresh;
363 __entry->dirty_thresh = dirty_thresh; 363 __entry->dirty_thresh = dirty_thresh;
364 __entry->dirty_limit = global_dirty_limit; 364 __entry->dirty_limit = global_wb_domain.dirty_limit;
365 ), 365 ),
366 366
367 TP_printk("dirty=%lu writeback=%lu unstable=%lu " 367 TP_printk("dirty=%lu writeback=%lu unstable=%lu "
@@ -463,8 +463,9 @@ TRACE_EVENT(balance_dirty_pages,
463 unsigned long freerun = (thresh + bg_thresh) / 2; 463 unsigned long freerun = (thresh + bg_thresh) / 2;
464 strlcpy(__entry->bdi, dev_name(bdi->dev), 32); 464 strlcpy(__entry->bdi, dev_name(bdi->dev), 32);
465 465
466 __entry->limit = global_dirty_limit; 466 __entry->limit = global_wb_domain.dirty_limit;
467 __entry->setpoint = (global_dirty_limit + freerun) / 2; 467 __entry->setpoint = (global_wb_domain.dirty_limit +
468 freerun) / 2;
468 __entry->dirty = dirty; 469 __entry->dirty = dirty;
469 __entry->bdi_setpoint = __entry->setpoint * 470 __entry->bdi_setpoint = __entry->setpoint *
470 bdi_thresh / (thresh + 1); 471 bdi_thresh / (thresh + 1);
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index 08e1737edb39..27e60ba8e688 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -122,9 +122,7 @@ EXPORT_SYMBOL(laptop_mode);
122 122
123/* End of sysctl-exported parameters */ 123/* End of sysctl-exported parameters */
124 124
125unsigned long global_dirty_limit; 125struct wb_domain global_wb_domain;
126
127static struct wb_domain global_wb_domain;
128 126
129/* 127/*
130 * Length of period for aging writeout fractions of bdis. This is an 128 * Length of period for aging writeout fractions of bdis. This is an
@@ -470,9 +468,15 @@ static void writeout_period(unsigned long t)
470int wb_domain_init(struct wb_domain *dom, gfp_t gfp) 468int wb_domain_init(struct wb_domain *dom, gfp_t gfp)
471{ 469{
472 memset(dom, 0, sizeof(*dom)); 470 memset(dom, 0, sizeof(*dom));
471
472 spin_lock_init(&dom->lock);
473
473 init_timer_deferrable(&dom->period_timer); 474 init_timer_deferrable(&dom->period_timer);
474 dom->period_timer.function = writeout_period; 475 dom->period_timer.function = writeout_period;
475 dom->period_timer.data = (unsigned long)dom; 476 dom->period_timer.data = (unsigned long)dom;
477
478 dom->dirty_limit_tstamp = jiffies;
479
476 return fprop_global_init(&dom->completions, gfp); 480 return fprop_global_init(&dom->completions, gfp);
477} 481}
478 482
@@ -532,7 +536,9 @@ static unsigned long dirty_freerun_ceiling(unsigned long thresh,
532 536
533static unsigned long hard_dirty_limit(unsigned long thresh) 537static unsigned long hard_dirty_limit(unsigned long thresh)
534{ 538{
535 return max(thresh, global_dirty_limit); 539 struct wb_domain *dom = &global_wb_domain;
540
541 return max(thresh, dom->dirty_limit);
536} 542}
537 543
538/** 544/**
@@ -916,17 +922,10 @@ out:
916 wb->avg_write_bandwidth = avg; 922 wb->avg_write_bandwidth = avg;
917} 923}
918 924
919/*
920 * The global dirtyable memory and dirty threshold could be suddenly knocked
921 * down by a large amount (eg. on the startup of KVM in a swapless system).
922 * This may throw the system into deep dirty exceeded state and throttle
923 * heavy/light dirtiers alike. To retain good responsiveness, maintain
924 * global_dirty_limit for tracking slowly down to the knocked down dirty
925 * threshold.
926 */
927static void update_dirty_limit(unsigned long thresh, unsigned long dirty) 925static void update_dirty_limit(unsigned long thresh, unsigned long dirty)
928{ 926{
929 unsigned long limit = global_dirty_limit; 927 struct wb_domain *dom = &global_wb_domain;
928 unsigned long limit = dom->dirty_limit;
930 929
931 /* 930 /*
932 * Follow up in one step. 931 * Follow up in one step.
@@ -939,7 +938,7 @@ static void update_dirty_limit(unsigned long thresh, unsigned long dirty)
939 /* 938 /*
940 * Follow down slowly. Use the higher one as the target, because thresh 939 * Follow down slowly. Use the higher one as the target, because thresh
941 * may drop below dirty. This is exactly the reason to introduce 940 * may drop below dirty. This is exactly the reason to introduce
942 * global_dirty_limit which is guaranteed to lie above the dirty pages. 941 * dom->dirty_limit which is guaranteed to lie above the dirty pages.
943 */ 942 */
944 thresh = max(thresh, dirty); 943 thresh = max(thresh, dirty);
945 if (limit > thresh) { 944 if (limit > thresh) {
@@ -948,28 +947,27 @@ static void update_dirty_limit(unsigned long thresh, unsigned long dirty)
948 } 947 }
949 return; 948 return;
950update: 949update:
951 global_dirty_limit = limit; 950 dom->dirty_limit = limit;
952} 951}
953 952
954static void global_update_bandwidth(unsigned long thresh, 953static void global_update_bandwidth(unsigned long thresh,
955 unsigned long dirty, 954 unsigned long dirty,
956 unsigned long now) 955 unsigned long now)
957{ 956{
958 static DEFINE_SPINLOCK(dirty_lock); 957 struct wb_domain *dom = &global_wb_domain;
959 static unsigned long update_time = INITIAL_JIFFIES;
960 958
961 /* 959 /*
962 * check locklessly first to optimize away locking for the most time 960 * check locklessly first to optimize away locking for the most time
963 */ 961 */
964 if (time_before(now, update_time + BANDWIDTH_INTERVAL)) 962 if (time_before(now, dom->dirty_limit_tstamp + BANDWIDTH_INTERVAL))
965 return; 963 return;
966 964
967 spin_lock(&dirty_lock); 965 spin_lock(&dom->lock);
968 if (time_after_eq(now, update_time + BANDWIDTH_INTER