linux-block.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* alternative take on the same page merging leak fix v2
@ 2019-06-13  9:55 Christoph Hellwig
  2019-06-13  9:55 ` [PATCH 1/2] block: return from __bio_try_merge_page if merging occured in the same page Christoph Hellwig
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Christoph Hellwig @ 2019-06-13  9:55 UTC (permalink / raw)
  To: Jens Axboe, Ming Lei
  Cc: David Gibson, Darrick J. Wong, linux-block, linux-xfs

Hi Jens, hi Ming,

this is the tested and split version of what I think is the better
fix for the get_user_pages page leak, as it leaves the intelligence
in the callers instead of in bio_try_to_merge_page.

Changes since v1:
 - drop patches not required for 5.2
 - added Reviewed-by tags

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 1/2] block: return from __bio_try_merge_page if merging occured in the same page
  2019-06-13  9:55 alternative take on the same page merging leak fix v2 Christoph Hellwig
@ 2019-06-13  9:55 ` Christoph Hellwig
  2019-06-13  9:55 ` [PATCH 2/2] block: fix page leak when merging to " Christoph Hellwig
  2019-06-13 10:04 ` alternative take on the same page merging leak fix v2 Jens Axboe
  2 siblings, 0 replies; 9+ messages in thread
From: Christoph Hellwig @ 2019-06-13  9:55 UTC (permalink / raw)
  To: Jens Axboe, Ming Lei
  Cc: David Gibson, Darrick J. Wong, linux-block, linux-xfs

We currently have an input same_page parameter to __bio_try_merge_page
to prohibit merging in the same page.  The rationale for that is that
some callers need to account for every page added to a bio.  Instead of
letting these callers call twice into the merge code to account for the
new vs existing page cases, just turn the paramter into an output one that
returns if a merge in the same page occured and let them act accordingly.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
---
 block/bio.c         | 23 +++++++++--------------
 fs/iomap.c          | 12 ++++++++----
 fs/xfs/xfs_aops.c   | 11 ++++++++---
 include/linux/bio.h |  2 +-
 4 files changed, 26 insertions(+), 22 deletions(-)

diff --git a/block/bio.c b/block/bio.c
index 683cbb40f051..59588c57694d 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -636,7 +636,7 @@ EXPORT_SYMBOL(bio_clone_fast);
 
 static inline bool page_is_mergeable(const struct bio_vec *bv,
 		struct page *page, unsigned int len, unsigned int off,
-		bool same_page)
+		bool *same_page)
 {
 	phys_addr_t vec_end_addr = page_to_phys(bv->bv_page) +
 		bv->bv_offset + bv->bv_len - 1;
@@ -647,15 +647,9 @@ static inline bool page_is_mergeable(const struct bio_vec *bv,
 	if (xen_domain() && !xen_biovec_phys_mergeable(bv, page))
 		return false;
 
-	if ((vec_end_addr & PAGE_MASK) != page_addr) {
-		if (same_page)
-			return false;
-		if (pfn_to_page(PFN_DOWN(vec_end_addr)) + 1 != page)
-			return false;
-	}
-
-	WARN_ON_ONCE(same_page && (len + off) > PAGE_SIZE);
-
+	*same_page = ((vec_end_addr & PAGE_MASK) == page_addr);
+	if (!*same_page && pfn_to_page(PFN_DOWN(vec_end_addr)) + 1 != page)
+		return false;
 	return true;
 }
 
@@ -767,8 +761,7 @@ EXPORT_SYMBOL(bio_add_pc_page);
  * @page: start page to add
  * @len: length of the data to add
  * @off: offset of the data relative to @page
- * @same_page: if %true only merge if the new data is in the same physical
- *		page as the last segment of the bio.
+ * @same_page: return if the segment has been merged inside the same 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
@@ -779,7 +772,7 @@ EXPORT_SYMBOL(bio_add_pc_page);
  * 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, bool same_page)
+		unsigned int len, unsigned int off, bool *same_page)
 {
 	if (WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED)))
 		return false;
