Skip to content

Commit f23ce75

Browse files
jankarabrauner
authored andcommitted
fs: Establish locking order for unrelated directories
Currently the locking order of inode locks for directories that are not in ancestor relationship is not defined because all operations that needed to lock two directories like this were serialized by sb->s_vfs_rename_mutex. However some filesystems need to lock two subdirectories for RENAME_EXCHANGE operations and for this we need the locking order established even for two tree-unrelated directories. Provide a helper function lock_two_inodes() that establishes lock ordering for any two inodes and use it in lock_two_directories(). CC: [email protected] Signed-off-by: Jan Kara <[email protected]> Message-Id: <[email protected]> Signed-off-by: Christian Brauner <[email protected]>
1 parent cde3c9d commit f23ce75

File tree

3 files changed

+46
-2
lines changed

3 files changed

+46
-2
lines changed

fs/inode.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,48 @@ void discard_new_inode(struct inode *inode)
11031103
}
11041104
EXPORT_SYMBOL(discard_new_inode);
11051105

1106+
/**
1107+
* lock_two_inodes - lock two inodes (may be regular files but also dirs)
1108+
*
1109+
* Lock any non-NULL argument. The caller must make sure that if he is passing
1110+
* in two directories, one is not ancestor of the other. Zero, one or two
1111+
* objects may be locked by this function.
1112+
*
1113+
* @inode1: first inode to lock
1114+
* @inode2: second inode to lock
1115+
* @subclass1: inode lock subclass for the first lock obtained
1116+
* @subclass2: inode lock subclass for the second lock obtained
1117+
*/
1118+
void lock_two_inodes(struct inode *inode1, struct inode *inode2,
1119+
unsigned subclass1, unsigned subclass2)
1120+
{
1121+
if (!inode1 || !inode2) {
1122+
/*
1123+
* Make sure @subclass1 will be used for the acquired lock.
1124+
* This is not strictly necessary (no current caller cares) but
1125+
* let's keep things consistent.
1126+
*/
1127+
if (!inode1)
1128+
swap(inode1, inode2);
1129+
goto lock;
1130+
}
1131+
1132+
/*
1133+
* If one object is directory and the other is not, we must make sure
1134+
* to lock directory first as the other object may be its child.
1135+
*/
1136+
if (S_ISDIR(inode2->i_mode) == S_ISDIR(inode1->i_mode)) {
1137+
if (inode1 > inode2)
1138+
swap(inode1, inode2);
1139+
} else if (!S_ISDIR(inode1->i_mode))
1140+
swap(inode1, inode2);
1141+
lock:
1142+
if (inode1)
1143+
inode_lock_nested(inode1, subclass1);
1144+
if (inode2 && inode2 != inode1)
1145+
inode_lock_nested(inode2, subclass2);
1146+
}
1147+
11061148
/**
11071149
* lock_two_nondirectories - take two i_mutexes on non-directory objects
11081150
*

fs/internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ extern long prune_icache_sb(struct super_block *sb, struct shrink_control *sc);
152152
int dentry_needs_remove_privs(struct mnt_idmap *, struct dentry *dentry);
153153
bool in_group_or_capable(struct mnt_idmap *idmap,
154154
const struct inode *inode, vfsgid_t vfsgid);
155+
void lock_two_inodes(struct inode *inode1, struct inode *inode2,
156+
unsigned subclass1, unsigned subclass2);
155157

156158
/*
157159
* fs-writeback.c

fs/namei.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3028,8 +3028,8 @@ static struct dentry *lock_two_directories(struct dentry *p1, struct dentry *p2)
30283028
return p;
30293029
}
30303030

3031-
inode_lock_nested(p1->d_inode, I_MUTEX_PARENT);
3032-
inode_lock_nested(p2->d_inode, I_MUTEX_PARENT2);
3031+
lock_two_inodes(p1->d_inode, p2->d_inode,
3032+
I_MUTEX_PARENT, I_MUTEX_PARENT2);
30333033
return NULL;
30343034
}
30353035

0 commit comments

Comments
 (0)