]>
Commit | Line | Data |
---|---|---|
dbb1ed6d TL |
1 | From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
2 | From: Christoph Hellwig <hch@lst.de> | |
3 | Date: Tue, 9 Oct 2018 17:04:39 +0100 | |
4 | Subject: [PATCH] block: add a lower-level bio_add_page interface | |
5 | ||
6 | Buglink: https://bugs.launchpad.net/bugs/1796542 | |
7 | ||
8 | For the upcoming removal of buffer heads in XFS we need to keep track of | |
9 | the number of outstanding writeback requests per page. For this we need | |
10 | to know if bio_add_page merged a region with the previous bvec or not. | |
11 | Instead of adding additional arguments this refactors bio_add_page to | |
12 | be implemented using three lower level helpers which users like XFS can | |
13 | use directly if they care about the merge decisions. | |
14 | ||
15 | Signed-off-by: Christoph Hellwig <hch@lst.de> | |
16 | Reviewed-by: Jens Axboe <axboe@kernel.dk> | |
17 | Reviewed-by: Ming Lei <ming.lei@redhat.com> | |
18 | Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> | |
19 | Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> | |
20 | (cherry picked from commit 0aa69fd32a5f766e997ca8ab4723c5a1146efa8b) | |
21 | Signed-off-by: Colin Ian King <colin.king@canonical.com> | |
22 | Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com> | |
23 | --- | |
24 | block/bio.c | 98 ++++++++++++++++++++++++++++++++++------------------- | |
25 | include/linux/bio.h | 9 +++++ | |
26 | 2 files changed, 73 insertions(+), 34 deletions(-) | |
27 | ||
28 | diff --git a/block/bio.c b/block/bio.c | |
29 | index 4b48f8eefc4c..2636d15af979 100644 | |
30 | --- a/block/bio.c | |
31 | +++ b/block/bio.c | |
32 | @@ -773,7 +773,7 @@ int bio_add_pc_page(struct request_queue *q, struct bio *bio, struct page | |
33 | return 0; | |
34 | } | |
35 | ||
36 | - if (bio->bi_vcnt >= bio->bi_max_vecs) | |
37 | + if (bio_full(bio)) | |
38 | return 0; | |
39 | ||
40 | /* | |
41 | @@ -821,6 +821,65 @@ int bio_add_pc_page(struct request_queue *q, struct bio *bio, struct page | |
42 | EXPORT_SYMBOL(bio_add_pc_page); | |
43 | ||
44 | /** | |
45 | + * __bio_try_merge_page - try appending data to an existing bvec. | |
46 | + * @bio: destination bio | |
47 | + * @page: page to add | |
48 | + * @len: length of the data to add | |
49 | + * @off: offset of the data in @page | |
50 | + * | |
51 | + * Try to add the data at @page + @off to the last bvec of @bio. This is a | |
52 | + * a useful optimisation for file systems with a block size smaller than the | |
53 | + * page size. | |
54 | + * | |
55 | + * Return %true on success or %false on failure. | |
56 | + */ | |
57 | +bool __bio_try_merge_page(struct bio *bio, struct page *page, | |
58 | + unsigned int len, unsigned int off) | |
59 | +{ | |
60 | + if (WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED))) | |
61 | + return false; | |
62 | + | |
63 | + if (bio->bi_vcnt > 0) { | |
64 | + struct bio_vec *bv = &bio->bi_io_vec[bio->bi_vcnt - 1]; | |
65 | + | |
66 | + if (page == bv->bv_page && off == bv->bv_offset + bv->bv_len) { | |
67 | + bv->bv_len += len; | |
68 | + bio->bi_iter.bi_size += len; | |
69 | + return true; | |
70 | + } | |
71 | + } | |
72 | + return false; | |
73 | +} | |
74 | +EXPORT_SYMBOL_GPL(__bio_try_merge_page); | |
75 | + | |
76 | +/** | |
77 | + * __bio_add_page - add page to a bio in a new segment | |
78 | + * @bio: destination bio | |
79 | + * @page: page to add | |
80 | + * @len: length of the data to add | |
81 | + * @off: offset of the data in @page | |
82 | + * | |
83 | + * Add the data at @page + @off to @bio as a new bvec. The caller must ensure | |
84 | + * that @bio has space for another bvec. | |
85 | + */ | |
86 | +void __bio_add_page(struct bio *bio, struct page *page, | |
87 | + unsigned int len, unsigned int off) | |
88 | +{ | |
89 | + struct bio_vec *bv = &bio->bi_io_vec[bio->bi_vcnt]; | |
90 | + | |
91 | + WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED)); | |
92 | + WARN_ON_ONCE(bio_full(bio)); | |
93 | + | |
94 | + bv->bv_page = page; | |
95 | + bv->bv_offset = off; | |
96 | + bv->bv_len = len; | |
97 | + | |
98 | + bio->bi_iter.bi_size += len; | |
99 | + bio->bi_vcnt++; | |
100 | +} | |
101 | +EXPORT_SYMBOL_GPL(__bio_add_page); | |
102 | + | |
103 | +/** | |
104 | * bio_add_page - attempt to add page to bio | |
105 | * @bio: destination bio | |
106 | * @page: page to add | |
107 | @@ -833,40 +892,11 @@ EXPORT_SYMBOL(bio_add_pc_page); | |
108 | int bio_add_page(struct bio *bio, struct page *page, | |
109 | unsigned int len, unsigned int offset) | |
110 | { | |
111 | - struct bio_vec *bv; | |
112 | - | |
113 | - /* | |
114 | - * cloned bio must not modify vec list | |
115 | - */ | |
116 | - if (WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED))) | |
117 | - return 0; | |
118 | - | |
119 | - /* | |
120 | - * For filesystems with a blocksize smaller than the pagesize | |
121 | - * we will often be called with the same page as last time and | |
122 | - * a consecutive offset. Optimize this special case. | |
123 | - */ | |
124 | - if (bio->bi_vcnt > 0) { | |
125 | - bv = &bio->bi_io_vec[bio->bi_vcnt - 1]; | |
126 | - | |
127 | - if (page == bv->bv_page && | |
128 | - offset == bv->bv_offset + bv->bv_len) { | |
129 | - bv->bv_len += len; | |
130 | - goto done; | |
131 | - } | |
132 | + if (!__bio_try_merge_page(bio, page, len, offset)) { | |
133 | + if (bio_full(bio)) | |
134 | + return 0; | |
135 | + __bio_add_page(bio, page, len, offset); | |
136 | } | |
137 | - | |
138 | - if (bio->bi_vcnt >= bio->bi_max_vecs) | |
139 | - return 0; | |
140 | - | |
141 | - bv = &bio->bi_io_vec[bio->bi_vcnt]; | |
142 | - bv->bv_page = page; | |
143 | - bv->bv_len = len; | |
144 | - bv->bv_offset = offset; | |
145 | - | |
146 | - bio->bi_vcnt++; | |
147 | -done: | |
148 | - bio->bi_iter.bi_size += len; | |
149 | return len; | |
150 | } | |
151 | EXPORT_SYMBOL(bio_add_page); | |
152 | diff --git a/include/linux/bio.h b/include/linux/bio.h | |
153 | index a98c6ac575cf..3440870712d4 100644 | |
154 | --- a/include/linux/bio.h | |
155 | +++ b/include/linux/bio.h | |
156 | @@ -123,6 +123,11 @@ static inline void *bio_data(struct bio *bio) | |
157 | return NULL; | |
158 | } | |
159 | ||
160 | +static inline bool bio_full(struct bio *bio) | |
161 | +{ | |
162 | + return bio->bi_vcnt >= bio->bi_max_vecs; | |
163 | +} | |
164 | + | |
165 | /* | |
166 | * will die | |
167 | */ | |
168 | @@ -447,6 +452,10 @@ void bio_chain(struct bio *, struct bio *); | |
169 | extern int bio_add_page(struct bio *, struct page *, unsigned int,unsigned int); | |
170 | extern int bio_add_pc_page(struct request_queue *, struct bio *, struct page *, | |
171 | unsigned int, unsigned int); | |
172 | +bool __bio_try_merge_page(struct bio *bio, struct page *page, | |
173 | + unsigned int len, unsigned int off); | |
174 | +void __bio_add_page(struct bio *bio, struct page *page, | |
175 | + unsigned int len, unsigned int off); | |
176 | int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter); | |
177 | struct rq_map_data; | |
178 | extern struct bio *bio_map_user_iov(struct request_queue *, |