xfs: allow xfs_lock_two_inodes to take different EXCL/SHARED modes

Refactor xfs_lock_two_inodes to take separate locking modes for each
inode.  Specifically, this enables us to take a SHARED lock on one inode
and an EXCL lock on the other.  The lock class (MMAPLOCK/ILOCK) must be
the same for each inode.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
Darrick J. Wong 2018-01-26 15:27:33 -08:00
parent 1364b1d4b5
commit 7c2d238ac6
4 changed files with 39 additions and 22 deletions

View File

@ -1872,7 +1872,7 @@ xfs_swap_extents(
*/ */
lock_two_nondirectories(VFS_I(ip), VFS_I(tip)); lock_two_nondirectories(VFS_I(ip), VFS_I(tip));
lock_flags = XFS_MMAPLOCK_EXCL; lock_flags = XFS_MMAPLOCK_EXCL;
xfs_lock_two_inodes(ip, tip, XFS_MMAPLOCK_EXCL); xfs_lock_two_inodes(ip, XFS_MMAPLOCK_EXCL, tip, XFS_MMAPLOCK_EXCL);
/* Verify that both files have the same format */ /* Verify that both files have the same format */
if ((VFS_I(ip)->i_mode & S_IFMT) != (VFS_I(tip)->i_mode & S_IFMT)) { if ((VFS_I(ip)->i_mode & S_IFMT) != (VFS_I(tip)->i_mode & S_IFMT)) {
@ -1919,7 +1919,7 @@ xfs_swap_extents(
* Lock and join the inodes to the tansaction so that transaction commit * Lock and join the inodes to the tansaction so that transaction commit
* or cancel will unlock the inodes from this point onwards. * or cancel will unlock the inodes from this point onwards.
*/ */
xfs_lock_two_inodes(ip, tip, XFS_ILOCK_EXCL); xfs_lock_two_inodes(ip, XFS_ILOCK_EXCL, tip, XFS_ILOCK_EXCL);
lock_flags |= XFS_ILOCK_EXCL; lock_flags |= XFS_ILOCK_EXCL;
xfs_trans_ijoin(tp, ip, 0); xfs_trans_ijoin(tp, ip, 0);
xfs_trans_ijoin(tp, tip, 0); xfs_trans_ijoin(tp, tip, 0);

View File

@ -546,23 +546,36 @@ again:
/* /*
* xfs_lock_two_inodes() can only be used to lock one type of lock at a time - * xfs_lock_two_inodes() can only be used to lock one type of lock at a time -
* the iolock, the mmaplock or the ilock, but not more than one at a time. If we * the mmaplock or the ilock, but not more than one type at a time. If we lock
* lock more than one at a time, lockdep will report false positives saying we * more than one at a time, lockdep will report false positives saying we have
* have violated locking orders. * violated locking orders. The iolock must be double-locked separately since
* we use i_rwsem for that. We now support taking one lock EXCL and the other
* SHARED.
*/ */
void void
xfs_lock_two_inodes( xfs_lock_two_inodes(
xfs_inode_t *ip0, struct xfs_inode *ip0,
xfs_inode_t *ip1, uint ip0_mode,
uint lock_mode) struct xfs_inode *ip1,
uint ip1_mode)
{ {
xfs_inode_t *temp; struct xfs_inode *temp;
uint mode_temp;
int attempts = 0; int attempts = 0;
xfs_log_item_t *lp; xfs_log_item_t *lp;
ASSERT(!(lock_mode & (XFS_IOLOCK_SHARED|XFS_IOLOCK_EXCL))); ASSERT(hweight32(ip0_mode) == 1);
if (lock_mode & (XFS_MMAPLOCK_SHARED|XFS_MMAPLOCK_EXCL)) ASSERT(hweight32(ip1_mode) == 1);
ASSERT(!(lock_mode & (XFS_ILOCK_SHARED|XFS_ILOCK_EXCL))); ASSERT(!(ip0_mode & (XFS_IOLOCK_SHARED|XFS_IOLOCK_EXCL)));
ASSERT(!(ip1_mode & (XFS_IOLOCK_SHARED|XFS_IOLOCK_EXCL)));
ASSERT(!(ip0_mode & (XFS_MMAPLOCK_SHARED|XFS_MMAPLOCK_EXCL)) ||
!(ip0_mode & (XFS_ILOCK_SHARED|XFS_ILOCK_EXCL)));
ASSERT(!(ip1_mode & (XFS_MMAPLOCK_SHARED|XFS_MMAPLOCK_EXCL)) ||
!(ip1_mode & (XFS_ILOCK_SHARED|XFS_ILOCK_EXCL)));
ASSERT(!(ip1_mode & (XFS_MMAPLOCK_SHARED|XFS_MMAPLOCK_EXCL)) ||
!(ip0_mode & (XFS_ILOCK_SHARED|XFS_ILOCK_EXCL)));
ASSERT(!(ip0_mode & (XFS_MMAPLOCK_SHARED|XFS_MMAPLOCK_EXCL)) ||
!(ip1_mode & (XFS_ILOCK_SHARED|XFS_ILOCK_EXCL)));
ASSERT(ip0->i_ino != ip1->i_ino); ASSERT(ip0->i_ino != ip1->i_ino);
@ -570,10 +583,13 @@ xfs_lock_two_inodes(
temp = ip0; temp = ip0;
ip0 = ip1; ip0 = ip1;
ip1 = temp; ip1 = temp;
mode_temp = ip0_mode;
ip0_mode = ip1_mode;
ip1_mode = mode_temp;
} }
again: again:
xfs_ilock(ip0, xfs_lock_inumorder(lock_mode, 0)); xfs_ilock(ip0, xfs_lock_inumorder(ip0_mode, 0));
/* /*
* If the first lock we have locked is in the AIL, we must TRY to get * If the first lock we have locked is in the AIL, we must TRY to get
@ -582,18 +598,17 @@ xfs_lock_two_inodes(
*/ */
lp = (xfs_log_item_t *)ip0->i_itemp; lp = (xfs_log_item_t *)ip0->i_itemp;
if (lp && (lp->li_flags & XFS_LI_IN_AIL)) { if (lp && (lp->li_flags & XFS_LI_IN_AIL)) {
if (!xfs_ilock_nowait(ip1, xfs_lock_inumorder(lock_mode, 1))) { if (!xfs_ilock_nowait(ip1, xfs_lock_inumorder(ip1_mode, 1))) {
xfs_iunlock(ip0, lock_mode); xfs_iunlock(ip0, ip0_mode);
if ((++attempts % 5) == 0) if ((++attempts % 5) == 0)
delay(1); /* Don't just spin the CPU */ delay(1); /* Don't just spin the CPU */
goto again; goto again;
} }
} else { } else {
xfs_ilock(ip1, xfs_lock_inumorder(lock_mode, 1)); xfs_ilock(ip1, xfs_lock_inumorder(ip1_mode, 1));
} }
} }
void void
__xfs_iflock( __xfs_iflock(
struct xfs_inode *ip) struct xfs_inode *ip)
@ -1421,7 +1436,7 @@ xfs_link(
if (error) if (error)
goto std_return; goto std_return;
xfs_lock_two_inodes(sip, tdp, XFS_ILOCK_EXCL); xfs_lock_two_inodes(sip, XFS_ILOCK_EXCL, tdp, XFS_ILOCK_EXCL);
xfs_trans_ijoin(tp, sip, XFS_ILOCK_EXCL); xfs_trans_ijoin(tp, sip, XFS_ILOCK_EXCL);
xfs_trans_ijoin(tp, tdp, XFS_ILOCK_EXCL); xfs_trans_ijoin(tp, tdp, XFS_ILOCK_EXCL);
@ -2585,7 +2600,7 @@ xfs_remove(
goto std_return; goto std_return;
} }
xfs_lock_two_inodes(dp, ip, XFS_ILOCK_EXCL); xfs_lock_two_inodes(dp, XFS_ILOCK_EXCL, ip, XFS_ILOCK_EXCL);
xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL); xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL);
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL); xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);

