|
@@ -164,7 +164,7 @@ xfs_ilock(
|
|
|
(XFS_MMAPLOCK_SHARED | XFS_MMAPLOCK_EXCL));
|
|
|
ASSERT((lock_flags & (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)) !=
|
|
|
(XFS_ILOCK_SHARED | XFS_ILOCK_EXCL));
|
|
|
- ASSERT((lock_flags & ~(XFS_LOCK_MASK | XFS_LOCK_DEP_MASK)) == 0);
|
|
|
+ ASSERT((lock_flags & ~(XFS_LOCK_MASK | XFS_LOCK_SUBCLASS_MASK)) == 0);
|
|
|
|
|
|
if (lock_flags & XFS_IOLOCK_EXCL)
|
|
|
mrupdate_nested(&ip->i_iolock, XFS_IOLOCK_DEP(lock_flags));
|
|
@@ -212,7 +212,7 @@ xfs_ilock_nowait(
|
|
|
(XFS_MMAPLOCK_SHARED | XFS_MMAPLOCK_EXCL));
|
|
|
ASSERT((lock_flags & (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)) !=
|
|
|
(XFS_ILOCK_SHARED | XFS_ILOCK_EXCL));
|
|
|
- ASSERT((lock_flags & ~(XFS_LOCK_MASK | XFS_LOCK_DEP_MASK)) == 0);
|
|
|
+ ASSERT((lock_flags & ~(XFS_LOCK_MASK | XFS_LOCK_SUBCLASS_MASK)) == 0);
|
|
|
|
|
|
if (lock_flags & XFS_IOLOCK_EXCL) {
|
|
|
if (!mrtryupdate(&ip->i_iolock))
|
|
@@ -281,7 +281,7 @@ xfs_iunlock(
|
|
|
(XFS_MMAPLOCK_SHARED | XFS_MMAPLOCK_EXCL));
|
|
|
ASSERT((lock_flags & (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)) !=
|
|
|
(XFS_ILOCK_SHARED | XFS_ILOCK_EXCL));
|
|
|
- ASSERT((lock_flags & ~(XFS_LOCK_MASK | XFS_LOCK_DEP_MASK)) == 0);
|
|
|
+ ASSERT((lock_flags & ~(XFS_LOCK_MASK | XFS_LOCK_SUBCLASS_MASK)) == 0);
|
|
|
ASSERT(lock_flags != 0);
|
|
|
|
|
|
if (lock_flags & XFS_IOLOCK_EXCL)
|
|
@@ -362,32 +362,52 @@ int xfs_lots_retries;
|
|
|
int xfs_lock_delays;
|
|
|
#endif
|
|
|
|
|
|
+#ifdef CONFIG_LOCKDEP
|
|
|
+static bool
|
|
|
+xfs_lockdep_subclass_ok(
|
|
|
+ int subclass)
|
|
|
+{
|
|
|
+ return subclass < MAX_LOCKDEP_SUBCLASSES;
|
|
|
+}
|
|
|
+#else
|
|
|
+#define xfs_lockdep_subclass_ok(subclass) (true)
|
|
|
+#endif
|
|
|
+
|
|
|
/*
|
|
|
* Bump the subclass so xfs_lock_inodes() acquires each lock with a different
|
|
|
- * value. This shouldn't be called for page fault locking, but we also need to
|
|
|
- * ensure we don't overrun the number of lockdep subclasses for the iolock or
|
|
|
- * mmaplock as that is limited to 12 by the mmap lock lockdep annotations.
|
|
|
+ * value. This can be called for any type of inode lock combination, including
|
|
|
+ * parent locking. Care must be taken to ensure we don't overrun the subclass
|
|
|
+ * storage fields in the class mask we build.
|
|
|
*/
|
|
|
static inline int
|
|
|
xfs_lock_inumorder(int lock_mode, int subclass)
|
|
|
{
|
|
|
+ int class = 0;
|
|
|
+
|
|
|
+ ASSERT(!(lock_mode & (XFS_ILOCK_PARENT | XFS_ILOCK_RTBITMAP |
|
|
|
+ XFS_ILOCK_RTSUM)));
|
|
|
+ ASSERT(xfs_lockdep_subclass_ok(subclass));
|
|
|
+
|
|
|
if (lock_mode & (XFS_IOLOCK_SHARED|XFS_IOLOCK_EXCL)) {
|
|
|
- ASSERT(subclass + XFS_LOCK_INUMORDER <
|
|
|
- (1 << (XFS_MMAPLOCK_SHIFT - XFS_IOLOCK_SHIFT)));
|
|
|
- lock_mode |= (subclass + XFS_LOCK_INUMORDER) << XFS_IOLOCK_SHIFT;
|
|
|
+ ASSERT(subclass <= XFS_IOLOCK_MAX_SUBCLASS);
|
|
|
+ ASSERT(xfs_lockdep_subclass_ok(subclass +
|
|
|
+ XFS_IOLOCK_PARENT_VAL));
|
|
|
+ class += subclass << XFS_IOLOCK_SHIFT;
|
|
|
+ if (lock_mode & XFS_IOLOCK_PARENT)
|
|
|
+ class += XFS_IOLOCK_PARENT_VAL << XFS_IOLOCK_SHIFT;
|
|
|
}
|
|
|
|
|
|
if (lock_mode & (XFS_MMAPLOCK_SHARED|XFS_MMAPLOCK_EXCL)) {
|
|
|
- ASSERT(subclass + XFS_LOCK_INUMORDER <
|
|
|
- (1 << (XFS_ILOCK_SHIFT - XFS_MMAPLOCK_SHIFT)));
|
|
|
- lock_mode |= (subclass + XFS_LOCK_INUMORDER) <<
|
|
|
- XFS_MMAPLOCK_SHIFT;
|
|
|
+ ASSERT(subclass <= XFS_MMAPLOCK_MAX_SUBCLASS);
|
|
|
+ class += subclass << XFS_MMAPLOCK_SHIFT;
|
|
|
}
|
|
|
|
|
|
- if (lock_mode & (XFS_ILOCK_SHARED|XFS_ILOCK_EXCL))
|
|
|
- lock_mode |= (subclass + XFS_LOCK_INUMORDER) << XFS_ILOCK_SHIFT;
|
|
|
+ if (lock_mode & (XFS_ILOCK_SHARED|XFS_ILOCK_EXCL)) {
|
|
|
+ ASSERT(subclass <= XFS_ILOCK_MAX_SUBCLASS);
|
|
|
+ class += subclass << XFS_ILOCK_SHIFT;
|
|
|
+ }
|
|
|
|
|
|
- return lock_mode;
|
|
|
+ return (lock_mode & ~XFS_LOCK_SUBCLASS_MASK) | class;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -399,6 +419,11 @@ xfs_lock_inumorder(int lock_mode, int subclass)
|
|
|
* transaction (such as truncate). This can result in deadlock since the long
|
|
|
* running trans might need to wait for the inode we just locked in order to
|
|
|
* push the tail and free space in the log.
|
|
|
+ *
|
|
|
+ * xfs_lock_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
|
|
|
+ * lock more than one at a time, lockdep will report false positives saying we
|
|
|
+ * have violated locking orders.
|
|
|
*/
|
|
|
void
|
|
|
xfs_lock_inodes(
|
|
@@ -409,8 +434,29 @@ xfs_lock_inodes(
|
|
|
int attempts = 0, i, j, try_lock;
|
|
|
xfs_log_item_t *lp;
|
|
|
|
|
|
- /* currently supports between 2 and 5 inodes */
|
|
|
+ /*
|
|
|
+ * Currently supports between 2 and 5 inodes with exclusive locking. We
|
|
|
+ * support an arbitrary depth of locking here, but absolute limits on
|
|
|
+ * inodes depend on the the type of locking and the limits placed by
|
|
|
+ * lockdep annotations in xfs_lock_inumorder. These are all checked by
|
|
|
+ * the asserts.
|
|
|
+ */
|
|
|
ASSERT(ips && inodes >= 2 && inodes <= 5);
|
|
|
+ ASSERT(lock_mode & (XFS_IOLOCK_EXCL | XFS_MMAPLOCK_EXCL |
|
|
|
+ XFS_ILOCK_EXCL));
|
|
|
+ ASSERT(!(lock_mode & (XFS_IOLOCK_SHARED | XFS_MMAPLOCK_SHARED |
|
|
|
+ XFS_ILOCK_SHARED)));
|
|
|
+ ASSERT(!(lock_mode & XFS_IOLOCK_EXCL) ||
|
|
|
+ inodes <= XFS_IOLOCK_MAX_SUBCLASS + 1);
|
|
|
+ ASSERT(!(lock_mode & XFS_MMAPLOCK_EXCL) ||
|
|
|
+ inodes <= XFS_MMAPLOCK_MAX_SUBCLASS + 1);
|
|
|
+ ASSERT(!(lock_mode & XFS_ILOCK_EXCL) ||
|
|
|
+ inodes <= XFS_ILOCK_MAX_SUBCLASS + 1);
|
|
|
+
|
|
|
+ if (lock_mode & XFS_IOLOCK_EXCL) {
|
|
|
+ ASSERT(!(lock_mode & (XFS_MMAPLOCK_EXCL | XFS_ILOCK_EXCL)));
|
|
|
+ } else if (lock_mode & XFS_MMAPLOCK_EXCL)
|
|
|
+ ASSERT(!(lock_mode & XFS_ILOCK_EXCL));
|
|
|
|
|
|
try_lock = 0;
|
|
|
i = 0;
|
|
@@ -629,30 +675,29 @@ xfs_lookup(
|
|
|
{
|
|
|
xfs_ino_t inum;
|
|
|
int error;
|
|
|
- uint lock_mode;
|
|
|
|
|
|
trace_xfs_lookup(dp, name);
|
|
|
|
|
|
if (XFS_FORCED_SHUTDOWN(dp->i_mount))
|
|
|
return -EIO;
|
|
|
|
|
|
- lock_mode = xfs_ilock_data_map_shared(dp);
|
|
|
+ xfs_ilock(dp, XFS_IOLOCK_SHARED);
|
|
|
error = xfs_dir_lookup(NULL, dp, name, &inum, ci_name);
|
|
|
- xfs_iunlock(dp, lock_mode);
|
|
|
-
|
|
|
if (error)
|
|
|
- goto out;
|
|
|
+ goto out_unlock;
|
|
|
|
|
|
error = xfs_iget(dp->i_mount, NULL, inum, 0, 0, ipp);
|
|
|
if (error)
|
|
|
goto out_free_name;
|
|
|
|
|
|
+ xfs_iunlock(dp, XFS_IOLOCK_SHARED);
|
|
|
return 0;
|
|
|
|
|
|
out_free_name:
|
|
|
if (ci_name)
|
|
|
kmem_free(ci_name->name);
|
|
|
-out:
|
|
|
+out_unlock:
|
|
|
+ xfs_iunlock(dp, XFS_IOLOCK_SHARED);
|
|
|
*ipp = NULL;
|
|
|
return error;
|
|
|
}
|
|
@@ -787,7 +832,7 @@ xfs_ialloc(
|
|
|
|
|
|
if (ip->i_d.di_version == 3) {
|
|
|
ASSERT(ip->i_d.di_ino == ino);
|
|
|
- ASSERT(uuid_equal(&ip->i_d.di_uuid, &mp->m_sb.sb_uuid));
|
|
|
+ ASSERT(uuid_equal(&ip->i_d.di_uuid, &mp->m_sb.sb_meta_uuid));
|
|
|
ip->i_d.di_crc = 0;
|
|
|
ip->i_d.di_changecount = 1;
|
|
|
ip->i_d.di_lsn = 0;
|
|
@@ -1149,7 +1194,8 @@ xfs_create(
|
|
|
goto out_trans_cancel;
|
|
|
|
|
|
|
|
|
- xfs_ilock(dp, XFS_ILOCK_EXCL | XFS_ILOCK_PARENT);
|
|
|
+ xfs_ilock(dp, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL |
|
|
|
+ XFS_IOLOCK_PARENT | XFS_ILOCK_PARENT);
|
|
|
unlock_dp_on_error = true;
|
|
|
|
|
|
xfs_bmap_init(&free_list, &first_block);
|
|
@@ -1185,7 +1231,7 @@ xfs_create(
|
|
|
* the transaction cancel unlocking dp so don't do it explicitly in the
|
|
|
* error path.
|
|
|
*/
|
|
|
- xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL);
|
|
|
+ xfs_trans_ijoin(tp, dp, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);
|
|
|
unlock_dp_on_error = false;
|
|
|
|
|
|
error = xfs_dir_createname(tp, dp, name, ip->i_ino,
|
|
@@ -1258,7 +1304,7 @@ xfs_create(
|
|
|
xfs_qm_dqrele(pdqp);
|
|
|
|
|
|
if (unlock_dp_on_error)
|
|
|
- xfs_iunlock(dp, XFS_ILOCK_EXCL);
|
|
|
+ xfs_iunlock(dp, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);
|
|
|
return error;
|
|
|
}
|
|
|
|
|
@@ -1403,10 +1449,11 @@ xfs_link(
|
|
|
if (error)
|
|
|
goto error_return;
|
|
|
|
|
|
+ xfs_ilock(tdp, XFS_IOLOCK_EXCL | XFS_IOLOCK_PARENT);
|
|
|
xfs_lock_two_inodes(sip, tdp, 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_IOLOCK_EXCL | XFS_ILOCK_EXCL);
|
|
|
|
|
|
/*
|
|
|
* If we are using project inheritance, we only allow hard link
|
|
@@ -2510,9 +2557,10 @@ xfs_remove(
|
|
|
goto out_trans_cancel;
|
|
|
}
|
|
|
|
|
|
+ xfs_ilock(dp, XFS_IOLOCK_EXCL | XFS_IOLOCK_PARENT);
|
|
|
xfs_lock_two_inodes(dp, ip, XFS_ILOCK_EXCL);
|
|
|
|
|
|
- xfs_trans_ijoin(tp, dp, XFS_ILOCK_EXCL);
|
|
|
+ xfs_trans_ijoin(tp, dp, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);
|
|
|
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
|
|
|
|
|
|
/*
|
|
@@ -2893,6 +2941,12 @@ xfs_rename(
|
|
|
* whether the target directory is the same as the source
|
|
|
* directory, we can lock from 2 to 4 inodes.
|
|
|
*/
|
|
|
+ if (!new_parent)
|
|
|
+ xfs_ilock(src_dp, XFS_IOLOCK_EXCL | XFS_IOLOCK_PARENT);
|
|
|
+ else
|
|
|
+ xfs_lock_two_inodes(src_dp, target_dp,
|
|
|
+ XFS_IOLOCK_EXCL | XFS_IOLOCK_PARENT);
|
|
|
+
|
|
|
xfs_lock_inodes(inodes, num_inodes, XFS_ILOCK_EXCL);
|
|
|
|
|
|
/*
|
|
@@ -2900,9 +2954,9 @@ xfs_rename(
|
|
|
* we can rely on either trans_commit or trans_cancel to unlock
|
|
|
* them.
|
|
|
*/
|
|
|
- xfs_trans_ijoin(tp, src_dp, XFS_ILOCK_EXCL);
|
|
|
+ xfs_trans_ijoin(tp, src_dp, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);
|
|
|
if (new_parent)
|
|
|
- xfs_trans_ijoin(tp, target_dp, XFS_ILOCK_EXCL);
|
|
|
+ xfs_trans_ijoin(tp, target_dp, XFS_IOLOCK_EXCL | XFS_ILOCK_EXCL);
|
|
|
xfs_trans_ijoin(tp, src_ip, XFS_ILOCK_EXCL);
|
|
|
if (target_ip)
|
|
|
xfs_trans_ijoin(tp, target_ip, XFS_ILOCK_EXCL);
|