aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJens Axboe <axboe@suse.de>2006-05-01 14:02:33 -0400
committerJens Axboe <axboe@suse.de>2006-05-01 14:02:33 -0400
commit7afa6fd037e51e95d322990cb127bb2b1217251a (patch)
treedbe4052947bff32a6ada378576b8d7669c581cf4
parentf6762b7ad8edd6abc802542ce845d3bc8adcb92f (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.c28
-rw-r--r--include/linux/pipe_fs_i.h2
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 = {
141static int user_page_pipe_buf_steal(struct pipe_inode_info *pipe, 141static 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
147static struct pipe_buf_operations user_page_pipe_buf_ops = { 150static 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 */
1074static int get_iovec_page_array(const struct iovec __user *iov, 1080static 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
11struct pipe_buffer { 12struct 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