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; |