* only do a single COW fork lookup in writeback
@ 2016-09-24 15:19 Christoph Hellwig
2016-09-24 15:19 ` [PATCH 1/2] xfs: kill xfs_reflink_is_cow_pending Christoph Hellwig
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Christoph Hellwig @ 2016-09-24 15:19 UTC (permalink / raw)
To: darrick.wong; +Cc: linux-xfs
I've got a bug report with a slightly older version of the reflink
code, in which I get a bogus NULL xfs_bmbt_rec_host pointer back from
xfs_iext_bno_to_ext in xfs_reflink_find_cow_mapping. I've not
reproduced that bug myself yet, but what's clear from the report is
that it's not just inefficient but also potentially dangerous to
do the blind dereference in xfs_reflink_find_cow_mapping after
we dropped the ilock from the previous xfs_reflink_find_cow_mapping
call.
So just combine that two into one function, and then rewrite the
COW writeback code to only do a single call in the second step.
I think that also cleans up the writeback code quite a bit and
makes it much easier to follow as well.
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 1/2] xfs: kill xfs_reflink_is_cow_pending
2016-09-24 15:19 only do a single COW fork lookup in writeback Christoph Hellwig
@ 2016-09-24 15:19 ` Christoph Hellwig
2016-09-26 21:08 ` Darrick J. Wong
2016-09-24 15:19 ` [PATCH 2/2] xfs: rewrite the COW writeback mapping code Christoph Hellwig
2016-09-25 3:47 ` only do a single COW fork lookup in writeback Christoph Hellwig
2 siblings, 1 reply; 7+ messages in thread
From: Christoph Hellwig @ 2016-09-24 15:19 UTC (permalink / raw)
To: darrick.wong; +Cc: linux-xfs
Instead enhance xfs_reflink_find_cow_mapping to properly deal with the
fact that we might not have a COW mapping at the offset, and just
return false in this case.
This avoids code duplication, makes xfs_reflink_find_cow_mapping more
robust, and last but not least halves the number of extent map lookups
needed in COW writeback operations.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
fs/xfs/xfs_aops.c | 28 ++++++++++++++++------------
fs/xfs/xfs_reflink.c | 42 +++++++++++-------------------------------
fs/xfs/xfs_reflink.h | 3 +--
3 files changed, 28 insertions(+), 45 deletions(-)
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index d8ce18b..1933803 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -379,10 +379,13 @@ xfs_map_blocks(
end_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)offset + count);
offset_fsb = XFS_B_TO_FSBT(mp, offset);
- if (type == XFS_IO_COW)
- error = xfs_reflink_find_cow_mapping(ip, offset, imap,
- &need_alloc);
- else {
+ if (type == XFS_IO_COW) {
+ if (!xfs_reflink_find_cow_mapping(ip, offset, imap,
+ &need_alloc)) {
+ WARN_ON_ONCE(1);
+ return -EIO;
+ }
+ } else {
error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb,
imap, &nimaps, bmapi_flags);
/*
@@ -712,13 +715,14 @@ xfs_is_cow_io(
struct xfs_inode *ip,
xfs_off_t offset)
{
- bool is_cow;
+ struct xfs_bmbt_irec imap;
+ bool is_cow, need_alloc;
if (!xfs_sb_version_hasreflink(&ip->i_mount->m_sb))
return false;
xfs_ilock(ip, XFS_ILOCK_SHARED);
- is_cow = xfs_reflink_is_cow_pending(ip, offset);
+ is_cow = xfs_reflink_find_cow_mapping(ip, offset, &imap, &need_alloc);
xfs_iunlock(ip, XFS_ILOCK_SHARED);
return is_cow;
@@ -1316,12 +1320,12 @@ __xfs_get_blocks(
end_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)offset + size);
offset_fsb = XFS_B_TO_FSBT(mp, offset);
- if (create && direct)
- is_cow = xfs_reflink_is_cow_pending(ip, offset);
- if (is_cow)
- error = xfs_reflink_find_cow_mapping(ip, offset, &imap,
- &need_alloc);
- else {
+ if (create && direct) {
+ is_cow = xfs_reflink_find_cow_mapping(ip, offset, &imap,
+ &need_alloc);
+ }
+
+ if (!is_cow) {
error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb,
&imap, &nimaps, XFS_BMAPI_ENTIRE);
/*
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
index 6f87b1e..a1ba7f5 100644
--- a/fs/xfs/xfs_reflink.c
+++ b/fs/xfs/xfs_reflink.c
@@ -429,26 +429,31 @@ advloop:
}
/*
- * Determine if there's a CoW reservation at a byte offset of an inode.
+ * Find the CoW reservation (and whether or not it needs block allocation)
+ * for a given byte offset of a file.
*/
bool
-xfs_reflink_is_cow_pending(
+xfs_reflink_find_cow_mapping(
struct xfs_inode *ip,
- xfs_off_t offset)
+ xfs_off_t offset,
+ struct xfs_bmbt_irec *imap,
+ bool *need_alloc)
{
+ struct xfs_bmbt_irec irec;
struct xfs_ifork *ifp;
struct xfs_bmbt_rec_host *gotp;
- struct xfs_bmbt_irec irec;
xfs_fileoff_t bno;
xfs_extnum_t idx;
+ ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL | XFS_ILOCK_SHARED));
+
if (!xfs_is_reflink_inode(ip))
return false;
+ /* Find the extent in the CoW fork. */
ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
bno = XFS_B_TO_FSBT(ip->i_mount, offset);
gotp = xfs_iext_bno_to_ext(ifp, bno, &idx);
-
if (!gotp)
return false;
@@ -456,31 +461,6 @@ xfs_reflink_is_cow_pending(
if (bno >= irec.br_startoff + irec.br_blockcount ||
bno < irec.br_startoff)
return false;
- return true;
-}
-
-/*
- * Find the CoW reservation (and whether or not it needs block allocation)
- * for a given byte offset of a file.
- */
-int
-xfs_reflink_find_cow_mapping(
- struct xfs_inode *ip,
- xfs_off_t offset,
- struct xfs_bmbt_irec *imap,
- bool *need_alloc)
-{
- struct xfs_bmbt_irec irec;
- struct xfs_ifork *ifp;
- struct xfs_bmbt_rec_host *gotp;
- xfs_fileoff_t bno;
- xfs_extnum_t idx;
-
- /* Find the extent in the CoW fork. */
- ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
- bno = XFS_B_TO_FSBT(ip->i_mount, offset);
- gotp = xfs_iext_bno_to_ext(ifp, bno, &idx);
- xfs_bmbt_get_all(gotp, &irec);
trace_xfs_reflink_find_cow_mapping(ip, offset, 1, XFS_IO_OVERWRITE,
&irec);
@@ -489,7 +469,7 @@ xfs_reflink_find_cow_mapping(
*imap = irec;
*need_alloc = !!(isnullstartblock(irec.br_startblock));
- return 0;
+ return true;
}
/*
diff --git a/fs/xfs/xfs_reflink.h b/fs/xfs/xfs_reflink.h
index 398e726..6519f19 100644
--- a/fs/xfs/xfs_reflink.h
+++ b/fs/xfs/xfs_reflink.h
@@ -30,8 +30,7 @@ extern int xfs_reflink_reserve_cow_range(struct xfs_inode *ip,
xfs_fileoff_t offset_fsb, xfs_fileoff_t end_fsb);
extern int xfs_reflink_allocate_cow_range(struct xfs_inode *ip, xfs_off_t pos,
xfs_off_t len);
-extern bool xfs_reflink_is_cow_pending(struct xfs_inode *ip, xfs_off_t offset);
-extern int xfs_reflink_find_cow_mapping(struct xfs_inode *ip, xfs_off_t offset,
+extern bool xfs_reflink_find_cow_mapping(struct xfs_inode *ip, xfs_off_t offset,
struct xfs_bmbt_irec *imap, bool *need_alloc);
extern int xfs_reflink_trim_irec_to_next_cow(struct xfs_inode *ip,
xfs_fileoff_t offset_fsb, struct xfs_bmbt_irec *imap);
--
2.1.4
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 2/2] xfs: rewrite the COW writeback mapping code
2016-09-24 15:19 only do a single COW fork lookup in writeback Christoph Hellwig
2016-09-24 15:19 ` [PATCH 1/2] xfs: kill xfs_reflink_is_cow_pending Christoph Hellwig
@ 2016-09-24 15:19 ` Christoph Hellwig
2016-09-26 21:35 ` Darrick J. Wong
2016-09-25 3:47 ` only do a single COW fork lookup in writeback Christoph Hellwig
2 siblings, 1 reply; 7+ messages in thread
From: Christoph Hellwig @ 2016-09-24 15:19 UTC (permalink / raw)
To: darrick.wong; +Cc: linux-xfs
Add a new xfs_map_cow helper that does a single consistent lookup in the
COW fork extent map, and remove the existing COW handling code from
xfs_map_blocks.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
fs/xfs/xfs_aops.c | 119 +++++++++++++++++++++++++++++++-----------------------
1 file changed, 69 insertions(+), 50 deletions(-)
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index 1933803..2a3f4c1 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -357,15 +357,11 @@ xfs_map_blocks(
int error = 0;
int bmapi_flags = XFS_BMAPI_ENTIRE;
int nimaps = 1;
- int whichfork;
- bool need_alloc;
if (XFS_FORCED_SHUTDOWN(mp))
return -EIO;
- whichfork = (type == XFS_IO_COW ? XFS_COW_FORK : XFS_DATA_FORK);
- need_alloc = (type == XFS_IO_DELALLOC);
-
+ ASSERT(type != XFS_IO_COW);
if (type == XFS_IO_UNWRITTEN)
bmapi_flags |= XFS_BMAPI_IGSTATE;
@@ -378,36 +374,26 @@ xfs_map_blocks(
count = mp->m_super->s_maxbytes - offset;
end_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)offset + count);
offset_fsb = XFS_B_TO_FSBT(mp, offset);
-
- if (type == XFS_IO_COW) {
- if (!xfs_reflink_find_cow_mapping(ip, offset, imap,
- &need_alloc)) {
- WARN_ON_ONCE(1);
- return -EIO;
- }
- } else {
- error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb,
- imap, &nimaps, bmapi_flags);
- /*
- * Truncate an overwrite extent if there's a pending CoW
- * reservation before the end of this extent. This forces us
- * to come back to writepage to take care of the CoW.
- */
- if (nimaps && type == XFS_IO_OVERWRITE)
- xfs_reflink_trim_irec_to_next_cow(ip, offset_fsb, imap);
- }
+ error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb,
+ imap, &nimaps, bmapi_flags);
+ /*
+ * Truncate an overwrite extent if there's a pending CoW
+ * reservation before the end of this extent. This forces us
+ * to come back to writepage to take care of the CoW.
+ */
+ if (nimaps && type == XFS_IO_OVERWRITE)
+ xfs_reflink_trim_irec_to_next_cow(ip, offset_fsb, imap);
xfs_iunlock(ip, XFS_ILOCK_SHARED);
if (error)
return error;
- if (need_alloc &&
+ if (type == XFS_IO_DELALLOC &&
(!nimaps || isnullstartblock(imap->br_startblock))) {
- error = xfs_iomap_write_allocate(ip, whichfork, offset,
+ error = xfs_iomap_write_allocate(ip, XFS_DATA_FORK, offset,
imap);
if (!error)
- trace_xfs_map_blocks_alloc(ip, offset, count, type,
- imap);
+ trace_xfs_map_blocks_alloc(ip, offset, count, type, imap);
return error;
}
@@ -707,27 +693,6 @@ xfs_check_page_type(
return false;
}
-/*
- * Figure out if CoW is pending at this offset.
- */
-static bool
-xfs_is_cow_io(
- struct xfs_inode *ip,
- xfs_off_t offset)
-{
- struct xfs_bmbt_irec imap;
- bool is_cow, need_alloc;
-
- if (!xfs_sb_version_hasreflink(&ip->i_mount->m_sb))
- return false;
-
- xfs_ilock(ip, XFS_ILOCK_SHARED);
- is_cow = xfs_reflink_find_cow_mapping(ip, offset, &imap, &need_alloc);
- xfs_iunlock(ip, XFS_ILOCK_SHARED);
-
- return is_cow;
-}
-
STATIC void
xfs_vm_invalidatepage(
struct page *page,
@@ -804,6 +769,56 @@ out_invalidate:
return;
}
+static int
+xfs_map_cow(
+ struct xfs_writepage_ctx *wpc,
+ struct inode *inode,
+ loff_t offset,
+ unsigned int *new_type)
+{
+ struct xfs_inode *ip = XFS_I(inode);
+ struct xfs_bmbt_irec imap;
+ bool is_cow = false, need_alloc = false;
+ int error;
+
+ /*
+ * If we already have a valid COW mapping keep using it.
+ */
+ if (wpc->io_type == XFS_IO_COW) {
+ wpc->imap_valid = xfs_imap_valid(inode, &wpc->imap, offset);
+ if (wpc->imap_valid) {
+ *new_type = XFS_IO_COW;
+ return 0;
+ }
+ }
+
+ /*
+ * Else we need to check if there is a COW mapping at this offset.
+ */
+ xfs_ilock(ip, XFS_ILOCK_SHARED);
+ is_cow = xfs_reflink_find_cow_mapping(ip, offset, &imap, &need_alloc);
+ xfs_iunlock(ip, XFS_ILOCK_SHARED);
+
+ if (!is_cow)
+ return 0;
+
+ /*
+ * And if the COW mapping has a delayed extent here we need to
+ * allocate real space for it now.
+ */
+ if (need_alloc) {
+ error = xfs_iomap_write_allocate(ip, XFS_COW_FORK, offset,
+ &imap);
+ if (error)
+ return error;
+ }
+
+ wpc->io_type = *new_type = XFS_IO_COW;
+ wpc->imap_valid = true;
+ wpc->imap = imap;
+ return 0;
+}
+
/*
* We implement an immediate ioend submission policy here to avoid needing to
* chain multiple ioends and hence nest mempool allocations which can violate
@@ -876,8 +891,12 @@ xfs_writepage_map(
continue;
}
- if (xfs_is_cow_io(XFS_I(inode), offset))
- new_type = XFS_IO_COW;
+ if (xfs_sb_version_hasreflink(&XFS_I(inode)->i_mount->m_sb)) {
+ error = xfs_map_cow(wpc, inode, offset, &new_type);
+ if (error)
+ goto out;
+ }
+
if (wpc->io_type != new_type) {
wpc->io_type = new_type;
wpc->imap_valid = false;
--
2.1.4
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: only do a single COW fork lookup in writeback
2016-09-24 15:19 only do a single COW fork lookup in writeback Christoph Hellwig
2016-09-24 15:19 ` [PATCH 1/2] xfs: kill xfs_reflink_is_cow_pending Christoph Hellwig
2016-09-24 15:19 ` [PATCH 2/2] xfs: rewrite the COW writeback mapping code Christoph Hellwig
@ 2016-09-25 3:47 ` Christoph Hellwig
2 siblings, 0 replies; 7+ messages in thread
From: Christoph Hellwig @ 2016-09-25 3:47 UTC (permalink / raw)
To: darrick.wong; +Cc: linux-xfs
On Sat, Sep 24, 2016 at 08:19:19AM -0700, Christoph Hellwig wrote:
> I've got a bug report with a slightly older version of the reflink
> code, in which I get a bogus NULL xfs_bmbt_rec_host pointer back from
> xfs_iext_bno_to_ext in xfs_reflink_find_cow_mapping. I've not
> reproduced that bug myself yet, but what's clear from the report is
> that it's not just inefficient but also potentially dangerous to
> do the blind dereference in xfs_reflink_find_cow_mapping after
> we dropped the ilock from the previous xfs_reflink_find_cow_mapping
> call.
FYI, based on further analsys I suspect that a xfs_reflink_end_cow
called from xfs_end_io cause the extent index to be invalid during
the xfs_reflink_find_cow_mapping mapping, as that can easily shift
the extent indices around and race with writeback elsewhere on the
same file.
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 1/2] xfs: kill xfs_reflink_is_cow_pending
2016-09-24 15:19 ` [PATCH 1/2] xfs: kill xfs_reflink_is_cow_pending Christoph Hellwig
@ 2016-09-26 21:08 ` Darrick J. Wong
0 siblings, 0 replies; 7+ messages in thread
From: Darrick J. Wong @ 2016-09-26 21:08 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: linux-xfs
On Sat, Sep 24, 2016 at 08:19:20AM -0700, Christoph Hellwig wrote:
> Instead enhance xfs_reflink_find_cow_mapping to properly deal with the
> fact that we might not have a COW mapping at the offset, and just
> return false in this case.
>
> This avoids code duplication, makes xfs_reflink_find_cow_mapping more
> robust, and last but not least halves the number of extent map lookups
> needed in COW writeback operations.
Hooray!
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
> fs/xfs/xfs_aops.c | 28 ++++++++++++++++------------
> fs/xfs/xfs_reflink.c | 42 +++++++++++-------------------------------
> fs/xfs/xfs_reflink.h | 3 +--
> 3 files changed, 28 insertions(+), 45 deletions(-)
>
> diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
> index d8ce18b..1933803 100644
> --- a/fs/xfs/xfs_aops.c
> +++ b/fs/xfs/xfs_aops.c
> @@ -379,10 +379,13 @@ xfs_map_blocks(
> end_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)offset + count);
> offset_fsb = XFS_B_TO_FSBT(mp, offset);
>
> - if (type == XFS_IO_COW)
> - error = xfs_reflink_find_cow_mapping(ip, offset, imap,
> - &need_alloc);
> - else {
> + if (type == XFS_IO_COW) {
> + if (!xfs_reflink_find_cow_mapping(ip, offset, imap,
> + &need_alloc)) {
> + WARN_ON_ONCE(1);
> + return -EIO;
> + }
I squinted at this for a second but then realized it goes away in the
next patch anyway.
> + } else {
> error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb,
> imap, &nimaps, bmapi_flags);
> /*
> @@ -712,13 +715,14 @@ xfs_is_cow_io(
> struct xfs_inode *ip,
> xfs_off_t offset)
> {
> - bool is_cow;
> + struct xfs_bmbt_irec imap;
> + bool is_cow, need_alloc;
>
> if (!xfs_sb_version_hasreflink(&ip->i_mount->m_sb))
> return false;
>
> xfs_ilock(ip, XFS_ILOCK_SHARED);
> - is_cow = xfs_reflink_is_cow_pending(ip, offset);
> + is_cow = xfs_reflink_find_cow_mapping(ip, offset, &imap, &need_alloc);
> xfs_iunlock(ip, XFS_ILOCK_SHARED);
>
> return is_cow;
> @@ -1316,12 +1320,12 @@ __xfs_get_blocks(
> end_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)offset + size);
> offset_fsb = XFS_B_TO_FSBT(mp, offset);
>
> - if (create && direct)
> - is_cow = xfs_reflink_is_cow_pending(ip, offset);
> - if (is_cow)
> - error = xfs_reflink_find_cow_mapping(ip, offset, &imap,
> - &need_alloc);
> - else {
> + if (create && direct) {
> + is_cow = xfs_reflink_find_cow_mapping(ip, offset, &imap,
> + &need_alloc);
> + }
> +
> + if (!is_cow) {
> error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb,
> &imap, &nimaps, XFS_BMAPI_ENTIRE);
> /*
> diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c
> index 6f87b1e..a1ba7f5 100644
> --- a/fs/xfs/xfs_reflink.c
> +++ b/fs/xfs/xfs_reflink.c
> @@ -429,26 +429,31 @@ advloop:
> }
>
> /*
> - * Determine if there's a CoW reservation at a byte offset of an inode.
> + * Find the CoW reservation (and whether or not it needs block allocation)
> + * for a given byte offset of a file.
> */
> bool
> -xfs_reflink_is_cow_pending(
> +xfs_reflink_find_cow_mapping(
> struct xfs_inode *ip,
> - xfs_off_t offset)
> + xfs_off_t offset,
> + struct xfs_bmbt_irec *imap,
> + bool *need_alloc)
> {
> + struct xfs_bmbt_irec irec;
> struct xfs_ifork *ifp;
> struct xfs_bmbt_rec_host *gotp;
> - struct xfs_bmbt_irec irec;
> xfs_fileoff_t bno;
> xfs_extnum_t idx;
>
> + ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL | XFS_ILOCK_SHARED));
> +
> if (!xfs_is_reflink_inode(ip))
> return false;
>
> + /* Find the extent in the CoW fork. */
> ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
> bno = XFS_B_TO_FSBT(ip->i_mount, offset);
> gotp = xfs_iext_bno_to_ext(ifp, bno, &idx);
> -
> if (!gotp)
> return false;
>
> @@ -456,31 +461,6 @@ xfs_reflink_is_cow_pending(
> if (bno >= irec.br_startoff + irec.br_blockcount ||
> bno < irec.br_startoff)
> return false;
> - return true;
> -}
> -
> -/*
> - * Find the CoW reservation (and whether or not it needs block allocation)
> - * for a given byte offset of a file.
> - */
> -int
> -xfs_reflink_find_cow_mapping(
> - struct xfs_inode *ip,
> - xfs_off_t offset,
> - struct xfs_bmbt_irec *imap,
> - bool *need_alloc)
> -{
> - struct xfs_bmbt_irec irec;
> - struct xfs_ifork *ifp;
> - struct xfs_bmbt_rec_host *gotp;
> - xfs_fileoff_t bno;
> - xfs_extnum_t idx;
> -
> - /* Find the extent in the CoW fork. */
> - ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
> - bno = XFS_B_TO_FSBT(ip->i_mount, offset);
> - gotp = xfs_iext_bno_to_ext(ifp, bno, &idx);
> - xfs_bmbt_get_all(gotp, &irec);
>
> trace_xfs_reflink_find_cow_mapping(ip, offset, 1, XFS_IO_OVERWRITE,
> &irec);
> @@ -489,7 +469,7 @@ xfs_reflink_find_cow_mapping(
> *imap = irec;
> *need_alloc = !!(isnullstartblock(irec.br_startblock));
>
> - return 0;
> + return true;
Otherwise looks resonable enough, so
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
--D
> }
>
> /*
> diff --git a/fs/xfs/xfs_reflink.h b/fs/xfs/xfs_reflink.h
> index 398e726..6519f19 100644
> --- a/fs/xfs/xfs_reflink.h
> +++ b/fs/xfs/xfs_reflink.h
> @@ -30,8 +30,7 @@ extern int xfs_reflink_reserve_cow_range(struct xfs_inode *ip,
> xfs_fileoff_t offset_fsb, xfs_fileoff_t end_fsb);
> extern int xfs_reflink_allocate_cow_range(struct xfs_inode *ip, xfs_off_t pos,
> xfs_off_t len);
> -extern bool xfs_reflink_is_cow_pending(struct xfs_inode *ip, xfs_off_t offset);
> -extern int xfs_reflink_find_cow_mapping(struct xfs_inode *ip, xfs_off_t offset,
> +extern bool xfs_reflink_find_cow_mapping(struct xfs_inode *ip, xfs_off_t offset,
> struct xfs_bmbt_irec *imap, bool *need_alloc);
> extern int xfs_reflink_trim_irec_to_next_cow(struct xfs_inode *ip,
> xfs_fileoff_t offset_fsb, struct xfs_bmbt_irec *imap);
> --
> 2.1.4
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 2/2] xfs: rewrite the COW writeback mapping code
2016-09-24 15:19 ` [PATCH 2/2] xfs: rewrite the COW writeback mapping code Christoph Hellwig
@ 2016-09-26 21:35 ` Darrick J. Wong
2016-09-27 18:48 ` Christoph Hellwig
0 siblings, 1 reply; 7+ messages in thread
From: Darrick J. Wong @ 2016-09-26 21:35 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: linux-xfs
On Sat, Sep 24, 2016 at 08:19:21AM -0700, Christoph Hellwig wrote:
> Add a new xfs_map_cow helper that does a single consistent lookup in the
> COW fork extent map, and remove the existing COW handling code from
> xfs_map_blocks.
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
> fs/xfs/xfs_aops.c | 119 +++++++++++++++++++++++++++++++-----------------------
> 1 file changed, 69 insertions(+), 50 deletions(-)
>
> diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
> index 1933803..2a3f4c1 100644
> --- a/fs/xfs/xfs_aops.c
> +++ b/fs/xfs/xfs_aops.c
> @@ -357,15 +357,11 @@ xfs_map_blocks(
> int error = 0;
> int bmapi_flags = XFS_BMAPI_ENTIRE;
> int nimaps = 1;
> - int whichfork;
> - bool need_alloc;
>
> if (XFS_FORCED_SHUTDOWN(mp))
> return -EIO;
>
> - whichfork = (type == XFS_IO_COW ? XFS_COW_FORK : XFS_DATA_FORK);
> - need_alloc = (type == XFS_IO_DELALLOC);
> -
> + ASSERT(type != XFS_IO_COW);
> if (type == XFS_IO_UNWRITTEN)
> bmapi_flags |= XFS_BMAPI_IGSTATE;
>
> @@ -378,36 +374,26 @@ xfs_map_blocks(
> count = mp->m_super->s_maxbytes - offset;
> end_fsb = XFS_B_TO_FSB(mp, (xfs_ufsize_t)offset + count);
> offset_fsb = XFS_B_TO_FSBT(mp, offset);
> -
> - if (type == XFS_IO_COW) {
> - if (!xfs_reflink_find_cow_mapping(ip, offset, imap,
> - &need_alloc)) {
> - WARN_ON_ONCE(1);
> - return -EIO;
> - }
> - } else {
> - error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb,
> - imap, &nimaps, bmapi_flags);
> - /*
> - * Truncate an overwrite extent if there's a pending CoW
> - * reservation before the end of this extent. This forces us
> - * to come back to writepage to take care of the CoW.
> - */
> - if (nimaps && type == XFS_IO_OVERWRITE)
> - xfs_reflink_trim_irec_to_next_cow(ip, offset_fsb, imap);
> - }
> + error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb,
> + imap, &nimaps, bmapi_flags);
> + /*
> + * Truncate an overwrite extent if there's a pending CoW
> + * reservation before the end of this extent. This forces us
> + * to come back to writepage to take care of the CoW.
> + */
> + if (nimaps && type == XFS_IO_OVERWRITE)
> + xfs_reflink_trim_irec_to_next_cow(ip, offset_fsb, imap);
> xfs_iunlock(ip, XFS_ILOCK_SHARED);
>
> if (error)
> return error;
>
> - if (need_alloc &&
> + if (type == XFS_IO_DELALLOC &&
> (!nimaps || isnullstartblock(imap->br_startblock))) {
> - error = xfs_iomap_write_allocate(ip, whichfork, offset,
> + error = xfs_iomap_write_allocate(ip, XFS_DATA_FORK, offset,
> imap);
> if (!error)
> - trace_xfs_map_blocks_alloc(ip, offset, count, type,
> - imap);
> + trace_xfs_map_blocks_alloc(ip, offset, count, type, imap);
> return error;
> }
>
> @@ -707,27 +693,6 @@ xfs_check_page_type(
> return false;
> }
>
> -/*
> - * Figure out if CoW is pending at this offset.
> - */
> -static bool
> -xfs_is_cow_io(
> - struct xfs_inode *ip,
> - xfs_off_t offset)
> -{
> - struct xfs_bmbt_irec imap;
> - bool is_cow, need_alloc;
> -
> - if (!xfs_sb_version_hasreflink(&ip->i_mount->m_sb))
> - return false;
> -
> - xfs_ilock(ip, XFS_ILOCK_SHARED);
> - is_cow = xfs_reflink_find_cow_mapping(ip, offset, &imap, &need_alloc);
> - xfs_iunlock(ip, XFS_ILOCK_SHARED);
> -
> - return is_cow;
> -}
> -
> STATIC void
> xfs_vm_invalidatepage(
> struct page *page,
> @@ -804,6 +769,56 @@ out_invalidate:
> return;
> }
>
> +static int
> +xfs_map_cow(
> + struct xfs_writepage_ctx *wpc,
> + struct inode *inode,
> + loff_t offset,
> + unsigned int *new_type)
> +{
> + struct xfs_inode *ip = XFS_I(inode);
> + struct xfs_bmbt_irec imap;
> + bool is_cow = false, need_alloc = false;
> + int error;
> +
> + /*
> + * If we already have a valid COW mapping keep using it.
> + */
> + if (wpc->io_type == XFS_IO_COW) {
> + wpc->imap_valid = xfs_imap_valid(inode, &wpc->imap, offset);
> + if (wpc->imap_valid) {
> + *new_type = XFS_IO_COW;
> + return 0;
> + }
> + }
> +
> + /*
> + * Else we need to check if there is a COW mapping at this offset.
> + */
> + xfs_ilock(ip, XFS_ILOCK_SHARED);
> + is_cow = xfs_reflink_find_cow_mapping(ip, offset, &imap, &need_alloc);
> + xfs_iunlock(ip, XFS_ILOCK_SHARED);
> +
> + if (!is_cow)
> + return 0;
> +
> + /*
> + * And if the COW mapping has a delayed extent here we need to
> + * allocate real space for it now.
> + */
> + if (need_alloc) {
> + error = xfs_iomap_write_allocate(ip, XFS_COW_FORK, offset,
> + &imap);
> + if (error)
> + return error;
> + }
> +
> + wpc->io_type = *new_type = XFS_IO_COW;
> + wpc->imap_valid = true;
> + wpc->imap = imap;
> + return 0;
> +}
> +
> /*
> * We implement an immediate ioend submission policy here to avoid needing to
> * chain multiple ioends and hence nest mempool allocations which can violate
> @@ -876,8 +891,12 @@ xfs_writepage_map(
> continue;
> }
>
> - if (xfs_is_cow_io(XFS_I(inode), offset))
> - new_type = XFS_IO_COW;
> + if (xfs_sb_version_hasreflink(&XFS_I(inode)->i_mount->m_sb)) {
This could be:
if (xfs_is_reflink_inode(XFS_I(inode))) {
since we don't have to care about COW mappings unless the inode also has
shared extents. The code you got this from seems to have this bug too;
I'll just fix this when I push the patch onto my stack.
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
--D
> + error = xfs_map_cow(wpc, inode, offset, &new_type);
> + if (error)
> + goto out;
> + }
> +
> if (wpc->io_type != new_type) {
> wpc->io_type = new_type;
> wpc->imap_valid = false;
> --
> 2.1.4
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH 2/2] xfs: rewrite the COW writeback mapping code
2016-09-26 21:35 ` Darrick J. Wong
@ 2016-09-27 18:48 ` Christoph Hellwig
0 siblings, 0 replies; 7+ messages in thread
From: Christoph Hellwig @ 2016-09-27 18:48 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: linux-xfs
> if (xfs_is_reflink_inode(XFS_I(inode))) {
>
> since we don't have to care about COW mappings unless the inode also has
> shared extents. The code you got this from seems to have this bug too;
> I'll just fix this when I push the patch onto my stack.
Indeed. I felt the check to be a bit odd, but for some reason didn't
follow up on it.
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2016-09-27 18:48 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-09-24 15:19 only do a single COW fork lookup in writeback Christoph Hellwig
2016-09-24 15:19 ` [PATCH 1/2] xfs: kill xfs_reflink_is_cow_pending Christoph Hellwig
2016-09-26 21:08 ` Darrick J. Wong
2016-09-24 15:19 ` [PATCH 2/2] xfs: rewrite the COW writeback mapping code Christoph Hellwig
2016-09-26 21:35 ` Darrick J. Wong
2016-09-27 18:48 ` Christoph Hellwig
2016-09-25 3:47 ` only do a single COW fork lookup in writeback Christoph Hellwig
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).