]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs: try to avoid blowing out the transaction reservation when bunmaping a shared...
authorDarrick J. Wong <darrick.wong@oracle.com>
Mon, 31 Jul 2017 20:08:09 +0000 (15:08 -0500)
committerEric Sandeen <sandeen@redhat.com>
Mon, 31 Jul 2017 20:08:09 +0000 (15:08 -0500)
Source kernel commit: e1a4e37cc7b665b6804fba812aca2f4d7402c249

In a pathological scenario where we are trying to bunmapi a single
extent in which every other block is shared, it's possible that trying
to unmap the entire large extent in a single transaction can generate so
many EFIs that we overflow the transaction reservation.

Therefore, use a heuristic to guess at the number of blocks we can
safely unmap from a reflink file's data fork in an single transaction.
This should prevent problems such as the log head slamming into the tail
and ASSERTs that trigger because we've exceeded the transaction
reservation.

Note that since bunmapi can fail to unmap the entire range, we must also
teach the deferred unmap code to roll into a new transaction whenever we
get low on reservation.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
[hch: random edits, all bugs are my fault]
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
libxfs/defer_item.c
libxfs/xfs_bmap.c
libxfs/xfs_bmap.h
libxfs/xfs_refcount.c
libxfs/xfs_refcount.h

index 49bf7f8bdde31518814b2331ae25ed197d40903d..17ec2082eedf891b48a1298e633acfdf3e1e1197 100644 (file)
@@ -445,16 +445,23 @@ xfs_bmap_update_finish_item(
        void                            **state)
 {
        struct xfs_bmap_intent          *bmap;
+       xfs_filblks_t                   count;
        int                             error;
 
        bmap = container_of(item, struct xfs_bmap_intent, bi_list);
+       count = bmap->bi_bmap.br_blockcount;
        error = xfs_bmap_finish_one(tp, dop,
                        bmap->bi_owner,
                        bmap->bi_type, bmap->bi_whichfork,
                        bmap->bi_bmap.br_startoff,
                        bmap->bi_bmap.br_startblock,
-                       bmap->bi_bmap.br_blockcount,
+                       &count,
                        bmap->bi_bmap.br_state);
+       if (!error && count > 0) {
+               ASSERT(bmap->bi_type == XFS_BMAP_UNMAP);
+               bmap->bi_bmap.br_blockcount = count;
+               return -EAGAIN;
+       }
        kmem_free(bmap);
        return error;
 }
