]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - fs/xfs/xfs_reflink.c
Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm
[karo-tx-linux.git] / fs / xfs / xfs_reflink.c
index 95d6828967f095dd95b23ce878362f0ce5673ec6..07593a362cd03d0fdae120122d6d1c5aba8d809b 100644 (file)
@@ -243,12 +243,11 @@ xfs_reflink_reserve_cow(
        struct xfs_bmbt_irec    *imap,
        bool                    *shared)
 {
-       struct xfs_bmbt_irec    got, prev;
-       xfs_fileoff_t           end_fsb, orig_end_fsb;
-       int                     eof = 0, error = 0;
-       bool                    trimmed;
+       struct xfs_ifork        *ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
+       struct xfs_bmbt_irec    got;
+       int                     error = 0;
+       bool                    eof = false, trimmed;
        xfs_extnum_t            idx;
-       xfs_extlen_t            align;
 
        /*
         * Search the COW fork extent list first.  This serves two purposes:
@@ -258,8 +257,9 @@ xfs_reflink_reserve_cow(
         * extent list is generally faster than going out to the shared extent
         * tree.
         */
-       xfs_bmap_search_extents(ip, imap->br_startoff, XFS_COW_FORK, &eof, &idx,
-                       &got, &prev);
+
+       if (!xfs_iext_lookup_extent(ip, ifp, imap->br_startoff, &idx, &got))
+               eof = true;
        if (!eof && got.br_startoff <= imap->br_startoff) {
                trace_xfs_reflink_cow_found(ip, imap);
                xfs_trim_extent(imap, got.br_startoff, got.br_blockcount);
@@ -285,33 +285,12 @@ xfs_reflink_reserve_cow(
        if (error)
                return error;
 
-       end_fsb = orig_end_fsb = imap->br_startoff + imap->br_blockcount;
-
-       align = xfs_eof_alignment(ip, xfs_get_cowextsz_hint(ip));
-       if (align)
-               end_fsb = roundup_64(end_fsb, align);
-
-retry:
        error = xfs_bmapi_reserve_delalloc(ip, XFS_COW_FORK, imap->br_startoff,
-                       end_fsb - imap->br_startoff, &got, &prev, &idx, eof);
-       switch (error) {
-       case 0:
-               break;
-       case -ENOSPC:
-       case -EDQUOT:
-               /* retry without any preallocation */
+                       imap->br_blockcount, 0, &got, &idx, eof);
+       if (error == -ENOSPC || error == -EDQUOT)
                trace_xfs_reflink_cow_enospc(ip, imap);
-               if (end_fsb != orig_end_fsb) {
-                       end_fsb = orig_end_fsb;
-                       goto retry;
-               }
-               /*FALLTHRU*/
-       default:
+       if (error)
                return error;
-       }
-
-       if (end_fsb != orig_end_fsb)
-               xfs_inode_set_cowblocks_tag(ip);
 
        trace_xfs_reflink_cow_alloc(ip, &got);
        return 0;
@@ -418,87 +397,65 @@ xfs_reflink_allocate_cow_range(
 }
 
 /*
- * Find the CoW reservation (and whether or not it needs block allocation)
- * for a given byte offset of a file.
+ * Find the CoW reservation for a given byte offset of a file.
  */
 bool
 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            *imap)
 {
-       struct xfs_bmbt_irec            irec;
-       struct xfs_ifork                *ifp;
-       struct xfs_bmbt_rec_host        *gotp;
-       xfs_fileoff_t                   bno;
+       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;
 
        ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL | XFS_ILOCK_SHARED));
        ASSERT(xfs_is_reflink_inode(ip));
 
-       /* 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)
+       offset_fsb = XFS_B_TO_FSBT(ip->i_mount, offset);
+       if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &idx, &got))
                return false;
-
-       xfs_bmbt_get_all(gotp, &irec);
-       if (bno >= irec.br_startoff + irec.br_blockcount ||
-           bno < irec.br_startoff)
+       if (got.br_startoff > offset_fsb)
                return false;
 
        trace_xfs_reflink_find_cow_mapping(ip, offset, 1, XFS_IO_OVERWRITE,
-                       &irec);
-
-       /* If it's still delalloc, we must allocate later. */
-       *imap = irec;
-       *need_alloc = !!(isnullstartblock(irec.br_startblock));
-
+                       &got);
+       *imap = got;
        return true;
 }
 
 /*
  * Trim an extent to end at the next CoW reservation past offset_fsb.
  */
