diff options
Diffstat (limited to 'fs/pipe.c')
-rw-r--r-- | fs/pipe.c | 189 |
1 files changed, 151 insertions, 38 deletions
@@ -55,7 +55,8 @@ void pipe_wait(struct pipe_inode_info *pipe) | |||
55 | } | 55 | } |
56 | 56 | ||
57 | static int | 57 | static int |
58 | pipe_iov_copy_from_user(void *to, struct iovec *iov, unsigned long len) | 58 | pipe_iov_copy_from_user(void *to, struct iovec *iov, unsigned long len, |
59 | int atomic) | ||
59 | { | 60 | { |
60 | unsigned long copy; | 61 | unsigned long copy; |
61 | 62 | ||
@@ -64,8 +65,13 @@ pipe_iov_copy_from_user(void *to, struct iovec *iov, unsigned long len) | |||
64 | iov++; | 65 | iov++; |
65 | copy = min_t(unsigned long, len, iov->iov_len); | 66 | copy = min_t(unsigned long, len, iov->iov_len); |
66 | 67 | ||
67 | if (copy_from_user(to, iov->iov_base, copy)) | 68 | if (atomic) { |
68 | return -EFAULT; | 69 | if (__copy_from_user_inatomic(to, iov->iov_base, copy)) |
70 | return -EFAULT; | ||
71 | } else { | ||
72 | if (copy_from_user(to, iov->iov_base, copy)) | ||
73 | return -EFAULT; | ||
74 | } | ||
69 | to += copy; | 75 | to += copy; |
70 | len -= copy; | 76 | len -= copy; |
71 | iov->iov_base += copy; | 77 | iov->iov_base += copy; |
@@ -75,7 +81,8 @@ pipe_iov_copy_from_user(void *to, struct iovec *iov, unsigned long len) | |||
75 | } | 81 | } |
76 | 82 | ||
77 | static int | 83 | static int |
78 | pipe_iov_copy_to_user(struct iovec *iov, const void *from, unsigned long len) | 84 | pipe_iov_copy_to_user(struct iovec *iov, const void *from, unsigned long len, |
85 | int atomic) | ||
79 | { | 86 | { |
80 | unsigned long copy; | 87 | unsigned long copy; |
81 | 88 | ||
@@ -84,8 +91,13 @@ pipe_iov_copy_to_user(struct iovec *iov, const void *from, unsigned long len) | |||
84 | iov++; | 91 | iov++; |
85 | copy = min_t(unsigned long, len, iov->iov_len); | 92 | copy = min_t(unsigned long, len, iov->iov_len); |
86 | 93 | ||
87 | if (copy_to_user(iov->iov_base, from, copy)) | 94 | if (atomic) { |
88 | return -EFAULT; | 95 | if (__copy_to_user_inatomic(iov->iov_base, from, copy)) |
96 | return -EFAULT; | ||
97 | } else { | ||
98 | if (copy_to_user(iov->iov_base, from, copy)) | ||
99 | return -EFAULT; | ||
100 | } | ||
89 | from += copy; | 101 | from += copy; |
90 | len -= copy; | 102 | len -= copy; |
91 | iov->iov_base += copy; | 103 | iov->iov_base += copy; |
@@ -94,13 +106,52 @@ pipe_iov_copy_to_user(struct iovec *iov, const void *from, unsigned long len) | |||
94 | return 0; | 106 | return 0; |
95 | } | 107 | } |
96 | 108 | ||
109 | /* | ||
110 | * Attempt to pre-fault in the user memory, so we can use atomic copies. | ||
111 | * Returns the number of bytes not faulted in. | ||
112 | */ | ||
113 | static int iov_fault_in_pages_write(struct iovec *iov, unsigned long len) | ||
114 | { | ||
115 | while (!iov->iov_len) | ||
116 | iov++; | ||
117 | |||
118 | while (len > 0) { | ||
119 | unsigned long this_len; | ||
120 | |||
121 | this_len = min_t(unsigned long, len, iov->iov_len); | ||
122 | if (fault_in_pages_writeable(iov->iov_base, this_len)) | ||
123 | break; | ||
124 | |||
125 | len -= this_len; | ||
126 | iov++; | ||
127 | } | ||
128 | |||
129 | return len; | ||
130 | } | ||
131 | |||
132 | /* | ||
133 | * Pre-fault in the user memory, so we can use atomic copies. | ||
134 | */ | ||
135 | static void iov_fault_in_pages_read(struct iovec *iov, unsigned long len) | ||
136 | { | ||
137 | while (!iov->iov_len) | ||
138 | iov++; | ||
139 | |||
140 | while (len > 0) { | ||
141 | unsigned long this_len; | ||
142 | |||
143 | this_len = min_t(unsigned long, len, iov->iov_len); | ||
144 | fault_in_pages_readable(iov->iov_base, this_len); | ||
145 | len -= this_len; | ||
146 | iov++; | ||
147 | } | ||
148 | } | ||
149 | |||
97 | static void anon_pipe_buf_release(struct pipe_inode_info *pipe, | 150 | static void anon_pipe_buf_release(struct pipe_inode_info *pipe, |
98 | struct pipe_buffer *buf) | 151 | struct pipe_buffer *buf) |
99 | { | 152 | { |
100 | struct page *page = buf->page; | 153 | struct page *page = buf->page; |
101 | 154 | ||
102 | buf->flags &= ~PIPE_BUF_FLAG_STOLEN; | ||
103 | |||
104 | /* | 155 | /* |
105 | * If nobody else uses this page, and we don't already have a | 156 | * If nobody else uses this page, and we don't already have a |
106 | * temporary page, let's keep track of it as a one-deep | 157 | * temporary page, let's keep track of it as a one-deep |
@@ -112,31 +163,58 @@ static void anon_pipe_buf_release(struct pipe_inode_info *pipe, | |||
112 | page_cache_release(page); | 163 | page_cache_release(page); |
113 | } | 164 | } |
114 | 165 | ||
115 | static void * anon_pipe_buf_map(struct file *file, struct pipe_inode_info *pipe, | 166 | void *generic_pipe_buf_map(struct pipe_inode_info *pipe, |
116 | struct pipe_buffer *buf) | 167 | struct pipe_buffer *buf, int atomic) |
117 | { | 168 | { |
169 | if (atomic) { | ||
170 | buf->flags |= PIPE_BUF_FLAG_ATOMIC; | ||
171 | return kmap_atomic(buf->page, KM_USER0); | ||
172 | } | ||
173 | |||
118 | return kmap(buf->page); | 174 | return kmap(buf->page); |
119 | } | 175 | } |
120 | 176 | ||
121 | static void anon_pipe_buf_unmap(struct pipe_inode_info *pipe, | 177 | void generic_pipe_buf_unmap(struct pipe_inode_info *pipe, |
122 | struct pipe_buffer *buf) | 178 | struct pipe_buffer *buf, void *map_data) |
179 | { | ||
180 | if (buf->flags & PIPE_BUF_FLAG_ATOMIC) { | ||
181 | buf->flags &= ~PIPE_BUF_FLAG_ATOMIC; | ||
182 | kunmap_atomic(map_data, KM_USER0); | ||
183 | } else | ||
184 | kunmap(buf->page); | ||
185 | } | ||
186 | |||
187 | int generic_pipe_buf_steal(struct pipe_inode_info *pipe, | ||
188 | struct pipe_buffer *buf) | ||
189 | { | ||
190 | struct page *page = buf->page; | ||
191 | |||
192 | if (page_count(page) == 1) { | ||
193 | lock_page(page); | ||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | return 1; | ||
198 | } | ||
199 | |||
200 | void generic_pipe_buf_get(struct pipe_inode_info *info, struct pipe_buffer *buf) | ||
123 | { | 201 | { |
124 | kunmap(buf->page); | 202 | page_cache_get(buf->page); |
125 | } | 203 | } |
126 | 204 | ||
127 | static int anon_pipe_buf_steal(struct pipe_inode_info *pipe, | 205 | int generic_pipe_buf_pin(struct pipe_inode_info *info, struct pipe_buffer *buf) |
128 | struct pipe_buffer *buf) | ||
129 | { | 206 | { |
130 | buf->flags |= PIPE_BUF_FLAG_STOLEN; | ||
131 | return 0; | 207 | return 0; |
132 | } | 208 | } |
133 | 209 | ||
134 | static struct pipe_buf_operations anon_pipe_buf_ops = { | 210 | static struct pipe_buf_operations anon_pipe_buf_ops = { |
135 | .can_merge = 1, | 211 | .can_merge = 1, |
136 | .map = anon_pipe_buf_map, | 212 | .map = generic_pipe_buf_map, |
137 | .unmap = anon_pipe_buf_unmap, | 213 | .unmap = generic_pipe_buf_unmap, |
214 | .pin = generic_pipe_buf_pin, | ||
138 | .release = anon_pipe_buf_release, | 215 | .release = anon_pipe_buf_release, |
139 | .steal = anon_pipe_buf_steal, | 216 | .steal = generic_pipe_buf_steal, |
217 | .get = generic_pipe_buf_get, | ||
140 | }; | 218 | }; |
141 | 219 | ||
142 | static ssize_t | 220 | static ssize_t |
@@ -167,22 +245,33 @@ pipe_readv(struct file *filp, const struct iovec *_iov, | |||
167 | struct pipe_buf_operations *ops = buf->ops; | 245 | struct pipe_buf_operations *ops = buf->ops; |
168 | void *addr; | 246 | void *addr; |
169 | size_t chars = buf->len; | 247 | size_t chars = buf->len; |
170 | int error; | 248 | int error, atomic; |
171 | 249 | ||
172 | if (chars > total_len) | 250 | if (chars > total_len) |
173 | chars = total_len; | 251 | chars = total_len; |
174 | 252 | ||
175 | addr = ops->map(filp, pipe, buf); | 253 | error = ops->pin(pipe, buf); |
176 | if (IS_ERR(addr)) { | 254 | if (error) { |
177 | if (!ret) | 255 | if (!ret) |
178 | ret = PTR_ERR(addr); | 256 | error = ret; |
179 | break; | 257 | break; |
180 | } | 258 | } |
181 | error = pipe_iov_copy_to_user(iov, addr + buf->offset, chars); | 259 | |
182 | ops->unmap(pipe, buf); | 260 | atomic = !iov_fault_in_pages_write(iov, chars); |
261 | redo: | ||
262 | addr = ops->map(pipe, buf, atomic); | ||
263 | error = pipe_iov_copy_to_user(iov, addr + buf->offset, chars, atomic); | ||
264 | ops->unmap(pipe, buf, addr); | ||
183 | if (unlikely(error)) { | 265 | if (unlikely(error)) { |
266 | /* | ||
267 | * Just retry with the slow path if we failed. | ||
268 | */ | ||
269 | if (atomic) { | ||
270 | atomic = 0; | ||
271 | goto redo; | ||
272 | } | ||
184 | if (!ret) | 273 | if (!ret) |
185 | ret = -EFAULT; | 274 | ret = error; |
186 | break; | 275 | break; |
187 | } | 276 | } |
188 | ret += chars; | 277 | ret += chars; |
@@ -286,21 +375,28 @@ pipe_writev(struct file *filp, const struct iovec *_iov, | |||
286 | int offset = buf->offset + buf->len; | 375 | int offset = buf->offset + buf->len; |
287 | 376 | ||
288 | if (ops->can_merge && offset + chars <= PAGE_SIZE) { | 377 | if (ops->can_merge && offset + chars <= PAGE_SIZE) { |
378 | int error, atomic = 1; | ||
289 | void *addr; | 379 | void *addr; |
290 | int error; | ||
291 | 380 | ||
292 | addr = ops->map(filp, pipe, buf); | 381 | error = ops->pin(pipe, buf); |
293 | if (IS_ERR(addr)) { | 382 | if (error) |
294 | error = PTR_ERR(addr); | ||
295 | goto out; | 383 | goto out; |
296 | } | 384 | |
385 | iov_fault_in_pages_read(iov, chars); | ||
386 | redo1: | ||
387 | addr = ops->map(pipe, buf, atomic); | ||
297 | error = pipe_iov_copy_from_user(offset + addr, iov, | 388 | error = pipe_iov_copy_from_user(offset + addr, iov, |
298 | chars); | 389 | chars, atomic); |
299 | ops->unmap(pipe, buf); | 390 | ops->unmap(pipe, buf, addr); |
300 | ret = error; | 391 | ret = error; |
301 | do_wakeup = 1; | 392 | do_wakeup = 1; |
302 | if (error) | 393 | if (error) { |
394 | if (atomic) { | ||
395 | atomic = 0; | ||
396 | goto redo1; | ||
397 | } | ||
303 | goto out; | 398 | goto out; |
399 | } | ||
304 | buf->len += chars; | 400 | buf->len += chars; |
305 | total_len -= chars; | 401 | total_len -= chars; |
306 | ret = chars; | 402 | ret = chars; |
@@ -323,7 +419,8 @@ pipe_writev(struct file *filp, const struct iovec *_iov, | |||
323 | int newbuf = (pipe->curbuf + bufs) & (PIPE_BUFFERS-1); | 419 | int newbuf = (pipe->curbuf + bufs) & (PIPE_BUFFERS-1); |
324 | struct pipe_buffer *buf = pipe->bufs + newbuf; | 420 | struct pipe_buffer *buf = pipe->bufs + newbuf; |
325 | struct page *page = pipe->tmp_page; | 421 | struct page *page = pipe->tmp_page; |
326 | int error; | 422 | char *src; |
423 | int error, atomic = 1; | ||
327 | 424 | ||
328 | if (!page) { | 425 | if (!page) { |
329 | page = alloc_page(GFP_HIGHUSER); | 426 | page = alloc_page(GFP_HIGHUSER); |
@@ -343,11 +440,27 @@ pipe_writev(struct file *filp, const struct iovec *_iov, | |||
343 | if (chars > total_len) | 440 | if (chars > total_len) |
344 | chars = total_len; | 441 | chars = total_len; |
345 | 442 | ||
346 | error = pipe_iov_copy_from_user(kmap(page), iov, chars); | 443 | iov_fault_in_pages_read(iov, chars); |
347 | kunmap(page); | 444 | redo2: |
445 | if (atomic) | ||
446 | src = kmap_atomic(page, KM_USER0); | ||
447 | else | ||
448 | src = kmap(page); | ||
449 | |||
450 | error = pipe_iov_copy_from_user(src, iov, chars, | ||
451 | atomic); | ||
452 | if (atomic) | ||
453 | kunmap_atomic(src, KM_USER0); | ||
454 | else | ||
455 | kunmap(page); | ||
456 | |||
348 | if (unlikely(error)) { | 457 | if (unlikely(error)) { |
458 | if (atomic) { | ||
459 | atomic = 0; | ||
460 | goto redo2; | ||
461 | } | ||
349 | if (!ret) | 462 | if (!ret) |
350 | ret = -EFAULT; | 463 | ret = error; |
351 | break; | 464 | break; |
352 | } | 465 | } |
353 | ret += chars; | 466 | ret += chars; |