View File

@ -423,7 +423,8 @@ void xfs_iunpin_wait(xfs_inode_t *);
#define xfs_ipincount(ip) ((unsigned int) atomic_read(&ip->i_pincount)) #define xfs_ipincount(ip) ((unsigned int) atomic_read(&ip->i_pincount))
int xfs_iflush(struct xfs_inode *, struct xfs_buf **); int xfs_iflush(struct xfs_inode *, struct xfs_buf **);
void xfs_lock_two_inodes(xfs_inode_t *, xfs_inode_t *, uint); void xfs_lock_two_inodes(struct xfs_inode *ip0, uint ip0_mode,
struct xfs_inode *ip1, uint ip1_mode);
xfs_extlen_t xfs_get_extsz_hint(struct xfs_inode *ip); xfs_extlen_t xfs_get_extsz_hint(struct xfs_inode *ip);
xfs_extlen_t xfs_get_cowextsz_hint(struct xfs_inode *ip); xfs_extlen_t xfs_get_cowextsz_hint(struct xfs_inode *ip);

View File

@ -944,7 +944,7 @@ xfs_reflink_set_inode_flag(
if (src->i_ino == dest->i_ino) if (src->i_ino == dest->i_ino)
xfs_ilock(src, XFS_ILOCK_EXCL); xfs_ilock(src, XFS_ILOCK_EXCL);
else else
xfs_lock_two_inodes(src, dest, XFS_ILOCK_EXCL); xfs_lock_two_inodes(src, XFS_ILOCK_EXCL, dest, XFS_ILOCK_EXCL);
if (!xfs_is_reflink_inode(src)) { if (!xfs_is_reflink_inode(src)) {
trace_xfs_reflink_set_inode_flag(src); trace_xfs_reflink_set_inode_flag(src);
@ -1324,7 +1324,8 @@ xfs_reflink_remap_range(
if (same_inode) if (same_inode)
xfs_ilock(src, XFS_MMAPLOCK_EXCL); xfs_ilock(src, XFS_MMAPLOCK_EXCL);
else else
xfs_lock_two_inodes(src, dest, XFS_MMAPLOCK_EXCL); xfs_lock_two_inodes(src, XFS_MMAPLOCK_EXCL, dest,
XFS_MMAPLOCK_EXCL);
/* Check file eligibility and prepare for block sharing. */ /* Check file eligibility and prepare for block sharing. */
ret = -EINVAL; ret = -EINVAL;