diff options
Diffstat (limited to 'fs/pipe.c')
| -rw-r--r-- | fs/pipe.c | 41 |
1 files changed, 36 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) |
| @@ -111,11 +121,19 @@ static void anon_pipe_buf_unmap(struct pipe_inode_info *info, struct pipe_buffer | |||
| 111 | kunmap(buf->page); | 121 | kunmap(buf->page); |
| 112 | } | 122 | } |
| 113 | 123 | ||
| 124 | static int anon_pipe_buf_steal(struct pipe_inode_info *info, | ||
| 125 | struct pipe_buffer *buf) | ||
| 126 | { | ||
| 127 | buf->stolen = 1; | ||
| 128 | return 0; | ||
| 129 | } | ||
| 130 | |||
| 114 | static struct pipe_buf_operations anon_pipe_buf_ops = { | 131 | static struct pipe_buf_operations anon_pipe_buf_ops = { |
| 115 | .can_merge = 1, | 132 | .can_merge = 1, |
| 116 | .map = anon_pipe_buf_map, | 133 | .map = anon_pipe_buf_map, |
| 117 | .unmap = anon_pipe_buf_unmap, | 134 | .unmap = anon_pipe_buf_unmap, |
| 118 | .release = anon_pipe_buf_release, | 135 | .release = anon_pipe_buf_release, |
| 136 | .steal = anon_pipe_buf_steal, | ||
| 119 | }; | 137 | }; |
| 120 | 138 | ||
| 121 | static ssize_t | 139 | static ssize_t |
| @@ -152,6 +170,11 @@ pipe_readv(struct file *filp, const struct iovec *_iov, | |||
| 152 | chars = total_len; | 170 | chars = total_len; |
| 153 | 171 | ||
| 154 | addr = ops->map(filp, info, buf); | 172 | addr = ops->map(filp, info, buf); |
| 173 | if (IS_ERR(addr)) { | ||
| 174 | if (!ret) | ||
| 175 | ret = PTR_ERR(addr); | ||
| 176 | break; | ||
| 177 | } | ||
| 155 | error = pipe_iov_copy_to_user(iov, addr + buf->offset, chars); | 178 | error = pipe_iov_copy_to_user(iov, addr + buf->offset, chars); |
| 156 | ops->unmap(info, buf); | 179 | ops->unmap(info, buf); |
| 157 | if (unlikely(error)) { | 180 | if (unlikely(error)) { |
| @@ -254,8 +277,16 @@ pipe_writev(struct file *filp, const struct iovec *_iov, | |||
| 254 | struct pipe_buf_operations *ops = buf->ops; | 277 | struct pipe_buf_operations *ops = buf->ops; |
| 255 | int offset = buf->offset + buf->len; | 278 | int offset = buf->offset + buf->len; |
| 256 | if (ops->can_merge && offset + chars <= PAGE_SIZE) { | 279 | if (ops->can_merge && offset + chars <= PAGE_SIZE) { |
| 257 | void *addr = ops->map(filp, info, buf); | 280 | void *addr; |
| 258 | int error = pipe_iov_copy_from_user(offset + addr, iov, chars); | 281 | int error; |
| 282 | |||
| 283 | addr = ops->map(filp, info, buf); | ||
| 284 | if (IS_ERR(addr)) { | ||
| 285 | error = PTR_ERR(addr); | ||
| 286 | goto out; | ||
| 287 | } | ||
| 288 | error = pipe_iov_copy_from_user(offset + addr, iov, | ||
| 289 | chars); | ||
| 259 | ops->unmap(info, buf); | 290 | ops->unmap(info, buf); |
| 260 | ret = error; | 291 | ret = error; |
| 261 | do_wakeup = 1; | 292 | do_wakeup = 1; |
