[PATCH] splice: improve writeback and clean up page stealing

By cleaning up the writeback logic (killing write_one_page() and the manual
set_page_dirty()), we can get rid of ->stolen inside the pipe_buffer and
just keep it local in pipe_to_file().

This also adds dirty page balancing logic and O_SYNC handling.

Signed-off-by: Jens Axboe <axboe@suse.de>

+48 -18
-1
fs/pipe.c
··· 124 124 static int anon_pipe_buf_steal(struct pipe_inode_info *info, 125 125 struct pipe_buffer *buf) 126 126 { 127 - buf->stolen = 1; 128 127 return 0; 129 128 } 130 129
+48 -16
fs/splice.c
··· 22 22 #include <linux/pipe_fs_i.h> 23 23 #include <linux/mm_inline.h> 24 24 #include <linux/swap.h> 25 + #include <linux/writeback.h> 26 + #include <linux/buffer_head.h> 25 27 #include <linux/module.h> 28 + #include <linux/syscalls.h> 26 29 27 30 /* 28 31 * Passed to the actors ··· 41 38 struct pipe_buffer *buf) 42 39 { 43 40 struct page *page = buf->page; 41 + struct address_space *mapping = page_mapping(page); 44 42 45 43 WARN_ON(!PageLocked(page)); 46 44 WARN_ON(!PageUptodate(page)); 47 45 48 - if (!remove_mapping(page_mapping(page), page)) 46 + if (PagePrivate(page)) 47 + try_to_release_page(page, mapping_gfp_mask(mapping)); 48 + 49 + if (!remove_mapping(mapping, page)) 49 50 return 1; 50 51 51 52 if (PageLRU(page)) { ··· 62 55 spin_unlock_irq(&zone->lru_lock); 63 56 } 64 57 65 - buf->stolen = 1; 66 58 return 0; 67 59 } 68 60 ··· 70 64 { 71 65 page_cache_release(buf->page); 72 66 buf->page = NULL; 73 - buf->stolen = 0; 74 67 } 75 68 76 69 static void *page_cache_pipe_buf_map(struct file *file, ··· 96 91 static void page_cache_pipe_buf_unmap(struct pipe_inode_info *info, 97 92 struct pipe_buffer *buf) 98 93 { 99 - if (!buf->stolen) 100 - unlock_page(buf->page); 94 + unlock_page(buf->page); 101 95 kunmap(buf->page); 102 96 } 103 97 ··· 323 319 } 324 320 325 321 /* 326 - * Send 'len' bytes to socket from 'file' at position 'pos' using sendpage(). 322 + * Send 'sd->len' bytes to socket from 'sd->file' at position 'sd->pos' 323 + * using sendpage(). 327 324 */ 328 325 static int pipe_to_sendpage(struct pipe_inode_info *info, 329 326 struct pipe_buffer *buf, struct splice_desc *sd) ··· 384 379 struct page *page; 385 380 pgoff_t index; 386 381 char *src; 387 - int ret; 382 + int ret, stolen; 388 383 389 384 /* 390 385 * after this, page will be locked and unmapped ··· 395 390 396 391 index = sd->pos >> PAGE_CACHE_SHIFT; 397 392 offset = sd->pos & ~PAGE_CACHE_MASK; 393 + stolen = 0; 398 394 399 395 /* 400 396 * reuse buf page, if SPLICE_F_MOVE is set ··· 405 399 goto find_page; 406 400 407 401 page = buf->page; 402 + stolen = 1; 408 403 if (add_to_page_cache_lru(page, mapping, index, 409 404 mapping_gfp_mask(mapping))) 410 405 goto find_page; ··· 450 443 } 451 444 452 445 ret = mapping->a_ops->prepare_write(file, page, 0, sd->len); 453 - if (ret) 446 + if (ret == AOP_TRUNCATED_PAGE) { 447 + page_cache_release(page); 448 + goto find_page; 449 + } else if (ret) 454 450 goto out; 455 451 456 - if (!buf->stolen) { 452 + if (!stolen) { 457 453 char *dst = kmap_atomic(page, KM_USER0); 458 454 459 455 memcpy(dst + offset, src + buf->offset, sd->len); ··· 465 455 } 466 456 467 457 ret = mapping->a_ops->commit_write(file, page, 0, sd->len); 468 - if (ret < 0) 458 + if (ret == AOP_TRUNCATED_PAGE) { 459 + page_cache_release(page); 460 + goto find_page; 461 + } else if (ret) 469 462 goto out; 470 463 471 - set_page_dirty(page); 472 - ret = write_one_page(page, 0); 464 + balance_dirty_pages_ratelimited(mapping); 473 465 out: 474 - if (ret < 0) 475 - unlock_page(page); 476 - if (!buf->stolen) 466 + if (!stolen) { 477 467 page_cache_release(page); 468 + unlock_page(page); 469 + } 478 470 buf->ops->unmap(info, buf); 479 471 return ret; 480 472 } ··· 588 576 ssize_t generic_file_splice_write(struct inode *inode, struct file *out, 589 577 size_t len, unsigned int flags) 590 578 { 591 - return move_from_pipe(inode, out, len, flags, pipe_to_file); 579 + struct address_space *mapping = out->f_mapping; 580 + ssize_t ret = move_from_pipe(inode, out, len, flags, pipe_to_file); 581 + 582 + /* 583 + * if file or inode is SYNC and we actually wrote some data, sync it 584 + */ 585 + if (unlikely((out->f_flags & O_SYNC) || IS_SYNC(mapping->host)) 586 + && ret > 0) { 587 + struct inode *inode = mapping->host; 588 + int err; 589 + 590 + mutex_lock(&inode->i_mutex); 591 + err = generic_osync_inode(mapping->host, mapping, 592 + OSYNC_METADATA|OSYNC_DATA); 593 + mutex_unlock(&inode->i_mutex); 594 + 595 + if (err) 596 + ret = err; 597 + } 598 + 599 + return ret; 592 600 } 593 601 594 602 ssize_t generic_splice_sendpage(struct inode *inode, struct file *out,
-1
include/linux/pipe_fs_i.h
··· 9 9 struct page *page; 10 10 unsigned int offset, len; 11 11 struct pipe_buf_operations *ops; 12 - unsigned int stolen; 13 12 }; 14 13 15 14 struct pipe_buf_operations {