]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs: Fix double unlock in defer capture code
authorAllison Henderson <allison.henderson@oracle.com>
Wed, 22 Jun 2022 19:28:52 +0000 (14:28 -0500)
committerEric Sandeen <sandeen@sandeen.net>
Wed, 22 Jun 2022 19:28:52 +0000 (14:28 -0500)
Source kernel commit: 7b3ec2b20e44f579c022ad62243aa18c04c6addc

The new deferred attr patch set uncovered a double unlock in the
recent port of the defer ops capture and continue code.  During log
recovery, we're allowed to hold buffers to a transaction that's being
used to replay an intent item.  When we capture the resources as part
of scheduling a continuation of an intent chain, we call xfs_buf_hold
to retain our reference to the buffer beyond the transaction commit,
but we do /not/ call xfs_trans_bhold to maintain the buffer lock.
This means that xfs_defer_ops_continue needs to relock the buffers
before xfs_defer_restore_resources joins then tothe new transaction.

Additionally, the buffers should not be passed back via the dres
structure since they need to remain locked unlike the inodes.  So
simply set dr_bufs to zero after populating the dres structure.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Allison Henderson <allison.henderson@oracle.com>
Reviewed-by: Chandan Babu R <chandan.babu@oracle.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
libxfs/libxfs_io.h
libxfs/rdwr.c
libxfs/xfs_defer.c

index 8a42f500c2eeb0b6af873f148b27e8b6e01731a2..9c0e2704d115410412b1e29b940d5a1ac3e5bb5c 100644 (file)
@@ -225,6 +225,8 @@ xfs_buf_hold(struct xfs_buf *bp)
        bp->b_node.cn_count++;
 }
 
+void xfs_buf_lock(struct xfs_buf *bp);
+
 int libxfs_buf_get_uncached(struct xfs_buftarg *targ, size_t bblen, int flags,
                struct xfs_buf **bpp);
 int libxfs_buf_read_uncached(struct xfs_buftarg *targ, xfs_daddr_t daddr,
index c283928b1a7b307cbcaca1188d05a10b269d3165..fe69f9b419dab520a0697e44d1cc57506d182c15 100644 (file)
@@ -376,6 +376,14 @@ libxfs_getbufr_map(struct xfs_buftarg *btp, xfs_daddr_t blkno, int bblen,
        return bp;
 }
 
+void
+xfs_buf_lock(
+       struct xfs_buf  *bp)
+{
+       if (use_xfs_buf_lock)
+               pthread_mutex_lock(&bp->b_lock);
+}
+
 static int
 __cache_lookup(
        struct xfs_bufkey       *key,
index d654a7d9af82bec42d9fe63af551e6528b136fac..8af4ae7b9b582e9619b97939c2beaae2b6642dad 100644 (file)
@@ -776,17 +776,25 @@ xfs_defer_ops_continue(
        struct xfs_trans                *tp,
        struct xfs_defer_resources      *dres)
 {
+       unsigned int                    i;
+
        ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
        ASSERT(!(tp->t_flags & XFS_TRANS_DIRTY));
 
-       /* Lock and join the captured inode to the new transaction. */
+       /* Lock the captured resources to the new transaction. */
        if (dfc->dfc_held.dr_inos == 2)
                xfs_lock_two_inodes(dfc->dfc_held.dr_ip[0], XFS_ILOCK_EXCL,
                                    dfc->dfc_held.dr_ip[1], XFS_ILOCK_EXCL);
        else if (dfc->dfc_held.dr_inos == 1)
                xfs_ilock(dfc->dfc_held.dr_ip[0], XFS_ILOCK_EXCL);
+
+       for (i = 0; i < dfc->dfc_held.dr_bufs; i++)
+               xfs_buf_lock(dfc->dfc_held.dr_bp[i]);
+
+       /* Join the captured resources to the new transaction. */
        xfs_defer_restore_resources(tp, &dfc->dfc_held);
        memcpy(dres, &dfc->dfc_held, sizeof(struct xfs_defer_resources));
+       dres->dr_bufs = 0;
 
        /* Move captured dfops chain and state to the transaction. */
        list_splice_init(&dfc->dfc_dfops, &tp->t_dfops);