aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJens Axboe <axboe@suse.de>2006-03-30 08:16:46 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-30 15:28:18 -0500
commit5abc97aa25b2c41413b3a520faee83f2282d9f18 (patch)
tree4ba13ae0e91f15d02986df7cdca5e9455212d7d4
parent5274f052e7b3dbd81935772eb551dfd0325dfa9d (diff)
[PATCH] splice: add support for SPLICE_F_MOVE flag
This enables the caller to migrate pages from one address space page cache to another. In buzz word marketing, you can do zero-copy file copies! Signed-off-by: Jens Axboe <axboe@suse.de> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--fs/pipe.c8
-rw-r--r--fs/splice.c121
-rw-r--r--include/linux/pipe_fs_i.h8
3 files changed, 100 insertions, 37 deletions
diff --git a/fs/pipe.c b/fs/pipe.c
index 2414bf270db6..109a102c150d 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -121,11 +121,19 @@ static void anon_pipe_buf_unmap(struct pipe_inode_info *info, struct pipe_buffer
121 kunmap(buf->page); 121 kunmap(buf->page);
122} 122}
123 123
124static 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
124static struct pipe_buf_operations anon_pipe_buf_ops = { 131static struct pipe_buf_operations anon_pipe_buf_ops = {
125 .can_merge = 1, 132 .can_merge = 1,
126 .map = anon_pipe_buf_map, 133 .map = anon_pipe_buf_map,
127 .unmap = anon_pipe_buf_unmap, 134 .unmap = anon_pipe_buf_unmap,
128 .release = anon_pipe_buf_release, 135 .release = anon_pipe_buf_release,
136 .steal = anon_pipe_buf_steal,
129}; 137};
130 138
131static ssize_t 139static ssize_t
diff --git a/fs/splice.c b/fs/splice.c
index efa47c1c4e13..4a026f95884f 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -21,6 +21,7 @@
21#include <linux/pagemap.h> 21#include <linux/pagemap.h>
22#include <linux/pipe_fs_i.h> 22#include <linux/pipe_fs_i.h>
23#include <linux/mm_inline.h> 23#include <linux/mm_inline.h>
24#include <linux/swap.h>
24 25
25/* 26/*
26 * Passed to the actors 27 * Passed to the actors
@@ -32,11 +33,37 @@ struct splice_desc {
32 loff_t pos; /* file position */ 33 loff_t pos; /* file position */
33}; 34};
34 35
36static int page_cache_pipe_buf_steal(struct pipe_inode_info *info,
37 struct pipe_buffer *buf)
38{
39 struct page *page = buf->page;
40
41 WARN_ON(!PageLocked(page));
42 WARN_ON(!PageUptodate(page));
43
44 if (!remove_mapping(page_mapping(page), page))
45 return 1;
46
47 if (PageLRU(page)) {
48 struct zone *zone = page_zone(page);
49
50 spin_lock_irq(&zone->lru_lock);
51 BUG_ON(!PageLRU(page));
52 __ClearPageLRU(page);
53 del_page_from_lru(zone, page);
54 spin_unlock_irq(&zone->lru_lock);
55 }
56
57 buf->stolen = 1;
58 return 0;
59}
60
35static void page_cache_pipe_buf_release(struct pipe_inode_info *info, 61static void page_cache_pipe_buf_release(struct pipe_inode_info *info,
36 struct pipe_buffer *buf) 62 struct pipe_buffer *buf)
37{ 63{
38 page_cache_release(buf->page); 64 page_cache_release(buf->page);
39 buf->page = NULL; 65 buf->page = NULL;
66 buf->stolen = 0;
40} 67}
41 68
42static void *page_cache_pipe_buf_map(struct file *file, 69static void *page_cache_pipe_buf_map(struct file *file,
@@ -63,7 +90,8 @@ static void *page_cache_pipe_buf_map(struct file *file,
63static void page_cache_pipe_buf_unmap(struct pipe_inode_info *info, 90static void page_cache_pipe_buf_unmap(struct pipe_inode_info *info,
64 struct pipe_buffer *buf) 91 struct pipe_buffer *buf)
65{ 92{
66 unlock_page(buf->page); 93 if (!buf->stolen)
94 unlock_page(buf->page);
67 kunmap(buf->page); 95 kunmap(buf->page);
68} 96}
69 97
@@ -72,6 +100,7 @@ static struct pipe_buf_operations page_cache_pipe_buf_ops = {
72 .map = page_cache_pipe_buf_map, 100 .map = page_cache_pipe_buf_map,
73 .unmap = page_cache_pipe_buf_unmap, 101 .unmap = page_cache_pipe_buf_unmap,
74 .release = page_cache_pipe_buf_release, 102 .release = page_cache_pipe_buf_release,
103 .steal = page_cache_pipe_buf_steal,
75}; 104};
76 105
77static ssize_t move_to_pipe(struct inode *inode, struct page **pages, 106static ssize_t move_to_pipe(struct inode *inode, struct page **pages,
@@ -336,8 +365,8 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf,
336 struct address_space *mapping = file->f_mapping; 365 struct address_space *mapping = file->f_mapping;
337 unsigned int offset; 366 unsigned int offset;
338 struct page *page; 367 struct page *page;
339 char *src, *dst;
340 pgoff_t index; 368 pgoff_t index;
369 char *src;
341 int ret; 370 int ret;
342 371
343 /* 372 /*
@@ -350,40 +379,54 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf,
350 index = sd->pos >> PAGE_CACHE_SHIFT; 379 index = sd->pos >> PAGE_CACHE_SHIFT;
351 offset = sd->pos & ~PAGE_CACHE_MASK; 380 offset = sd->pos & ~PAGE_CACHE_MASK;
352 381
353find_page:
354 ret = -ENOMEM;
355 page = find_or_create_page(mapping, index, mapping_gfp_mask(mapping));
356 if (!page)
357 goto out;
358
359 /* 382 /*
360 * If the page is uptodate, it is also locked. If it isn't 383 * reuse buf page, if SPLICE_F_MOVE is set
361 * uptodate, we can mark it uptodate if we are filling the
362 * full page. Otherwise we need to read it in first...
363 */ 384 */
364 if (!PageUptodate(page)) { 385 if (sd->flags & SPLICE_F_MOVE) {
365 if (sd->len < PAGE_CACHE_SIZE) { 386 if (buf->ops->steal(info, buf))
366 ret = mapping->a_ops->readpage(file, page); 387 goto find_page;
367 if (unlikely(ret)) 388
368 goto out; 389 page = buf->page;
369 390 if (add_to_page_cache_lru(page, mapping, index,
370 lock_page(page); 391 mapping_gfp_mask(mapping)))
371 392 goto find_page;
372 if (!PageUptodate(page)) { 393 } else {
373 /* 394find_page:
374 * page got invalidated, repeat 395 ret = -ENOMEM;
375 */ 396 page = find_or_create_page(mapping, index,
376 if (!page->mapping) { 397 mapping_gfp_mask(mapping));
377 unlock_page(page); 398 if (!page)
378 page_cache_release(page); 399 goto out;
379 goto find_page; 400
401 /*
402 * If the page is uptodate, it is also locked. If it isn't
403 * uptodate, we can mark it uptodate if we are filling the
404 * full page. Otherwise we need to read it in first...
405 */
406 if (!PageUptodate(page)) {
407 if (sd->len < PAGE_CACHE_SIZE) {
408 ret = mapping->a_ops->readpage(file, page);
409 if (unlikely(ret))
410 goto out;
411
412 lock_page(page);
413
414 if (!PageUptodate(page)) {
415 /*
416 * page got invalidated, repeat
417 */
418 if (!page->mapping) {
419 unlock_page(page);
420 page_cache_release(page);
421 goto find_page;
422 }
423 ret = -EIO;
424 goto out;
380 } 425 }
381 ret = -EIO; 426 } else {
382 goto out; 427 WARN_ON(!PageLocked(page));
428 SetPageUptodate(page);
383 } 429 }
384 } else {
385 WARN_ON(!PageLocked(page));
386 SetPageUptodate(page);
387 } 430 }
388 } 431 }
389 432
@@ -391,10 +434,13 @@ find_page:
391 if (ret) 434 if (ret)
392 goto out; 435 goto out;
393 436
394 dst = kmap_atomic(page, KM_USER0); 437 if (!buf->stolen) {
395 memcpy(dst + offset, src + buf->offset, sd->len); 438 char *dst = kmap_atomic(page, KM_USER0);
396 flush_dcache_page(page); 439
397 kunmap_atomic(dst, KM_USER0); 440 memcpy(dst + offset, src + buf->offset, sd->len);
441 flush_dcache_page(page);
442 kunmap_atomic(dst, KM_USER0);
443 }
398 444
399 ret = mapping->a_ops->commit_write(file, page, 0, sd->len); 445 ret = mapping->a_ops->commit_write(file, page, 0, sd->len);
400 if (ret < 0) 446 if (ret < 0)
@@ -405,7 +451,8 @@ find_page:
405out: 451out:
406 if (ret < 0) 452 if (ret < 0)
407 unlock_page(page); 453 unlock_page(page);
408 page_cache_release(page); 454 if (!buf->stolen)
455 page_cache_release(page);
409 buf->ops->unmap(info, buf); 456 buf->ops->unmap(info, buf);
410 return ret; 457 return ret;
411} 458}
diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h
index b12e59c75752..75c7f55023ab 100644
--- a/include/linux/pipe_fs_i.h
+++ b/include/linux/pipe_fs_i.h
@@ -9,6 +9,7 @@ struct pipe_buffer {
9 struct page *page; 9 struct page *page;
10 unsigned int offset, len; 10 unsigned int offset, len;
11 struct pipe_buf_operations *ops; 11 struct pipe_buf_operations *ops;
12 unsigned int stolen;
12}; 13};
13 14
14struct pipe_buf_operations { 15struct pipe_buf_operations {
@@ -16,6 +17,7 @@ struct pipe_buf_operations {
16 void * (*map)(struct file *, struct pipe_inode_info *, struct pipe_buffer *); 17 void * (*map)(struct file *, struct pipe_inode_info *, struct pipe_buffer *);
17 void (*unmap)(struct pipe_inode_info *, struct pipe_buffer *); 18 void (*unmap)(struct pipe_inode_info *, struct pipe_buffer *);
18 void (*release)(struct pipe_inode_info *, struct pipe_buffer *); 19 void (*release)(struct pipe_inode_info *, struct pipe_buffer *);
20 int (*steal)(struct pipe_inode_info *, struct pipe_buffer *);
19}; 21};
20 22
21struct pipe_inode_info { 23struct pipe_inode_info {
@@ -53,4 +55,10 @@ void pipe_wait(struct inode * inode);
53struct inode* pipe_new(struct inode* inode); 55struct inode* pipe_new(struct inode* inode);
54void free_pipe_info(struct inode* inode); 56void free_pipe_info(struct inode* inode);
55 57
58/*
59 * splice is tied to pipes as a transport (at least for now), so we'll just
60 * add the splice flags here.
61 */
62#define SPLICE_F_MOVE (0x01) /* move pages instead of copying */
63
56#endif 64#endif