diff options
author | Linus Torvalds <torvalds@g5.osdl.org> | 2006-05-04 13:25:40 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-05-04 13:25:40 -0400 |
commit | cbdf811c77cf2906a099b8da92e9f6f335b68a73 (patch) | |
tree | c4f96893936eddb31c60476ea4e776499d9046df | |
parent | 936ef1d48ac9a9fc90746bacd93624969711775a (diff) | |
parent | 98232d504db0a1f91ecaa93686ed3bf61963103b (diff) |
Merge branch 'splice' of git://brick.kernel.dk/data/git/linux-2.6-block
* 'splice' of git://brick.kernel.dk/data/git/linux-2.6-block:
[PATCH] compat_sys_vmsplice: one-off in UIO_MAXIOV check
[PATCH] splice: redo page lookup if add_to_page_cache() returns -EEXIST
[PATCH] splice: rename remaining info variables to pipe
[PATCH] splice: LRU fixups
[PATCH] splice: fix unlocking of page on error ->prepare_write()
-rw-r--r-- | fs/compat.c | 2 | ||||
-rw-r--r-- | fs/splice.c | 74 | ||||
-rw-r--r-- | include/linux/pipe_fs_i.h | 5 |
3 files changed, 43 insertions, 38 deletions
diff --git a/fs/compat.c b/fs/compat.c index 3f3e8f4d43d6..970888aad843 100644 --- a/fs/compat.c +++ b/fs/compat.c | |||
@@ -1323,7 +1323,7 @@ compat_sys_vmsplice(int fd, const struct compat_iovec __user *iov32, | |||
1323 | { | 1323 | { |
1324 | unsigned i; | 1324 | unsigned i; |
1325 | struct iovec *iov; | 1325 | struct iovec *iov; |
1326 | if (nr_segs >= UIO_MAXIOV) | 1326 | if (nr_segs > UIO_MAXIOV) |
1327 | return -EINVAL; | 1327 | return -EINVAL; |
1328 | iov = compat_alloc_user_space(nr_segs * sizeof(struct iovec)); | 1328 | iov = compat_alloc_user_space(nr_segs * sizeof(struct iovec)); |
1329 | for (i = 0; i < nr_segs; i++) { | 1329 | for (i = 0; i < nr_segs; i++) { |
diff --git a/fs/splice.c b/fs/splice.c index 7fb04970c72d..a285fd746dc0 100644 --- a/fs/splice.c +++ b/fs/splice.c | |||
@@ -51,7 +51,7 @@ struct splice_pipe_desc { | |||
51 | * addition of remove_mapping(). If success is returned, the caller may | 51 | * addition of remove_mapping(). If success is returned, the caller may |
52 | * attempt to reuse this page for another destination. | 52 | * attempt to reuse this page for another destination. |
53 | */ | 53 | */ |
54 | static int page_cache_pipe_buf_steal(struct pipe_inode_info *info, | 54 | static int page_cache_pipe_buf_steal(struct pipe_inode_info *pipe, |
55 | struct pipe_buffer *buf) | 55 | struct pipe_buffer *buf) |
56 | { | 56 | { |
57 | struct page *page = buf->page; | 57 | struct page *page = buf->page; |
@@ -78,16 +78,18 @@ static int page_cache_pipe_buf_steal(struct pipe_inode_info *info, | |||
78 | return 1; | 78 | return 1; |
79 | } | 79 | } |
80 | 80 | ||
81 | buf->flags |= PIPE_BUF_FLAG_LRU; | ||
81 | return 0; | 82 | return 0; |
82 | } | 83 | } |
83 | 84 | ||
84 | static void page_cache_pipe_buf_release(struct pipe_inode_info *info, | 85 | static void page_cache_pipe_buf_release(struct pipe_inode_info *pipe, |
85 | struct pipe_buffer *buf) | 86 | struct pipe_buffer *buf) |
86 | { | 87 | { |
87 | page_cache_release(buf->page); | 88 | page_cache_release(buf->page); |
89 | buf->flags &= ~PIPE_BUF_FLAG_LRU; | ||
88 | } | 90 | } |
89 | 91 | ||
90 | static int page_cache_pipe_buf_pin(struct pipe_inode_info *info, | 92 | static int page_cache_pipe_buf_pin(struct pipe_inode_info *pipe, |
91 | struct pipe_buffer *buf) | 93 | struct pipe_buffer *buf) |
92 | { | 94 | { |
93 | struct page *page = buf->page; | 95 | struct page *page = buf->page; |
@@ -141,6 +143,7 @@ static int user_page_pipe_buf_steal(struct pipe_inode_info *pipe, | |||
141 | if (!(buf->flags & PIPE_BUF_FLAG_GIFT)) | 143 | if (!(buf->flags & PIPE_BUF_FLAG_GIFT)) |
142 | return 1; | 144 | return 1; |
143 | 145 | ||
146 | buf->flags |= PIPE_BUF_FLAG_LRU; | ||
144 | return generic_pipe_buf_steal(pipe, buf); | 147 | return generic_pipe_buf_steal(pipe, buf); |
145 | } | 148 | } |
146 | 149 | ||
@@ -321,6 +324,8 @@ __generic_file_splice_read(struct file *in, loff_t *ppos, | |||
321 | mapping_gfp_mask(mapping)); | 324 | mapping_gfp_mask(mapping)); |
322 | if (unlikely(error)) { | 325 | if (unlikely(error)) { |
323 | page_cache_release(page); | 326 | page_cache_release(page); |
327 | if (error == -EEXIST) | ||
328 | continue; | ||
324 | break; | 329 | break; |
325 | } | 330 | } |
326 | /* | 331 | /* |
@@ -497,14 +502,14 @@ EXPORT_SYMBOL(generic_file_splice_read); | |||
497 | * Send 'sd->len' bytes to socket from 'sd->file' at position 'sd->pos' | 502 | * Send 'sd->len' bytes to socket from 'sd->file' at position 'sd->pos' |
498 | * using sendpage(). Return the number of bytes sent. | 503 | * using sendpage(). Return the number of bytes sent. |
499 | */ | 504 | */ |
500 | static int pipe_to_sendpage(struct pipe_inode_info *info, | 505 | static int pipe_to_sendpage(struct pipe_inode_info *pipe, |
501 | struct pipe_buffer *buf, struct splice_desc *sd) | 506 | struct pipe_buffer *buf, struct splice_desc *sd) |
502 | { | 507 | { |
503 | struct file *file = sd->file; | 508 | struct file *file = sd->file; |
504 | loff_t pos = sd->pos; | 509 | loff_t pos = sd->pos; |
505 | int ret, more; | 510 | int ret, more; |
506 | 511 | ||
507 | ret = buf->ops->pin(info, buf); | 512 | ret = buf->ops->pin(pipe, buf); |
508 | if (!ret) { | 513 | if (!ret) { |
509 | more = (sd->flags & SPLICE_F_MORE) || sd->len < sd->total_len; | 514 | more = (sd->flags & SPLICE_F_MORE) || sd->len < sd->total_len; |
510 | 515 | ||
@@ -535,7 +540,7 @@ static int pipe_to_sendpage(struct pipe_inode_info *info, | |||
535 | * SPLICE_F_MOVE isn't set, or we cannot move the page, we simply create | 540 | * SPLICE_F_MOVE isn't set, or we cannot move the page, we simply create |
536 | * a new page in the output file page cache and fill/dirty that. | 541 | * a new page in the output file page cache and fill/dirty that. |
537 | */ | 542 | */ |
538 | static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf, | 543 | static int pipe_to_file(struct pipe_inode_info *pipe, struct pipe_buffer *buf, |
539 | struct splice_desc *sd) | 544 | struct splice_desc *sd) |
540 | { | 545 | { |
541 | struct file *file = sd->file; | 546 | struct file *file = sd->file; |
@@ -549,7 +554,7 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf, | |||
549 | /* | 554 | /* |
550 | * make sure the data in this buffer is uptodate | 555 | * make sure the data in this buffer is uptodate |
551 | */ | 556 | */ |
552 | ret = buf->ops->pin(info, buf); | 557 | ret = buf->ops->pin(pipe, buf); |
553 | if (unlikely(ret)) | 558 | if (unlikely(ret)) |
554 | return ret; | 559 | return ret; |
555 | 560 | ||
@@ -566,37 +571,23 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf, | |||
566 | */ | 571 | */ |
567 | if ((sd->flags & SPLICE_F_MOVE) && this_len == PAGE_CACHE_SIZE) { | 572 | if ((sd->flags & SPLICE_F_MOVE) && this_len == PAGE_CACHE_SIZE) { |
568 | /* | 573 | /* |
569 | * If steal succeeds, buf->page is now pruned from the vm | 574 | * If steal succeeds, buf->page is now pruned from the |
570 | * side (page cache) and we can reuse it. The page will also | 575 | * pagecache and we can reuse it. The page will also be |
571 | * be locked on successful return. | 576 | * locked on successful return. |
572 | */ | 577 | */ |
573 | if (buf->ops->steal(info, buf)) | 578 | if (buf->ops->steal(pipe, buf)) |
574 | goto find_page; | 579 | goto find_page; |
575 | 580 | ||
576 | page = buf->page; | 581 | page = buf->page; |
577 | page_cache_get(page); | ||
578 | |||
579 | /* | ||
580 | * page must be on the LRU for adding to the pagecache. | ||
581 | * Check this without grabbing the zone lock, if it isn't | ||
582 | * the do grab the zone lock, recheck, and add if necessary. | ||
583 | */ | ||
584 | if (!PageLRU(page)) { | ||
585 | struct zone *zone = page_zone(page); | ||
586 | |||
587 | spin_lock_irq(&zone->lru_lock); | ||
588 | if (!PageLRU(page)) { | ||
589 | SetPageLRU(page); | ||
590 | add_page_to_inactive_list(zone, page); | ||
591 | } | ||
592 | spin_unlock_irq(&zone->lru_lock); | ||
593 | } | ||
594 | |||
595 | if (add_to_page_cache(page, mapping, index, gfp_mask)) { | 582 | if (add_to_page_cache(page, mapping, index, gfp_mask)) { |
596 | page_cache_release(page); | ||
597 | unlock_page(page); | 583 | unlock_page(page); |
598 | goto find_page; | 584 | goto find_page; |
599 | } | 585 | } |
586 | |||
587 | page_cache_get(page); | ||
588 | |||
589 | if (!(buf->flags & PIPE_BUF_FLAG_LRU)) | ||
590 | lru_cache_add(page); | ||
600 | } else { | 591 | } else { |
601 | find_page: | 592 | find_page: |
602 | page = find_lock_page(mapping, index); | 593 | page = find_lock_page(mapping, index); |
@@ -647,23 +638,36 @@ find_page: | |||
647 | } | 638 | } |
648 | 639 | ||
649 | ret = mapping->a_ops->prepare_write(file, page, offset, offset+this_len); | 640 | ret = mapping->a_ops->prepare_write(file, page, offset, offset+this_len); |
650 | if (ret == AOP_TRUNCATED_PAGE) { | 641 | if (unlikely(ret)) { |
642 | loff_t isize = i_size_read(mapping->host); | ||
643 | |||
644 | if (ret != AOP_TRUNCATED_PAGE) | ||
645 | unlock_page(page); | ||
651 | page_cache_release(page); | 646 | page_cache_release(page); |
652 | goto find_page; | 647 | if (ret == AOP_TRUNCATED_PAGE) |
653 | } else if (ret) | 648 | goto find_page; |
649 | |||
650 | /* | ||
651 | * prepare_write() may have instantiated a few blocks | ||
652 | * outside i_size. Trim these off again. | ||
653 | */ | ||
654 | if (sd->pos + this_len > isize) | ||
655 | vmtruncate(mapping->host, isize); | ||
656 | |||
654 | goto out; | 657 | goto out; |
658 | } | ||
655 | 659 | ||
656 | if (buf->page != page) { | 660 | if (buf->page != page) { |
657 | /* | 661 | /* |
658 | * Careful, ->map() uses KM_USER0! | 662 | * Careful, ->map() uses KM_USER0! |
659 | */ | 663 | */ |
660 | char *src = buf->ops->map(info, buf, 1); | 664 | char *src = buf->ops->map(pipe, buf, 1); |
661 | char *dst = kmap_atomic(page, KM_USER1); | 665 | char *dst = kmap_atomic(page, KM_USER1); |
662 | 666 | ||
663 | memcpy(dst + offset, src + buf->offset, this_len); | 667 | memcpy(dst + offset, src + buf->offset, this_len); |
664 | flush_dcache_page(page); | 668 | flush_dcache_page(page); |
665 | kunmap_atomic(dst, KM_USER1); | 669 | kunmap_atomic(dst, KM_USER1); |
666 | buf->ops->unmap(info, buf, src); | 670 | buf->ops->unmap(pipe, buf, src); |
667 | } | 671 | } |
668 | 672 | ||
669 | ret = mapping->a_ops->commit_write(file, page, offset, offset+this_len); | 673 | ret = mapping->a_ops->commit_write(file, page, offset, offset+this_len); |
diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index ba73108cbf8b..ea4f7cd7bfd8 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h | |||
@@ -5,8 +5,9 @@ | |||
5 | 5 | ||
6 | #define PIPE_BUFFERS (16) | 6 | #define PIPE_BUFFERS (16) |
7 | 7 | ||
8 | #define PIPE_BUF_FLAG_ATOMIC 0x01 /* was atomically mapped */ | 8 | #define PIPE_BUF_FLAG_LRU 0x01 /* page is on the LRU */ |
9 | #define PIPE_BUF_FLAG_GIFT 0x02 /* page is a gift */ | 9 | #define PIPE_BUF_FLAG_ATOMIC 0x02 /* was atomically mapped */ |
10 | #define PIPE_BUF_FLAG_GIFT 0x04 /* page is a gift */ | ||
10 | 11 | ||
11 | struct pipe_buffer { | 12 | struct pipe_buffer { |
12 | struct page *page; | 13 | struct page *page; |