aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4
diff options
context:
space:
mode:
authorDmitry Monakhov <dmonakhov@openvz.org>2012-09-26 12:32:19 -0400
committerTheodore Ts'o <tytso@mit.edu>2012-09-26 12:32:19 -0400
commit03bd8b9b896c8e940f282f540e6b4de90d666b7c (patch)
tree76e770537d52af02d3664c296560b3d0d708ab39 /fs/ext4
parent0acdb8876fead922c9ffa6768c5675a37485c48c (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.c167
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 */
148static int
149mext_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 */
176static void 148static void
177double_down_write_data_sem(struct inode *orig_inode, struct inode *donor_inode) 149double_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 */
1078static int 1035static void
1079mext_inode_double_lock(struct inode *inode1, struct inode *inode2) 1036mext_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
1102out:
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
1115static int 1056static void
1116mext_inode_double_unlock(struct inode *inode1, struct inode *inode2) 1057mext_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
1132out:
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}