diff options
author | Jens Axboe <axboe@suse.de> | 2006-05-01 14:02:33 -0400 |
---|---|---|
committer | Jens Axboe <axboe@suse.de> | 2006-05-01 14:02:33 -0400 |
commit | 7afa6fd037e51e95d322990cb127bb2b1217251a (patch) | |
tree | dbe4052947bff32a6ada378576b8d7669c581cf4 | |
parent | f6762b7ad8edd6abc802542ce845d3bc8adcb92f (diff) |
[PATCH] vmsplice: allow user to pass in gift pages
If SPLICE_F_GIFT is set, the user is basically giving this pages away to
the kernel. That means we can steal them for eg page cache uses instead
of copying it.
The data must be properly page aligned and also a multiple of the page size
in length.
Signed-off-by: Jens Axboe <axboe@suse.de>
-rw-r--r-- | fs/splice.c | 28 | ||||
-rw-r--r-- | include/linux/pipe_fs_i.h | 2 |
2 files changed, 27 insertions, 3 deletions
diff --git a/fs/splice.c b/fs/splice.c index d4664a297bab..b150493b6fc3 100644 --- a/fs/splice.c +++ b/fs/splice.c | |||
@@ -141,7 +141,10 @@ static struct pipe_buf_operations page_cache_pipe_buf_ops = { | |||
141 | static int user_page_pipe_buf_steal(struct pipe_inode_info *pipe, | 141 | static int user_page_pipe_buf_steal(struct pipe_inode_info *pipe, |
142 | struct pipe_buffer *buf) | 142 | struct pipe_buffer *buf) |
143 | { | 143 | { |
144 | return 1; | 144 | if (!(buf->flags & PIPE_BUF_FLAG_GIFT)) |
145 | return 1; | ||
146 | |||
147 | return 0; | ||
145 | } | 148 | } |
146 | 149 | ||
147 | static struct pipe_buf_operations user_page_pipe_buf_ops = { | 150 | static struct pipe_buf_operations user_page_pipe_buf_ops = { |
@@ -186,6 +189,9 @@ static ssize_t splice_to_pipe(struct pipe_inode_info *pipe, | |||
186 | buf->offset = spd->partial[page_nr].offset; | 189 | buf->offset = spd->partial[page_nr].offset; |
187 | buf->len = spd->partial[page_nr].len; | 190 | buf->len = spd->partial[page_nr].len; |
188 | buf->ops = spd->ops; | 191 | buf->ops = spd->ops; |
192 | if (spd->flags & SPLICE_F_GIFT) | ||
193 | buf->flags |= PIPE_BUF_FLAG_GIFT; | ||
194 | |||
189 | pipe->nrbufs++; | 195 | pipe->nrbufs++; |
190 | page_nr++; | 196 | page_nr++; |
191 | ret += buf->len; | 197 | ret += buf->len; |
@@ -1073,7 +1079,7 @@ static long do_splice(struct file *in, loff_t __user *off_in, | |||
1073 | */ | 1079 | */ |
1074 | static int get_iovec_page_array(const struct iovec __user *iov, | 1080 | static int get_iovec_page_array(const struct iovec __user *iov, |
1075 | unsigned int nr_vecs, struct page **pages, | 1081 | unsigned int nr_vecs, struct page **pages, |
1076 | struct partial_page *partial) | 1082 | struct partial_page *partial, int aligned) |
1077 | { | 1083 | { |
1078 | int buffers = 0, error = 0; | 1084 | int buffers = 0, error = 0; |
1079 | 1085 | ||
@@ -1113,6 +1119,15 @@ static int get_iovec_page_array(const struct iovec __user *iov, | |||
1113 | * in the user pages. | 1119 | * in the user pages. |
1114 | */ | 1120 | */ |
1115 | off = (unsigned long) base & ~PAGE_MASK; | 1121 | off = (unsigned long) base & ~PAGE_MASK; |
1122 | |||
1123 | /* | ||
1124 | * If asked for alignment, the offset must be zero and the | ||
1125 | * length a multiple of the PAGE_SIZE. | ||
1126 | */ | ||
1127 | error = -EINVAL; | ||
1128 | if (aligned && (off || len & ~PAGE_MASK)) | ||
1129 | break; | ||
1130 | |||
1116 | npages = (off + len + PAGE_SIZE - 1) >> PAGE_SHIFT; | 1131 | npages = (off + len + PAGE_SIZE - 1) >> PAGE_SHIFT; |
1117 | if (npages > PIPE_BUFFERS - buffers) | 1132 | if (npages > PIPE_BUFFERS - buffers) |
1118 | npages = PIPE_BUFFERS - buffers; | 1133 | npages = PIPE_BUFFERS - buffers; |
@@ -1206,7 +1221,8 @@ static long do_vmsplice(struct file *file, const struct iovec __user *iov, | |||
1206 | else if (unlikely(!nr_segs)) | 1221 | else if (unlikely(!nr_segs)) |
1207 | return 0; | 1222 | return 0; |
1208 | 1223 | ||
1209 | spd.nr_pages = get_iovec_page_array(iov, nr_segs, pages, partial); | 1224 | spd.nr_pages = get_iovec_page_array(iov, nr_segs, pages, partial, |
1225 | flags & SPLICE_F_GIFT); | ||
1210 | if (spd.nr_pages <= 0) | 1226 | if (spd.nr_pages <= 0) |
1211 | return spd.nr_pages; | 1227 | return spd.nr_pages; |
1212 | 1228 | ||
@@ -1314,6 +1330,12 @@ static int link_pipe(struct pipe_inode_info *ipipe, | |||
1314 | obuf = opipe->bufs + nbuf; | 1330 | obuf = opipe->bufs + nbuf; |
1315 | *obuf = *ibuf; | 1331 | *obuf = *ibuf; |
1316 | 1332 | ||
1333 | /* | ||
1334 | * Don't inherit the gift flag, we need to | ||
1335 | * prevent multiple steals of this page. | ||
1336 | */ | ||
1337 | obuf->flags &= ~PIPE_BUF_FLAG_GIFT; | ||
1338 | |||
1317 | if (obuf->len > len) | 1339 | if (obuf->len > len) |
1318 | obuf->len = len; | 1340 | obuf->len = len; |
1319 | 1341 | ||
diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index 4c054491e38e..df4d3fa7d3dc 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h | |||
@@ -7,6 +7,7 @@ | |||
7 | 7 | ||
8 | #define PIPE_BUF_FLAG_LRU 0x01 /* page is on the LRU */ | 8 | #define PIPE_BUF_FLAG_LRU 0x01 /* page is on the LRU */ |
9 | #define PIPE_BUF_FLAG_ATOMIC 0x02 /* was atomically mapped */ | 9 | #define PIPE_BUF_FLAG_ATOMIC 0x02 /* was atomically mapped */ |
10 | #define PIPE_BUF_FLAG_GIFT 0x04 /* page is a gift */ | ||
10 | 11 | ||
11 | struct pipe_buffer { | 12 | struct pipe_buffer { |
12 | struct page *page; | 13 | struct page *page; |
@@ -79,6 +80,7 @@ int generic_pipe_buf_pin(struct pipe_inode_info *, struct pipe_buffer *); | |||
79 | /* we may still block on the fd we splice */ | 80 | /* we may still block on the fd we splice */ |
80 | /* from/to, of course */ | 81 | /* from/to, of course */ |
81 | #define SPLICE_F_MORE (0x04) /* expect more data */ | 82 | #define SPLICE_F_MORE (0x04) /* expect more data */ |
83 | #define SPLICE_F_GIFT (0x08) /* pages passed in are a gift */ | ||
82 | 84 | ||
83 | /* | 85 | /* |
84 | * Passed to the actors | 86 | * Passed to the actors |