* addition of remove_mapping(). If success is returned, the caller may
* attempt to reuse this page for another destination.
*/
-static int page_cache_pipe_buf_steal(struct pipe_inode_info *info,
+static int page_cache_pipe_buf_steal(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
{
struct page *page = buf->page;
- struct address_space *mapping = page_mapping(page);
+ struct address_space *mapping;
lock_page(page);
- WARN_ON(!PageUptodate(page));
+ mapping = page_mapping(page);
+ if (mapping) {
+ WARN_ON(!PageUptodate(page));
- /*
- * At least for ext2 with nobh option, we need to wait on writeback
- * completing on this page, since we'll remove it from the pagecache.
- * Otherwise truncate wont wait on the page, allowing the disk
- * blocks to be reused by someone else before we actually wrote our
- * data to them. fs corruption ensues.
- */
- wait_on_page_writeback(page);
+ /*
+ * At least for ext2 with nobh option, we need to wait on
+ * writeback completing on this page, since we'll remove it
+ * from the pagecache. Otherwise truncate wont wait on the
+ * page, allowing the disk blocks to be reused by someone else
+ * before we actually wrote our data to them. fs corruption
+ * ensues.
+ */
+ wait_on_page_writeback(page);
- if (PagePrivate(page))
- try_to_release_page(page, mapping_gfp_mask(mapping));
+ if (PagePrivate(page))
+ try_to_release_page(page, mapping_gfp_mask(mapping));
- if (!remove_mapping(mapping, page)) {
- unlock_page(page);
- return 1;
+ /*
+ * If we succeeded in removing the mapping, set LRU flag
+ * and return good.
+ */
+ if (remove_mapping(mapping, page)) {
+ buf->flags |= PIPE_BUF_FLAG_LRU;
+ return 0;
+ }
}
- buf->flags |= PIPE_BUF_FLAG_LRU;
- return 0;
+ /*
+ * Raced with truncate or failed to remove page from current
+ * address space, unlock and return failure.
+ */
+ unlock_page(page);
+ return 1;
}
-static void page_cache_pipe_buf_release(struct pipe_inode_info *info,
+static void page_cache_pipe_buf_release(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
{
page_cache_release(buf->page);
buf->flags &= ~PIPE_BUF_FLAG_LRU;
}
-static int page_cache_pipe_buf_pin(struct pipe_inode_info *info,
+static int page_cache_pipe_buf_pin(struct pipe_inode_info *pipe,
struct pipe_buffer *buf)
{
struct page *page = buf->page;
mapping_gfp_mask(mapping));
if (unlikely(error)) {
page_cache_release(page);
+ if (error == -EEXIST)
+ continue;
break;
}
/*
* Send 'sd->len' bytes to socket from 'sd->file' at position 'sd->pos'
* using sendpage(). Return the number of bytes sent.
*/
-static int pipe_to_sendpage(struct pipe_inode_info *info,
+static int pipe_to_sendpage(struct pipe_inode_info *pipe,
struct pipe_buffer *buf, struct splice_desc *sd)
{
struct file *file = sd->file;
loff_t pos = sd->pos;
int ret, more;
- ret = buf->ops->pin(info, buf);
+ ret = buf->ops->pin(pipe, buf);
if (!ret) {
more = (sd->flags & SPLICE_F_MORE) || sd->len < sd->total_len;
* SPLICE_F_MOVE isn't set, or we cannot move the page, we simply create
* a new page in the output file page cache and fill/dirty that.
*/
-static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf,
+static int pipe_to_file(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
struct splice_desc *sd)
{
struct file *file = sd->file;
/*
* make sure the data in this buffer is uptodate
*/
- ret = buf->ops->pin(info, buf);
+ ret = buf->ops->pin(pipe, buf);
if (unlikely(ret))
return ret;
* pagecache and we can reuse it. The page will also be
* locked on successful return.
*/
- if (buf->ops->steal(info, buf))
+ if (buf->ops->steal(pipe, buf))
goto find_page;
page = buf->page;
/*
* Careful, ->map() uses KM_USER0!
*/
- char *src = buf->ops->map(info, buf, 1);
+ char *src = buf->ops->map(pipe, buf, 1);
char *dst = kmap_atomic(page, KM_USER1);
memcpy(dst + offset, src + buf->offset, this_len);
flush_dcache_page(page);
kunmap_atomic(dst, KM_USER1);
- buf->ops->unmap(info, buf, src);
+ buf->ops->unmap(pipe, buf, src);
}
ret = mapping->a_ops->commit_write(file, page, offset, offset+this_len);