-int
+void
 xfs_reflink_trim_irec_to_next_cow(
        struct xfs_inode                *ip,
        xfs_fileoff_t                   offset_fsb,
        struct xfs_bmbt_irec            *imap)
 {
-       struct xfs_bmbt_irec            irec;
-       struct xfs_ifork                *ifp;
-       struct xfs_bmbt_rec_host        *gotp;
+       struct xfs_ifork                *ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
+       struct xfs_bmbt_irec            got;
        xfs_extnum_t                    idx;
 
        if (!xfs_is_reflink_inode(ip))
-               return 0;
+               return;
 
        /* Find the extent in the CoW fork. */
-       ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
-       gotp = xfs_iext_bno_to_ext(ifp, offset_fsb, &idx);
-       if (!gotp)
-               return 0;
-       xfs_bmbt_get_all(gotp, &irec);
+       if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &idx, &got))
+               return;
 
        /* This is the extent before; try sliding up one. */
-       if (irec.br_startoff < offset_fsb) {
-               idx++;
-               if (idx >= ifp->if_bytes / sizeof(xfs_bmbt_rec_t))
-                       return 0;
-               gotp = xfs_iext_get_ext(ifp, idx);
-               xfs_bmbt_get_all(gotp, &irec);
+       if (got.br_startoff < offset_fsb) {
+               if (!xfs_iext_get_extent(ifp, idx + 1, &got))
+                       return;
        }
 
-       if (irec.br_startoff >= imap->br_startoff + imap->br_blockcount)
-               return 0;
+       if (got.br_startoff >= imap->br_startoff + imap->br_blockcount)
+               return;
 
-       imap->br_blockcount = irec.br_startoff - imap->br_startoff;
+       imap->br_blockcount = got.br_startoff - imap->br_startoff;
        trace_xfs_reflink_trim_irec(ip, imap);
-
-       return 0;
 }
 
 /*
@@ -512,18 +469,15 @@ xfs_reflink_cancel_cow_blocks(
        xfs_fileoff_t                   end_fsb)
 {
        struct xfs_ifork                *ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
-       struct xfs_bmbt_irec            got, prev, del;
+       struct xfs_bmbt_irec            got, del;
        xfs_extnum_t                    idx;
        xfs_fsblock_t                   firstfsb;
        struct xfs_defer_ops            dfops;
-       int                             error = 0, eof = 0;
+       int                             error = 0;
 
        if (!xfs_is_reflink_inode(ip))
                return 0;
-
-       xfs_bmap_search_extents(ip, offset_fsb, XFS_COW_FORK, &eof, &idx,
-                       &got, &prev);
-       if (eof)
+       if (!xfs_iext_lookup_extent(ip, ifp, offset_fsb, &idx, &got))
                return 0;
 
        while (got.br_startoff < end_fsb) {
@@ -566,9 +520,8 @@ xfs_reflink_cancel_cow_blocks(
                        xfs_bmap_del_extent_cow(ip, &idx, &got, &del);
                }
 
-               if (++idx >= ifp->if_bytes / sizeof(struct xfs_bmbt_rec))
+               if (!xfs_iext_get_extent(ifp, ++idx, &got))
                        break;
-               xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx), &got);
        }
 
        /* clear tag if cow fork is emptied */
