Commit 0952c818 authored by Dave Chinner's avatar Dave Chinner Committed by Dave Chinner

xfs: clean up inode lockdep annotations

Lockdep annotations are a maintenance nightmare. Locking has to be
modified to suit the limitations of the annotations, and we're
always having to fix the annotations because they are unable to
express the complexity of locking heirarchies correctly.

So, next up, we've got more issues with lockdep annotations for
inode locking w.r.t. XFS_LOCK_PARENT:

	- lockdep classes are exclusive and can't be ORed together
	  to form new classes.
	- IOLOCK needs multiple PARENT subclasses to express the
	  changes needed for the readdir locking rework needed to
	  stop the endless flow of lockdep false positives involving
	  readdir calling filldir under the ILOCK.
	- there are only 8 unique lockdep subclasses available,
	  so we can't create a generic solution.

IOWs we need to treat the 3-bit space available to each lock type
differently:

	- IOLOCK uses xfs_lock_two_inodes(), so needs:
		- at least 2 IOLOCK subclasses
		- at least 2 IOLOCK_PARENT subclasses
	- MMAPLOCK uses xfs_lock_two_inodes(), so needs:
		- at least 2 MMAPLOCK subclasses
	- ILOCK uses xfs_lock_inodes with up to 5 inodes, so needs:
		- at least 5 ILOCK subclasses
		- one ILOCK_PARENT subclass
		- one RTBITMAP subclass
		- one RTSUM subclass

For the IOLOCK, split the space into two sets of subclasses.
For the MMAPLOCK, just use half the space for the one subclass to
match the non-parent lock classes of the IOLOCK.
For the ILOCK, use 0-4 as the ILOCK subclasses, 5-7 for the
remaining individual subclasses.

