diff options
author | James Morris <james.l.morris@oracle.com> | 2017-11-29 12:47:41 +1100 |
---|---|---|
committer | James Morris <james.l.morris@oracle.com> | 2017-11-29 12:47:41 +1100 |
commit | cf40a76e7d5874bb25f4404eecc58a2e033af885 (patch) | |
tree | 8fd81cbea03c87b3d41d7ae5b1d11eadd35d6ef5 /fs/xfs/xfs_reflink.c | |
parent | ab5348c9c23cd253f5902980d2d8fe067dc24c82 (diff) | |
parent | 4fbd8d194f06c8a3fd2af1ce560ddb31f7ec8323 (diff) |
Merge tag 'v4.15-rc1' into next-seccomp
Linux 4.15-rc1
Diffstat (limited to 'fs/xfs/xfs_reflink.c')
-rw-r--r-- | fs/xfs/xfs_reflink.c | 118 |
1 files changed, 63 insertions, 55 deletions
diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index ab2270a87196..cc041a29eb70 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -170,6 +170,8 @@ xfs_reflink_find_shared( error = xfs_alloc_read_agf(mp, tp, agno, 0, &agbp); if (error) return error; + if (!agbp) + return -ENOMEM; cur = xfs_refcountbt_init_cursor(mp, tp, agbp, agno, NULL); @@ -271,7 +273,7 @@ xfs_reflink_reserve_cow( struct xfs_bmbt_irec got; int error = 0; bool eof = false, trimmed; - xfs_extnum_t idx; + struct xfs_iext_cursor icur; /* * Search the COW fork extent list first. This serves two purposes: @@ -282,7 +284,7 @@ xfs_reflink_reserve_cow( * tree. */ - if (!xfs_iext_lookup_extent(ip, ifp, imap->br_startoff, &idx, &got)) + if (!xfs_iext_lookup_extent(ip, ifp, imap->br_startoff, &icur, &got)) eof = true; if (!eof && got.br_startoff <= imap->br_startoff) { trace_xfs_reflink_cow_found(ip, imap); @@ -310,7 +312,7 @@ xfs_reflink_reserve_cow( return error; error = xfs_bmapi_reserve_delalloc(ip, XFS_COW_FORK, imap->br_startoff, - imap->br_blockcount, 0, &got, &idx, eof); + imap->br_blockcount, 0, &got, &icur, eof); if (error == -ENOSPC || error == -EDQUOT) trace_xfs_reflink_cow_enospc(ip, imap); if (error) @@ -329,7 +331,7 @@ xfs_reflink_convert_cow_extent( xfs_filblks_t count_fsb, struct xfs_defer_ops *dfops) { - xfs_fsblock_t first_block; + xfs_fsblock_t first_block = NULLFSBLOCK; int nimaps = 1; if (imap->br_state == XFS_EXT_NORM) @@ -351,29 +353,22 @@ xfs_reflink_convert_cow( xfs_off_t offset, xfs_off_t count) { - struct xfs_bmbt_irec got; - struct xfs_defer_ops dfops; struct xfs_mount *mp = ip->i_mount; - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK); xfs_fileoff_t offset_fsb = XFS_B_TO_FSBT(mp, offset); xfs_fileoff_t end_fsb = XFS_B_TO_FSB(mp, offset + count); - xfs_extnum_t idx; - bool found; - int error = 0; - - xfs_ilock(ip, XFS_ILOCK_EXCL); + xfs_filblks_t count_fsb = end_fsb - offset_fsb; + struct xfs_bmbt_irec imap; + struct xfs_defer_ops dfops; + xfs_fsblock_t first_block = NULLFSBLOCK; + int nimaps = 1, error = 0; - /* Convert all the extents to real from unwritten. */ - for (found = xfs_iext_lookup_extent(ip, ifp, offset_fsb, &idx, &got); - found && got.br_startoff < end_fsb; - found = xfs_iext_get_extent(ifp, ++idx, &got)) { - error = xfs_reflink_convert_cow_extent(ip, &got, offset_fsb, - end_fsb - offset_fsb, &dfops); - if (error) - break; - } + ASSERT(count != 0); - /* Finish up. */ + xfs_ilock(ip, XFS_ILOCK_EXCL); + error = xfs_bmapi_write(NULL, ip, offset_fsb, count_fsb, + XFS_BMAPI_COWFORK | XFS_BMAPI_CONVERT | + XFS_BMAPI_CONVERT_ONLY, &first_block, 0, &imap, &nimaps, + &dfops); xfs_iunlock(ip, XFS_ILOCK_EXCL); return error; } @@ -397,7 +392,7 @@ xfs_reflink_allocate_cow( bool trimmed; xfs_filblks_t resaligned; xfs_extlen_t resblks = 0; - xfs_extnum_t idx; + struct xfs_iext_cursor icur; retry: ASSERT(xfs_is_reflink_inode(ip)); @@ -407,7 +402,7 @@ retry: * Even if the extent is not shared we might have a preallocation for * it in the COW fork. If so use it. */ - if (xfs_iext_lookup_extent(ip, ip->i_cowfp, offset_fsb, &idx, &got) && + if (xfs_iext_lookup_extent(ip, ip->i_cowfp, offset_fsb, &icur, &got) && got.br_startoff <= offset_fsb) { *shared = true; @@ -462,7 +457,7 @@ retry: goto out_bmap_cancel; /* Finish up. */ - error = xfs_defer_finish(&tp, &dfops, NULL); + error = xfs_defer_finish(&tp, &dfops); if (error) goto out_bmap_cancel; @@ -494,13 +489,13 @@ xfs_reflink_find_cow_mapping( struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK); xfs_fileoff_t offset_fsb; struct xfs_bmbt_irec got; - xfs_extnum_t idx; + struct xfs_iext_cursor icur; ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL | XFS_ILOCK_SHARED)); ASSERT(xfs_is_reflink_inode(ip)); offset_fsb = XFS_B_TO_FSBT(ip->i_mount, offset); - if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &idx, &got)) + if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &icur, &got)) return false; if (got.br_startoff > offset_fsb) return false; @@ -522,18 +517,18 @@ xfs_reflink_trim_irec_to_next_cow( { struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK); struct xfs_bmbt_irec got; - xfs_extnum_t idx; + struct xfs_iext_cursor icur; if (!xfs_is_reflink_inode(ip)) return; /* Find the extent in the CoW fork. */ - if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &idx, &got)) + if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &icur, &got)) return; /* This is the extent before; try sliding up one. */ if (got.br_startoff < offset_fsb) { - if (!xfs_iext_get_extent(ifp, idx + 1, &got)) + if (!xfs_iext_next_extent(ifp, &icur, &got)) return; } @@ -560,24 +555,32 @@ xfs_reflink_cancel_cow_blocks( { struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK); struct xfs_bmbt_irec got, del; - xfs_extnum_t idx; + struct xfs_iext_cursor icur; xfs_fsblock_t firstfsb; struct xfs_defer_ops dfops; int error = 0; if (!xfs_is_reflink_inode(ip)) return 0; - if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &idx, &got)) + if (!xfs_iext_lookup_extent_before(ip, ifp, &end_fsb, &icur, &got)) return 0; - while (got.br_startoff < end_fsb) { + /* Walk backwards until we're out of the I/O range... */ + while (got.br_startoff + got.br_blockcount > offset_fsb) { del = got; xfs_trim_extent(&del, offset_fsb, end_fsb - offset_fsb); + + /* Extent delete may have bumped ext forward */ + if (!del.br_blockcount) { + xfs_iext_prev(ifp, &icur); + goto next_extent; + } + trace_xfs_reflink_cancel_cow(ip, &del); if (isnullstartblock(del.br_startblock)) { error = xfs_bmap_del_extent_delay(ip, XFS_COW_FORK, - &idx, &got, &del); + &icur, &got, &del); if (error) break; } else if (del.br_state == XFS_EXT_UNWRITTEN || cancel_real) { @@ -600,17 +603,18 @@ xfs_reflink_cancel_cow_blocks( -(long)del.br_blockcount); /* Roll the transaction */ - error = xfs_defer_finish(tpp, &dfops, ip); + xfs_defer_ijoin(&dfops, ip); + error = xfs_defer_finish(tpp, &dfops); if (error) { xfs_defer_cancel(&dfops); break; } /* Remove the mapping from the CoW fork. */ - xfs_bmap_del_extent_cow(ip, &idx, &got, &del); + xfs_bmap_del_extent_cow(ip, &icur, &got, &del); } - - if (!xfs_iext_get_extent(ifp, ++idx, &got)) +next_extent: + if (!xfs_iext_get_extent(ifp, &icur, &got)) break; } @@ -695,7 +699,7 @@ xfs_reflink_end_cow( int error; unsigned int resblks; xfs_filblks_t rlen; - xfs_extnum_t idx; + struct xfs_iext_cursor icur; trace_xfs_reflink_end_cow(ip, offset, count); @@ -730,21 +734,22 @@ xfs_reflink_end_cow( xfs_ilock(ip, XFS_ILOCK_EXCL); xfs_trans_ijoin(tp, ip, 0); - /* If there is a hole at end_fsb - 1 go to the previous extent */ - if (!xfs_iext_lookup_extent(ip, ifp, end_fsb - 1, &idx, &got) || - got.br_startoff > end_fsb) { - ASSERT(idx > 0); - xfs_iext_get_extent(ifp, --idx, &got); - } + /* + * In case of racing, overlapping AIO writes no COW extents might be + * left by the time I/O completes for the loser of the race. In that + * case we are done. + */ + if (!xfs_iext_lookup_extent_before(ip, ifp, &end_fsb, &icur, &got)) + goto out_cancel; /* Walk backwards until we're out of the I/O range... */ while (got.br_startoff + got.br_blockcount > offset_fsb) { del = got; xfs_trim_extent(&del, offset_fsb, end_fsb - offset_fsb); - /* Extent delete may have bumped idx forward */ + /* Extent delete may have bumped ext forward */ if (!del.br_blockcount) { - idx--; + xfs_iext_prev(ifp, &icur); goto next_extent; } @@ -756,7 +761,7 @@ xfs_reflink_end_cow( * allocated but have not yet been involved in a write. */ if (got.br_state == XFS_EXT_UNWRITTEN) { - idx--; + xfs_iext_prev(ifp, &icur); goto next_extent; } @@ -787,13 +792,14 @@ xfs_reflink_end_cow( goto out_defer; /* Remove the mapping from the CoW fork. */ - xfs_bmap_del_extent_cow(ip, &idx, &got, &del); + xfs_bmap_del_extent_cow(ip, &icur, &got, &del); - error = xfs_defer_finish(&tp, &dfops, ip); + xfs_defer_ijoin(&dfops, ip); + error = xfs_defer_finish(&tp, &dfops); if (error) goto out_defer; next_extent: - if (!xfs_iext_get_extent(ifp, idx, &got)) + if (!xfs_iext_get_extent(ifp, &icur, &got)) break; } @@ -805,6 +811,7 @@ next_extent: out_defer: xfs_defer_cancel(&dfops); +out_cancel: xfs_trans_cancel(tp); xfs_iunlock(ip, XFS_ILOCK_EXCL); out: @@ -1150,7 +1157,8 @@ xfs_reflink_remap_extent( next_extent: /* Process all the deferred stuff. */ - error = xfs_defer_finish(&tp, &dfops, ip); + xfs_defer_ijoin(&dfops, ip); + error = xfs_defer_finish(&tp, &dfops); if (error) goto out_defer; } @@ -1421,7 +1429,7 @@ xfs_reflink_inode_has_shared_extents( xfs_extlen_t aglen; xfs_agblock_t rbno; xfs_extlen_t rlen; - xfs_extnum_t idx; + struct xfs_iext_cursor icur; bool found; int error; @@ -1433,7 +1441,7 @@ xfs_reflink_inode_has_shared_extents( } *has_shared = false; - found = xfs_iext_lookup_extent(ip, ifp, 0, &idx, &got); + found = xfs_iext_lookup_extent(ip, ifp, 0, &icur, &got); while (found) { if (isnullstartblock(got.br_startblock) || got.br_state != XFS_EXT_NORM) @@ -1452,7 +1460,7 @@ xfs_reflink_inode_has_shared_extents( return 0; } next: - found = xfs_iext_get_extent(ifp, ++idx, &got); + found = xfs_iext_next_extent(ifp, &icur, &got); } return 0; |