On Wed, May 9, 2018 at 3:47 PM, Christoph Hellwig <hch@xxxxxx> wrote: > For the upcoming removal of buffer heads in XFS we need to keep track of > the number of outstanding writeback requests per page. For this we need > to know if bio_add_page merged a region with the previous bvec or not. > Instead of adding additional arguments this refactors bio_add_page to > be implemented using three lower level helpers which users like XFS can > use directly if they care about the merge decisions. The merge policy may be transparent to fs, such as multipage bvec. > > Signed-off-by: Christoph Hellwig <hch@xxxxxx> > --- > block/bio.c | 87 ++++++++++++++++++++++++++++++--------------- > include/linux/bio.h | 9 +++++ > 2 files changed, 67 insertions(+), 29 deletions(-) > > diff --git a/block/bio.c b/block/bio.c > index 53e0f0a1ed94..6ceba6adbf42 100644 > --- a/block/bio.c > +++ b/block/bio.c > @@ -773,7 +773,7 @@ int bio_add_pc_page(struct request_queue *q, struct bio *bio, struct page > return 0; > } > > - if (bio->bi_vcnt >= bio->bi_max_vecs) > + if (bio_full(bio)) > return 0; > > /* > @@ -820,6 +820,59 @@ int bio_add_pc_page(struct request_queue *q, struct bio *bio, struct page > } > EXPORT_SYMBOL(bio_add_pc_page); > > +/** > + * __bio_try_merge_page - try adding data to an existing bvec > + * @bio: destination bio > + * @page: page to add > + * @len: length of the range to add > + * @off: offset into @page > + * > + * Try adding the data described at @page + @offset to the last bvec of @bio. > + * Return %true on success or %false on failure. This can happen frequently > + * for file systems with a block size smaller than the page size. > + */ > +bool __bio_try_merge_page(struct bio *bio, struct page *page, > + unsigned int len, unsigned int off) > +{ > + if (bio->bi_vcnt > 0) { > + struct bio_vec *bv = &bio->bi_io_vec[bio->bi_vcnt - 1]; > + > + if (page == bv->bv_page && off == bv->bv_offset + bv->bv_len) { > + bv->bv_len += len; > + bio->bi_iter.bi_size += len; > + return true; > + } > + } > + return false; > +} > +EXPORT_SYMBOL_GPL(__bio_try_merge_page); > + > +/** > + * __bio_add_page - add page to a bio in a new segment > + * @bio: destination bio > + * @page: page to add > + * @len: length of the range to add > + * @off: offset into @page > + * > + * Add the data at @page + @offset to @bio as a new bvec. The caller must > + * ensure that @bio has space for another bvec. > + */ > +void __bio_add_page(struct bio *bio, struct page *page, > + unsigned int len, unsigned int off) > +{ > + struct bio_vec *bv = &bio->bi_io_vec[bio->bi_vcnt]; > + > + WARN_ON_ONCE(bio_full(bio)); > + > + bv->bv_page = page; > + bv->bv_offset = off; > + bv->bv_len = len; > + > + bio->bi_iter.bi_size += len; > + bio->bi_vcnt++; > +} > +EXPORT_SYMBOL_GPL(__bio_add_page); Given both __bio_try_merge_page() and __bio_add_page() are exported, please add WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED)), otherwise both may be misused by external users. -- Ming Lei