Because they are now all different, modify xfs_lock_inumorder() to
handle the nested subclasses, and to assert fail if passed an
invalid subclass. Further, annotate xfs_lock_inodes() to assert fail
if an invalid combination of lock primitives and inode counts are
passed that would result in a lockdep subclass annotation overflow.
Signed-off-by: default avatarDave Chinner <dchinner@redhat.com>
Reviewed-by: default avatarBrian Foster <bfoster@redhat.com>
Signed-off-by: default avatarDave Chinner <david@fromorbit.com>
parent 7df1c170
...@@ -164,7 +164,7 @@ xfs_ilock( ...@@ -164,7 +164,7 @@ xfs_ilock(
(XFS_MMAPLOCK_SHARED | XFS_MMAPLOCK_EXCL)); (XFS_MMAPLOCK_SHARED | XFS_MMAPLOCK_EXCL));
ASSERT((lock_flags & (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)) != ASSERT((lock_flags & (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)) !=
(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 (lock_flags & XFS_IOLOCK_EXCL)
mrupdate_nested(&ip->i_iolock, XFS_IOLOCK_DEP(lock_flags)); mrupdate_nested(&ip->i_iolock, XFS_IOLOCK_DEP(lock_flags));
...@@ -212,7 +212,7 @@ xfs_ilock_nowait( ...@@ -212,7 +212,7 @@ xfs_ilock_nowait(
(XFS_MMAPLOCK_SHARED | XFS_MMAPLOCK_EXCL)); (XFS_MMAPLOCK_SHARED | XFS_MMAPLOCK_EXCL));
ASSERT((lock_flags & (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)) != ASSERT((lock_flags & (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)) !=
(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 (lock_flags & XFS_IOLOCK_EXCL) {
if (!mrtryupdate(&ip->i_iolock)) if (!mrtryupdate(&ip->i_iolock))
...@@ -281,7 +281,7 @@ xfs_iunlock( ...@@ -281,7 +281,7 @@ xfs_iunlock(
(XFS_MMAPLOCK_SHARED | XFS_MMAPLOCK_EXCL)); (XFS_MMAPLOCK_SHARED | XFS_MMAPLOCK_EXCL));
ASSERT((lock_flags & (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)) != ASSERT((lock_flags & (XFS_ILOCK_SHARED | XFS_ILOCK_EXCL)) !=
(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); ASSERT(lock_flags != 0);
if (lock_flags & XFS_IOLOCK_EXCL) if (lock_flags & XFS_IOLOCK_EXCL)
...@@ -364,30 +364,38 @@ int xfs_lock_delays; ...@@ -364,30 +364,38 @@ int xfs_lock_delays;
/* /*
* Bump the subclass so xfs_lock_inodes() acquires each lock with a different * 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 * value. This can be called for any type of inode lock combination, including
* ensure we don't overrun the number of lockdep subclasses for the iolock or * parent locking. Care must be taken to ensure we don't overrun the subclass
* mmaplock as that is limited to 12 by the mmap lock lockdep annotations. * storage fields in the class mask we build.
*/ */
static inline int static inline int
xfs_lock_inumorder(int lock_mode, int subclass) xfs_lock_inumorder(int lock_mode, int subclass)
{ {
int class = 0;
ASSERT(!(lock_mode & (XFS_ILOCK_PARENT | XFS_ILOCK_RTBITMAP |
XFS_ILOCK_RTSUM)));
if (lock_mode & (XFS_IOLOCK_SHARED|XFS_IOLOCK_EXCL)) { if (lock_mode & (XFS_IOLOCK_SHARED|XFS_IOLOCK_EXCL)) {
ASSERT(subclass + XFS_LOCK_INUMORDER < ASSERT(subclass <= XFS_IOLOCK_MAX_SUBCLASS);
(1 << (XFS_MMAPLOCK_SHIFT - XFS_IOLOCK_SHIFT))); ASSERT(subclass + XFS_IOLOCK_PARENT_VAL <
lock_mode |= (subclass + XFS_LOCK_INUMORDER) << XFS_IOLOCK_SHIFT; MAX_LOCKDEP_SUBCLASSES);
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)) { if (lock_mode & (XFS_MMAPLOCK_SHARED|XFS_MMAPLOCK_EXCL)) {
ASSERT(subclass + XFS_LOCK_INUMORDER < ASSERT(subclass <= XFS_MMAPLOCK_MAX_SUBCLASS);
(1 << (XFS_ILOCK_SHIFT - XFS_MMAPLOCK_SHIFT))); class += subclass << XFS_MMAPLOCK_SHIFT;
lock_mode |= (subclass + XFS_LOCK_INUMORDER) <<
XFS_MMAPLOCK_SHIFT;
} }
if (lock_mode & (XFS_ILOCK_SHARED|XFS_ILOCK_EXCL)) if (lock_mode & (XFS_ILOCK_SHARED|XFS_ILOCK_EXCL)) {
lock_mode |= (subclass + XFS_LOCK_INUMORDER) << XFS_ILOCK_SHIFT; ASSERT(subclass <= XFS_ILOCK_MAX_SUBCLASS);
class += subclass << XFS_ILOCK_SHIFT;
}
return lock_mode; return (lock_mode & ~XFS_LOCK_SUBCLASS_MASK) | class;
} }
/* /*
...@@ -399,6 +407,11 @@ xfs_lock_inumorder(int lock_mode, int subclass) ...@@ -399,6 +407,11 @@ xfs_lock_inumorder(int lock_mode, int subclass)
* transaction (such as truncate). This can result in deadlock since the long * 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 * running trans might need to wait for the inode we just locked in order to
* push the tail and free space in the log. * 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 void
xfs_lock_inodes( xfs_lock_inodes(
...@@ -409,8 +422,29 @@ xfs_lock_inodes( ...@@ -409,8 +422,29 @@ xfs_lock_inodes(
int attempts = 0, i, j, try_lock; int attempts = 0, i, j, try_lock;
xfs_log_item_t *lp; 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(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; try_lock = 0;
i = 0; i = 0;
......
...@@ -284,9 +284,9 @@ static inline int xfs_isiflocked(struct xfs_inode *ip) ...@@ -284,9 +284,9 @@ static inline int xfs_isiflocked(struct xfs_inode *ip)
* Flags for lockdep annotations. * Flags for lockdep annotations.
* *
* XFS_LOCK_PARENT - for directory operations that require locking a * XFS_LOCK_PARENT - for directory operations that require locking a
* parent directory inode and a child entry inode. The parent gets locked * parent directory inode and a child entry inode. IOLOCK requires nesting,
* with this flag so it gets a lockdep subclass of 1 and the child entry * MMAPLOCK does not support this class, ILOCK requires a single subclass
* lock will have a lockdep subclass of 0. * to differentiate parent from child.
* *
* XFS_LOCK_RTBITMAP/XFS_LOCK_RTSUM - the realtime device bitmap and summary * XFS_LOCK_RTBITMAP/XFS_LOCK_RTSUM - the realtime device bitmap and summary
* inodes do not participate in the normal lock order, and thus have their * inodes do not participate in the normal lock order, and thus have their
...@@ -295,30 +295,63 @@ static inline int xfs_isiflocked(struct xfs_inode *ip) ...@@ -295,30 +295,63 @@ static inline int xfs_isiflocked(struct xfs_inode *ip)
* XFS_LOCK_INUMORDER - for locking several inodes at the some time * XFS_LOCK_INUMORDER - for locking several inodes at the some time
* with xfs_lock_inodes(). This flag is used as the starting subclass * with xfs_lock_inodes(). This flag is used as the starting subclass
* and each subsequent lock acquired will increment the subclass by one. * and each subsequent lock acquired will increment the subclass by one.
* So the first lock acquired will have a lockdep subclass of 4, the * However, MAX_LOCKDEP_SUBCLASSES == 8, which means we are greatly
* second lock will have a lockdep subclass of 5, and so on. It is * limited to the subclasses we can represent via nesting. We need at least
* the responsibility of the class builder to shift this to the correct * 5 inodes nest depth for the ILOCK through rename, and we also have to support
* portion of the lock_mode lockdep mask. * XFS_ILOCK_PARENT, which gives 6 subclasses. Then we have XFS_ILOCK_RTBITMAP
* and XFS_ILOCK_RTSUM, which are another 2 unique subclasses, so that's all
* 8 subclasses supported by lockdep.
*
* This also means we have to number the sub-classes in the lowest bits of
* the mask we keep, and we have to ensure we never exceed 3 bits of lockdep
* mask and we can't use bit-masking to build the subclasses. What a mess.
*
* Bit layout:
*
* Bit Lock Region
* 16-19 XFS_IOLOCK_SHIFT dependencies
* 20-23 XFS_MMAPLOCK_SHIFT dependencies
* 24-31 XFS_ILOCK_SHIFT dependencies
*
* IOLOCK values
*
* 0-3 subclass value
* 4-7 PARENT subclass values
*
* MMAPLOCK values
*
* 0-3 subclass value
* 4-7 unused
*
* ILOCK values
* 0-4 subclass values
* 5 PARENT subclass (not nestable)
* 6 RTBITMAP subclass (not nestable)
* 7 RTSUM subclass (not nestable)
*
*/ */
#define XFS_LOCK_PARENT 1
#define XFS_LOCK_RTBITMAP 2
#define XFS_LOCK_RTSUM 3
#define XFS_LOCK_INUMORDER 4
#define XFS_IOLOCK_SHIFT 16 #define XFS_IOLOCK_SHIFT 16
#define XFS_IOLOCK_PARENT (XFS_LOCK_PARENT << XFS_IOLOCK_SHIFT) #define XFS_IOLOCK_PARENT_VAL 4
#define XFS_IOLOCK_MAX_SUBCLASS (XFS_IOLOCK_PARENT_VAL - 1)
#define XFS_IOLOCK_DEP_MASK 0x000f0000
#define XFS_IOLOCK_PARENT (XFS_IOLOCK_PARENT_VAL << XFS_IOLOCK_SHIFT)
#define XFS_MMAPLOCK_SHIFT 20 #define XFS_MMAPLOCK_SHIFT 20
#define XFS_MMAPLOCK_NUMORDER 0
#define XFS_MMAPLOCK_MAX_SUBCLASS 3
#define XFS_MMAPLOCK_DEP_MASK 0x00f00000
#define XFS_ILOCK_SHIFT 24 #define XFS_ILOCK_SHIFT 24
#define XFS_ILOCK_PARENT (XFS_LOCK_PARENT << XFS_ILOCK_SHIFT) #define XFS_ILOCK_PARENT_VAL 5
#define XFS_ILOCK_RTBITMAP (XFS_LOCK_RTBITMAP << XFS_ILOCK_SHIFT) #define XFS_ILOCK_MAX_SUBCLASS (XFS_ILOCK_PARENT_VAL - 1)
#define XFS_ILOCK_RTSUM (XFS_LOCK_RTSUM << XFS_ILOCK_SHIFT) #define XFS_ILOCK_RTBITMAP_VAL 6
#define XFS_ILOCK_RTSUM_VAL 7
#define XFS_IOLOCK_DEP_MASK 0x000f0000
#define XFS_MMAPLOCK_DEP_MASK 0x00f00000
#define XFS_ILOCK_DEP_MASK 0xff000000 #define XFS_ILOCK_DEP_MASK 0xff000000
#define XFS_LOCK_DEP_MASK (XFS_IOLOCK_DEP_MASK | \ #define XFS_ILOCK_PARENT (XFS_ILOCK_PARENT_VAL << XFS_ILOCK_SHIFT)
#define XFS_ILOCK_RTBITMAP (XFS_ILOCK_RTBITMAP_VAL << XFS_ILOCK_SHIFT)
#define XFS_ILOCK_RTSUM (XFS_ILOCK_RTSUM_VAL << XFS_ILOCK_SHIFT)
#define XFS_LOCK_SUBCLASS_MASK (XFS_IOLOCK_DEP_MASK | \
XFS_MMAPLOCK_DEP_MASK | \ XFS_MMAPLOCK_DEP_MASK | \
XFS_ILOCK_DEP_MASK) XFS_ILOCK_DEP_MASK)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment