summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@lst.de>2019-04-11 02:23:31 -0400
committerJens Axboe <axboe@kernel.dk>2019-04-12 11:06:42 -0400
commit52d52d1c98a90cfe860b83498e4b6074aad95c15 (patch)
tree98776fc54f50c9a9168162e030ee9db6b89c2d46
parent7321ecbfc7cf85211460a1dc6bb0ccfc3dcf9df0 (diff)
block: only allow contiguous page structs in a bio_vec
We currently have to call nth_page when iterating over pages inside a bio_vec. Jens complained a while ago that this is fairly expensive. To mitigate this we can check that that the actual page structures are contiguous when adding them to the bio, and just do check pointer arithmetics later on. Signed-off-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Jens Axboe <axboe@kernel.dk>
-rw-r--r--block/bio.c9
-rw-r--r--include/linux/bvec.h13
2 files changed, 11 insertions, 11 deletions
diff --git a/block/bio.c b/block/bio.c
index d3490aeb1a7e..8adc2a20d57d 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -659,8 +659,13 @@ static inline bool page_is_mergeable(const struct bio_vec *bv,
659 return false; 659 return false;
660 if (xen_domain() && !xen_biovec_phys_mergeable(bv, page)) 660 if (xen_domain() && !xen_biovec_phys_mergeable(bv, page))
661 return false; 661 return false;
662 if (same_page && (vec_end_addr & PAGE_MASK) != page_addr) 662
663 return false; 663 if ((vec_end_addr & PAGE_MASK) != page_addr) {
664 if (same_page)
665 return false;
666 if (pfn_to_page(PFN_DOWN(vec_end_addr)) + 1 != page)
667 return false;
668 }
664 669
665 return true; 670 return true;
666} 671}
diff --git a/include/linux/bvec.h b/include/linux/bvec.h
index 307bbda62b7b..44b0f4684190 100644
--- a/include/linux/bvec.h
+++ b/include/linux/bvec.h
@@ -51,11 +51,6 @@ struct bvec_iter_all {
51 unsigned done; 51 unsigned done;
52}; 52};
53 53
54static inline struct page *bvec_nth_page(struct page *page, int idx)
55{
56 return idx == 0 ? page : nth_page(page, idx);
57}
58
59/* 54/*
60 * various member access, note that bio_data should of course not be used 55 * various member access, note that bio_data should of course not be used
61 * on highmem page vectors 56 * on highmem page vectors
@@ -92,8 +87,8 @@ static inline struct page *bvec_nth_page(struct page *page, int idx)
92 PAGE_SIZE - bvec_iter_offset((bvec), (iter))) 87 PAGE_SIZE - bvec_iter_offset((bvec), (iter)))
93 88
94#define bvec_iter_page(bvec, iter) \ 89#define bvec_iter_page(bvec, iter) \
95 bvec_nth_page(mp_bvec_iter_page((bvec), (iter)), \ 90 (mp_bvec_iter_page((bvec), (iter)) + \
96 mp_bvec_iter_page_idx((bvec), (iter))) 91 mp_bvec_iter_page_idx((bvec), (iter)))
97 92
98#define bvec_iter_bvec(bvec, iter) \ 93#define bvec_iter_bvec(bvec, iter) \
99((struct bio_vec) { \ 94((struct bio_vec) { \
@@ -157,7 +152,7 @@ static inline void mp_bvec_next_segment(const struct bio_vec *bvec,
157 struct bio_vec *bv = &iter_all->bv; 152 struct bio_vec *bv = &iter_all->bv;
158 153
159 if (bv->bv_page) { 154 if (bv->bv_page) {
160 bv->bv_page = nth_page(bv->bv_page, 1); 155 bv->bv_page++;
161 bv->bv_offset = 0; 156 bv->bv_offset = 0;
162 } else { 157 } else {
163 bv->bv_page = bvec->bv_page; 158 bv->bv_page = bvec->bv_page;
@@ -177,7 +172,7 @@ static inline void mp_bvec_last_segment(const struct bio_vec *bvec,
177 unsigned total = bvec->bv_offset + bvec->bv_len; 172 unsigned total = bvec->bv_offset + bvec->bv_len;
178 unsigned last_page = (total - 1) / PAGE_SIZE; 173 unsigned last_page = (total - 1) / PAGE_SIZE;
179 174
180 seg->bv_page = bvec_nth_page(bvec->bv_page, last_page); 175 seg->bv_page = bvec->bv_page + last_page;
181 176
182 /* the whole segment is inside the last page */ 177 /* the whole segment is inside the last page */
183 if (bvec->bv_offset >= last_page * PAGE_SIZE) { 178 if (bvec->bv_offset >= last_page * PAGE_SIZE) {