linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* lift the xfs writepage code into iomap
@ 2019-06-24  5:52 Christoph Hellwig
  2019-06-24  5:52 ` [PATCH 01/12] list.h: add a list_pop helper Christoph Hellwig
                   ` (11 more replies)
  0 siblings, 12 replies; 52+ messages in thread
From: Christoph Hellwig @ 2019-06-24  5:52 UTC (permalink / raw)
  To: Darrick J . Wong
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel

Hi all,

this series cleans up the xfs writepage code and then lifts it to
fs/iomap.c so that it could be use by other file system.  I've been
wanting to this for a while so that I could eventually convert gfs2
over to it, but I never got to it.  Now Damien has a new zonefs
file system for semi-raw access to zoned block devices that would
like to use the iomap code instead of reinventing it, so I finally
had to do the work.

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

* [PATCH 01/12] list.h: add a list_pop helper
  2019-06-24  5:52 lift the xfs writepage code into iomap Christoph Hellwig
@ 2019-06-24  5:52 ` Christoph Hellwig
  2019-06-24 14:53   ` Darrick J. Wong
  2019-06-24 15:51   ` Matthew Wilcox
  2019-06-24  5:52 ` [PATCH 02/12] xfs: simplify xfs_chain_bio Christoph Hellwig
                   ` (10 subsequent siblings)
  11 siblings, 2 replies; 52+ messages in thread
From: Christoph Hellwig @ 2019-06-24  5:52 UTC (permalink / raw)
  To: Darrick J . Wong
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel

We have a very common pattern where we want to delete the first entry
from a list and return it as the properly typed container structure.

Add a list_pop helper to implement this behavior.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 include/linux/list.h | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/include/linux/list.h b/include/linux/list.h
index e951228db4b2..e07a5f54cc9d 100644
--- a/include/linux/list.h
+++ b/include/linux/list.h
@@ -500,6 +500,28 @@ static inline void list_splice_tail_init(struct list_head *list,
 	pos__ != head__ ? list_entry(pos__, type, member) : NULL; \
 })
 
+/**
+ * list_pop - delete the first entry from a list and return it
+ * @list:	the list to take the element from.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_head within the struct.
+ *
+ * Note that if the list is empty, it returns NULL.
+ */
+#define list_pop(list, type, member) 				\
+({								\
+	struct list_head *head__ = (list);			\
+	struct list_head *pos__ = READ_ONCE(head__->next);	\
+	type *entry__ = NULL;					\
+								\
+	if (pos__ != head__) {					\
+		entry__ = list_entry(pos__, type, member);	\
+		list_del(pos__);				\
+	}							\
+								\
+	entry__;						\
+})
+
 /**
  * list_next_entry - get the next element in list
  * @pos:	the type * to cursor
-- 
2.20.1


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

* [PATCH 02/12] xfs: simplify xfs_chain_bio
  2019-06-24  5:52 lift the xfs writepage code into iomap Christoph Hellwig
  2019-06-24  5:52 ` [PATCH 01/12] list.h: add a list_pop helper Christoph Hellwig
@ 2019-06-24  5:52 ` Christoph Hellwig
  2019-06-24 15:14   ` Darrick J. Wong
  2019-06-24  5:52 ` [PATCH 03/12] xfs: fix a comment typo in xfs_submit_ioend Christoph Hellwig
                   ` (9 subsequent siblings)
  11 siblings, 1 reply; 52+ messages in thread
From: Christoph Hellwig @ 2019-06-24  5:52 UTC (permalink / raw)
  To: Darrick J . Wong
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel

Move setting up operation and write hint to xfs_alloc_ioend, and
then just copy over all needed information from the previous bio
in xfs_chain_bio and stop passing various parameters to it.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/xfs_aops.c | 35 +++++++++++++++++------------------
 1 file changed, 17 insertions(+), 18 deletions(-)

diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index a6f0f4761a37..9cceb90e77c5 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -665,7 +665,6 @@ xfs_submit_ioend(
 
 	ioend->io_bio->bi_private = ioend;
 	ioend->io_bio->bi_end_io = xfs_end_bio;
-	ioend->io_bio->bi_opf = REQ_OP_WRITE | wbc_to_write_flags(wbc);
 
 	/*
 	 * If we are failing the IO now, just mark the ioend with an
@@ -679,7 +678,6 @@ xfs_submit_ioend(
 		return status;
 	}
 
-	ioend->io_bio->bi_write_hint = ioend->io_inode->i_write_hint;
 	submit_bio(ioend->io_bio);
 	return 0;
 }
@@ -691,7 +689,8 @@ xfs_alloc_ioend(
 	xfs_exntst_t		state,
 	xfs_off_t		offset,
 	struct block_device	*bdev,
-	sector_t		sector)
+	sector_t		sector,
+	struct writeback_control *wbc)
 {
 	struct xfs_ioend	*ioend;
 	struct bio		*bio;
@@ -699,6 +698,8 @@ xfs_alloc_ioend(
 	bio = bio_alloc_bioset(GFP_NOFS, BIO_MAX_PAGES, &xfs_ioend_bioset);
 	bio_set_dev(bio, bdev);
 	bio->bi_iter.bi_sector = sector;
+	bio->bi_opf = REQ_OP_WRITE | wbc_to_write_flags(wbc);
+	bio->bi_write_hint = inode->i_write_hint;
 
 	ioend = container_of(bio, struct xfs_ioend, io_inline_bio);
 	INIT_LIST_HEAD(&ioend->io_list);
@@ -719,24 +720,22 @@ xfs_alloc_ioend(
  * so that the bi_private linkage is set up in the right direction for the
  * traversal in xfs_destroy_ioend().
  */
-static void
+static struct bio *
 xfs_chain_bio(
-	struct xfs_ioend	*ioend,
-	struct writeback_control *wbc,
-	struct block_device	*bdev,
-	sector_t		sector)
+	struct bio		*prev)
 {
 	struct bio *new;
 
 	new = bio_alloc(GFP_NOFS, BIO_MAX_PAGES);
-	bio_set_dev(new, bdev);
-	new->bi_iter.bi_sector = sector;
-	bio_chain(ioend->io_bio, new);
-	bio_get(ioend->io_bio);		/* for xfs_destroy_ioend */
-	ioend->io_bio->bi_opf = REQ_OP_WRITE | wbc_to_write_flags(wbc);
-	ioend->io_bio->bi_write_hint = ioend->io_inode->i_write_hint;
-	submit_bio(ioend->io_bio);
-	ioend->io_bio = new;
+	bio_copy_dev(new, prev);
+	new->bi_iter.bi_sector = bio_end_sector(prev);
+	new->bi_opf = prev->bi_opf;
+	new->bi_write_hint = prev->bi_write_hint;
+
+	bio_chain(prev, new);
+	bio_get(prev);		/* for xfs_destroy_ioend */
+	submit_bio(prev);
+	return new;
 }
 
 /*
@@ -771,14 +770,14 @@ xfs_add_to_ioend(
 		if (wpc->ioend)
 			list_add(&wpc->ioend->io_list, iolist);
 		wpc->ioend = xfs_alloc_ioend(inode, wpc->fork,
-				wpc->imap.br_state, offset, bdev, sector);
+				wpc->imap.br_state, offset, bdev, sector, wbc);
 	}
 
 	if (!__bio_try_merge_page(wpc->ioend->io_bio, page, len, poff, true)) {
 		if (iop)
 			atomic_inc(&iop->write_count);
 		if (bio_full(wpc->ioend->io_bio))
-			xfs_chain_bio(wpc->ioend, wbc, bdev, sector);
+			wpc->ioend->io_bio = xfs_chain_bio(wpc->ioend->io_bio);
 		bio_add_page(wpc->ioend->io_bio, page, len, poff);
 	}
 
-- 
2.20.1


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

* [PATCH 03/12] xfs: fix a comment typo in xfs_submit_ioend
  2019-06-24  5:52 lift the xfs writepage code into iomap Christoph Hellwig
  2019-06-24  5:52 ` [PATCH 01/12] list.h: add a list_pop helper Christoph Hellwig
  2019-06-24  5:52 ` [PATCH 02/12] xfs: simplify xfs_chain_bio Christoph Hellwig
@ 2019-06-24  5:52 ` Christoph Hellwig
  2019-06-24 14:53   ` Darrick J. Wong
  2019-06-24  5:52 ` [PATCH 04/12] xfs: initialize ioma->flags in xfs_bmbt_to_iomap Christoph Hellwig
                   ` (8 subsequent siblings)
  11 siblings, 1 reply; 52+ messages in thread
From: Christoph Hellwig @ 2019-06-24  5:52 UTC (permalink / raw)
  To: Darrick J . Wong
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel

The fail argument is long gone, update the comment.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/xfs_aops.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index 9cceb90e77c5..dc60aec0c5a7 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -626,7 +626,7 @@ xfs_map_blocks(
  * reference to the ioend to ensure that the ioend completion is only done once
  * all bios have been submitted and the ioend is really done.
  *
- * If @fail is non-zero, it means that we have a situation where some part of
+ * If @status is non-zero, it means that we have a situation where some part of
  * the submission process has failed after we have marked paged for writeback
  * and unlocked them. In this situation, we need to fail the bio and ioend
  * rather than submit it to IO. This typically only happens on a filesystem
-- 
2.20.1


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

* [PATCH 04/12] xfs: initialize ioma->flags in xfs_bmbt_to_iomap
  2019-06-24  5:52 lift the xfs writepage code into iomap Christoph Hellwig
                   ` (2 preceding siblings ...)
  2019-06-24  5:52 ` [PATCH 03/12] xfs: fix a comment typo in xfs_submit_ioend Christoph Hellwig
@ 2019-06-24  5:52 ` Christoph Hellwig
  2019-06-24 14:57   ` Darrick J. Wong
  2019-06-24  5:52 ` [PATCH 05/12] xfs: use a struct iomap in xfs_writepage_ctx Christoph Hellwig
                   ` (7 subsequent siblings)
  11 siblings, 1 reply; 52+ messages in thread
From: Christoph Hellwig @ 2019-06-24  5:52 UTC (permalink / raw)
  To: Darrick J . Wong
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel

Currently we don't overwrite the flags field in the iomap in
xfs_bmbt_to_iomap.  This works fine with 0-initialized iomaps on stack,
but is harmful once we want to be able to reuse an iomap in the
writeback code.  Replace the shared paramter with a set of initial
flags an thus ensures the flags field is always reinitialized.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/xfs_iomap.c | 28 +++++++++++++++++-----------
 fs/xfs/xfs_iomap.h |  2 +-
 fs/xfs/xfs_pnfs.c  |  2 +-
 3 files changed, 19 insertions(+), 13 deletions(-)

diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index 63d323916bba..6b29452bfba0 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -57,7 +57,7 @@ xfs_bmbt_to_iomap(
 	struct xfs_inode	*ip,
 	struct iomap		*iomap,
 	struct xfs_bmbt_irec	*imap,
-	bool			shared)
+	u16			flags)
 {
 	struct xfs_mount	*mp = ip->i_mount;
 
@@ -82,12 +82,11 @@ xfs_bmbt_to_iomap(
 	iomap->length = XFS_FSB_TO_B(mp, imap->br_blockcount);
 	iomap->bdev = xfs_find_bdev_for_inode(VFS_I(ip));
 	iomap->dax_dev = xfs_find_daxdev_for_inode(VFS_I(ip));
+	iomap->flags = flags;
 
 	if (xfs_ipincount(ip) &&
 	    (ip->i_itemp->ili_fsync_fields & ~XFS_ILOG_TIMESTAMP))
 		iomap->flags |= IOMAP_F_DIRTY;
-	if (shared)
-		iomap->flags |= IOMAP_F_SHARED;
 	return 0;
 }
 
@@ -543,6 +542,7 @@ xfs_file_iomap_begin_delay(
 	struct xfs_iext_cursor	icur, ccur;
 	xfs_fsblock_t		prealloc_blocks = 0;
 	bool			eof = false, cow_eof = false, shared = false;
+	u16			iomap_flags = 0;
 	int			whichfork = XFS_DATA_FORK;
 	int			error = 0;
 
@@ -710,7 +710,7 @@ xfs_file_iomap_begin_delay(
 	 * Flag newly allocated delalloc blocks with IOMAP_F_NEW so we punch
 	 * them out if the write happens to fail.
 	 */
-	iomap->flags |= IOMAP_F_NEW;
+	iomap_flags |= IOMAP_F_NEW;
 	trace_xfs_iomap_alloc(ip, offset, count, whichfork,
 			whichfork == XFS_DATA_FORK ? &imap : &cmap);
 done:
@@ -718,14 +718,17 @@ xfs_file_iomap_begin_delay(
 		if (imap.br_startoff > offset_fsb) {
 			xfs_trim_extent(&cmap, offset_fsb,
 					imap.br_startoff - offset_fsb);
-			error = xfs_bmbt_to_iomap(ip, iomap, &cmap, true);
+			error = xfs_bmbt_to_iomap(ip, iomap, &cmap,
+					IOMAP_F_SHARED);
 			goto out_unlock;
 		}
 		/* ensure we only report blocks we have a reservation for */
 		xfs_trim_extent(&imap, cmap.br_startoff, cmap.br_blockcount);
 		shared = true;
 	}
-	error = xfs_bmbt_to_iomap(ip, iomap, &imap, shared);
+	if (shared)
+		iomap_flags |= IOMAP_F_SHARED;
+	error = xfs_bmbt_to_iomap(ip, iomap, &imap, iomap_flags);
 out_unlock:
 	xfs_iunlock(ip, XFS_ILOCK_EXCL);
 	return error;
@@ -933,6 +936,7 @@ xfs_file_iomap_begin(
 	xfs_fileoff_t		offset_fsb, end_fsb;
 	int			nimaps = 1, error = 0;
 	bool			shared = false;
+	u16			iomap_flags = 0;
 	unsigned		lockmode;
 
 	if (XFS_FORCED_SHUTDOWN(mp))
@@ -1048,11 +1052,13 @@ xfs_file_iomap_begin(
 	if (error)
 		return error;
 
-	iomap->flags |= IOMAP_F_NEW;
+	iomap_flags |= IOMAP_F_NEW;
 	trace_xfs_iomap_alloc(ip, offset, length, XFS_DATA_FORK, &imap);
 
 out_finish:
-	return xfs_bmbt_to_iomap(ip, iomap, &imap, shared);
+	if (shared)
+		iomap_flags |= IOMAP_F_SHARED;
+	return xfs_bmbt_to_iomap(ip, iomap, &imap, iomap_flags);
 
 out_found:
 	ASSERT(nimaps);
@@ -1196,7 +1202,7 @@ xfs_seek_iomap_begin(
 		if (data_fsb < cow_fsb + cmap.br_blockcount)
 			end_fsb = min(end_fsb, data_fsb);
 		xfs_trim_extent(&cmap, offset_fsb, end_fsb);
-		error = xfs_bmbt_to_iomap(ip, iomap, &cmap, true);
+		error = xfs_bmbt_to_iomap(ip, iomap, &cmap, IOMAP_F_SHARED);
 		/*
 		 * This is a COW extent, so we must probe the page cache
 		 * because there could be dirty page cache being backed
@@ -1218,7 +1224,7 @@ xfs_seek_iomap_begin(
 	imap.br_state = XFS_EXT_NORM;
 done:
 	xfs_trim_extent(&imap, offset_fsb, end_fsb);
-	error = xfs_bmbt_to_iomap(ip, iomap, &imap, false);
+	error = xfs_bmbt_to_iomap(ip, iomap, &imap, 0);
 out_unlock:
 	xfs_iunlock(ip, lockmode);
 	return error;
@@ -1264,7 +1270,7 @@ xfs_xattr_iomap_begin(
 	if (error)
 		return error;
 	ASSERT(nimaps);
-	return xfs_bmbt_to_iomap(ip, iomap, &imap, false);
+	return xfs_bmbt_to_iomap(ip, iomap, &imap, 0);
 }
 
 const struct iomap_ops xfs_xattr_iomap_ops = {
diff --git a/fs/xfs/xfs_iomap.h b/fs/xfs/xfs_iomap.h
index 5c2f6aa6d78f..71d0ae460c44 100644
--- a/fs/xfs/xfs_iomap.h
+++ b/fs/xfs/xfs_iomap.h
@@ -16,7 +16,7 @@ int xfs_iomap_write_direct(struct xfs_inode *, xfs_off_t, size_t,
 int xfs_iomap_write_unwritten(struct xfs_inode *, xfs_off_t, xfs_off_t, bool);
 
 int xfs_bmbt_to_iomap(struct xfs_inode *, struct iomap *,
-		struct xfs_bmbt_irec *, bool shared);
+		struct xfs_bmbt_irec *, u16);
 xfs_extlen_t xfs_eof_alignment(struct xfs_inode *ip, xfs_extlen_t extsize);
 
 static inline xfs_filblks_t
diff --git a/fs/xfs/xfs_pnfs.c b/fs/xfs/xfs_pnfs.c
index bde2c9f56a46..12f664785248 100644
--- a/fs/xfs/xfs_pnfs.c
+++ b/fs/xfs/xfs_pnfs.c
@@ -185,7 +185,7 @@ xfs_fs_map_blocks(
 	}
 	xfs_iunlock(ip, XFS_IOLOCK_EXCL);
 
-	error = xfs_bmbt_to_iomap(ip, iomap, &imap, false);
+	error = xfs_bmbt_to_iomap(ip, iomap, &imap, 0);
 	*device_generation = mp->m_generation;
 	return error;
 out_unlock:
-- 
2.20.1


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

* [PATCH 05/12] xfs: use a struct iomap in xfs_writepage_ctx
  2019-06-24  5:52 lift the xfs writepage code into iomap Christoph Hellwig
                   ` (3 preceding siblings ...)
  2019-06-24  5:52 ` [PATCH 04/12] xfs: initialize ioma->flags in xfs_bmbt_to_iomap Christoph Hellwig
@ 2019-06-24  5:52 ` Christoph Hellwig
  2019-06-24 15:50   ` Darrick J. Wong
  2019-06-24  5:52 ` [PATCH 06/12] xfs: remove XFS_TRANS_NOFS Christoph Hellwig
                   ` (6 subsequent siblings)
  11 siblings, 1 reply; 52+ messages in thread
From: Christoph Hellwig @ 2019-06-24  5:52 UTC (permalink / raw)
  To: Darrick J . Wong
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel

In preparation for moving the XFS writeback code to fs/iomap.c, switch
it to use struct iomap instead of the XFS-specific struct xfs_bmbt_irec.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/libxfs/xfs_bmap.c | 14 +++++--
 fs/xfs/libxfs/xfs_bmap.h |  3 +-
 fs/xfs/xfs_aops.c        | 80 +++++++++++++++++++---------------------
 fs/xfs/xfs_aops.h        |  2 +-
 4 files changed, 50 insertions(+), 49 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
index 4133bc461e3e..de35a0376156 100644
--- a/fs/xfs/libxfs/xfs_bmap.c
+++ b/fs/xfs/libxfs/xfs_bmap.c
@@ -39,6 +39,7 @@
 #include "xfs_ag_resv.h"
 #include "xfs_refcount.h"
 #include "xfs_icache.h"
+#include "xfs_iomap.h"
 
 
 kmem_zone_t		*xfs_bmap_free_item_zone;
@@ -4457,16 +4458,21 @@ int
 xfs_bmapi_convert_delalloc(
 	struct xfs_inode	*ip,
 	int			whichfork,
-	xfs_fileoff_t		offset_fsb,
-	struct xfs_bmbt_irec	*imap,
+	xfs_off_t		offset,
+	struct iomap		*iomap,
 	unsigned int		*seq)
 {
 	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);
 	struct xfs_mount	*mp = ip->i_mount;
+	xfs_fileoff_t		offset_fsb = XFS_B_TO_FSBT(mp, offset);
 	struct xfs_bmalloca	bma = { NULL };
+	u16			flags = 0;
 	struct xfs_trans	*tp;
 	int			error;
 
+	if (whichfork == XFS_COW_FORK)
+		flags |= IOMAP_F_SHARED;
+
 	/*
 	 * Space for the extent and indirect blocks was reserved when the
 	 * delalloc extent was created so there's no need to do so here.
@@ -4496,7 +4502,7 @@ xfs_bmapi_convert_delalloc(
 	 * the extent.  Just return the real extent at this offset.
 	 */
 	if (!isnullstartblock(bma.got.br_startblock)) {
-		*imap = bma.got;
+		xfs_bmbt_to_iomap(ip, iomap, &bma.got, flags);
 		*seq = READ_ONCE(ifp->if_seq);
 		goto out_trans_cancel;
 	}
@@ -4529,7 +4535,7 @@ xfs_bmapi_convert_delalloc(
 	XFS_STATS_INC(mp, xs_xstrat_quick);
 
 	ASSERT(!isnullstartblock(bma.got.br_startblock));
-	*imap = bma.got;
+	xfs_bmbt_to_iomap(ip, iomap, &bma.got, flags);
 	*seq = READ_ONCE(ifp->if_seq);
 
 	if (whichfork == XFS_COW_FORK) {
diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h
index 8f597f9abdbe..3c3470f11648 100644
--- a/fs/xfs/libxfs/xfs_bmap.h
+++ b/fs/xfs/libxfs/xfs_bmap.h
@@ -220,8 +220,7 @@ int	xfs_bmapi_reserve_delalloc(struct xfs_inode *ip, int whichfork,
 		struct xfs_bmbt_irec *got, struct xfs_iext_cursor *cur,
 		int eof);
 int	xfs_bmapi_convert_delalloc(struct xfs_inode *ip, int whichfork,
-		xfs_fileoff_t offset_fsb, struct xfs_bmbt_irec *imap,
-		unsigned int *seq);
+		xfs_off_t offset, struct iomap *iomap, unsigned int *seq);
 int	xfs_bmap_add_extent_unwritten_real(struct xfs_trans *tp,
 		struct xfs_inode *ip, int whichfork,
 		struct xfs_iext_cursor *icur, struct xfs_btree_cur **curp,
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index dc60aec0c5a7..93a760f13017 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -27,7 +27,7 @@
  * structure owned by writepages passed to individual writepage calls
  */
 struct xfs_writepage_ctx {
-	struct xfs_bmbt_irec    imap;
+	struct iomap		iomap;
 	int			fork;
 	unsigned int		data_seq;
 	unsigned int		cow_seq;
@@ -265,7 +265,7 @@ xfs_end_ioend(
 	 */
 	if (ioend->io_fork == XFS_COW_FORK)
 		error = xfs_reflink_end_cow(ip, offset, size);
-	else if (ioend->io_state == XFS_EXT_UNWRITTEN)
+	else if (ioend->io_type == IOMAP_UNWRITTEN)
 		error = xfs_iomap_write_unwritten(ip, offset, size, false);
 	else
 		ASSERT(!xfs_ioend_is_append(ioend) || ioend->io_append_trans);
@@ -300,8 +300,8 @@ xfs_ioend_can_merge(
 		return false;
 	if ((ioend->io_fork == XFS_COW_FORK) ^ (next->io_fork == XFS_COW_FORK))
 		return false;
-	if ((ioend->io_state == XFS_EXT_UNWRITTEN) ^
-	    (next->io_state == XFS_EXT_UNWRITTEN))
+	if ((ioend->io_type == IOMAP_UNWRITTEN) ^
+	    (next->io_type == IOMAP_UNWRITTEN))
 		return false;
 	if (ioend->io_offset + ioend->io_size != next->io_offset)
 		return false;
@@ -395,7 +395,7 @@ xfs_end_bio(
 	unsigned long		flags;
 
 	if (ioend->io_fork == XFS_COW_FORK ||
-	    ioend->io_state == XFS_EXT_UNWRITTEN ||
+	    ioend->io_type == IOMAP_UNWRITTEN ||
 	    ioend->io_append_trans != NULL) {
 		spin_lock_irqsave(&ip->i_ioend_lock, flags);
 		if (list_empty(&ip->i_ioend_list))
@@ -415,10 +415,10 @@ static bool
 xfs_imap_valid(
 	struct xfs_writepage_ctx	*wpc,
 	struct xfs_inode		*ip,
-	xfs_fileoff_t			offset_fsb)
+	loff_t				offset)
 {
-	if (offset_fsb < wpc->imap.br_startoff ||
-	    offset_fsb >= wpc->imap.br_startoff + wpc->imap.br_blockcount)
+	if (offset < wpc->iomap.offset ||
+	    offset >= wpc->iomap.offset + wpc->iomap.length)
 		return false;
 	/*
 	 * If this is a COW mapping, it is sufficient to check that the mapping
@@ -445,7 +445,7 @@ xfs_imap_valid(
 
 /*
  * Pass in a dellalloc extent and convert it to real extents, return the real
- * extent that maps offset_fsb in wpc->imap.
+ * extent that maps offset_fsb in wpc->iomap.
  *
  * The current page is held locked so nothing could have removed the block
  * backing offset_fsb, although it could have moved from the COW to the data
@@ -455,23 +455,23 @@ static int
 xfs_convert_blocks(
 	struct xfs_writepage_ctx *wpc,
 	struct xfs_inode	*ip,
-	xfs_fileoff_t		offset_fsb)
+	loff_t			offset)
 {
 	int			error;
 
 	/*
-	 * Attempt to allocate whatever delalloc extent currently backs
-	 * offset_fsb and put the result into wpc->imap.  Allocate in a loop
-	 * because it may take several attempts to allocate real blocks for a
-	 * contiguous delalloc extent if free space is sufficiently fragmented.
+	 * Attempt to allocate whatever delalloc extent currently backs offset
+	 * and put the result into wpc->imap.  Allocate in a loop because it may
+	 * take several attempts to allocate real blocks for a contiguous
+	 * delalloc extent if free space is sufficiently fragmented.
 	 */
 	do {
-		error = xfs_bmapi_convert_delalloc(ip, wpc->fork, offset_fsb,
-				&wpc->imap, wpc->fork == XFS_COW_FORK ?
+		error = xfs_bmapi_convert_delalloc(ip, wpc->fork, offset,
+				&wpc->iomap, wpc->fork == XFS_COW_FORK ?
 					&wpc->cow_seq : &wpc->data_seq);
 		if (error)
 			return error;
-	} while (wpc->imap.br_startoff + wpc->imap.br_blockcount <= offset_fsb);
+	} while (wpc->iomap.offset + wpc->iomap.length <= offset);
 
 	return 0;
 }
@@ -511,7 +511,7 @@ xfs_map_blocks(
 	 * against concurrent updates and provides a memory barrier on the way
 	 * out that ensures that we always see the current value.
 	 */
-	if (xfs_imap_valid(wpc, ip, offset_fsb))
+	if (xfs_imap_valid(wpc, ip, offset))
 		return 0;
 
 	/*
@@ -544,7 +544,7 @@ xfs_map_blocks(
 	 * No COW extent overlap. Revalidate now that we may have updated
 	 * ->cow_seq. If the data mapping is still valid, we're done.
 	 */
-	if (xfs_imap_valid(wpc, ip, offset_fsb)) {
+	if (xfs_imap_valid(wpc, ip, offset)) {
 		xfs_iunlock(ip, XFS_ILOCK_SHARED);
 		return 0;
 	}
@@ -584,11 +584,11 @@ xfs_map_blocks(
 	    isnullstartblock(imap.br_startblock))
 		goto allocate_blocks;
 
-	wpc->imap = imap;
+	xfs_bmbt_to_iomap(ip, &wpc->iomap, &imap, 0);
 	trace_xfs_map_blocks_found(ip, offset, count, wpc->fork, &imap);
 	return 0;
 allocate_blocks:
-	error = xfs_convert_blocks(wpc, ip, offset_fsb);
+	error = xfs_convert_blocks(wpc, ip, offset);
 	if (error) {
 		/*
 		 * If we failed to find the extent in the COW fork we might have
@@ -608,12 +608,15 @@ xfs_map_blocks(
 	 * original delalloc one.  Trim the return extent to the next COW
 	 * boundary again to force a re-lookup.
 	 */
-	if (wpc->fork != XFS_COW_FORK && cow_fsb != NULLFILEOFF &&
-	    cow_fsb < wpc->imap.br_startoff + wpc->imap.br_blockcount)
-		wpc->imap.br_blockcount = cow_fsb - wpc->imap.br_startoff;
+	if (wpc->fork != XFS_COW_FORK && cow_fsb != NULLFILEOFF) {
+		loff_t		cow_offset = XFS_FSB_TO_B(mp, cow_fsb);
+
+		if (cow_offset < wpc->iomap.offset + wpc->iomap.length)
+			wpc->iomap.length = cow_offset - wpc->iomap.offset;
+	}
 
-	ASSERT(wpc->imap.br_startoff <= offset_fsb);
-	ASSERT(wpc->imap.br_startoff + wpc->imap.br_blockcount > offset_fsb);
+	ASSERT(wpc->iomap.offset <= offset);
+	ASSERT(wpc->iomap.offset + wpc->iomap.length > offset);
 	trace_xfs_map_blocks_alloc(ip, offset, count, wpc->fork, &imap);
 	return 0;
 }
@@ -658,7 +661,7 @@ xfs_submit_ioend(
 	/* Reserve log space if we might write beyond the on-disk inode size. */
 	if (!status &&
 	    (ioend->io_fork == XFS_COW_FORK ||
-	     ioend->io_state != XFS_EXT_UNWRITTEN) &&
+	     ioend->io_type != IOMAP_UNWRITTEN) &&
 	    xfs_ioend_is_append(ioend) &&
 	    !ioend->io_append_trans)
 		status = xfs_setfilesize_trans_alloc(ioend);
@@ -685,10 +688,8 @@ xfs_submit_ioend(
 static struct xfs_ioend *
 xfs_alloc_ioend(
 	struct inode		*inode,
-	int			fork,
-	xfs_exntst_t		state,
+	struct xfs_writepage_ctx *wpc,
 	xfs_off_t		offset,
-	struct block_device	*bdev,
 	sector_t		sector,
 	struct writeback_control *wbc)
 {
@@ -696,15 +697,15 @@ xfs_alloc_ioend(
 	struct bio		*bio;
 
 	bio = bio_alloc_bioset(GFP_NOFS, BIO_MAX_PAGES, &xfs_ioend_bioset);
-	bio_set_dev(bio, bdev);
+	bio_set_dev(bio, wpc->iomap.bdev);
 	bio->bi_iter.bi_sector = sector;
 	bio->bi_opf = REQ_OP_WRITE | wbc_to_write_flags(wbc);
 	bio->bi_write_hint = inode->i_write_hint;
 
 	ioend = container_of(bio, struct xfs_ioend, io_inline_bio);
 	INIT_LIST_HEAD(&ioend->io_list);
-	ioend->io_fork = fork;
-	ioend->io_state = state;
+	ioend->io_fork = wpc->fork;
+	ioend->io_type = wpc->iomap.type;
 	ioend->io_inode = inode;
 	ioend->io_size = 0;
 	ioend->io_offset = offset;
@@ -752,25 +753,20 @@ xfs_add_to_ioend(
 	struct writeback_control *wbc,
 	struct list_head	*iolist)
 {
-	struct xfs_inode	*ip = XFS_I(inode);
-	struct xfs_mount	*mp = ip->i_mount;
-	struct block_device	*bdev = xfs_find_bdev_for_inode(inode);
 	unsigned		len = i_blocksize(inode);
 	unsigned		poff = offset & (PAGE_SIZE - 1);
 	sector_t		sector;
 
-	sector = xfs_fsb_to_db(ip, wpc->imap.br_startblock) +
-		((offset - XFS_FSB_TO_B(mp, wpc->imap.br_startoff)) >> 9);
+	sector = (wpc->iomap.addr + offset - wpc->iomap.offset) >> 9;
 
 	if (!wpc->ioend ||
 	    wpc->fork != wpc->ioend->io_fork ||
-	    wpc->imap.br_state != wpc->ioend->io_state ||
+	    wpc->iomap.type != wpc->ioend->io_type ||
 	    sector != bio_end_sector(wpc->ioend->io_bio) ||
 	    offset != wpc->ioend->io_offset + wpc->ioend->io_size) {
 		if (wpc->ioend)
 			list_add(&wpc->ioend->io_list, iolist);
-		wpc->ioend = xfs_alloc_ioend(inode, wpc->fork,
-				wpc->imap.br_state, offset, bdev, sector, wbc);
+		wpc->ioend = xfs_alloc_ioend(inode, wpc, offset, sector, wbc);
 	}
 
 	if (!__bio_try_merge_page(wpc->ioend->io_bio, page, len, poff, true)) {
@@ -879,7 +875,7 @@ xfs_writepage_map(
 		error = xfs_map_blocks(wpc, inode, file_offset);
 		if (error)
 			break;
-		if (wpc->imap.br_startblock == HOLESTARTBLOCK)
+		if (wpc->iomap.type == IOMAP_HOLE)
 			continue;
 		xfs_add_to_ioend(inode, file_offset, page, iop, wpc, wbc,
 				 &submit_list);
diff --git a/fs/xfs/xfs_aops.h b/fs/xfs/xfs_aops.h
index f62b03186c62..72e30d1c3bdf 100644
--- a/fs/xfs/xfs_aops.h
+++ b/fs/xfs/xfs_aops.h
@@ -14,7 +14,7 @@ extern struct bio_set xfs_ioend_bioset;
 struct xfs_ioend {
 	struct list_head	io_list;	/* next ioend in chain */
 	int			io_fork;	/* inode fork written back */
-	xfs_exntst_t		io_state;	/* extent state */
+	u16			io_type;
 	struct inode		*io_inode;	/* file being written to */
 	size_t			io_size;	/* size of the extent */
 	xfs_off_t		io_offset;	/* offset in the file */
-- 
2.20.1


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

* [PATCH 06/12] xfs: remove XFS_TRANS_NOFS
  2019-06-24  5:52 lift the xfs writepage code into iomap Christoph Hellwig
                   ` (4 preceding siblings ...)
  2019-06-24  5:52 ` [PATCH 05/12] xfs: use a struct iomap in xfs_writepage_ctx Christoph Hellwig
@ 2019-06-24  5:52 ` Christoph Hellwig
  2019-06-24 15:58   ` Darrick J. Wong
  2019-06-24 22:59   ` Dave Chinner
  2019-06-24  5:52 ` [PATCH 07/12] xfs: don't preallocate a transaction for file size updates Christoph Hellwig
                   ` (5 subsequent siblings)
  11 siblings, 2 replies; 52+ messages in thread
From: Christoph Hellwig @ 2019-06-24  5:52 UTC (permalink / raw)
  To: Darrick J . Wong
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel

Instead of a magic flag for xfs_trans_alloc, just ensure all callers
that can't relclaim through the file system use memalloc_nofs_save to
set the per-task nofs flag.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/libxfs/xfs_shared.h |  1 -
 fs/xfs/xfs_aops.c          | 12 +++++++++---
 fs/xfs/xfs_file.c          | 12 +++++++++---
 fs/xfs/xfs_iomap.c         |  2 +-
 fs/xfs/xfs_reflink.c       |  4 ++--
 fs/xfs/xfs_trans.c         |  4 +---
 6 files changed, 22 insertions(+), 13 deletions(-)

diff --git a/fs/xfs/libxfs/xfs_shared.h b/fs/xfs/libxfs/xfs_shared.h
index 4e909791aeac..1f2b5a0c71b4 100644
--- a/fs/xfs/libxfs/xfs_shared.h
+++ b/fs/xfs/libxfs/xfs_shared.h
@@ -65,7 +65,6 @@ void	xfs_log_get_max_trans_res(struct xfs_mount *mp,
 #define XFS_TRANS_DQ_DIRTY	0x10	/* at least one dquot in trx dirty */
 #define XFS_TRANS_RESERVE	0x20    /* OK to use reserved data blocks */
 #define XFS_TRANS_NO_WRITECOUNT 0x40	/* do not elevate SB writecount */
-#define XFS_TRANS_NOFS		0x80	/* pass KM_NOFS to kmem_alloc */
 /*
  * LOWMODE is used by the allocator to activate the lowspace algorithm - when
  * free space is running low the extent allocator may choose to allocate an
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index 93a760f13017..633baaaff7ae 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -138,8 +138,7 @@ xfs_setfilesize_trans_alloc(
 	struct xfs_trans	*tp;
 	int			error;
 
-	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_fsyncts, 0, 0,
-				XFS_TRANS_NOFS, &tp);
+	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_fsyncts, 0, 0, 0, &tp);
 	if (error)
 		return error;
 
@@ -236,6 +235,7 @@ STATIC void
 xfs_end_ioend(
 	struct xfs_ioend	*ioend)
 {
+	unsigned int		nofs_flag = memalloc_nofs_save();
 	struct list_head	ioend_list;
 	struct xfs_inode	*ip = XFS_I(ioend->io_inode);
 	xfs_off_t		offset = ioend->io_offset;
@@ -282,6 +282,8 @@ xfs_end_ioend(
 		list_del_init(&ioend->io_list);
 		xfs_destroy_ioend(ioend, error);
 	}
+
+	memalloc_nofs_restore(nofs_flag);
 }
 
 /*
@@ -663,8 +665,12 @@ xfs_submit_ioend(
 	    (ioend->io_fork == XFS_COW_FORK ||
 	     ioend->io_type != IOMAP_UNWRITTEN) &&
 	    xfs_ioend_is_append(ioend) &&
-	    !ioend->io_append_trans)
+	    !ioend->io_append_trans) {
+		unsigned nofs_flag = memalloc_nofs_save();
+
 		status = xfs_setfilesize_trans_alloc(ioend);
+		memalloc_nofs_restore(nofs_flag);
+	}
 
 	ioend->io_bio->bi_private = ioend;
 	ioend->io_bio->bi_end_io = xfs_end_bio;
diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
index 916a35cae5e9..f2d806ef8f06 100644
--- a/fs/xfs/xfs_file.c
+++ b/fs/xfs/xfs_file.c
@@ -379,6 +379,7 @@ xfs_dio_write_end_io(
 	struct inode		*inode = file_inode(iocb->ki_filp);
 	struct xfs_inode	*ip = XFS_I(inode);
 	loff_t			offset = iocb->ki_pos;
+	unsigned int		nofs_flag;
 	int			error = 0;
 
 	trace_xfs_end_io_direct_write(ip, offset, size);
@@ -395,10 +396,11 @@ xfs_dio_write_end_io(
 	 */
 	XFS_STATS_ADD(ip->i_mount, xs_write_bytes, size);
 
+	nofs_flag = memalloc_nofs_save();
 	if (flags & IOMAP_DIO_COW) {
 		error = xfs_reflink_end_cow(ip, offset, size);
 		if (error)
-			return error;
+			goto out;
 	}
 
 	/*
@@ -407,8 +409,10 @@ xfs_dio_write_end_io(
 	 * earlier allows a racing dio read to find unwritten extents before
 	 * they are converted.
 	 */
-	if (flags & IOMAP_DIO_UNWRITTEN)
-		return xfs_iomap_write_unwritten(ip, offset, size, true);
+	if (flags & IOMAP_DIO_UNWRITTEN) {
+		error = xfs_iomap_write_unwritten(ip, offset, size, true);
+		goto out;
+	}
 
 	/*
 	 * We need to update the in-core inode size here so that we don't end up
@@ -430,6 +434,8 @@ xfs_dio_write_end_io(
 		spin_unlock(&ip->i_flags_lock);
 	}
 
+out:
+	memalloc_nofs_restore(nofs_flag);
 	return error;
 }
 
diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
index 6b29452bfba0..461ea023b910 100644
--- a/fs/xfs/xfs_iomap.c
+++ b/fs/xfs/xfs_iomap.c
@@ -782,7 +782,7 @@ xfs_iomap_write_unwritten(
 		 * complete here and might deadlock on the iolock.
 		 */
 		error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0,
-				XFS_TRANS_RESERVE | XFS_TRANS_NOFS, &tp);
+				XFS_TRANS_RESERVE, &tp);
 		if (error)
 			return error;
 
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
index 680ae7662a78..0b23c2b29609 100644
--- a/fs/xfs/xfs_reflink.c
+++ b/fs/xfs/xfs_reflink.c
@@ -572,7 +572,7 @@ xfs_reflink_cancel_cow_range(
 
 	/* Start a rolling transaction to remove the mappings */
 	error = xfs_trans_alloc(ip->i_mount, &M_RES(ip->i_mount)->tr_write,
-			0, 0, XFS_TRANS_NOFS, &tp);
+			0, 0, 0, &tp);
 	if (error)
 		goto out;
 
@@ -631,7 +631,7 @@ xfs_reflink_end_cow_extent(
 
 	resblks = XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK);
 	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0,
-			XFS_TRANS_RESERVE | XFS_TRANS_NOFS, &tp);
+			XFS_TRANS_RESERVE, &tp);
 	if (error)
 		return error;
 
diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c
index 0746b329a937..21228d7455af 100644
--- a/fs/xfs/xfs_trans.c
+++ b/fs/xfs/xfs_trans.c
@@ -264,9 +264,7 @@ xfs_trans_alloc(
 	 * GFP_NOFS allocation context so that we avoid lockdep false positives
 	 * by doing GFP_KERNEL allocations inside sb_start_intwrite().
 	 */
-	tp = kmem_zone_zalloc(xfs_trans_zone,
-		(flags & XFS_TRANS_NOFS) ? KM_NOFS : KM_SLEEP);
-
+	tp = kmem_zone_zalloc(xfs_trans_zone, KM_SLEEP);
 	if (!(flags & XFS_TRANS_NO_WRITECOUNT))
 		sb_start_intwrite(mp->m_super);
 
-- 
2.20.1


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

* [PATCH 07/12] xfs: don't preallocate a transaction for file size updates
  2019-06-24  5:52 lift the xfs writepage code into iomap Christoph Hellwig
                   ` (5 preceding siblings ...)
  2019-06-24  5:52 ` [PATCH 06/12] xfs: remove XFS_TRANS_NOFS Christoph Hellwig
@ 2019-06-24  5:52 ` Christoph Hellwig
  2019-06-24 16:17   ` Darrick J. Wong
  2019-06-24  5:52 ` [PATCH 08/12] xfs: simplify xfs_ioend_can_merge Christoph Hellwig
                   ` (4 subsequent siblings)
  11 siblings, 1 reply; 52+ messages in thread
From: Christoph Hellwig @ 2019-06-24  5:52 UTC (permalink / raw)
  To: Darrick J . Wong
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel

We have historically decided that we want to preallocate the xfs_trans
structure at writeback time so that we don't have to allocate on in
the I/O completion handler.  But we treat unwrittent extent and COW
fork conversions different already, which proves that the transaction
allocations in the end I/O handler are not a problem.  Removing the
preallocation gets rid of a lot of corner case code, and also ensures
we only allocate one and log a transaction when actually required,
as the ioend merging can reduce the number of actual i_size updates
significantly.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/xfs_aops.c | 110 +++++-----------------------------------------
 fs/xfs/xfs_aops.h |   1 -
 2 files changed, 12 insertions(+), 99 deletions(-)

diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index 633baaaff7ae..017b87b7765f 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -130,44 +130,23 @@ static inline bool xfs_ioend_is_append(struct xfs_ioend *ioend)
 		XFS_I(ioend->io_inode)->i_d.di_size;
 }
 
-STATIC int
-xfs_setfilesize_trans_alloc(
-	struct xfs_ioend	*ioend)
-{
-	struct xfs_mount	*mp = XFS_I(ioend->io_inode)->i_mount;
-	struct xfs_trans	*tp;
-	int			error;
-
-	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_fsyncts, 0, 0, 0, &tp);
-	if (error)
-		return error;
-
-	ioend->io_append_trans = tp;
-
-	/*
-	 * We may pass freeze protection with a transaction.  So tell lockdep
-	 * we released it.
-	 */
-	__sb_writers_release(ioend->io_inode->i_sb, SB_FREEZE_FS);
-	/*
-	 * We hand off the transaction to the completion thread now, so
-	 * clear the flag here.
-	 */
-	current_restore_flags_nested(&tp->t_pflags, PF_MEMALLOC_NOFS);
-	return 0;
-}
-
 /*
  * Update on-disk file size now that data has been written to disk.
  */
-STATIC int
-__xfs_setfilesize(
+int
+xfs_setfilesize(
 	struct xfs_inode	*ip,
-	struct xfs_trans	*tp,
 	xfs_off_t		offset,
 	size_t			size)
 {
+	struct xfs_mount	*mp = ip->i_mount;
+	struct xfs_trans	*tp;
 	xfs_fsize_t		isize;
+	int			error;
+
+	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_fsyncts, 0, 0, 0, &tp);
+	if (error)
+		return error;
 
 	xfs_ilock(ip, XFS_ILOCK_EXCL);
 	isize = xfs_new_eof(ip, offset + size);
@@ -186,48 +165,6 @@ __xfs_setfilesize(
 	return xfs_trans_commit(tp);
 }
 
-int
-xfs_setfilesize(
-	struct xfs_inode	*ip,
-	xfs_off_t		offset,
-	size_t			size)
-{
-	struct xfs_mount	*mp = ip->i_mount;
-	struct xfs_trans	*tp;
-	int			error;
-
-	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_fsyncts, 0, 0, 0, &tp);
-	if (error)
-		return error;
-
-	return __xfs_setfilesize(ip, tp, offset, size);
-}
-
-STATIC int
-xfs_setfilesize_ioend(
-	struct xfs_ioend	*ioend,
-	int			error)
-{
-	struct xfs_inode	*ip = XFS_I(ioend->io_inode);
-	struct xfs_trans	*tp = ioend->io_append_trans;
-
-	/*
-	 * The transaction may have been allocated in the I/O submission thread,
-	 * thus we need to mark ourselves as being in a transaction manually.
-	 * Similarly for freeze protection.
-	 */
-	current_set_flags_nested(&tp->t_pflags, PF_MEMALLOC_NOFS);
-	__sb_writers_acquired(VFS_I(ip)->i_sb, SB_FREEZE_FS);
-
-	/* we abort the update if there was an IO error */
-	if (error) {
-		xfs_trans_cancel(tp);
-		return error;
-	}
-
-	return __xfs_setfilesize(ip, tp, ioend->io_offset, ioend->io_size);
-}
-
 /*
  * IO write completion.
  */
@@ -267,12 +204,9 @@ xfs_end_ioend(
 		error = xfs_reflink_end_cow(ip, offset, size);
 	else if (ioend->io_type == IOMAP_UNWRITTEN)
 		error = xfs_iomap_write_unwritten(ip, offset, size, false);
-	else
-		ASSERT(!xfs_ioend_is_append(ioend) || ioend->io_append_trans);
-
+	if (!error && xfs_ioend_is_append(ioend))
+		error = xfs_setfilesize(ip, offset, size);
 done:
-	if (ioend->io_append_trans)
-		error = xfs_setfilesize_ioend(ioend, error);
 	list_replace_init(&ioend->io_list, &ioend_list);
 	xfs_destroy_ioend(ioend, error);
 
@@ -307,8 +241,6 @@ xfs_ioend_can_merge(
 		return false;
 	if (ioend->io_offset + ioend->io_size != next->io_offset)
 		return false;
-	if (xfs_ioend_is_append(ioend) != xfs_ioend_is_append(next))
-		return false;
 	return true;
 }
 
@@ -320,7 +252,6 @@ xfs_ioend_try_merge(
 {
 	struct xfs_ioend	*next_ioend;
 	int			ioend_error;
-	int			error;
 
 	if (list_empty(more_ioends))
 		return;
@@ -334,10 +265,6 @@ xfs_ioend_try_merge(
 			break;
 		list_move_tail(&next_ioend->io_list, &ioend->io_list);
 		ioend->io_size += next_ioend->io_size;
-		if (ioend->io_append_trans) {
-			error = xfs_setfilesize_ioend(next_ioend, 1);
-			ASSERT(error == 1);
-		}
 	}
 }
 
@@ -398,7 +325,7 @@ xfs_end_bio(
 
 	if (ioend->io_fork == XFS_COW_FORK ||
 	    ioend->io_type == IOMAP_UNWRITTEN ||
-	    ioend->io_append_trans != NULL) {
+	    xfs_ioend_is_append(ioend)) {
 		spin_lock_irqsave(&ip->i_ioend_lock, flags);
 		if (list_empty(&ip->i_ioend_list))
 			WARN_ON_ONCE(!queue_work(mp->m_unwritten_workqueue,
@@ -660,18 +587,6 @@ xfs_submit_ioend(
 		memalloc_nofs_restore(nofs_flag);
 	}
 
-	/* Reserve log space if we might write beyond the on-disk inode size. */
-	if (!status &&
-	    (ioend->io_fork == XFS_COW_FORK ||
-	     ioend->io_type != IOMAP_UNWRITTEN) &&
-	    xfs_ioend_is_append(ioend) &&
-	    !ioend->io_append_trans) {
-		unsigned nofs_flag = memalloc_nofs_save();
-
-		status = xfs_setfilesize_trans_alloc(ioend);
-		memalloc_nofs_restore(nofs_flag);
-	}
-
 	ioend->io_bio->bi_private = ioend;
 	ioend->io_bio->bi_end_io = xfs_end_bio;
 
@@ -715,7 +630,6 @@ xfs_alloc_ioend(
 	ioend->io_inode = inode;
 	ioend->io_size = 0;
 	ioend->io_offset = offset;
-	ioend->io_append_trans = NULL;
 	ioend->io_bio = bio;
 	return ioend;
 }
diff --git a/fs/xfs/xfs_aops.h b/fs/xfs/xfs_aops.h
index 72e30d1c3bdf..23c087f0bcbf 100644
--- a/fs/xfs/xfs_aops.h
+++ b/fs/xfs/xfs_aops.h
@@ -18,7 +18,6 @@ struct xfs_ioend {
 	struct inode		*io_inode;	/* file being written to */
 	size_t			io_size;	/* size of the extent */
 	xfs_off_t		io_offset;	/* offset in the file */
-	struct xfs_trans	*io_append_trans;/* xact. for size update */
 	struct bio		*io_bio;	/* bio being built */
 	struct bio		io_inline_bio;	/* MUST BE LAST! */
 };
-- 
2.20.1


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

* [PATCH 08/12] xfs: simplify xfs_ioend_can_merge
  2019-06-24  5:52 lift the xfs writepage code into iomap Christoph Hellwig
                   ` (6 preceding siblings ...)
  2019-06-24  5:52 ` [PATCH 07/12] xfs: don't preallocate a transaction for file size updates Christoph Hellwig
@ 2019-06-24  5:52 ` Christoph Hellwig
  2019-06-24 16:00   ` Darrick J. Wong
  2019-06-24  5:52 ` [PATCH 09/12] xfs: refactor the ioend merging code Christoph Hellwig
                   ` (3 subsequent siblings)
  11 siblings, 1 reply; 52+ messages in thread
From: Christoph Hellwig @ 2019-06-24  5:52 UTC (permalink / raw)
  To: Darrick J . Wong
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel

Compare the block layer status directly instead of converting it to
an errno first.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/xfs_aops.c | 14 ++------------
 1 file changed, 2 insertions(+), 12 deletions(-)

diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index 017b87b7765f..acbd73976067 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -226,13 +226,9 @@ xfs_end_ioend(
 static bool
 xfs_ioend_can_merge(
 	struct xfs_ioend	*ioend,
-	int			ioend_error,
 	struct xfs_ioend	*next)
 {
-	int			next_error;
-
-	next_error = blk_status_to_errno(next->io_bio->bi_status);
-	if (ioend_error != next_error)
+	if (ioend->io_bio->bi_status != next->io_bio->bi_status)
 		return false;
 	if ((ioend->io_fork == XFS_COW_FORK) ^ (next->io_fork == XFS_COW_FORK))
 		return false;
@@ -251,17 +247,11 @@ xfs_ioend_try_merge(
 	struct list_head	*more_ioends)
 {
 	struct xfs_ioend	*next_ioend;
-	int			ioend_error;
-
-	if (list_empty(more_ioends))
-		return;
-
-	ioend_error = blk_status_to_errno(ioend->io_bio->bi_status);
 
 	while (!list_empty(more_ioends)) {
 		next_ioend = list_first_entry(more_ioends, struct xfs_ioend,
 				io_list);
-		if (!xfs_ioend_can_merge(ioend, ioend_error, next_ioend))
+		if (!xfs_ioend_can_merge(ioend, next_ioend))
 			break;
 		list_move_tail(&next_ioend->io_list, &ioend->io_list);
 		ioend->io_size += next_ioend->io_size;
-- 
2.20.1


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

* [PATCH 09/12] xfs: refactor the ioend merging code
  2019-06-24  5:52 lift the xfs writepage code into iomap Christoph Hellwig
                   ` (7 preceding siblings ...)
  2019-06-24  5:52 ` [PATCH 08/12] xfs: simplify xfs_ioend_can_merge Christoph Hellwig
@ 2019-06-24  5:52 ` Christoph Hellwig
  2019-06-24 16:04   ` Darrick J. Wong
  2019-06-24 16:06   ` Nikolay Borisov
  2019-06-24  5:52 ` [PATCH 10/12] xfs: remove the fork fields in the writepage_ctx and ioend Christoph Hellwig
                   ` (2 subsequent siblings)
  11 siblings, 2 replies; 52+ messages in thread
From: Christoph Hellwig @ 2019-06-24  5:52 UTC (permalink / raw)
  To: Darrick J . Wong
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel

Introduce two nicely abstracted helper, which can be moved to the
iomap code later.  Also use list_pop and list_first_entry_or_null
to simplify the code a bit.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/xfs_aops.c | 66 ++++++++++++++++++++++++++---------------------
 1 file changed, 36 insertions(+), 30 deletions(-)

diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index acbd73976067..5d302ebe2a33 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -121,6 +121,19 @@ xfs_destroy_ioend(
 	}
 }
 
+static void
+xfs_destroy_ioends(
+	struct xfs_ioend	*ioend,
+	int			error)
+{
+	struct list_head	tmp;
+
+	list_replace_init(&ioend->io_list, &tmp);
+	xfs_destroy_ioend(ioend, error);
+	while ((ioend = list_pop(&tmp, struct xfs_ioend, io_list)))
+		xfs_destroy_ioend(ioend, error);
+}
+
 /*
  * Fast and loose check if this write could update the on-disk inode size.
  */
@@ -173,7 +186,6 @@ xfs_end_ioend(
 	struct xfs_ioend	*ioend)
 {
 	unsigned int		nofs_flag = memalloc_nofs_save();
-	struct list_head	ioend_list;
 	struct xfs_inode	*ip = XFS_I(ioend->io_inode);
 	xfs_off_t		offset = ioend->io_offset;
 	size_t			size = ioend->io_size;
@@ -207,16 +219,7 @@ xfs_end_ioend(
 	if (!error && xfs_ioend_is_append(ioend))
 		error = xfs_setfilesize(ip, offset, size);
 done:
-	list_replace_init(&ioend->io_list, &ioend_list);
-	xfs_destroy_ioend(ioend, error);
-
-	while (!list_empty(&ioend_list)) {
-		ioend = list_first_entry(&ioend_list, struct xfs_ioend,
-				io_list);
-		list_del_init(&ioend->io_list);
-		xfs_destroy_ioend(ioend, error);
-	}
-
+	xfs_destroy_ioends(ioend, error);
 	memalloc_nofs_restore(nofs_flag);
 }
 
@@ -246,15 +249,16 @@ xfs_ioend_try_merge(
 	struct xfs_ioend	*ioend,
 	struct list_head	*more_ioends)
 {
-	struct xfs_ioend	*next_ioend;
+	struct xfs_ioend	*next;
 
-	while (!list_empty(more_ioends)) {
-		next_ioend = list_first_entry(more_ioends, struct xfs_ioend,
-				io_list);
-		if (!xfs_ioend_can_merge(ioend, next_ioend))
+	INIT_LIST_HEAD(&ioend->io_list);
+
+	while ((next = list_first_entry_or_null(more_ioends, struct xfs_ioend,
+			io_list))) {
+		if (!xfs_ioend_can_merge(ioend, next))
 			break;
-		list_move_tail(&next_ioend->io_list, &ioend->io_list);
-		ioend->io_size += next_ioend->io_size;
+		list_move_tail(&next->io_list, &ioend->io_list);
+		ioend->io_size += next->io_size;
 	}
 }
 
@@ -277,29 +281,31 @@ xfs_ioend_compare(
 	return 0;
 }
 
+static void
+xfs_sort_ioends(
+	struct list_head	*ioend_list)
+{
+	list_sort(NULL, ioend_list, xfs_ioend_compare);
+}
+
 /* Finish all pending io completions. */
 void
 xfs_end_io(
 	struct work_struct	*work)
 {
-	struct xfs_inode	*ip;
+	struct xfs_inode	*ip =
+		container_of(work, struct xfs_inode, i_ioend_work);
 	struct xfs_ioend	*ioend;
-	struct list_head	completion_list;
+	struct list_head	tmp;
 	unsigned long		flags;
 
-	ip = container_of(work, struct xfs_inode, i_ioend_work);
-
 	spin_lock_irqsave(&ip->i_ioend_lock, flags);
-	list_replace_init(&ip->i_ioend_list, &completion_list);
+	list_replace_init(&ip->i_ioend_list, &tmp);
 	spin_unlock_irqrestore(&ip->i_ioend_lock, flags);
 
-	list_sort(NULL, &completion_list, xfs_ioend_compare);
-
-	while (!list_empty(&completion_list)) {
-		ioend = list_first_entry(&completion_list, struct xfs_ioend,
-				io_list);
-		list_del_init(&ioend->io_list);
-		xfs_ioend_try_merge(ioend, &completion_list);
+	xfs_sort_ioends(&tmp);
+	while ((ioend = list_pop(&tmp, struct xfs_ioend, io_list))) {
+		xfs_ioend_try_merge(ioend, &tmp);
 		xfs_end_ioend(ioend);
 	}
 }
-- 
2.20.1


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

* [PATCH 10/12] xfs: remove the fork fields in the writepage_ctx and ioend
  2019-06-24  5:52 lift the xfs writepage code into iomap Christoph Hellwig
                   ` (8 preceding siblings ...)
  2019-06-24  5:52 ` [PATCH 09/12] xfs: refactor the ioend merging code Christoph Hellwig
@ 2019-06-24  5:52 ` Christoph Hellwig
  2019-06-24 16:08   ` Darrick J. Wong
  2019-06-24  5:52 ` [PATCH 11/12] iomap: move the xfs writeback code to iomap.c Christoph Hellwig
  2019-06-24  5:52 ` [PATCH 12/12] iomap: add tracing for the address space operations Christoph Hellwig
  11 siblings, 1 reply; 52+ messages in thread
From: Christoph Hellwig @ 2019-06-24  5:52 UTC (permalink / raw)
  To: Darrick J . Wong
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel

In preparation for moving the writeback code to iomap.c, replace the
XFS-specific COW fork concept with the iomap IOMAP_F_SHARED flag.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/xfs/xfs_aops.c | 40 +++++++++++++++++++++-------------------
 fs/xfs/xfs_aops.h |  2 +-
 2 files changed, 22 insertions(+), 20 deletions(-)

diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index 5d302ebe2a33..d9a7a9e6b912 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -28,7 +28,6 @@
  */
 struct xfs_writepage_ctx {
 	struct iomap		iomap;
-	int			fork;
 	unsigned int		data_seq;
 	unsigned int		cow_seq;
 	struct xfs_ioend	*ioend;
@@ -204,7 +203,7 @@ xfs_end_ioend(
 	 */
 	error = blk_status_to_errno(ioend->io_bio->bi_status);
 	if (unlikely(error)) {
-		if (ioend->io_fork == XFS_COW_FORK)
+		if (ioend->io_flags & IOMAP_F_SHARED)
 			xfs_reflink_cancel_cow_range(ip, offset, size, true);
 		goto done;
 	}
@@ -212,7 +211,7 @@ xfs_end_ioend(
 	/*
 	 * Success: commit the COW or unwritten blocks if needed.
 	 */
-	if (ioend->io_fork == XFS_COW_FORK)
+	if (ioend->io_flags & IOMAP_F_SHARED)
 		error = xfs_reflink_end_cow(ip, offset, size);
 	else if (ioend->io_type == IOMAP_UNWRITTEN)
 		error = xfs_iomap_write_unwritten(ip, offset, size, false);
@@ -233,7 +232,8 @@ xfs_ioend_can_merge(
 {
 	if (ioend->io_bio->bi_status != next->io_bio->bi_status)
 		return false;
-	if ((ioend->io_fork == XFS_COW_FORK) ^ (next->io_fork == XFS_COW_FORK))
+	if ((ioend->io_flags & IOMAP_F_SHARED) ^
+	    (next->io_flags & IOMAP_F_SHARED))
 		return false;
 	if ((ioend->io_type == IOMAP_UNWRITTEN) ^
 	    (next->io_type == IOMAP_UNWRITTEN))
@@ -319,7 +319,7 @@ xfs_end_bio(
 	struct xfs_mount	*mp = ip->i_mount;
 	unsigned long		flags;
 
-	if (ioend->io_fork == XFS_COW_FORK ||
+	if ((ioend->io_flags & IOMAP_F_SHARED) ||
 	    ioend->io_type == IOMAP_UNWRITTEN ||
 	    xfs_ioend_is_append(ioend)) {
 		spin_lock_irqsave(&ip->i_ioend_lock, flags);
@@ -350,7 +350,7 @@ xfs_imap_valid(
 	 * covers the offset. Be careful to check this first because the caller
 	 * can revalidate a COW mapping without updating the data seqno.
 	 */
-	if (wpc->fork == XFS_COW_FORK)
+	if (wpc->iomap.flags & IOMAP_F_SHARED)
 		return true;
 
 	/*
@@ -380,6 +380,7 @@ static int
 xfs_convert_blocks(
 	struct xfs_writepage_ctx *wpc,
 	struct xfs_inode	*ip,
+	int			whichfork,
 	loff_t			offset)
 {
 	int			error;
@@ -391,8 +392,8 @@ xfs_convert_blocks(
 	 * delalloc extent if free space is sufficiently fragmented.
 	 */
 	do {
-		error = xfs_bmapi_convert_delalloc(ip, wpc->fork, offset,
-				&wpc->iomap, wpc->fork == XFS_COW_FORK ?
+		error = xfs_bmapi_convert_delalloc(ip, whichfork, offset,
+				&wpc->iomap, whichfork == XFS_COW_FORK ?
 					&wpc->cow_seq : &wpc->data_seq);
 		if (error)
 			return error;
@@ -413,6 +414,7 @@ xfs_map_blocks(
 	xfs_fileoff_t		offset_fsb = XFS_B_TO_FSBT(mp, offset);
 	xfs_fileoff_t		end_fsb = XFS_B_TO_FSB(mp, offset + count);
 	xfs_fileoff_t		cow_fsb = NULLFILEOFF;
+	int			whichfork = XFS_DATA_FORK;
 	struct xfs_bmbt_irec	imap;
 	struct xfs_iext_cursor	icur;
 	int			retries = 0;
@@ -461,7 +463,7 @@ xfs_map_blocks(
 		wpc->cow_seq = READ_ONCE(ip->i_cowfp->if_seq);
 		xfs_iunlock(ip, XFS_ILOCK_SHARED);
 
-		wpc->fork = XFS_COW_FORK;
+		whichfork = XFS_COW_FORK;
 		goto allocate_blocks;
 	}
 
@@ -484,8 +486,6 @@ xfs_map_blocks(
 	wpc->data_seq = READ_ONCE(ip->i_df.if_seq);
 	xfs_iunlock(ip, XFS_ILOCK_SHARED);
 
-	wpc->fork = XFS_DATA_FORK;
-
 	/* landed in a hole or beyond EOF? */
 	if (imap.br_startoff > offset_fsb) {
 		imap.br_blockcount = imap.br_startoff - offset_fsb;
@@ -510,10 +510,10 @@ xfs_map_blocks(
 		goto allocate_blocks;
 
 	xfs_bmbt_to_iomap(ip, &wpc->iomap, &imap, 0);
-	trace_xfs_map_blocks_found(ip, offset, count, wpc->fork, &imap);
+	trace_xfs_map_blocks_found(ip, offset, count, whichfork, &imap);
 	return 0;
 allocate_blocks:
-	error = xfs_convert_blocks(wpc, ip, offset);
+	error = xfs_convert_blocks(wpc, ip, whichfork, offset);
 	if (error) {
 		/*
 		 * If we failed to find the extent in the COW fork we might have
@@ -522,7 +522,8 @@ xfs_map_blocks(
 		 * the former case, but prevent additional retries to avoid
 		 * looping forever for the latter case.
 		 */
-		if (error == -EAGAIN && wpc->fork == XFS_COW_FORK && !retries++)
+		if (error == -EAGAIN && (wpc->iomap.flags & IOMAP_F_SHARED) &&
+		    !retries++)
 			goto retry;
 		ASSERT(error != -EAGAIN);
 		return error;
@@ -533,7 +534,7 @@ xfs_map_blocks(
 	 * original delalloc one.  Trim the return extent to the next COW
 	 * boundary again to force a re-lookup.
 	 */
-	if (wpc->fork != XFS_COW_FORK && cow_fsb != NULLFILEOFF) {
+	if (!(wpc->iomap.flags & IOMAP_F_SHARED) && cow_fsb != NULLFILEOFF) {
 		loff_t		cow_offset = XFS_FSB_TO_B(mp, cow_fsb);
 
 		if (cow_offset < wpc->iomap.offset + wpc->iomap.length)
@@ -542,7 +543,7 @@ xfs_map_blocks(
 
 	ASSERT(wpc->iomap.offset <= offset);
 	ASSERT(wpc->iomap.offset + wpc->iomap.length > offset);
-	trace_xfs_map_blocks_alloc(ip, offset, count, wpc->fork, &imap);
+	trace_xfs_map_blocks_alloc(ip, offset, count, whichfork, &imap);
 	return 0;
 }
 
@@ -567,7 +568,7 @@ xfs_submit_ioend(
 	int			status)
 {
 	/* Convert CoW extents to regular */
-	if (!status && ioend->io_fork == XFS_COW_FORK) {
+	if (!status && (ioend->io_flags & IOMAP_F_SHARED)) {
 		/*
 		 * Yuk. This can do memory allocation, but is not a
 		 * transactional operation so everything is done in GFP_KERNEL
@@ -621,8 +622,8 @@ xfs_alloc_ioend(
 
 	ioend = container_of(bio, struct xfs_ioend, io_inline_bio);
 	INIT_LIST_HEAD(&ioend->io_list);
-	ioend->io_fork = wpc->fork;
 	ioend->io_type = wpc->iomap.type;
+	ioend->io_flags = wpc->iomap.flags;
 	ioend->io_inode = inode;
 	ioend->io_size = 0;
 	ioend->io_offset = offset;
@@ -676,7 +677,8 @@ xfs_add_to_ioend(
 	sector = (wpc->iomap.addr + offset - wpc->iomap.offset) >> 9;
 
 	if (!wpc->ioend ||
-	    wpc->fork != wpc->ioend->io_fork ||
+	    (wpc->iomap.flags & IOMAP_F_SHARED) !=
+	    (wpc->ioend->io_flags & IOMAP_F_SHARED) ||
 	    wpc->iomap.type != wpc->ioend->io_type ||
 	    sector != bio_end_sector(wpc->ioend->io_bio) ||
 	    offset != wpc->ioend->io_offset + wpc->ioend->io_size) {
diff --git a/fs/xfs/xfs_aops.h b/fs/xfs/xfs_aops.h
index 23c087f0bcbf..bf95837c59af 100644
--- a/fs/xfs/xfs_aops.h
+++ b/fs/xfs/xfs_aops.h
@@ -13,8 +13,8 @@ extern struct bio_set xfs_ioend_bioset;
  */
 struct xfs_ioend {
 	struct list_head	io_list;	/* next ioend in chain */
-	int			io_fork;	/* inode fork written back */
 	u16			io_type;
+	u16			io_flags;
 	struct inode		*io_inode;	/* file being written to */
 	size_t			io_size;	/* size of the extent */
 	xfs_off_t		io_offset;	/* offset in the file */
-- 
2.20.1


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

* [PATCH 11/12] iomap: move the xfs writeback code to iomap.c
  2019-06-24  5:52 lift the xfs writepage code into iomap Christoph Hellwig
                   ` (9 preceding siblings ...)
  2019-06-24  5:52 ` [PATCH 10/12] xfs: remove the fork fields in the writepage_ctx and ioend Christoph Hellwig
@ 2019-06-24  5:52 ` Christoph Hellwig
  2019-06-24 15:46   ` Darrick J. Wong
  2019-06-24 23:43   ` Dave Chinner
  2019-06-24  5:52 ` [PATCH 12/12] iomap: add tracing for the address space operations Christoph Hellwig
  11 siblings, 2 replies; 52+ messages in thread
From: Christoph Hellwig @ 2019-06-24  5:52 UTC (permalink / raw)
  To: Darrick J . Wong
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel

Takes the xfs writeback code and move it to iomap.c.  A new structure
with three methods is added as the abstraction from the generic
writeback code to the file system.  These methods are used to map
blocks, submit an ioend, and cancel a page that encountered an error
before it was added to an ioend.

Note that we temporarily lose the writepage tracing, but that will
be added back soon.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/iomap.c            | 521 ++++++++++++++++++++++++++++++++++++-
 fs/xfs/xfs_aops.c     | 584 ++++--------------------------------------
 fs/xfs/xfs_aops.h     |  16 --
 fs/xfs/xfs_super.c    |  11 +-
 include/linux/iomap.h |  41 +++
 5 files changed, 605 insertions(+), 568 deletions(-)

diff --git a/fs/iomap.c b/fs/iomap.c
index 23ef63fd1669..72a1b622e634 100644
--- a/fs/iomap.c
+++ b/fs/iomap.c
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
  * Copyright (C) 2010 Red Hat, Inc.
- * Copyright (c) 2016-2018 Christoph Hellwig.
+ * Copyright (c) 2016-2019 Christoph Hellwig.
  */
 #include <linux/module.h>
 #include <linux/compiler.h>
@@ -12,6 +12,7 @@
 #include <linux/migrate.h>
 #include <linux/mm.h>
 #include <linux/mm_inline.h>
+#include <linux/list_sort.h>
 #include <linux/swap.h>
 #include <linux/pagemap.h>
 #include <linux/pagevec.h>
@@ -25,6 +26,8 @@
 
 #include "internal.h"
 
+static struct bio_set iomap_ioend_bioset;
+
 /*
  * Execute a iomap write on a segment of the mapping that spans a
  * contiguous range of pages that have identical block mapping state.
@@ -2192,3 +2195,519 @@ iomap_bmap(struct address_space *mapping, sector_t bno,
 	return bno;
 }
 EXPORT_SYMBOL_GPL(iomap_bmap);
+
+static void
+iomap_finish_page_writeback(struct inode *inode, struct bio_vec *bvec,
+		int error)
+{
+	struct iomap_page *iop = to_iomap_page(bvec->bv_page);
+
+	if (error) {
+		SetPageError(bvec->bv_page);
+		mapping_set_error(inode->i_mapping, -EIO);
+	}
+
+	WARN_ON_ONCE(i_blocksize(inode) < PAGE_SIZE && !iop);
+	WARN_ON_ONCE(iop && atomic_read(&iop->write_count) <= 0);
+
+	if (!iop || atomic_dec_and_test(&iop->write_count))
+		end_page_writeback(bvec->bv_page);
+}
+
+/*
+ * We're now finished for good with this ioend structure.  Update the page
+ * state, release holds on bios, and finally free up memory.  Do not use the
+ * ioend after this.
+ */
+void
+iomap_finish_ioend(struct iomap_ioend *ioend, int error)
+{
+	struct inode *inode = ioend->io_inode;
+	struct bio *bio = &ioend->io_inline_bio;
+	struct bio *last = ioend->io_bio, *next;
+	u64 start = bio->bi_iter.bi_sector;
+	bool quiet = bio_flagged(bio, BIO_QUIET);
+
+	for (bio = &ioend->io_inline_bio; bio; bio = next) {
+		struct bio_vec	*bvec;
+		struct bvec_iter_all iter_all;
+
+		/*
+		 * For the last bio, bi_private points to the ioend, so we
+		 * need to explicitly end the iteration here.
+		 */
+		if (bio == last)
+			next = NULL;
+		else
+			next = bio->bi_private;
+
+		/* walk each page on bio, ending page IO on them */
+		bio_for_each_segment_all(bvec, bio, iter_all)
+			iomap_finish_page_writeback(inode, bvec, error);
+		bio_put(bio);
+	}
+
+	if (unlikely(error && !quiet)) {
+		printk_ratelimited(KERN_ERR
+			"%s: writeback error on sector %llu",
+			inode->i_sb->s_id, start);
+	}
+}
+EXPORT_SYMBOL_GPL(iomap_finish_ioend);
+
+void
+iomap_finish_ioends(struct iomap_ioend *ioend, int error)
+{
+	struct list_head tmp;
+
+	list_replace_init(&ioend->io_list, &tmp);
+	iomap_finish_ioend(ioend, error);
+	while ((ioend = list_pop(&tmp, struct iomap_ioend, io_list)))
+		iomap_finish_ioend(ioend, error);
+}
+EXPORT_SYMBOL_GPL(iomap_finish_ioends);
+
+/*
+ * We can merge two adjacent ioends if they have the same set of work to do.
+ */
+static bool
+iomap_ioend_can_merge(struct iomap_ioend *ioend, struct iomap_ioend *next)
+{
+	if (ioend->io_bio->bi_status != next->io_bio->bi_status)
+		return false;
+	if ((ioend->io_flags & IOMAP_F_SHARED) ^
+	    (next->io_flags & IOMAP_F_SHARED))
+		return false;
+	if ((ioend->io_type == IOMAP_UNWRITTEN) ^
+	    (next->io_type == IOMAP_UNWRITTEN))
+		return false;
+	if (ioend->io_offset + ioend->io_size != next->io_offset)
+		return false;
+	return true;
+}
+
+void
+iomap_ioend_try_merge(struct iomap_ioend *ioend, struct list_head *more_ioends)
+{
+	struct iomap_ioend *next;
+
+	INIT_LIST_HEAD(&ioend->io_list);
+
+	while ((next = list_first_entry_or_null(more_ioends, struct iomap_ioend,
+			io_list))) {
+		if (!iomap_ioend_can_merge(ioend, next))
+			break;
+		list_move_tail(&next->io_list, &ioend->io_list);
+		ioend->io_size += next->io_size;
+	}
+}
+EXPORT_SYMBOL_GPL(iomap_ioend_try_merge);
+
+static int
+iomap_ioend_compare(void *priv, struct list_head *a, struct list_head *b)
+{
+	struct iomap_ioend *ia, *ib;
+
+	ia = container_of(a, struct iomap_ioend, io_list);
+	ib = container_of(b, struct iomap_ioend, io_list);
+	if (ia->io_offset < ib->io_offset)
+		return -1;
+	else if (ia->io_offset > ib->io_offset)
+		return 1;
+	return 0;
+}
+
+void
+iomap_sort_ioends(struct list_head *ioend_list)
+{
+	list_sort(NULL, ioend_list, iomap_ioend_compare);
+}
+EXPORT_SYMBOL_GPL(iomap_sort_ioends);
+
+/*
+ * Submit the bio for an ioend. We are passed an ioend with a bio attached to
+ * it, and we submit that bio. The ioend may be used for multiple bio
+ * submissions, so we only want to allocate an append transaction for the ioend
+ * once. In the case of multiple bio submission, each bio will take an IO
+ * reference to the ioend to ensure that the ioend completion is only done once
+ * all bios have been submitted and the ioend is really done.
+ *
+ * If @error is non-zero, it means that we have a situation where some part of
+ * the submission process has failed after we have marked paged for writeback
+ * and unlocked them. In this situation, we need to fail the bio and ioend
+ * rather than submit it to IO. This typically only happens on a filesystem
+ * shutdown.
+ */
+static int
+iomap_submit_ioend(struct iomap_writepage_ctx *wpc, struct iomap_ioend *ioend,
+		int error)
+{
+	/*
+	 * If we are failing the IO now, just mark the ioend with an error and
+	 * finish it.  This will run IO completion immediately as there is only
+	 * one reference to the ioend at this point in time.
+	 */
+	ioend->io_bio->bi_private = ioend;
+	error = wpc->ops->submit_ioend(ioend, error);
+	if (error) {
+		ioend->io_bio->bi_status = errno_to_blk_status(error);
+		bio_endio(ioend->io_bio);
+		return error;
+	}
+
+	submit_bio(ioend->io_bio);
+	return 0;
+}
+
+static struct iomap_ioend *
+iomap_alloc_ioend(struct inode *inode, struct iomap_writepage_ctx *wpc,
+		loff_t offset, sector_t sector, struct writeback_control *wbc)
+{
+	struct iomap_ioend *ioend;
+	struct bio *bio;
+
+	bio = bio_alloc_bioset(GFP_NOFS, BIO_MAX_PAGES, &iomap_ioend_bioset);
+	bio_set_dev(bio, wpc->iomap.bdev);
+	bio->bi_iter.bi_sector = sector;
+	bio->bi_opf = REQ_OP_WRITE | wbc_to_write_flags(wbc);
+	bio->bi_write_hint = inode->i_write_hint;
+
+	ioend = container_of(bio, struct iomap_ioend, io_inline_bio);
+	INIT_LIST_HEAD(&ioend->io_list);
+	ioend->io_type = wpc->iomap.type;
+	ioend->io_flags = wpc->iomap.flags;
+	ioend->io_inode = inode;
+	ioend->io_size = 0;
+	ioend->io_offset = offset;
+	ioend->io_bio = bio;
+	return ioend;
+}
+
+/*
+ * Allocate a new bio, and chain the old bio to the new one.
+ *
+ * Note that we have to do perform the chaining in this unintuitive order
+ * so that the bi_private linkage is set up in the right direction for the
+ * traversal in iomap_finish_ioend().
+ */
+static struct bio *
+iomap_chain_bio(struct bio *prev)
+{
+	struct bio *new;
+
+	new = bio_alloc(GFP_NOFS, BIO_MAX_PAGES);
+	bio_copy_dev(new, prev);
+	new->bi_iter.bi_sector = bio_end_sector(prev);
+	new->bi_opf = prev->bi_opf;
+	new->bi_write_hint = prev->bi_write_hint;
+
+	bio_chain(prev, new);
+	bio_get(prev);		/* for iomap_finish_ioend */
+	submit_bio(prev);
+	return new;
+}
+
+/*
+ * Test to see if we have an existing ioend structure that we could append to
+ * first, otherwise finish off the current ioend and start another.
+ */
+static void
+iomap_add_to_ioend(struct inode *inode, loff_t offset, struct page *page,
+		struct iomap_page *iop, struct iomap_writepage_ctx *wpc,
+		struct writeback_control *wbc, struct list_head *iolist)
+{
+	unsigned len = i_blocksize(inode);
+	unsigned poff = offset & (PAGE_SIZE - 1);
+	sector_t sector = iomap_sector(&wpc->iomap, offset);
+
+	if (!wpc->ioend ||
+	    (wpc->iomap.flags & IOMAP_F_SHARED) !=
+	    (wpc->ioend->io_flags & IOMAP_F_SHARED) ||
+	    wpc->iomap.type != wpc->ioend->io_type ||
+	    sector != bio_end_sector(wpc->ioend->io_bio) ||
+	    offset != wpc->ioend->io_offset + wpc->ioend->io_size) {
+		if (wpc->ioend)
+			list_add(&wpc->ioend->io_list, iolist);
+		wpc->ioend = iomap_alloc_ioend(inode, wpc, offset, sector, wbc);
+	}
+
+	if (!__bio_try_merge_page(wpc->ioend->io_bio, page, len, poff, true)) {
+		if (iop)
+			atomic_inc(&iop->write_count);
+		if (bio_full(wpc->ioend->io_bio)) {
+			wpc->ioend->io_bio =
+				iomap_chain_bio(wpc->ioend->io_bio);
+		}
+		bio_add_page(wpc->ioend->io_bio, page, len, poff);
+	}
+
+	wpc->ioend->io_size += len;
+}
+
+/*
+ * We implement an immediate ioend submission policy here to avoid needing to
+ * chain multiple ioends and hence nest mempool allocations which can violate
+ * forward progress guarantees we need to provide. The current ioend we are
+ * adding blocks to is cached on the writepage context, and if the new block
+ * does not append to the cached ioend it will create a new ioend and cache that
+ * instead.
+ *
+ * If a new ioend is created and cached, the old ioend is returned and queued
+ * locally for submission once the entire page is processed or an error has been
+ * detected.  While ioends are submitted immediately after they are completed,
+ * batching optimisations are provided by higher level block plugging.
+ *
+ * At the end of a writeback pass, there will be a cached ioend remaining on the
+ * writepage context that the caller will need to submit.
+ */
+static int
+iomap_writepage_map(struct iomap_writepage_ctx *wpc,
+		struct writeback_control *wbc, struct inode *inode,
+		struct page *page, u64 end_offset)
+{
+	struct iomap_page *iop = to_iomap_page(page);
+	struct iomap_ioend *ioend, *next;
+	unsigned len = i_blocksize(inode);
+	u64 file_offset; /* file offset of page */
+	int error = 0, count = 0, i;
+	LIST_HEAD(submit_list);
+
+	WARN_ON_ONCE(i_blocksize(inode) < PAGE_SIZE && !iop);
+	WARN_ON_ONCE(iop && atomic_read(&iop->write_count) != 0);
+
+	/*
+	 * Walk through the page to find areas to write back. If we run off the
+	 * end of the current map or find the current map invalid, grab a new
+	 * one.
+	 */
+	for (i = 0, file_offset = page_offset(page);
+	     i < (PAGE_SIZE >> inode->i_blkbits) && file_offset < end_offset;
+	     i++, file_offset += len) {
+		if (iop && !test_bit(i, iop->uptodate))
+			continue;
+
+		error = wpc->ops->map_blocks(wpc, inode, file_offset);
+		if (error)
+			break;
+		if (wpc->iomap.type == IOMAP_HOLE)
+			continue;
+		iomap_add_to_ioend(inode, file_offset, page, iop, wpc, wbc,
+				 &submit_list);
+		count++;
+	}
+
+	WARN_ON_ONCE(!wpc->ioend && !list_empty(&submit_list));
+	WARN_ON_ONCE(!PageLocked(page));
+	WARN_ON_ONCE(PageWriteback(page));
+
+	/*
+	 * On error, we have to fail the ioend here because we may have set
+	 * pages under writeback, we have to make sure we run IO completion to
+	 * mark the error state of the IO appropriately, so we can't cancel the
+	 * ioend directly here.  That means we have to mark this page as under
+	 * writeback if we included any blocks from it in the ioend chain so
+	 * that completion treats it correctly.
+	 *
+	 * If we didn't include the page in the ioend, the on error we can
+	 * simply discard and unlock it as there are no other users of the page
+	 * now.  The caller will still need to trigger submission of outstanding
+	 * ioends on the writepage context so they are treated correctly on
+	 * error.
+	 */
+	if (unlikely(error)) {
+		if (!count) {
+			wpc->ops->discard_page(page);
+			ClearPageUptodate(page);
+			unlock_page(page);
+			goto done;
+		}
+
+		/*
+		 * If the page was not fully cleaned, we need to ensure that the
+		 * higher layers come back to it correctly.  That means we need
+		 * to keep the page dirty, and for WB_SYNC_ALL writeback we need
+		 * to ensure the PAGECACHE_TAG_TOWRITE index mark is not removed
+		 * so another attempt to write this page in this writeback sweep
+		 * will be made.
+		 */
+		set_page_writeback_keepwrite(page);
+	} else {
+		clear_page_dirty_for_io(page);
+		set_page_writeback(page);
+	}
+
+	unlock_page(page);
+
+	/*
+	 * Preserve the original error if there was one, otherwise catch
+	 * submission errors here and propagate into subsequent ioend
+	 * submissions.
+	 */
+	list_for_each_entry_safe(ioend, next, &submit_list, io_list) {
+		int error2;
+
+		list_del_init(&ioend->io_list);
+		error2 = iomap_submit_ioend(wpc, ioend, error);
+		if (error2 && !error)
+			error = error2;
+	}
+
+	/*
+	 * We can end up here with no error and nothing to write only if we race
+	 * with a partial page truncate on a sub-page block sized filesystem.
+	 */
+	if (!count)
+		end_page_writeback(page);
+done:
+	mapping_set_error(page->mapping, error);
+	return error;
+}
+
+/*
+ * Write out a dirty page.
+ *
+ * For delalloc space on the page we need to allocate space and flush it.
+ * For unwritten space on the page we need to start the conversion to
+ * regular allocated space.
+ */
+static int
+iomap_do_writepage(struct page *page, struct writeback_control *wbc, void *data)
+{
+	struct iomap_writepage_ctx *wpc = data;
+	struct inode *inode = page->mapping->host;
+	pgoff_t end_index;
+	u64 end_offset;
+	loff_t offset;
+
+	/*
+	 * Refuse to write the page out if we are called from reclaim context.
+	 *
+	 * This avoids stack overflows when called from deeply used stacks in
+	 * random callers for direct reclaim or memcg reclaim.  We explicitly
+	 * allow reclaim from kswapd as the stack usage there is relatively low.
+	 *
+	 * This should never happen except in the case of a VM regression so
+	 * warn about it.
+	 */
+	if (WARN_ON_ONCE((current->flags & (PF_MEMALLOC|PF_KSWAPD)) ==
+			PF_MEMALLOC))
+		goto redirty;
+
+	/*
+	 * Given that we do not allow direct reclaim to call us, we should
+	 * never be called while in a filesystem transaction.
+	 */
+	if (WARN_ON_ONCE(current->flags & PF_MEMALLOC_NOFS))
+		goto redirty;
+
+	/*
+	 * Is this page beyond the end of the file?
+	 *
+	 * The page index is less than the end_index, adjust the end_offset
+	 * to the highest offset that this page should represent.
+	 * -----------------------------------------------------
+	 * |			file mapping	       | <EOF> |
+	 * -----------------------------------------------------
+	 * | Page ... | Page N-2 | Page N-1 |  Page N  |       |
+	 * ^--------------------------------^----------|--------
+	 * |     desired writeback range    |      see else    |
+	 * ---------------------------------^------------------|
+	 */
+	offset = i_size_read(inode);
+	end_index = offset >> PAGE_SHIFT;
+	if (page->index < end_index)
+		end_offset = (loff_t)(page->index + 1) << PAGE_SHIFT;
+	else {
+		/*
+		 * Check whether the page to write out is beyond or straddles
+		 * i_size or not.
+		 * -------------------------------------------------------
+		 * |		file mapping		        | <EOF>  |
+		 * -------------------------------------------------------
+		 * | Page ... | Page N-2 | Page N-1 |  Page N   | Beyond |
+		 * ^--------------------------------^-----------|---------
+		 * |				    |      Straddles     |
+		 * ---------------------------------^-----------|--------|
+		 */
+		unsigned offset_into_page = offset & (PAGE_SIZE - 1);
+
+		/*
+		 * Skip the page if it is fully outside i_size, e.g. due to a
+		 * truncate operation that is in progress. We must redirty the
+		 * page so that reclaim stops reclaiming it. Otherwise
+		 * iomap_vm_releasepage() is called on it and gets confused.
+		 *
+		 * Note that the end_index is unsigned long, it would overflow
+		 * if the given offset is greater than 16TB on 32-bit system
+		 * and if we do check the page is fully outside i_size or not
+		 * via "if (page->index >= end_index + 1)" as "end_index + 1"
+		 * will be evaluated to 0.  Hence this page will be redirtied
+		 * and be written out repeatedly which would result in an
+		 * infinite loop, the user program that perform this operation
+		 * will hang.  Instead, we can verify this situation by checking
+		 * if the page to write is totally beyond the i_size or if it's
+		 * offset is just equal to the EOF.
+		 */
+		if (page->index > end_index ||
+		    (page->index == end_index && offset_into_page == 0))
+			goto redirty;
+
+		/*
+		 * The page straddles i_size.  It must be zeroed out on each
+		 * and every writepage invocation because it may be mmapped.
+		 * "A file is mapped in multiples of the page size.  For a file
+		 * that is not a multiple of the page size, the remaining
+		 * memory is zeroed when mapped, and writes to that region are
+		 * not written out to the file."
+		 */
+		zero_user_segment(page, offset_into_page, PAGE_SIZE);
+
+		/* Adjust the end_offset to the end of file */
+		end_offset = offset;
+	}
+
+	return iomap_writepage_map(wpc, wbc, inode, page, end_offset);
+
+redirty:
+	redirty_page_for_writepage(wbc, page);
+	unlock_page(page);
+	return 0;
+}
+
+int
+iomap_writepage(struct page *page, struct writeback_control *wbc,
+		struct iomap_writepage_ctx *wpc,
+		const struct iomap_writeback_ops *ops)
+{
+	int ret;
+
+	wpc->ops = ops;
+	ret = iomap_do_writepage(page, wbc, wpc);
+	if (!wpc->ioend)
+		return ret;
+	return iomap_submit_ioend(wpc, wpc->ioend, ret);
+}
+EXPORT_SYMBOL_GPL(iomap_writepage);
+
+int
+iomap_writepages(struct address_space *mapping, struct writeback_control *wbc,
+		struct iomap_writepage_ctx *wpc,
+		const struct iomap_writeback_ops *ops)
+{
+	int			ret;
+
+	wpc->ops = ops;
+	ret = write_cache_pages(mapping, wbc, iomap_do_writepage, wpc);
+	if (!wpc->ioend)
+		return ret;
+	return iomap_submit_ioend(wpc, wpc->ioend, ret);
+}
+EXPORT_SYMBOL_GPL(iomap_writepages);
+
+static int __init iomap_init(void)
+{
+	return bioset_init(&iomap_ioend_bioset, 4 * (PAGE_SIZE / SECTOR_SIZE),
+			   offsetof(struct iomap_ioend, io_inline_bio),
+			   BIOSET_NEED_BVECS);
+}
+fs_initcall(iomap_init);
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index d9a7a9e6b912..26b838aea2db 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -23,16 +23,18 @@
 #include "xfs_reflink.h"
 #include <linux/writeback.h>
 
-/*
- * structure owned by writepages passed to individual writepage calls
- */
 struct xfs_writepage_ctx {
-	struct iomap		iomap;
+	struct iomap_writepage_ctx ctx;
 	unsigned int		data_seq;
 	unsigned int		cow_seq;
-	struct xfs_ioend	*ioend;
 };
 
+static inline struct xfs_writepage_ctx *
+XFS_WPC(struct iomap_writepage_ctx *ctx)
+{
+	return container_of(ctx, struct xfs_writepage_ctx, ctx);
+}
+
 struct block_device *
 xfs_find_bdev_for_inode(
 	struct inode		*inode)
@@ -59,84 +61,10 @@ xfs_find_daxdev_for_inode(
 		return mp->m_ddev_targp->bt_daxdev;
 }
 
-static void
-xfs_finish_page_writeback(
-	struct inode		*inode,
-	struct bio_vec	*bvec,
-	int			error)
-{
-	struct iomap_page	*iop = to_iomap_page(bvec->bv_page);
-
-	if (error) {
-		SetPageError(bvec->bv_page);
-		mapping_set_error(inode->i_mapping, -EIO);
-	}
-
-	ASSERT(iop || i_blocksize(inode) == PAGE_SIZE);
-	ASSERT(!iop || atomic_read(&iop->write_count) > 0);
-
-	if (!iop || atomic_dec_and_test(&iop->write_count))
-		end_page_writeback(bvec->bv_page);
-}
-
-/*
- * We're now finished for good with this ioend structure.  Update the page
- * state, release holds on bios, and finally free up memory.  Do not use the
- * ioend after this.
- */
-STATIC void
-xfs_destroy_ioend(
-	struct xfs_ioend	*ioend,
-	int			error)
-{
-	struct inode		*inode = ioend->io_inode;
-	struct bio		*bio = &ioend->io_inline_bio;
-	struct bio		*last = ioend->io_bio, *next;
-	u64			start = bio->bi_iter.bi_sector;
-	bool			quiet = bio_flagged(bio, BIO_QUIET);
-
-	for (bio = &ioend->io_inline_bio; bio; bio = next) {
-		struct bio_vec	*bvec;
-		struct bvec_iter_all iter_all;
-
-		/*
-		 * For the last bio, bi_private points to the ioend, so we
-		 * need to explicitly end the iteration here.
-		 */
-		if (bio == last)
-			next = NULL;
-		else
-			next = bio->bi_private;
-
-		/* walk each page on bio, ending page IO on them */
-		bio_for_each_segment_all(bvec, bio, iter_all)
-			xfs_finish_page_writeback(inode, bvec, error);
-		bio_put(bio);
-	}
-
-	if (unlikely(error && !quiet)) {
-		xfs_err_ratelimited(XFS_I(inode)->i_mount,
-			"writeback error on sector %llu", start);
-	}
-}
-
-static void
-xfs_destroy_ioends(
-	struct xfs_ioend	*ioend,
-	int			error)
-{
-	struct list_head	tmp;
-
-	list_replace_init(&ioend->io_list, &tmp);
-	xfs_destroy_ioend(ioend, error);
-	while ((ioend = list_pop(&tmp, struct xfs_ioend, io_list)))
-		xfs_destroy_ioend(ioend, error);
-}
-
 /*
  * Fast and loose check if this write could update the on-disk inode size.
  */
-static inline bool xfs_ioend_is_append(struct xfs_ioend *ioend)
+static inline bool xfs_ioend_is_append(struct iomap_ioend *ioend)
 {
 	return ioend->io_offset + ioend->io_size >
 		XFS_I(ioend->io_inode)->i_d.di_size;
@@ -182,7 +110,7 @@ xfs_setfilesize(
  */
 STATIC void
 xfs_end_ioend(
-	struct xfs_ioend	*ioend)
+	struct iomap_ioend	*ioend)
 {
 	unsigned int		nofs_flag = memalloc_nofs_save();
 	struct xfs_inode	*ip = XFS_I(ioend->io_inode);
@@ -218,76 +146,10 @@ xfs_end_ioend(
 	if (!error && xfs_ioend_is_append(ioend))
 		error = xfs_setfilesize(ip, offset, size);
 done:
-	xfs_destroy_ioends(ioend, error);
+	iomap_finish_ioends(ioend, error);
 	memalloc_nofs_restore(nofs_flag);
 }
 
-/*
- * We can merge two adjacent ioends if they have the same set of work to do.
- */
-static bool
-xfs_ioend_can_merge(
-	struct xfs_ioend	*ioend,
-	struct xfs_ioend	*next)
-{
-	if (ioend->io_bio->bi_status != next->io_bio->bi_status)
-		return false;
-	if ((ioend->io_flags & IOMAP_F_SHARED) ^
-	    (next->io_flags & IOMAP_F_SHARED))
-		return false;
-	if ((ioend->io_type == IOMAP_UNWRITTEN) ^
-	    (next->io_type == IOMAP_UNWRITTEN))
-		return false;
-	if (ioend->io_offset + ioend->io_size != next->io_offset)
-		return false;
-	return true;
-}
-
-/* Try to merge adjacent completions. */
-STATIC void
-xfs_ioend_try_merge(
-	struct xfs_ioend	*ioend,
-	struct list_head	*more_ioends)
-{
-	struct xfs_ioend	*next;
-
-	INIT_LIST_HEAD(&ioend->io_list);
-
-	while ((next = list_first_entry_or_null(more_ioends, struct xfs_ioend,
-			io_list))) {
-		if (!xfs_ioend_can_merge(ioend, next))
-			break;
-		list_move_tail(&next->io_list, &ioend->io_list);
-		ioend->io_size += next->io_size;
-	}
-}
-
-/* list_sort compare function for ioends */
-static int
-xfs_ioend_compare(
-	void			*priv,
-	struct list_head	*a,
-	struct list_head	*b)
-{
-	struct xfs_ioend	*ia;
-	struct xfs_ioend	*ib;
-
-	ia = container_of(a, struct xfs_ioend, io_list);
-	ib = container_of(b, struct xfs_ioend, io_list);
-	if (ia->io_offset < ib->io_offset)
-		return -1;
-	else if (ia->io_offset > ib->io_offset)
-		return 1;
-	return 0;
-}
-
-static void
-xfs_sort_ioends(
-	struct list_head	*ioend_list)
-{
-	list_sort(NULL, ioend_list, xfs_ioend_compare);
-}
-
 /* Finish all pending io completions. */
 void
 xfs_end_io(
@@ -295,7 +157,7 @@ xfs_end_io(
 {
 	struct xfs_inode	*ip =
 		container_of(work, struct xfs_inode, i_ioend_work);
-	struct xfs_ioend	*ioend;
+	struct iomap_ioend	*ioend;
 	struct list_head	tmp;
 	unsigned long		flags;
 
@@ -303,9 +165,9 @@ xfs_end_io(
 	list_replace_init(&ip->i_ioend_list, &tmp);
 	spin_unlock_irqrestore(&ip->i_ioend_lock, flags);
 
-	xfs_sort_ioends(&tmp);
-	while ((ioend = list_pop(&tmp, struct xfs_ioend, io_list))) {
-		xfs_ioend_try_merge(ioend, &tmp);
+	iomap_sort_ioends(&tmp);
+	while ((ioend = list_pop(&tmp, struct iomap_ioend, io_list))) {
+		iomap_ioend_try_merge(ioend, &tmp);
 		xfs_end_ioend(ioend);
 	}
 }
@@ -314,7 +176,7 @@ STATIC void
 xfs_end_bio(
 	struct bio		*bio)
 {
-	struct xfs_ioend	*ioend = bio->bi_private;
+	struct iomap_ioend	*ioend = bio->bi_private;
 	struct xfs_inode	*ip = XFS_I(ioend->io_inode);
 	struct xfs_mount	*mp = ip->i_mount;
 	unsigned long		flags;
@@ -329,7 +191,7 @@ xfs_end_bio(
 		list_add_tail(&ioend->io_list, &ip->i_ioend_list);
 		spin_unlock_irqrestore(&ip->i_ioend_lock, flags);
 	} else
-		xfs_destroy_ioend(ioend, blk_status_to_errno(bio->bi_status));
+		iomap_finish_ioend(ioend, blk_status_to_errno(bio->bi_status));
 }
 
 /*
@@ -338,7 +200,7 @@ xfs_end_bio(
  */
 static bool
 xfs_imap_valid(
-	struct xfs_writepage_ctx	*wpc,
+	struct iomap_writepage_ctx	*wpc,
 	struct xfs_inode		*ip,
 	loff_t				offset)
 {
@@ -360,10 +222,10 @@ xfs_imap_valid(
 	 * checked (and found nothing at this offset) could have added
 	 * overlapping blocks.
 	 */
-	if (wpc->data_seq != READ_ONCE(ip->i_df.if_seq))
+	if (XFS_WPC(wpc)->data_seq != READ_ONCE(ip->i_df.if_seq))
 		return false;
 	if (xfs_inode_has_cow_data(ip) &&
-	    wpc->cow_seq != READ_ONCE(ip->i_cowfp->if_seq))
+	    XFS_WPC(wpc)->cow_seq != READ_ONCE(ip->i_cowfp->if_seq))
 		return false;
 	return true;
 }
@@ -378,12 +240,18 @@ xfs_imap_valid(
  */
 static int
 xfs_convert_blocks(
-	struct xfs_writepage_ctx *wpc,
+	struct iomap_writepage_ctx *wpc,
 	struct xfs_inode	*ip,
 	int			whichfork,
 	loff_t			offset)
 {
 	int			error;
+	unsigned		*seq;
+
+	if (whichfork == XFS_COW_FORK)
+		seq = &XFS_WPC(wpc)->cow_seq;
+	else
+		seq = &XFS_WPC(wpc)->data_seq;
 
 	/*
 	 * Attempt to allocate whatever delalloc extent currently backs offset
@@ -393,8 +261,7 @@ xfs_convert_blocks(
 	 */
 	do {
 		error = xfs_bmapi_convert_delalloc(ip, whichfork, offset,
-				&wpc->iomap, whichfork == XFS_COW_FORK ?
-					&wpc->cow_seq : &wpc->data_seq);
+				&wpc->iomap, seq);
 		if (error)
 			return error;
 	} while (wpc->iomap.offset + wpc->iomap.length <= offset);
@@ -402,9 +269,9 @@ xfs_convert_blocks(
 	return 0;
 }
 
-STATIC int
+static int
 xfs_map_blocks(
-	struct xfs_writepage_ctx *wpc,
+	struct iomap_writepage_ctx *wpc,
 	struct inode		*inode,
 	loff_t			offset)
 {
@@ -460,7 +327,7 @@ xfs_map_blocks(
 	    xfs_iext_lookup_extent(ip, ip->i_cowfp, offset_fsb, &icur, &imap))
 		cow_fsb = imap.br_startoff;
 	if (cow_fsb != NULLFILEOFF && cow_fsb <= offset_fsb) {
-		wpc->cow_seq = READ_ONCE(ip->i_cowfp->if_seq);
+		XFS_WPC(wpc)->cow_seq = READ_ONCE(ip->i_cowfp->if_seq);
 		xfs_iunlock(ip, XFS_ILOCK_SHARED);
 
 		whichfork = XFS_COW_FORK;
@@ -483,7 +350,7 @@ xfs_map_blocks(
 	 */
 	if (!xfs_iext_lookup_extent(ip, &ip->i_df, offset_fsb, &icur, &imap))
 		imap.br_startoff = end_fsb;	/* fake a hole past EOF */
-	wpc->data_seq = READ_ONCE(ip->i_df.if_seq);
+	XFS_WPC(wpc)->data_seq = READ_ONCE(ip->i_df.if_seq);
 	xfs_iunlock(ip, XFS_ILOCK_SHARED);
 
 	/* landed in a hole or beyond EOF? */
@@ -547,24 +414,9 @@ xfs_map_blocks(
 	return 0;
 }
 
-/*
- * Submit the bio for an ioend. We are passed an ioend with a bio attached to
- * it, and we submit that bio. The ioend may be used for multiple bio
- * submissions, so we only want to allocate an append transaction for the ioend
- * once. In the case of multiple bio submission, each bio will take an IO
- * reference to the ioend to ensure that the ioend completion is only done once
- * all bios have been submitted and the ioend is really done.
- *
- * If @status is non-zero, it means that we have a situation where some part of
- * the submission process has failed after we have marked paged for writeback
- * and unlocked them. In this situation, we need to fail the bio and ioend
- * rather than submit it to IO. This typically only happens on a filesystem
- * shutdown.
- */
-STATIC int
+static int
 xfs_submit_ioend(
-	struct writeback_control *wbc,
-	struct xfs_ioend	*ioend,
+	struct iomap_ioend	*ioend,
 	int			status)
 {
 	/* Convert CoW extents to regular */
@@ -584,118 +436,8 @@ xfs_submit_ioend(
 		memalloc_nofs_restore(nofs_flag);
 	}
 
-	ioend->io_bio->bi_private = ioend;
 	ioend->io_bio->bi_end_io = xfs_end_bio;
-
-	/*
-	 * If we are failing the IO now, just mark the ioend with an
-	 * error and finish it. This will run IO completion immediately
-	 * as there is only one reference to the ioend at this point in
-	 * time.
-	 */
-	if (status) {
-		ioend->io_bio->bi_status = errno_to_blk_status(status);
-		bio_endio(ioend->io_bio);
-		return status;
-	}
-
-	submit_bio(ioend->io_bio);
-	return 0;
-}
-
-static struct xfs_ioend *
-xfs_alloc_ioend(
-	struct inode		*inode,
-	struct xfs_writepage_ctx *wpc,
-	xfs_off_t		offset,
-	sector_t		sector,
-	struct writeback_control *wbc)
-{
-	struct xfs_ioend	*ioend;
-	struct bio		*bio;
-
-	bio = bio_alloc_bioset(GFP_NOFS, BIO_MAX_PAGES, &xfs_ioend_bioset);
-	bio_set_dev(bio, wpc->iomap.bdev);
-	bio->bi_iter.bi_sector = sector;
-	bio->bi_opf = REQ_OP_WRITE | wbc_to_write_flags(wbc);
-	bio->bi_write_hint = inode->i_write_hint;
-
-	ioend = container_of(bio, struct xfs_ioend, io_inline_bio);
-	INIT_LIST_HEAD(&ioend->io_list);
-	ioend->io_type = wpc->iomap.type;
-	ioend->io_flags = wpc->iomap.flags;
-	ioend->io_inode = inode;
-	ioend->io_size = 0;
-	ioend->io_offset = offset;
-	ioend->io_bio = bio;
-	return ioend;
-}
-
-/*
- * Allocate a new bio, and chain the old bio to the new one.
- *
- * Note that we have to do perform the chaining in this unintuitive order
- * so that the bi_private linkage is set up in the right direction for the
- * traversal in xfs_destroy_ioend().
- */
-static struct bio *
-xfs_chain_bio(
-	struct bio		*prev)
-{
-	struct bio *new;
-
-	new = bio_alloc(GFP_NOFS, BIO_MAX_PAGES);
-	bio_copy_dev(new, prev);
-	new->bi_iter.bi_sector = bio_end_sector(prev);
-	new->bi_opf = prev->bi_opf;
-	new->bi_write_hint = prev->bi_write_hint;
-
-	bio_chain(prev, new);
-	bio_get(prev);		/* for xfs_destroy_ioend */
-	submit_bio(prev);
-	return new;
-}
-
-/*
- * Test to see if we have an existing ioend structure that we could append to
- * first, otherwise finish off the current ioend and start another.
- */
-STATIC void
-xfs_add_to_ioend(
-	struct inode		*inode,
-	xfs_off_t		offset,
-	struct page		*page,
-	struct iomap_page	*iop,
-	struct xfs_writepage_ctx *wpc,
-	struct writeback_control *wbc,
-	struct list_head	*iolist)
-{
-	unsigned		len = i_blocksize(inode);
-	unsigned		poff = offset & (PAGE_SIZE - 1);
-	sector_t		sector;
-
-	sector = (wpc->iomap.addr + offset - wpc->iomap.offset) >> 9;
-
-	if (!wpc->ioend ||
-	    (wpc->iomap.flags & IOMAP_F_SHARED) !=
-	    (wpc->ioend->io_flags & IOMAP_F_SHARED) ||
-	    wpc->iomap.type != wpc->ioend->io_type ||
-	    sector != bio_end_sector(wpc->ioend->io_bio) ||
-	    offset != wpc->ioend->io_offset + wpc->ioend->io_size) {
-		if (wpc->ioend)
-			list_add(&wpc->ioend->io_list, iolist);
-		wpc->ioend = xfs_alloc_ioend(inode, wpc, offset, sector, wbc);
-	}
-
-	if (!__bio_try_merge_page(wpc->ioend->io_bio, page, len, poff, true)) {
-		if (iop)
-			atomic_inc(&iop->write_count);
-		if (bio_full(wpc->ioend->io_bio))
-			wpc->ioend->io_bio = xfs_chain_bio(wpc->ioend->io_bio);
-		bio_add_page(wpc->ioend->io_bio, page, len, poff);
-	}
-
-	wpc->ioend->io_size += len;
+	return status;
 }
 
 STATIC void
@@ -719,8 +461,8 @@ xfs_vm_invalidatepage(
  * transaction as there is no space left for block reservation (typically why we
  * see a ENOSPC in writeback).
  */
-STATIC void
-xfs_aops_discard_page(
+static void
+xfs_discard_page(
 	struct page		*page)
 {
 	struct inode		*inode = page->mapping->host;
@@ -745,243 +487,11 @@ xfs_aops_discard_page(
 	xfs_vm_invalidatepage(page, 0, PAGE_SIZE);
 }
 
-/*
- * We implement an immediate ioend submission policy here to avoid needing to
- * chain multiple ioends and hence nest mempool allocations which can violate
- * forward progress guarantees we need to provide. The current ioend we are
- * adding blocks to is cached on the writepage context, and if the new block
- * does not append to the cached ioend it will create a new ioend and cache that
- * instead.
- *
- * If a new ioend is created and cached, the old ioend is returned and queued
- * locally for submission once the entire page is processed or an error has been
- * detected.  While ioends are submitted immediately after they are completed,
- * batching optimisations are provided by higher level block plugging.
- *
- * At the end of a writeback pass, there will be a cached ioend remaining on the
- * writepage context that the caller will need to submit.
- */
-static int
-xfs_writepage_map(
-	struct xfs_writepage_ctx *wpc,
-	struct writeback_control *wbc,
-	struct inode		*inode,
-	struct page		*page,
-	uint64_t		end_offset)
-{
-	LIST_HEAD(submit_list);
-	struct iomap_page	*iop = to_iomap_page(page);
-	unsigned		len = i_blocksize(inode);
-	struct xfs_ioend	*ioend, *next;
-	uint64_t		file_offset;	/* file offset of page */
-	int			error = 0, count = 0, i;
-
-	ASSERT(iop || i_blocksize(inode) == PAGE_SIZE);
-	ASSERT(!iop || atomic_read(&iop->write_count) == 0);
-
-	/*
-	 * Walk through the page to find areas to write back. If we run off the
-	 * end of the current map or find the current map invalid, grab a new
-	 * one.
-	 */
-	for (i = 0, file_offset = page_offset(page);
-	     i < (PAGE_SIZE >> inode->i_blkbits) && file_offset < end_offset;
-	     i++, file_offset += len) {
-		if (iop && !test_bit(i, iop->uptodate))
-			continue;
-
-		error = xfs_map_blocks(wpc, inode, file_offset);
-		if (error)
-			break;
-		if (wpc->iomap.type == IOMAP_HOLE)
-			continue;
-		xfs_add_to_ioend(inode, file_offset, page, iop, wpc, wbc,
-				 &submit_list);
-		count++;
-	}
-
-	ASSERT(wpc->ioend || list_empty(&submit_list));
-	ASSERT(PageLocked(page));
-	ASSERT(!PageWriteback(page));
-
-	/*
-	 * On error, we have to fail the ioend here because we may have set
-	 * pages under writeback, we have to make sure we run IO completion to
-	 * mark the error state of the IO appropriately, so we can't cancel the
-	 * ioend directly here.  That means we have to mark this page as under
-	 * writeback if we included any blocks from it in the ioend chain so
-	 * that completion treats it correctly.
-	 *
-	 * If we didn't include the page in the ioend, the on error we can
-	 * simply discard and unlock it as there are no other users of the page
-	 * now.  The caller will still need to trigger submission of outstanding
-	 * ioends on the writepage context so they are treated correctly on
-	 * error.
-	 */
-	if (unlikely(error)) {
-		if (!count) {
-			xfs_aops_discard_page(page);
-			ClearPageUptodate(page);
-			unlock_page(page);
-			goto done;
-		}
-
-		/*
-		 * If the page was not fully cleaned, we need to ensure that the
-		 * higher layers come back to it correctly.  That means we need
-		 * to keep the page dirty, and for WB_SYNC_ALL writeback we need
-		 * to ensure the PAGECACHE_TAG_TOWRITE index mark is not removed
-		 * so another attempt to write this page in this writeback sweep
-		 * will be made.
-		 */
-		set_page_writeback_keepwrite(page);
-	} else {
-		clear_page_dirty_for_io(page);
-		set_page_writeback(page);
-	}
-
-	unlock_page(page);
-
-	/*
-	 * Preserve the original error if there was one, otherwise catch
-	 * submission errors here and propagate into subsequent ioend
-	 * submissions.
-	 */
-	list_for_each_entry_safe(ioend, next, &submit_list, io_list) {
-		int error2;
-
-		list_del_init(&ioend->io_list);
-		error2 = xfs_submit_ioend(wbc, ioend, error);
-		if (error2 && !error)
-			error = error2;
-	}
-
-	/*
-	 * We can end up here with no error and nothing to write only if we race
-	 * with a partial page truncate on a sub-page block sized filesystem.
-	 */
-	if (!count)
-		end_page_writeback(page);
-done:
-	mapping_set_error(page->mapping, error);
-	return error;
-}
-
-/*
- * Write out a dirty page.
- *
- * For delalloc space on the page we need to allocate space and flush it.
- * For unwritten space on the page we need to start the conversion to
- * regular allocated space.
- */
-STATIC int
-xfs_do_writepage(
-	struct page		*page,
-	struct writeback_control *wbc,
-	void			*data)
-{
-	struct xfs_writepage_ctx *wpc = data;
-	struct inode		*inode = page->mapping->host;
-	loff_t			offset;
-	uint64_t              end_offset;
-	pgoff_t                 end_index;
-
-	trace_xfs_writepage(inode, page, 0, 0);
-
-	/*
-	 * Refuse to write the page out if we are called from reclaim context.
-	 *
-	 * This avoids stack overflows when called from deeply used stacks in
-	 * random callers for direct reclaim or memcg reclaim.  We explicitly
-	 * allow reclaim from kswapd as the stack usage there is relatively low.
-	 *
-	 * This should never happen except in the case of a VM regression so
-	 * warn about it.
-	 */
-	if (WARN_ON_ONCE((current->flags & (PF_MEMALLOC|PF_KSWAPD)) ==
-			PF_MEMALLOC))
-		goto redirty;
-
-	/*
-	 * Given that we do not allow direct reclaim to call us, we should
-	 * never be called while in a filesystem transaction.
-	 */
-	if (WARN_ON_ONCE(current->flags & PF_MEMALLOC_NOFS))
-		goto redirty;
-
-	/*
-	 * Is this page beyond the end of the file?
-	 *
-	 * The page index is less than the end_index, adjust the end_offset
-	 * to the highest offset that this page should represent.
-	 * -----------------------------------------------------
-	 * |			file mapping	       | <EOF> |
-	 * -----------------------------------------------------
-	 * | Page ... | Page N-2 | Page N-1 |  Page N  |       |
-	 * ^--------------------------------^----------|--------
-	 * |     desired writeback range    |      see else    |
-	 * ---------------------------------^------------------|
-	 */
-	offset = i_size_read(inode);
-	end_index = offset >> PAGE_SHIFT;
-	if (page->index < end_index)
-		end_offset = (xfs_off_t)(page->index + 1) << PAGE_SHIFT;
-	else {
-		/*
-		 * Check whether the page to write out is beyond or straddles
-		 * i_size or not.
-		 * -------------------------------------------------------
-		 * |		file mapping		        | <EOF>  |
-		 * -------------------------------------------------------
-		 * | Page ... | Page N-2 | Page N-1 |  Page N   | Beyond |
-		 * ^--------------------------------^-----------|---------
-		 * |				    |      Straddles     |
-		 * ---------------------------------^-----------|--------|
-		 */
-		unsigned offset_into_page = offset & (PAGE_SIZE - 1);
-
-		/*
-		 * Skip the page if it is fully outside i_size, e.g. due to a
-		 * truncate operation that is in progress. We must redirty the
-		 * page so that reclaim stops reclaiming it. Otherwise
-		 * xfs_vm_releasepage() is called on it and gets confused.
-		 *
-		 * Note that the end_index is unsigned long, it would overflow
-		 * if the given offset is greater than 16TB on 32-bit system
-		 * and if we do check the page is fully outside i_size or not
-		 * via "if (page->index >= end_index + 1)" as "end_index + 1"
-		 * will be evaluated to 0.  Hence this page will be redirtied
-		 * and be written out repeatedly which would result in an
-		 * infinite loop, the user program that perform this operation
-		 * will hang.  Instead, we can verify this situation by checking
-		 * if the page to write is totally beyond the i_size or if it's
-		 * offset is just equal to the EOF.
-		 */
-		if (page->index > end_index ||
-		    (page->index == end_index && offset_into_page == 0))
-			goto redirty;
-
-		/*
-		 * The page straddles i_size.  It must be zeroed out on each
-		 * and every writepage invocation because it may be mmapped.
-		 * "A file is mapped in multiples of the page size.  For a file
-		 * that is not a multiple of the page size, the remaining
-		 * memory is zeroed when mapped, and writes to that region are
-		 * not written out to the file."
-		 */
-		zero_user_segment(page, offset_into_page, PAGE_SIZE);
-
-		/* Adjust the end_offset to the end of file */
-		end_offset = offset;
-	}
-
-	return xfs_writepage_map(wpc, wbc, inode, page, end_offset);
-
-redirty:
-	redirty_page_for_writepage(wbc, page);
-	unlock_page(page);
-	return 0;
-}
+static const struct iomap_writeback_ops xfs_writeback_ops = {
+	.map_blocks		= xfs_map_blocks,
+	.submit_ioend		= xfs_submit_ioend,
+	.discard_page		= xfs_discard_page,
+};
 
 STATIC int
 xfs_vm_writepage(
@@ -989,12 +499,8 @@ xfs_vm_writepage(
 	struct writeback_control *wbc)
 {
 	struct xfs_writepage_ctx wpc = { };
-	int			ret;
 
-	ret = xfs_do_writepage(page, wbc, &wpc);
-	if (wpc.ioend)
-		ret = xfs_submit_ioend(wbc, wpc.ioend, ret);
-	return ret;
+	return iomap_writepage(page, wbc, &wpc.ctx, &xfs_writeback_ops);
 }
 
 STATIC int
@@ -1003,13 +509,9 @@ xfs_vm_writepages(
 	struct writeback_control *wbc)
 {
 	struct xfs_writepage_ctx wpc = { };
-	int			ret;
 
 	xfs_iflags_clear(XFS_I(mapping->host), XFS_ITRUNCATED);
-	ret = write_cache_pages(mapping, wbc, xfs_do_writepage, &wpc);
-	if (wpc.ioend)
-		ret = xfs_submit_ioend(wbc, wpc.ioend, ret);
-	return ret;
+	return iomap_writepages(mapping, wbc, &wpc.ctx, &xfs_writeback_ops);
 }
 
 STATIC int
diff --git a/fs/xfs/xfs_aops.h b/fs/xfs/xfs_aops.h
index bf95837c59af..26a7772d4b81 100644
--- a/fs/xfs/xfs_aops.h
+++ b/fs/xfs/xfs_aops.h
@@ -6,22 +6,6 @@
 #ifndef __XFS_AOPS_H__
 #define __XFS_AOPS_H__
 
-extern struct bio_set xfs_ioend_bioset;
-
-/*
- * Structure for buffered I/O completions.
- */
-struct xfs_ioend {
-	struct list_head	io_list;	/* next ioend in chain */
-	u16			io_type;
-	u16			io_flags;
-	struct inode		*io_inode;	/* file being written to */
-	size_t			io_size;	/* size of the extent */
-	xfs_off_t		io_offset;	/* offset in the file */
-	struct bio		*io_bio;	/* bio being built */
-	struct bio		io_inline_bio;	/* MUST BE LAST! */
-};
-
 extern const struct address_space_operations xfs_address_space_operations;
 extern const struct address_space_operations xfs_dax_aops;
 
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index 594c119824cc..52b89e175bc5 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -53,7 +53,6 @@
 #include <linux/parser.h>
 
 static const struct super_operations xfs_super_operations;
-struct bio_set xfs_ioend_bioset;
 
 static struct kset *xfs_kset;		/* top-level xfs sysfs dir */
 #ifdef DEBUG
@@ -1870,15 +1869,10 @@ MODULE_ALIAS_FS("xfs");
 STATIC int __init
 xfs_init_zones(void)
 {
-	if (bioset_init(&xfs_ioend_bioset, 4 * (PAGE_SIZE / SECTOR_SIZE),
-			offsetof(struct xfs_ioend, io_inline_bio),
-			BIOSET_NEED_BVECS))
-		goto out;
-
 	xfs_log_ticket_zone = kmem_zone_init(sizeof(xlog_ticket_t),
 						"xfs_log_ticket");
 	if (!xfs_log_ticket_zone)
-		goto out_free_ioend_bioset;
+		goto out;
 
 	xfs_bmap_free_item_zone = kmem_zone_init(
 			sizeof(struct xfs_extent_free_item),
@@ -2013,8 +2007,6 @@ xfs_init_zones(void)
 	kmem_zone_destroy(xfs_bmap_free_item_zone);
  out_destroy_log_ticket_zone:
 	kmem_zone_destroy(xfs_log_ticket_zone);
- out_free_ioend_bioset:
-	bioset_exit(&xfs_ioend_bioset);
  out:
 	return -ENOMEM;
 }
@@ -2045,7 +2037,6 @@ xfs_destroy_zones(void)
 	kmem_zone_destroy(xfs_btree_cur_zone);
 	kmem_zone_destroy(xfs_bmap_free_item_zone);
 	kmem_zone_destroy(xfs_log_ticket_zone);
-	bioset_exit(&xfs_ioend_bioset);
 }
 
 STATIC int __init
diff --git a/include/linux/iomap.h b/include/linux/iomap.h
index 2103b94cb1bf..e87f44810c53 100644
--- a/include/linux/iomap.h
+++ b/include/linux/iomap.h
@@ -4,6 +4,7 @@
 
 #include <linux/atomic.h>
 #include <linux/bitmap.h>
+#include <linux/blk_types.h>
 #include <linux/mm.h>
 #include <linux/types.h>
 #include <linux/mm_types.h>
@@ -11,6 +12,7 @@
 struct address_space;
 struct fiemap_extent_info;
 struct inode;
+struct iomap_writepage_ctx;
 struct iov_iter;
 struct kiocb;
 struct page;
@@ -165,6 +167,45 @@ loff_t iomap_seek_data(struct inode *inode, loff_t offset,
 sector_t iomap_bmap(struct address_space *mapping, sector_t bno,
 		const struct iomap_ops *ops);
 
+/*
+ * Structure for writeback I/O completions.
+ */
+struct iomap_ioend {
+	struct list_head	io_list;	/* next ioend in chain */
+	u16			io_type;
+	u16			io_flags;
+	struct inode		*io_inode;	/* file being written to */
+	size_t			io_size;	/* size of the extent */
+	loff_t			io_offset;	/* offset in the file */
+	struct bio		*io_bio;	/* bio being built */
+	struct bio		io_inline_bio;	/* MUST BE LAST! */
+};
+
+struct iomap_writeback_ops {
+	int (*map_blocks)(struct iomap_writepage_ctx *wpc, struct inode *inode,
+				loff_t offset);
+	int (*submit_ioend)(struct iomap_ioend *ioend, int status);
+	void (*discard_page)(struct page *page);
+};
+
+struct iomap_writepage_ctx {
+	struct iomap		iomap;
+	struct iomap_ioend	*ioend;
+	const struct iomap_writeback_ops *ops;
+};
+
+void iomap_finish_ioend(struct iomap_ioend *ioend, int error);
+void iomap_finish_ioends(struct iomap_ioend *ioend, int error);
+void iomap_ioend_try_merge(struct iomap_ioend *ioend,
+		struct list_head *more_ioends);
+void iomap_sort_ioends(struct list_head *ioend_list);
+int iomap_writepage(struct page *page, struct writeback_control *wbc,
+		struct iomap_writepage_ctx *wpc,
+		const struct iomap_writeback_ops *ops);
+int iomap_writepages(struct address_space *mapping,
+		struct writeback_control *wbc, struct iomap_writepage_ctx *wpc,
+		const struct iomap_writeback_ops *ops);
+
 /*
  * Flags for direct I/O ->end_io:
  */
-- 
2.20.1


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

* [PATCH 12/12] iomap: add tracing for the address space operations
  2019-06-24  5:52 lift the xfs writepage code into iomap Christoph Hellwig
                   ` (10 preceding siblings ...)
  2019-06-24  5:52 ` [PATCH 11/12] iomap: move the xfs writeback code to iomap.c Christoph Hellwig
@ 2019-06-24  5:52 ` Christoph Hellwig
  2019-06-24 23:49   ` Dave Chinner
  11 siblings, 1 reply; 52+ messages in thread
From: Christoph Hellwig @ 2019-06-24  5:52 UTC (permalink / raw)
  To: Darrick J . Wong
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel

Lift the xfs code for tracing address space operations to the iomap
layer.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 fs/iomap.c                   | 13 +++++-
 fs/xfs/xfs_aops.c            | 27 ++----------
 fs/xfs/xfs_trace.h           | 65 ----------------------------
 include/trace/events/iomap.h | 82 ++++++++++++++++++++++++++++++++++++
 4 files changed, 97 insertions(+), 90 deletions(-)
 create mode 100644 include/trace/events/iomap.h

diff --git a/fs/iomap.c b/fs/iomap.c
index 72a1b622e634..c98107a6bf81 100644
--- a/fs/iomap.c
+++ b/fs/iomap.c
@@ -23,7 +23,8 @@
 #include <linux/task_io_accounting_ops.h>
 #include <linux/dax.h>
 #include <linux/sched/signal.h>
-
+#define CREATE_TRACE_POINTS
+#include <trace/events/iomap.h>
 #include "internal.h"
 
 static struct bio_set iomap_ioend_bioset;
@@ -369,6 +370,8 @@ iomap_readpage(struct page *page, const struct iomap_ops *ops)
 	unsigned poff;
 	loff_t ret;
 
+	trace_iomap_readpage(page->mapping->host, 1);
+
 	for (poff = 0; poff < PAGE_SIZE; poff += ret) {
 		ret = iomap_apply(inode, page_offset(page) + poff,
 				PAGE_SIZE - poff, 0, ops, &ctx,
@@ -465,6 +468,8 @@ iomap_readpages(struct address_space *mapping, struct list_head *pages,
 	loff_t last = page_offset(list_entry(pages->next, struct page, lru));
 	loff_t length = last - pos + PAGE_SIZE, ret = 0;
 
+	trace_iomap_readpages(mapping->host, nr_pages);
+
 	while (length > 0) {
 		ret = iomap_apply(mapping->host, pos, length, 0, ops,
 				&ctx, iomap_readpages_actor);
@@ -531,6 +536,8 @@ EXPORT_SYMBOL_GPL(iomap_is_partially_uptodate);
 int
 iomap_releasepage(struct page *page, gfp_t gfp_mask)
 {
+	trace_iomap_releasepage(page->mapping->host, page, 0, 0);
+
 	/*
 	 * mm accommodates an old ext3 case where clean pages might not have had
 	 * the dirty bit cleared. Thus, it can send actual dirty pages to
@@ -546,6 +553,8 @@ EXPORT_SYMBOL_GPL(iomap_releasepage);
 void
 iomap_invalidatepage(struct page *page, unsigned int offset, unsigned int len)
 {
+	trace_iomap_invalidatepage(page->mapping->host, page, offset, len);
+
 	/*
 	 * If we are invalidating the entire page, clear the dirty state from it
 	 * and release it to avoid unnecessary buildup of the LRU.
@@ -2579,6 +2588,8 @@ iomap_do_writepage(struct page *page, struct writeback_control *wbc, void *data)
 	u64 end_offset;
 	loff_t offset;
 
+	trace_iomap_writepage(inode, page, 0, 0);
+
 	/*
 	 * Refuse to write the page out if we are called from reclaim context.
 	 *
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index 26b838aea2db..a27ecce31c88 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -440,16 +440,6 @@ xfs_submit_ioend(
 	return status;
 }
 
-STATIC void
-xfs_vm_invalidatepage(
-	struct page		*page,
-	unsigned int		offset,
-	unsigned int		length)
-{
-	trace_xfs_invalidatepage(page->mapping->host, page, offset, length);
-	iomap_invalidatepage(page, offset, length);
-}
-
 /*
  * If the page has delalloc blocks on it, we need to punch them out before we
  * invalidate the page.  If we don't, we leave a stale delalloc mapping on the
@@ -484,7 +474,7 @@ xfs_discard_page(
 	if (error && !XFS_FORCED_SHUTDOWN(mp))
 		xfs_alert(mp, "page discard unable to remove delalloc mapping.");
 out_invalidate:
-	xfs_vm_invalidatepage(page, 0, PAGE_SIZE);
+	iomap_invalidatepage(page, 0, PAGE_SIZE);
 }
 
 static const struct iomap_writeback_ops xfs_writeback_ops = {
@@ -524,15 +514,6 @@ xfs_dax_writepages(
 			xfs_find_bdev_for_inode(mapping->host), wbc);
 }
 
-STATIC int
-xfs_vm_releasepage(
-	struct page		*page,
-	gfp_t			gfp_mask)
-{
-	trace_xfs_releasepage(page->mapping->host, page, 0, 0);
-	return iomap_releasepage(page, gfp_mask);
-}
-
 STATIC sector_t
 xfs_vm_bmap(
 	struct address_space	*mapping,
@@ -561,7 +542,6 @@ xfs_vm_readpage(
 	struct file		*unused,
 	struct page		*page)
 {
-	trace_xfs_vm_readpage(page->mapping->host, 1);
 	return iomap_readpage(page, &xfs_iomap_ops);
 }
 
@@ -572,7 +552,6 @@ xfs_vm_readpages(
 	struct list_head	*pages,
 	unsigned		nr_pages)
 {
-	trace_xfs_vm_readpages(mapping->host, nr_pages);
 	return iomap_readpages(mapping, pages, nr_pages, &xfs_iomap_ops);
 }
 
@@ -592,8 +571,8 @@ const struct address_space_operations xfs_address_space_operations = {
 	.writepage		= xfs_vm_writepage,
 	.writepages		= xfs_vm_writepages,
 	.set_page_dirty		= iomap_set_page_dirty,
-	.releasepage		= xfs_vm_releasepage,
-	.invalidatepage		= xfs_vm_invalidatepage,
+	.releasepage		= iomap_releasepage,
+	.invalidatepage		= iomap_invalidatepage,
 	.bmap			= xfs_vm_bmap,
 	.direct_IO		= noop_direct_IO,
 	.migratepage		= iomap_migrate_page,
diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
index 2464ea351f83..051bd7d4769a 100644
--- a/fs/xfs/xfs_trace.h
+++ b/fs/xfs/xfs_trace.h
@@ -1153,71 +1153,6 @@ DEFINE_RW_EVENT(xfs_file_buffered_write);
 DEFINE_RW_EVENT(xfs_file_direct_write);
 DEFINE_RW_EVENT(xfs_file_dax_write);
 
-DECLARE_EVENT_CLASS(xfs_page_class,
-	TP_PROTO(struct inode *inode, struct page *page, unsigned long off,
-		 unsigned int len),
-	TP_ARGS(inode, page, off, len),
-	TP_STRUCT__entry(
-		__field(dev_t, dev)
-		__field(xfs_ino_t, ino)
-		__field(pgoff_t, pgoff)
-		__field(loff_t, size)
-		__field(unsigned long, offset)
-		__field(unsigned int, length)
-	),
-	TP_fast_assign(
-		__entry->dev = inode->i_sb->s_dev;
-		__entry->ino = XFS_I(inode)->i_ino;
-		__entry->pgoff = page_offset(page);
-		__entry->size = i_size_read(inode);
-		__entry->offset = off;
-		__entry->length = len;
-	),
-	TP_printk("dev %d:%d ino 0x%llx pgoff 0x%lx size 0x%llx offset %lx "
-		  "length %x",
-		  MAJOR(__entry->dev), MINOR(__entry->dev),
-		  __entry->ino,
-		  __entry->pgoff,
-		  __entry->size,
-		  __entry->offset,
-		  __entry->length)
-)
-
-#define DEFINE_PAGE_EVENT(name)		\
-DEFINE_EVENT(xfs_page_class, name,	\
-	TP_PROTO(struct inode *inode, struct page *page, unsigned long off, \
-		 unsigned int len),	\
-	TP_ARGS(inode, page, off, len))
-DEFINE_PAGE_EVENT(xfs_writepage);
-DEFINE_PAGE_EVENT(xfs_releasepage);
-DEFINE_PAGE_EVENT(xfs_invalidatepage);
-
-DECLARE_EVENT_CLASS(xfs_readpage_class,
-	TP_PROTO(struct inode *inode, int nr_pages),
-	TP_ARGS(inode, nr_pages),
-	TP_STRUCT__entry(
-		__field(dev_t, dev)
-		__field(xfs_ino_t, ino)
-		__field(int, nr_pages)
-	),
-	TP_fast_assign(
-		__entry->dev = inode->i_sb->s_dev;
-		__entry->ino = inode->i_ino;
-		__entry->nr_pages = nr_pages;
-	),
-	TP_printk("dev %d:%d ino 0x%llx nr_pages %d",
-		  MAJOR(__entry->dev), MINOR(__entry->dev),
-		  __entry->ino,
-		  __entry->nr_pages)
-)
-
-#define DEFINE_READPAGE_EVENT(name)		\
-DEFINE_EVENT(xfs_readpage_class, name,	\
-	TP_PROTO(struct inode *inode, int nr_pages), \
-	TP_ARGS(inode, nr_pages))
-DEFINE_READPAGE_EVENT(xfs_vm_readpage);
-DEFINE_READPAGE_EVENT(xfs_vm_readpages);
-
 DECLARE_EVENT_CLASS(xfs_imap_class,
 	TP_PROTO(struct xfs_inode *ip, xfs_off_t offset, ssize_t count,
 		 int whichfork, struct xfs_bmbt_irec *irec),
diff --git a/include/trace/events/iomap.h b/include/trace/events/iomap.h
new file mode 100644
index 000000000000..da50ece663f8
--- /dev/null
+++ b/include/trace/events/iomap.h
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2009-2019, Christoph Hellwig
+ * All Rights Reserved.
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM iomap
+
+#if !defined(_TRACE_IOMAP_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_IOMAP_H
+
+#include <linux/tracepoint.h>
+
+DECLARE_EVENT_CLASS(iomap_page_class,
+	TP_PROTO(struct inode *inode, struct page *page, unsigned long off,
+		 unsigned int len),
+	TP_ARGS(inode, page, off, len),
+	TP_STRUCT__entry(
+		__field(dev_t, dev)
+		__field(u64, ino)
+		__field(pgoff_t, pgoff)
+		__field(loff_t, size)
+		__field(unsigned long, offset)
+		__field(unsigned int, length)
+	),
+	TP_fast_assign(
+		__entry->dev = inode->i_sb->s_dev;
+		__entry->ino = inode->i_ino;
+		__entry->pgoff = page_offset(page);
+		__entry->size = i_size_read(inode);
+		__entry->offset = off;
+		__entry->length = len;
+	),
+	TP_printk("dev %d:%d ino 0x%llx pgoff 0x%lx size 0x%llx offset %lx "
+		  "length %x",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  __entry->ino,
+		  __entry->pgoff,
+		  __entry->size,
+		  __entry->offset,
+		  __entry->length)
+)
+
+#define DEFINE_PAGE_EVENT(name)		\
+DEFINE_EVENT(iomap_page_class, name,	\
+	TP_PROTO(struct inode *inode, struct page *page, unsigned long off, \
+		 unsigned int len),	\
+	TP_ARGS(inode, page, off, len))
+DEFINE_PAGE_EVENT(iomap_writepage);
+DEFINE_PAGE_EVENT(iomap_releasepage);
+DEFINE_PAGE_EVENT(iomap_invalidatepage);
+
+DECLARE_EVENT_CLASS(iomap_readpage_class,
+	TP_PROTO(struct inode *inode, int nr_pages),
+	TP_ARGS(inode, nr_pages),
+	TP_STRUCT__entry(
+		__field(dev_t, dev)
+		__field(u64, ino)
+		__field(int, nr_pages)
+	),
+	TP_fast_assign(
+		__entry->dev = inode->i_sb->s_dev;
+		__entry->ino = inode->i_ino;
+		__entry->nr_pages = nr_pages;
+	),
+	TP_printk("dev %d:%d ino 0x%llx nr_pages %d",
+		  MAJOR(__entry->dev), MINOR(__entry->dev),
+		  __entry->ino,
+		  __entry->nr_pages)
+)
+
+#define DEFINE_READPAGE_EVENT(name)		\
+DEFINE_EVENT(iomap_readpage_class, name,	\
+	TP_PROTO(struct inode *inode, int nr_pages), \
+	TP_ARGS(inode, nr_pages))
+DEFINE_READPAGE_EVENT(iomap_readpage);
+DEFINE_READPAGE_EVENT(iomap_readpages);
+
+#endif /* _TRACE_IOMAP_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
-- 
2.20.1


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

* Re: [PATCH 01/12] list.h: add a list_pop helper
  2019-06-24  5:52 ` [PATCH 01/12] list.h: add a list_pop helper Christoph Hellwig
@ 2019-06-24 14:53   ` Darrick J. Wong
  2019-06-24 15:51   ` Matthew Wilcox
  1 sibling, 0 replies; 52+ messages in thread
From: Darrick J. Wong @ 2019-06-24 14:53 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel

On Mon, Jun 24, 2019 at 07:52:42AM +0200, Christoph Hellwig wrote:
> We have a very common pattern where we want to delete the first entry
> from a list and return it as the properly typed container structure.
> 
> Add a list_pop helper to implement this behavior.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
>  include/linux/list.h | 22 ++++++++++++++++++++++
>  1 file changed, 22 insertions(+)
> 
> diff --git a/include/linux/list.h b/include/linux/list.h
> index e951228db4b2..e07a5f54cc9d 100644
> --- a/include/linux/list.h
> +++ b/include/linux/list.h
> @@ -500,6 +500,28 @@ static inline void list_splice_tail_init(struct list_head *list,
>  	pos__ != head__ ? list_entry(pos__, type, member) : NULL; \
>  })
>  
> +/**
> + * list_pop - delete the first entry from a list and return it
> + * @list:	the list to take the element from.
> + * @type:	the type of the struct this is embedded in.
> + * @member:	the name of the list_head within the struct.
> + *
> + * Note that if the list is empty, it returns NULL.
> + */
> +#define list_pop(list, type, member) 				\

Unnecessary space after the ')' here, though I can fix that up if I
merge this version of the series...

Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

--D


> +({								\
> +	struct list_head *head__ = (list);			\
> +	struct list_head *pos__ = READ_ONCE(head__->next);	\
> +	type *entry__ = NULL;					\
> +								\
> +	if (pos__ != head__) {					\
> +		entry__ = list_entry(pos__, type, member);	\
> +		list_del(pos__);				\
> +	}							\
> +								\
> +	entry__;						\
> +})
> +
>  /**
>   * list_next_entry - get the next element in list
>   * @pos:	the type * to cursor
> -- 
> 2.20.1
> 

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

* Re: [PATCH 03/12] xfs: fix a comment typo in xfs_submit_ioend
  2019-06-24  5:52 ` [PATCH 03/12] xfs: fix a comment typo in xfs_submit_ioend Christoph Hellwig
@ 2019-06-24 14:53   ` Darrick J. Wong
  0 siblings, 0 replies; 52+ messages in thread
From: Darrick J. Wong @ 2019-06-24 14:53 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel

On Mon, Jun 24, 2019 at 07:52:44AM +0200, Christoph Hellwig wrote:
> The fail argument is long gone, update the comment.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>

Looks ok,
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

--D

> ---
>  fs/xfs/xfs_aops.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
> index 9cceb90e77c5..dc60aec0c5a7 100644
> --- a/fs/xfs/xfs_aops.c
> +++ b/fs/xfs/xfs_aops.c
> @@ -626,7 +626,7 @@ xfs_map_blocks(
>   * reference to the ioend to ensure that the ioend completion is only done once
>   * all bios have been submitted and the ioend is really done.
>   *
> - * If @fail is non-zero, it means that we have a situation where some part of
> + * If @status is non-zero, it means that we have a situation where some part of
>   * the submission process has failed after we have marked paged for writeback
>   * and unlocked them. In this situation, we need to fail the bio and ioend
>   * rather than submit it to IO. This typically only happens on a filesystem
> -- 
> 2.20.1
> 

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

* Re: [PATCH 04/12] xfs: initialize ioma->flags in xfs_bmbt_to_iomap
  2019-06-24  5:52 ` [PATCH 04/12] xfs: initialize ioma->flags in xfs_bmbt_to_iomap Christoph Hellwig
@ 2019-06-24 14:57   ` Darrick J. Wong
  2019-06-25 10:07     ` Christoph Hellwig
  0 siblings, 1 reply; 52+ messages in thread
From: Darrick J. Wong @ 2019-06-24 14:57 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel

On Mon, Jun 24, 2019 at 07:52:45AM +0200, Christoph Hellwig wrote:
> Currently we don't overwrite the flags field in the iomap in
> xfs_bmbt_to_iomap.  This works fine with 0-initialized iomaps on stack,
> but is harmful once we want to be able to reuse an iomap in the
> writeback code.

Is that going to affect all the other iomap users, or is xfs the only
one that assumes zero-initialized iomaps being passed into
->iomap_begin?

--D

> Replace the shared paramter with a set of initial
> flags an thus ensures the flags field is always reinitialized.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
>  fs/xfs/xfs_iomap.c | 28 +++++++++++++++++-----------
>  fs/xfs/xfs_iomap.h |  2 +-
>  fs/xfs/xfs_pnfs.c  |  2 +-
>  3 files changed, 19 insertions(+), 13 deletions(-)
> 
> diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
> index 63d323916bba..6b29452bfba0 100644
> --- a/fs/xfs/xfs_iomap.c
> +++ b/fs/xfs/xfs_iomap.c
> @@ -57,7 +57,7 @@ xfs_bmbt_to_iomap(
>  	struct xfs_inode	*ip,
>  	struct iomap		*iomap,
>  	struct xfs_bmbt_irec	*imap,
> -	bool			shared)
> +	u16			flags)
>  {
>  	struct xfs_mount	*mp = ip->i_mount;
>  
> @@ -82,12 +82,11 @@ xfs_bmbt_to_iomap(
>  	iomap->length = XFS_FSB_TO_B(mp, imap->br_blockcount);
>  	iomap->bdev = xfs_find_bdev_for_inode(VFS_I(ip));
>  	iomap->dax_dev = xfs_find_daxdev_for_inode(VFS_I(ip));
> +	iomap->flags = flags;
>  
>  	if (xfs_ipincount(ip) &&
>  	    (ip->i_itemp->ili_fsync_fields & ~XFS_ILOG_TIMESTAMP))
>  		iomap->flags |= IOMAP_F_DIRTY;
> -	if (shared)
> -		iomap->flags |= IOMAP_F_SHARED;
>  	return 0;
>  }
>  
> @@ -543,6 +542,7 @@ xfs_file_iomap_begin_delay(
>  	struct xfs_iext_cursor	icur, ccur;
>  	xfs_fsblock_t		prealloc_blocks = 0;
>  	bool			eof = false, cow_eof = false, shared = false;
> +	u16			iomap_flags = 0;
>  	int			whichfork = XFS_DATA_FORK;
>  	int			error = 0;
>  
> @@ -710,7 +710,7 @@ xfs_file_iomap_begin_delay(
>  	 * Flag newly allocated delalloc blocks with IOMAP_F_NEW so we punch
>  	 * them out if the write happens to fail.
>  	 */
> -	iomap->flags |= IOMAP_F_NEW;
> +	iomap_flags |= IOMAP_F_NEW;
>  	trace_xfs_iomap_alloc(ip, offset, count, whichfork,
>  			whichfork == XFS_DATA_FORK ? &imap : &cmap);
>  done:
> @@ -718,14 +718,17 @@ xfs_file_iomap_begin_delay(
>  		if (imap.br_startoff > offset_fsb) {
>  			xfs_trim_extent(&cmap, offset_fsb,
>  					imap.br_startoff - offset_fsb);
> -			error = xfs_bmbt_to_iomap(ip, iomap, &cmap, true);
> +			error = xfs_bmbt_to_iomap(ip, iomap, &cmap,
> +					IOMAP_F_SHARED);
>  			goto out_unlock;
>  		}
>  		/* ensure we only report blocks we have a reservation for */
>  		xfs_trim_extent(&imap, cmap.br_startoff, cmap.br_blockcount);
>  		shared = true;
>  	}
> -	error = xfs_bmbt_to_iomap(ip, iomap, &imap, shared);
> +	if (shared)
> +		iomap_flags |= IOMAP_F_SHARED;
> +	error = xfs_bmbt_to_iomap(ip, iomap, &imap, iomap_flags);
>  out_unlock:
>  	xfs_iunlock(ip, XFS_ILOCK_EXCL);
>  	return error;
> @@ -933,6 +936,7 @@ xfs_file_iomap_begin(
>  	xfs_fileoff_t		offset_fsb, end_fsb;
>  	int			nimaps = 1, error = 0;
>  	bool			shared = false;
> +	u16			iomap_flags = 0;
>  	unsigned		lockmode;
>  
>  	if (XFS_FORCED_SHUTDOWN(mp))
> @@ -1048,11 +1052,13 @@ xfs_file_iomap_begin(
>  	if (error)
>  		return error;
>  
> -	iomap->flags |= IOMAP_F_NEW;
> +	iomap_flags |= IOMAP_F_NEW;
>  	trace_xfs_iomap_alloc(ip, offset, length, XFS_DATA_FORK, &imap);
>  
>  out_finish:
> -	return xfs_bmbt_to_iomap(ip, iomap, &imap, shared);
> +	if (shared)
> +		iomap_flags |= IOMAP_F_SHARED;
> +	return xfs_bmbt_to_iomap(ip, iomap, &imap, iomap_flags);
>  
>  out_found:
>  	ASSERT(nimaps);
> @@ -1196,7 +1202,7 @@ xfs_seek_iomap_begin(
>  		if (data_fsb < cow_fsb + cmap.br_blockcount)
>  			end_fsb = min(end_fsb, data_fsb);
>  		xfs_trim_extent(&cmap, offset_fsb, end_fsb);
> -		error = xfs_bmbt_to_iomap(ip, iomap, &cmap, true);
> +		error = xfs_bmbt_to_iomap(ip, iomap, &cmap, IOMAP_F_SHARED);
>  		/*
>  		 * This is a COW extent, so we must probe the page cache
>  		 * because there could be dirty page cache being backed
> @@ -1218,7 +1224,7 @@ xfs_seek_iomap_begin(
>  	imap.br_state = XFS_EXT_NORM;
>  done:
>  	xfs_trim_extent(&imap, offset_fsb, end_fsb);
> -	error = xfs_bmbt_to_iomap(ip, iomap, &imap, false);
> +	error = xfs_bmbt_to_iomap(ip, iomap, &imap, 0);
>  out_unlock:
>  	xfs_iunlock(ip, lockmode);
>  	return error;
> @@ -1264,7 +1270,7 @@ xfs_xattr_iomap_begin(
>  	if (error)
>  		return error;
>  	ASSERT(nimaps);
> -	return xfs_bmbt_to_iomap(ip, iomap, &imap, false);
> +	return xfs_bmbt_to_iomap(ip, iomap, &imap, 0);
>  }
>  
>  const struct iomap_ops xfs_xattr_iomap_ops = {
> diff --git a/fs/xfs/xfs_iomap.h b/fs/xfs/xfs_iomap.h
> index 5c2f6aa6d78f..71d0ae460c44 100644
> --- a/fs/xfs/xfs_iomap.h
> +++ b/fs/xfs/xfs_iomap.h
> @@ -16,7 +16,7 @@ int xfs_iomap_write_direct(struct xfs_inode *, xfs_off_t, size_t,
>  int xfs_iomap_write_unwritten(struct xfs_inode *, xfs_off_t, xfs_off_t, bool);
>  
>  int xfs_bmbt_to_iomap(struct xfs_inode *, struct iomap *,
> -		struct xfs_bmbt_irec *, bool shared);
> +		struct xfs_bmbt_irec *, u16);
>  xfs_extlen_t xfs_eof_alignment(struct xfs_inode *ip, xfs_extlen_t extsize);
>  
>  static inline xfs_filblks_t
> diff --git a/fs/xfs/xfs_pnfs.c b/fs/xfs/xfs_pnfs.c
> index bde2c9f56a46..12f664785248 100644
> --- a/fs/xfs/xfs_pnfs.c
> +++ b/fs/xfs/xfs_pnfs.c
> @@ -185,7 +185,7 @@ xfs_fs_map_blocks(
>  	}
>  	xfs_iunlock(ip, XFS_IOLOCK_EXCL);
>  
> -	error = xfs_bmbt_to_iomap(ip, iomap, &imap, false);
> +	error = xfs_bmbt_to_iomap(ip, iomap, &imap, 0);
>  	*device_generation = mp->m_generation;
>  	return error;
>  out_unlock:
> -- 
> 2.20.1
> 

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

* Re: [PATCH 02/12] xfs: simplify xfs_chain_bio
  2019-06-24  5:52 ` [PATCH 02/12] xfs: simplify xfs_chain_bio Christoph Hellwig
@ 2019-06-24 15:14   ` Darrick J. Wong
  0 siblings, 0 replies; 52+ messages in thread
From: Darrick J. Wong @ 2019-06-24 15:14 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel

On Mon, Jun 24, 2019 at 07:52:43AM +0200, Christoph Hellwig wrote:
> Move setting up operation and write hint to xfs_alloc_ioend, and
> then just copy over all needed information from the previous bio
> in xfs_chain_bio and stop passing various parameters to it.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>

I always thought it was a little odd that we were still setting bio
fields in the submission function...

Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

--D

> ---
>  fs/xfs/xfs_aops.c | 35 +++++++++++++++++------------------
>  1 file changed, 17 insertions(+), 18 deletions(-)
> 
> diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
> index a6f0f4761a37..9cceb90e77c5 100644
> --- a/fs/xfs/xfs_aops.c
> +++ b/fs/xfs/xfs_aops.c
> @@ -665,7 +665,6 @@ xfs_submit_ioend(
>  
>  	ioend->io_bio->bi_private = ioend;
>  	ioend->io_bio->bi_end_io = xfs_end_bio;
> -	ioend->io_bio->bi_opf = REQ_OP_WRITE | wbc_to_write_flags(wbc);
>  
>  	/*
>  	 * If we are failing the IO now, just mark the ioend with an
> @@ -679,7 +678,6 @@ xfs_submit_ioend(
>  		return status;
>  	}
>  
> -	ioend->io_bio->bi_write_hint = ioend->io_inode->i_write_hint;
>  	submit_bio(ioend->io_bio);
>  	return 0;
>  }
> @@ -691,7 +689,8 @@ xfs_alloc_ioend(
>  	xfs_exntst_t		state,
>  	xfs_off_t		offset,
>  	struct block_device	*bdev,
> -	sector_t		sector)
> +	sector_t		sector,
> +	struct writeback_control *wbc)
>  {
>  	struct xfs_ioend	*ioend;
>  	struct bio		*bio;
> @@ -699,6 +698,8 @@ xfs_alloc_ioend(
>  	bio = bio_alloc_bioset(GFP_NOFS, BIO_MAX_PAGES, &xfs_ioend_bioset);
>  	bio_set_dev(bio, bdev);
>  	bio->bi_iter.bi_sector = sector;
> +	bio->bi_opf = REQ_OP_WRITE | wbc_to_write_flags(wbc);
> +	bio->bi_write_hint = inode->i_write_hint;
>  
>  	ioend = container_of(bio, struct xfs_ioend, io_inline_bio);
>  	INIT_LIST_HEAD(&ioend->io_list);
> @@ -719,24 +720,22 @@ xfs_alloc_ioend(
>   * so that the bi_private linkage is set up in the right direction for the
>   * traversal in xfs_destroy_ioend().
>   */
> -static void
> +static struct bio *
>  xfs_chain_bio(
> -	struct xfs_ioend	*ioend,
> -	struct writeback_control *wbc,
> -	struct block_device	*bdev,
> -	sector_t		sector)
> +	struct bio		*prev)
>  {
>  	struct bio *new;
>  
>  	new = bio_alloc(GFP_NOFS, BIO_MAX_PAGES);
> -	bio_set_dev(new, bdev);
> -	new->bi_iter.bi_sector = sector;
> -	bio_chain(ioend->io_bio, new);
> -	bio_get(ioend->io_bio);		/* for xfs_destroy_ioend */
> -	ioend->io_bio->bi_opf = REQ_OP_WRITE | wbc_to_write_flags(wbc);
> -	ioend->io_bio->bi_write_hint = ioend->io_inode->i_write_hint;
> -	submit_bio(ioend->io_bio);
> -	ioend->io_bio = new;
> +	bio_copy_dev(new, prev);
> +	new->bi_iter.bi_sector = bio_end_sector(prev);
> +	new->bi_opf = prev->bi_opf;
> +	new->bi_write_hint = prev->bi_write_hint;
> +
> +	bio_chain(prev, new);
> +	bio_get(prev);		/* for xfs_destroy_ioend */
> +	submit_bio(prev);
> +	return new;
>  }
>  
>  /*
> @@ -771,14 +770,14 @@ xfs_add_to_ioend(
>  		if (wpc->ioend)
>  			list_add(&wpc->ioend->io_list, iolist);
>  		wpc->ioend = xfs_alloc_ioend(inode, wpc->fork,
> -				wpc->imap.br_state, offset, bdev, sector);
> +				wpc->imap.br_state, offset, bdev, sector, wbc);
>  	}
>  
>  	if (!__bio_try_merge_page(wpc->ioend->io_bio, page, len, poff, true)) {
>  		if (iop)
>  			atomic_inc(&iop->write_count);
>  		if (bio_full(wpc->ioend->io_bio))
> -			xfs_chain_bio(wpc->ioend, wbc, bdev, sector);
> +			wpc->ioend->io_bio = xfs_chain_bio(wpc->ioend->io_bio);
>  		bio_add_page(wpc->ioend->io_bio, page, len, poff);
>  	}
>  
> -- 
> 2.20.1
> 

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

* Re: [PATCH 11/12] iomap: move the xfs writeback code to iomap.c
  2019-06-24  5:52 ` [PATCH 11/12] iomap: move the xfs writeback code to iomap.c Christoph Hellwig
@ 2019-06-24 15:46   ` Darrick J. Wong
  2019-06-25 10:08     ` Christoph Hellwig
  2019-06-24 23:43   ` Dave Chinner
  1 sibling, 1 reply; 52+ messages in thread
From: Darrick J. Wong @ 2019-06-24 15:46 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel

On Mon, Jun 24, 2019 at 07:52:52AM +0200, Christoph Hellwig wrote:
> Takes the xfs writeback code and move it to iomap.c.  A new structure
> with three methods is added as the abstraction from the generic
> writeback code to the file system.  These methods are used to map
> blocks, submit an ioend, and cancel a page that encountered an error
> before it was added to an ioend.
> 
> Note that we temporarily lose the writepage tracing, but that will
> be added back soon.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
>  fs/iomap.c            | 521 ++++++++++++++++++++++++++++++++++++-
>  fs/xfs/xfs_aops.c     | 584 ++++--------------------------------------
>  fs/xfs/xfs_aops.h     |  16 --
>  fs/xfs/xfs_super.c    |  11 +-
>  include/linux/iomap.h |  41 +++
>  5 files changed, 605 insertions(+), 568 deletions(-)
> 
> diff --git a/fs/iomap.c b/fs/iomap.c
> index 23ef63fd1669..72a1b622e634 100644
> --- a/fs/iomap.c
> +++ b/fs/iomap.c
> @@ -1,7 +1,7 @@
>  // SPDX-License-Identifier: GPL-2.0
>  /*
>   * Copyright (C) 2010 Red Hat, Inc.
> - * Copyright (c) 2016-2018 Christoph Hellwig.
> + * Copyright (c) 2016-2019 Christoph Hellwig.
>   */
>  #include <linux/module.h>
>  #include <linux/compiler.h>
> @@ -12,6 +12,7 @@
>  #include <linux/migrate.h>
>  #include <linux/mm.h>
>  #include <linux/mm_inline.h>
> +#include <linux/list_sort.h>
>  #include <linux/swap.h>
>  #include <linux/pagemap.h>
>  #include <linux/pagevec.h>
> @@ -25,6 +26,8 @@
>  
>  #include "internal.h"
>  
> +static struct bio_set iomap_ioend_bioset;
> +
>  /*
>   * Execute a iomap write on a segment of the mapping that spans a
>   * contiguous range of pages that have identical block mapping state.
> @@ -2192,3 +2195,519 @@ iomap_bmap(struct address_space *mapping, sector_t bno,

This looks like a straight code copy from fs/xfs/ into fs/iomap.c.
That's fine with me, but seeing as this file is now ~2700 lines long,
perhaps we should break this up among major functional lines?

Looking at fs/iomap.c, I see...

 * Basic iomap iterator functions (~40 lines)
 * Page cache management (readpage*, write, mkwrite) (~860 lines)
 * Zeroing (~80 lines)
 * FIEMAP and seek hole / seek data (~300 lines)
 * directio (~500 lines)
 * swapfiles (~170 lines)
 * and now, page cache writeback (~520 lines)

If I have spare time this week (ha ha) I'll see if I can break all this
up (as a separate patch series), so for this:

Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

--D

>  	return bno;
>  }
>  EXPORT_SYMBOL_GPL(iomap_bmap);
> +
> +static void
> +iomap_finish_page_writeback(struct inode *inode, struct bio_vec *bvec,
> +		int error)
> +{
> +	struct iomap_page *iop = to_iomap_page(bvec->bv_page);
> +
> +	if (error) {
> +		SetPageError(bvec->bv_page);
> +		mapping_set_error(inode->i_mapping, -EIO);
> +	}
> +
> +	WARN_ON_ONCE(i_blocksize(inode) < PAGE_SIZE && !iop);
> +	WARN_ON_ONCE(iop && atomic_read(&iop->write_count) <= 0);
> +
> +	if (!iop || atomic_dec_and_test(&iop->write_count))
> +		end_page_writeback(bvec->bv_page);
> +}
> +
> +/*
> + * We're now finished for good with this ioend structure.  Update the page
> + * state, release holds on bios, and finally free up memory.  Do not use the
> + * ioend after this.
> + */
> +void
> +iomap_finish_ioend(struct iomap_ioend *ioend, int error)
> +{
> +	struct inode *inode = ioend->io_inode;
> +	struct bio *bio = &ioend->io_inline_bio;
> +	struct bio *last = ioend->io_bio, *next;
> +	u64 start = bio->bi_iter.bi_sector;
> +	bool quiet = bio_flagged(bio, BIO_QUIET);
> +
> +	for (bio = &ioend->io_inline_bio; bio; bio = next) {
> +		struct bio_vec	*bvec;
> +		struct bvec_iter_all iter_all;
> +
> +		/*
> +		 * For the last bio, bi_private points to the ioend, so we
> +		 * need to explicitly end the iteration here.
> +		 */
> +		if (bio == last)
> +			next = NULL;
> +		else
> +			next = bio->bi_private;
> +
> +		/* walk each page on bio, ending page IO on them */
> +		bio_for_each_segment_all(bvec, bio, iter_all)
> +			iomap_finish_page_writeback(inode, bvec, error);
> +		bio_put(bio);
> +	}
> +
> +	if (unlikely(error && !quiet)) {
> +		printk_ratelimited(KERN_ERR
> +			"%s: writeback error on sector %llu",
> +			inode->i_sb->s_id, start);
> +	}
> +}
> +EXPORT_SYMBOL_GPL(iomap_finish_ioend);
> +
> +void
> +iomap_finish_ioends(struct iomap_ioend *ioend, int error)
> +{
> +	struct list_head tmp;
> +
> +	list_replace_init(&ioend->io_list, &tmp);
> +	iomap_finish_ioend(ioend, error);
> +	while ((ioend = list_pop(&tmp, struct iomap_ioend, io_list)))
> +		iomap_finish_ioend(ioend, error);
> +}
> +EXPORT_SYMBOL_GPL(iomap_finish_ioends);
> +
> +/*
> + * We can merge two adjacent ioends if they have the same set of work to do.
> + */
> +static bool
> +iomap_ioend_can_merge(struct iomap_ioend *ioend, struct iomap_ioend *next)
> +{
> +	if (ioend->io_bio->bi_status != next->io_bio->bi_status)
> +		return false;
> +	if ((ioend->io_flags & IOMAP_F_SHARED) ^
> +	    (next->io_flags & IOMAP_F_SHARED))
> +		return false;
> +	if ((ioend->io_type == IOMAP_UNWRITTEN) ^
> +	    (next->io_type == IOMAP_UNWRITTEN))
> +		return false;
> +	if (ioend->io_offset + ioend->io_size != next->io_offset)
> +		return false;
> +	return true;
> +}
> +
> +void
> +iomap_ioend_try_merge(struct iomap_ioend *ioend, struct list_head *more_ioends)
> +{
> +	struct iomap_ioend *next;
> +
> +	INIT_LIST_HEAD(&ioend->io_list);
> +
> +	while ((next = list_first_entry_or_null(more_ioends, struct iomap_ioend,
> +			io_list))) {
> +		if (!iomap_ioend_can_merge(ioend, next))
> +			break;
> +		list_move_tail(&next->io_list, &ioend->io_list);
> +		ioend->io_size += next->io_size;
> +	}
> +}
> +EXPORT_SYMBOL_GPL(iomap_ioend_try_merge);
> +
> +static int
> +iomap_ioend_compare(void *priv, struct list_head *a, struct list_head *b)
> +{
> +	struct iomap_ioend *ia, *ib;
> +
> +	ia = container_of(a, struct iomap_ioend, io_list);
> +	ib = container_of(b, struct iomap_ioend, io_list);
> +	if (ia->io_offset < ib->io_offset)
> +		return -1;
> +	else if (ia->io_offset > ib->io_offset)
> +		return 1;
> +	return 0;
> +}
> +
> +void
> +iomap_sort_ioends(struct list_head *ioend_list)
> +{
> +	list_sort(NULL, ioend_list, iomap_ioend_compare);
> +}
> +EXPORT_SYMBOL_GPL(iomap_sort_ioends);
> +
> +/*
> + * Submit the bio for an ioend. We are passed an ioend with a bio attached to
> + * it, and we submit that bio. The ioend may be used for multiple bio
> + * submissions, so we only want to allocate an append transaction for the ioend
> + * once. In the case of multiple bio submission, each bio will take an IO
> + * reference to the ioend to ensure that the ioend completion is only done once
> + * all bios have been submitted and the ioend is really done.
> + *
> + * If @error is non-zero, it means that we have a situation where some part of
> + * the submission process has failed after we have marked paged for writeback
> + * and unlocked them. In this situation, we need to fail the bio and ioend
> + * rather than submit it to IO. This typically only happens on a filesystem
> + * shutdown.
> + */
> +static int
> +iomap_submit_ioend(struct iomap_writepage_ctx *wpc, struct iomap_ioend *ioend,
> +		int error)
> +{
> +	/*
> +	 * If we are failing the IO now, just mark the ioend with an error and
> +	 * finish it.  This will run IO completion immediately as there is only
> +	 * one reference to the ioend at this point in time.
> +	 */
> +	ioend->io_bio->bi_private = ioend;
> +	error = wpc->ops->submit_ioend(ioend, error);
> +	if (error) {
> +		ioend->io_bio->bi_status = errno_to_blk_status(error);
> +		bio_endio(ioend->io_bio);
> +		return error;
> +	}
> +
> +	submit_bio(ioend->io_bio);
> +	return 0;
> +}
> +
> +static struct iomap_ioend *
> +iomap_alloc_ioend(struct inode *inode, struct iomap_writepage_ctx *wpc,
> +		loff_t offset, sector_t sector, struct writeback_control *wbc)
> +{
> +	struct iomap_ioend *ioend;
> +	struct bio *bio;
> +
> +	bio = bio_alloc_bioset(GFP_NOFS, BIO_MAX_PAGES, &iomap_ioend_bioset);
> +	bio_set_dev(bio, wpc->iomap.bdev);
> +	bio->bi_iter.bi_sector = sector;
> +	bio->bi_opf = REQ_OP_WRITE | wbc_to_write_flags(wbc);
> +	bio->bi_write_hint = inode->i_write_hint;
> +
> +	ioend = container_of(bio, struct iomap_ioend, io_inline_bio);
> +	INIT_LIST_HEAD(&ioend->io_list);
> +	ioend->io_type = wpc->iomap.type;
> +	ioend->io_flags = wpc->iomap.flags;
> +	ioend->io_inode = inode;
> +	ioend->io_size = 0;
> +	ioend->io_offset = offset;
> +	ioend->io_bio = bio;
> +	return ioend;
> +}
> +
> +/*
> + * Allocate a new bio, and chain the old bio to the new one.
> + *
> + * Note that we have to do perform the chaining in this unintuitive order
> + * so that the bi_private linkage is set up in the right direction for the
> + * traversal in iomap_finish_ioend().
> + */
> +static struct bio *
> +iomap_chain_bio(struct bio *prev)
> +{
> +	struct bio *new;
> +
> +	new = bio_alloc(GFP_NOFS, BIO_MAX_PAGES);
> +	bio_copy_dev(new, prev);
> +	new->bi_iter.bi_sector = bio_end_sector(prev);
> +	new->bi_opf = prev->bi_opf;
> +	new->bi_write_hint = prev->bi_write_hint;
> +
> +	bio_chain(prev, new);
> +	bio_get(prev);		/* for iomap_finish_ioend */
> +	submit_bio(prev);
> +	return new;
> +}
> +
> +/*
> + * Test to see if we have an existing ioend structure that we could append to
> + * first, otherwise finish off the current ioend and start another.
> + */
> +static void
> +iomap_add_to_ioend(struct inode *inode, loff_t offset, struct page *page,
> +		struct iomap_page *iop, struct iomap_writepage_ctx *wpc,
> +		struct writeback_control *wbc, struct list_head *iolist)
> +{
> +	unsigned len = i_blocksize(inode);
> +	unsigned poff = offset & (PAGE_SIZE - 1);
> +	sector_t sector = iomap_sector(&wpc->iomap, offset);
> +
> +	if (!wpc->ioend ||
> +	    (wpc->iomap.flags & IOMAP_F_SHARED) !=
> +	    (wpc->ioend->io_flags & IOMAP_F_SHARED) ||
> +	    wpc->iomap.type != wpc->ioend->io_type ||
> +	    sector != bio_end_sector(wpc->ioend->io_bio) ||
> +	    offset != wpc->ioend->io_offset + wpc->ioend->io_size) {
> +		if (wpc->ioend)
> +			list_add(&wpc->ioend->io_list, iolist);
> +		wpc->ioend = iomap_alloc_ioend(inode, wpc, offset, sector, wbc);
> +	}
> +
> +	if (!__bio_try_merge_page(wpc->ioend->io_bio, page, len, poff, true)) {
> +		if (iop)
> +			atomic_inc(&iop->write_count);
> +		if (bio_full(wpc->ioend->io_bio)) {
> +			wpc->ioend->io_bio =
> +				iomap_chain_bio(wpc->ioend->io_bio);
> +		}
> +		bio_add_page(wpc->ioend->io_bio, page, len, poff);
> +	}
> +
> +	wpc->ioend->io_size += len;
> +}
> +
> +/*
> + * We implement an immediate ioend submission policy here to avoid needing to
> + * chain multiple ioends and hence nest mempool allocations which can violate
> + * forward progress guarantees we need to provide. The current ioend we are
> + * adding blocks to is cached on the writepage context, and if the new block
> + * does not append to the cached ioend it will create a new ioend and cache that
> + * instead.
> + *
> + * If a new ioend is created and cached, the old ioend is returned and queued
> + * locally for submission once the entire page is processed or an error has been
> + * detected.  While ioends are submitted immediately after they are completed,
> + * batching optimisations are provided by higher level block plugging.
> + *
> + * At the end of a writeback pass, there will be a cached ioend remaining on the
> + * writepage context that the caller will need to submit.
> + */
> +static int
> +iomap_writepage_map(struct iomap_writepage_ctx *wpc,
> +		struct writeback_control *wbc, struct inode *inode,
> +		struct page *page, u64 end_offset)
> +{
> +	struct iomap_page *iop = to_iomap_page(page);
> +	struct iomap_ioend *ioend, *next;
> +	unsigned len = i_blocksize(inode);
> +	u64 file_offset; /* file offset of page */
> +	int error = 0, count = 0, i;
> +	LIST_HEAD(submit_list);
> +
> +	WARN_ON_ONCE(i_blocksize(inode) < PAGE_SIZE && !iop);
> +	WARN_ON_ONCE(iop && atomic_read(&iop->write_count) != 0);
> +
> +	/*
> +	 * Walk through the page to find areas to write back. If we run off the
> +	 * end of the current map or find the current map invalid, grab a new
> +	 * one.
> +	 */
> +	for (i = 0, file_offset = page_offset(page);
> +	     i < (PAGE_SIZE >> inode->i_blkbits) && file_offset < end_offset;
> +	     i++, file_offset += len) {
> +		if (iop && !test_bit(i, iop->uptodate))
> +			continue;
> +
> +		error = wpc->ops->map_blocks(wpc, inode, file_offset);
> +		if (error)
> +			break;
> +		if (wpc->iomap.type == IOMAP_HOLE)
> +			continue;
> +		iomap_add_to_ioend(inode, file_offset, page, iop, wpc, wbc,
> +				 &submit_list);
> +		count++;
> +	}
> +
> +	WARN_ON_ONCE(!wpc->ioend && !list_empty(&submit_list));
> +	WARN_ON_ONCE(!PageLocked(page));
> +	WARN_ON_ONCE(PageWriteback(page));
> +
> +	/*
> +	 * On error, we have to fail the ioend here because we may have set
> +	 * pages under writeback, we have to make sure we run IO completion to
> +	 * mark the error state of the IO appropriately, so we can't cancel the
> +	 * ioend directly here.  That means we have to mark this page as under
> +	 * writeback if we included any blocks from it in the ioend chain so
> +	 * that completion treats it correctly.
> +	 *
> +	 * If we didn't include the page in the ioend, the on error we can
> +	 * simply discard and unlock it as there are no other users of the page
> +	 * now.  The caller will still need to trigger submission of outstanding
> +	 * ioends on the writepage context so they are treated correctly on
> +	 * error.
> +	 */
> +	if (unlikely(error)) {
> +		if (!count) {
> +			wpc->ops->discard_page(page);
> +			ClearPageUptodate(page);
> +			unlock_page(page);
> +			goto done;
> +		}
> +
> +		/*
> +		 * If the page was not fully cleaned, we need to ensure that the
> +		 * higher layers come back to it correctly.  That means we need
> +		 * to keep the page dirty, and for WB_SYNC_ALL writeback we need
> +		 * to ensure the PAGECACHE_TAG_TOWRITE index mark is not removed
> +		 * so another attempt to write this page in this writeback sweep
> +		 * will be made.
> +		 */
> +		set_page_writeback_keepwrite(page);
> +	} else {
> +		clear_page_dirty_for_io(page);
> +		set_page_writeback(page);
> +	}
> +
> +	unlock_page(page);
> +
> +	/*
> +	 * Preserve the original error if there was one, otherwise catch
> +	 * submission errors here and propagate into subsequent ioend
> +	 * submissions.
> +	 */
> +	list_for_each_entry_safe(ioend, next, &submit_list, io_list) {
> +		int error2;
> +
> +		list_del_init(&ioend->io_list);
> +		error2 = iomap_submit_ioend(wpc, ioend, error);
> +		if (error2 && !error)
> +			error = error2;
> +	}
> +
> +	/*
> +	 * We can end up here with no error and nothing to write only if we race
> +	 * with a partial page truncate on a sub-page block sized filesystem.
> +	 */
> +	if (!count)
> +		end_page_writeback(page);
> +done:
> +	mapping_set_error(page->mapping, error);
> +	return error;
> +}
> +
> +/*
> + * Write out a dirty page.
> + *
> + * For delalloc space on the page we need to allocate space and flush it.
> + * For unwritten space on the page we need to start the conversion to
> + * regular allocated space.
> + */
> +static int
> +iomap_do_writepage(struct page *page, struct writeback_control *wbc, void *data)
> +{
> +	struct iomap_writepage_ctx *wpc = data;
> +	struct inode *inode = page->mapping->host;
> +	pgoff_t end_index;
> +	u64 end_offset;
> +	loff_t offset;
> +
> +	/*
> +	 * Refuse to write the page out if we are called from reclaim context.
> +	 *
> +	 * This avoids stack overflows when called from deeply used stacks in
> +	 * random callers for direct reclaim or memcg reclaim.  We explicitly
> +	 * allow reclaim from kswapd as the stack usage there is relatively low.
> +	 *
> +	 * This should never happen except in the case of a VM regression so
> +	 * warn about it.
> +	 */
> +	if (WARN_ON_ONCE((current->flags & (PF_MEMALLOC|PF_KSWAPD)) ==
> +			PF_MEMALLOC))
> +		goto redirty;
> +
> +	/*
> +	 * Given that we do not allow direct reclaim to call us, we should
> +	 * never be called while in a filesystem transaction.
> +	 */
> +	if (WARN_ON_ONCE(current->flags & PF_MEMALLOC_NOFS))
> +		goto redirty;
> +
> +	/*
> +	 * Is this page beyond the end of the file?
> +	 *
> +	 * The page index is less than the end_index, adjust the end_offset
> +	 * to the highest offset that this page should represent.
> +	 * -----------------------------------------------------
> +	 * |			file mapping	       | <EOF> |
> +	 * -----------------------------------------------------
> +	 * | Page ... | Page N-2 | Page N-1 |  Page N  |       |
> +	 * ^--------------------------------^----------|--------
> +	 * |     desired writeback range    |      see else    |
> +	 * ---------------------------------^------------------|
> +	 */
> +	offset = i_size_read(inode);
> +	end_index = offset >> PAGE_SHIFT;
> +	if (page->index < end_index)
> +		end_offset = (loff_t)(page->index + 1) << PAGE_SHIFT;
> +	else {
> +		/*
> +		 * Check whether the page to write out is beyond or straddles
> +		 * i_size or not.
> +		 * -------------------------------------------------------
> +		 * |		file mapping		        | <EOF>  |
> +		 * -------------------------------------------------------
> +		 * | Page ... | Page N-2 | Page N-1 |  Page N   | Beyond |
> +		 * ^--------------------------------^-----------|---------
> +		 * |				    |      Straddles     |
> +		 * ---------------------------------^-----------|--------|
> +		 */
> +		unsigned offset_into_page = offset & (PAGE_SIZE - 1);
> +
> +		/*
> +		 * Skip the page if it is fully outside i_size, e.g. due to a
> +		 * truncate operation that is in progress. We must redirty the
> +		 * page so that reclaim stops reclaiming it. Otherwise
> +		 * iomap_vm_releasepage() is called on it and gets confused.
> +		 *
> +		 * Note that the end_index is unsigned long, it would overflow
> +		 * if the given offset is greater than 16TB on 32-bit system
> +		 * and if we do check the page is fully outside i_size or not
> +		 * via "if (page->index >= end_index + 1)" as "end_index + 1"
> +		 * will be evaluated to 0.  Hence this page will be redirtied
> +		 * and be written out repeatedly which would result in an
> +		 * infinite loop, the user program that perform this operation
> +		 * will hang.  Instead, we can verify this situation by checking
> +		 * if the page to write is totally beyond the i_size or if it's
> +		 * offset is just equal to the EOF.
> +		 */
> +		if (page->index > end_index ||
> +		    (page->index == end_index && offset_into_page == 0))
> +			goto redirty;
> +
> +		/*
> +		 * The page straddles i_size.  It must be zeroed out on each
> +		 * and every writepage invocation because it may be mmapped.
> +		 * "A file is mapped in multiples of the page size.  For a file
> +		 * that is not a multiple of the page size, the remaining
> +		 * memory is zeroed when mapped, and writes to that region are
> +		 * not written out to the file."
> +		 */
> +		zero_user_segment(page, offset_into_page, PAGE_SIZE);
> +
> +		/* Adjust the end_offset to the end of file */
> +		end_offset = offset;
> +	}
> +
> +	return iomap_writepage_map(wpc, wbc, inode, page, end_offset);
> +
> +redirty:
> +	redirty_page_for_writepage(wbc, page);
> +	unlock_page(page);
> +	return 0;
> +}
> +
> +int
> +iomap_writepage(struct page *page, struct writeback_control *wbc,
> +		struct iomap_writepage_ctx *wpc,
> +		const struct iomap_writeback_ops *ops)
> +{
> +	int ret;
> +
> +	wpc->ops = ops;
> +	ret = iomap_do_writepage(page, wbc, wpc);
> +	if (!wpc->ioend)
> +		return ret;
> +	return iomap_submit_ioend(wpc, wpc->ioend, ret);
> +}
> +EXPORT_SYMBOL_GPL(iomap_writepage);
> +
> +int
> +iomap_writepages(struct address_space *mapping, struct writeback_control *wbc,
> +		struct iomap_writepage_ctx *wpc,
> +		const struct iomap_writeback_ops *ops)
> +{
> +	int			ret;
> +
> +	wpc->ops = ops;
> +	ret = write_cache_pages(mapping, wbc, iomap_do_writepage, wpc);
> +	if (!wpc->ioend)
> +		return ret;
> +	return iomap_submit_ioend(wpc, wpc->ioend, ret);
> +}
> +EXPORT_SYMBOL_GPL(iomap_writepages);
> +
> +static int __init iomap_init(void)
> +{
> +	return bioset_init(&iomap_ioend_bioset, 4 * (PAGE_SIZE / SECTOR_SIZE),
> +			   offsetof(struct iomap_ioend, io_inline_bio),
> +			   BIOSET_NEED_BVECS);
> +}
> +fs_initcall(iomap_init);
> diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
> index d9a7a9e6b912..26b838aea2db 100644
> --- a/fs/xfs/xfs_aops.c
> +++ b/fs/xfs/xfs_aops.c
> @@ -23,16 +23,18 @@
>  #include "xfs_reflink.h"
>  #include <linux/writeback.h>
>  
> -/*
> - * structure owned by writepages passed to individual writepage calls
> - */
>  struct xfs_writepage_ctx {
> -	struct iomap		iomap;
> +	struct iomap_writepage_ctx ctx;
>  	unsigned int		data_seq;
>  	unsigned int		cow_seq;
> -	struct xfs_ioend	*ioend;
>  };
>  
> +static inline struct xfs_writepage_ctx *
> +XFS_WPC(struct iomap_writepage_ctx *ctx)
> +{
> +	return container_of(ctx, struct xfs_writepage_ctx, ctx);
> +}
> +
>  struct block_device *
>  xfs_find_bdev_for_inode(
>  	struct inode		*inode)
> @@ -59,84 +61,10 @@ xfs_find_daxdev_for_inode(
>  		return mp->m_ddev_targp->bt_daxdev;
>  }
>  
> -static void
> -xfs_finish_page_writeback(
> -	struct inode		*inode,
> -	struct bio_vec	*bvec,
> -	int			error)
> -{
> -	struct iomap_page	*iop = to_iomap_page(bvec->bv_page);
> -
> -	if (error) {
> -		SetPageError(bvec->bv_page);
> -		mapping_set_error(inode->i_mapping, -EIO);
> -	}
> -
> -	ASSERT(iop || i_blocksize(inode) == PAGE_SIZE);
> -	ASSERT(!iop || atomic_read(&iop->write_count) > 0);
> -
> -	if (!iop || atomic_dec_and_test(&iop->write_count))
> -		end_page_writeback(bvec->bv_page);
> -}
> -
> -/*
> - * We're now finished for good with this ioend structure.  Update the page
> - * state, release holds on bios, and finally free up memory.  Do not use the
> - * ioend after this.
> - */
> -STATIC void
> -xfs_destroy_ioend(
> -	struct xfs_ioend	*ioend,
> -	int			error)
> -{
> -	struct inode		*inode = ioend->io_inode;
> -	struct bio		*bio = &ioend->io_inline_bio;
> -	struct bio		*last = ioend->io_bio, *next;
> -	u64			start = bio->bi_iter.bi_sector;
> -	bool			quiet = bio_flagged(bio, BIO_QUIET);
> -
> -	for (bio = &ioend->io_inline_bio; bio; bio = next) {
> -		struct bio_vec	*bvec;
> -		struct bvec_iter_all iter_all;
> -
> -		/*
> -		 * For the last bio, bi_private points to the ioend, so we
> -		 * need to explicitly end the iteration here.
> -		 */
> -		if (bio == last)
> -			next = NULL;
> -		else
> -			next = bio->bi_private;
> -
> -		/* walk each page on bio, ending page IO on them */
> -		bio_for_each_segment_all(bvec, bio, iter_all)
> -			xfs_finish_page_writeback(inode, bvec, error);
> -		bio_put(bio);
> -	}
> -
> -	if (unlikely(error && !quiet)) {
> -		xfs_err_ratelimited(XFS_I(inode)->i_mount,
> -			"writeback error on sector %llu", start);
> -	}
> -}
> -
> -static void
> -xfs_destroy_ioends(
> -	struct xfs_ioend	*ioend,
> -	int			error)
> -{
> -	struct list_head	tmp;
> -
> -	list_replace_init(&ioend->io_list, &tmp);
> -	xfs_destroy_ioend(ioend, error);
> -	while ((ioend = list_pop(&tmp, struct xfs_ioend, io_list)))
> -		xfs_destroy_ioend(ioend, error);
> -}
> -
>  /*
>   * Fast and loose check if this write could update the on-disk inode size.
>   */
> -static inline bool xfs_ioend_is_append(struct xfs_ioend *ioend)
> +static inline bool xfs_ioend_is_append(struct iomap_ioend *ioend)
>  {
>  	return ioend->io_offset + ioend->io_size >
>  		XFS_I(ioend->io_inode)->i_d.di_size;
> @@ -182,7 +110,7 @@ xfs_setfilesize(
>   */
>  STATIC void
>  xfs_end_ioend(
> -	struct xfs_ioend	*ioend)
> +	struct iomap_ioend	*ioend)
>  {
>  	unsigned int		nofs_flag = memalloc_nofs_save();
>  	struct xfs_inode	*ip = XFS_I(ioend->io_inode);
> @@ -218,76 +146,10 @@ xfs_end_ioend(
>  	if (!error && xfs_ioend_is_append(ioend))
>  		error = xfs_setfilesize(ip, offset, size);
>  done:
> -	xfs_destroy_ioends(ioend, error);
> +	iomap_finish_ioends(ioend, error);
>  	memalloc_nofs_restore(nofs_flag);
>  }
>  
> -/*
> - * We can merge two adjacent ioends if they have the same set of work to do.
> - */
> -static bool
> -xfs_ioend_can_merge(
> -	struct xfs_ioend	*ioend,
> -	struct xfs_ioend	*next)
> -{
> -	if (ioend->io_bio->bi_status != next->io_bio->bi_status)
> -		return false;
> -	if ((ioend->io_flags & IOMAP_F_SHARED) ^
> -	    (next->io_flags & IOMAP_F_SHARED))
> -		return false;
> -	if ((ioend->io_type == IOMAP_UNWRITTEN) ^
> -	    (next->io_type == IOMAP_UNWRITTEN))
> -		return false;
> -	if (ioend->io_offset + ioend->io_size != next->io_offset)
> -		return false;
> -	return true;
> -}
> -
> -/* Try to merge adjacent completions. */
> -STATIC void
> -xfs_ioend_try_merge(
> -	struct xfs_ioend	*ioend,
> -	struct list_head	*more_ioends)
> -{
> -	struct xfs_ioend	*next;
> -
> -	INIT_LIST_HEAD(&ioend->io_list);
> -
> -	while ((next = list_first_entry_or_null(more_ioends, struct xfs_ioend,
> -			io_list))) {
> -		if (!xfs_ioend_can_merge(ioend, next))
> -			break;
> -		list_move_tail(&next->io_list, &ioend->io_list);
> -		ioend->io_size += next->io_size;
> -	}
> -}
> -
> -/* list_sort compare function for ioends */
> -static int
> -xfs_ioend_compare(
> -	void			*priv,
> -	struct list_head	*a,
> -	struct list_head	*b)
> -{
> -	struct xfs_ioend	*ia;
> -	struct xfs_ioend	*ib;
> -
> -	ia = container_of(a, struct xfs_ioend, io_list);
> -	ib = container_of(b, struct xfs_ioend, io_list);
> -	if (ia->io_offset < ib->io_offset)
> -		return -1;
> -	else if (ia->io_offset > ib->io_offset)
> -		return 1;
> -	return 0;
> -}
> -
> -static void
> -xfs_sort_ioends(
> -	struct list_head	*ioend_list)
> -{
> -	list_sort(NULL, ioend_list, xfs_ioend_compare);
> -}
> -
>  /* Finish all pending io completions. */
>  void
>  xfs_end_io(
> @@ -295,7 +157,7 @@ xfs_end_io(
>  {
>  	struct xfs_inode	*ip =
>  		container_of(work, struct xfs_inode, i_ioend_work);
> -	struct xfs_ioend	*ioend;
> +	struct iomap_ioend	*ioend;
>  	struct list_head	tmp;
>  	unsigned long		flags;
>  
> @@ -303,9 +165,9 @@ xfs_end_io(
>  	list_replace_init(&ip->i_ioend_list, &tmp);
>  	spin_unlock_irqrestore(&ip->i_ioend_lock, flags);
>  
> -	xfs_sort_ioends(&tmp);
> -	while ((ioend = list_pop(&tmp, struct xfs_ioend, io_list))) {
> -		xfs_ioend_try_merge(ioend, &tmp);
> +	iomap_sort_ioends(&tmp);
> +	while ((ioend = list_pop(&tmp, struct iomap_ioend, io_list))) {
> +		iomap_ioend_try_merge(ioend, &tmp);
>  		xfs_end_ioend(ioend);
>  	}
>  }
> @@ -314,7 +176,7 @@ STATIC void
>  xfs_end_bio(
>  	struct bio		*bio)
>  {
> -	struct xfs_ioend	*ioend = bio->bi_private;
> +	struct iomap_ioend	*ioend = bio->bi_private;
>  	struct xfs_inode	*ip = XFS_I(ioend->io_inode);
>  	struct xfs_mount	*mp = ip->i_mount;
>  	unsigned long		flags;
> @@ -329,7 +191,7 @@ xfs_end_bio(
>  		list_add_tail(&ioend->io_list, &ip->i_ioend_list);
>  		spin_unlock_irqrestore(&ip->i_ioend_lock, flags);
>  	} else
> -		xfs_destroy_ioend(ioend, blk_status_to_errno(bio->bi_status));
> +		iomap_finish_ioend(ioend, blk_status_to_errno(bio->bi_status));
>  }
>  
>  /*
> @@ -338,7 +200,7 @@ xfs_end_bio(
>   */
>  static bool
>  xfs_imap_valid(
> -	struct xfs_writepage_ctx	*wpc,
> +	struct iomap_writepage_ctx	*wpc,
>  	struct xfs_inode		*ip,
>  	loff_t				offset)
>  {
> @@ -360,10 +222,10 @@ xfs_imap_valid(
>  	 * checked (and found nothing at this offset) could have added
>  	 * overlapping blocks.
>  	 */
> -	if (wpc->data_seq != READ_ONCE(ip->i_df.if_seq))
> +	if (XFS_WPC(wpc)->data_seq != READ_ONCE(ip->i_df.if_seq))
>  		return false;
>  	if (xfs_inode_has_cow_data(ip) &&
> -	    wpc->cow_seq != READ_ONCE(ip->i_cowfp->if_seq))
> +	    XFS_WPC(wpc)->cow_seq != READ_ONCE(ip->i_cowfp->if_seq))
>  		return false;
>  	return true;
>  }
> @@ -378,12 +240,18 @@ xfs_imap_valid(
>   */
>  static int
>  xfs_convert_blocks(
> -	struct xfs_writepage_ctx *wpc,
> +	struct iomap_writepage_ctx *wpc,
>  	struct xfs_inode	*ip,
>  	int			whichfork,
>  	loff_t			offset)
>  {
>  	int			error;
> +	unsigned		*seq;
> +
> +	if (whichfork == XFS_COW_FORK)
> +		seq = &XFS_WPC(wpc)->cow_seq;
> +	else
> +		seq = &XFS_WPC(wpc)->data_seq;
>  
>  	/*
>  	 * Attempt to allocate whatever delalloc extent currently backs offset
> @@ -393,8 +261,7 @@ xfs_convert_blocks(
>  	 */
>  	do {
>  		error = xfs_bmapi_convert_delalloc(ip, whichfork, offset,
> -				&wpc->iomap, whichfork == XFS_COW_FORK ?
> -					&wpc->cow_seq : &wpc->data_seq);
> +				&wpc->iomap, seq);
>  		if (error)
>  			return error;
>  	} while (wpc->iomap.offset + wpc->iomap.length <= offset);
> @@ -402,9 +269,9 @@ xfs_convert_blocks(
>  	return 0;
>  }
>  
> -STATIC int
> +static int
>  xfs_map_blocks(
> -	struct xfs_writepage_ctx *wpc,
> +	struct iomap_writepage_ctx *wpc,
>  	struct inode		*inode,
>  	loff_t			offset)
>  {
> @@ -460,7 +327,7 @@ xfs_map_blocks(
>  	    xfs_iext_lookup_extent(ip, ip->i_cowfp, offset_fsb, &icur, &imap))
>  		cow_fsb = imap.br_startoff;
>  	if (cow_fsb != NULLFILEOFF && cow_fsb <= offset_fsb) {
> -		wpc->cow_seq = READ_ONCE(ip->i_cowfp->if_seq);
> +		XFS_WPC(wpc)->cow_seq = READ_ONCE(ip->i_cowfp->if_seq);
>  		xfs_iunlock(ip, XFS_ILOCK_SHARED);
>  
>  		whichfork = XFS_COW_FORK;
> @@ -483,7 +350,7 @@ xfs_map_blocks(
>  	 */
>  	if (!xfs_iext_lookup_extent(ip, &ip->i_df, offset_fsb, &icur, &imap))
>  		imap.br_startoff = end_fsb;	/* fake a hole past EOF */
> -	wpc->data_seq = READ_ONCE(ip->i_df.if_seq);
> +	XFS_WPC(wpc)->data_seq = READ_ONCE(ip->i_df.if_seq);
>  	xfs_iunlock(ip, XFS_ILOCK_SHARED);
>  
>  	/* landed in a hole or beyond EOF? */
> @@ -547,24 +414,9 @@ xfs_map_blocks(
>  	return 0;
>  }
>  
> -/*
> - * Submit the bio for an ioend. We are passed an ioend with a bio attached to
> - * it, and we submit that bio. The ioend may be used for multiple bio
> - * submissions, so we only want to allocate an append transaction for the ioend
> - * once. In the case of multiple bio submission, each bio will take an IO
> - * reference to the ioend to ensure that the ioend completion is only done once
> - * all bios have been submitted and the ioend is really done.
> - *
> - * If @status is non-zero, it means that we have a situation where some part of
> - * the submission process has failed after we have marked paged for writeback
> - * and unlocked them. In this situation, we need to fail the bio and ioend
> - * rather than submit it to IO. This typically only happens on a filesystem
> - * shutdown.
> - */
> -STATIC int
> +static int
>  xfs_submit_ioend(
> -	struct writeback_control *wbc,
> -	struct xfs_ioend	*ioend,
> +	struct iomap_ioend	*ioend,
>  	int			status)
>  {
>  	/* Convert CoW extents to regular */
> @@ -584,118 +436,8 @@ xfs_submit_ioend(
>  		memalloc_nofs_restore(nofs_flag);
>  	}
>  
> -	ioend->io_bio->bi_private = ioend;
>  	ioend->io_bio->bi_end_io = xfs_end_bio;
> -
> -	/*
> -	 * If we are failing the IO now, just mark the ioend with an
> -	 * error and finish it. This will run IO completion immediately
> -	 * as there is only one reference to the ioend at this point in
> -	 * time.
> -	 */
> -	if (status) {
> -		ioend->io_bio->bi_status = errno_to_blk_status(status);
> -		bio_endio(ioend->io_bio);
> -		return status;
> -	}
> -
> -	submit_bio(ioend->io_bio);
> -	return 0;
> -}
> -
> -static struct xfs_ioend *
> -xfs_alloc_ioend(
> -	struct inode		*inode,
> -	struct xfs_writepage_ctx *wpc,
> -	xfs_off_t		offset,
> -	sector_t		sector,
> -	struct writeback_control *wbc)
> -{
> -	struct xfs_ioend	*ioend;
> -	struct bio		*bio;
> -
> -	bio = bio_alloc_bioset(GFP_NOFS, BIO_MAX_PAGES, &xfs_ioend_bioset);
> -	bio_set_dev(bio, wpc->iomap.bdev);
> -	bio->bi_iter.bi_sector = sector;
> -	bio->bi_opf = REQ_OP_WRITE | wbc_to_write_flags(wbc);
> -	bio->bi_write_hint = inode->i_write_hint;
> -
> -	ioend = container_of(bio, struct xfs_ioend, io_inline_bio);
> -	INIT_LIST_HEAD(&ioend->io_list);
> -	ioend->io_type = wpc->iomap.type;
> -	ioend->io_flags = wpc->iomap.flags;
> -	ioend->io_inode = inode;
> -	ioend->io_size = 0;
> -	ioend->io_offset = offset;
> -	ioend->io_bio = bio;
> -	return ioend;
> -}
> -
> -/*
> - * Allocate a new bio, and chain the old bio to the new one.
> - *
> - * Note that we have to do perform the chaining in this unintuitive order
> - * so that the bi_private linkage is set up in the right direction for the
> - * traversal in xfs_destroy_ioend().
> - */
> -static struct bio *
> -xfs_chain_bio(
> -	struct bio		*prev)
> -{
> -	struct bio *new;
> -
> -	new = bio_alloc(GFP_NOFS, BIO_MAX_PAGES);
> -	bio_copy_dev(new, prev);
> -	new->bi_iter.bi_sector = bio_end_sector(prev);
> -	new->bi_opf = prev->bi_opf;
> -	new->bi_write_hint = prev->bi_write_hint;
> -
> -	bio_chain(prev, new);
> -	bio_get(prev);		/* for xfs_destroy_ioend */
> -	submit_bio(prev);
> -	return new;
> -}
> -
> -/*
> - * Test to see if we have an existing ioend structure that we could append to
> - * first, otherwise finish off the current ioend and start another.
> - */
> -STATIC void
> -xfs_add_to_ioend(
> -	struct inode		*inode,
> -	xfs_off_t		offset,
> -	struct page		*page,
> -	struct iomap_page	*iop,
> -	struct xfs_writepage_ctx *wpc,
> -	struct writeback_control *wbc,
> -	struct list_head	*iolist)
> -{
> -	unsigned		len = i_blocksize(inode);
> -	unsigned		poff = offset & (PAGE_SIZE - 1);
> -	sector_t		sector;
> -
> -	sector = (wpc->iomap.addr + offset - wpc->iomap.offset) >> 9;
> -
> -	if (!wpc->ioend ||
> -	    (wpc->iomap.flags & IOMAP_F_SHARED) !=
> -	    (wpc->ioend->io_flags & IOMAP_F_SHARED) ||
> -	    wpc->iomap.type != wpc->ioend->io_type ||
> -	    sector != bio_end_sector(wpc->ioend->io_bio) ||
> -	    offset != wpc->ioend->io_offset + wpc->ioend->io_size) {
> -		if (wpc->ioend)
> -			list_add(&wpc->ioend->io_list, iolist);
> -		wpc->ioend = xfs_alloc_ioend(inode, wpc, offset, sector, wbc);
> -	}
> -
> -	if (!__bio_try_merge_page(wpc->ioend->io_bio, page, len, poff, true)) {
> -		if (iop)
> -			atomic_inc(&iop->write_count);
> -		if (bio_full(wpc->ioend->io_bio))
> -			wpc->ioend->io_bio = xfs_chain_bio(wpc->ioend->io_bio);
> -		bio_add_page(wpc->ioend->io_bio, page, len, poff);
> -	}
> -
> -	wpc->ioend->io_size += len;
> +	return status;
>  }
>  
>  STATIC void
> @@ -719,8 +461,8 @@ xfs_vm_invalidatepage(
>   * transaction as there is no space left for block reservation (typically why we
>   * see a ENOSPC in writeback).
>   */
> -STATIC void
> -xfs_aops_discard_page(
> +static void
> +xfs_discard_page(
>  	struct page		*page)
>  {
>  	struct inode		*inode = page->mapping->host;
> @@ -745,243 +487,11 @@ xfs_aops_discard_page(
>  	xfs_vm_invalidatepage(page, 0, PAGE_SIZE);
>  }
>  
> -/*
> - * We implement an immediate ioend submission policy here to avoid needing to
> - * chain multiple ioends and hence nest mempool allocations which can violate
> - * forward progress guarantees we need to provide. The current ioend we are
> - * adding blocks to is cached on the writepage context, and if the new block
> - * does not append to the cached ioend it will create a new ioend and cache that
> - * instead.
> - *
> - * If a new ioend is created and cached, the old ioend is returned and queued
> - * locally for submission once the entire page is processed or an error has been
> - * detected.  While ioends are submitted immediately after they are completed,
> - * batching optimisations are provided by higher level block plugging.
> - *
> - * At the end of a writeback pass, there will be a cached ioend remaining on the
> - * writepage context that the caller will need to submit.
> - */
> -static int
> -xfs_writepage_map(
> -	struct xfs_writepage_ctx *wpc,
> -	struct writeback_control *wbc,
> -	struct inode		*inode,
> -	struct page		*page,
> -	uint64_t		end_offset)
> -{
> -	LIST_HEAD(submit_list);
> -	struct iomap_page	*iop = to_iomap_page(page);
> -	unsigned		len = i_blocksize(inode);
> -	struct xfs_ioend	*ioend, *next;
> -	uint64_t		file_offset;	/* file offset of page */
> -	int			error = 0, count = 0, i;
> -
> -	ASSERT(iop || i_blocksize(inode) == PAGE_SIZE);
> -	ASSERT(!iop || atomic_read(&iop->write_count) == 0);
> -
> -	/*
> -	 * Walk through the page to find areas to write back. If we run off the
> -	 * end of the current map or find the current map invalid, grab a new
> -	 * one.
> -	 */
> -	for (i = 0, file_offset = page_offset(page);
> -	     i < (PAGE_SIZE >> inode->i_blkbits) && file_offset < end_offset;
> -	     i++, file_offset += len) {
> -		if (iop && !test_bit(i, iop->uptodate))
> -			continue;
> -
> -		error = xfs_map_blocks(wpc, inode, file_offset);
> -		if (error)
> -			break;
> -		if (wpc->iomap.type == IOMAP_HOLE)
> -			continue;
> -		xfs_add_to_ioend(inode, file_offset, page, iop, wpc, wbc,
> -				 &submit_list);
> -		count++;
> -	}
> -
> -	ASSERT(wpc->ioend || list_empty(&submit_list));
> -	ASSERT(PageLocked(page));
> -	ASSERT(!PageWriteback(page));
> -
> -	/*
> -	 * On error, we have to fail the ioend here because we may have set
> -	 * pages under writeback, we have to make sure we run IO completion to
> -	 * mark the error state of the IO appropriately, so we can't cancel the
> -	 * ioend directly here.  That means we have to mark this page as under
> -	 * writeback if we included any blocks from it in the ioend chain so
> -	 * that completion treats it correctly.
> -	 *
> -	 * If we didn't include the page in the ioend, the on error we can
> -	 * simply discard and unlock it as there are no other users of the page
> -	 * now.  The caller will still need to trigger submission of outstanding
> -	 * ioends on the writepage context so they are treated correctly on
> -	 * error.
> -	 */
> -	if (unlikely(error)) {
> -		if (!count) {
> -			xfs_aops_discard_page(page);
> -			ClearPageUptodate(page);
> -			unlock_page(page);
> -			goto done;
> -		}
> -
> -		/*
> -		 * If the page was not fully cleaned, we need to ensure that the
> -		 * higher layers come back to it correctly.  That means we need
> -		 * to keep the page dirty, and for WB_SYNC_ALL writeback we need
> -		 * to ensure the PAGECACHE_TAG_TOWRITE index mark is not removed
> -		 * so another attempt to write this page in this writeback sweep
> -		 * will be made.
> -		 */
> -		set_page_writeback_keepwrite(page);
> -	} else {
> -		clear_page_dirty_for_io(page);
> -		set_page_writeback(page);
> -	}
> -
> -	unlock_page(page);
> -
> -	/*
> -	 * Preserve the original error if there was one, otherwise catch
> -	 * submission errors here and propagate into subsequent ioend
> -	 * submissions.
> -	 */
> -	list_for_each_entry_safe(ioend, next, &submit_list, io_list) {
> -		int error2;
> -
> -		list_del_init(&ioend->io_list);
> -		error2 = xfs_submit_ioend(wbc, ioend, error);
> -		if (error2 && !error)
> -			error = error2;
> -	}
> -
> -	/*
> -	 * We can end up here with no error and nothing to write only if we race
> -	 * with a partial page truncate on a sub-page block sized filesystem.
> -	 */
> -	if (!count)
> -		end_page_writeback(page);
> -done:
> -	mapping_set_error(page->mapping, error);
> -	return error;
> -}
> -
> -/*
> - * Write out a dirty page.
> - *
> - * For delalloc space on the page we need to allocate space and flush it.
> - * For unwritten space on the page we need to start the conversion to
> - * regular allocated space.
> - */
> -STATIC int
> -xfs_do_writepage(
> -	struct page		*page,
> -	struct writeback_control *wbc,
> -	void			*data)
> -{
> -	struct xfs_writepage_ctx *wpc = data;
> -	struct inode		*inode = page->mapping->host;
> -	loff_t			offset;
> -	uint64_t              end_offset;
> -	pgoff_t                 end_index;
> -
> -	trace_xfs_writepage(inode, page, 0, 0);
> -
> -	/*
> -	 * Refuse to write the page out if we are called from reclaim context.
> -	 *
> -	 * This avoids stack overflows when called from deeply used stacks in
> -	 * random callers for direct reclaim or memcg reclaim.  We explicitly
> -	 * allow reclaim from kswapd as the stack usage there is relatively low.
> -	 *
> -	 * This should never happen except in the case of a VM regression so
> -	 * warn about it.
> -	 */
> -	if (WARN_ON_ONCE((current->flags & (PF_MEMALLOC|PF_KSWAPD)) ==
> -			PF_MEMALLOC))
> -		goto redirty;
> -
> -	/*
> -	 * Given that we do not allow direct reclaim to call us, we should
> -	 * never be called while in a filesystem transaction.
> -	 */
> -	if (WARN_ON_ONCE(current->flags & PF_MEMALLOC_NOFS))
> -		goto redirty;
> -
> -	/*
> -	 * Is this page beyond the end of the file?
> -	 *
> -	 * The page index is less than the end_index, adjust the end_offset
> -	 * to the highest offset that this page should represent.
> -	 * -----------------------------------------------------
> -	 * |			file mapping	       | <EOF> |
> -	 * -----------------------------------------------------
> -	 * | Page ... | Page N-2 | Page N-1 |  Page N  |       |
> -	 * ^--------------------------------^----------|--------
> -	 * |     desired writeback range    |      see else    |
> -	 * ---------------------------------^------------------|
> -	 */
> -	offset = i_size_read(inode);
> -	end_index = offset >> PAGE_SHIFT;
> -	if (page->index < end_index)
> -		end_offset = (xfs_off_t)(page->index + 1) << PAGE_SHIFT;
> -	else {
> -		/*
> -		 * Check whether the page to write out is beyond or straddles
> -		 * i_size or not.
> -		 * -------------------------------------------------------
> -		 * |		file mapping		        | <EOF>  |
> -		 * -------------------------------------------------------
> -		 * | Page ... | Page N-2 | Page N-1 |  Page N   | Beyond |
> -		 * ^--------------------------------^-----------|---------
> -		 * |				    |      Straddles     |
> -		 * ---------------------------------^-----------|--------|
> -		 */
> -		unsigned offset_into_page = offset & (PAGE_SIZE - 1);
> -
> -		/*
> -		 * Skip the page if it is fully outside i_size, e.g. due to a
> -		 * truncate operation that is in progress. We must redirty the
> -		 * page so that reclaim stops reclaiming it. Otherwise
> -		 * xfs_vm_releasepage() is called on it and gets confused.
> -		 *
> -		 * Note that the end_index is unsigned long, it would overflow
> -		 * if the given offset is greater than 16TB on 32-bit system
> -		 * and if we do check the page is fully outside i_size or not
> -		 * via "if (page->index >= end_index + 1)" as "end_index + 1"
> -		 * will be evaluated to 0.  Hence this page will be redirtied
> -		 * and be written out repeatedly which would result in an
> -		 * infinite loop, the user program that perform this operation
> -		 * will hang.  Instead, we can verify this situation by checking
> -		 * if the page to write is totally beyond the i_size or if it's
> -		 * offset is just equal to the EOF.
> -		 */
> -		if (page->index > end_index ||
> -		    (page->index == end_index && offset_into_page == 0))
> -			goto redirty;
> -
> -		/*
> -		 * The page straddles i_size.  It must be zeroed out on each
> -		 * and every writepage invocation because it may be mmapped.
> -		 * "A file is mapped in multiples of the page size.  For a file
> -		 * that is not a multiple of the page size, the remaining
> -		 * memory is zeroed when mapped, and writes to that region are
> -		 * not written out to the file."
> -		 */
> -		zero_user_segment(page, offset_into_page, PAGE_SIZE);
> -
> -		/* Adjust the end_offset to the end of file */
> -		end_offset = offset;
> -	}
> -
> -	return xfs_writepage_map(wpc, wbc, inode, page, end_offset);
> -
> -redirty:
> -	redirty_page_for_writepage(wbc, page);
> -	unlock_page(page);
> -	return 0;
> -}
> +static const struct iomap_writeback_ops xfs_writeback_ops = {
> +	.map_blocks		= xfs_map_blocks,
> +	.submit_ioend		= xfs_submit_ioend,
> +	.discard_page		= xfs_discard_page,
> +};
>  
>  STATIC int
>  xfs_vm_writepage(
> @@ -989,12 +499,8 @@ xfs_vm_writepage(
>  	struct writeback_control *wbc)
>  {
>  	struct xfs_writepage_ctx wpc = { };
> -	int			ret;
>  
> -	ret = xfs_do_writepage(page, wbc, &wpc);
> -	if (wpc.ioend)
> -		ret = xfs_submit_ioend(wbc, wpc.ioend, ret);
> -	return ret;
> +	return iomap_writepage(page, wbc, &wpc.ctx, &xfs_writeback_ops);
>  }
>  
>  STATIC int
> @@ -1003,13 +509,9 @@ xfs_vm_writepages(
>  	struct writeback_control *wbc)
>  {
>  	struct xfs_writepage_ctx wpc = { };
> -	int			ret;
>  
>  	xfs_iflags_clear(XFS_I(mapping->host), XFS_ITRUNCATED);
> -	ret = write_cache_pages(mapping, wbc, xfs_do_writepage, &wpc);
> -	if (wpc.ioend)
> -		ret = xfs_submit_ioend(wbc, wpc.ioend, ret);
> -	return ret;
> +	return iomap_writepages(mapping, wbc, &wpc.ctx, &xfs_writeback_ops);
>  }
>  
>  STATIC int
> diff --git a/fs/xfs/xfs_aops.h b/fs/xfs/xfs_aops.h
> index bf95837c59af..26a7772d4b81 100644
> --- a/fs/xfs/xfs_aops.h
> +++ b/fs/xfs/xfs_aops.h
> @@ -6,22 +6,6 @@
>  #ifndef __XFS_AOPS_H__
>  #define __XFS_AOPS_H__
>  
> -extern struct bio_set xfs_ioend_bioset;
> -
> -/*
> - * Structure for buffered I/O completions.
> - */
> -struct xfs_ioend {
> -	struct list_head	io_list;	/* next ioend in chain */
> -	u16			io_type;
> -	u16			io_flags;
> -	struct inode		*io_inode;	/* file being written to */
> -	size_t			io_size;	/* size of the extent */
> -	xfs_off_t		io_offset;	/* offset in the file */
> -	struct bio		*io_bio;	/* bio being built */
> -	struct bio		io_inline_bio;	/* MUST BE LAST! */
> -};
> -
>  extern const struct address_space_operations xfs_address_space_operations;
>  extern const struct address_space_operations xfs_dax_aops;
>  
> diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
> index 594c119824cc..52b89e175bc5 100644
> --- a/fs/xfs/xfs_super.c
> +++ b/fs/xfs/xfs_super.c
> @@ -53,7 +53,6 @@
>  #include <linux/parser.h>
>  
>  static const struct super_operations xfs_super_operations;
> -struct bio_set xfs_ioend_bioset;
>  
>  static struct kset *xfs_kset;		/* top-level xfs sysfs dir */
>  #ifdef DEBUG
> @@ -1870,15 +1869,10 @@ MODULE_ALIAS_FS("xfs");
>  STATIC int __init
>  xfs_init_zones(void)
>  {
> -	if (bioset_init(&xfs_ioend_bioset, 4 * (PAGE_SIZE / SECTOR_SIZE),
> -			offsetof(struct xfs_ioend, io_inline_bio),
> -			BIOSET_NEED_BVECS))
> -		goto out;
> -
>  	xfs_log_ticket_zone = kmem_zone_init(sizeof(xlog_ticket_t),
>  						"xfs_log_ticket");
>  	if (!xfs_log_ticket_zone)
> -		goto out_free_ioend_bioset;
> +		goto out;
>  
>  	xfs_bmap_free_item_zone = kmem_zone_init(
>  			sizeof(struct xfs_extent_free_item),
> @@ -2013,8 +2007,6 @@ xfs_init_zones(void)
>  	kmem_zone_destroy(xfs_bmap_free_item_zone);
>   out_destroy_log_ticket_zone:
>  	kmem_zone_destroy(xfs_log_ticket_zone);
> - out_free_ioend_bioset:
> -	bioset_exit(&xfs_ioend_bioset);
>   out:
>  	return -ENOMEM;
>  }
> @@ -2045,7 +2037,6 @@ xfs_destroy_zones(void)
>  	kmem_zone_destroy(xfs_btree_cur_zone);
>  	kmem_zone_destroy(xfs_bmap_free_item_zone);
>  	kmem_zone_destroy(xfs_log_ticket_zone);
> -	bioset_exit(&xfs_ioend_bioset);
>  }
>  
>  STATIC int __init
> diff --git a/include/linux/iomap.h b/include/linux/iomap.h
> index 2103b94cb1bf..e87f44810c53 100644
> --- a/include/linux/iomap.h
> +++ b/include/linux/iomap.h
> @@ -4,6 +4,7 @@
>  
>  #include <linux/atomic.h>
>  #include <linux/bitmap.h>
> +#include <linux/blk_types.h>
>  #include <linux/mm.h>
>  #include <linux/types.h>
>  #include <linux/mm_types.h>
> @@ -11,6 +12,7 @@
>  struct address_space;
>  struct fiemap_extent_info;
>  struct inode;
> +struct iomap_writepage_ctx;
>  struct iov_iter;
>  struct kiocb;
>  struct page;
> @@ -165,6 +167,45 @@ loff_t iomap_seek_data(struct inode *inode, loff_t offset,
>  sector_t iomap_bmap(struct address_space *mapping, sector_t bno,
>  		const struct iomap_ops *ops);
>  
> +/*
> + * Structure for writeback I/O completions.
> + */
> +struct iomap_ioend {
> +	struct list_head	io_list;	/* next ioend in chain */
> +	u16			io_type;
> +	u16			io_flags;
> +	struct inode		*io_inode;	/* file being written to */
> +	size_t			io_size;	/* size of the extent */
> +	loff_t			io_offset;	/* offset in the file */
> +	struct bio		*io_bio;	/* bio being built */
> +	struct bio		io_inline_bio;	/* MUST BE LAST! */
> +};
> +
> +struct iomap_writeback_ops {
> +	int (*map_blocks)(struct iomap_writepage_ctx *wpc, struct inode *inode,
> +				loff_t offset);
> +	int (*submit_ioend)(struct iomap_ioend *ioend, int status);
> +	void (*discard_page)(struct page *page);
> +};
> +
> +struct iomap_writepage_ctx {
> +	struct iomap		iomap;
> +	struct iomap_ioend	*ioend;
> +	const struct iomap_writeback_ops *ops;
> +};
> +
> +void iomap_finish_ioend(struct iomap_ioend *ioend, int error);
> +void iomap_finish_ioends(struct iomap_ioend *ioend, int error);
> +void iomap_ioend_try_merge(struct iomap_ioend *ioend,
> +		struct list_head *more_ioends);
> +void iomap_sort_ioends(struct list_head *ioend_list);
> +int iomap_writepage(struct page *page, struct writeback_control *wbc,
> +		struct iomap_writepage_ctx *wpc,
> +		const struct iomap_writeback_ops *ops);
> +int iomap_writepages(struct address_space *mapping,
> +		struct writeback_control *wbc, struct iomap_writepage_ctx *wpc,
> +		const struct iomap_writeback_ops *ops);
> +
>  /*
>   * Flags for direct I/O ->end_io:
>   */
> -- 
> 2.20.1
> 

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

* Re: [PATCH 05/12] xfs: use a struct iomap in xfs_writepage_ctx
  2019-06-24  5:52 ` [PATCH 05/12] xfs: use a struct iomap in xfs_writepage_ctx Christoph Hellwig
@ 2019-06-24 15:50   ` Darrick J. Wong
  0 siblings, 0 replies; 52+ messages in thread
From: Darrick J. Wong @ 2019-06-24 15:50 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel

On Mon, Jun 24, 2019 at 07:52:46AM +0200, Christoph Hellwig wrote:
> In preparation for moving the XFS writeback code to fs/iomap.c, switch
> it to use struct iomap instead of the XFS-specific struct xfs_bmbt_irec.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>

Looks ok,
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

--D

> ---
>  fs/xfs/libxfs/xfs_bmap.c | 14 +++++--
>  fs/xfs/libxfs/xfs_bmap.h |  3 +-
>  fs/xfs/xfs_aops.c        | 80 +++++++++++++++++++---------------------
>  fs/xfs/xfs_aops.h        |  2 +-
>  4 files changed, 50 insertions(+), 49 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c
> index 4133bc461e3e..de35a0376156 100644
> --- a/fs/xfs/libxfs/xfs_bmap.c
> +++ b/fs/xfs/libxfs/xfs_bmap.c
> @@ -39,6 +39,7 @@
>  #include "xfs_ag_resv.h"
>  #include "xfs_refcount.h"
>  #include "xfs_icache.h"
> +#include "xfs_iomap.h"
>  
>  
>  kmem_zone_t		*xfs_bmap_free_item_zone;
> @@ -4457,16 +4458,21 @@ int
>  xfs_bmapi_convert_delalloc(
>  	struct xfs_inode	*ip,
>  	int			whichfork,
> -	xfs_fileoff_t		offset_fsb,
> -	struct xfs_bmbt_irec	*imap,
> +	xfs_off_t		offset,
> +	struct iomap		*iomap,
>  	unsigned int		*seq)
>  {
>  	struct xfs_ifork	*ifp = XFS_IFORK_PTR(ip, whichfork);
>  	struct xfs_mount	*mp = ip->i_mount;
> +	xfs_fileoff_t		offset_fsb = XFS_B_TO_FSBT(mp, offset);
>  	struct xfs_bmalloca	bma = { NULL };
> +	u16			flags = 0;
>  	struct xfs_trans	*tp;
>  	int			error;
>  
> +	if (whichfork == XFS_COW_FORK)
> +		flags |= IOMAP_F_SHARED;
> +
>  	/*
>  	 * Space for the extent and indirect blocks was reserved when the
>  	 * delalloc extent was created so there's no need to do so here.
> @@ -4496,7 +4502,7 @@ xfs_bmapi_convert_delalloc(
>  	 * the extent.  Just return the real extent at this offset.
>  	 */
>  	if (!isnullstartblock(bma.got.br_startblock)) {
> -		*imap = bma.got;
> +		xfs_bmbt_to_iomap(ip, iomap, &bma.got, flags);
>  		*seq = READ_ONCE(ifp->if_seq);
>  		goto out_trans_cancel;
>  	}
> @@ -4529,7 +4535,7 @@ xfs_bmapi_convert_delalloc(
>  	XFS_STATS_INC(mp, xs_xstrat_quick);
>  
>  	ASSERT(!isnullstartblock(bma.got.br_startblock));
> -	*imap = bma.got;
> +	xfs_bmbt_to_iomap(ip, iomap, &bma.got, flags);
>  	*seq = READ_ONCE(ifp->if_seq);
>  
>  	if (whichfork == XFS_COW_FORK) {
> diff --git a/fs/xfs/libxfs/xfs_bmap.h b/fs/xfs/libxfs/xfs_bmap.h
> index 8f597f9abdbe..3c3470f11648 100644
> --- a/fs/xfs/libxfs/xfs_bmap.h
> +++ b/fs/xfs/libxfs/xfs_bmap.h
> @@ -220,8 +220,7 @@ int	xfs_bmapi_reserve_delalloc(struct xfs_inode *ip, int whichfork,
>  		struct xfs_bmbt_irec *got, struct xfs_iext_cursor *cur,
>  		int eof);
>  int	xfs_bmapi_convert_delalloc(struct xfs_inode *ip, int whichfork,
> -		xfs_fileoff_t offset_fsb, struct xfs_bmbt_irec *imap,
> -		unsigned int *seq);
> +		xfs_off_t offset, struct iomap *iomap, unsigned int *seq);
>  int	xfs_bmap_add_extent_unwritten_real(struct xfs_trans *tp,
>  		struct xfs_inode *ip, int whichfork,
>  		struct xfs_iext_cursor *icur, struct xfs_btree_cur **curp,
> diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
> index dc60aec0c5a7..93a760f13017 100644
> --- a/fs/xfs/xfs_aops.c
> +++ b/fs/xfs/xfs_aops.c
> @@ -27,7 +27,7 @@
>   * structure owned by writepages passed to individual writepage calls
>   */
>  struct xfs_writepage_ctx {
> -	struct xfs_bmbt_irec    imap;
> +	struct iomap		iomap;
>  	int			fork;
>  	unsigned int		data_seq;
>  	unsigned int		cow_seq;
> @@ -265,7 +265,7 @@ xfs_end_ioend(
>  	 */
>  	if (ioend->io_fork == XFS_COW_FORK)
>  		error = xfs_reflink_end_cow(ip, offset, size);
> -	else if (ioend->io_state == XFS_EXT_UNWRITTEN)
> +	else if (ioend->io_type == IOMAP_UNWRITTEN)
>  		error = xfs_iomap_write_unwritten(ip, offset, size, false);
>  	else
>  		ASSERT(!xfs_ioend_is_append(ioend) || ioend->io_append_trans);
> @@ -300,8 +300,8 @@ xfs_ioend_can_merge(
>  		return false;
>  	if ((ioend->io_fork == XFS_COW_FORK) ^ (next->io_fork == XFS_COW_FORK))
>  		return false;
> -	if ((ioend->io_state == XFS_EXT_UNWRITTEN) ^
> -	    (next->io_state == XFS_EXT_UNWRITTEN))
> +	if ((ioend->io_type == IOMAP_UNWRITTEN) ^
> +	    (next->io_type == IOMAP_UNWRITTEN))
>  		return false;
>  	if (ioend->io_offset + ioend->io_size != next->io_offset)
>  		return false;
> @@ -395,7 +395,7 @@ xfs_end_bio(
>  	unsigned long		flags;
>  
>  	if (ioend->io_fork == XFS_COW_FORK ||
> -	    ioend->io_state == XFS_EXT_UNWRITTEN ||
> +	    ioend->io_type == IOMAP_UNWRITTEN ||
>  	    ioend->io_append_trans != NULL) {
>  		spin_lock_irqsave(&ip->i_ioend_lock, flags);
>  		if (list_empty(&ip->i_ioend_list))
> @@ -415,10 +415,10 @@ static bool
>  xfs_imap_valid(
>  	struct xfs_writepage_ctx	*wpc,
>  	struct xfs_inode		*ip,
> -	xfs_fileoff_t			offset_fsb)
> +	loff_t				offset)
>  {
> -	if (offset_fsb < wpc->imap.br_startoff ||
> -	    offset_fsb >= wpc->imap.br_startoff + wpc->imap.br_blockcount)
> +	if (offset < wpc->iomap.offset ||
> +	    offset >= wpc->iomap.offset + wpc->iomap.length)
>  		return false;
>  	/*
>  	 * If this is a COW mapping, it is sufficient to check that the mapping
> @@ -445,7 +445,7 @@ xfs_imap_valid(
>  
>  /*
>   * Pass in a dellalloc extent and convert it to real extents, return the real
> - * extent that maps offset_fsb in wpc->imap.
> + * extent that maps offset_fsb in wpc->iomap.
>   *
>   * The current page is held locked so nothing could have removed the block
>   * backing offset_fsb, although it could have moved from the COW to the data
> @@ -455,23 +455,23 @@ static int
>  xfs_convert_blocks(
>  	struct xfs_writepage_ctx *wpc,
>  	struct xfs_inode	*ip,
> -	xfs_fileoff_t		offset_fsb)
> +	loff_t			offset)
>  {
>  	int			error;
>  
>  	/*
> -	 * Attempt to allocate whatever delalloc extent currently backs
> -	 * offset_fsb and put the result into wpc->imap.  Allocate in a loop
> -	 * because it may take several attempts to allocate real blocks for a
> -	 * contiguous delalloc extent if free space is sufficiently fragmented.
> +	 * Attempt to allocate whatever delalloc extent currently backs offset
> +	 * and put the result into wpc->imap.  Allocate in a loop because it may
> +	 * take several attempts to allocate real blocks for a contiguous
> +	 * delalloc extent if free space is sufficiently fragmented.
>  	 */
>  	do {
> -		error = xfs_bmapi_convert_delalloc(ip, wpc->fork, offset_fsb,
> -				&wpc->imap, wpc->fork == XFS_COW_FORK ?
> +		error = xfs_bmapi_convert_delalloc(ip, wpc->fork, offset,
> +				&wpc->iomap, wpc->fork == XFS_COW_FORK ?
>  					&wpc->cow_seq : &wpc->data_seq);
>  		if (error)
>  			return error;
> -	} while (wpc->imap.br_startoff + wpc->imap.br_blockcount <= offset_fsb);
> +	} while (wpc->iomap.offset + wpc->iomap.length <= offset);
>  
>  	return 0;
>  }
> @@ -511,7 +511,7 @@ xfs_map_blocks(
>  	 * against concurrent updates and provides a memory barrier on the way
>  	 * out that ensures that we always see the current value.
>  	 */
> -	if (xfs_imap_valid(wpc, ip, offset_fsb))
> +	if (xfs_imap_valid(wpc, ip, offset))
>  		return 0;
>  
>  	/*
> @@ -544,7 +544,7 @@ xfs_map_blocks(
>  	 * No COW extent overlap. Revalidate now that we may have updated
>  	 * ->cow_seq. If the data mapping is still valid, we're done.
>  	 */
> -	if (xfs_imap_valid(wpc, ip, offset_fsb)) {
> +	if (xfs_imap_valid(wpc, ip, offset)) {
>  		xfs_iunlock(ip, XFS_ILOCK_SHARED);
>  		return 0;
>  	}
> @@ -584,11 +584,11 @@ xfs_map_blocks(
>  	    isnullstartblock(imap.br_startblock))
>  		goto allocate_blocks;
>  
> -	wpc->imap = imap;
> +	xfs_bmbt_to_iomap(ip, &wpc->iomap, &imap, 0);
>  	trace_xfs_map_blocks_found(ip, offset, count, wpc->fork, &imap);
>  	return 0;
>  allocate_blocks:
> -	error = xfs_convert_blocks(wpc, ip, offset_fsb);
> +	error = xfs_convert_blocks(wpc, ip, offset);
>  	if (error) {
>  		/*
>  		 * If we failed to find the extent in the COW fork we might have
> @@ -608,12 +608,15 @@ xfs_map_blocks(
>  	 * original delalloc one.  Trim the return extent to the next COW
>  	 * boundary again to force a re-lookup.
>  	 */
> -	if (wpc->fork != XFS_COW_FORK && cow_fsb != NULLFILEOFF &&
> -	    cow_fsb < wpc->imap.br_startoff + wpc->imap.br_blockcount)
> -		wpc->imap.br_blockcount = cow_fsb - wpc->imap.br_startoff;
> +	if (wpc->fork != XFS_COW_FORK && cow_fsb != NULLFILEOFF) {
> +		loff_t		cow_offset = XFS_FSB_TO_B(mp, cow_fsb);
> +
> +		if (cow_offset < wpc->iomap.offset + wpc->iomap.length)
> +			wpc->iomap.length = cow_offset - wpc->iomap.offset;
> +	}
>  
> -	ASSERT(wpc->imap.br_startoff <= offset_fsb);
> -	ASSERT(wpc->imap.br_startoff + wpc->imap.br_blockcount > offset_fsb);
> +	ASSERT(wpc->iomap.offset <= offset);
> +	ASSERT(wpc->iomap.offset + wpc->iomap.length > offset);
>  	trace_xfs_map_blocks_alloc(ip, offset, count, wpc->fork, &imap);
>  	return 0;
>  }
> @@ -658,7 +661,7 @@ xfs_submit_ioend(
>  	/* Reserve log space if we might write beyond the on-disk inode size. */
>  	if (!status &&
>  	    (ioend->io_fork == XFS_COW_FORK ||
> -	     ioend->io_state != XFS_EXT_UNWRITTEN) &&
> +	     ioend->io_type != IOMAP_UNWRITTEN) &&
>  	    xfs_ioend_is_append(ioend) &&
>  	    !ioend->io_append_trans)
>  		status = xfs_setfilesize_trans_alloc(ioend);
> @@ -685,10 +688,8 @@ xfs_submit_ioend(
>  static struct xfs_ioend *
>  xfs_alloc_ioend(
>  	struct inode		*inode,
> -	int			fork,
> -	xfs_exntst_t		state,
> +	struct xfs_writepage_ctx *wpc,
>  	xfs_off_t		offset,
> -	struct block_device	*bdev,
>  	sector_t		sector,
>  	struct writeback_control *wbc)
>  {
> @@ -696,15 +697,15 @@ xfs_alloc_ioend(
>  	struct bio		*bio;
>  
>  	bio = bio_alloc_bioset(GFP_NOFS, BIO_MAX_PAGES, &xfs_ioend_bioset);
> -	bio_set_dev(bio, bdev);
> +	bio_set_dev(bio, wpc->iomap.bdev);
>  	bio->bi_iter.bi_sector = sector;
>  	bio->bi_opf = REQ_OP_WRITE | wbc_to_write_flags(wbc);
>  	bio->bi_write_hint = inode->i_write_hint;
>  
>  	ioend = container_of(bio, struct xfs_ioend, io_inline_bio);
>  	INIT_LIST_HEAD(&ioend->io_list);
> -	ioend->io_fork = fork;
> -	ioend->io_state = state;
> +	ioend->io_fork = wpc->fork;
> +	ioend->io_type = wpc->iomap.type;
>  	ioend->io_inode = inode;
>  	ioend->io_size = 0;
>  	ioend->io_offset = offset;
> @@ -752,25 +753,20 @@ xfs_add_to_ioend(
>  	struct writeback_control *wbc,
>  	struct list_head	*iolist)
>  {
> -	struct xfs_inode	*ip = XFS_I(inode);
> -	struct xfs_mount	*mp = ip->i_mount;
> -	struct block_device	*bdev = xfs_find_bdev_for_inode(inode);
>  	unsigned		len = i_blocksize(inode);
>  	unsigned		poff = offset & (PAGE_SIZE - 1);
>  	sector_t		sector;
>  
> -	sector = xfs_fsb_to_db(ip, wpc->imap.br_startblock) +
> -		((offset - XFS_FSB_TO_B(mp, wpc->imap.br_startoff)) >> 9);
> +	sector = (wpc->iomap.addr + offset - wpc->iomap.offset) >> 9;
>  
>  	if (!wpc->ioend ||
>  	    wpc->fork != wpc->ioend->io_fork ||
> -	    wpc->imap.br_state != wpc->ioend->io_state ||
> +	    wpc->iomap.type != wpc->ioend->io_type ||
>  	    sector != bio_end_sector(wpc->ioend->io_bio) ||
>  	    offset != wpc->ioend->io_offset + wpc->ioend->io_size) {
>  		if (wpc->ioend)
>  			list_add(&wpc->ioend->io_list, iolist);
> -		wpc->ioend = xfs_alloc_ioend(inode, wpc->fork,
> -				wpc->imap.br_state, offset, bdev, sector, wbc);
> +		wpc->ioend = xfs_alloc_ioend(inode, wpc, offset, sector, wbc);
>  	}
>  
>  	if (!__bio_try_merge_page(wpc->ioend->io_bio, page, len, poff, true)) {
> @@ -879,7 +875,7 @@ xfs_writepage_map(
>  		error = xfs_map_blocks(wpc, inode, file_offset);
>  		if (error)
>  			break;
> -		if (wpc->imap.br_startblock == HOLESTARTBLOCK)
> +		if (wpc->iomap.type == IOMAP_HOLE)
>  			continue;
>  		xfs_add_to_ioend(inode, file_offset, page, iop, wpc, wbc,
>  				 &submit_list);
> diff --git a/fs/xfs/xfs_aops.h b/fs/xfs/xfs_aops.h
> index f62b03186c62..72e30d1c3bdf 100644
> --- a/fs/xfs/xfs_aops.h
> +++ b/fs/xfs/xfs_aops.h
> @@ -14,7 +14,7 @@ extern struct bio_set xfs_ioend_bioset;
>  struct xfs_ioend {
>  	struct list_head	io_list;	/* next ioend in chain */
>  	int			io_fork;	/* inode fork written back */
> -	xfs_exntst_t		io_state;	/* extent state */
> +	u16			io_type;
>  	struct inode		*io_inode;	/* file being written to */
>  	size_t			io_size;	/* size of the extent */
>  	xfs_off_t		io_offset;	/* offset in the file */
> -- 
> 2.20.1
> 

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

* Re: [PATCH 01/12] list.h: add a list_pop helper
  2019-06-24  5:52 ` [PATCH 01/12] list.h: add a list_pop helper Christoph Hellwig
  2019-06-24 14:53   ` Darrick J. Wong
@ 2019-06-24 15:51   ` Matthew Wilcox
  2019-06-25 10:06     ` Christoph Hellwig
  1 sibling, 1 reply; 52+ messages in thread
From: Matthew Wilcox @ 2019-06-24 15:51 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Darrick J . Wong, Damien Le Moal, Andreas Gruenbacher, linux-xfs,
	linux-fsdevel, linux-kernel

On Mon, Jun 24, 2019 at 07:52:42AM +0200, Christoph Hellwig wrote:
> +/**
> + * list_pop - delete the first entry from a list and return it
> + * @list:	the list to take the element from.
> + * @type:	the type of the struct this is embedded in.
> + * @member:	the name of the list_head within the struct.
> + *
> + * Note that if the list is empty, it returns NULL.
> + */
> +#define list_pop(list, type, member) 				\

The usual convention in list.h is that list_foo uses the list head and
list_foo_entry uses the container type.  So I think this should be
renamed to list_pop_entry() at least.  Do we also want:

static inline struct list_head *list_pop(struct list_head *head)
{
	struct list_head *first = READ_ONCE(head->next);

	if (first == head)
		return NULL;
	__list_del(head, first->next);
	return first;
}

we also seem to prefer using inline functions over #defines in this
header file.

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

* Re: [PATCH 06/12] xfs: remove XFS_TRANS_NOFS
  2019-06-24  5:52 ` [PATCH 06/12] xfs: remove XFS_TRANS_NOFS Christoph Hellwig
@ 2019-06-24 15:58   ` Darrick J. Wong
  2019-06-24 22:59   ` Dave Chinner
  1 sibling, 0 replies; 52+ messages in thread
From: Darrick J. Wong @ 2019-06-24 15:58 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel

On Mon, Jun 24, 2019 at 07:52:47AM +0200, Christoph Hellwig wrote:
> Instead of a magic flag for xfs_trans_alloc, just ensure all callers
> that can't relclaim through the file system use memalloc_nofs_save to
> set the per-task nofs flag.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>

Hmm this finally fixes up the mess I left where COW fork cleanup
sometimes needs nofs and other times doesn't... :)

> ---
>  fs/xfs/libxfs/xfs_shared.h |  1 -
>  fs/xfs/xfs_aops.c          | 12 +++++++++---
>  fs/xfs/xfs_file.c          | 12 +++++++++---
>  fs/xfs/xfs_iomap.c         |  2 +-
>  fs/xfs/xfs_reflink.c       |  4 ++--
>  fs/xfs/xfs_trans.c         |  4 +---
>  6 files changed, 22 insertions(+), 13 deletions(-)
> 
> diff --git a/fs/xfs/libxfs/xfs_shared.h b/fs/xfs/libxfs/xfs_shared.h
> index 4e909791aeac..1f2b5a0c71b4 100644
> --- a/fs/xfs/libxfs/xfs_shared.h
> +++ b/fs/xfs/libxfs/xfs_shared.h
> @@ -65,7 +65,6 @@ void	xfs_log_get_max_trans_res(struct xfs_mount *mp,
>  #define XFS_TRANS_DQ_DIRTY	0x10	/* at least one dquot in trx dirty */
>  #define XFS_TRANS_RESERVE	0x20    /* OK to use reserved data blocks */
>  #define XFS_TRANS_NO_WRITECOUNT 0x40	/* do not elevate SB writecount */
> -#define XFS_TRANS_NOFS		0x80	/* pass KM_NOFS to kmem_alloc */
>  /*
>   * LOWMODE is used by the allocator to activate the lowspace algorithm - when
>   * free space is running low the extent allocator may choose to allocate an
> diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
> index 93a760f13017..633baaaff7ae 100644
> --- a/fs/xfs/xfs_aops.c
> +++ b/fs/xfs/xfs_aops.c
> @@ -138,8 +138,7 @@ xfs_setfilesize_trans_alloc(
>  	struct xfs_trans	*tp;
>  	int			error;
>  
> -	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_fsyncts, 0, 0,
> -				XFS_TRANS_NOFS, &tp);
> +	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_fsyncts, 0, 0, 0, &tp);
>  	if (error)
>  		return error;
>  
> @@ -236,6 +235,7 @@ STATIC void
>  xfs_end_ioend(
>  	struct xfs_ioend	*ioend)
>  {
> +	unsigned int		nofs_flag = memalloc_nofs_save();
>  	struct list_head	ioend_list;
>  	struct xfs_inode	*ip = XFS_I(ioend->io_inode);
>  	xfs_off_t		offset = ioend->io_offset;
> @@ -282,6 +282,8 @@ xfs_end_ioend(
>  		list_del_init(&ioend->io_list);
>  		xfs_destroy_ioend(ioend, error);
>  	}
> +
> +	memalloc_nofs_restore(nofs_flag);
>  }
>  
>  /*
> @@ -663,8 +665,12 @@ xfs_submit_ioend(
>  	    (ioend->io_fork == XFS_COW_FORK ||
>  	     ioend->io_type != IOMAP_UNWRITTEN) &&
>  	    xfs_ioend_is_append(ioend) &&
> -	    !ioend->io_append_trans)
> +	    !ioend->io_append_trans) {
> +		unsigned nofs_flag = memalloc_nofs_save();

unsigned int?  Seeing as you use that everywhere else...

--D

> +
>  		status = xfs_setfilesize_trans_alloc(ioend);
> +		memalloc_nofs_restore(nofs_flag);
> +	}
>  
>  	ioend->io_bio->bi_private = ioend;
>  	ioend->io_bio->bi_end_io = xfs_end_bio;
> diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c
> index 916a35cae5e9..f2d806ef8f06 100644
> --- a/fs/xfs/xfs_file.c
> +++ b/fs/xfs/xfs_file.c
> @@ -379,6 +379,7 @@ xfs_dio_write_end_io(
>  	struct inode		*inode = file_inode(iocb->ki_filp);
>  	struct xfs_inode	*ip = XFS_I(inode);
>  	loff_t			offset = iocb->ki_pos;
> +	unsigned int		nofs_flag;
>  	int			error = 0;
>  
>  	trace_xfs_end_io_direct_write(ip, offset, size);
> @@ -395,10 +396,11 @@ xfs_dio_write_end_io(
>  	 */
>  	XFS_STATS_ADD(ip->i_mount, xs_write_bytes, size);
>  
> +	nofs_flag = memalloc_nofs_save();
>  	if (flags & IOMAP_DIO_COW) {
>  		error = xfs_reflink_end_cow(ip, offset, size);
>  		if (error)
> -			return error;
> +			goto out;
>  	}
>  
>  	/*
> @@ -407,8 +409,10 @@ xfs_dio_write_end_io(
>  	 * earlier allows a racing dio read to find unwritten extents before
>  	 * they are converted.
>  	 */
> -	if (flags & IOMAP_DIO_UNWRITTEN)
> -		return xfs_iomap_write_unwritten(ip, offset, size, true);
> +	if (flags & IOMAP_DIO_UNWRITTEN) {
> +		error = xfs_iomap_write_unwritten(ip, offset, size, true);
> +		goto out;
> +	}
>  
>  	/*
>  	 * We need to update the in-core inode size here so that we don't end up
> @@ -430,6 +434,8 @@ xfs_dio_write_end_io(
>  		spin_unlock(&ip->i_flags_lock);
>  	}
>  
> +out:
> +	memalloc_nofs_restore(nofs_flag);
>  	return error;
>  }
>  
> diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
> index 6b29452bfba0..461ea023b910 100644
> --- a/fs/xfs/xfs_iomap.c
> +++ b/fs/xfs/xfs_iomap.c
> @@ -782,7 +782,7 @@ xfs_iomap_write_unwritten(
>  		 * complete here and might deadlock on the iolock.
>  		 */
>  		error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0,
> -				XFS_TRANS_RESERVE | XFS_TRANS_NOFS, &tp);
> +				XFS_TRANS_RESERVE, &tp);
>  		if (error)
>  			return error;
>  
> diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
> index 680ae7662a78..0b23c2b29609 100644
> --- a/fs/xfs/xfs_reflink.c
> +++ b/fs/xfs/xfs_reflink.c
> @@ -572,7 +572,7 @@ xfs_reflink_cancel_cow_range(
>  
>  	/* Start a rolling transaction to remove the mappings */
>  	error = xfs_trans_alloc(ip->i_mount, &M_RES(ip->i_mount)->tr_write,
> -			0, 0, XFS_TRANS_NOFS, &tp);
> +			0, 0, 0, &tp);
>  	if (error)
>  		goto out;
>  
> @@ -631,7 +631,7 @@ xfs_reflink_end_cow_extent(
>  
>  	resblks = XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK);
>  	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0,
> -			XFS_TRANS_RESERVE | XFS_TRANS_NOFS, &tp);
> +			XFS_TRANS_RESERVE, &tp);
>  	if (error)
>  		return error;
>  
> diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c
> index 0746b329a937..21228d7455af 100644
> --- a/fs/xfs/xfs_trans.c
> +++ b/fs/xfs/xfs_trans.c
> @@ -264,9 +264,7 @@ xfs_trans_alloc(
>  	 * GFP_NOFS allocation context so that we avoid lockdep false positives
>  	 * by doing GFP_KERNEL allocations inside sb_start_intwrite().
>  	 */
> -	tp = kmem_zone_zalloc(xfs_trans_zone,
> -		(flags & XFS_TRANS_NOFS) ? KM_NOFS : KM_SLEEP);
> -
> +	tp = kmem_zone_zalloc(xfs_trans_zone, KM_SLEEP);
>  	if (!(flags & XFS_TRANS_NO_WRITECOUNT))
>  		sb_start_intwrite(mp->m_super);
>  
> -- 
> 2.20.1
> 

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

* Re: [PATCH 08/12] xfs: simplify xfs_ioend_can_merge
  2019-06-24  5:52 ` [PATCH 08/12] xfs: simplify xfs_ioend_can_merge Christoph Hellwig
@ 2019-06-24 16:00   ` Darrick J. Wong
  0 siblings, 0 replies; 52+ messages in thread
From: Darrick J. Wong @ 2019-06-24 16:00 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel

On Mon, Jun 24, 2019 at 07:52:49AM +0200, Christoph Hellwig wrote:
> Compare the block layer status directly instead of converting it to
> an errno first.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>

Looks ok,
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

--D

> ---
>  fs/xfs/xfs_aops.c | 14 ++------------
>  1 file changed, 2 insertions(+), 12 deletions(-)
> 
> diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
> index 017b87b7765f..acbd73976067 100644
> --- a/fs/xfs/xfs_aops.c
> +++ b/fs/xfs/xfs_aops.c
> @@ -226,13 +226,9 @@ xfs_end_ioend(
>  static bool
>  xfs_ioend_can_merge(
>  	struct xfs_ioend	*ioend,
> -	int			ioend_error,
>  	struct xfs_ioend	*next)
>  {
> -	int			next_error;
> -
> -	next_error = blk_status_to_errno(next->io_bio->bi_status);
> -	if (ioend_error != next_error)
> +	if (ioend->io_bio->bi_status != next->io_bio->bi_status)
>  		return false;
>  	if ((ioend->io_fork == XFS_COW_FORK) ^ (next->io_fork == XFS_COW_FORK))
>  		return false;
> @@ -251,17 +247,11 @@ xfs_ioend_try_merge(
>  	struct list_head	*more_ioends)
>  {
>  	struct xfs_ioend	*next_ioend;
> -	int			ioend_error;
> -
> -	if (list_empty(more_ioends))
> -		return;
> -
> -	ioend_error = blk_status_to_errno(ioend->io_bio->bi_status);
>  
>  	while (!list_empty(more_ioends)) {
>  		next_ioend = list_first_entry(more_ioends, struct xfs_ioend,
>  				io_list);
> -		if (!xfs_ioend_can_merge(ioend, ioend_error, next_ioend))
> +		if (!xfs_ioend_can_merge(ioend, next_ioend))
>  			break;
>  		list_move_tail(&next_ioend->io_list, &ioend->io_list);
>  		ioend->io_size += next_ioend->io_size;
> -- 
> 2.20.1
> 

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

* Re: [PATCH 09/12] xfs: refactor the ioend merging code
  2019-06-24  5:52 ` [PATCH 09/12] xfs: refactor the ioend merging code Christoph Hellwig
@ 2019-06-24 16:04   ` Darrick J. Wong
  2019-06-24 16:06   ` Nikolay Borisov
  1 sibling, 0 replies; 52+ messages in thread
From: Darrick J. Wong @ 2019-06-24 16:04 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel

On Mon, Jun 24, 2019 at 07:52:50AM +0200, Christoph Hellwig wrote:
> Introduce two nicely abstracted helper, which can be moved to the
> iomap code later.  Also use list_pop and list_first_entry_or_null
> to simplify the code a bit.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>

Looks ok,
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

--D

> ---
>  fs/xfs/xfs_aops.c | 66 ++++++++++++++++++++++++++---------------------
>  1 file changed, 36 insertions(+), 30 deletions(-)
> 
> diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
> index acbd73976067..5d302ebe2a33 100644
> --- a/fs/xfs/xfs_aops.c
> +++ b/fs/xfs/xfs_aops.c
> @@ -121,6 +121,19 @@ xfs_destroy_ioend(
>  	}
>  }
>  
> +static void
> +xfs_destroy_ioends(
> +	struct xfs_ioend	*ioend,
> +	int			error)
> +{
> +	struct list_head	tmp;
> +
> +	list_replace_init(&ioend->io_list, &tmp);
> +	xfs_destroy_ioend(ioend, error);
> +	while ((ioend = list_pop(&tmp, struct xfs_ioend, io_list)))
> +		xfs_destroy_ioend(ioend, error);
> +}
> +
>  /*
>   * Fast and loose check if this write could update the on-disk inode size.
>   */
> @@ -173,7 +186,6 @@ xfs_end_ioend(
>  	struct xfs_ioend	*ioend)
>  {
>  	unsigned int		nofs_flag = memalloc_nofs_save();
> -	struct list_head	ioend_list;
>  	struct xfs_inode	*ip = XFS_I(ioend->io_inode);
>  	xfs_off_t		offset = ioend->io_offset;
>  	size_t			size = ioend->io_size;
> @@ -207,16 +219,7 @@ xfs_end_ioend(
>  	if (!error && xfs_ioend_is_append(ioend))
>  		error = xfs_setfilesize(ip, offset, size);
>  done:
> -	list_replace_init(&ioend->io_list, &ioend_list);
> -	xfs_destroy_ioend(ioend, error);
> -
> -	while (!list_empty(&ioend_list)) {
> -		ioend = list_first_entry(&ioend_list, struct xfs_ioend,
> -				io_list);
> -		list_del_init(&ioend->io_list);
> -		xfs_destroy_ioend(ioend, error);
> -	}
> -
> +	xfs_destroy_ioends(ioend, error);
>  	memalloc_nofs_restore(nofs_flag);
>  }
>  
> @@ -246,15 +249,16 @@ xfs_ioend_try_merge(
>  	struct xfs_ioend	*ioend,
>  	struct list_head	*more_ioends)
>  {
> -	struct xfs_ioend	*next_ioend;
> +	struct xfs_ioend	*next;
>  
> -	while (!list_empty(more_ioends)) {
> -		next_ioend = list_first_entry(more_ioends, struct xfs_ioend,
> -				io_list);
> -		if (!xfs_ioend_can_merge(ioend, next_ioend))
> +	INIT_LIST_HEAD(&ioend->io_list);
> +
> +	while ((next = list_first_entry_or_null(more_ioends, struct xfs_ioend,
> +			io_list))) {
> +		if (!xfs_ioend_can_merge(ioend, next))
>  			break;
> -		list_move_tail(&next_ioend->io_list, &ioend->io_list);
> -		ioend->io_size += next_ioend->io_size;
> +		list_move_tail(&next->io_list, &ioend->io_list);
> +		ioend->io_size += next->io_size;
>  	}
>  }
>  
> @@ -277,29 +281,31 @@ xfs_ioend_compare(
>  	return 0;
>  }
>  
> +static void
> +xfs_sort_ioends(
> +	struct list_head	*ioend_list)
> +{
> +	list_sort(NULL, ioend_list, xfs_ioend_compare);
> +}
> +
>  /* Finish all pending io completions. */
>  void
>  xfs_end_io(
>  	struct work_struct	*work)
>  {
> -	struct xfs_inode	*ip;
> +	struct xfs_inode	*ip =
> +		container_of(work, struct xfs_inode, i_ioend_work);
>  	struct xfs_ioend	*ioend;
> -	struct list_head	completion_list;
> +	struct list_head	tmp;
>  	unsigned long		flags;
>  
> -	ip = container_of(work, struct xfs_inode, i_ioend_work);
> -
>  	spin_lock_irqsave(&ip->i_ioend_lock, flags);
> -	list_replace_init(&ip->i_ioend_list, &completion_list);
> +	list_replace_init(&ip->i_ioend_list, &tmp);
>  	spin_unlock_irqrestore(&ip->i_ioend_lock, flags);
>  
> -	list_sort(NULL, &completion_list, xfs_ioend_compare);
> -
> -	while (!list_empty(&completion_list)) {
> -		ioend = list_first_entry(&completion_list, struct xfs_ioend,
> -				io_list);
> -		list_del_init(&ioend->io_list);
> -		xfs_ioend_try_merge(ioend, &completion_list);
> +	xfs_sort_ioends(&tmp);
> +	while ((ioend = list_pop(&tmp, struct xfs_ioend, io_list))) {
> +		xfs_ioend_try_merge(ioend, &tmp);
>  		xfs_end_ioend(ioend);
>  	}
>  }
> -- 
> 2.20.1
> 

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

* Re: [PATCH 09/12] xfs: refactor the ioend merging code
  2019-06-24  5:52 ` [PATCH 09/12] xfs: refactor the ioend merging code Christoph Hellwig
  2019-06-24 16:04   ` Darrick J. Wong
@ 2019-06-24 16:06   ` Nikolay Borisov
  2019-06-25 10:14     ` Christoph Hellwig
  1 sibling, 1 reply; 52+ messages in thread
From: Nikolay Borisov @ 2019-06-24 16:06 UTC (permalink / raw)
  To: Christoph Hellwig, Darrick J . Wong
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel



On 24.06.19 г. 8:52 ч., Christoph Hellwig wrote:
> Introduce two nicely abstracted helper, which can be moved to the
> iomap code later.  Also use list_pop and list_first_entry_or_null
> to simplify the code a bit.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
>  fs/xfs/xfs_aops.c | 66 ++++++++++++++++++++++++++---------------------
>  1 file changed, 36 insertions(+), 30 deletions(-)
> 
> diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
> index acbd73976067..5d302ebe2a33 100644
> --- a/fs/xfs/xfs_aops.c
> +++ b/fs/xfs/xfs_aops.c
> @@ -121,6 +121,19 @@ xfs_destroy_ioend(
>  	}
>  }
>  
> +static void
> +xfs_destroy_ioends(
> +	struct xfs_ioend	*ioend,
> +	int			error)
> +{
> +	struct list_head	tmp;
> +
> +	list_replace_init(&ioend->io_list, &tmp);
> +	xfs_destroy_ioend(ioend, error);
> +	while ((ioend = list_pop(&tmp, struct xfs_ioend, io_list)))
> +		xfs_destroy_ioend(ioend, error);

nit: I'd prefer if the list_pop patch is right before this one since
this is the first user of it. Additionally, I don't think list_pop is
really a net-negative win in comparison to list_for_each_entry_safe
here. In fact this "delete the list" would seems more idiomatic if
implemented via list_for_each_entry_safe

> +}
> +
>  /*
>   * Fast and loose check if this write could update the on-disk inode size.
>   */
> @@ -173,7 +186,6 @@ xfs_end_ioend(
>  	struct xfs_ioend	*ioend)
>  {
>  	unsigned int		nofs_flag = memalloc_nofs_save();
> -	struct list_head	ioend_list;
>  	struct xfs_inode	*ip = XFS_I(ioend->io_inode);
>  	xfs_off_t		offset = ioend->io_offset;
>  	size_t			size = ioend->io_size;
> @@ -207,16 +219,7 @@ xfs_end_ioend(
>  	if (!error && xfs_ioend_is_append(ioend))
>  		error = xfs_setfilesize(ip, offset, size);
>  done:
> -	list_replace_init(&ioend->io_list, &ioend_list);
> -	xfs_destroy_ioend(ioend, error);
> -
> -	while (!list_empty(&ioend_list)) {
> -		ioend = list_first_entry(&ioend_list, struct xfs_ioend,
> -				io_list);
> -		list_del_init(&ioend->io_list);
> -		xfs_destroy_ioend(ioend, error);
> -	}
> -
> +	xfs_destroy_ioends(ioend, error);
>  	memalloc_nofs_restore(nofs_flag);
>  }
>  
> @@ -246,15 +249,16 @@ xfs_ioend_try_merge(
>  	struct xfs_ioend	*ioend,
>  	struct list_head	*more_ioends)
>  {
> -	struct xfs_ioend	*next_ioend;
> +	struct xfs_ioend	*next;
>  
> -	while (!list_empty(more_ioends)) {
> -		next_ioend = list_first_entry(more_ioends, struct xfs_ioend,
> -				io_list);
> -		if (!xfs_ioend_can_merge(ioend, next_ioend))
> +	INIT_LIST_HEAD(&ioend->io_list);
> +
> +	while ((next = list_first_entry_or_null(more_ioends, struct xfs_ioend,
> +			io_list))) {
> +		if (!xfs_ioend_can_merge(ioend, next))
>  			break;
> -		list_move_tail(&next_ioend->io_list, &ioend->io_list);
> -		ioend->io_size += next_ioend->io_size;
> +		list_move_tail(&next->io_list, &ioend->io_list);
> +		ioend->io_size += next->io_size;
>  	}
>  }
>  
> @@ -277,29 +281,31 @@ xfs_ioend_compare(
>  	return 0;
>  }
>  
> +static void
> +xfs_sort_ioends(
> +	struct list_head	*ioend_list)
> +{
> +	list_sort(NULL, ioend_list, xfs_ioend_compare);
> +}
> +
>  /* Finish all pending io completions. */
>  void
>  xfs_end_io(
>  	struct work_struct	*work)
>  {
> -	struct xfs_inode	*ip;
> +	struct xfs_inode	*ip =
> +		container_of(work, struct xfs_inode, i_ioend_work);
>  	struct xfs_ioend	*ioend;
> -	struct list_head	completion_list;
> +	struct list_head	tmp;
>  	unsigned long		flags;
>  
> -	ip = container_of(work, struct xfs_inode, i_ioend_work);
> -
>  	spin_lock_irqsave(&ip->i_ioend_lock, flags);
> -	list_replace_init(&ip->i_ioend_list, &completion_list);
> +	list_replace_init(&ip->i_ioend_list, &tmp);
>  	spin_unlock_irqrestore(&ip->i_ioend_lock, flags);
>  
> -	list_sort(NULL, &completion_list, xfs_ioend_compare);
> -
> -	while (!list_empty(&completion_list)) {
> -		ioend = list_first_entry(&completion_list, struct xfs_ioend,
> -				io_list);
> -		list_del_init(&ioend->io_list);
> -		xfs_ioend_try_merge(ioend, &completion_list);
> +	xfs_sort_ioends(&tmp);
> +	while ((ioend = list_pop(&tmp, struct xfs_ioend, io_list))) {
> +		xfs_ioend_try_merge(ioend, &tmp);
>  		xfs_end_ioend(ioend);

Here again, tmp is a local copy that is immutable so using while()
instead of list_for_each_entry_safe doesn't seem to be providing much.

>  	}
>  }
> 

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

* Re: [PATCH 10/12] xfs: remove the fork fields in the writepage_ctx and ioend
  2019-06-24  5:52 ` [PATCH 10/12] xfs: remove the fork fields in the writepage_ctx and ioend Christoph Hellwig
@ 2019-06-24 16:08   ` Darrick J. Wong
  0 siblings, 0 replies; 52+ messages in thread
From: Darrick J. Wong @ 2019-06-24 16:08 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel

On Mon, Jun 24, 2019 at 07:52:51AM +0200, Christoph Hellwig wrote:
> In preparation for moving the writeback code to iomap.c, replace the
> XFS-specific COW fork concept with the iomap IOMAP_F_SHARED flag.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>

Looks ok,
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>

--D

> ---
>  fs/xfs/xfs_aops.c | 40 +++++++++++++++++++++-------------------
>  fs/xfs/xfs_aops.h |  2 +-
>  2 files changed, 22 insertions(+), 20 deletions(-)
> 
> diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
> index 5d302ebe2a33..d9a7a9e6b912 100644
> --- a/fs/xfs/xfs_aops.c
> +++ b/fs/xfs/xfs_aops.c
> @@ -28,7 +28,6 @@
>   */
>  struct xfs_writepage_ctx {
>  	struct iomap		iomap;
> -	int			fork;
>  	unsigned int		data_seq;
>  	unsigned int		cow_seq;
>  	struct xfs_ioend	*ioend;
> @@ -204,7 +203,7 @@ xfs_end_ioend(
>  	 */
>  	error = blk_status_to_errno(ioend->io_bio->bi_status);
>  	if (unlikely(error)) {
> -		if (ioend->io_fork == XFS_COW_FORK)
> +		if (ioend->io_flags & IOMAP_F_SHARED)
>  			xfs_reflink_cancel_cow_range(ip, offset, size, true);
>  		goto done;
>  	}
> @@ -212,7 +211,7 @@ xfs_end_ioend(
>  	/*
>  	 * Success: commit the COW or unwritten blocks if needed.
>  	 */
> -	if (ioend->io_fork == XFS_COW_FORK)
> +	if (ioend->io_flags & IOMAP_F_SHARED)
>  		error = xfs_reflink_end_cow(ip, offset, size);
>  	else if (ioend->io_type == IOMAP_UNWRITTEN)
>  		error = xfs_iomap_write_unwritten(ip, offset, size, false);
> @@ -233,7 +232,8 @@ xfs_ioend_can_merge(
>  {
>  	if (ioend->io_bio->bi_status != next->io_bio->bi_status)
>  		return false;
> -	if ((ioend->io_fork == XFS_COW_FORK) ^ (next->io_fork == XFS_COW_FORK))
> +	if ((ioend->io_flags & IOMAP_F_SHARED) ^
> +	    (next->io_flags & IOMAP_F_SHARED))
>  		return false;
>  	if ((ioend->io_type == IOMAP_UNWRITTEN) ^
>  	    (next->io_type == IOMAP_UNWRITTEN))
> @@ -319,7 +319,7 @@ xfs_end_bio(
>  	struct xfs_mount	*mp = ip->i_mount;
>  	unsigned long		flags;
>  
> -	if (ioend->io_fork == XFS_COW_FORK ||
> +	if ((ioend->io_flags & IOMAP_F_SHARED) ||
>  	    ioend->io_type == IOMAP_UNWRITTEN ||
>  	    xfs_ioend_is_append(ioend)) {
>  		spin_lock_irqsave(&ip->i_ioend_lock, flags);
> @@ -350,7 +350,7 @@ xfs_imap_valid(
>  	 * covers the offset. Be careful to check this first because the caller
>  	 * can revalidate a COW mapping without updating the data seqno.
>  	 */
> -	if (wpc->fork == XFS_COW_FORK)
> +	if (wpc->iomap.flags & IOMAP_F_SHARED)
>  		return true;
>  
>  	/*
> @@ -380,6 +380,7 @@ static int
>  xfs_convert_blocks(
>  	struct xfs_writepage_ctx *wpc,
>  	struct xfs_inode	*ip,
> +	int			whichfork,
>  	loff_t			offset)
>  {
>  	int			error;
> @@ -391,8 +392,8 @@ xfs_convert_blocks(
>  	 * delalloc extent if free space is sufficiently fragmented.
>  	 */
>  	do {
> -		error = xfs_bmapi_convert_delalloc(ip, wpc->fork, offset,
> -				&wpc->iomap, wpc->fork == XFS_COW_FORK ?
> +		error = xfs_bmapi_convert_delalloc(ip, whichfork, offset,
> +				&wpc->iomap, whichfork == XFS_COW_FORK ?
>  					&wpc->cow_seq : &wpc->data_seq);
>  		if (error)
>  			return error;
> @@ -413,6 +414,7 @@ xfs_map_blocks(
>  	xfs_fileoff_t		offset_fsb = XFS_B_TO_FSBT(mp, offset);
>  	xfs_fileoff_t		end_fsb = XFS_B_TO_FSB(mp, offset + count);
>  	xfs_fileoff_t		cow_fsb = NULLFILEOFF;
> +	int			whichfork = XFS_DATA_FORK;
>  	struct xfs_bmbt_irec	imap;
>  	struct xfs_iext_cursor	icur;
>  	int			retries = 0;
> @@ -461,7 +463,7 @@ xfs_map_blocks(
>  		wpc->cow_seq = READ_ONCE(ip->i_cowfp->if_seq);
>  		xfs_iunlock(ip, XFS_ILOCK_SHARED);
>  
> -		wpc->fork = XFS_COW_FORK;
> +		whichfork = XFS_COW_FORK;
>  		goto allocate_blocks;
>  	}
>  
> @@ -484,8 +486,6 @@ xfs_map_blocks(
>  	wpc->data_seq = READ_ONCE(ip->i_df.if_seq);
>  	xfs_iunlock(ip, XFS_ILOCK_SHARED);
>  
> -	wpc->fork = XFS_DATA_FORK;
> -
>  	/* landed in a hole or beyond EOF? */
>  	if (imap.br_startoff > offset_fsb) {
>  		imap.br_blockcount = imap.br_startoff - offset_fsb;
> @@ -510,10 +510,10 @@ xfs_map_blocks(
>  		goto allocate_blocks;
>  
>  	xfs_bmbt_to_iomap(ip, &wpc->iomap, &imap, 0);
> -	trace_xfs_map_blocks_found(ip, offset, count, wpc->fork, &imap);
> +	trace_xfs_map_blocks_found(ip, offset, count, whichfork, &imap);
>  	return 0;
>  allocate_blocks:
> -	error = xfs_convert_blocks(wpc, ip, offset);
> +	error = xfs_convert_blocks(wpc, ip, whichfork, offset);
>  	if (error) {
>  		/*
>  		 * If we failed to find the extent in the COW fork we might have
> @@ -522,7 +522,8 @@ xfs_map_blocks(
>  		 * the former case, but prevent additional retries to avoid
>  		 * looping forever for the latter case.
>  		 */
> -		if (error == -EAGAIN && wpc->fork == XFS_COW_FORK && !retries++)
> +		if (error == -EAGAIN && (wpc->iomap.flags & IOMAP_F_SHARED) &&
> +		    !retries++)
>  			goto retry;
>  		ASSERT(error != -EAGAIN);
>  		return error;
> @@ -533,7 +534,7 @@ xfs_map_blocks(
>  	 * original delalloc one.  Trim the return extent to the next COW
>  	 * boundary again to force a re-lookup.
>  	 */
> -	if (wpc->fork != XFS_COW_FORK && cow_fsb != NULLFILEOFF) {
> +	if (!(wpc->iomap.flags & IOMAP_F_SHARED) && cow_fsb != NULLFILEOFF) {
>  		loff_t		cow_offset = XFS_FSB_TO_B(mp, cow_fsb);
>  
>  		if (cow_offset < wpc->iomap.offset + wpc->iomap.length)
> @@ -542,7 +543,7 @@ xfs_map_blocks(
>  
>  	ASSERT(wpc->iomap.offset <= offset);
>  	ASSERT(wpc->iomap.offset + wpc->iomap.length > offset);
> -	trace_xfs_map_blocks_alloc(ip, offset, count, wpc->fork, &imap);
> +	trace_xfs_map_blocks_alloc(ip, offset, count, whichfork, &imap);
>  	return 0;
>  }
>  
> @@ -567,7 +568,7 @@ xfs_submit_ioend(
>  	int			status)
>  {
>  	/* Convert CoW extents to regular */
> -	if (!status && ioend->io_fork == XFS_COW_FORK) {
> +	if (!status && (ioend->io_flags & IOMAP_F_SHARED)) {
>  		/*
>  		 * Yuk. This can do memory allocation, but is not a
>  		 * transactional operation so everything is done in GFP_KERNEL
> @@ -621,8 +622,8 @@ xfs_alloc_ioend(
>  
>  	ioend = container_of(bio, struct xfs_ioend, io_inline_bio);
>  	INIT_LIST_HEAD(&ioend->io_list);
> -	ioend->io_fork = wpc->fork;
>  	ioend->io_type = wpc->iomap.type;
> +	ioend->io_flags = wpc->iomap.flags;
>  	ioend->io_inode = inode;
>  	ioend->io_size = 0;
>  	ioend->io_offset = offset;
> @@ -676,7 +677,8 @@ xfs_add_to_ioend(
>  	sector = (wpc->iomap.addr + offset - wpc->iomap.offset) >> 9;
>  
>  	if (!wpc->ioend ||
> -	    wpc->fork != wpc->ioend->io_fork ||
> +	    (wpc->iomap.flags & IOMAP_F_SHARED) !=
> +	    (wpc->ioend->io_flags & IOMAP_F_SHARED) ||
>  	    wpc->iomap.type != wpc->ioend->io_type ||
>  	    sector != bio_end_sector(wpc->ioend->io_bio) ||
>  	    offset != wpc->ioend->io_offset + wpc->ioend->io_size) {
> diff --git a/fs/xfs/xfs_aops.h b/fs/xfs/xfs_aops.h
> index 23c087f0bcbf..bf95837c59af 100644
> --- a/fs/xfs/xfs_aops.h
> +++ b/fs/xfs/xfs_aops.h
> @@ -13,8 +13,8 @@ extern struct bio_set xfs_ioend_bioset;
>   */
>  struct xfs_ioend {
>  	struct list_head	io_list;	/* next ioend in chain */
> -	int			io_fork;	/* inode fork written back */
>  	u16			io_type;
> +	u16			io_flags;
>  	struct inode		*io_inode;	/* file being written to */
>  	size_t			io_size;	/* size of the extent */
>  	xfs_off_t		io_offset;	/* offset in the file */
> -- 
> 2.20.1
> 

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

* Re: [PATCH 07/12] xfs: don't preallocate a transaction for file size updates
  2019-06-24  5:52 ` [PATCH 07/12] xfs: don't preallocate a transaction for file size updates Christoph Hellwig
@ 2019-06-24 16:17   ` Darrick J. Wong
  2019-06-24 23:15     ` Dave Chinner
  0 siblings, 1 reply; 52+ messages in thread
From: Darrick J. Wong @ 2019-06-24 16:17 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel

On Mon, Jun 24, 2019 at 07:52:48AM +0200, Christoph Hellwig wrote:
> We have historically decided that we want to preallocate the xfs_trans
> structure at writeback time so that we don't have to allocate on in
> the I/O completion handler.  But we treat unwrittent extent and COW
> fork conversions different already, which proves that the transaction
> allocations in the end I/O handler are not a problem.  Removing the
> preallocation gets rid of a lot of corner case code, and also ensures
> we only allocate one and log a transaction when actually required,
> as the ioend merging can reduce the number of actual i_size updates
> significantly.

That's what I thought when I wrote the ioend merging patches, but IIRC
Dave objected on the grounds that most file writes are trivial file
extending writes and therefore we should leave this alone to avoid
slowing down the ioend path even if it came at a cost of cancelling a
lot of empty transactions.

I wasn't 100% convinced it mattered but ran out of time in the
development window and never got around to researching if it made any
difference.

So, uh, how much of a hit do we take for having to allocate a
transaction for a file size extension?  Particularly since we can
combine those things now?

--D

> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
>  fs/xfs/xfs_aops.c | 110 +++++-----------------------------------------
>  fs/xfs/xfs_aops.h |   1 -
>  2 files changed, 12 insertions(+), 99 deletions(-)
> 
> diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
> index 633baaaff7ae..017b87b7765f 100644
> --- a/fs/xfs/xfs_aops.c
> +++ b/fs/xfs/xfs_aops.c
> @@ -130,44 +130,23 @@ static inline bool xfs_ioend_is_append(struct xfs_ioend *ioend)
>  		XFS_I(ioend->io_inode)->i_d.di_size;
>  }
>  
> -STATIC int
> -xfs_setfilesize_trans_alloc(
> -	struct xfs_ioend	*ioend)
> -{
> -	struct xfs_mount	*mp = XFS_I(ioend->io_inode)->i_mount;
> -	struct xfs_trans	*tp;
> -	int			error;
> -
> -	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_fsyncts, 0, 0, 0, &tp);
> -	if (error)
> -		return error;
> -
> -	ioend->io_append_trans = tp;
> -
> -	/*
> -	 * We may pass freeze protection with a transaction.  So tell lockdep
> -	 * we released it.
> -	 */
> -	__sb_writers_release(ioend->io_inode->i_sb, SB_FREEZE_FS);
> -	/*
> -	 * We hand off the transaction to the completion thread now, so
> -	 * clear the flag here.
> -	 */
> -	current_restore_flags_nested(&tp->t_pflags, PF_MEMALLOC_NOFS);
> -	return 0;
> -}
> -
>  /*
>   * Update on-disk file size now that data has been written to disk.
>   */
> -STATIC int
> -__xfs_setfilesize(
> +int
> +xfs_setfilesize(
>  	struct xfs_inode	*ip,
> -	struct xfs_trans	*tp,
>  	xfs_off_t		offset,
>  	size_t			size)
>  {
> +	struct xfs_mount	*mp = ip->i_mount;
> +	struct xfs_trans	*tp;
>  	xfs_fsize_t		isize;
> +	int			error;
> +
> +	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_fsyncts, 0, 0, 0, &tp);
> +	if (error)
> +		return error;
>  
>  	xfs_ilock(ip, XFS_ILOCK_EXCL);
>  	isize = xfs_new_eof(ip, offset + size);
> @@ -186,48 +165,6 @@ __xfs_setfilesize(
>  	return xfs_trans_commit(tp);
>  }
>  
> -int
> -xfs_setfilesize(
> -	struct xfs_inode	*ip,
> -	xfs_off_t		offset,
> -	size_t			size)
> -{
> -	struct xfs_mount	*mp = ip->i_mount;
> -	struct xfs_trans	*tp;
> -	int			error;
> -
> -	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_fsyncts, 0, 0, 0, &tp);
> -	if (error)
> -		return error;
> -
> -	return __xfs_setfilesize(ip, tp, offset, size);
> -}
> -
> -STATIC int
> -xfs_setfilesize_ioend(
> -	struct xfs_ioend	*ioend,
> -	int			error)
> -{
> -	struct xfs_inode	*ip = XFS_I(ioend->io_inode);
> -	struct xfs_trans	*tp = ioend->io_append_trans;
> -
> -	/*
> -	 * The transaction may have been allocated in the I/O submission thread,
> -	 * thus we need to mark ourselves as being in a transaction manually.
> -	 * Similarly for freeze protection.
> -	 */
> -	current_set_flags_nested(&tp->t_pflags, PF_MEMALLOC_NOFS);
> -	__sb_writers_acquired(VFS_I(ip)->i_sb, SB_FREEZE_FS);
> -
> -	/* we abort the update if there was an IO error */
> -	if (error) {
> -		xfs_trans_cancel(tp);
> -		return error;
> -	}
> -
> -	return __xfs_setfilesize(ip, tp, ioend->io_offset, ioend->io_size);
> -}
> -
>  /*
>   * IO write completion.
>   */
> @@ -267,12 +204,9 @@ xfs_end_ioend(
>  		error = xfs_reflink_end_cow(ip, offset, size);
>  	else if (ioend->io_type == IOMAP_UNWRITTEN)
>  		error = xfs_iomap_write_unwritten(ip, offset, size, false);
> -	else
> -		ASSERT(!xfs_ioend_is_append(ioend) || ioend->io_append_trans);
> -
> +	if (!error && xfs_ioend_is_append(ioend))
> +		error = xfs_setfilesize(ip, offset, size);
>  done:
> -	if (ioend->io_append_trans)
> -		error = xfs_setfilesize_ioend(ioend, error);
>  	list_replace_init(&ioend->io_list, &ioend_list);
>  	xfs_destroy_ioend(ioend, error);
>  
> @@ -307,8 +241,6 @@ xfs_ioend_can_merge(
>  		return false;
>  	if (ioend->io_offset + ioend->io_size != next->io_offset)
>  		return false;
> -	if (xfs_ioend_is_append(ioend) != xfs_ioend_is_append(next))
> -		return false;
>  	return true;
>  }
>  
> @@ -320,7 +252,6 @@ xfs_ioend_try_merge(
>  {
>  	struct xfs_ioend	*next_ioend;
>  	int			ioend_error;
> -	int			error;
>  
>  	if (list_empty(more_ioends))
>  		return;
> @@ -334,10 +265,6 @@ xfs_ioend_try_merge(
>  			break;
>  		list_move_tail(&next_ioend->io_list, &ioend->io_list);
>  		ioend->io_size += next_ioend->io_size;
> -		if (ioend->io_append_trans) {
> -			error = xfs_setfilesize_ioend(next_ioend, 1);
> -			ASSERT(error == 1);
> -		}
>  	}
>  }
>  
> @@ -398,7 +325,7 @@ xfs_end_bio(
>  
>  	if (ioend->io_fork == XFS_COW_FORK ||
>  	    ioend->io_type == IOMAP_UNWRITTEN ||
> -	    ioend->io_append_trans != NULL) {
> +	    xfs_ioend_is_append(ioend)) {
>  		spin_lock_irqsave(&ip->i_ioend_lock, flags);
>  		if (list_empty(&ip->i_ioend_list))
>  			WARN_ON_ONCE(!queue_work(mp->m_unwritten_workqueue,
> @@ -660,18 +587,6 @@ xfs_submit_ioend(
>  		memalloc_nofs_restore(nofs_flag);
>  	}
>  
> -	/* Reserve log space if we might write beyond the on-disk inode size. */
> -	if (!status &&
> -	    (ioend->io_fork == XFS_COW_FORK ||
> -	     ioend->io_type != IOMAP_UNWRITTEN) &&
> -	    xfs_ioend_is_append(ioend) &&
> -	    !ioend->io_append_trans) {
> -		unsigned nofs_flag = memalloc_nofs_save();
> -
> -		status = xfs_setfilesize_trans_alloc(ioend);
> -		memalloc_nofs_restore(nofs_flag);
> -	}
> -
>  	ioend->io_bio->bi_private = ioend;
>  	ioend->io_bio->bi_end_io = xfs_end_bio;
>  
> @@ -715,7 +630,6 @@ xfs_alloc_ioend(
>  	ioend->io_inode = inode;
>  	ioend->io_size = 0;
>  	ioend->io_offset = offset;
> -	ioend->io_append_trans = NULL;
>  	ioend->io_bio = bio;
>  	return ioend;
>  }
> diff --git a/fs/xfs/xfs_aops.h b/fs/xfs/xfs_aops.h
> index 72e30d1c3bdf..23c087f0bcbf 100644
> --- a/fs/xfs/xfs_aops.h
> +++ b/fs/xfs/xfs_aops.h
> @@ -18,7 +18,6 @@ struct xfs_ioend {
>  	struct inode		*io_inode;	/* file being written to */
>  	size_t			io_size;	/* size of the extent */
>  	xfs_off_t		io_offset;	/* offset in the file */
> -	struct xfs_trans	*io_append_trans;/* xact. for size update */
>  	struct bio		*io_bio;	/* bio being built */
>  	struct bio		io_inline_bio;	/* MUST BE LAST! */
>  };
> -- 
> 2.20.1
> 

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

* Re: [PATCH 06/12] xfs: remove XFS_TRANS_NOFS
  2019-06-24  5:52 ` [PATCH 06/12] xfs: remove XFS_TRANS_NOFS Christoph Hellwig
  2019-06-24 15:58   ` Darrick J. Wong
@ 2019-06-24 22:59   ` Dave Chinner
  2019-06-25 10:12     ` Christoph Hellwig
  1 sibling, 1 reply; 52+ messages in thread
From: Dave Chinner @ 2019-06-24 22:59 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Darrick J . Wong, Damien Le Moal, Andreas Gruenbacher, linux-xfs,
	linux-fsdevel, linux-kernel

On Mon, Jun 24, 2019 at 07:52:47AM +0200, Christoph Hellwig wrote:
> Instead of a magic flag for xfs_trans_alloc, just ensure all callers
> that can't relclaim through the file system use memalloc_nofs_save to
> set the per-task nofs flag.

I'm thinking that it would be a good idea to add comments to explain
exactly what the memalloc_nofs_save/restore() are protecting where
they are used. Right now the XFS_TRANS_NOFS flag is largely
undocumented, so a reader is left guessing as to why the flag is
necessary and what contexts it may apply to. Hence I think we should
fix that while we are changing over to a different GFP_NOFS
allocation context mechanism....

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

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

* Re: [PATCH 07/12] xfs: don't preallocate a transaction for file size updates
  2019-06-24 16:17   ` Darrick J. Wong
@ 2019-06-24 23:15     ` Dave Chinner
  2019-06-25 10:25       ` Christoph Hellwig
  0 siblings, 1 reply; 52+ messages in thread
From: Dave Chinner @ 2019-06-24 23:15 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Christoph Hellwig, Damien Le Moal, Andreas Gruenbacher,
	linux-xfs, linux-fsdevel, linux-kernel

On Mon, Jun 24, 2019 at 09:17:20AM -0700, Darrick J. Wong wrote:
> On Mon, Jun 24, 2019 at 07:52:48AM +0200, Christoph Hellwig wrote:
> > We have historically decided that we want to preallocate the xfs_trans
> > structure at writeback time so that we don't have to allocate on in
> > the I/O completion handler.  But we treat unwrittent extent and COW
> > fork conversions different already, which proves that the transaction
> > allocations in the end I/O handler are not a problem.  Removing the
> > preallocation gets rid of a lot of corner case code, and also ensures
> > we only allocate one and log a transaction when actually required,
> > as the ioend merging can reduce the number of actual i_size updates
> > significantly.
> 
> That's what I thought when I wrote the ioend merging patches, but IIRC
> Dave objected on the grounds that most file writes are trivial file
> extending writes and therefore we should leave this alone to avoid
> slowing down the ioend path even if it came at a cost of cancelling a
> lot of empty transactions.

The issue is stuff like extracting a tarball, where we might write a
hundred thousand files and they are all written in a single IO. i.e.
there is no IO completion merging at all.

> I wasn't 100% convinced it mattered but ran out of time in the
> development window and never got around to researching if it made any
> difference.

Yeah, it's not all that simple :/

In these cases, we always have to allocate a transaction for every
file being written. If we do it before we submit the IO, then all
the transactions are allocated from the single writeback context. If
we don't have log space, data writeback pauses while the tail of the
AIL is pushed, metadata writeback occurs, and then the transaction
allocation for data writeback is woken, and data writeback
submission continues. It's fairly orderly, and we don't end up
trying to write back data while we are doing bulk metadata flushing
from the AIL.

If we delay the transaction allocation to the ioend context and we
are low on log space, we end up blocking a kworker on a transaction
allocation which then has to wait for metadata writeback. The
kworker infrastructure will then issue the next ioend work, which
then blocks on transaction allocation. Delayed allocation can cause
thousands of small file IOs to be inflight at the same time due to
intra-file contiguous allocation and IO merging in the block layer,
hence we can have thousands of individual IO completions that
require transaction allocation to be completed.

> So, uh, how much of a hit do we take for having to allocate a
> transaction for a file size extension?  Particularly since we can
> combine those things now?

Unless we are out of log space, the transaction allocation and free
should be largely uncontended and so it's just a small amount of CPU
usage. i.e it's a slab allocation/free and then lockless space
reservation/free. If we are out of log space, then we sleep waiting
for space - the issue really comes down to where it is better to
sleep in that case....

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

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

* Re: [PATCH 11/12] iomap: move the xfs writeback code to iomap.c
  2019-06-24  5:52 ` [PATCH 11/12] iomap: move the xfs writeback code to iomap.c Christoph Hellwig
  2019-06-24 15:46   ` Darrick J. Wong
@ 2019-06-24 23:43   ` Dave Chinner
  2019-06-25 10:10     ` Christoph Hellwig
  1 sibling, 1 reply; 52+ messages in thread
From: Dave Chinner @ 2019-06-24 23:43 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Darrick J . Wong, Damien Le Moal, Andreas Gruenbacher, linux-xfs,
	linux-fsdevel, linux-kernel

On Mon, Jun 24, 2019 at 07:52:52AM +0200, Christoph Hellwig wrote:
> Takes the xfs writeback code and move it to iomap.c.  A new structure
> with three methods is added as the abstraction from the generic
> writeback code to the file system.  These methods are used to map
> blocks, submit an ioend, and cancel a page that encountered an error
> before it was added to an ioend.
> 
> Note that we temporarily lose the writepage tracing, but that will
> be added back soon.

I'm a little concerned this is going to limit what we can do
with the XFS IO path because now we can't change this code without
considering the direct impact on other filesystems. The QA burden of
changing the XFS writeback code goes through the roof with this
change (i.e. we can break multiple filesystems, not just XFS).

The writepage code is one of the areas that, historically speaking,
has one of the highest rates of modification in XFS - we've
substantially reworked this code from top to bottom 4 or 5 times in
a bit over ten years, and each time it's been removing abstraction
layers and getting the writeback code closer to the internal XFS
extent mapping infrastructure.

This steps the other way - it adds abstraction to move the XFS code
to be generic, and now we have to be concerned about how changes to
the XFS IO path affects other filesystems. While I can see the
desire to use this code in other filesystems, no other filesystem
does COW or delayed allocation like XFS and this functionality is
tightly tied into the iomap page writeback architecture.

As such, I'm not convinced that a wholesale lifting of this code
into the generic iomap code is going to make our life easier or
better. The stuff we've already got in fs/iomap.c is largely
uncontroversial and straight forward, but this writeback code is
anything but straight forward.....

Another issue this raises is that fs/iomap.c is already huge chunk
of code with lots of different functionality in it. Adding another
500+ lines of new functionality to it doesn't make it any easier to
navigate or find things.

If we are going to move this writeback code to the generic iomap
infrastructure, can we please split the iomap code up in to smaller
files first?  e.g. fs/iomap-dio.c for all the direct IO code,
fs/iomap-pageio.c for all the page-based IO, fs/iomap.c for all the
core functionality (like iomap_apply()) and fs/iomap-util.c for all
the miscellaneous one-off functions like fiemap, etc?

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

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

* Re: [PATCH 12/12] iomap: add tracing for the address space operations
  2019-06-24  5:52 ` [PATCH 12/12] iomap: add tracing for the address space operations Christoph Hellwig
@ 2019-06-24 23:49   ` Dave Chinner
  2019-06-25 10:15     ` Christoph Hellwig
  0 siblings, 1 reply; 52+ messages in thread
From: Dave Chinner @ 2019-06-24 23:49 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Darrick J . Wong, Damien Le Moal, Andreas Gruenbacher, linux-xfs,
	linux-fsdevel, linux-kernel

On Mon, Jun 24, 2019 at 07:52:53AM +0200, Christoph Hellwig wrote:
> Lift the xfs code for tracing address space operations to the iomap
> layer.
> 
> Signed-off-by: Christoph Hellwig <hch@lst.de>

....

> diff --git a/include/trace/events/iomap.h b/include/trace/events/iomap.h
> new file mode 100644
> index 000000000000..da50ece663f8
> --- /dev/null
> +++ b/include/trace/events/iomap.h
> @@ -0,0 +1,82 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2009-2019, Christoph Hellwig
> + * All Rights Reserved.
> + */
> +#undef TRACE_SYSTEM
> +#define TRACE_SYSTEM iomap

Can you add a comment somewhere here that says these tracepoints are
volatile and we reserve the right to change them at any time so they
don't form any sort of persistent UAPI that we have to maintain?

-Dave.
-- 
Dave Chinner
david@fromorbit.com

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

* Re: [PATCH 01/12] list.h: add a list_pop helper
  2019-06-24 15:51   ` Matthew Wilcox
@ 2019-06-25 10:06     ` Christoph Hellwig
  0 siblings, 0 replies; 52+ messages in thread
From: Christoph Hellwig @ 2019-06-25 10:06 UTC (permalink / raw)
  To: Matthew Wilcox
  Cc: Christoph Hellwig, Darrick J . Wong, Damien Le Moal,
	Andreas Gruenbacher, linux-xfs, linux-fsdevel, linux-kernel

On Mon, Jun 24, 2019 at 08:51:37AM -0700, Matthew Wilcox wrote:
> The usual convention in list.h is that list_foo uses the list head and
> list_foo_entry uses the container type.  So I think this should be
> renamed to list_pop_entry() at least.  Do we also want:
> 
> static inline struct list_head *list_pop(struct list_head *head)
> {
> 	struct list_head *first = READ_ONCE(head->next);
> 
> 	if (first == head)
> 		return NULL;
> 	__list_del(head, first->next);
> 	return first;
> }
> 
> we also seem to prefer using inline functions over #defines in this
> header file.

Sure, I can rename it and split the implementation.

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

* Re: [PATCH 04/12] xfs: initialize ioma->flags in xfs_bmbt_to_iomap
  2019-06-24 14:57   ` Darrick J. Wong
@ 2019-06-25 10:07     ` Christoph Hellwig
  2019-06-25 15:13       ` Darrick J. Wong
  0 siblings, 1 reply; 52+ messages in thread
From: Christoph Hellwig @ 2019-06-25 10:07 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Christoph Hellwig, Damien Le Moal, Andreas Gruenbacher,
	linux-xfs, linux-fsdevel, linux-kernel

On Mon, Jun 24, 2019 at 07:57:07AM -0700, Darrick J. Wong wrote:
> On Mon, Jun 24, 2019 at 07:52:45AM +0200, Christoph Hellwig wrote:
> > Currently we don't overwrite the flags field in the iomap in
> > xfs_bmbt_to_iomap.  This works fine with 0-initialized iomaps on stack,
> > but is harmful once we want to be able to reuse an iomap in the
> > writeback code.
> 
> Is that going to affect all the other iomap users, or is xfs the only
> one that assumes zero-initialized iomaps being passed into
> ->iomap_begin?

This doesn't affect any existing user as they all get a zeroed iomap
passed from the caller in iomap.c.  It affects the writeback code
once it uses struct iomap as it overwrites a previously used iomap.

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

* Re: [PATCH 11/12] iomap: move the xfs writeback code to iomap.c
  2019-06-24 15:46   ` Darrick J. Wong
@ 2019-06-25 10:08     ` Christoph Hellwig
  0 siblings, 0 replies; 52+ messages in thread
From: Christoph Hellwig @ 2019-06-25 10:08 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Christoph Hellwig, Damien Le Moal, Andreas Gruenbacher,
	linux-xfs, linux-fsdevel, linux-kernel

On Mon, Jun 24, 2019 at 08:46:01AM -0700, Darrick J. Wong wrote:
> This looks like a straight code copy from fs/xfs/ into fs/iomap.c.
> That's fine with me, but seeing as this file is now ~2700 lines long,
> perhaps we should break this up among major functional lines?
> 
> Looking at fs/iomap.c, I see...
> 
>  * Basic iomap iterator functions (~40 lines)
>  * Page cache management (readpage*, write, mkwrite) (~860 lines)
>  * Zeroing (~80 lines)
>  * FIEMAP and seek hole / seek data (~300 lines)
>  * directio (~500 lines)
>  * swapfiles (~170 lines)
>  * and now, page cache writeback (~520 lines)
> 
> If I have spare time this week (ha ha) I'll see if I can break all this
> up (as a separate patch series), so for this:

Meh.  Not sure I'm a fan of too fine grained splits like the one
above.  And ~3k lines is still pretty manageable.  But yes, once it
grows むore it might be worth splitting a bit.

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

* Re: [PATCH 11/12] iomap: move the xfs writeback code to iomap.c
  2019-06-24 23:43   ` Dave Chinner
@ 2019-06-25 10:10     ` Christoph Hellwig
  2019-06-28  0:45       ` Dave Chinner
  0 siblings, 1 reply; 52+ messages in thread
From: Christoph Hellwig @ 2019-06-25 10:10 UTC (permalink / raw)
  To: Dave Chinner
  Cc: Christoph Hellwig, Darrick J . Wong, Damien Le Moal,
	Andreas Gruenbacher, linux-xfs, linux-fsdevel, linux-kernel

On Tue, Jun 25, 2019 at 09:43:04AM +1000, Dave Chinner wrote:
> I'm a little concerned this is going to limit what we can do
> with the XFS IO path because now we can't change this code without
> considering the direct impact on other filesystems. The QA burden of
> changing the XFS writeback code goes through the roof with this
> change (i.e. we can break multiple filesystems, not just XFS).

Going through the roof is a little exaggerated.  Yes, it will be more
testing overhead, but that is life in a world where we try to share
code rather than duplicating it, which is pretty much a general
kernel policy that has served us well.

> The writepage code is one of the areas that, historically speaking,
> has one of the highest rates of modification in XFS - we've
> substantially reworked this code from top to bottom 4 or 5 times in
> a bit over ten years, and each time it's been removing abstraction
> layers and getting the writeback code closer to the internal XFS
> extent mapping infrastructure.

I don't think we had all that much churn.  Yes, we've improved it a
lot, but much of that was in response to core changes, and pretty much
all of it benefits other users as well.  And the more users we have
for this infrastructure that more clout it has with core VM folks
when we have to push back odd design decisions.

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

* Re: [PATCH 06/12] xfs: remove XFS_TRANS_NOFS
  2019-06-24 22:59   ` Dave Chinner
@ 2019-06-25 10:12     ` Christoph Hellwig
  0 siblings, 0 replies; 52+ messages in thread
From: Christoph Hellwig @ 2019-06-25 10:12 UTC (permalink / raw)
  To: Dave Chinner
  Cc: Christoph Hellwig, Darrick J . Wong, Damien Le Moal,
	Andreas Gruenbacher, linux-xfs, linux-fsdevel, linux-kernel

On Tue, Jun 25, 2019 at 08:59:04AM +1000, Dave Chinner wrote:
> On Mon, Jun 24, 2019 at 07:52:47AM +0200, Christoph Hellwig wrote:
> > Instead of a magic flag for xfs_trans_alloc, just ensure all callers
> > that can't relclaim through the file system use memalloc_nofs_save to
> > set the per-task nofs flag.
> 
> I'm thinking that it would be a good idea to add comments to explain
> exactly what the memalloc_nofs_save/restore() are protecting where
> they are used. Right now the XFS_TRANS_NOFS flag is largely
> undocumented, so a reader is left guessing as to why the flag is
> necessary and what contexts it may apply to. Hence I think we should
> fix that while we are changing over to a different GFP_NOFS
> allocation context mechanism....

Sure.

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

* Re: [PATCH 09/12] xfs: refactor the ioend merging code
  2019-06-24 16:06   ` Nikolay Borisov
@ 2019-06-25 10:14     ` Christoph Hellwig
  2019-06-25 12:42       ` Nikolay Borisov
  0 siblings, 1 reply; 52+ messages in thread
From: Christoph Hellwig @ 2019-06-25 10:14 UTC (permalink / raw)
  To: Nikolay Borisov
  Cc: Christoph Hellwig, Darrick J . Wong, Damien Le Moal,
	Andreas Gruenbacher, linux-xfs, linux-fsdevel, linux-kernel

On Mon, Jun 24, 2019 at 07:06:22PM +0300, Nikolay Borisov wrote:
> > +{
> > +	struct list_head	tmp;
> > +
> > +	list_replace_init(&ioend->io_list, &tmp);
> > +	xfs_destroy_ioend(ioend, error);
> > +	while ((ioend = list_pop(&tmp, struct xfs_ioend, io_list)))
> > +		xfs_destroy_ioend(ioend, error);
> 
> nit: I'd prefer if the list_pop patch is right before this one since
> this is the first user of it.

I try to keep generic infrastructure first instead of interveawing
it with subystem-specific patches.

> Additionally, I don't think list_pop is
> really a net-negative win 

What is a "net-negative win" ?

> in comparison to list_for_each_entry_safe
> here. In fact this "delete the list" would seems more idiomatic if
> implemented via list_for_each_entry_safe

I disagree.  The for_each loops require an additional next iterator,
and also don't clearly express what is going on, but require additional
spotting of the list_del.

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

* Re: [PATCH 12/12] iomap: add tracing for the address space operations
  2019-06-24 23:49   ` Dave Chinner
@ 2019-06-25 10:15     ` Christoph Hellwig
  2019-06-25 14:47       ` Darrick J. Wong
  2019-06-27 22:35       ` Dave Chinner
  0 siblings, 2 replies; 52+ messages in thread
From: Christoph Hellwig @ 2019-06-25 10:15 UTC (permalink / raw)
  To: Dave Chinner
  Cc: Christoph Hellwig, Darrick J . Wong, Damien Le Moal,
	Andreas Gruenbacher, linux-xfs, linux-fsdevel, linux-kernel

On Tue, Jun 25, 2019 at 09:49:21AM +1000, Dave Chinner wrote:
> > +#undef TRACE_SYSTEM
> > +#define TRACE_SYSTEM iomap
> 
> Can you add a comment somewhere here that says these tracepoints are
> volatile and we reserve the right to change them at any time so they
> don't form any sort of persistent UAPI that we have to maintain?

Sure.  Note that we don't have any such comment in xfs either..

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

* Re: [PATCH 07/12] xfs: don't preallocate a transaction for file size updates
  2019-06-24 23:15     ` Dave Chinner
@ 2019-06-25 10:25       ` Christoph Hellwig
  2019-06-27 22:23         ` Dave Chinner
  0 siblings, 1 reply; 52+ messages in thread
From: Christoph Hellwig @ 2019-06-25 10:25 UTC (permalink / raw)
  To: Dave Chinner
  Cc: Darrick J. Wong, Christoph Hellwig, Damien Le Moal,
	Andreas Gruenbacher, linux-xfs, linux-fsdevel, linux-kernel

On Tue, Jun 25, 2019 at 09:15:23AM +1000, Dave Chinner wrote:
> > So, uh, how much of a hit do we take for having to allocate a
> > transaction for a file size extension?  Particularly since we can
> > combine those things now?
> 
> Unless we are out of log space, the transaction allocation and free
> should be largely uncontended and so it's just a small amount of CPU
> usage. i.e it's a slab allocation/free and then lockless space
> reservation/free. If we are out of log space, then we sleep waiting
> for space - the issue really comes down to where it is better to
> sleep in that case....

I see the general point, but we'll still have the same issue with
unwritten extent conversion and cow completions, and I don't remember
seeing any issue in that regard.  And we'd hit exactly that case
with random writes to preallocated or COW files, i.e. the typical image
file workload.

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

* Re: [PATCH 09/12] xfs: refactor the ioend merging code
  2019-06-25 10:14     ` Christoph Hellwig
@ 2019-06-25 12:42       ` Nikolay Borisov
  2019-06-25 14:45         ` Darrick J. Wong
  0 siblings, 1 reply; 52+ messages in thread
From: Nikolay Borisov @ 2019-06-25 12:42 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Darrick J . Wong, Damien Le Moal, Andreas Gruenbacher, linux-xfs,
	linux-fsdevel, linux-kernel



On 25.06.19 г. 13:14 ч., Christoph Hellwig wrote:
> On Mon, Jun 24, 2019 at 07:06:22PM +0300, Nikolay Borisov wrote:
>>> +{
>>> +	struct list_head	tmp;
>>> +
>>> +	list_replace_init(&ioend->io_list, &tmp);
>>> +	xfs_destroy_ioend(ioend, error);
>>> +	while ((ioend = list_pop(&tmp, struct xfs_ioend, io_list)))
>>> +		xfs_destroy_ioend(ioend, error);
>>
>> nit: I'd prefer if the list_pop patch is right before this one since
>> this is the first user of it.
> 
> I try to keep generic infrastructure first instead of interveawing
> it with subystem-specific patches.
> 
>> Additionally, I don't think list_pop is
>> really a net-negative win 
> 
> What is a "net-negative win" ?

What I meant was 'net-positive win', in terms of making the code more
readable or optimised.

> 
>> in comparison to list_for_each_entry_safe
>> here. In fact this "delete the list" would seems more idiomatic if
>> implemented via list_for_each_entry_safe
> 
> I disagree.  The for_each loops require an additional next iterator,
> and also don't clearly express what is going on, but require additional
> spotting of the list_del.

That is of course your opinion. At the very least we can agree to disagree.

What I'm worried about, though, is now you've essentially introduced a
new idiom to dispose of lists, which is used only in your code. If it
doesn't become more widespread and gradually start replacing current
list_for_each_entry_safe usage then you would have increased the public
list interface to cater for one specific use case, just because it seems
more natural to you. I guess only time will show whether it makes sense
to have list_pop_entry


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

* Re: [PATCH 09/12] xfs: refactor the ioend merging code
  2019-06-25 12:42       ` Nikolay Borisov
@ 2019-06-25 14:45         ` Darrick J. Wong
  0 siblings, 0 replies; 52+ messages in thread
From: Darrick J. Wong @ 2019-06-25 14:45 UTC (permalink / raw)
  To: Nikolay Borisov
  Cc: Christoph Hellwig, Damien Le Moal, Andreas Gruenbacher,
	linux-xfs, linux-fsdevel, linux-kernel

On Tue, Jun 25, 2019 at 03:42:20PM +0300, Nikolay Borisov wrote:
> 
> 
> On 25.06.19 г. 13:14 ч., Christoph Hellwig wrote:
> > On Mon, Jun 24, 2019 at 07:06:22PM +0300, Nikolay Borisov wrote:
> >>> +{
> >>> +	struct list_head	tmp;
> >>> +
> >>> +	list_replace_init(&ioend->io_list, &tmp);
> >>> +	xfs_destroy_ioend(ioend, error);
> >>> +	while ((ioend = list_pop(&tmp, struct xfs_ioend, io_list)))
> >>> +		xfs_destroy_ioend(ioend, error);
> >>
> >> nit: I'd prefer if the list_pop patch is right before this one since
> >> this is the first user of it.
> > 
> > I try to keep generic infrastructure first instead of interveawing
> > it with subystem-specific patches.
> > 
> >> Additionally, I don't think list_pop is
> >> really a net-negative win 
> > 
> > What is a "net-negative win" ?
> 
> What I meant was 'net-positive win', in terms of making the code more
> readable or optimised.
> 
> > 
> >> in comparison to list_for_each_entry_safe
> >> here. In fact this "delete the list" would seems more idiomatic if
> >> implemented via list_for_each_entry_safe
> > 
> > I disagree.  The for_each loops require an additional next iterator,
> > and also don't clearly express what is going on, but require additional
> > spotting of the list_del.
> 
> That is of course your opinion. At the very least we can agree to disagree.
> 
> What I'm worried about, though, is now you've essentially introduced a
> new idiom to dispose of lists, which is used only in your code. If it
> doesn't become more widespread and gradually start replacing current
> list_for_each_entry_safe usage then you would have increased the public
> list interface to cater for one specific use case, just because it seems
> more natural to you. I guess only time will show whether it makes sense
> to have list_pop_entry

I for one would love to replace all the opencoded "walk a list and drop
each entry before we move on" code in fs/xfs/scrub/ with list_pop_entry.
Quickly scanning fs/xfs/, there seem to be a couple dozen places where
we could probably do that too.

--D

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

* Re: [PATCH 12/12] iomap: add tracing for the address space operations
  2019-06-25 10:15     ` Christoph Hellwig
@ 2019-06-25 14:47       ` Darrick J. Wong
  2019-06-27 22:35       ` Dave Chinner
  1 sibling, 0 replies; 52+ messages in thread
From: Darrick J. Wong @ 2019-06-25 14:47 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Dave Chinner, Damien Le Moal, Andreas Gruenbacher, linux-xfs,
	linux-fsdevel, linux-kernel

On Tue, Jun 25, 2019 at 12:15:15PM +0200, Christoph Hellwig wrote:
> On Tue, Jun 25, 2019 at 09:49:21AM +1000, Dave Chinner wrote:
> > > +#undef TRACE_SYSTEM
> > > +#define TRACE_SYSTEM iomap
> > 
> > Can you add a comment somewhere here that says these tracepoints are
> > volatile and we reserve the right to change them at any time so they
> > don't form any sort of persistent UAPI that we have to maintain?
> 
> Sure.  Note that we don't have any such comment in xfs either..

I think we ought to add a comment to both of the tracepoint header files
in xfs then...

--D

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

* Re: [PATCH 04/12] xfs: initialize ioma->flags in xfs_bmbt_to_iomap
  2019-06-25 10:07     ` Christoph Hellwig
@ 2019-06-25 15:13       ` Darrick J. Wong
  2019-06-25 15:21         ` Christoph Hellwig
  0 siblings, 1 reply; 52+ messages in thread
From: Darrick J. Wong @ 2019-06-25 15:13 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	linux-kernel

On Tue, Jun 25, 2019 at 12:07:01PM +0200, Christoph Hellwig wrote:
> On Mon, Jun 24, 2019 at 07:57:07AM -0700, Darrick J. Wong wrote:
> > On Mon, Jun 24, 2019 at 07:52:45AM +0200, Christoph Hellwig wrote:
> > > Currently we don't overwrite the flags field in the iomap in
> > > xfs_bmbt_to_iomap.  This works fine with 0-initialized iomaps on stack,
> > > but is harmful once we want to be able to reuse an iomap in the
> > > writeback code.
> > 
> > Is that going to affect all the other iomap users, or is xfs the only
> > one that assumes zero-initialized iomaps being passed into
> > ->iomap_begin?
> 
> This doesn't affect any existing user as they all get a zeroed iomap
> passed from the caller in iomap.c.  It affects the writeback code
> once it uses struct iomap as it overwrites a previously used iomap.

Then shouldn't this new writeback code zero the iomap before calling
back into the filesystem, just to maintain consistent behavior?

--D

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

* Re: [PATCH 04/12] xfs: initialize ioma->flags in xfs_bmbt_to_iomap
  2019-06-25 15:13       ` Darrick J. Wong
@ 2019-06-25 15:21         ` Christoph Hellwig
  0 siblings, 0 replies; 52+ messages in thread
From: Christoph Hellwig @ 2019-06-25 15:21 UTC (permalink / raw)
  To: Darrick J. Wong
  Cc: Christoph Hellwig, Damien Le Moal, Andreas Gruenbacher,
	linux-xfs, linux-fsdevel, linux-kernel

On Tue, Jun 25, 2019 at 08:13:57AM -0700, Darrick J. Wong wrote:
> > This doesn't affect any existing user as they all get a zeroed iomap
> > passed from the caller in iomap.c.  It affects the writeback code
> > once it uses struct iomap as it overwrites a previously used iomap.
> 
> Then shouldn't this new writeback code zero the iomap before calling
> back into the filesystem, just to maintain consistent behavior?

The core code doesn't decide when to overwrite it, that ->map_blocks
method does that based on a few factors (including the data_seq/cow_seq
counters that are entirely inside xfs).

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

* Re: [PATCH 07/12] xfs: don't preallocate a transaction for file size updates
  2019-06-25 10:25       ` Christoph Hellwig
@ 2019-06-27 22:23         ` Dave Chinner
  0 siblings, 0 replies; 52+ messages in thread
From: Dave Chinner @ 2019-06-27 22:23 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Darrick J. Wong, Damien Le Moal, Andreas Gruenbacher, linux-xfs,
	linux-fsdevel, linux-kernel

On Tue, Jun 25, 2019 at 12:25:07PM +0200, Christoph Hellwig wrote:
> On Tue, Jun 25, 2019 at 09:15:23AM +1000, Dave Chinner wrote:
> > > So, uh, how much of a hit do we take for having to allocate a
> > > transaction for a file size extension?  Particularly since we can
> > > combine those things now?
> > 
> > Unless we are out of log space, the transaction allocation and free
> > should be largely uncontended and so it's just a small amount of CPU
> > usage. i.e it's a slab allocation/free and then lockless space
> > reservation/free. If we are out of log space, then we sleep waiting
> > for space - the issue really comes down to where it is better to
> > sleep in that case....
> 
> I see the general point, but we'll still have the same issue with
> unwritten extent conversion and cow completions, and I don't remember
> seeing any issue in that regard.

These are realtively rare for small file workloads - I'm really
talking about the effect of delalloc and how we've optimised
allocation during writeback to merge small, cross-file writeback
into much larger large physical IOs. Unwritten extents nor COW are
used in these (common) cases, and if they are then the allocation
patterns prevent the cross-file IO merging in the block layer and so
we don't get the "hundred ioends for a hundred inodes from a single
a physical IO completion" thundering heard problem....

> And we'd hit exactly that case
> with random writes to preallocated or COW files, i.e. the typical image
> file workload.

I do see a noticable amount of IO completion overhead in the host
when hitting unwritten extents in VM image workloads. I'll see if I
can track the number of kworkers we're stalling in under some of
these workloads, but I think it's still largely bound by the request
queue depth of the IO stack inside the VM because there is no IO
merging in these cases.

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

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

* Re: [PATCH 12/12] iomap: add tracing for the address space operations
  2019-06-25 10:15     ` Christoph Hellwig
  2019-06-25 14:47       ` Darrick J. Wong
@ 2019-06-27 22:35       ` Dave Chinner
  1 sibling, 0 replies; 52+ messages in thread
From: Dave Chinner @ 2019-06-27 22:35 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Darrick J . Wong, Damien Le Moal, Andreas Gruenbacher, linux-xfs,
	linux-fsdevel, linux-kernel

On Tue, Jun 25, 2019 at 12:15:15PM +0200, Christoph Hellwig wrote:
> On Tue, Jun 25, 2019 at 09:49:21AM +1000, Dave Chinner wrote:
> > > +#undef TRACE_SYSTEM
> > > +#define TRACE_SYSTEM iomap
> > 
> > Can you add a comment somewhere here that says these tracepoints are
> > volatile and we reserve the right to change them at any time so they
> > don't form any sort of persistent UAPI that we have to maintain?
> 
> Sure.  Note that we don't have any such comment in xfs either..

Yes, but that is buries inside the xfs code where we largely set our
own rules. This, however, is generic code where people have a habit
of arguing that tracepoints are stable API and they can never be
changed because some random userspace application may have hard
coded a dependency on it...

Hence we need to be explicit here that this is diagnostic/debug code
and anyone who tries to rely on it as a stable API gets to keep all
the broken bits to themselves.

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

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

* Re: [PATCH 11/12] iomap: move the xfs writeback code to iomap.c
  2019-06-25 10:10     ` Christoph Hellwig
@ 2019-06-28  0:45       ` Dave Chinner
  2019-06-28  5:33         ` Christoph Hellwig
  2019-06-28 22:27         ` Luis Chamberlain
  0 siblings, 2 replies; 52+ messages in thread
From: Dave Chinner @ 2019-06-28  0:45 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Darrick J . Wong, Damien Le Moal, Andreas Gruenbacher, linux-xfs,
	linux-fsdevel, linux-kernel

On Tue, Jun 25, 2019 at 12:10:20PM +0200, Christoph Hellwig wrote:
> On Tue, Jun 25, 2019 at 09:43:04AM +1000, Dave Chinner wrote:
> > I'm a little concerned this is going to limit what we can do
> > with the XFS IO path because now we can't change this code without
> > considering the direct impact on other filesystems. The QA burden of
> > changing the XFS writeback code goes through the roof with this
> > change (i.e. we can break multiple filesystems, not just XFS).
> 
> Going through the roof is a little exaggerated.

You've already mentioned two new users you want to add. I don't even
have zone capable hardware here to test one of the users you are
indicating will use this code, and I suspect that very few people
do.  That's a non-trivial increase in testing requirements for
filesystem developers and distro QA departments who will want to
change and/or validate this code path.

> Yes, it will be more
> testing overhead, but that is life in a world where we try to share
> code rather than duplicating it, which is pretty much a general
> kernel policy that has served us well.

Yes, but we also need to acknowledge why we have re-implemented
everything in fs/iomap.c - we haven't lifted code from XFS to do
that - we've replaced existing generic code that didn't do what we
needed and couldn't easily be modified to do what we needed because
of all it's external dependencies.

Indeed, integrating gfs2 into the existing generic iomap code has
required quite a bit of munging and adding new code paths and so on.
That's mostly been straight forward because it's just been adding
flags and conditional code to the existing paths. The way we
regularly rewrite sections of the XFS writeback code is a very
different sort of modification, and one that will be much harder to
do if we have to make those changes to generic code.

i.e. shared code is good if it's simple and doesn't have a lot of
external dependencies that restrict the type and scope of
modifications that can be made easily. Shared code that is complex
and comes from code that was tightly integrated with a specific
subsystem architecture is going to carry all those architectural
foilbles into the new "generic" code. Once it gets sufficient
users it's going to end up with the same "we can't change this code"
problems that we had with the existing IO path, and we'll go back to
implementing our own writeback path....

> > The writepage code is one of the areas that, historically speaking,
> > has one of the highest rates of modification in XFS - we've
> > substantially reworked this code from top to bottom 4 or 5 times in
> > a bit over ten years, and each time it's been removing abstraction
> > layers and getting the writeback code closer to the internal XFS
> > extent mapping infrastructure.
> 
> I don't think we had all that much churn.

We've had more churn over time to the writeback code than just about
any other subsystem in XFS.

It also represents a complete 180-degree flip on how we've been
streamlining the writeback path in XFS over the past few years.
We've been moving closer and closer to the generic writeback
infrastructure as well as closer to the XFS inode extent tree.

I've been planning on taking it even closer to the extent tree to
give us lockless, modification range coherent extent map caching in
this path (e.g. write() can add new delalloc extents without
invalidating cached writeback maps).  This patchset re-introduces
the iomap abstraction over the bmbt - an abstraction we removed some
time ago - and that makes these sorts of improvements much harder
and more complex to implement....

IOWs, I'm not convinced that lifting this code out of XFS is the
best long term plan for XFS or the iomap code here....

> Yes, we've improved it a
> lot, but much of that was in response to core changes, and pretty much
> all of it benefits other users as well.  And the more users we have
> for this infrastructure that more clout it has with core VM folks
> when we have to push back odd design decisions.

If we have to make stuff "generic" to be able to influence how other
subsystems go about providing infrastructure to filesytsems, then
our development community and processes are even more broken than I
think they are. Developer communication and design influence are not
problems we should be trying to fix with code.

Cheers,

Dave.
-Dave.
-- 
Dave Chinner
david@fromorbit.com

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

* Re: [PATCH 11/12] iomap: move the xfs writeback code to iomap.c
  2019-06-28  0:45       ` Dave Chinner
@ 2019-06-28  5:33         ` Christoph Hellwig
  2019-07-01  0:08           ` Dave Chinner
  2019-06-28 22:27         ` Luis Chamberlain
  1 sibling, 1 reply; 52+ messages in thread
From: Christoph Hellwig @ 2019-06-28  5:33 UTC (permalink / raw)
  To: Dave Chinner
  Cc: Christoph Hellwig, Darrick J . Wong, Damien Le Moal,
	Andreas Gruenbacher, linux-xfs, linux-fsdevel, linux-kernel

On Fri, Jun 28, 2019 at 10:45:42AM +1000, Dave Chinner wrote:
> You've already mentioned two new users you want to add. I don't even
> have zone capable hardware here to test one of the users you are
> indicating will use this code, and I suspect that very few people
> do.  That's a non-trivial increase in testing requirements for
> filesystem developers and distro QA departments who will want to
> change and/or validate this code path.

Why do you assume you have to test it?  Back when we shared
generic_file_read with everyone you also didn't test odd change to
it with every possible fs.  If you change iomap.c, you'll test it
with XFS, and Cc other maintainers so that they get a chance to
also test it and comment on it, just like we do with other shared
code in the kernel.

> Indeed, integrating gfs2 into the existing generic iomap code has
> required quite a bit of munging and adding new code paths and so on.
> That's mostly been straight forward because it's just been adding
> flags and conditional code to the existing paths. The way we
> regularly rewrite sections of the XFS writeback code is a very
> different sort of modification, and one that will be much harder to
> do if we have to make those changes to generic code.

As the person who has done a lot of the recent rewriting of the
writeback code I disagree.  Most of it has been do divorce is from
leftovers of the buffer_head based sinle page at a time design from
stone age.  Very little is about XFS itself, most of it has been
about not being stupid in a fairly generic way.  And every since
I got rid of buffer heads xfs_aops.c has been intimately tied
into the iomap infrastructure, and I'd rather keep those details in
one place.  I.e. with this series now XFS doesn't even need to know
about the details of the iomap_page structure and the uptodate
bits.  If for example I'd want to add sub-page dirty bits (which I
don't if I can avoid it) I can handle this entirely in iomap now
instead of spreading around iomap, xfs and duplicating the thing
in every copy of the XFS code that would otherwise show up.

> i.e. shared code is good if it's simple and doesn't have a lot of
> external dependencies that restrict the type and scope of
> modifications that can be made easily. Shared code that is complex
> and comes from code that was tightly integrated with a specific
> subsystem architecture is going to carry all those architectural
> foilbles into the new "generic" code. Once it gets sufficient
> users it's going to end up with the same "we can't change this code"
> problems that we had with the existing IO path, and we'll go back to
> implementing our own writeback path....

From the high level POV I agree with your stance.  But the point is
that the writeback code is not tightly integrated with xfs, and that
is why I don't want it in XFS.  It is on other other hand very
tightly integrated with the iomap buffer read and write into pagecache
code, which is why I want to keep it together with that.

> I've been planning on taking it even closer to the extent tree to
> give us lockless, modification range coherent extent map caching in
> this path (e.g. write() can add new delalloc extents without
> invalidating cached writeback maps).  This patchset re-introduces
> the iomap abstraction over the bmbt - an abstraction we removed some
> time ago - and that makes these sorts of improvements much harder
> and more complex to implement....

FYI, I had an earlier but not quite optimal implementation of lockless
extent lookups using rcu updates in the btree.  And at least for that
scheme all the details stay 100% in XFS in the split code, as the
abstraction between iomap and xfs is very clear and allows for that.

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

* Re: [PATCH 11/12] iomap: move the xfs writeback code to iomap.c
  2019-06-28  0:45       ` Dave Chinner
  2019-06-28  5:33         ` Christoph Hellwig
@ 2019-06-28 22:27         ` Luis Chamberlain
  2019-07-11 21:31           ` Brendan Higgins
  1 sibling, 1 reply; 52+ messages in thread
From: Luis Chamberlain @ 2019-06-28 22:27 UTC (permalink / raw)
  To: Dave Chinner, Brendan Higgins
  Cc: Christoph Hellwig, Darrick J . Wong, Damien Le Moal,
	Andreas Gruenbacher, linux-xfs, linux-fsdevel, linux-kernel

On Fri, Jun 28, 2019 at 10:45:42AM +1000, Dave Chinner wrote:
> On Tue, Jun 25, 2019 at 12:10:20PM +0200, Christoph Hellwig wrote:
> > On Tue, Jun 25, 2019 at 09:43:04AM +1000, Dave Chinner wrote:
> > > I'm a little concerned this is going to limit what we can do
> > > with the XFS IO path because now we can't change this code without
> > > considering the direct impact on other filesystems. The QA burden of
> > > changing the XFS writeback code goes through the roof with this
> > > change (i.e. we can break multiple filesystems, not just XFS).
> > 
> > Going through the roof is a little exaggerated.
> 
> You've already mentioned two new users you want to add. I don't even
> have zone capable hardware here to test one of the users you are
> indicating will use this code, and I suspect that very few people
> do.  That's a non-trivial increase in testing requirements for
> filesystem developers and distro QA departments who will want to
> change and/or validate this code path.

A side topic here:

Looking towards the future of prosects here with regards to helping QA
and developers with more confidence in API changes (kunit is one
prospect we're evaluating)...

If... we could somehow... codify what XFS *requires* from the API
precisely...  would that help alleviate concerns or bring confidence in
the prospect of sharing code?

Or is it simply an *impossibility* to address these concerns in question by
codifying tests for the promised API?

Ie, are the concerns something which could be addressed with strict
testing on adherence to an API, or are the concerns *unknown* side
dependencies which could not possibly be codified?

As an example of the extent possible to codify API promise (although
I beleive it was unintentional at first), see:

http://lkml.kernel.org/r/20190626021744.GU19023@42.do-not-panic.com

  Luis

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

* Re: [PATCH 11/12] iomap: move the xfs writeback code to iomap.c
  2019-06-28  5:33         ` Christoph Hellwig
@ 2019-07-01  0:08           ` Dave Chinner
  2019-07-01  6:43             ` Christoph Hellwig
  0 siblings, 1 reply; 52+ messages in thread
From: Dave Chinner @ 2019-07-01  0:08 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Darrick J . Wong, Damien Le Moal, Andreas Gruenbacher, linux-xfs,
	linux-fsdevel, linux-kernel

On Fri, Jun 28, 2019 at 07:33:20AM +0200, Christoph Hellwig wrote:
> On Fri, Jun 28, 2019 at 10:45:42AM +1000, Dave Chinner wrote:
> > You've already mentioned two new users you want to add. I don't even
> > have zone capable hardware here to test one of the users you are
> > indicating will use this code, and I suspect that very few people
> > do.  That's a non-trivial increase in testing requirements for
> > filesystem developers and distro QA departments who will want to
> > change and/or validate this code path.
> 
> Why do you assume you have to test it?  Back when we shared
> generic_file_read with everyone you also didn't test odd change to
> it with every possible fs.

I'm not sure what function you are referring to here. Can you
clarify?

> If you change iomap.c, you'll test it
> with XFS, and Cc other maintainers so that they get a chance to
> also test it and comment on it, just like we do with other shared
> code in the kernel.

Which is why we've had problems with the generic code paths in the
past and other filesystems just copy and paste then before making
signficant modifications. e.g. both ext4 and btrfs re-implement
write_cache_pages() rather than use the generic writeback code
because they have slightly different requirements and those
developers don't want to have to worry about other filesystems every
time there is an internal filesystem change that affects their
writeback constraints...

That's kinda what I'm getting at here: writeback isn't being shared
by any of the major filesystems for good reasons...

> > Indeed, integrating gfs2 into the existing generic iomap code has
> > required quite a bit of munging and adding new code paths and so on.
> > That's mostly been straight forward because it's just been adding
> > flags and conditional code to the existing paths. The way we
> > regularly rewrite sections of the XFS writeback code is a very
> > different sort of modification, and one that will be much harder to
> > do if we have to make those changes to generic code.
> 
> As the person who has done a lot of the recent rewriting of the
> writeback code I disagree.  Most of it has been do divorce is from
> leftovers of the buffer_head based sinle page at a time design from
> stone age.  Very little is about XFS itself, most of it has been
> about not being stupid in a fairly generic way.  And every since
> I got rid of buffer heads xfs_aops.c has been intimately tied
  ^^^^^^^^^^^^^^^^^^^^^^^^^

*cough*

Getting rid of bufferheads in writeback was largely a result of work
I did over a period of several years, thank you very much. Yes, work
you did over the same time period also got us there, but it's not
all your work.

> into the iomap infrastructure, and I'd rather keep those details in
> one place.  I.e. with this series now XFS doesn't even need to know
> about the details of the iomap_page structure and the uptodate
> bits.  If for example I'd want to add sub-page dirty bits (which I
> don't if I can avoid it) I can handle this entirely in iomap now
> instead of spreading around iomap, xfs and duplicating the thing
> in every copy of the XFS code that would otherwise show up.

Yes, I understand your motivations, I'm just not convinced that it
is the right thing to do given the history of this code and the
history of filesystem writeback code in general....

> > i.e. shared code is good if it's simple and doesn't have a lot of
> > external dependencies that restrict the type and scope of
> > modifications that can be made easily. Shared code that is complex
> > and comes from code that was tightly integrated with a specific
> > subsystem architecture is going to carry all those architectural
> > foilbles into the new "generic" code. Once it gets sufficient
> > users it's going to end up with the same "we can't change this code"
> > problems that we had with the existing IO path, and we'll go back to
> > implementing our own writeback path....
> 
> From the high level POV I agree with your stance.  But the point is
> that the writeback code is not tightly integrated with xfs, and that

Except it is....

> is why I don't want it in XFS.  It is on other other hand very
> tightly integrated with the iomap buffer read and write into pagecache
> code, which is why I want to keep it together with that.

It's not tightly integrated into the iomap read side or page cache
implementations.  Writeback currently gets a dirty page, we look up
a block map, we add it to/create a cached ioend/bio pair.  There are
four lines of code in the entire XFS writeback code path that
interact with iomap specific state, and that's the grand total of
interactions needed to support block size < page size writeback.

IOWs, we barely interact with the page cache or page/iomap state at
all in writeback anymore - we just write whole pages based on the
current inode extent map state. Yes, the writepage context, the
ioends and the extent map structures we use to implement this can be
made generic, but it's all the other details that are the problem
here.

e.g. If we have an error, we have to do very XFS specific things
(like punching out delalloc ranges) and so the generic iomap code
has a hook for doing this XFS specific thing when necessary.

e.g. XFS requires COW fork manipulation on ioend submission
(xfs_submit_ioend() calls xfs_reflink_convert_cow()) and this has
some nasty memory allocation requirements (potential deadlock
situation). So the generic code has a hook for this XFS specific
functionality, even though no other filesystem if likely to ever
need this. And this is something we've been discussion getting rid
of from the XFS writeback path. i.e. reworking how we do all
the COW fork interactions in writeback. So some of these hooks are
suspect even now, and we're already trying to work out how to
re-work the XFS writeback path to sort out problems we have with it.

That's the point I'm trying to make - the whole "generic" iomap
writeback API proposal is based around exactly the functionality XFS
- and only XFS - requires at this point in time. There are no other
users of this API and until there are, we've got no idea how
generic this functionality really is and just how much overhead
making fundamental changes to the XFS writeback code are going to
entail in future.

IOWs, before we go any further I'd really like to see how the other
proposed users of this functionality fit into the code and how
generic these XFS hooks are and what new hooks they require to
implement their specific functionality...

Cheers,

Dave.
-- 
Dave Chinner
david@fromorbit.com

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

* Re: [PATCH 11/12] iomap: move the xfs writeback code to iomap.c
  2019-07-01  0:08           ` Dave Chinner
@ 2019-07-01  6:43             ` Christoph Hellwig
  2019-07-01 23:09               ` Dave Chinner
  0 siblings, 1 reply; 52+ messages in thread
From: Christoph Hellwig @ 2019-07-01  6:43 UTC (permalink / raw)
  To: Dave Chinner
  Cc: Christoph Hellwig, Darrick J . Wong, Damien Le Moal,
	Andreas Gruenbacher, linux-xfs, linux-fsdevel, linux-kernel

On Mon, Jul 01, 2019 at 10:08:59AM +1000, Dave Chinner wrote:
> > Why do you assume you have to test it?  Back when we shared
> > generic_file_read with everyone you also didn't test odd change to
> > it with every possible fs.
> 
> I'm not sure what function you are referring to here. Can you
> clarify?

Right now it is generic_file_read_iter(), but before iter it was
generic_file_readv, generic_file_read, etc.

> > If you change iomap.c, you'll test it
> > with XFS, and Cc other maintainers so that they get a chance to
> > also test it and comment on it, just like we do with other shared
> > code in the kernel.
> 
> Which is why we've had problems with the generic code paths in the
> past and other filesystems just copy and paste then before making
> signficant modifications. e.g. both ext4 and btrfs re-implement
> write_cache_pages() rather than use the generic writeback code
> because they have slightly different requirements and those
> developers don't want to have to worry about other filesystems every
> time there is an internal filesystem change that affects their
> writeback constraints...
> 
> That's kinda what I'm getting at here: writeback isn't being shared
> by any of the major filesystems for good reasons...

I very fundamentally disagree.  It is not shared for a bad reasons,
and that is people not understanding the mess that the buffer head
based code is, and not wanting to understand it.  So they come up
with their own piecemeal "improvements" for it making the situation
worse.  Writeback is fundamentally not fs specific in any way.  Different
file system might use different optional features like unwrittent
extents, delalloc, data checksums, but once they implement them the
behavior should be uniform.

And I'd much rather fix this than going down the copy an paste and
slightly tweak it while fucking up something else route.

> > stone age.  Very little is about XFS itself, most of it has been
> > about not being stupid in a fairly generic way.  And every since
> > I got rid of buffer heads xfs_aops.c has been intimately tied
>   ^^^^^^^^^^^^^^^^^^^^^^^^^
> 
> *cough*
> 
> Getting rid of bufferheads in writeback was largely a result of work
> I did over a period of several years, thank you very much. Yes, work
> you did over the same time period also got us there, but it's not
> all your work.

Sorry Dave - this isn't avoud taking credit of past work.  But ever
since I finally got rid of bufferhads and introduced struct iomap_page
we have this intimate tie up, which is the point here.

> e.g. XFS requires COW fork manipulation on ioend submission
> (xfs_submit_ioend() calls xfs_reflink_convert_cow()) and this has
> some nasty memory allocation requirements (potential deadlock
> situation). So the generic code has a hook for this XFS specific
> functionality, even though no other filesystem if likely to ever
> need this. And this is something we've been discussion getting rid
> of from the XFS writeback path. i.e. reworking how we do all
> the COW fork interactions in writeback. So some of these hooks are
> suspect even now, and we're already trying to work out how to
> re-work the XFS writeback path to sort out problems we have with it.

Every file system that writes out of place will need some sort of
hook here with the same issue, no matter if they call it COW fork
or manipulate some all integrated data structure like btrfs.  Moreover
btrfs will also have to deal with their data checksum in exactly this
place.

> That's the point I'm trying to make - the whole "generic" iomap
> writeback API proposal is based around exactly the functionality XFS
> - and only XFS - requires at this point in time. There are no other
> users of this API and until there are, we've got no idea how
> generic this functionality really is and just how much overhead
> making fundamental changes to the XFS writeback code are going to
> entail in future.

No, it is based around generalizing what we have in xfs so that we
can use it elsewhere.  With zonefs and gfs2 as the prime users
initially, and other like btrfs hopefully to not far away.

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

* Re: [PATCH 11/12] iomap: move the xfs writeback code to iomap.c
  2019-07-01  6:43             ` Christoph Hellwig
@ 2019-07-01 23:09               ` Dave Chinner
  0 siblings, 0 replies; 52+ messages in thread
From: Dave Chinner @ 2019-07-01 23:09 UTC (permalink / raw)
  To: Christoph Hellwig
  Cc: Darrick J . Wong, Damien Le Moal, Andreas Gruenbacher, linux-xfs,
	linux-fsdevel, linux-kernel

On Mon, Jul 01, 2019 at 08:43:33AM +0200, Christoph Hellwig wrote:
> On Mon, Jul 01, 2019 at 10:08:59AM +1000, Dave Chinner wrote:
> > > Why do you assume you have to test it?  Back when we shared
> > > generic_file_read with everyone you also didn't test odd change to
> > > it with every possible fs.
> > 
> > I'm not sure what function you are referring to here. Can you
> > clarify?
> 
> Right now it is generic_file_read_iter(), but before iter it was
> generic_file_readv, generic_file_read, etc.

This generic code never came from XFS, so I'm still not sure what
you are refering to here? Some pointers to commits would help me
remember. :/

> > > If you change iomap.c, you'll test it
> > > with XFS, and Cc other maintainers so that they get a chance to
> > > also test it and comment on it, just like we do with other shared
> > > code in the kernel.
> > 
> > Which is why we've had problems with the generic code paths in the
> > past and other filesystems just copy and paste then before making
> > signficant modifications. e.g. both ext4 and btrfs re-implement
> > write_cache_pages() rather than use the generic writeback code
> > because they have slightly different requirements and those
> > developers don't want to have to worry about other filesystems every
> > time there is an internal filesystem change that affects their
> > writeback constraints...
> > 
> > That's kinda what I'm getting at here: writeback isn't being shared
> > by any of the major filesystems for good reasons...
> 
> I very fundamentally disagree.  It is not shared for a bad reasons,
> and that is people not understanding the mess that the buffer head
> based code is, and not wanting to understand it. 

The problem with heavily shared code is that it requires far more
expertise, knowledge, capability and time to modify it. The code
essentially ossifies, because changing something fundamental risks
breaking other stuff that nobody actually understands anymore and is
unwilling to risk changing.

That's not a problem with bufferheads - that's a problem of widely
shared code that has been slowly hacked to pieces to "fix' random
problems that show up from different users of the shared code.

When the shared code ossifies like this, the only way to make
progress is to either copy it and do whatever you need privately,
or re-implement it completely. ext4 and btrfs have taken the route
of "copy and modify privately", whereas XFS has taken the
"re-implement it completely" path.

We're now starting down the "share the XFS re-implementation" and
we're slowly adding more complexity to the iomap code to handle the
different things each filesystem that is converted needs. With each
new fs adding their own little quirks, it gets harder to make
significant modifications without unknowingly breaking something in
some other filesystem.

It takes highly capable developers to make serious modifications
across highly shared code and the reality is that there are very few
of them around. most developers simply aren't capable of taking on
such a task, especially given that they see capable, experienced
developers who won't even try because of past experiences akin to
a game of Running Man(*)....

Shared code is good, up to the point where the sharing gets so
complex that even people with the capability are not willing to
touch/fix the code. That's what happened to bufferheads and it's a
pattern repeated across lots of kernel infrastructure code. Just
because you can handle these modifications doesn't mean everyone
else can or even wants to.

> And I'd much rather fix this than going down the copy an paste and
> slightly tweak it while fucking up something else route.

The copy-n-paste is a result of developers who have little knowledge
of things outside their domain of interest/expertise making the sane
decision to minimise risk of breaking something they know nothing
about. From an individual subsystem perspective, that's a -good
decision- to make, and that's the point I was trying to make.

You see that as a bad decision, because you equating "shared code"
with "high quality" code. The reality is that shared code is often
poor quality because people get too scared to touch it. That's
exactly the situation I don't want us to get stuck with, and why I
want to see how multiple implementations of this abstracted writeback
path change what we have now before we start moving code about...

i.e. I'm not saying "we shouldn't do this", I'm just saying that "we
should do this because shared code is good" fundamentally conflicts
with the fact we've just re-implemented a bunch of stuff because
the *shared code was really bad*. And taking the same path that lead
to really bad shared code (i.e. organic growth without planning or
design) is likely to end up in the same place....

Cheers,

Dave.

(*) https://www.imdb.com/title/tt0093894/

"A wrongly convicted man must try to survive a public execution
gauntlet staged as a game show."

-- 
Dave Chinner
david@fromorbit.com

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

* Re: [PATCH 11/12] iomap: move the xfs writeback code to iomap.c
  2019-06-28 22:27         ` Luis Chamberlain
@ 2019-07-11 21:31           ` Brendan Higgins
  0 siblings, 0 replies; 52+ messages in thread
From: Brendan Higgins @ 2019-07-11 21:31 UTC (permalink / raw)
  To: Luis Chamberlain
  Cc: Dave Chinner, Christoph Hellwig, Darrick J . Wong,
	Damien Le Moal, Andreas Gruenbacher, linux-xfs, linux-fsdevel,
	Linux Kernel Mailing List

On Fri, Jun 28, 2019 at 3:28 PM Luis Chamberlain <mcgrof@kernel.org> wrote:
>
> On Fri, Jun 28, 2019 at 10:45:42AM +1000, Dave Chinner wrote:
> > On Tue, Jun 25, 2019 at 12:10:20PM +0200, Christoph Hellwig wrote:
> > > On Tue, Jun 25, 2019 at 09:43:04AM +1000, Dave Chinner wrote:
> > > > I'm a little concerned this is going to limit what we can do
> > > > with the XFS IO path because now we can't change this code without
> > > > considering the direct impact on other filesystems. The QA burden of
> > > > changing the XFS writeback code goes through the roof with this
> > > > change (i.e. we can break multiple filesystems, not just XFS).
> > >
> > > Going through the roof is a little exaggerated.
> >
> > You've already mentioned two new users you want to add. I don't even
> > have zone capable hardware here to test one of the users you are
> > indicating will use this code, and I suspect that very few people
> > do.  That's a non-trivial increase in testing requirements for
> > filesystem developers and distro QA departments who will want to
> > change and/or validate this code path.
>
> A side topic here:
>
> Looking towards the future of prosects here with regards to helping QA
> and developers with more confidence in API changes (kunit is one
> prospect we're evaluating)...
>
> If... we could somehow... codify what XFS *requires* from the API
> precisely...  would that help alleviate concerns or bring confidence in
> the prospect of sharing code?
>
> Or is it simply an *impossibility* to address these concerns in question by
> codifying tests for the promised API?
>
> Ie, are the concerns something which could be addressed with strict
> testing on adherence to an API, or are the concerns *unknown* side
> dependencies which could not possibly be codified?

Thanks for pointing this out, Luis. This is a really important
distinction. In the former case, I think as has become apparent in
your example below; KUnit has a strong potential to be able to
formally specify API behavior and guarantee compliance.

However, as you point out there are many *unknown* dependencies which
always have a way of sneaking into API informal specifications. I have
some colleagues working on this problem for unknown server API
dependencies; nevertheless, to my knowledge this is an unsolved
problem.

One partial solution I have seen is to put a system in place to record
live traffic so that it can be later replayed in a test environment.

Another partial solution is a modified form of fuzz testing similar to
what Haskell's QuickCheck[1] does, which basically attempts to allow
users to specify the kinds of data they expect to handle in such a way
that QuickCheck is able to generate random data, pass it into the API,
and verify the results satisfy the contract. I actually wrote a
prototype of this for KUnit, but haven't publicly shared it yet since
I thought it was kind of an out there idea (plus KUnit was pretty far
away from being merged at the time).

Still, a QuickCheck style test will always have the problem that the
contract will likely underspecify things, and if not, the test may
still never run long enough to cover all interesting cases. I have
heard of attempts to solve this problem by combining the two prior
approaches in novel ways (like using a QuckCheck style specification
to mutate real recorded data).

Anyway, sorry for the tangent, but I would be really interested to
know whether you think the problem is more of the just testing the
formally specified contract or the problem lies in unknown
dependencies that Luis mentioned, and in either case whether you would
find any of these ideas useful.

> As an example of the extent possible to codify API promise (although
> I beleive it was unintentional at first), see:
>
> http://lkml.kernel.org/r/20190626021744.GU19023@42.do-not-panic.com

[1] http://www.cse.chalmers.se/~rjmh/QuickCheck/manual.html

Cheers!

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

end of thread, other threads:[~2019-07-11 21:31 UTC | newest]

Thread overview: 52+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-06-24  5:52 lift the xfs writepage code into iomap Christoph Hellwig
2019-06-24  5:52 ` [PATCH 01/12] list.h: add a list_pop helper Christoph Hellwig
2019-06-24 14:53   ` Darrick J. Wong
2019-06-24 15:51   ` Matthew Wilcox
2019-06-25 10:06     ` Christoph Hellwig
2019-06-24  5:52 ` [PATCH 02/12] xfs: simplify xfs_chain_bio Christoph Hellwig
2019-06-24 15:14   ` Darrick J. Wong
2019-06-24  5:52 ` [PATCH 03/12] xfs: fix a comment typo in xfs_submit_ioend Christoph Hellwig
2019-06-24 14:53   ` Darrick J. Wong
2019-06-24  5:52 ` [PATCH 04/12] xfs: initialize ioma->flags in xfs_bmbt_to_iomap Christoph Hellwig
2019-06-24 14:57   ` Darrick J. Wong
2019-06-25 10:07     ` Christoph Hellwig
2019-06-25 15:13       ` Darrick J. Wong
2019-06-25 15:21         ` Christoph Hellwig
2019-06-24  5:52 ` [PATCH 05/12] xfs: use a struct iomap in xfs_writepage_ctx Christoph Hellwig
2019-06-24 15:50   ` Darrick J. Wong
2019-06-24  5:52 ` [PATCH 06/12] xfs: remove XFS_TRANS_NOFS Christoph Hellwig
2019-06-24 15:58   ` Darrick J. Wong
2019-06-24 22:59   ` Dave Chinner
2019-06-25 10:12     ` Christoph Hellwig
2019-06-24  5:52 ` [PATCH 07/12] xfs: don't preallocate a transaction for file size updates Christoph Hellwig
2019-06-24 16:17   ` Darrick J. Wong
2019-06-24 23:15     ` Dave Chinner
2019-06-25 10:25       ` Christoph Hellwig
2019-06-27 22:23         ` Dave Chinner
2019-06-24  5:52 ` [PATCH 08/12] xfs: simplify xfs_ioend_can_merge Christoph Hellwig
2019-06-24 16:00   ` Darrick J. Wong
2019-06-24  5:52 ` [PATCH 09/12] xfs: refactor the ioend merging code Christoph Hellwig
2019-06-24 16:04   ` Darrick J. Wong
2019-06-24 16:06   ` Nikolay Borisov
2019-06-25 10:14     ` Christoph Hellwig
2019-06-25 12:42       ` Nikolay Borisov
2019-06-25 14:45         ` Darrick J. Wong
2019-06-24  5:52 ` [PATCH 10/12] xfs: remove the fork fields in the writepage_ctx and ioend Christoph Hellwig
2019-06-24 16:08   ` Darrick J. Wong
2019-06-24  5:52 ` [PATCH 11/12] iomap: move the xfs writeback code to iomap.c Christoph Hellwig
2019-06-24 15:46   ` Darrick J. Wong
2019-06-25 10:08     ` Christoph Hellwig
2019-06-24 23:43   ` Dave Chinner
2019-06-25 10:10     ` Christoph Hellwig
2019-06-28  0:45       ` Dave Chinner
2019-06-28  5:33         ` Christoph Hellwig
2019-07-01  0:08           ` Dave Chinner
2019-07-01  6:43             ` Christoph Hellwig
2019-07-01 23:09               ` Dave Chinner
2019-06-28 22:27         ` Luis Chamberlain
2019-07-11 21:31           ` Brendan Higgins
2019-06-24  5:52 ` [PATCH 12/12] iomap: add tracing for the address space operations Christoph Hellwig
2019-06-24 23:49   ` Dave Chinner
2019-06-25 10:15     ` Christoph Hellwig
2019-06-25 14:47       ` Darrick J. Wong
2019-06-27 22:35       ` Dave Chinner

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).