diff options
Diffstat (limited to 'fs/gfs2/aops.c')
| -rw-r--r-- | fs/gfs2/aops.c | 132 |
1 files changed, 96 insertions, 36 deletions
diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 49436fa7cd4f..ce62dcac90b6 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c | |||
| @@ -21,6 +21,7 @@ | |||
| 21 | #include <linux/gfs2_ondisk.h> | 21 | #include <linux/gfs2_ondisk.h> |
| 22 | #include <linux/backing-dev.h> | 22 | #include <linux/backing-dev.h> |
| 23 | #include <linux/aio.h> | 23 | #include <linux/aio.h> |
| 24 | #include <trace/events/writeback.h> | ||
| 24 | 25 | ||
| 25 | #include "gfs2.h" | 26 | #include "gfs2.h" |
| 26 | #include "incore.h" | 27 | #include "incore.h" |
| @@ -230,13 +231,11 @@ static int gfs2_writepages(struct address_space *mapping, | |||
| 230 | static int gfs2_write_jdata_pagevec(struct address_space *mapping, | 231 | static int gfs2_write_jdata_pagevec(struct address_space *mapping, |
| 231 | struct writeback_control *wbc, | 232 | struct writeback_control *wbc, |
| 232 | struct pagevec *pvec, | 233 | struct pagevec *pvec, |
| 233 | int nr_pages, pgoff_t end) | 234 | int nr_pages, pgoff_t end, |
| 235 | pgoff_t *done_index) | ||
| 234 | { | 236 | { |
| 235 | struct inode *inode = mapping->host; | 237 | struct inode *inode = mapping->host; |
| 236 | struct gfs2_sbd *sdp = GFS2_SB(inode); | 238 | struct gfs2_sbd *sdp = GFS2_SB(inode); |
| 237 | loff_t i_size = i_size_read(inode); | ||
| 238 | pgoff_t end_index = i_size >> PAGE_CACHE_SHIFT; | ||
| 239 | unsigned offset = i_size & (PAGE_CACHE_SIZE-1); | ||
| 240 | unsigned nrblocks = nr_pages * (PAGE_CACHE_SIZE/inode->i_sb->s_blocksize); | 239 | unsigned nrblocks = nr_pages * (PAGE_CACHE_SIZE/inode->i_sb->s_blocksize); |
| 241 | int i; | 240 | int i; |
| 242 | int ret; | 241 | int ret; |
| @@ -248,40 +247,83 @@ static int gfs2_write_jdata_pagevec(struct address_space *mapping, | |||
| 248 | for(i = 0; i < nr_pages; i++) { | 247 | for(i = 0; i < nr_pages; i++) { |
| 249 | struct page *page = pvec->pages[i]; | 248 | struct page *page = pvec->pages[i]; |
| 250 | 249 | ||
| 250 | /* | ||
| 251 | * At this point, the page may be truncated or | ||
| 252 | * invalidated (changing page->mapping to NULL), or | ||
| 253 | * even swizzled back from swapper_space to tmpfs file | ||
| 254 | * mapping. However, page->index will not change | ||
| 255 | * because we have a reference on the page. | ||
| 256 | */ | ||
| 257 | if (page->index > end) { | ||
| 258 | /* | ||
| 259 | * can't be range_cyclic (1st pass) because | ||
| 260 | * end == -1 in that case. | ||
| 261 | */ | ||
| 262 | ret = 1; | ||
| 263 | break; | ||
| 264 | } | ||
| 265 | |||
| 266 | *done_index = page->index; | ||
| 267 | |||
| 251 | lock_page(page); | 268 | lock_page(page); |
| 252 | 269 | ||
| 253 | if (unlikely(page->mapping != mapping)) { | 270 | if (unlikely(page->mapping != mapping)) { |
| 271 | continue_unlock: | ||
| 254 | unlock_page(page); | 272 | unlock_page(page); |
| 255 | continue; | 273 | continue; |
| 256 | } | 274 | } |
| 257 | 275 | ||
| 258 | if (!wbc->range_cyclic && page->index > end) { | 276 | if (!PageDirty(page)) { |
| 259 | ret = 1; | 277 | /* someone wrote it for us */ |
| 260 | unlock_page(page); | 278 | goto continue_unlock; |
| 261 | continue; | ||
| 262 | } | 279 | } |
| 263 | 280 | ||
| 264 | if (wbc->sync_mode != WB_SYNC_NONE) | 281 | if (PageWriteback(page)) { |
| 265 | wait_on_page_writeback(page); | 282 | if (wbc->sync_mode != WB_SYNC_NONE) |
| 266 | 283 | wait_on_page_writeback(page); | |
| 267 | if (PageWriteback(page) || | 284 | else |
| 268 | !clear_page_dirty_for_io(page)) { | 285 | goto continue_unlock; |
| 269 | unlock_page(page); | ||
| 270 | continue; | ||
| 271 | } | 286 | } |
| 272 | 287 | ||
| 273 | /* Is the page fully outside i_size? (truncate in progress) */ | 288 | BUG_ON(PageWriteback(page)); |
| 274 | if (page->index > end_index || (page->index == end_index && !offset)) { | 289 | if (!clear_page_dirty_for_io(page)) |
| 275 | page->mapping->a_ops->invalidatepage(page, 0, | 290 | goto continue_unlock; |
| 276 | PAGE_CACHE_SIZE); | 291 | |
| 277 | unlock_page(page); | 292 | trace_wbc_writepage(wbc, mapping->backing_dev_info); |
| 278 | continue; | ||
| 279 | } | ||
| 280 | 293 | ||
| 281 | ret = __gfs2_jdata_writepage(page, wbc); | 294 | ret = __gfs2_jdata_writepage(page, wbc); |
| 295 | if (unlikely(ret)) { | ||
| 296 | if (ret == AOP_WRITEPAGE_ACTIVATE) { | ||
| 297 | unlock_page(page); | ||
| 298 | ret = 0; | ||
| 299 | } else { | ||
| 300 | |||
| 301 | /* | ||
| 302 | * done_index is set past this page, | ||
| 303 | * so media errors will not choke | ||
| 304 | * background writeout for the entire | ||
| 305 | * file. This has consequences for | ||
| 306 | * range_cyclic semantics (ie. it may | ||
| 307 | * not be suitable for data integrity | ||
| 308 | * writeout). | ||
| 309 | */ | ||
| 310 | *done_index = page->index + 1; | ||
| 311 | ret = 1; | ||
| 312 | break; | ||
| 313 | } | ||
| 314 | } | ||
| 282 | 315 | ||
| 283 | if (ret || (--(wbc->nr_to_write) <= 0)) | 316 | /* |
| 317 | * We stop writing back only if we are not doing | ||
| 318 | * integrity sync. In case of integrity sync we have to | ||
| 319 | * keep going until we have written all the pages | ||
| 320 | * we tagged for writeback prior to entering this loop. | ||
| 321 | */ | ||
| 322 | if (--wbc->nr_to_write <= 0 && wbc->sync_mode == WB_SYNC_NONE) { | ||
| 284 | ret = 1; | 323 | ret = 1; |
| 324 | break; | ||
| 325 | } | ||
| 326 | |||
| 285 | } | 327 | } |
| 286 | gfs2_trans_end(sdp); | 328 | gfs2_trans_end(sdp); |
| 287 | return ret; | 329 | return ret; |
| @@ -306,51 +348,69 @@ static int gfs2_write_cache_jdata(struct address_space *mapping, | |||
| 306 | int done = 0; | 348 | int done = 0; |
| 307 | struct pagevec pvec; | 349 | struct pagevec pvec; |
| 308 | int nr_pages; | 350 | int nr_pages; |
| 351 | pgoff_t uninitialized_var(writeback_index); | ||
| 309 | pgoff_t index; | 352 | pgoff_t index; |
| 310 | pgoff_t end; | 353 | pgoff_t end; |
| 311 | int scanned = 0; | 354 | pgoff_t done_index; |
| 355 | int cycled; | ||
| 312 | int range_whole = 0; | 356 | int range_whole = 0; |
| 357 | int tag; | ||
| 313 | 358 | ||
| 314 | pagevec_init(&pvec, 0); | 359 | pagevec_init(&pvec, 0); |
| 315 | if (wbc->range_cyclic) { | 360 | if (wbc->range_cyclic) { |
| 316 | index = mapping->writeback_index; /* Start from prev offset */ | 361 | writeback_index = mapping->writeback_index; /* prev offset */ |
| 362 | index = writeback_index; | ||
| 363 | if (index == 0) | ||
| 364 | cycled = 1; | ||
| 365 | else | ||
| 366 | cycled = 0; | ||
| 317 | end = -1; | 367 | end = -1; |
| 318 | } else { | 368 | } else { |
| 319 | index = wbc->range_start >> PAGE_CACHE_SHIFT; | 369 | index = wbc->range_start >> PAGE_CACHE_SHIFT; |
| 320 | end = wbc->range_end >> PAGE_CACHE_SHIFT; | 370 | end = wbc->range_end >> PAGE_CACHE_SHIFT; |
| 321 | if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX) | 371 | if (wbc->range_start == 0 && wbc->range_end == LLONG_MAX) |
| 322 | range_whole = 1; | 372 | range_whole = 1; |
| 323 | scanned = 1; | 373 | cycled = 1; /* ignore range_cyclic tests */ |
| 324 | } | 374 | } |
| 375 | if (wbc->sync_mode == WB_SYNC_ALL || wbc->tagged_writepages) | ||
| 376 | tag = PAGECACHE_TAG_TOWRITE; | ||
| 377 | else | ||
| 378 | tag = PAGECACHE_TAG_DIRTY; | ||
| 325 | 379 | ||
| 326 | retry: | 380 | retry: |
| 327 | while (!done && (index <= end) && | 381 | if (wbc->sync_mode == WB_SYNC_ALL || wbc->tagged_writepages) |
| 328 | (nr_pages = pagevec_lookup_tag(&pvec, mapping, &index, | 382 | tag_pages_for_writeback(mapping, index, end); |
| 329 | PAGECACHE_TAG_DIRTY, | 383 | done_index = index; |
| 330 | min(end - index, (pgoff_t)PAGEVEC_SIZE-1) + 1))) { | 384 | while (!done && (index <= end)) { |
| 331 | scanned = 1; | 385 | nr_pages = pagevec_lookup_tag(&pvec, mapping, &index, tag, |
| 332 | ret = gfs2_write_jdata_pagevec(mapping, wbc, &pvec, nr_pages, end); | 386 | min(end - index, (pgoff_t)PAGEVEC_SIZE-1) + 1); |
| 387 | if (nr_pages == 0) | ||
| 388 | break; | ||
| 389 | |||
| 390 | ret = gfs2_write_jdata_pagevec(mapping, wbc, &pvec, nr_pages, end, &done_index); | ||
| 333 | if (ret) | 391 | if (ret) |
| 334 | done = 1; | 392 | done = 1; |
| 335 | if (ret > 0) | 393 | if (ret > 0) |
| 336 | ret = 0; | 394 | ret = 0; |
| 337 | |||
| 338 | pagevec_release(&pvec); | 395 | pagevec_release(&pvec); |
| 339 | cond_resched(); | 396 | cond_resched(); |
| 340 | } | 397 | } |
| 341 | 398 | ||
| 342 | if (!scanned && !done) { | 399 | if (!cycled && !done) { |
| 343 | /* | 400 | /* |
| 401 | * range_cyclic: | ||
| 344 | * We hit the last page and there is more work to be done: wrap | 402 | * We hit the last page and there is more work to be done: wrap |
| 345 | * back to the start of the file | 403 | * back to the start of the file |
| 346 | */ | 404 | */ |
| 347 | scanned = 1; | 405 | cycled = 1; |
| 348 | index = 0; | 406 | index = 0; |
| 407 | end = writeback_index - 1; | ||
| 349 | goto retry; | 408 | goto retry; |
| 350 | } | 409 | } |
| 351 | 410 | ||
| 352 | if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0)) | 411 | if (wbc->range_cyclic || (range_whole && wbc->nr_to_write > 0)) |
| 353 | mapping->writeback_index = index; | 412 | mapping->writeback_index = done_index; |
| 413 | |||
| 354 | return ret; | 414 | return ret; |
| 355 | } | 415 | } |
| 356 | 416 | ||
