aboutsummaryrefslogtreecommitdiffstats
path: root/fs/gfs2
diff options
context:
space:
mode:
authorSteven Whitehouse <swhiteho@redhat.com>2011-04-18 09:18:09 -0400
committerSteven Whitehouse <swhiteho@redhat.com>2011-04-20 04:01:37 -0400
commit4667a0ec32867865fd4deccf834594b3ea831baf (patch)
treebff74fb13700e4087972fce94e45fd69dae7939b /fs/gfs2
parentf42ab0852946c1fb5103682c5897eb3da908e4b0 (diff)
GFS2: Make writeback more responsive to system conditions
This patch adds writeback_control to writing back the AIL list. This means that we can then take advantage of the information we get in ->write_inode() in order to set off some pre-emptive writeback. In addition, the AIL code is cleaned up a bit to make it a bit simpler to understand. There is still more which can usefully be done in this area, but this is a good start at least. Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
Diffstat (limited to 'fs/gfs2')
-rw-r--r--fs/gfs2/export.c2
-rw-r--r--fs/gfs2/glock.c2
-rw-r--r--fs/gfs2/incore.h4
-rw-r--r--fs/gfs2/inode.c4
-rw-r--r--fs/gfs2/inode.h2
-rw-r--r--fs/gfs2/log.c165
-rw-r--r--fs/gfs2/log.h2
-rw-r--r--fs/gfs2/super.c7
8 files changed, 98 insertions, 90 deletions
diff --git a/fs/gfs2/export.c b/fs/gfs2/export.c
index b5a5e60df0d5..fe9945f2ff72 100644
--- a/fs/gfs2/export.c
+++ b/fs/gfs2/export.c
@@ -139,7 +139,7 @@ static struct dentry *gfs2_get_dentry(struct super_block *sb,
139 struct gfs2_sbd *sdp = sb->s_fs_info; 139 struct gfs2_sbd *sdp = sb->s_fs_info;
140 struct inode *inode; 140 struct inode *inode;
141 141
142 inode = gfs2_ilookup(sb, inum->no_addr); 142 inode = gfs2_ilookup(sb, inum->no_addr, 0);
143 if (inode) { 143 if (inode) {
144 if (GFS2_I(inode)->i_no_formal_ino != inum->no_formal_ino) { 144 if (GFS2_I(inode)->i_no_formal_ino != inum->no_formal_ino) {
145 iput(inode); 145 iput(inode);
diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c
index cb8776f0102e..eed4b6855614 100644
--- a/fs/gfs2/glock.c
+++ b/fs/gfs2/glock.c
@@ -649,7 +649,7 @@ static void delete_work_func(struct work_struct *work)
649 /* Note: Unsafe to dereference ip as we don't hold right refs/locks */ 649 /* Note: Unsafe to dereference ip as we don't hold right refs/locks */
650 650
651 if (ip) 651 if (ip)
652 inode = gfs2_ilookup(sdp->sd_vfs, no_addr); 652 inode = gfs2_ilookup(sdp->sd_vfs, no_addr, 1);
653 else 653 else
654 inode = gfs2_lookup_by_inum(sdp, no_addr, NULL, GFS2_BLKST_UNLINKED); 654 inode = gfs2_lookup_by_inum(sdp, no_addr, NULL, GFS2_BLKST_UNLINKED);
655 if (inode && !IS_ERR(inode)) { 655 if (inode && !IS_ERR(inode)) {
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index 5067beaffa68..69a63823f7c5 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -20,7 +20,6 @@
20 20
21#define DIO_WAIT 0x00000010 21#define DIO_WAIT 0x00000010
22#define DIO_METADATA 0x00000020 22#define DIO_METADATA 0x00000020
23#define DIO_ALL 0x00000100
24 23
25struct gfs2_log_operations; 24struct gfs2_log_operations;
26struct gfs2_log_element; 25struct gfs2_log_element;
@@ -377,8 +376,6 @@ struct gfs2_ail {
377 unsigned int ai_first; 376 unsigned int ai_first;
378 struct list_head ai_ail1_list; 377 struct list_head ai_ail1_list;
379 struct list_head ai_ail2_list; 378 struct list_head ai_ail2_list;
380
381 u64 ai_sync_gen;
382}; 379};
383 380
384struct gfs2_journal_extent { 381struct gfs2_journal_extent {
@@ -657,7 +654,6 @@ struct gfs2_sbd {
657 spinlock_t sd_ail_lock; 654 spinlock_t sd_ail_lock;
658 struct list_head sd_ail1_list; 655 struct list_head sd_ail1_list;
659 struct list_head sd_ail2_list; 656 struct list_head sd_ail2_list;
660 u64 sd_ail_sync_gen;
661 657
662 /* Replay stuff */ 658 /* Replay stuff */
663 659
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index 9b7b9e40073b..94c3a7db1116 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -74,14 +74,14 @@ static int iget_set(struct inode *inode, void *opaque)
74 return 0; 74 return 0;
75} 75}
76 76
77struct inode *gfs2_ilookup(struct super_block *sb, u64 no_addr) 77struct inode *gfs2_ilookup(struct super_block *sb, u64 no_addr, int non_block)
78{ 78{
79 unsigned long hash = (unsigned long)no_addr; 79 unsigned long hash = (unsigned long)no_addr;
80 struct gfs2_skip_data data; 80 struct gfs2_skip_data data;
81 81
82 data.no_addr = no_addr; 82 data.no_addr = no_addr;
83 data.skipped = 0; 83 data.skipped = 0;
84 data.non_block = 0; 84 data.non_block = non_block;
85 return ilookup5(sb, hash, iget_test, &data); 85 return ilookup5(sb, hash, iget_test, &data);
86} 86}
87 87
diff --git a/fs/gfs2/inode.h b/fs/gfs2/inode.h
index 842346eae836..8d1344a4e673 100644
--- a/fs/gfs2/inode.h
+++ b/fs/gfs2/inode.h
@@ -102,7 +102,7 @@ extern struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned type,
102extern struct inode *gfs2_lookup_by_inum(struct gfs2_sbd *sdp, u64 no_addr, 102extern struct inode *gfs2_lookup_by_inum(struct gfs2_sbd *sdp, u64 no_addr,
103 u64 *no_formal_ino, 103 u64 *no_formal_ino,
104 unsigned int blktype); 104 unsigned int blktype);
105extern struct inode *gfs2_ilookup(struct super_block *sb, u64 no_addr); 105extern struct inode *gfs2_ilookup(struct super_block *sb, u64 no_addr, int nonblock);
106 106
107extern int gfs2_inode_refresh(struct gfs2_inode *ip); 107extern int gfs2_inode_refresh(struct gfs2_inode *ip);
108 108
diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c
index 3ebafa1efad0..03e00417061b 100644
--- a/fs/gfs2/log.c
+++ b/fs/gfs2/log.c
@@ -18,6 +18,7 @@
18#include <linux/kthread.h> 18#include <linux/kthread.h>
19#include <linux/freezer.h> 19#include <linux/freezer.h>
20#include <linux/bio.h> 20#include <linux/bio.h>
21#include <linux/writeback.h>
21 22
22#include "gfs2.h" 23#include "gfs2.h"
23#include "incore.h" 24#include "incore.h"
@@ -83,50 +84,90 @@ void gfs2_remove_from_ail(struct gfs2_bufdata *bd)
83/** 84/**
84 * gfs2_ail1_start_one - Start I/O on a part of the AIL 85 * gfs2_ail1_start_one - Start I/O on a part of the AIL
85 * @sdp: the filesystem 86 * @sdp: the filesystem
86 * @tr: the part of the AIL 87 * @wbc: The writeback control structure
88 * @ai: The ail structure
87 * 89 *
88 */ 90 */
89 91
90static void gfs2_ail1_start_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai) 92static void gfs2_ail1_start_one(struct gfs2_sbd *sdp,
93 struct writeback_control *wbc,
94 struct gfs2_ail *ai)
91__releases(&sdp->sd_ail_lock) 95__releases(&sdp->sd_ail_lock)
92__acquires(&sdp->sd_ail_lock) 96__acquires(&sdp->sd_ail_lock)
93{ 97{
94 struct gfs2_glock *gl = NULL; 98 struct gfs2_glock *gl = NULL;
99 struct address_space *mapping;
95 struct gfs2_bufdata *bd, *s; 100 struct gfs2_bufdata *bd, *s;
96 struct buffer_head *bh; 101 struct buffer_head *bh;
97 int retry;
98 102
99 do { 103restart:
100 retry = 0; 104 list_for_each_entry_safe_reverse(bd, s, &ai->ai_ail1_list, bd_ail_st_list) {
105 bh = bd->bd_bh;
101 106
102 list_for_each_entry_safe_reverse(bd, s, &ai->ai_ail1_list, 107 gfs2_assert(sdp, bd->bd_ail == ai);
103 bd_ail_st_list) {
104 bh = bd->bd_bh;
105 108
106 gfs2_assert(sdp, bd->bd_ail == ai); 109 if (!buffer_busy(bh)) {
110 if (!buffer_uptodate(bh))
111 gfs2_io_error_bh(sdp, bh);
112 list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list);
113 continue;
114 }
115
116 if (!buffer_dirty(bh))
117 continue;
118 if (gl == bd->bd_gl)
119 continue;
120 gl = bd->bd_gl;
121 list_move(&bd->bd_ail_st_list, &ai->ai_ail1_list);
122 mapping = bh->b_page->mapping;
123 spin_unlock(&sdp->sd_ail_lock);
124 generic_writepages(mapping, wbc);
125 spin_lock(&sdp->sd_ail_lock);
126 if (wbc->nr_to_write <= 0)
127 break;
128 goto restart;
129 }
130}
107 131
108 if (!buffer_busy(bh)) {
109 if (!buffer_uptodate(bh))
110 gfs2_io_error_bh(sdp, bh);
111 list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list);
112 continue;
113 }
114 132
115 if (!buffer_dirty(bh)) 133/**
116 continue; 134 * gfs2_ail1_flush - start writeback of some ail1 entries
117 if (gl == bd->bd_gl) 135 * @sdp: The super block
118 continue; 136 * @wbc: The writeback control structure
119 gl = bd->bd_gl; 137 *
120 list_move(&bd->bd_ail_st_list, &ai->ai_ail1_list); 138 * Writes back some ail1 entries, according to the limits in the
139 * writeback control structure
140 */
121 141
122 spin_unlock(&sdp->sd_ail_lock); 142void gfs2_ail1_flush(struct gfs2_sbd *sdp, struct writeback_control *wbc)
123 filemap_fdatawrite(gfs2_glock2aspace(gl)); 143{
124 spin_lock(&sdp->sd_ail_lock); 144 struct list_head *head = &sdp->sd_ail1_list;
145 struct gfs2_ail *ai;
125 146
126 retry = 1; 147 spin_lock(&sdp->sd_ail_lock);
148 list_for_each_entry_reverse(ai, head, ai_list) {
149 if (wbc->nr_to_write <= 0)
127 break; 150 break;
128 } 151 gfs2_ail1_start_one(sdp, wbc, ai); /* This may drop ail lock */
129 } while (retry); 152 }
153 spin_unlock(&sdp->sd_ail_lock);
154}
155
156/**
157 * gfs2_ail1_start - start writeback of all ail1 entries
158 * @sdp: The superblock
159 */
160
161static void gfs2_ail1_start(struct gfs2_sbd *sdp)
162{
163 struct writeback_control wbc = {
164 .sync_mode = WB_SYNC_NONE,
165 .nr_to_write = LONG_MAX,
166 .range_start = 0,
167 .range_end = LLONG_MAX,
168 };
169
170 return gfs2_ail1_flush(sdp, &wbc);
130} 171}
131 172
132/** 173/**
@@ -136,7 +177,7 @@ __acquires(&sdp->sd_ail_lock)
136 * 177 *
137 */ 178 */
138 179
139static int gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai, int flags) 180static void gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai)
140{ 181{
141 struct gfs2_bufdata *bd, *s; 182 struct gfs2_bufdata *bd, *s;
142 struct buffer_head *bh; 183 struct buffer_head *bh;
@@ -144,71 +185,37 @@ static int gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai, int fl
144 list_for_each_entry_safe_reverse(bd, s, &ai->ai_ail1_list, 185 list_for_each_entry_safe_reverse(bd, s, &ai->ai_ail1_list,
145 bd_ail_st_list) { 186 bd_ail_st_list) {
146 bh = bd->bd_bh; 187 bh = bd->bd_bh;
147
148 gfs2_assert(sdp, bd->bd_ail == ai); 188 gfs2_assert(sdp, bd->bd_ail == ai);
149 189 if (buffer_busy(bh))
150 if (buffer_busy(bh)) { 190 continue;
151 if (flags & DIO_ALL)
152 continue;
153 else
154 break;
155 }
156
157 if (!buffer_uptodate(bh)) 191 if (!buffer_uptodate(bh))
158 gfs2_io_error_bh(sdp, bh); 192 gfs2_io_error_bh(sdp, bh);
159
160 list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list); 193 list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list);
161 } 194 }
162 195
163 return list_empty(&ai->ai_ail1_list);
164} 196}
165 197
166static void gfs2_ail1_start(struct gfs2_sbd *sdp) 198/**
167{ 199 * gfs2_ail1_empty - Try to empty the ail1 lists
168 struct list_head *head; 200 * @sdp: The superblock
169 u64 sync_gen; 201 *
170 struct gfs2_ail *ai; 202 * Tries to empty the ail1 lists, starting with the oldest first
171 int done = 0; 203 */
172
173 spin_lock(&sdp->sd_ail_lock);
174 head = &sdp->sd_ail1_list;
175 if (list_empty(head)) {
176 spin_unlock(&sdp->sd_ail_lock);
177 return;
178 }
179 sync_gen = sdp->sd_ail_sync_gen++;
180
181 while(!done) {
182 done = 1;
183 list_for_each_entry_reverse(ai, head, ai_list) {
184 if (ai->ai_sync_gen >= sync_gen)
185 continue;
186 ai->ai_sync_gen = sync_gen;
187 gfs2_ail1_start_one(sdp, ai); /* This may drop ail lock */
188 done = 0;
189 break;
190 }
191 }
192
193 spin_unlock(&sdp->sd_ail_lock);
194}
195 204
196static int gfs2_ail1_empty(struct gfs2_sbd *sdp, int flags) 205static int gfs2_ail1_empty(struct gfs2_sbd *sdp)
197{ 206{
198 struct gfs2_ail *ai, *s; 207 struct gfs2_ail *ai, *s;
199 int ret; 208 int ret;
200 209
201 spin_lock(&sdp->sd_ail_lock); 210 spin_lock(&sdp->sd_ail_lock);
202
203 list_for_each_entry_safe_reverse(ai, s, &sdp->sd_ail1_list, ai_list) { 211 list_for_each_entry_safe_reverse(ai, s, &sdp->sd_ail1_list, ai_list) {
204 if (gfs2_ail1_empty_one(sdp, ai, flags)) 212 gfs2_ail1_empty_one(sdp, ai);
213 if (list_empty(&ai->ai_ail1_list))
205 list_move(&ai->ai_list, &sdp->sd_ail2_list); 214 list_move(&ai->ai_list, &sdp->sd_ail2_list);
206 else if (!(flags & DIO_ALL)) 215 else
207 break; 216 break;
208 } 217 }
209
210 ret = list_empty(&sdp->sd_ail1_list); 218 ret = list_empty(&sdp->sd_ail1_list);
211
212 spin_unlock(&sdp->sd_ail_lock); 219 spin_unlock(&sdp->sd_ail_lock);
213 220
214 return ret; 221 return ret;
@@ -569,7 +576,7 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags, int pull)
569 set_buffer_uptodate(bh); 576 set_buffer_uptodate(bh);
570 clear_buffer_dirty(bh); 577 clear_buffer_dirty(bh);
571 578
572 gfs2_ail1_empty(sdp, 0); 579 gfs2_ail1_empty(sdp);
573 tail = current_tail(sdp); 580 tail = current_tail(sdp);
574 581
575 lh = (struct gfs2_log_header *)bh->b_data; 582 lh = (struct gfs2_log_header *)bh->b_data;
@@ -864,7 +871,7 @@ void gfs2_meta_syncfs(struct gfs2_sbd *sdp)
864 gfs2_log_flush(sdp, NULL); 871 gfs2_log_flush(sdp, NULL);
865 for (;;) { 872 for (;;) {
866 gfs2_ail1_start(sdp); 873 gfs2_ail1_start(sdp);
867 if (gfs2_ail1_empty(sdp, DIO_ALL)) 874 if (gfs2_ail1_empty(sdp))
868 break; 875 break;
869 msleep(10); 876 msleep(10);
870 } 877 }
@@ -900,17 +907,15 @@ int gfs2_logd(void *data)
900 907
901 preflush = atomic_read(&sdp->sd_log_pinned); 908 preflush = atomic_read(&sdp->sd_log_pinned);
902 if (gfs2_jrnl_flush_reqd(sdp) || t == 0) { 909 if (gfs2_jrnl_flush_reqd(sdp) || t == 0) {
903 gfs2_ail1_empty(sdp, DIO_ALL); 910 gfs2_ail1_empty(sdp);
904 gfs2_log_flush(sdp, NULL); 911 gfs2_log_flush(sdp, NULL);
905 gfs2_ail1_empty(sdp, DIO_ALL);
906 } 912 }
907 913
908 if (gfs2_ail_flush_reqd(sdp)) { 914 if (gfs2_ail_flush_reqd(sdp)) {
909 gfs2_ail1_start(sdp); 915 gfs2_ail1_start(sdp);
910 io_schedule(); 916 io_schedule();
911 gfs2_ail1_empty(sdp, 0); 917 gfs2_ail1_empty(sdp);
912 gfs2_log_flush(sdp, NULL); 918 gfs2_log_flush(sdp, NULL);
913 gfs2_ail1_empty(sdp, DIO_ALL);
914 } 919 }
915 920
916 wake_up(&sdp->sd_log_waitq); 921 wake_up(&sdp->sd_log_waitq);
diff --git a/fs/gfs2/log.h b/fs/gfs2/log.h
index 0d007f920234..ab0621698b73 100644
--- a/fs/gfs2/log.h
+++ b/fs/gfs2/log.h
@@ -12,6 +12,7 @@
12 12
13#include <linux/list.h> 13#include <linux/list.h>
14#include <linux/spinlock.h> 14#include <linux/spinlock.h>
15#include <linux/writeback.h>
15#include "incore.h" 16#include "incore.h"
16 17
17/** 18/**
@@ -59,6 +60,7 @@ extern struct buffer_head *gfs2_log_fake_buf(struct gfs2_sbd *sdp,
59extern void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl); 60extern void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl);
60extern void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *trans); 61extern void gfs2_log_commit(struct gfs2_sbd *sdp, struct gfs2_trans *trans);
61extern void gfs2_remove_from_ail(struct gfs2_bufdata *bd); 62extern void gfs2_remove_from_ail(struct gfs2_bufdata *bd);
63extern void gfs2_ail1_flush(struct gfs2_sbd *sdp, struct writeback_control *wbc);
62 64
63extern void gfs2_log_shutdown(struct gfs2_sbd *sdp); 65extern void gfs2_log_shutdown(struct gfs2_sbd *sdp);
64extern void gfs2_meta_syncfs(struct gfs2_sbd *sdp); 66extern void gfs2_meta_syncfs(struct gfs2_sbd *sdp);
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index 215c37bfc2a4..58fe3a4ac829 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -23,6 +23,7 @@
23#include <linux/time.h> 23#include <linux/time.h>
24#include <linux/wait.h> 24#include <linux/wait.h>
25#include <linux/writeback.h> 25#include <linux/writeback.h>
26#include <linux/backing-dev.h>
26 27
27#include "gfs2.h" 28#include "gfs2.h"
28#include "incore.h" 29#include "incore.h"
@@ -714,6 +715,7 @@ static int gfs2_write_inode(struct inode *inode, struct writeback_control *wbc)
714 struct gfs2_inode *ip = GFS2_I(inode); 715 struct gfs2_inode *ip = GFS2_I(inode);
715 struct gfs2_sbd *sdp = GFS2_SB(inode); 716 struct gfs2_sbd *sdp = GFS2_SB(inode);
716 struct address_space *metamapping = gfs2_glock2aspace(ip->i_gl); 717 struct address_space *metamapping = gfs2_glock2aspace(ip->i_gl);
718 struct backing_dev_info *bdi = metamapping->backing_dev_info;
717 struct gfs2_holder gh; 719 struct gfs2_holder gh;
718 struct buffer_head *bh; 720 struct buffer_head *bh;
719 struct timespec atime; 721 struct timespec atime;
@@ -747,6 +749,8 @@ do_flush:
747 if (wbc->sync_mode == WB_SYNC_ALL) 749 if (wbc->sync_mode == WB_SYNC_ALL)
748 gfs2_log_flush(GFS2_SB(inode), ip->i_gl); 750 gfs2_log_flush(GFS2_SB(inode), ip->i_gl);
749 filemap_fdatawrite(metamapping); 751 filemap_fdatawrite(metamapping);
752 if (bdi->dirty_exceeded)
753 gfs2_ail1_flush(sdp, wbc);
750 if (!ret && (wbc->sync_mode == WB_SYNC_ALL)) 754 if (!ret && (wbc->sync_mode == WB_SYNC_ALL))
751 ret = filemap_fdatawait(metamapping); 755 ret = filemap_fdatawait(metamapping);
752 if (ret) 756 if (ret)
@@ -1366,7 +1370,8 @@ static int gfs2_dinode_dealloc(struct gfs2_inode *ip)
1366 if (error) 1370 if (error)
1367 goto out_rindex_relse; 1371 goto out_rindex_relse;
1368 1372
1369 error = gfs2_trans_begin(sdp, RES_RG_BIT + RES_STATFS + RES_QUOTA, 1); 1373 error = gfs2_trans_begin(sdp, RES_RG_BIT + RES_STATFS + RES_QUOTA,
1374 sdp->sd_jdesc->jd_blocks);
1370 if (error) 1375 if (error)
1371 goto out_rg_gunlock; 1376 goto out_rg_gunlock;
1372 1377