diff options
Diffstat (limited to 'fs/splice.c')
-rw-r--r-- | fs/splice.c | 74 |
1 files changed, 63 insertions, 11 deletions
diff --git a/fs/splice.c b/fs/splice.c index c47b561edac0..e30743c2c06a 100644 --- a/fs/splice.c +++ b/fs/splice.c | |||
@@ -240,7 +240,7 @@ __generic_file_splice_read(struct file *in, struct pipe_inode_info *pipe, | |||
240 | struct page *pages[PIPE_BUFFERS]; | 240 | struct page *pages[PIPE_BUFFERS]; |
241 | struct page *page; | 241 | struct page *page; |
242 | pgoff_t index; | 242 | pgoff_t index; |
243 | int i; | 243 | int i, error; |
244 | 244 | ||
245 | index = in->f_pos >> PAGE_CACHE_SHIFT; | 245 | index = in->f_pos >> PAGE_CACHE_SHIFT; |
246 | offset = in->f_pos & ~PAGE_CACHE_MASK; | 246 | offset = in->f_pos & ~PAGE_CACHE_MASK; |
@@ -260,32 +260,84 @@ __generic_file_splice_read(struct file *in, struct pipe_inode_info *pipe, | |||
260 | /* | 260 | /* |
261 | * now fill in the holes | 261 | * now fill in the holes |
262 | */ | 262 | */ |
263 | error = 0; | ||
263 | for (i = 0; i < nr_pages; i++, index++) { | 264 | for (i = 0; i < nr_pages; i++, index++) { |
265 | find_page: | ||
264 | /* | 266 | /* |
265 | * no page there, look one up / create it | 267 | * lookup the page for this index |
266 | */ | 268 | */ |
267 | page = find_or_create_page(mapping, index, | 269 | page = find_get_page(mapping, index); |
268 | mapping_gfp_mask(mapping)); | 270 | if (!page) { |
269 | if (!page) | 271 | /* |
270 | break; | 272 | * If in nonblock mode then dont block on |
273 | * readpage (we've kicked readahead so there | ||
274 | * will be asynchronous progress): | ||
275 | */ | ||
276 | if (flags & SPLICE_F_NONBLOCK) | ||
277 | break; | ||
278 | |||
279 | /* | ||
280 | * page didn't exist, allocate one | ||
281 | */ | ||
282 | page = page_cache_alloc_cold(mapping); | ||
283 | if (!page) | ||
284 | break; | ||
285 | |||
286 | error = add_to_page_cache_lru(page, mapping, index, | ||
287 | mapping_gfp_mask(mapping)); | ||
288 | if (unlikely(error)) { | ||
289 | page_cache_release(page); | ||
290 | break; | ||
291 | } | ||
271 | 292 | ||
272 | if (PageUptodate(page)) | 293 | goto readpage; |
273 | unlock_page(page); | 294 | } |
274 | else { | 295 | |
275 | int error = mapping->a_ops->readpage(in, page); | 296 | /* |
297 | * If the page isn't uptodate, we may need to start io on it | ||
298 | */ | ||
299 | if (!PageUptodate(page)) { | ||
300 | lock_page(page); | ||
301 | |||
302 | /* | ||
303 | * page was truncated, stop here. if this isn't the | ||
304 | * first page, we'll just complete what we already | ||
305 | * added | ||
306 | */ | ||
307 | if (!page->mapping) { | ||
308 | unlock_page(page); | ||
309 | page_cache_release(page); | ||
310 | break; | ||
311 | } | ||
312 | /* | ||
313 | * page was already under io and is now done, great | ||
314 | */ | ||
315 | if (PageUptodate(page)) { | ||
316 | unlock_page(page); | ||
317 | goto fill_it; | ||
318 | } | ||
319 | |||
320 | readpage: | ||
321 | /* | ||
322 | * need to read in the page | ||
323 | */ | ||
324 | error = mapping->a_ops->readpage(in, page); | ||
276 | 325 | ||
277 | if (unlikely(error)) { | 326 | if (unlikely(error)) { |
278 | page_cache_release(page); | 327 | page_cache_release(page); |
328 | if (error == AOP_TRUNCATED_PAGE) | ||
329 | goto find_page; | ||
279 | break; | 330 | break; |
280 | } | 331 | } |
281 | } | 332 | } |
333 | fill_it: | ||
282 | pages[i] = page; | 334 | pages[i] = page; |
283 | } | 335 | } |
284 | 336 | ||
285 | if (i) | 337 | if (i) |
286 | return move_to_pipe(pipe, pages, i, offset, len, flags); | 338 | return move_to_pipe(pipe, pages, i, offset, len, flags); |
287 | 339 | ||
288 | return 0; | 340 | return error; |
289 | } | 341 | } |
290 | 342 | ||
291 | /** | 343 | /** |