aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Marzinski <bmarzins@redhat.com>2016-06-27 11:01:06 -0400
committerBob Peterson <rpeterso@redhat.com>2016-06-27 11:03:12 -0400
commitfd4c5748b8d3f7420e8932ed0bde3d53cc8acc9d (patch)
tree52a1f020985cf68c53ee18431a5df0742ac4785d
parentb4bba38909c21689de21355e84259cb7b38f25ac (diff)
gfs2: writeout truncated pages
When gfs2 attempts to write a page to a file that is being truncated, and notices that the page is completely outside of the file size, it tries to invalidate it. However, this may require a transaction for journaled data files to revoke any buffers from the page on the active items list. Unfortunately, this can happen inside a log flush, where a transaction cannot be started. Also, gfs2 may need to be able to remove the buffer from the ail1 list before it can finish the log flush. To deal with this, when writing a page of a file with data journalling enabled gfs2 now skips the check to see if the write is outside the file size, and simply writes it anyway. This situation can only occur when the truncate code still has the file locked exclusively, and hasn't marked this block as free in the metadata (which happens later in truc_dealloc). After gfs2 writes this page out, the truncation code will shortly invalidate it and write out any revokes if necessary. To do this, gfs2 now implements its own version of block_write_full_page without the check, and calls the newly exported __block_write_full_page. It also no longer calls gfs2_writepage_common from gfs2_jdata_writepage. Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com> Signed-off-by: Bob Peterson <rpeterso@redhat.com>
-rw-r--r--fs/gfs2/aops.c49
1 files changed, 34 insertions, 15 deletions
diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c
index 37b7bc14c8da..82df36886938 100644
--- a/fs/gfs2/aops.c
+++ b/fs/gfs2/aops.c
@@ -140,6 +140,32 @@ static int gfs2_writepage(struct page *page, struct writeback_control *wbc)
140 return nobh_writepage(page, gfs2_get_block_noalloc, wbc); 140 return nobh_writepage(page, gfs2_get_block_noalloc, wbc);
141} 141}
142 142
143/* This is the same as calling block_write_full_page, but it also
144 * writes pages outside of i_size
145 */
146int gfs2_write_full_page(struct page *page, get_block_t *get_block,
147 struct writeback_control *wbc)
148{
149 struct inode * const inode = page->mapping->host;
150 loff_t i_size = i_size_read(inode);
151 const pgoff_t end_index = i_size >> PAGE_SHIFT;
152 unsigned offset;
153
154 /*
155 * The page straddles i_size. It must be zeroed out on each and every
156 * writepage invocation because it may be mmapped. "A file is mapped
157 * in multiples of the page size. For a file that is not a multiple of
158 * the page size, the remaining memory is zeroed when mapped, and
159 * writes to that region are not written out to the file."
160 */
161 offset = i_size & (PAGE_SIZE-1);
162 if (page->index == end_index && offset)
163 zero_user_segment(page, offset, PAGE_SIZE);
164
165 return __block_write_full_page(inode, page, get_block, wbc,
166 end_buffer_async_write);
167}
168
143/** 169/**
144 * __gfs2_jdata_writepage - The core of jdata writepage 170 * __gfs2_jdata_writepage - The core of jdata writepage
145 * @page: The page to write 171 * @page: The page to write
@@ -165,7 +191,7 @@ static int __gfs2_jdata_writepage(struct page *page, struct writeback_control *w
165 } 191 }
166 gfs2_page_add_databufs(ip, page, 0, sdp->sd_vfs->s_blocksize-1); 192 gfs2_page_add_databufs(ip, page, 0, sdp->sd_vfs->s_blocksize-1);
167 } 193 }
168 return block_write_full_page(page, gfs2_get_block_noalloc, wbc); 194 return gfs2_write_full_page(page, gfs2_get_block_noalloc, wbc);
169} 195}
170 196
171/** 197/**
@@ -180,27 +206,20 @@ static int __gfs2_jdata_writepage(struct page *page, struct writeback_control *w
180static int gfs2_jdata_writepage(struct page *page, struct writeback_control *wbc) 206static int gfs2_jdata_writepage(struct page *page, struct writeback_control *wbc)
181{ 207{
182 struct inode *inode = page->mapping->host; 208 struct inode *inode = page->mapping->host;
209 struct gfs2_inode *ip = GFS2_I(inode);
183 struct gfs2_sbd *sdp = GFS2_SB(inode); 210 struct gfs2_sbd *sdp = GFS2_SB(inode);
184 int ret; 211 int ret;
185 int done_trans = 0;
186 212
187 if (PageChecked(page)) { 213 if (gfs2_assert_withdraw(sdp, gfs2_glock_is_held_excl(ip->i_gl)))
188 if (wbc->sync_mode != WB_SYNC_ALL) 214 goto out;
189 goto out_ignore; 215 if (PageChecked(page) || current->journal_info)
190 ret = gfs2_trans_begin(sdp, RES_DINODE + 1, 0); 216 goto out_ignore;
191 if (ret) 217 ret = __gfs2_jdata_writepage(page, wbc);
192 goto out_ignore;
193 done_trans = 1;
194 }
195 ret = gfs2_writepage_common(page, wbc);
196 if (ret > 0)
197 ret = __gfs2_jdata_writepage(page, wbc);
198 if (done_trans)
199 gfs2_trans_end(sdp);
200 return ret; 218 return ret;
201 219
202out_ignore: 220out_ignore:
203 redirty_page_for_writepage(wbc, page); 221 redirty_page_for_writepage(wbc, page);
222out:
204 unlock_page(page); 223 unlock_page(page);
205 return 0; 224 return 0;
206} 225}