diff options
| author | Tejun Heo <tj@kernel.org> | 2015-05-22 18:23:22 -0400 |
|---|---|---|
| committer | Jens Axboe <axboe@fb.com> | 2015-06-02 10:38:12 -0400 |
| commit | dcc25ae76eb7b8ff883eaaab57e30e8f2f085be3 (patch) | |
| tree | 7fb1d01278ad2b16a1c21ead3e567a3bb7d00c25 | |
| parent | 380c27ca33ebecc9da35aa90c8b3a9154f90aac2 (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.c | 2 | ||||
| -rw-r--r-- | include/linux/writeback.h | 17 | ||||
| -rw-r--r-- | include/trace/events/writeback.h | 7 | ||||
| -rw-r--r-- | mm/page-writeback.c | 46 |
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 | */ |
| 97 | struct wb_domain { | 97 | struct 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); | |||
| 153 | bool zone_dirty_ok(struct zone *zone); | 168 | bool zone_dirty_ok(struct zone *zone); |
| 154 | int wb_domain_init(struct wb_domain *dom, gfp_t gfp); | 169 | int wb_domain_init(struct wb_domain *dom, gfp_t gfp); |
| 155 | 170 | ||
| 156 | extern unsigned long global_dirty_limit; | 171 | extern struct wb_domain global_wb_domain; |
| 157 | 172 | ||
| 158 | /* These are exported to sysctl. */ | 173 | /* These are exported to sysctl. */ |
| 159 | extern int dirty_background_ratio; | 174 | extern 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 | ||
| 125 | unsigned long global_dirty_limit; | 125 | struct wb_domain global_wb_domain; |
| 126 | |||
| 127 | static 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) | |||
| 470 | int wb_domain_init(struct wb_domain *dom, gfp_t gfp) | 468 | int 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 | ||
| 533 | static unsigned long hard_dirty_limit(unsigned long thresh) | 537 | static 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 | */ | ||
| 927 | static void update_dirty_limit(unsigned long thresh, unsigned long dirty) | 925 | static 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; |
| 950 | update: | 949 | update: |
| 951 | global_dirty_limit = limit; | 950 | dom->dirty_limit = limit; |
| 952 | } | 951 | } |
| 953 | 952 | ||
| 954 | static void global_update_bandwidth(unsigned long thresh, | 953 | static 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 | ||
