diff options
author | Jens Axboe <axboe@suse.de> | 2006-05-01 13:59:03 -0400 |
---|---|---|
committer | Jens Axboe <axboe@suse.de> | 2006-05-01 13:59:03 -0400 |
commit | f84d751994441292593523c7069ed147176f6cab (patch) | |
tree | a1a0c4836289df86bb62e7eae5c80c66fca1643a | |
parent | 0568b409c74f7a125d92a09a3f386785700ef688 (diff) |
[PATCH] pipe: introduce ->pin() buffer operation
The ->map() function is really expensive on highmem machines right now,
since it has to use the slower kmap() instead of kmap_atomic(). Splice
rarely needs to access the virtual address of a page, so it's a waste
of time doing it.
Introduce ->pin() to take over the responsibility of making sure the
page data is valid. ->map() is then reduced to just kmap(). That way we
can also share a most of the pipe buffer ops between pipe.c and splice.c
Signed-off-by: Jens Axboe <axboe@suse.de>
-rw-r--r-- | fs/pipe.c | 39 | ||||
-rw-r--r-- | fs/splice.c | 91 | ||||
-rw-r--r-- | include/linux/pipe_fs_i.h | 21 |
3 files changed, 73 insertions, 78 deletions
@@ -110,14 +110,14 @@ static void anon_pipe_buf_release(struct pipe_inode_info *pipe, | |||
110 | page_cache_release(page); | 110 | page_cache_release(page); |
111 | } | 111 | } |
112 | 112 | ||
113 | static void * anon_pipe_buf_map(struct file *file, struct pipe_inode_info *pipe, | 113 | void *generic_pipe_buf_map(struct pipe_inode_info *pipe, |
114 | struct pipe_buffer *buf) | 114 | struct pipe_buffer *buf) |
115 | { | 115 | { |
116 | return kmap(buf->page); | 116 | return kmap(buf->page); |
117 | } | 117 | } |
118 | 118 | ||
119 | static void anon_pipe_buf_unmap(struct pipe_inode_info *pipe, | 119 | void generic_pipe_buf_unmap(struct pipe_inode_info *pipe, |
120 | struct pipe_buffer *buf) | 120 | struct pipe_buffer *buf) |
121 | { | 121 | { |
122 | kunmap(buf->page); | 122 | kunmap(buf->page); |
123 | } | 123 | } |
@@ -135,19 +135,24 @@ static int anon_pipe_buf_steal(struct pipe_inode_info *pipe, | |||
135 | return 1; | 135 | return 1; |
136 | } | 136 | } |
137 | 137 | ||
138 | static void anon_pipe_buf_get(struct pipe_inode_info *info, | 138 | void generic_pipe_buf_get(struct pipe_inode_info *info, struct pipe_buffer *buf) |
139 | struct pipe_buffer *buf) | ||
140 | { | 139 | { |
141 | page_cache_get(buf->page); | 140 | page_cache_get(buf->page); |
142 | } | 141 | } |
143 | 142 | ||
143 | int generic_pipe_buf_pin(struct pipe_inode_info *info, struct pipe_buffer *buf) | ||
144 | { | ||
145 | return 0; | ||
146 | } | ||
147 | |||
144 | static struct pipe_buf_operations anon_pipe_buf_ops = { | 148 | static struct pipe_buf_operations anon_pipe_buf_ops = { |
145 | .can_merge = 1, | 149 | .can_merge = 1, |
146 | .map = anon_pipe_buf_map, | 150 | .map = generic_pipe_buf_map, |
147 | .unmap = anon_pipe_buf_unmap, | 151 | .unmap = generic_pipe_buf_unmap, |
152 | .pin = generic_pipe_buf_pin, | ||
148 | .release = anon_pipe_buf_release, | 153 | .release = anon_pipe_buf_release, |
149 | .steal = anon_pipe_buf_steal, | 154 | .steal = anon_pipe_buf_steal, |
150 | .get = anon_pipe_buf_get, | 155 | .get = generic_pipe_buf_get, |
151 | }; | 156 | }; |
152 | 157 | ||
153 | static ssize_t | 158 | static ssize_t |
@@ -183,12 +188,14 @@ pipe_readv(struct file *filp, const struct iovec *_iov, | |||
183 | if (chars > total_len) | 188 | if (chars > total_len) |
184 | chars = total_len; | 189 | chars = total_len; |
185 | 190 | ||
186 | addr = ops->map(filp, pipe, buf); | 191 | error = ops->pin(pipe, buf); |
187 | if (IS_ERR(addr)) { | 192 | if (error) { |
188 | if (!ret) | 193 | if (!ret) |
189 | ret = PTR_ERR(addr); | 194 | error = ret; |
190 | break; | 195 | break; |
191 | } | 196 | } |
197 | |||
198 | addr = ops->map(pipe, buf); | ||
192 | error = pipe_iov_copy_to_user(iov, addr + buf->offset, chars); | 199 | error = pipe_iov_copy_to_user(iov, addr + buf->offset, chars); |
193 | ops->unmap(pipe, buf); | 200 | ops->unmap(pipe, buf); |
194 | if (unlikely(error)) { | 201 | if (unlikely(error)) { |
@@ -300,11 +307,11 @@ pipe_writev(struct file *filp, const struct iovec *_iov, | |||
300 | void *addr; | 307 | void *addr; |
301 | int error; | 308 | int error; |
302 | 309 | ||
303 | addr = ops->map(filp, pipe, buf); | 310 | error = ops->pin(pipe, buf); |
304 | if (IS_ERR(addr)) { | 311 | if (error) |
305 | error = PTR_ERR(addr); | ||
306 | goto out; | 312 | goto out; |
307 | } | 313 | |
314 | addr = ops->map(pipe, buf); | ||
308 | error = pipe_iov_copy_from_user(offset + addr, iov, | 315 | error = pipe_iov_copy_from_user(offset + addr, iov, |
309 | chars); | 316 | chars); |
310 | ops->unmap(pipe, buf); | 317 | ops->unmap(pipe, buf); |
diff --git a/fs/splice.c b/fs/splice.c index 1633778f3652..d7538d83c367 100644 --- a/fs/splice.c +++ b/fs/splice.c | |||
@@ -90,9 +90,8 @@ static void page_cache_pipe_buf_release(struct pipe_inode_info *info, | |||
90 | buf->flags &= ~PIPE_BUF_FLAG_LRU; | 90 | buf->flags &= ~PIPE_BUF_FLAG_LRU; |
91 | } | 91 | } |
92 | 92 | ||
93 | static void *page_cache_pipe_buf_map(struct file *file, | 93 | static int page_cache_pipe_buf_pin(struct pipe_inode_info *info, |
94 | struct pipe_inode_info *info, | 94 | struct pipe_buffer *buf) |
95 | struct pipe_buffer *buf) | ||
96 | { | 95 | { |
97 | struct page *page = buf->page; | 96 | struct page *page = buf->page; |
98 | int err; | 97 | int err; |
@@ -118,49 +117,25 @@ static void *page_cache_pipe_buf_map(struct file *file, | |||
118 | } | 117 | } |
119 | 118 | ||
120 | /* | 119 | /* |
121 | * Page is ok afterall, fall through to mapping. | 120 | * Page is ok afterall, we are done. |
122 | */ | 121 | */ |
123 | unlock_page(page); | 122 | unlock_page(page); |
124 | } | 123 | } |
125 | 124 | ||
126 | return kmap(page); | 125 | return 0; |
127 | error: | 126 | error: |
128 | unlock_page(page); | 127 | unlock_page(page); |
129 | return ERR_PTR(err); | 128 | return err; |
130 | } | ||
131 | |||
132 | static void page_cache_pipe_buf_unmap(struct pipe_inode_info *info, | ||
133 | struct pipe_buffer *buf) | ||
134 | { | ||
135 | kunmap(buf->page); | ||
136 | } | ||
137 | |||
138 | static void *user_page_pipe_buf_map(struct file *file, | ||
139 | struct pipe_inode_info *pipe, | ||
140 | struct pipe_buffer *buf) | ||
141 | { | ||
142 | return kmap(buf->page); | ||
143 | } | ||
144 | |||
145 | static void user_page_pipe_buf_unmap(struct pipe_inode_info *pipe, | ||
146 | struct pipe_buffer *buf) | ||
147 | { | ||
148 | kunmap(buf->page); | ||
149 | } | ||
150 | |||
151 | static void page_cache_pipe_buf_get(struct pipe_inode_info *info, | ||
152 | struct pipe_buffer *buf) | ||
153 | { | ||
154 | page_cache_get(buf->page); | ||
155 | } | 129 | } |
156 | 130 | ||
157 | static struct pipe_buf_operations page_cache_pipe_buf_ops = { | 131 | static struct pipe_buf_operations page_cache_pipe_buf_ops = { |
158 | .can_merge = 0, | 132 | .can_merge = 0, |
159 | .map = page_cache_pipe_buf_map, | 133 | .map = generic_pipe_buf_map, |
160 | .unmap = page_cache_pipe_buf_unmap, | 134 | .unmap = generic_pipe_buf_unmap, |
135 | .pin = page_cache_pipe_buf_pin, | ||
161 | .release = page_cache_pipe_buf_release, | 136 | .release = page_cache_pipe_buf_release, |
162 | .steal = page_cache_pipe_buf_steal, | 137 | .steal = page_cache_pipe_buf_steal, |
163 | .get = page_cache_pipe_buf_get, | 138 | .get = generic_pipe_buf_get, |
164 | }; | 139 | }; |
165 | 140 | ||
166 | 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, |
@@ -171,11 +146,12 @@ static int user_page_pipe_buf_steal(struct pipe_inode_info *pipe, | |||
171 | 146 | ||
172 | static struct pipe_buf_operations user_page_pipe_buf_ops = { | 147 | static struct pipe_buf_operations user_page_pipe_buf_ops = { |
173 | .can_merge = 0, | 148 | .can_merge = 0, |
174 | .map = user_page_pipe_buf_map, | 149 | .map = generic_pipe_buf_map, |
175 | .unmap = user_page_pipe_buf_unmap, | 150 | .unmap = generic_pipe_buf_unmap, |
151 | .pin = generic_pipe_buf_pin, | ||
176 | .release = page_cache_pipe_buf_release, | 152 | .release = page_cache_pipe_buf_release, |
177 | .steal = user_page_pipe_buf_steal, | 153 | .steal = user_page_pipe_buf_steal, |
178 | .get = page_cache_pipe_buf_get, | 154 | .get = generic_pipe_buf_get, |
179 | }; | 155 | }; |
180 | 156 | ||
181 | /* | 157 | /* |
@@ -517,26 +493,16 @@ static int pipe_to_sendpage(struct pipe_inode_info *info, | |||
517 | { | 493 | { |
518 | struct file *file = sd->file; | 494 | struct file *file = sd->file; |
519 | loff_t pos = sd->pos; | 495 | loff_t pos = sd->pos; |
520 | ssize_t ret; | 496 | int ret, more; |
521 | void *ptr; | ||
522 | int more; | ||
523 | 497 | ||
524 | /* | 498 | ret = buf->ops->pin(info, buf); |
525 | * Sub-optimal, but we are limited by the pipe ->map. We don't | 499 | if (!ret) { |
526 | * need a kmap'ed buffer here, we just want to make sure we | 500 | more = (sd->flags & SPLICE_F_MORE) || sd->len < sd->total_len; |
527 | * have the page pinned if the pipe page originates from the | ||
528 | * page cache. | ||
529 | */ | ||
530 | ptr = buf->ops->map(file, info, buf); | ||
531 | if (IS_ERR(ptr)) | ||
532 | return PTR_ERR(ptr); | ||
533 | |||
534 | more = (sd->flags & SPLICE_F_MORE) || sd->len < sd->total_len; | ||
535 | 501 | ||
536 | ret = file->f_op->sendpage(file, buf->page, buf->offset, sd->len, | 502 | ret = file->f_op->sendpage(file, buf->page, buf->offset, |
537 | &pos, more); | 503 | sd->len, &pos, more); |
504 | } | ||
538 | 505 | ||
539 | buf->ops->unmap(info, buf); | ||
540 | return ret; | 506 | return ret; |
541 | } | 507 | } |
542 | 508 | ||
@@ -569,15 +535,14 @@ static int pipe_to_file(struct pipe_inode_info *info, struct pipe_buffer *buf, | |||
569 | unsigned int offset, this_len; | 535 | unsigned int offset, this_len; |
570 | struct page *page; | 536 | struct page *page; |
571 | pgoff_t index; | 537 | pgoff_t index; |
572 | char *src; | ||
573 | int ret; | 538 | int ret; |
574 | 539 | ||
575 | /* | 540 | /* |
576 | * make sure the data in this buffer is uptodate | 541 | * make sure the data in this buffer is uptodate |
577 | */ | 542 | */ |
578 | src = buf->ops->map(file, info, buf); | 543 | ret = buf->ops->pin(info, buf); |
579 | if (IS_ERR(src)) | 544 | if (unlikely(ret)) |
580 | return PTR_ERR(src); | 545 | return ret; |
581 | 546 | ||
582 | index = sd->pos >> PAGE_CACHE_SHIFT; | 547 | index = sd->pos >> PAGE_CACHE_SHIFT; |
583 | offset = sd->pos & ~PAGE_CACHE_MASK; | 548 | offset = sd->pos & ~PAGE_CACHE_MASK; |
@@ -666,11 +631,16 @@ find_page: | |||
666 | goto out; | 631 | goto out; |
667 | 632 | ||
668 | if (buf->page != page) { | 633 | if (buf->page != page) { |
669 | char *dst = kmap_atomic(page, KM_USER0); | 634 | /* |
635 | * Careful, ->map() uses KM_USER0! | ||
636 | */ | ||
637 | char *src = buf->ops->map(info, buf); | ||
638 | char *dst = kmap_atomic(page, KM_USER1); | ||
670 | 639 | ||
671 | memcpy(dst + offset, src + buf->offset, this_len); | 640 | memcpy(dst + offset, src + buf->offset, this_len); |
672 | flush_dcache_page(page); | 641 | flush_dcache_page(page); |
673 | kunmap_atomic(dst, KM_USER0); | 642 | kunmap_atomic(dst, KM_USER1); |
643 | buf->ops->unmap(info, buf); | ||
674 | } | 644 | } |
675 | 645 | ||
676 | ret = mapping->a_ops->commit_write(file, page, offset, offset+this_len); | 646 | ret = mapping->a_ops->commit_write(file, page, offset, offset+this_len); |
@@ -690,7 +660,6 @@ out: | |||
690 | page_cache_release(page); | 660 | page_cache_release(page); |
691 | unlock_page(page); | 661 | unlock_page(page); |
692 | out_nomem: | 662 | out_nomem: |
693 | buf->ops->unmap(info, buf); | ||
694 | return ret; | 663 | return ret; |
695 | } | 664 | } |
696 | 665 | ||
diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h index 3130977fc6ab..b8aae1fc5185 100644 --- a/include/linux/pipe_fs_i.h +++ b/include/linux/pipe_fs_i.h | |||
@@ -14,10 +14,23 @@ struct pipe_buffer { | |||
14 | unsigned int flags; | 14 | unsigned int flags; |
15 | }; | 15 | }; |
16 | 16 | ||
17 | /* | ||
18 | * Note on the nesting of these functions: | ||
19 | * | ||
20 | * ->pin() | ||
21 | * ->steal() | ||
22 | * ... | ||
23 | * ->map() | ||
24 | * ... | ||
25 | * ->unmap() | ||
26 | * | ||
27 | * That is, ->map() must be called on a pinned buffer, same goes for ->steal(). | ||
28 | */ | ||
17 | struct pipe_buf_operations { | 29 | struct pipe_buf_operations { |
18 | int can_merge; | 30 | int can_merge; |
19 | void * (*map)(struct file *, struct pipe_inode_info *, struct pipe_buffer *); | 31 | void * (*map)(struct pipe_inode_info *, struct pipe_buffer *); |
20 | void (*unmap)(struct pipe_inode_info *, struct pipe_buffer *); | 32 | void (*unmap)(struct pipe_inode_info *, struct pipe_buffer *); |
33 | int (*pin)(struct pipe_inode_info *, struct pipe_buffer *); | ||
21 | void (*release)(struct pipe_inode_info *, struct pipe_buffer *); | 34 | void (*release)(struct pipe_inode_info *, struct pipe_buffer *); |
22 | int (*steal)(struct pipe_inode_info *, struct pipe_buffer *); | 35 | int (*steal)(struct pipe_inode_info *, struct pipe_buffer *); |
23 | void (*get)(struct pipe_inode_info *, struct pipe_buffer *); | 36 | void (*get)(struct pipe_inode_info *, struct pipe_buffer *); |
@@ -50,6 +63,12 @@ struct pipe_inode_info * alloc_pipe_info(struct inode * inode); | |||
50 | void free_pipe_info(struct inode * inode); | 63 | void free_pipe_info(struct inode * inode); |
51 | void __free_pipe_info(struct pipe_inode_info *); | 64 | void __free_pipe_info(struct pipe_inode_info *); |
52 | 65 | ||
66 | /* Generic pipe buffer ops functions */ | ||
67 | void *generic_pipe_buf_map(struct pipe_inode_info *, struct pipe_buffer *); | ||
68 | void generic_pipe_buf_unmap(struct pipe_inode_info *, struct pipe_buffer *); | ||
69 | void generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *); | ||
70 | int generic_pipe_buf_pin(struct pipe_inode_info *, struct pipe_buffer *); | ||
71 | |||
53 | /* | 72 | /* |
54 | * splice is tied to pipes as a transport (at least for now), so we'll just | 73 | * splice is tied to pipes as a transport (at least for now), so we'll just |
55 | * add the splice flags here. | 74 | * add the splice flags here. |