All of lore.kernel.org
 help / color / mirror / Atom feed
From: Andreas Gruenbacher <agruenba@redhat.com>
To: Christoph Hellwig <hch@infradead.org>,
	"Darrick J . Wong" <djwong@kernel.org>,
	Alexander Viro <viro@zeniv.linux.org.uk>,
	Matthew Wilcox <willy@infradead.org>
Cc: Andreas Gruenbacher <agruenba@redhat.com>,
	linux-xfs@vger.kernel.org, linux-fsdevel@vger.kernel.org,
	linux-ext4@vger.kernel.org, cluster-devel@redhat.com
Subject: [RFC v4 3/7] iomap/gfs2: Unlock and put folio in page_done handler
Date: Sun, 18 Dec 2022 23:10:50 +0100	[thread overview]
Message-ID: <20221218221054.3946886-4-agruenba@redhat.com> (raw)
In-Reply-To: <20221216150626.670312-1-agruenba@redhat.com>

When an iomap defines a ->page_done() handler in its page_ops, delegate
unlocking the folio and putting the folio reference to that handler.

This allows to fix a race between journaled data writes and folio
writeback in gfs2: before this change, gfs2_iomap_page_done() was called
after unlocking the folio, so writeback could start writing the folio's
buffers back before they could be marked for writing to the journal.
Also, try_to_free_buffers() could free the buffers before
gfs2_iomap_page_done() was done adding the buffers to the current
current transaction.  With this change, gfs2_iomap_page_done() can add
the buffers to the current transaction while the folio is still locked.
It can then unlock the folio and complete the current transaction.

(If we just moved the entire ->page_done() handler under the folio lock,
dirtying the inode could deadlock with the locked folio on filesystems
with a block size smaller than the page size.)

The only current user of ->page_done() is gfs2, so other filesystems are
not affected.  Still, to catch out any new users, switch from a page to
a folio in ->page_done().

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 fs/gfs2/bmap.c         | 15 ++++++++++++---
 fs/iomap/buffered-io.c |  8 ++++----
 include/linux/iomap.h  |  7 ++++---
 3 files changed, 20 insertions(+), 10 deletions(-)

diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index 3bdb2c668a71..11115fce68cb 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -971,14 +971,23 @@ static int gfs2_iomap_page_prepare(struct inode *inode, loff_t pos,
 }
 
 static void gfs2_iomap_page_done(struct inode *inode, loff_t pos,
-				 unsigned copied, struct page *page)
+				 unsigned copied, struct folio *folio)
 {
 	struct gfs2_trans *tr = current->journal_info;
 	struct gfs2_inode *ip = GFS2_I(inode);
 	struct gfs2_sbd *sdp = GFS2_SB(inode);
 
-	if (page && !gfs2_is_stuffed(ip))
-		gfs2_page_add_databufs(ip, page, offset_in_page(pos), copied);
+	if (!folio) {
+		gfs2_trans_end(sdp);
+		return;
+	}
+
+	if (!gfs2_is_stuffed(ip))
+		gfs2_page_add_databufs(ip, &folio->page, offset_in_page(pos),
+				       copied);
+
+	folio_unlock(folio);
+	folio_put(folio);
 
 	if (tr->tr_num_buf_new)
 		__mark_inode_dirty(inode, I_DIRTY_DATASYNC);
diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index 8ce9abb29d46..517ad5380a62 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -580,12 +580,12 @@ static void iomap_folio_done(struct iomap_iter *iter, loff_t pos, size_t ret,
 {
 	const struct iomap_page_ops *page_ops = iter->iomap.page_ops;
 
-	if (folio)
+	if (page_ops && page_ops->page_done) {
+		page_ops->page_done(iter->inode, pos, ret, folio);
+	} else if (folio) {
 		folio_unlock(folio);
-	if (page_ops && page_ops->page_done)
-		page_ops->page_done(iter->inode, pos, ret, &folio->page);
-	if (folio)
 		folio_put(folio);
+	}
 }
 
 static int iomap_write_begin_inline(const struct iomap_iter *iter,
diff --git a/include/linux/iomap.h b/include/linux/iomap.h
index 0983dfc9a203..743e2a909162 100644
--- a/include/linux/iomap.h
+++ b/include/linux/iomap.h
@@ -131,13 +131,14 @@ static inline bool iomap_inline_data_valid(const struct iomap *iomap)
  * associated with them.
  *
  * When page_prepare succeeds, page_done will always be called to do any
- * cleanup work necessary.  In that page_done call, @page will be NULL if the
- * associated page could not be obtained.
+ * cleanup work necessary.  In that page_done call, @folio will be NULL if the
+ * associated folio could not be obtained.  When folio is not NULL, page_done
+ * is responsible for unlocking and putting the folio.
  */
 struct iomap_page_ops {
 	int (*page_prepare)(struct inode *inode, loff_t pos, unsigned len);
 	void (*page_done)(struct inode *inode, loff_t pos, unsigned copied,
-			struct page *page);
+			struct folio *folio);
 
 	/*
 	 * Check that the cached iomap still maps correctly to the filesystem's
-- 
2.38.1


WARNING: multiple messages have this Message-ID (diff)
From: Andreas Gruenbacher <agruenba@redhat.com>
To: cluster-devel.redhat.com
Subject: [Cluster-devel] [RFC v4 3/7] iomap/gfs2: Unlock and put folio in page_done handler
Date: Sun, 18 Dec 2022 23:10:50 +0100	[thread overview]
Message-ID: <20221218221054.3946886-4-agruenba@redhat.com> (raw)
In-Reply-To: <20221216150626.670312-1-agruenba@redhat.com>

When an iomap defines a ->page_done() handler in its page_ops, delegate
unlocking the folio and putting the folio reference to that handler.

This allows to fix a race between journaled data writes and folio
writeback in gfs2: before this change, gfs2_iomap_page_done() was called
after unlocking the folio, so writeback could start writing the folio's
buffers back before they could be marked for writing to the journal.
Also, try_to_free_buffers() could free the buffers before
gfs2_iomap_page_done() was done adding the buffers to the current
current transaction.  With this change, gfs2_iomap_page_done() can add
the buffers to the current transaction while the folio is still locked.
It can then unlock the folio and complete the current transaction.

(If we just moved the entire ->page_done() handler under the folio lock,
dirtying the inode could deadlock with the locked folio on filesystems
with a block size smaller than the page size.)

The only current user of ->page_done() is gfs2, so other filesystems are
not affected.  Still, to catch out any new users, switch from a page to
a folio in ->page_done().

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 fs/gfs2/bmap.c         | 15 ++++++++++++---
 fs/iomap/buffered-io.c |  8 ++++----
 include/linux/iomap.h  |  7 ++++---
 3 files changed, 20 insertions(+), 10 deletions(-)

diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index 3bdb2c668a71..11115fce68cb 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -971,14 +971,23 @@ static int gfs2_iomap_page_prepare(struct inode *inode, loff_t pos,
 }
 
 static void gfs2_iomap_page_done(struct inode *inode, loff_t pos,
-				 unsigned copied, struct page *page)
+				 unsigned copied, struct folio *folio)
 {
 	struct gfs2_trans *tr = current->journal_info;
 	struct gfs2_inode *ip = GFS2_I(inode);
 	struct gfs2_sbd *sdp = GFS2_SB(inode);
 
-	if (page && !gfs2_is_stuffed(ip))
-		gfs2_page_add_databufs(ip, page, offset_in_page(pos), copied);
+	if (!folio) {
+		gfs2_trans_end(sdp);
+		return;
+	}
+
+	if (!gfs2_is_stuffed(ip))
+		gfs2_page_add_databufs(ip, &folio->page, offset_in_page(pos),
+				       copied);
+
+	folio_unlock(folio);
+	folio_put(folio);
 
 	if (tr->tr_num_buf_new)
 		__mark_inode_dirty(inode, I_DIRTY_DATASYNC);
diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index 8ce9abb29d46..517ad5380a62 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -580,12 +580,12 @@ static void iomap_folio_done(struct iomap_iter *iter, loff_t pos, size_t ret,
 {
 	const struct iomap_page_ops *page_ops = iter->iomap.page_ops;
 
-	if (folio)
+	if (page_ops && page_ops->page_done) {
+		page_ops->page_done(iter->inode, pos, ret, folio);
+	} else if (folio) {
 		folio_unlock(folio);
-	if (page_ops && page_ops->page_done)
-		page_ops->page_done(iter->inode, pos, ret, &folio->page);
-	if (folio)
 		folio_put(folio);
+	}
 }
 
 static int iomap_write_begin_inline(const struct iomap_iter *iter,
diff --git a/include/linux/iomap.h b/include/linux/iomap.h
index 0983dfc9a203..743e2a909162 100644
--- a/include/linux/iomap.h
+++ b/include/linux/iomap.h
@@ -131,13 +131,14 @@ static inline bool iomap_inline_data_valid(const struct iomap *iomap)
  * associated with them.
  *
  * When page_prepare succeeds, page_done will always be called to do any
- * cleanup work necessary.  In that page_done call, @page will be NULL if the
- * associated page could not be obtained.
+ * cleanup work necessary.  In that page_done call, @folio will be NULL if the
+ * associated folio could not be obtained.  When folio is not NULL, page_done
+ * is responsible for unlocking and putting the folio.
  */
 struct iomap_page_ops {
 	int (*page_prepare)(struct inode *inode, loff_t pos, unsigned len);
 	void (*page_done)(struct inode *inode, loff_t pos, unsigned copied,
-			struct page *page);
+			struct folio *folio);
 
 	/*
 	 * Check that the cached iomap still maps correctly to the filesystem's
-- 
2.38.1


  parent reply	other threads:[~2022-12-18 22:12 UTC|newest]

Thread overview: 63+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-12-16 15:06 [RFC v3 0/7] Turn iomap_page_ops into iomap_folio_ops Andreas Gruenbacher
2022-12-16 15:06 ` [Cluster-devel] " Andreas Gruenbacher
2022-12-16 15:06 ` [RFC v3 1/7] fs: Add folio_may_straddle_isize helper Andreas Gruenbacher
2022-12-16 15:06   ` [Cluster-devel] " Andreas Gruenbacher
2022-12-23 14:56   ` Christoph Hellwig
2022-12-23 14:56     ` [Cluster-devel] " Christoph Hellwig
2022-12-23 22:04     ` Andreas Grünbacher
2022-12-23 22:04       ` [Cluster-devel] " Andreas Grünbacher
2022-12-24  7:21       ` Christoph Hellwig
2022-12-16 15:06 ` [RFC v3 2/7] iomap: Add iomap_folio_done helper Andreas Gruenbacher
2022-12-16 15:06   ` [Cluster-devel] " Andreas Gruenbacher
2022-12-23 15:02   ` Christoph Hellwig
2022-12-23 15:02     ` [Cluster-devel] " Christoph Hellwig
2022-12-23 20:54     ` Andreas Grünbacher
2022-12-23 20:54       ` [Cluster-devel] " Andreas Grünbacher
2022-12-24  7:22       ` Christoph Hellwig
2022-12-24  7:22         ` [Cluster-devel] " Christoph Hellwig
2022-12-16 15:06 ` [RFC v3 3/7] iomap/gfs2: Unlock and put folio in page_done handler Andreas Gruenbacher
2022-12-16 15:06   ` [Cluster-devel] " Andreas Gruenbacher
2022-12-23 15:03   ` Christoph Hellwig
2022-12-23 15:03     ` [Cluster-devel] " Christoph Hellwig
2022-12-16 15:06 ` [RFC v3 4/7] iomap: Add iomap_folio_prepare helper Andreas Gruenbacher
2022-12-16 15:06   ` [Cluster-devel] " Andreas Gruenbacher
2022-12-23 15:04   ` Christoph Hellwig
2022-12-23 15:04     ` [Cluster-devel] " Christoph Hellwig
2022-12-23 21:05     ` Andreas Grünbacher
2022-12-23 21:05       ` [Cluster-devel] " Andreas Grünbacher
2022-12-24  7:23       ` Christoph Hellwig
2022-12-24  7:23         ` [Cluster-devel] " Christoph Hellwig
2022-12-25  9:12         ` Matthew Wilcox
2022-12-25  9:12           ` [Cluster-devel] " Matthew Wilcox
2022-12-28 15:55           ` Christoph Hellwig
2022-12-28 15:55             ` [Cluster-devel] " Christoph Hellwig
2022-12-16 15:06 ` [RFC v3 5/7] iomap: Get page in page_prepare handler Andreas Gruenbacher
2022-12-16 15:06   ` [Cluster-devel] " Andreas Gruenbacher
2022-12-16 16:30   ` Matthew Wilcox
2022-12-16 16:30     ` [Cluster-devel] " Matthew Wilcox
2022-12-16 17:15     ` Andreas Gruenbacher
2022-12-16 17:15       ` [Cluster-devel] " Andreas Gruenbacher
2022-12-23 15:07   ` Christoph Hellwig
2022-12-23 15:07     ` [Cluster-devel] " Christoph Hellwig
2022-12-16 15:06 ` [RFC v3 6/7] iomap/xfs: Eliminate the iomap_valid handler Andreas Gruenbacher
2022-12-16 15:06   ` [Cluster-devel] " Andreas Gruenbacher
2022-12-23 15:10   ` Christoph Hellwig
2022-12-23 15:10     ` [Cluster-devel] " Christoph Hellwig
2022-12-16 15:06 ` [RFC v3 7/7] iomap: Rename page_ops to folio_ops Andreas Gruenbacher
2022-12-16 15:06   ` [Cluster-devel] " Andreas Gruenbacher
2022-12-18 22:10 ` [RFC v4 0/7] Turn iomap_page_ops into iomap_folio_ops Andreas Gruenbacher
2022-12-18 22:10   ` [Cluster-devel] " Andreas Gruenbacher
2022-12-18 22:10 ` [RFC v4 1/7] fs: Add folio_may_straddle_isize helper Andreas Gruenbacher
2022-12-18 22:10   ` [Cluster-devel] " Andreas Gruenbacher
2022-12-18 22:10 ` [RFC v4 2/7] iomap: Add iomap_folio_done helper Andreas Gruenbacher
2022-12-18 22:10   ` [Cluster-devel] " Andreas Gruenbacher
2022-12-18 22:10 ` Andreas Gruenbacher [this message]
2022-12-18 22:10   ` [Cluster-devel] [RFC v4 3/7] iomap/gfs2: Unlock and put folio in page_done handler Andreas Gruenbacher
2022-12-18 22:10 ` [RFC v4 4/7] iomap: Add iomap_folio_prepare helper Andreas Gruenbacher
2022-12-18 22:10   ` [Cluster-devel] " Andreas Gruenbacher
2022-12-18 22:10 ` [RFC v4 5/7] iomap/gfs2: Get page in page_prepare handler Andreas Gruenbacher
2022-12-18 22:10   ` [Cluster-devel] " Andreas Gruenbacher
2022-12-18 22:10 ` [RFC v4 6/7] iomap/xfs: Eliminate the iomap_valid handler Andreas Gruenbacher
2022-12-18 22:10   ` [Cluster-devel] " Andreas Gruenbacher
2022-12-18 22:10 ` [RFC v4 7/7] iomap: Rename page_ops to folio_ops Andreas Gruenbacher
2022-12-18 22:10   ` [Cluster-devel] " Andreas Gruenbacher

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20221218221054.3946886-4-agruenba@redhat.com \
    --to=agruenba@redhat.com \
    --cc=cluster-devel@redhat.com \
    --cc=djwong@kernel.org \
    --cc=hch@infradead.org \
    --cc=linux-ext4@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-xfs@vger.kernel.org \
    --cc=viro@zeniv.linux.org.uk \
    --cc=willy@infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.