index 2ad6f5a6418f13c76df92b03bdf32bac1029a23c..2e6ac0321f53a4d213c97d39905c0e548b97e6d4 100644 (file)
@@ -5425,6 +5425,7 @@ __xfs_bunmapi(
        int                     whichfork;      /* data or attribute fork */
        xfs_fsblock_t           sum;
        xfs_filblks_t           len = *rlen;    /* length to unmap in file */
+       xfs_fileoff_t           max_len;
 
        trace_xfs_bunmap(ip, bno, len, flags, _RET_IP_);
 
@@ -5446,6 +5447,16 @@ __xfs_bunmapi(
        ASSERT(len > 0);
        ASSERT(nexts >= 0);
 
+       /*
+        * Guesstimate how many blocks we can unmap without running the risk of
+        * blowing out the transaction with a mix of EFIs and reflink
+        * adjustments.
+        */
+       if (xfs_is_reflink_inode(ip) && whichfork == XFS_DATA_FORK)
+               max_len = min(len, xfs_refcount_max_unmap(tp->t_log_res));
+       else
+               max_len = len;
+
        if (!(ifp->if_flags & XFS_IFEXTENTS) &&
            (error = xfs_iread_extents(tp, ip, whichfork)))
                return error;
@@ -5490,7 +5501,7 @@ __xfs_bunmapi(
 
        extno = 0;
        while (bno != (xfs_fileoff_t)-1 && bno >= start && lastx >= 0 &&
-              (nexts == 0 || extno < nexts)) {
+              (nexts == 0 || extno < nexts) && max_len > 0) {
                /*
                 * Is the found extent after a hole in which bno lives?
                 * Just back up to the previous extent, if so.
@@ -5522,6 +5533,15 @@ __xfs_bunmapi(
                }
                if (del.br_startoff + del.br_blockcount > bno + 1)
                        del.br_blockcount = bno + 1 - del.br_startoff;
+
+               /* How much can we safely unmap? */
+               if (max_len < del.br_blockcount) {
+                       del.br_startoff += del.br_blockcount - max_len;
+                       if (!wasdel)
+                               del.br_startblock += del.br_blockcount - max_len;
+                       del.br_blockcount = max_len;
+               }
+
                sum = del.br_startblock + del.br_blockcount;
                if (isrt &&
                    (mod = do_mod(sum, mp->m_sb.sb_rextsize))) {
@@ -5698,6 +5718,7 @@ __xfs_bunmapi(
                if (!isrt && wasdel)
                        xfs_mod_fdblocks(mp, (int64_t)del.br_blockcount, false);
 
+               max_len -= del.br_blockcount;
                bno = del.br_startoff - 1;
 nodelete:
                /*
@@ -6463,15 +6484,16 @@ xfs_bmap_finish_one(
        int                             whichfork,
        xfs_fileoff_t                   startoff,
        xfs_fsblock_t                   startblock,
-       xfs_filblks_t                   blockcount,
+       xfs_filblks_t                   *blockcount,
        xfs_exntst_t                    state)
 {
-       int                             error = 0, done;
+       xfs_fsblock_t                   firstfsb;
+       int                             error = 0;
 
        trace_xfs_bmap_deferred(tp->t_mountp,
                        XFS_FSB_TO_AGNO(tp->t_mountp, startblock), type,
                        XFS_FSB_TO_AGBNO(tp->t_mountp, startblock),
-                       ip->i_ino, whichfork, startoff, blockcount, state);
+                       ip->i_ino, whichfork, startoff, *blockcount, state);
 
        if (WARN_ON_ONCE(whichfork != XFS_DATA_FORK))
                return -EFSCORRUPTED;
@@ -6483,13 +6505,13 @@ xfs_bmap_finish_one(
 
        switch (type) {
        case XFS_BMAP_MAP:
-               error = xfs_bmapi_remap(tp, ip, startoff, blockcount,
+               error = xfs_bmapi_remap(tp, ip, startoff, *blockcount,
                                startblock, dfops);
+               *blockcount = 0;
                break;
        case XFS_BMAP_UNMAP:
-               error = xfs_bunmapi(tp, ip, startoff, blockcount,
-                               XFS_BMAPI_REMAP, 1, &startblock, dfops, &done);
-               ASSERT(done);
+               error = __xfs_bunmapi(tp, ip, startoff, blockcount,
+                               XFS_BMAPI_REMAP, 1, &firstfsb, dfops);
                break;
        default:
                ASSERT(0);
index c35a14fa15272ab5ef38f8b47953ae120d72fb4e..851982a5dfbc54b347d5836898264f56b3b4f957 100644 (file)
@@ -271,7 +271,7 @@ struct xfs_bmap_intent {
 int    xfs_bmap_finish_one(struct xfs_trans *tp, struct xfs_defer_ops *dfops,
                struct xfs_inode *ip, enum xfs_bmap_intent_type type,
                int whichfork, xfs_fileoff_t startoff, xfs_fsblock_t startblock,
-               xfs_filblks_t blockcount, xfs_exntst_t state);
+               xfs_filblks_t *blockcount, xfs_exntst_t state);
 int    xfs_bmap_map_extent(struct xfs_mount *mp, struct xfs_defer_ops *dfops,
                struct xfs_inode *ip, struct xfs_bmbt_irec *imap);
 int    xfs_bmap_unmap_extent(struct xfs_mount *mp, struct xfs_defer_ops *dfops,
index 713cfc93491c9ba258ec7a6bf23a250e659c9a25..9b05cc0e456f37244ad83cbaf55d33ad060a98ad 100644 (file)
@@ -783,14 +783,6 @@ xfs_refcount_merge_extents(
 }
 
 /*
- * While we're adjusting the refcounts records of an extent, we have
- * to keep an eye on the number of extents we're dirtying -- run too
- * many in a single transaction and we'll exceed the transaction's
- * reservation and crash the fs.  Each record adds 12 bytes to the
- * log (plus any key updates) so we'll conservatively assume 24 bytes
- * per record.  We must also leave space for btree splits on both ends
- * of the range and space for the CUD and a new CUI.
- *
  * XXX: This is a pretty hand-wavy estimate.  The penalty for guessing
  * true incorrectly is a shutdown FS; the penalty for guessing false
  * incorrectly is more transaction rolls than might be necessary.
@@ -821,7 +813,7 @@ xfs_refcount_still_have_space(
        else if (overhead > cur->bc_tp->t_log_res)
                return false;
        return  cur->bc_tp->t_log_res - overhead >
-               cur->bc_private.a.priv.refc.nr_ops * 32;
+               cur->bc_private.a.priv.refc.nr_ops * XFS_REFCOUNT_ITEM_OVERHEAD;
 }
 
 /*
index 098dc668ab2c0d6a97c1eda5ea515950cb07e151..eafb9d1f3b3748bb9943560bf1e10e4dc2723204 100644 (file)
@@ -67,4 +67,20 @@ extern int xfs_refcount_free_cow_extent(struct xfs_mount *mp,
 extern int xfs_refcount_recover_cow_leftovers(struct xfs_mount *mp,
                xfs_agnumber_t agno);
 
+/*
+ * While we're adjusting the refcounts records of an extent, we have
+ * to keep an eye on the number of extents we're dirtying -- run too
+ * many in a single transaction and we'll exceed the transaction's
+ * reservation and crash the fs.  Each record adds 12 bytes to the
+ * log (plus any key updates) so we'll conservatively assume 32 bytes
+ * per record.  We must also leave space for btree splits on both ends
+ * of the range and space for the CUD and a new CUI.
+ */
+#define XFS_REFCOUNT_ITEM_OVERHEAD     32
+
+static inline xfs_fileoff_t xfs_refcount_max_unmap(int log_res)
+{
+       return (log_res * 3 / 4) / XFS_REFCOUNT_ITEM_OVERHEAD;
+}
+
 #endif /* __XFS_REFCOUNT_H__ */