diff options
Diffstat (limited to 'fs/xfs/xfs_reflink.c')
-rw-r--r-- | fs/xfs/xfs_reflink.c | 101 |
1 files changed, 59 insertions, 42 deletions
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index ffe6fe7a7eb5..ab2270a87196 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -155,6 +155,7 @@ int xfs_reflink_find_shared( struct xfs_mount *mp, + struct xfs_trans *tp, xfs_agnumber_t agno, xfs_agblock_t agbno, xfs_extlen_t aglen, @@ -166,18 +167,18 @@ xfs_reflink_find_shared( struct xfs_btree_cur *cur; int error; - error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp); + error = xfs_alloc_read_agf(mp, tp, agno, 0, &agbp); if (error) return error; - cur = xfs_refcountbt_init_cursor(mp, NULL, agbp, agno, NULL); + cur = xfs_refcountbt_init_cursor(mp, tp, agbp, agno, NULL); error = xfs_refcount_find_shared(cur, agbno, aglen, fbno, flen, find_end_of_shared); xfs_btree_del_cursor(cur, error ? XFS_BTREE_ERROR : XFS_BTREE_NOERROR); - xfs_buf_relse(agbp); + xfs_trans_brelse(tp, agbp); return error; } @@ -217,7 +218,7 @@ xfs_reflink_trim_around_shared( agbno = XFS_FSB_TO_AGBNO(ip->i_mount, irec->br_startblock); aglen = irec->br_blockcount; - error = xfs_reflink_find_shared(ip->i_mount, agno, agbno, + error = xfs_reflink_find_shared(ip->i_mount, NULL, agno, agbno, aglen, &fbno, &flen, true); if (error) return error; @@ -1373,8 +1374,8 @@ xfs_reflink_dirty_extents( agbno = XFS_FSB_TO_AGBNO(mp, map[1].br_startblock); aglen = map[1].br_blockcount; - error = xfs_reflink_find_shared(mp, agno, agbno, aglen, - &rbno, &rlen, true); + error = xfs_reflink_find_shared(mp, NULL, agno, agbno, + aglen, &rbno, &rlen, true); if (error) goto out; if (rbno == NULLAGBLOCK) @@ -1405,57 +1406,73 @@ out: return error; } -/* Clear the inode reflink flag if there are no shared extents. */ +/* Does this inode need the reflink flag? */ int -xfs_reflink_clear_inode_flag( - struct xfs_inode *ip, - struct xfs_trans **tpp) +xfs_reflink_inode_has_shared_extents( + struct xfs_trans *tp, + struct xfs_inode *ip, + bool *has_shared) { - struct xfs_mount *mp = ip->i_mount; - xfs_fileoff_t fbno; - xfs_filblks_t end; - xfs_agnumber_t agno; - xfs_agblock_t agbno; - xfs_extlen_t aglen; - xfs_agblock_t rbno; - xfs_extlen_t rlen; - struct xfs_bmbt_irec map; - int nmaps; - int error = 0; - - ASSERT(xfs_is_reflink_inode(ip)); + struct xfs_bmbt_irec got; + struct xfs_mount *mp = ip->i_mount; + struct xfs_ifork *ifp; + xfs_agnumber_t agno; + xfs_agblock_t agbno; + xfs_extlen_t aglen; + xfs_agblock_t rbno; + xfs_extlen_t rlen; + xfs_extnum_t idx; + bool found; + int error; - fbno = 0; - end = XFS_B_TO_FSB(mp, i_size_read(VFS_I(ip))); - while (end - fbno > 0) { - nmaps = 1; - /* - * Look for extents in the file. Skip holes, delalloc, or - * unwritten extents; they can't be reflinked. - */ - error = xfs_bmapi_read(ip, fbno, end - fbno, &map, &nmaps, 0); + ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK); + if (!(ifp->if_flags & XFS_IFEXTENTS)) { + error = xfs_iread_extents(tp, ip, XFS_DATA_FORK); if (error) return error; - if (nmaps == 0) - break; - if (!xfs_bmap_is_real_extent(&map)) - goto next; + } - agno = XFS_FSB_TO_AGNO(mp, map.br_startblock); - agbno = XFS_FSB_TO_AGBNO(mp, map.br_startblock); - aglen = map.br_blockcount; + *has_shared = false; + found = xfs_iext_lookup_extent(ip, ifp, 0, &idx, &got); + while (found) { + if (isnullstartblock(got.br_startblock) || + got.br_state != XFS_EXT_NORM) + goto next; + agno = XFS_FSB_TO_AGNO(mp, got.br_startblock); + agbno = XFS_FSB_TO_AGBNO(mp, got.br_startblock); + aglen = got.br_blockcount; - error = xfs_reflink_find_shared(mp, agno, agbno, aglen, + error = xfs_reflink_find_shared(mp, tp, agno, agbno, aglen, &rbno, &rlen, false); if (error) return error; /* Is there still a shared block here? */ - if (rbno != NULLAGBLOCK) + if (rbno != NULLAGBLOCK) { + *has_shared = true; return 0; + } next: - fbno = map.br_startoff + map.br_blockcount; + found = xfs_iext_get_extent(ifp, ++idx, &got); } + return 0; +} + +/* Clear the inode reflink flag if there are no shared extents. */ +int +xfs_reflink_clear_inode_flag( + struct xfs_inode *ip, + struct xfs_trans **tpp) +{ + bool needs_flag; + int error = 0; + + ASSERT(xfs_is_reflink_inode(ip)); + + error = xfs_reflink_inode_has_shared_extents(*tpp, ip, &needs_flag); + if (error || needs_flag) + return error; + /* * We didn't find any shared blocks so turn off the reflink flag. * First, get rid of any leftover CoW mappings. |