diff options
author | J. Bruce Fields <bfields@redhat.com> | 2012-04-18 15:16:33 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2013-11-09 00:16:39 -0500 |
commit | 375e289ea85166c5241c570940e7e7e966c63a9f (patch) | |
tree | 406c6701e7a45a809114bfb925f474bdd20d252a | |
parent | f27c9298fd717e1f7e63e314a7a85a3a7e77139d (diff) |
vfs: pull ext4's double-i_mutex-locking into common code
We want to do this elsewhere as well.
Also catch any attempts to use it for directories (where this ordering
would conflict with ancestor-first directory ordering in lock_rename).
Cc: Andreas Dilger <adilger.kernel@dilger.ca>
Cc: Dave Chinner <david@fromorbit.com>
Acked-by: Jeff Layton <jlayton@redhat.com>
Acked-by: "Theodore Ts'o" <tytso@mit.edu>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | fs/ext4/ext4.h | 2 | ||||
-rw-r--r-- | fs/ext4/ioctl.c | 4 | ||||
-rw-r--r-- | fs/ext4/move_extent.c | 40 | ||||
-rw-r--r-- | fs/inode.c | 36 | ||||
-rw-r--r-- | include/linux/fs.h | 3 |
5 files changed, 43 insertions, 42 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index af815ea9d7cc..d01d62315f7e 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -2734,8 +2734,6 @@ extern void ext4_double_down_write_data_sem(struct inode *first, | |||
2734 | struct inode *second); | 2734 | struct inode *second); |
2735 | extern void ext4_double_up_write_data_sem(struct inode *orig_inode, | 2735 | extern void ext4_double_up_write_data_sem(struct inode *orig_inode, |
2736 | struct inode *donor_inode); | 2736 | struct inode *donor_inode); |
2737 | void ext4_inode_double_lock(struct inode *inode1, struct inode *inode2); | ||
2738 | void ext4_inode_double_unlock(struct inode *inode1, struct inode *inode2); | ||
2739 | extern int ext4_move_extents(struct file *o_filp, struct file *d_filp, | 2737 | extern int ext4_move_extents(struct file *o_filp, struct file *d_filp, |
2740 | __u64 start_orig, __u64 start_donor, | 2738 | __u64 start_orig, __u64 start_donor, |
2741 | __u64 len, __u64 *moved_len); | 2739 | __u64 len, __u64 *moved_len); |
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index a569d335f804..60589b60e9b0 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c | |||
@@ -130,7 +130,7 @@ static long swap_inode_boot_loader(struct super_block *sb, | |||
130 | 130 | ||
131 | /* Protect orig inodes against a truncate and make sure, | 131 | /* Protect orig inodes against a truncate and make sure, |
132 | * that only 1 swap_inode_boot_loader is running. */ | 132 | * that only 1 swap_inode_boot_loader is running. */ |
133 | ext4_inode_double_lock(inode, inode_bl); | 133 | lock_two_nondirectories(inode, inode_bl); |
134 | 134 | ||
135 | truncate_inode_pages(&inode->i_data, 0); | 135 | truncate_inode_pages(&inode->i_data, 0); |
136 | truncate_inode_pages(&inode_bl->i_data, 0); | 136 | truncate_inode_pages(&inode_bl->i_data, 0); |
@@ -205,7 +205,7 @@ static long swap_inode_boot_loader(struct super_block *sb, | |||
205 | ext4_inode_resume_unlocked_dio(inode); | 205 | ext4_inode_resume_unlocked_dio(inode); |
206 | ext4_inode_resume_unlocked_dio(inode_bl); | 206 | ext4_inode_resume_unlocked_dio(inode_bl); |
207 | 207 | ||
208 | ext4_inode_double_unlock(inode, inode_bl); | 208 | unlock_two_nondirectories(inode, inode_bl); |
209 | 209 | ||
210 | iput(inode_bl); | 210 | iput(inode_bl); |
211 | 211 | ||
diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c index 7fa4d855dbd5..773b503bd18c 100644 --- a/fs/ext4/move_extent.c +++ b/fs/ext4/move_extent.c | |||
@@ -1203,42 +1203,6 @@ mext_check_arguments(struct inode *orig_inode, | |||
1203 | } | 1203 | } |
1204 | 1204 | ||
1205 | /** | 1205 | /** |
1206 | * ext4_inode_double_lock - Lock i_mutex on both @inode1 and @inode2 | ||
1207 | * | ||
1208 | * @inode1: the inode structure | ||
1209 | * @inode2: the inode structure | ||
1210 | * | ||
1211 | * Lock two inodes' i_mutex | ||
1212 | */ | ||
1213 | void | ||
1214 | ext4_inode_double_lock(struct inode *inode1, struct inode *inode2) | ||
1215 | { | ||
1216 | BUG_ON(inode1 == inode2); | ||
1217 | if (inode1 < inode2) { | ||
1218 | mutex_lock_nested(&inode1->i_mutex, I_MUTEX_PARENT); | ||
1219 | mutex_lock_nested(&inode2->i_mutex, I_MUTEX_CHILD); | ||
1220 | } else { | ||
1221 | mutex_lock_nested(&inode2->i_mutex, I_MUTEX_PARENT); | ||
1222 | mutex_lock_nested(&inode1->i_mutex, I_MUTEX_CHILD); | ||
1223 | } | ||
1224 | } | ||
1225 | |||
1226 | /** | ||
1227 | * ext4_inode_double_unlock - Release i_mutex on both @inode1 and @inode2 | ||
1228 | * | ||
1229 | * @inode1: the inode that is released first | ||
1230 | * @inode2: the inode that is released second | ||
1231 | * | ||
1232 | */ | ||
1233 | |||
1234 | void | ||
1235 | ext4_inode_double_unlock(struct inode *inode1, struct inode *inode2) | ||
1236 | { | ||
1237 | mutex_unlock(&inode1->i_mutex); | ||
1238 | mutex_unlock(&inode2->i_mutex); | ||
1239 | } | ||
1240 | |||
1241 | /** | ||
1242 | * ext4_move_extents - Exchange the specified range of a file | 1206 | * ext4_move_extents - Exchange the specified range of a file |
1243 | * | 1207 | * |
1244 | * @o_filp: file structure of the original file | 1208 | * @o_filp: file structure of the original file |
@@ -1327,7 +1291,7 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, | |||
1327 | return -EINVAL; | 1291 | return -EINVAL; |
1328 | } | 1292 | } |
1329 | /* Protect orig and donor inodes against a truncate */ | 1293 | /* Protect orig and donor inodes against a truncate */ |
1330 | ext4_inode_double_lock(orig_inode, donor_inode); | 1294 | lock_two_nondirectories(orig_inode, donor_inode); |
1331 | 1295 | ||
1332 | /* Wait for all existing dio workers */ | 1296 | /* Wait for all existing dio workers */ |
1333 | ext4_inode_block_unlocked_dio(orig_inode); | 1297 | ext4_inode_block_unlocked_dio(orig_inode); |
@@ -1535,7 +1499,7 @@ out: | |||
1535 | ext4_double_up_write_data_sem(orig_inode, donor_inode); | 1499 | ext4_double_up_write_data_sem(orig_inode, donor_inode); |
1536 | ext4_inode_resume_unlocked_dio(orig_inode); | 1500 | ext4_inode_resume_unlocked_dio(orig_inode); |
1537 | ext4_inode_resume_unlocked_dio(donor_inode); | 1501 | ext4_inode_resume_unlocked_dio(donor_inode); |
1538 | ext4_inode_double_unlock(orig_inode, donor_inode); | 1502 | unlock_two_nondirectories(orig_inode, donor_inode); |
1539 | 1503 | ||
1540 | return ret; | 1504 | return ret; |
1541 | } | 1505 | } |
diff --git a/fs/inode.c b/fs/inode.c index bf7a8b3ab937..ef25afdf3906 100644 --- a/fs/inode.c +++ b/fs/inode.c | |||
@@ -943,6 +943,42 @@ void unlock_new_inode(struct inode *inode) | |||
943 | EXPORT_SYMBOL(unlock_new_inode); | 943 | EXPORT_SYMBOL(unlock_new_inode); |
944 | 944 | ||
945 | /** | 945 | /** |
946 | * lock_two_nondirectories - take two i_mutexes on non-directory objects | ||
947 | * @inode1: first inode to lock | ||
948 | * @inode2: second inode to lock | ||
949 | */ | ||
950 | void lock_two_nondirectories(struct inode *inode1, struct inode *inode2) | ||
951 | { | ||
952 | WARN_ON_ONCE(S_ISDIR(inode1->i_mode)); | ||
953 | if (inode1 == inode2 || !inode2) { | ||
954 | mutex_lock_nested(&inode1->i_mutex, I_MUTEX_PARENT); | ||
955 | return; | ||
956 | } | ||
957 | WARN_ON_ONCE(S_ISDIR(inode2->i_mode)); | ||
958 | if (inode1 < inode2) { | ||
959 | mutex_lock_nested(&inode1->i_mutex, I_MUTEX_PARENT); | ||
960 | mutex_lock_nested(&inode2->i_mutex, I_MUTEX_CHILD); | ||
961 | } else { | ||
962 | mutex_lock_nested(&inode2->i_mutex, I_MUTEX_PARENT); | ||
963 | mutex_lock_nested(&inode1->i_mutex, I_MUTEX_CHILD); | ||
964 | } | ||
965 | } | ||
966 | EXPORT_SYMBOL(lock_two_nondirectories); | ||
967 | |||
968 | /** | ||
969 | * unlock_two_nondirectories - release locks from lock_two_nondirectories() | ||
970 | * @inode1: first inode to unlock | ||
971 | * @inode2: second inode to unlock | ||
972 | */ | ||
973 | void unlock_two_nondirectories(struct inode *inode1, struct inode *inode2) | ||
974 | { | ||
975 | mutex_unlock(&inode1->i_mutex); | ||
976 | if (inode2 && inode2 != inode1) | ||
977 | mutex_unlock(&inode2->i_mutex); | ||
978 | } | ||
979 | EXPORT_SYMBOL(unlock_two_nondirectories); | ||
980 | |||
981 | /** | ||
946 | * iget5_locked - obtain an inode from a mounted file system | 982 | * iget5_locked - obtain an inode from a mounted file system |
947 | * @sb: super block of file system | 983 | * @sb: super block of file system |
948 | * @hashval: hash value (usually inode number) to get | 984 | * @hashval: hash value (usually inode number) to get |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 5e44b0893db8..4e1a0b41f966 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -637,6 +637,9 @@ enum inode_i_mutex_lock_class | |||
637 | I_MUTEX_QUOTA | 637 | I_MUTEX_QUOTA |
638 | }; | 638 | }; |
639 | 639 | ||
640 | void lock_two_nondirectories(struct inode *, struct inode*); | ||
641 | void unlock_two_nondirectories(struct inode *, struct inode*); | ||
642 | |||
640 | /* | 643 | /* |
641 | * NOTE: in a 32bit arch with a preemptable kernel and | 644 | * NOTE: in a 32bit arch with a preemptable kernel and |
642 | * an UP compile the i_size_read/write must be atomic | 645 | * an UP compile the i_size_read/write must be atomic |