@@ -638,13 +591,13 @@ xfs_reflink_end_cow(
        xfs_off_t                       count)
 {
        struct xfs_ifork                *ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
-       struct xfs_bmbt_irec            got, prev, del;
+       struct xfs_bmbt_irec            got, del;
        struct xfs_trans                *tp;
        xfs_fileoff_t                   offset_fsb;
        xfs_fileoff_t                   end_fsb;
        xfs_fsblock_t                   firstfsb;
        struct xfs_defer_ops            dfops;
-       int                             error, eof = 0;
+       int                             error;
        unsigned int                    resblks;
        xfs_filblks_t                   rlen;
        xfs_extnum_t                    idx;
@@ -668,13 +621,11 @@ xfs_reflink_end_cow(
        xfs_ilock(ip, XFS_ILOCK_EXCL);
        xfs_trans_ijoin(tp, ip, 0);
 
-       xfs_bmap_search_extents(ip, end_fsb - 1, XFS_COW_FORK, &eof, &idx,
-                       &got, &prev);
-
        /* If there is a hole at end_fsb - 1 go to the previous extent */
-       if (eof || got.br_startoff > end_fsb) {
+       if (!xfs_iext_lookup_extent(ip, ifp, end_fsb - 1, &idx, &got) ||
+           got.br_startoff > end_fsb) {
                ASSERT(idx > 0);
-               xfs_bmbt_get_all(xfs_iext_get_ext(ifp, --idx), &got);
+               xfs_iext_get_extent(ifp, --idx, &got);
        }
 
        /* Walk backwards until we're out of the I/O range... */
@@ -722,11 +673,9 @@ xfs_reflink_end_cow(
                error = xfs_defer_finish(&tp, &dfops, ip);
                if (error)
                        goto out_defer;
-
 next_extent:
-               if (idx < 0)
+               if (!xfs_iext_get_extent(ifp, idx, &got))
                        break;
-               xfs_bmbt_get_all(xfs_iext_get_ext(ifp, idx), &got);
        }
 
        error = xfs_trans_commit(tp);
@@ -1194,13 +1143,11 @@ xfs_reflink_remap_range(
                return -EIO;
 
        /* Lock both files against IO */
-       if (same_inode) {
-               xfs_ilock(src, XFS_IOLOCK_EXCL);
+       lock_two_nondirectories(inode_in, inode_out);
+       if (same_inode)
                xfs_ilock(src, XFS_MMAPLOCK_EXCL);
-       } else {
-               xfs_lock_two_inodes(src, dest, XFS_IOLOCK_EXCL);
+       else
                xfs_lock_two_inodes(src, dest, XFS_MMAPLOCK_EXCL);
-       }
 
        /* Check file eligibility and prepare for block sharing. */
        ret = -EINVAL;
@@ -1214,7 +1161,7 @@ xfs_reflink_remap_range(
 
        ret = vfs_clone_file_prep_inodes(inode_in, pos_in, inode_out, pos_out,
                        &len, is_dedupe);
-       if (ret || len == 0)
+       if (ret <= 0)
                goto out_unlock;
 
        trace_xfs_reflink_remap_range(src, pos_in, len, dest, pos_out);
@@ -1252,11 +1199,9 @@ xfs_reflink_remap_range(
 
 out_unlock:
        xfs_iunlock(src, XFS_MMAPLOCK_EXCL);
-       xfs_iunlock(src, XFS_IOLOCK_EXCL);
-       if (src->i_ino != dest->i_ino) {
+       if (!same_inode)
                xfs_iunlock(dest, XFS_MMAPLOCK_EXCL);
-               xfs_iunlock(dest, XFS_IOLOCK_EXCL);
-       }
+       unlock_two_nondirectories(inode_in, inode_out);
        if (ret)
                trace_xfs_reflink_remap_range_error(dest, ret, _RET_IP_);
        return ret;
@@ -1502,37 +1447,3 @@ out:
        trace_xfs_reflink_unshare_error(ip, error, _RET_IP_);
        return error;
 }
-
-/*
- * Does this inode have any real CoW reservations?
- */
-bool
-xfs_reflink_has_real_cow_blocks(
-       struct xfs_inode                *ip)
-{
-       struct xfs_bmbt_irec            irec;
-       struct xfs_ifork                *ifp;
-       struct xfs_bmbt_rec_host        *gotp;
-       xfs_extnum_t                    idx;
-
-       if (!xfs_is_reflink_inode(ip))
-               return false;
-
-       /* Go find the old extent in the CoW fork. */
-       ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK);
-       gotp = xfs_iext_bno_to_ext(ifp, 0, &idx);
-       while (gotp) {
-               xfs_bmbt_get_all(gotp, &irec);
-
-               if (!isnullstartblock(irec.br_startblock))
-                       return true;
-
-               /* Roll on... */
-               idx++;
-               if (idx >= ifp->if_bytes / sizeof(xfs_bmbt_rec_t))
-                       break;
-               gotp = xfs_iext_get_ext(ifp, idx);
-       }
-
-       return false;
-}