diff options
author | Dmitry Monakhov <dmonakhov@openvz.org> | 2012-09-26 12:32:19 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2012-09-26 12:32:19 -0400 |
commit | 03bd8b9b896c8e940f282f540e6b4de90d666b7c (patch) | |
tree | 76e770537d52af02d3664c296560b3d0d708ab39 /fs/ext4 | |
parent | 0acdb8876fead922c9ffa6768c5675a37485c48c (diff) |
ext4: move_extent code cleanup
- Remove usless checks, because it is too late to check that inode != NULL
at the moment it was referenced several times.
- Double lock routines looks very ugly and locking ordering relays on
order of i_ino, but other kernel code rely on order of pointers.
Let's make them simple and clean.
- check that inodes belongs to the same SB as soon as possible.
Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Cc: stable@vger.kernel.org
Diffstat (limited to 'fs/ext4')
-rw-r--r-- | fs/ext4/move_extent.c | 167 |
1 files changed, 47 insertions, 120 deletions
diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c index c5826c623e7a..df5cde5130c5 100644 --- a/fs/ext4/move_extent.c +++ b/fs/ext4/move_extent.c | |||
@@ -141,55 +141,21 @@ mext_next_extent(struct inode *inode, struct ext4_ext_path *path, | |||
141 | } | 141 | } |
142 | 142 | ||
143 | /** | 143 | /** |
144 | * mext_check_null_inode - NULL check for two inodes | ||
145 | * | ||
146 | * If inode1 or inode2 is NULL, return -EIO. Otherwise, return 0. | ||
147 | */ | ||
148 | static int | ||
149 | mext_check_null_inode(struct inode *inode1, struct inode *inode2, | ||
150 | const char *function, unsigned int line) | ||
151 | { | ||
152 | int ret = 0; | ||
153 | |||
154 | if (inode1 == NULL) { | ||
155 | __ext4_error(inode2->i_sb, function, line, | ||
156 | "Both inodes should not be NULL: " | ||
157 | "inode1 NULL inode2 %lu", inode2->i_ino); | ||
158 | ret = -EIO; | ||
159 | } else if (inode2 == NULL) { | ||
160 | __ext4_error(inode1->i_sb, function, line, | ||
161 | "Both inodes should not be NULL: " | ||
162 | "inode1 %lu inode2 NULL", inode1->i_ino); | ||
163 | ret = -EIO; | ||
164 | } | ||
165 | return ret; | ||
166 | } | ||
167 | |||
168 | /** | ||
169 | * double_down_write_data_sem - Acquire two inodes' write lock of i_data_sem | 144 | * double_down_write_data_sem - Acquire two inodes' write lock of i_data_sem |
170 | * | 145 | * |
171 | * @orig_inode: original inode structure | 146 | * Acquire write lock of i_data_sem of the two inodes |
172 | * @donor_inode: donor inode structure | ||
173 | * Acquire write lock of i_data_sem of the two inodes (orig and donor) by | ||
174 | * i_ino order. | ||
175 | */ | 147 | */ |
176 | static void | 148 | static void |
177 | double_down_write_data_sem(struct inode *orig_inode, struct inode *donor_inode) | 149 | double_down_write_data_sem(struct inode *first, struct inode *second) |
178 | { | 150 | { |
179 | struct inode *first = orig_inode, *second = donor_inode; | 151 | if (first < second) { |
152 | down_write(&EXT4_I(first)->i_data_sem); | ||
153 | down_write_nested(&EXT4_I(second)->i_data_sem, SINGLE_DEPTH_NESTING); | ||
154 | } else { | ||
155 | down_write(&EXT4_I(second)->i_data_sem); | ||
156 | down_write_nested(&EXT4_I(first)->i_data_sem, SINGLE_DEPTH_NESTING); | ||
180 | 157 | ||
181 | /* | ||
182 | * Use the inode number to provide the stable locking order instead | ||
183 | * of its address, because the C language doesn't guarantee you can | ||
184 | * compare pointers that don't come from the same array. | ||
185 | */ | ||
186 | if (donor_inode->i_ino < orig_inode->i_ino) { | ||
187 | first = donor_inode; | ||
188 | second = orig_inode; | ||
189 | } | 158 | } |
190 | |||
191 | down_write(&EXT4_I(first)->i_data_sem); | ||
192 | down_write_nested(&EXT4_I(second)->i_data_sem, SINGLE_DEPTH_NESTING); | ||
193 | } | 159 | } |
194 | 160 | ||
195 | /** | 161 | /** |
@@ -969,14 +935,6 @@ mext_check_arguments(struct inode *orig_inode, | |||
969 | return -EINVAL; | 935 | return -EINVAL; |
970 | } | 936 | } |
971 | 937 | ||
972 | /* Files should be in the same ext4 FS */ | ||
973 | if (orig_inode->i_sb != donor_inode->i_sb) { | ||
974 | ext4_debug("ext4 move extent: The argument files " | ||
975 | "should be in same FS [ino:orig %lu, donor %lu]\n", | ||
976 | orig_inode->i_ino, donor_inode->i_ino); | ||
977 | return -EINVAL; | ||
978 | } | ||
979 | |||
980 | /* Ext4 move extent supports only extent based file */ | 938 | /* Ext4 move extent supports only extent based file */ |
981 | if (!(ext4_test_inode_flag(orig_inode, EXT4_INODE_EXTENTS))) { | 939 | if (!(ext4_test_inode_flag(orig_inode, EXT4_INODE_EXTENTS))) { |
982 | ext4_debug("ext4 move extent: orig file is not extents " | 940 | ext4_debug("ext4 move extent: orig file is not extents " |
@@ -1072,35 +1030,19 @@ mext_check_arguments(struct inode *orig_inode, | |||
1072 | * @inode1: the inode structure | 1030 | * @inode1: the inode structure |
1073 | * @inode2: the inode structure | 1031 | * @inode2: the inode structure |
1074 | * | 1032 | * |
1075 | * Lock two inodes' i_mutex by i_ino order. | 1033 | * Lock two inodes' i_mutex |
1076 | * If inode1 or inode2 is NULL, return -EIO. Otherwise, return 0. | ||
1077 | */ | 1034 | */ |
1078 | static int | 1035 | static void |
1079 | mext_inode_double_lock(struct inode *inode1, struct inode *inode2) | 1036 | mext_inode_double_lock(struct inode *inode1, struct inode *inode2) |
1080 | { | 1037 | { |
1081 | int ret = 0; | 1038 | BUG_ON(inode1 == inode2); |
1082 | 1039 | if (inode1 < inode2) { | |
1083 | BUG_ON(inode1 == NULL && inode2 == NULL); | ||
1084 | |||
1085 | ret = mext_check_null_inode(inode1, inode2, __func__, __LINE__); | ||
1086 | if (ret < 0) | ||
1087 | goto out; | ||
1088 | |||
1089 | if (inode1 == inode2) { | ||
1090 | mutex_lock(&inode1->i_mutex); | ||
1091 | goto out; | ||
1092 | } | ||
1093 | |||
1094 | if (inode1->i_ino < inode2->i_ino) { | ||
1095 | mutex_lock_nested(&inode1->i_mutex, I_MUTEX_PARENT); | 1040 | mutex_lock_nested(&inode1->i_mutex, I_MUTEX_PARENT); |
1096 | mutex_lock_nested(&inode2->i_mutex, I_MUTEX_CHILD); | 1041 | mutex_lock_nested(&inode2->i_mutex, I_MUTEX_CHILD); |
1097 | } else { | 1042 | } else { |
1098 | mutex_lock_nested(&inode2->i_mutex, I_MUTEX_PARENT); | 1043 | mutex_lock_nested(&inode2->i_mutex, I_MUTEX_PARENT); |
1099 | mutex_lock_nested(&inode1->i_mutex, I_MUTEX_CHILD); | 1044 | mutex_lock_nested(&inode1->i_mutex, I_MUTEX_CHILD); |
1100 | } | 1045 | } |
1101 | |||
1102 | out: | ||
1103 | return ret; | ||
1104 | } | 1046 | } |
1105 | 1047 | ||
1106 | /** | 1048 | /** |
@@ -1109,28 +1051,13 @@ out: | |||
1109 | * @inode1: the inode that is released first | 1051 | * @inode1: the inode that is released first |
1110 | * @inode2: the inode that is released second | 1052 | * @inode2: the inode that is released second |
1111 | * | 1053 | * |
1112 | * If inode1 or inode2 is NULL, return -EIO. Otherwise, return 0. | ||
1113 | */ | 1054 | */ |
1114 | 1055 | ||
1115 | static int | 1056 | static void |
1116 | mext_inode_double_unlock(struct inode *inode1, struct inode *inode2) | 1057 | mext_inode_double_unlock(struct inode *inode1, struct inode *inode2) |
1117 | { | 1058 | { |
1118 | int ret = 0; | 1059 | mutex_unlock(&inode1->i_mutex); |
1119 | 1060 | mutex_unlock(&inode2->i_mutex); | |
1120 | BUG_ON(inode1 == NULL && inode2 == NULL); | ||
1121 | |||
1122 | ret = mext_check_null_inode(inode1, inode2, __func__, __LINE__); | ||
1123 | if (ret < 0) | ||
1124 | goto out; | ||
1125 | |||
1126 | if (inode1) | ||
1127 | mutex_unlock(&inode1->i_mutex); | ||
1128 | |||
1129 | if (inode2 && inode2 != inode1) | ||
1130 | mutex_unlock(&inode2->i_mutex); | ||
1131 | |||
1132 | out: | ||
1133 | return ret; | ||
1134 | } | 1061 | } |
1135 | 1062 | ||
1136 | /** | 1063 | /** |
@@ -1187,16 +1114,23 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, | |||
1187 | ext4_lblk_t block_end, seq_start, add_blocks, file_end, seq_blocks = 0; | 1114 | ext4_lblk_t block_end, seq_start, add_blocks, file_end, seq_blocks = 0; |
1188 | ext4_lblk_t rest_blocks; | 1115 | ext4_lblk_t rest_blocks; |
1189 | pgoff_t orig_page_offset = 0, seq_end_page; | 1116 | pgoff_t orig_page_offset = 0, seq_end_page; |
1190 | int ret1, ret2, depth, last_extent = 0; | 1117 | int ret, depth, last_extent = 0; |
1191 | int blocks_per_page = PAGE_CACHE_SIZE >> orig_inode->i_blkbits; | 1118 | int blocks_per_page = PAGE_CACHE_SIZE >> orig_inode->i_blkbits; |
1192 | int data_offset_in_page; | 1119 | int data_offset_in_page; |
1193 | int block_len_in_page; | 1120 | int block_len_in_page; |
1194 | int uninit; | 1121 | int uninit; |
1195 | 1122 | ||
1196 | /* orig and donor should be different file */ | 1123 | if (orig_inode->i_sb != donor_inode->i_sb) { |
1197 | if (orig_inode->i_ino == donor_inode->i_ino) { | 1124 | ext4_debug("ext4 move extent: The argument files " |
1125 | "should be in same FS [ino:orig %lu, donor %lu]\n", | ||
1126 | orig_inode->i_ino, donor_inode->i_ino); | ||
1127 | return -EINVAL; | ||
1128 | } | ||
1129 | |||
1130 | /* orig and donor should be different inodes */ | ||
1131 | if (orig_inode == donor_inode) { | ||
1198 | ext4_debug("ext4 move extent: The argument files should not " | 1132 | ext4_debug("ext4 move extent: The argument files should not " |
1199 | "be same file [ino:orig %lu, donor %lu]\n", | 1133 | "be same inode [ino:orig %lu, donor %lu]\n", |
1200 | orig_inode->i_ino, donor_inode->i_ino); | 1134 | orig_inode->i_ino, donor_inode->i_ino); |
1201 | return -EINVAL; | 1135 | return -EINVAL; |
1202 | } | 1136 | } |
@@ -1210,16 +1144,14 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, | |||
1210 | } | 1144 | } |
1211 | 1145 | ||
1212 | /* Protect orig and donor inodes against a truncate */ | 1146 | /* Protect orig and donor inodes against a truncate */ |
1213 | ret1 = mext_inode_double_lock(orig_inode, donor_inode); | 1147 | mext_inode_double_lock(orig_inode, donor_inode); |
1214 | if (ret1 < 0) | ||
1215 | return ret1; | ||
1216 | 1148 | ||
1217 | /* Protect extent tree against block allocations via delalloc */ | 1149 | /* Protect extent tree against block allocations via delalloc */ |
1218 | double_down_write_data_sem(orig_inode, donor_inode); | 1150 | double_down_write_data_sem(orig_inode, donor_inode); |
1219 | /* Check the filesystem environment whether move_extent can be done */ | 1151 | /* Check the filesystem environment whether move_extent can be done */ |
1220 | ret1 = mext_check_arguments(orig_inode, donor_inode, orig_start, | 1152 | ret = mext_check_arguments(orig_inode, donor_inode, orig_start, |
1221 | donor_start, &len); | 1153 | donor_start, &len); |
1222 | if (ret1) | 1154 | if (ret) |
1223 | goto out; | 1155 | goto out; |
1224 | 1156 | ||
1225 | file_end = (i_size_read(orig_inode) - 1) >> orig_inode->i_blkbits; | 1157 | file_end = (i_size_read(orig_inode) - 1) >> orig_inode->i_blkbits; |
@@ -1227,13 +1159,13 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, | |||
1227 | if (file_end < block_end) | 1159 | if (file_end < block_end) |
1228 | len -= block_end - file_end; | 1160 | len -= block_end - file_end; |
1229 | 1161 | ||
1230 | ret1 = get_ext_path(orig_inode, block_start, &orig_path); | 1162 | ret = get_ext_path(orig_inode, block_start, &orig_path); |
1231 | if (ret1) | 1163 | if (ret) |
1232 | goto out; | 1164 | goto out; |
1233 | 1165 | ||
1234 | /* Get path structure to check the hole */ | 1166 | /* Get path structure to check the hole */ |
1235 | ret1 = get_ext_path(orig_inode, block_start, &holecheck_path); | 1167 | ret = get_ext_path(orig_inode, block_start, &holecheck_path); |
1236 | if (ret1) | 1168 | if (ret) |
1237 | goto out; | 1169 | goto out; |
1238 | 1170 | ||
1239 | depth = ext_depth(orig_inode); | 1171 | depth = ext_depth(orig_inode); |
@@ -1252,13 +1184,13 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, | |||
1252 | last_extent = mext_next_extent(orig_inode, | 1184 | last_extent = mext_next_extent(orig_inode, |
1253 | holecheck_path, &ext_cur); | 1185 | holecheck_path, &ext_cur); |
1254 | if (last_extent < 0) { | 1186 | if (last_extent < 0) { |
1255 | ret1 = last_extent; | 1187 | ret = last_extent; |
1256 | goto out; | 1188 | goto out; |
1257 | } | 1189 | } |
1258 | last_extent = mext_next_extent(orig_inode, orig_path, | 1190 | last_extent = mext_next_extent(orig_inode, orig_path, |
1259 | &ext_dummy); | 1191 | &ext_dummy); |
1260 | if (last_extent < 0) { | 1192 | if (last_extent < 0) { |
1261 | ret1 = last_extent; | 1193 | ret = last_extent; |
1262 | goto out; | 1194 | goto out; |
1263 | } | 1195 | } |
1264 | seq_start = le32_to_cpu(ext_cur->ee_block); | 1196 | seq_start = le32_to_cpu(ext_cur->ee_block); |
@@ -1272,7 +1204,7 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, | |||
1272 | if (le32_to_cpu(ext_cur->ee_block) > block_end) { | 1204 | if (le32_to_cpu(ext_cur->ee_block) > block_end) { |
1273 | ext4_debug("ext4 move extent: The specified range of file " | 1205 | ext4_debug("ext4 move extent: The specified range of file " |
1274 | "may be the hole\n"); | 1206 | "may be the hole\n"); |
1275 | ret1 = -EINVAL; | 1207 | ret = -EINVAL; |
1276 | goto out; | 1208 | goto out; |
1277 | } | 1209 | } |
1278 | 1210 | ||
@@ -1292,7 +1224,7 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, | |||
1292 | last_extent = mext_next_extent(orig_inode, holecheck_path, | 1224 | last_extent = mext_next_extent(orig_inode, holecheck_path, |
1293 | &ext_cur); | 1225 | &ext_cur); |
1294 | if (last_extent < 0) { | 1226 | if (last_extent < 0) { |
1295 | ret1 = last_extent; | 1227 | ret = last_extent; |
1296 | break; | 1228 | break; |
1297 | } | 1229 | } |
1298 | add_blocks = ext4_ext_get_actual_len(ext_cur); | 1230 | add_blocks = ext4_ext_get_actual_len(ext_cur); |
@@ -1349,18 +1281,18 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, | |||
1349 | orig_page_offset, | 1281 | orig_page_offset, |
1350 | data_offset_in_page, | 1282 | data_offset_in_page, |
1351 | block_len_in_page, uninit, | 1283 | block_len_in_page, uninit, |
1352 | &ret1); | 1284 | &ret); |
1353 | 1285 | ||
1354 | /* Count how many blocks we have exchanged */ | 1286 | /* Count how many blocks we have exchanged */ |
1355 | *moved_len += block_len_in_page; | 1287 | *moved_len += block_len_in_page; |
1356 | if (ret1 < 0) | 1288 | if (ret < 0) |
1357 | break; | 1289 | break; |
1358 | if (*moved_len > len) { | 1290 | if (*moved_len > len) { |
1359 | EXT4_ERROR_INODE(orig_inode, | 1291 | EXT4_ERROR_INODE(orig_inode, |
1360 | "We replaced blocks too much! " | 1292 | "We replaced blocks too much! " |
1361 | "sum of replaced: %llu requested: %llu", | 1293 | "sum of replaced: %llu requested: %llu", |
1362 | *moved_len, len); | 1294 | *moved_len, len); |
1363 | ret1 = -EIO; | 1295 | ret = -EIO; |
1364 | break; | 1296 | break; |
1365 | } | 1297 | } |
1366 | 1298 | ||
@@ -1374,22 +1306,22 @@ ext4_move_extents(struct file *o_filp, struct file *d_filp, | |||
1374 | } | 1306 | } |
1375 | 1307 | ||
1376 | double_down_write_data_sem(orig_inode, donor_inode); | 1308 | double_down_write_data_sem(orig_inode, donor_inode); |
1377 | if (ret1 < 0) | 1309 | if (ret < 0) |
1378 | break; | 1310 | break; |
1379 | 1311 | ||
1380 | /* Decrease buffer counter */ | 1312 | /* Decrease buffer counter */ |
1381 | if (holecheck_path) | 1313 | if (holecheck_path) |
1382 | ext4_ext_drop_refs(holecheck_path); | 1314 | ext4_ext_drop_refs(holecheck_path); |
1383 | ret1 = get_ext_path(orig_inode, seq_start, &holecheck_path); | 1315 | ret = get_ext_path(orig_inode, seq_start, &holecheck_path); |
1384 | if (ret1) | 1316 | if (ret) |
1385 | break; | 1317 | break; |
1386 | depth = holecheck_path->p_depth; | 1318 | depth = holecheck_path->p_depth; |
1387 | 1319 | ||
1388 | /* Decrease buffer counter */ | 1320 | /* Decrease buffer counter */ |
1389 | if (orig_path) | 1321 | if (orig_path) |
1390 | ext4_ext_drop_refs(orig_path); | 1322 | ext4_ext_drop_refs(orig_path); |
1391 | ret1 = get_ext_path(orig_inode, seq_start, &orig_path); | 1323 | ret = get_ext_path(orig_inode, seq_start, &orig_path); |
1392 | if (ret1) | 1324 | if (ret) |
1393 | break; | 1325 | break; |
1394 | 1326 | ||
1395 | ext_cur = holecheck_path[depth].p_ext; | 1327 | ext_cur = holecheck_path[depth].p_ext; |
@@ -1412,12 +1344,7 @@ out: | |||
1412 | kfree(holecheck_path); | 1344 | kfree(holecheck_path); |
1413 | } | 1345 | } |
1414 | double_up_write_data_sem(orig_inode, donor_inode); | 1346 | double_up_write_data_sem(orig_inode, donor_inode); |
1415 | ret2 = mext_inode_double_unlock(orig_inode, donor_inode); | 1347 | mext_inode_double_unlock(orig_inode, donor_inode); |
1416 | 1348 | ||
1417 | if (ret1) | 1349 | return ret; |
1418 | return ret1; | ||
1419 | else if (ret2) | ||
1420 | return ret2; | ||
1421 | |||
1422 | return 0; | ||
1423 | } | 1350 | } |