Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

xfs: Fix double unlock in defer capture code

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>

authored by

Allison Henderson and committed by
Dave Chinner
7b3ec2b2 86810a9e

+10 -1
+10 -1
fs/xfs/libxfs/xfs_defer.c
··· 22 22 #include "xfs_refcount.h" 23 23 #include "xfs_bmap.h" 24 24 #include "xfs_alloc.h" 25 + #include "xfs_buf.h" 25 26 26 27 static struct kmem_cache *xfs_defer_pending_cache; 27 28 ··· 781 780 struct xfs_trans *tp, 782 781 struct xfs_defer_resources *dres) 783 782 { 783 + unsigned int i; 784 + 784 785 ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES); 785 786 ASSERT(!(tp->t_flags & XFS_TRANS_DIRTY)); 786 787 787 - /* Lock and join the captured inode to the new transaction. */ 788 + /* Lock the captured resources to the new transaction. */ 788 789 if (dfc->dfc_held.dr_inos == 2) 789 790 xfs_lock_two_inodes(dfc->dfc_held.dr_ip[0], XFS_ILOCK_EXCL, 790 791 dfc->dfc_held.dr_ip[1], XFS_ILOCK_EXCL); 791 792 else if (dfc->dfc_held.dr_inos == 1) 792 793 xfs_ilock(dfc->dfc_held.dr_ip[0], XFS_ILOCK_EXCL); 794 + 795 + for (i = 0; i < dfc->dfc_held.dr_bufs; i++) 796 + xfs_buf_lock(dfc->dfc_held.dr_bp[i]); 797 + 798 + /* Join the captured resources to the new transaction. */ 793 799 xfs_defer_restore_resources(tp, &dfc->dfc_held); 794 800 memcpy(dres, &dfc->dfc_held, sizeof(struct xfs_defer_resources)); 801 + dres->dr_bufs = 0; 795 802 796 803 /* Move captured dfops chain and state to the transaction. */ 797 804 list_splice_init(&dfc->dfc_dfops, &tp->t_dfops);