* 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
* 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
* [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: [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
* 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
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).