diff options
Diffstat (limited to 'fs/pipe.c')
-rw-r--r-- | fs/pipe.c | 33 |
1 files changed, 28 insertions, 5 deletions
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/pipe_fs_i.h> | 15 | #include <linux/pipe_fs_i.h> |
16 | #include <linux/uio.h> | 16 | #include <linux/uio.h> |
17 | #include <linux/highmem.h> | 17 | #include <linux/highmem.h> |
18 | #include <linux/pagemap.h> | ||
18 | 19 | ||
19 | #include <asm/uaccess.h> | 20 | #include <asm/uaccess.h> |
20 | #include <asm/ioctls.h> | 21 | #include <asm/ioctls.h> |
@@ -94,11 +95,20 @@ static void anon_pipe_buf_release(struct pipe_inode_info *info, struct pipe_buff | |||
94 | { | 95 | { |
95 | struct page *page = buf->page; | 96 | struct page *page = buf->page; |
96 | 97 | ||
97 | if (info->tmp_page) { | 98 | /* |
98 | __free_page(page); | 99 | * If nobody else uses this page, and we don't already have a |
100 | * temporary page, let's keep track of it as a one-deep | ||
101 | * allocation cache | ||
102 | */ | ||
103 | if (page_count(page) == 1 && !info->tmp_page) { | ||
104 | info->tmp_page = page; | ||
99 | return; | 105 | return; |
100 | } | 106 | } |
101 | info->tmp_page = page; | 107 | |
108 | /* | ||
109 | * Otherwise just release our reference to it | ||
110 | */ | ||
111 | page_cache_release(page); | ||
102 | } | 112 | } |
103 | 113 | ||
104 | static void *anon_pipe_buf_map(struct file *file, struct pipe_inode_info *info, struct pipe_buffer *buf) | 114 | static void *anon_pipe_buf_map(struct file *file, struct pipe_inode_info *info, struct pipe_buffer *buf) |
@@ -152,6 +162,11 @@ pipe_readv(struct file *filp, const struct iovec *_iov, | |||
152 | chars = total_len; | 162 | chars = total_len; |
153 | 163 | ||
154 | addr = ops->map(filp, info, buf); | 164 | addr = ops->map(filp, info, buf); |
165 | if (IS_ERR(addr)) { | ||
166 | if (!ret) | ||
167 | ret = PTR_ERR(addr); | ||
168 | break; | ||
169 | } | ||
155 | error = pipe_iov_copy_to_user(iov, addr + buf->offset, chars); | 170 | error = pipe_iov_copy_to_user(iov, addr + buf->offset, chars); |
156 | ops->unmap(info, buf); | 171 | ops->unmap(info, buf); |
157 | if (unlikely(error)) { | 172 | if (unlikely(error)) { |
@@ -254,8 +269,16 @@ pipe_writev(struct file *filp, const struct iovec *_iov, | |||
254 | struct pipe_buf_operations *ops = buf->ops; | 269 | struct pipe_buf_operations *ops = buf->ops; |
255 | int offset = buf->offset + buf->len; | 270 | int offset = buf->offset + buf->len; |
256 | if (ops->can_merge && offset + chars <= PAGE_SIZE) { | 271 | if (ops->can_merge && offset + chars <= PAGE_SIZE) { |
257 | void *addr = ops->map(filp, info, buf); | 272 | void *addr; |
258 | int error = pipe_iov_copy_from_user(offset + addr, iov, chars); | 273 | int error; |
274 | |||
275 | addr = ops->map(filp, info, buf); | ||
276 | if (IS_ERR(addr)) { | ||
277 | error = PTR_ERR(addr); | ||
278 | goto out; | ||
279 | } | ||
280 | error = pipe_iov_copy_from_user(offset + addr, iov, | ||
281 | chars); | ||
259 | ops->unmap(info, buf); | 282 | ops->unmap(info, buf); |
260 | ret = error; | 283 | ret = error; |
261 | do_wakeup = 1; | 284 | do_wakeup = 1; |