+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Christoph Hellwig <hch@lst.de>
+Date: Tue, 9 Oct 2018 17:04:39 +0100
+Subject: [PATCH] block: add a lower-level bio_add_page interface
+
+Buglink: https://bugs.launchpad.net/bugs/1796542
+
+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.
+
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Reviewed-by: Jens Axboe <axboe@kernel.dk>
+Reviewed-by: Ming Lei <ming.lei@redhat.com>
+Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
+Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
+(cherry picked from commit 0aa69fd32a5f766e997ca8ab4723c5a1146efa8b)
+Signed-off-by: Colin Ian King <colin.king@canonical.com>
+Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
+---
+ block/bio.c | 98 ++++++++++++++++++++++++++++++++++-------------------
+ include/linux/bio.h | 9 +++++
+ 2 files changed, 73 insertions(+), 34 deletions(-)
+
+diff --git a/block/bio.c b/block/bio.c
+index 4b48f8eefc4c..2636d15af979 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;
+
+ /*
+@@ -821,6 +821,65 @@ 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 appending data to an existing bvec.
++ * @bio: destination bio
++ * @page: page to add
++ * @len: length of the data to add
++ * @off: offset of the data in @page
++ *
++ * Try to add the data at @page + @off to the last bvec of @bio. This is a
++ * a useful optimisation for file systems with a block size smaller than the
++ * page size.
++ *
++ * Return %true on success or %false on failure.
++ */
++bool __bio_try_merge_page(struct bio *bio, struct page *page,
++ unsigned int len, unsigned int off)
++{
++ if (WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED)))
++ return false;
++
++ 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 data to add
++ * @off: offset of the data in @page
++ *
++ * Add the data at @page + @off 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_flagged(bio, BIO_CLONED));
++ 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);
++
++/**
+ * bio_add_page - attempt to add page to bio
+ * @bio: destination bio
+ * @page: page to add
+@@ -833,40 +892,11 @@ EXPORT_SYMBOL(bio_add_pc_page);
+ int bio_add_page(struct bio *bio, struct page *page,
+ unsigned int len, unsigned int offset)
+ {
+- struct bio_vec *bv;
+-
+- /*
+- * cloned bio must not modify vec list
+- */
+- if (WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED)))
+- return 0;
+-
+- /*
+- * For filesystems with a blocksize smaller than the pagesize
+- * we will often be called with the same page as last time and
+- * a consecutive offset. Optimize this special case.
+- */
+- if (bio->bi_vcnt > 0) {
+- bv = &bio->bi_io_vec[bio->bi_vcnt - 1];
+-
+- if (page == bv->bv_page &&
+- offset == bv->bv_offset + bv->bv_len) {
+- bv->bv_len += len;
+- goto done;
+- }
++ if (!__bio_try_merge_page(bio, page, len, offset)) {
++ if (bio_full(bio))
++ return 0;
++ __bio_add_page(bio, page, len, offset);
+ }
+-
+- if (bio->bi_vcnt >= bio->bi_max_vecs)
+- return 0;
+-
+- bv = &bio->bi_io_vec[bio->bi_vcnt];
+- bv->bv_page = page;
+- bv->bv_len = len;
+- bv->bv_offset = offset;
+-
+- bio->bi_vcnt++;
+-done:
+- bio->bi_iter.bi_size += len;
+ return len;
+ }
+ EXPORT_SYMBOL(bio_add_page);
+diff --git a/include/linux/bio.h b/include/linux/bio.h
+index a98c6ac575cf..3440870712d4 100644
+--- a/include/linux/bio.h
++++ b/include/linux/bio.h
+@@ -123,6 +123,11 @@ static inline void *bio_data(struct bio *bio)
+ return NULL;
+ }
+
++static inline bool bio_full(struct bio *bio)
++{
++ return bio->bi_vcnt >= bio->bi_max_vecs;
++}
++
+ /*
+ * will die
+ */
+@@ -447,6 +452,10 @@ void bio_chain(struct bio *, struct bio *);
+ extern int bio_add_page(struct bio *, struct page *, unsigned int,unsigned int);
+ extern int bio_add_pc_page(struct request_queue *, struct bio *, struct page *,
+ unsigned int, unsigned int);
++bool __bio_try_merge_page(struct bio *bio, struct page *page,
++ unsigned int len, unsigned int off);
++void __bio_add_page(struct bio *bio, struct page *page,
++ unsigned int len, unsigned int off);
+ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter);
+ struct rq_map_data;
+ extern struct bio *bio_map_user_iov(struct request_queue *,