@@ -837,7 +830,9 @@ EXPORT_SYMBOL_GPL(__bio_add_page);
 int bio_add_page(struct bio *bio, struct page *page,
 		 unsigned int len, unsigned int offset)
 {
-	if (!__bio_try_merge_page(bio, page, len, offset, false)) {
+	bool same_page = false;
+
+	if (!__bio_try_merge_page(bio, page, len, offset, &same_page)) {
 		if (bio_full(bio))
 			return 0;
 		__bio_add_page(bio, page, len, offset);
diff --git a/fs/iomap.c b/fs/iomap.c
index 23ef63fd1669..12654c2e78f8 100644
--- a/fs/iomap.c
+++ b/fs/iomap.c
@@ -287,7 +287,7 @@ iomap_readpage_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
 	struct iomap_readpage_ctx *ctx = data;
 	struct page *page = ctx->cur_page;
 	struct iomap_page *iop = iomap_page_create(inode, page);
-	bool is_contig = false;
+	bool same_page = false, is_contig = false;
 	loff_t orig_pos = pos;
 	unsigned poff, plen;
 	sector_t sector;
@@ -315,10 +315,14 @@ iomap_readpage_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
 	 * Try to merge into a previous segment if we can.
 	 */
 	sector = iomap_sector(iomap, pos);
-	if (ctx->bio && bio_end_sector(ctx->bio) == sector) {
-		if (__bio_try_merge_page(ctx->bio, page, plen, poff, true))
-			goto done;
+	if (ctx->bio && bio_end_sector(ctx->bio) == sector)
 		is_contig = true;
+
+	if (is_contig &&
+	    __bio_try_merge_page(ctx->bio, page, plen, poff, &same_page)) {
+		if (!same_page && iop)
+			atomic_inc(&iop->read_count);
+		goto done;
 	}
 
 	/*
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index a6f0f4761a37..8da5e6637771 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -758,6 +758,7 @@ xfs_add_to_ioend(
 	struct block_device	*bdev = xfs_find_bdev_for_inode(inode);
 	unsigned		len = i_blocksize(inode);
 	unsigned		poff = offset & (PAGE_SIZE - 1);
+	bool			merged, same_page = false;
 	sector_t		sector;
 
 	sector = xfs_fsb_to_db(ip, wpc->imap.br_startblock) +
@@ -774,9 +775,13 @@ xfs_add_to_ioend(
 				wpc->imap.br_state, offset, bdev, sector);
 	}
 
-	if (!__bio_try_merge_page(wpc->ioend->io_bio, page, len, poff, true)) {
-		if (iop)
-			atomic_inc(&iop->write_count);
+	merged = __bio_try_merge_page(wpc->ioend->io_bio, page, len, poff,
+			&same_page);
+
+	if (iop && !same_page)
+		atomic_inc(&iop->write_count);
+
+	if (!merged) {
 		if (bio_full(wpc->ioend->io_bio))
 			xfs_chain_bio(wpc->ioend, wbc, bdev, sector);
 		bio_add_page(wpc->ioend->io_bio, page, len, poff);
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 0f23b5682640..f87abaa898f0 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -423,7 +423,7 @@ 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, bool same_page);
+		unsigned int len, unsigned int off, bool *same_page);
 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);
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 2/2] block: fix page leak when merging to same page
  2019-06-13  9:55 alternative take on the same page merging leak fix v2 Christoph Hellwig
  2019-06-13  9:55 ` [PATCH 1/2] block: return from __bio_try_merge_page if merging occured in the same page Christoph Hellwig
@ 2019-06-13  9:55 ` Christoph Hellwig
  2019-06-13 10:04 ` alternative take on the same page merging leak fix v2 Jens Axboe
  2 siblings, 0 replies; 9+ messages in thread
From: Christoph Hellwig @ 2019-06-13  9:55 UTC (permalink / raw)
  To: Jens Axboe, Ming Lei
  Cc: David Gibson, Darrick J. Wong, linux-block, linux-xfs

When multiple iovecs reference the same page, each get_user_page call
will add a reference to the page.  But once we've created the bio that
information gets lost and only a single reference will be dropped after
I/O completion.  Use the same_page information returned from
__bio_try_merge_page to drop additional references to pages that were
already present in the bio.

Based on a patch from Ming Lei.

Link: https://lkml.org/lkml/2019/4/23/64
Fixes: 576ed913 ("block: use bio_add_page in bio_iov_iter_get_pages")
Reported-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
---
 block/bio.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/block/bio.c b/block/bio.c
index 59588c57694d..35b3c568a48f 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -895,6 +895,7 @@ static int __bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter)
 	unsigned short entries_left = bio->bi_max_vecs - bio->bi_vcnt;
 	struct bio_vec *bv = bio->bi_io_vec + bio->bi_vcnt;
 	struct page **pages = (struct page **)bv;
+	bool same_page = false;
 	ssize_t size, left;
 	unsigned len, i;
 	size_t offset;
@@ -915,8 +916,15 @@ static int __bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter)
 		struct page *page = pages[i];
 
 		len = min_t(size_t, PAGE_SIZE - offset, left);
-		if (WARN_ON_ONCE(bio_add_page(bio, page, len, offset) != len))
-			return -EINVAL;
+
+		if (__bio_try_merge_page(bio, page, len, offset, &same_page)) {
+			if (same_page)
+				put_page(page);
+		} else {
+			if (WARN_ON_ONCE(bio_full(bio)))
+                                return -EINVAL;
+			__bio_add_page(bio, page, len, offset);
+		}
 		offset = 0;
 	}
 
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: alternative take on the same page merging leak fix v2
  2019-06-13  9:55 alternative take on the same page merging leak fix v2 Christoph Hellwig
  2019-06-13  9:55 ` [PATCH 1/2] block: return from __bio_try_merge_page if merging occured in the same page Christoph Hellwig
  2019-06-13  9:55 ` [PATCH 2/2] block: fix page leak when merging to " Christoph Hellwig
@ 2019-06-13 10:04 ` Jens Axboe
  2019-06-14  1:19   ` Ming Lei
  2 siblings, 1 reply; 9+ messages in thread
From: Jens Axboe @ 2019-06-13 10:04 UTC (permalink / raw)
  To: Christoph Hellwig, Ming Lei
  Cc: David Gibson, Darrick J. Wong, linux-block, linux-xfs

On 6/13/19 3:55 AM, Christoph Hellwig wrote:
> Hi Jens, hi Ming,
> 
> this is the tested and split version of what I think is the better
> fix for the get_user_pages page leak, as it leaves the intelligence
> in the callers instead of in bio_try_to_merge_page.
> 
> Changes since v1:
>   - drop patches not required for 5.2
>   - added Reviewed-by tags

Applied for 5.2, thanks.

-- 
Jens Axboe


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: alternative take on the same page merging leak fix v2
  2019-06-13 10:04 ` alternative take on the same page merging leak fix v2 Jens Axboe
@ 2019-06-14  1:19   ` Ming Lei
  2019-06-14  5:45     ` Christoph Hellwig
  0 siblings, 1 reply; 9+ messages in thread
From: Ming Lei @ 2019-06-14  1:19 UTC (permalink / raw)
  To: Jens Axboe
  Cc: Christoph Hellwig, David Gibson, Darrick J. Wong, linux-block, linux-xfs

On Thu, Jun 13, 2019 at 04:04:03AM -0600, Jens Axboe wrote:
> On 6/13/19 3:55 AM, Christoph Hellwig wrote:
> > Hi Jens, hi Ming,
> > 
> > this is the tested and split version of what I think is the better
> > fix for the get_user_pages page leak, as it leaves the intelligence
> > in the callers instead of in bio_try_to_merge_page.
> > 
> > Changes since v1:
> >   - drop patches not required for 5.2
> >   - added Reviewed-by tags
> 
> Applied for 5.2, thanks.

Hi Jens & Christoph,

Looks the following change is missed in patch 1, otherwise kernel oops
is triggered during kernel booting:

diff --git a/block/bio.c b/block/bio.c
index 35b3c568a48f..9ccf07c666f7 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -706,6 +706,8 @@ static int __bio_add_pc_page(struct request_queue *q, struct bio *bio,
 		return 0;
 
 	if (bio->bi_vcnt > 0) {
+		bool same_page;
+
 		bvec = &bio->bi_io_vec[bio->bi_vcnt - 1];
 
 		if (page == bvec->bv_page &&
@@ -723,7 +725,7 @@ static int __bio_add_pc_page(struct request_queue *q, struct bio *bio,
 		if (bvec_gap_to_prev(q, bvec, offset))
 			return 0;
 
-		if (page_is_mergeable(bvec, page, len, offset, false) &&
+		if (page_is_mergeable(bvec, page, len, offset, &same_page) &&
 		    can_add_page_to_seg(q, bvec, page, len, offset)) {
 			bvec->bv_len += len;
 			goto done;

Thanks,
Ming

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: alternative take on the same page merging leak fix v2
  2019-06-14  1:19   ` Ming Lei
@ 2019-06-14  5:45     ` Christoph Hellwig
  2019-06-14  6:52       ` Ming Lei
  2019-06-14  6:57       ` Jens Axboe
  0 siblings, 2 replies; 9+ messages in thread
From: Christoph Hellwig @ 2019-06-14  5:45 UTC (permalink / raw)
  To: Ming Lei
  Cc: Jens Axboe, Christoph Hellwig, David Gibson, Darrick J. Wong,
	linux-block, linux-xfs

On Fri, Jun 14, 2019 at 09:19:47AM +0800, Ming Lei wrote:
> On Thu, Jun 13, 2019 at 04:04:03AM -0600, Jens Axboe wrote:
> > On 6/13/19 3:55 AM, Christoph Hellwig wrote:
> > > Hi Jens, hi Ming,
> > > 
> > > this is the tested and split version of what I think is the better
> > > fix for the get_user_pages page leak, as it leaves the intelligence
> > > in the callers instead of in bio_try_to_merge_page.
> > > 
> > > Changes since v1:
> > >   - drop patches not required for 5.2
> > >   - added Reviewed-by tags
> > 
> > Applied for 5.2, thanks.
> 
> Hi Jens & Christoph,
> 
> Looks the following change is missed in patch 1, otherwise kernel oops
> is triggered during kernel booting:

Ok, true, sorry.

Reviewed-by: Christoph Hellwig <hch@lst.de>

and you probably want to cook it up for Jens with a proper changelog.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: alternative take on the same page merging leak fix v2
  2019-06-14  5:45     ` Christoph Hellwig
@ 2019-06-14  6:52       ` Ming Lei
  2019-06-14  6:57       ` Jens Axboe
  1 sibling, 0 replies; 9+ messages in thread
From: Ming Lei @ 2019-06-14  6:52 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Jens Axboe, David Gibson, Darrick J. Wong, linux-block, linux-xfs

On Fri, Jun 14, 2019 at 07:45:14AM +0200, Christoph Hellwig wrote:
> On Fri, Jun 14, 2019 at 09:19:47AM +0800, Ming Lei wrote:
> > On Thu, Jun 13, 2019 at 04:04:03AM -0600, Jens Axboe wrote:
> > > On 6/13/19 3:55 AM, Christoph Hellwig wrote:
> > > > Hi Jens, hi Ming,
> > > > 
> > > > this is the tested and split version of what I think is the better
> > > > fix for the get_user_pages page leak, as it leaves the intelligence
> > > > in the callers instead of in bio_try_to_merge_page.
> > > > 
> > > > Changes since v1:
> > > >   - drop patches not required for 5.2
> > > >   - added Reviewed-by tags
> > > 
> > > Applied for 5.2, thanks.
> > 
> > Hi Jens & Christoph,
> > 
> > Looks the following change is missed in patch 1, otherwise kernel oops
> > is triggered during kernel booting:
> 
> Ok, true, sorry.
> 
> Reviewed-by: Christoph Hellwig <hch@lst.de>
> 
> and you probably want to cook it up for Jens with a proper changelog.

This one can be wrapped into the patch 1 given it is just at top of
for-linus, so I guess Jens may do it.

Thanks,
Ming

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: alternative take on the same page merging leak fix v2
  2019-06-14  5:45     ` Christoph Hellwig
  2019-06-14  6:52       ` Ming Lei
@ 2019-06-14  6:57       ` Jens Axboe
  1 sibling, 0 replies; 9+ messages in thread
From: Jens Axboe @ 2019-06-14  6:57 UTC (permalink / raw)
  To: Christoph Hellwig, Ming Lei
  Cc: David Gibson, Darrick J. Wong, linux-block, linux-xfs

On 6/13/19 11:45 PM, Christoph Hellwig wrote:
> On Fri, Jun 14, 2019 at 09:19:47AM +0800, Ming Lei wrote:
>> On Thu, Jun 13, 2019 at 04:04:03AM -0600, Jens Axboe wrote:
>>> On 6/13/19 3:55 AM, Christoph Hellwig wrote:
>>>> Hi Jens, hi Ming,
>>>>
>>>> this is the tested and split version of what I think is the better
>>>> fix for the get_user_pages page leak, as it leaves the intelligence
>>>> in the callers instead of in bio_try_to_merge_page.
>>>>
>>>> Changes since v1:
>>>>    - drop patches not required for 5.2
>>>>    - added Reviewed-by tags
>>>
>>> Applied for 5.2, thanks.
>>
>> Hi Jens & Christoph,
>>
>> Looks the following change is missed in patch 1, otherwise kernel oops
>> is triggered during kernel booting:
> 
> Ok, true, sorry.
> 
> Reviewed-by: Christoph Hellwig <hch@lst.de>

I've dropped the two patches. Please send them again, when they are
tested. Not making -rc5 at this time.

-- 
Jens Axboe


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 1/2] block: return from __bio_try_merge_page if merging occured in the same page
  2019-06-17  9:14 same page merging leak fix v4 Christoph Hellwig
@ 2019-06-17  9:14 ` Christoph Hellwig
  0 siblings, 0 replies; 9+ messages in thread
From: Christoph Hellwig @ 2019-06-17  9:14 UTC (permalink / raw)
  To: Jens Axboe, Ming Lei
  Cc: David Gibson, Darrick J. Wong, linux-block, linux-xfs

We currently have an input same_page parameter to __bio_try_merge_page
to prohibit merging in the same page.  The rationale for that is that
some callers need to account for every page added to a bio.  Instead of
letting these callers call twice into the merge code to account for the
new vs existing page cases, just turn the paramter into an output one that
returns if a merge in the same page occured and let them act accordingly.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
---
 block/bio.c         | 26 +++++++++++---------------
 fs/iomap.c          | 12 ++++++++----
 fs/xfs/xfs_aops.c   | 11 ++++++++---
 include/linux/bio.h |  2 +-
 4 files changed, 28 insertions(+), 23 deletions(-)

diff --git a/block/bio.c b/block/bio.c
index 683cbb40f051..daa1c1ae72cd 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -636,7 +636,7 @@ EXPORT_SYMBOL(bio_clone_fast);
 
 static inline bool page_is_mergeable(const struct bio_vec *bv,
 		struct page *page, unsigned int len, unsigned int off,
-		bool same_page)
+		bool *same_page)
 {
 	phys_addr_t vec_end_addr = page_to_phys(bv->bv_page) +
 		bv->bv_offset + bv->bv_len - 1;
@@ -647,15 +647,9 @@ static inline bool page_is_mergeable(const struct bio_vec *bv,
 	if (xen_domain() && !xen_biovec_phys_mergeable(bv, page))
 		return false;
 
-	if ((vec_end_addr & PAGE_MASK) != page_addr) {
-		if (same_page)
-			return false;
-		if (pfn_to_page(PFN_DOWN(vec_end_addr)) + 1 != page)
-			return false;
-	}
-
-	WARN_ON_ONCE(same_page && (len + off) > PAGE_SIZE);
-
+	*same_page = ((vec_end_addr & PAGE_MASK) == page_addr);
+	if (!*same_page && pfn_to_page(PFN_DOWN(vec_end_addr)) + 1 != page)
+		return false;
 	return true;
 }
 
@@ -701,6 +695,7 @@ static int __bio_add_pc_page(struct request_queue *q, struct bio *bio,
 		bool put_same_page)
 {
 	struct bio_vec *bvec;
+	bool same_page = false;
 
 	/*
 	 * cloned bio must not modify vec list
@@ -729,7 +724,7 @@ static int __bio_add_pc_page(struct request_queue *q, struct bio *bio,
 		if (bvec_gap_to_prev(q, bvec, offset))
 			return 0;
 
-		if (page_is_mergeable(bvec, page, len, offset, false) &&
+		if (page_is_mergeable(bvec, page, len, offset, &same_page) &&
 		    can_add_page_to_seg(q, bvec, page, len, offset)) {
 			bvec->bv_len += len;
 			goto done;
@@ -767,8 +762,7 @@ EXPORT_SYMBOL(bio_add_pc_page);
  * @page: start page to add
  * @len: length of the data to add
  * @off: offset of the data relative to @page
- * @same_page: if %true only merge if the new data is in the same physical
- *		page as the last segment of the bio.
+ * @same_page: return if the segment has been merged inside the same 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
@@ -779,7 +773,7 @@ EXPORT_SYMBOL(bio_add_pc_page);
  * 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, bool same_page)
+		unsigned int len, unsigned int off, bool *same_page)
 {
 	if (WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED)))
 		return false;
@@ -837,7 +831,9 @@ EXPORT_SYMBOL_GPL(__bio_add_page);
 int bio_add_page(struct bio *bio, struct page *page,
 		 unsigned int len, unsigned int offset)
 {
-	if (!__bio_try_merge_page(bio, page, len, offset, false)) {
+	bool same_page = false;
+
+	if (!__bio_try_merge_page(bio, page, len, offset, &same_page)) {
 		if (bio_full(bio))
 			return 0;
 		__bio_add_page(bio, page, len, offset);
diff --git a/fs/iomap.c b/fs/iomap.c
index 23ef63fd1669..12654c2e78f8 100644
--- a/fs/iomap.c
+++ b/fs/iomap.c
@@ -287,7 +287,7 @@ iomap_readpage_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
 	struct iomap_readpage_ctx *ctx = data;
 	struct page *page = ctx->cur_page;
 	struct iomap_page *iop = iomap_page_create(inode, page);
-	bool is_contig = false;
+	bool same_page = false, is_contig = false;
 	loff_t orig_pos = pos;
 	unsigned poff, plen;
 	sector_t sector;
@@ -315,10 +315,14 @@ iomap_readpage_actor(struct inode *inode, loff_t pos, loff_t length, void *data,
 	 * Try to merge into a previous segment if we can.
 	 */
 	sector = iomap_sector(iomap, pos);
-	if (ctx->bio && bio_end_sector(ctx->bio) == sector) {
-		if (__bio_try_merge_page(ctx->bio, page, plen, poff, true))
-			goto done;
+	if (ctx->bio && bio_end_sector(ctx->bio) == sector)
 		is_contig = true;
+
+	if (is_contig &&
+	    __bio_try_merge_page(ctx->bio, page, plen, poff, &same_page)) {
+		if (!same_page && iop)
+			atomic_inc(&iop->read_count);
+		goto done;
 	}
 
 	/*
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index a6f0f4761a37..8da5e6637771 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -758,6 +758,7 @@ xfs_add_to_ioend(
 	struct block_device	*bdev = xfs_find_bdev_for_inode(inode);
 	unsigned		len = i_blocksize(inode);
 	unsigned		poff = offset & (PAGE_SIZE - 1);
+	bool			merged, same_page = false;
 	sector_t		sector;
 
 	sector = xfs_fsb_to_db(ip, wpc->imap.br_startblock) +
@@ -774,9 +775,13 @@ xfs_add_to_ioend(
 				wpc->imap.br_state, offset, bdev, sector);
 	}
 
-	if (!__bio_try_merge_page(wpc->ioend->io_bio, page, len, poff, true)) {
-		if (iop)
-			atomic_inc(&iop->write_count);
+	merged = __bio_try_merge_page(wpc->ioend->io_bio, page, len, poff,
+			&same_page);
+
+	if (iop && !same_page)
+		atomic_inc(&iop->write_count);
+
+	if (!merged) {
 		if (bio_full(wpc->ioend->io_bio))
 			xfs_chain_bio(wpc->ioend, wbc, bdev, sector);
 		bio_add_page(wpc->ioend->io_bio, page, len, poff);
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 0f23b5682640..f87abaa898f0 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -423,7 +423,7 @@ 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, bool same_page);
+		unsigned int len, unsigned int off, bool *same_page);
 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);
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2019-06-17  9:14 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-06-13  9:55 alternative take on the same page merging leak fix v2 Christoph Hellwig
2019-06-13  9:55 ` [PATCH 1/2] block: return from __bio_try_merge_page if merging occured in the same page Christoph Hellwig
2019-06-13  9:55 ` [PATCH 2/2] block: fix page leak when merging to " Christoph Hellwig
2019-06-13 10:04 ` alternative take on the same page merging leak fix v2 Jens Axboe
2019-06-14  1:19   ` Ming Lei
2019-06-14  5:45     ` Christoph Hellwig
2019-06-14  6:52       ` Ming Lei
2019-06-14  6:57       ` Jens Axboe
2019-06-17  9:14 same page merging leak fix v4 Christoph Hellwig
2019-06-17  9:14 ` [PATCH 1/2] block: return from __bio_try_merge_page if merging occured in the same page Christoph Hellwig

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).