diff options
author | Theodore Ts'o <tytso@mit.edu> | 2013-04-03 12:47:17 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2013-04-03 12:47:17 -0400 |
commit | 819c4920b7e60ecfd6f0f61d890af4cdf3873d18 (patch) | |
tree | 85f995b14c7308080d196d85b9b3d148337a6382 | |
parent | 26a4c0c6ccecf6814cf44f951c97222bd795bc1a (diff) |
ext4: refactor truncate code
Move common code in ext4_ind_truncate() and ext4_ext_truncate() into
ext4_truncate(). This saves over 60 lines of code.
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
-rw-r--r-- | fs/ext4/ext4.h | 4 | ||||
-rw-r--r-- | fs/ext4/extents.c | 60 | ||||
-rw-r--r-- | fs/ext4/indirect.c | 88 | ||||
-rw-r--r-- | fs/ext4/inode.c | 74 |
4 files changed, 78 insertions, 148 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 0649253804c4..d05ba3886f33 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -2109,7 +2109,7 @@ extern ssize_t ext4_ind_direct_IO(int rw, struct kiocb *iocb, | |||
2109 | unsigned long nr_segs); | 2109 | unsigned long nr_segs); |
2110 | extern int ext4_ind_calc_metadata_amount(struct inode *inode, sector_t lblock); | 2110 | extern int ext4_ind_calc_metadata_amount(struct inode *inode, sector_t lblock); |
2111 | extern int ext4_ind_trans_blocks(struct inode *inode, int nrblocks, int chunk); | 2111 | extern int ext4_ind_trans_blocks(struct inode *inode, int nrblocks, int chunk); |
2112 | extern void ext4_ind_truncate(struct inode *inode); | 2112 | extern void ext4_ind_truncate(handle_t *, struct inode *inode); |
2113 | extern int ext4_free_hole_blocks(handle_t *handle, struct inode *inode, | 2113 | extern int ext4_free_hole_blocks(handle_t *handle, struct inode *inode, |
2114 | ext4_lblk_t first, ext4_lblk_t stop); | 2114 | ext4_lblk_t first, ext4_lblk_t stop); |
2115 | 2115 | ||
@@ -2575,7 +2575,7 @@ extern int ext4_ext_index_trans_blocks(struct inode *inode, int nrblocks, | |||
2575 | int chunk); | 2575 | int chunk); |
2576 | extern int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, | 2576 | extern int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, |
2577 | struct ext4_map_blocks *map, int flags); | 2577 | struct ext4_map_blocks *map, int flags); |
2578 | extern void ext4_ext_truncate(struct inode *); | 2578 | extern void ext4_ext_truncate(handle_t *, struct inode *); |
2579 | extern int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start, | 2579 | extern int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start, |
2580 | ext4_lblk_t end); | 2580 | ext4_lblk_t end); |
2581 | extern void ext4_ext_init(struct super_block *); | 2581 | extern void ext4_ext_init(struct super_block *); |
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index d58365e40df7..cbbe8a4deac6 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c | |||
@@ -4257,48 +4257,13 @@ out3: | |||
4257 | return err ? err : allocated; | 4257 | return err ? err : allocated; |
4258 | } | 4258 | } |
4259 | 4259 | ||
4260 | void ext4_ext_truncate(struct inode *inode) | 4260 | void ext4_ext_truncate(handle_t *handle, struct inode *inode) |
4261 | { | 4261 | { |
4262 | struct address_space *mapping = inode->i_mapping; | ||
4263 | struct super_block *sb = inode->i_sb; | 4262 | struct super_block *sb = inode->i_sb; |
4264 | ext4_lblk_t last_block; | 4263 | ext4_lblk_t last_block; |
4265 | handle_t *handle; | ||
4266 | loff_t page_len; | ||
4267 | int err = 0; | 4264 | int err = 0; |
4268 | 4265 | ||
4269 | /* | 4266 | /* |
4270 | * finish any pending end_io work so we won't run the risk of | ||
4271 | * converting any truncated blocks to initialized later | ||
4272 | */ | ||
4273 | ext4_flush_unwritten_io(inode); | ||
4274 | |||
4275 | /* | ||
4276 | * probably first extent we're gonna free will be last in block | ||
4277 | */ | ||
4278 | err = ext4_writepage_trans_blocks(inode); | ||
4279 | handle = ext4_journal_start(inode, EXT4_HT_TRUNCATE, err); | ||
4280 | if (IS_ERR(handle)) | ||
4281 | return; | ||
4282 | |||
4283 | if (inode->i_size % PAGE_CACHE_SIZE != 0) { | ||
4284 | page_len = PAGE_CACHE_SIZE - | ||
4285 | (inode->i_size & (PAGE_CACHE_SIZE - 1)); | ||
4286 | |||
4287 | err = ext4_discard_partial_page_buffers(handle, | ||
4288 | mapping, inode->i_size, page_len, 0); | ||
4289 | |||
4290 | if (err) | ||
4291 | goto out_stop; | ||
4292 | } | ||
4293 | |||
4294 | if (ext4_orphan_add(handle, inode)) | ||
4295 | goto out_stop; | ||
4296 | |||
4297 | down_write(&EXT4_I(inode)->i_data_sem); | ||
4298 | |||
4299 | ext4_discard_preallocations(inode); | ||
4300 | |||
4301 | /* | ||
4302 | * TODO: optimization is possible here. | 4267 | * TODO: optimization is possible here. |
4303 | * Probably we need not scan at all, | 4268 | * Probably we need not scan at all, |
4304 | * because page truncation is enough. | 4269 | * because page truncation is enough. |
@@ -4313,29 +4278,6 @@ void ext4_ext_truncate(struct inode *inode) | |||
4313 | err = ext4_es_remove_extent(inode, last_block, | 4278 | err = ext4_es_remove_extent(inode, last_block, |
4314 | EXT_MAX_BLOCKS - last_block); | 4279 | EXT_MAX_BLOCKS - last_block); |
4315 | err = ext4_ext_remove_space(inode, last_block, EXT_MAX_BLOCKS - 1); | 4280 | err = ext4_ext_remove_space(inode, last_block, EXT_MAX_BLOCKS - 1); |
4316 | |||
4317 | /* In a multi-transaction truncate, we only make the final | ||
4318 | * transaction synchronous. | ||
4319 | */ | ||
4320 | if (IS_SYNC(inode)) | ||
4321 | ext4_handle_sync(handle); | ||
4322 | |||
4323 | up_write(&EXT4_I(inode)->i_data_sem); | ||
4324 | |||
4325 | out_stop: | ||
4326 | /* | ||
4327 | * If this was a simple ftruncate() and the file will remain alive, | ||
4328 | * then we need to clear up the orphan record which we created above. | ||
4329 | * However, if this was a real unlink then we were called by | ||
4330 | * ext4_delete_inode(), and we allow that function to clean up the | ||
4331 | * orphan info for us. | ||
4332 | */ | ||
4333 | if (inode->i_nlink) | ||
4334 | ext4_orphan_del(handle, inode); | ||
4335 | |||
4336 | inode->i_mtime = inode->i_ctime = ext4_current_time(inode); | ||
4337 | ext4_mark_inode_dirty(handle, inode); | ||
4338 | ext4_journal_stop(handle); | ||
4339 | } | 4281 | } |
4340 | 4282 | ||
4341 | static void ext4_falloc_update_inode(struct inode *inode, | 4283 | static void ext4_falloc_update_inode(struct inode *inode, |
diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c index d8846779f4ea..98be6f697463 100644 --- a/fs/ext4/indirect.c +++ b/fs/ext4/indirect.c | |||
@@ -806,26 +806,9 @@ int ext4_ind_trans_blocks(struct inode *inode, int nrblocks, int chunk) | |||
806 | * be able to restart the transaction at a conventient checkpoint to make | 806 | * be able to restart the transaction at a conventient checkpoint to make |
807 | * sure we don't overflow the journal. | 807 | * sure we don't overflow the journal. |
808 | * | 808 | * |
809 | * start_transaction gets us a new handle for a truncate transaction, | 809 | * Try to extend this transaction for the purposes of truncation. If |
810 | * and extend_transaction tries to extend the existing one a bit. If | ||
811 | * extend fails, we need to propagate the failure up and restart the | 810 | * extend fails, we need to propagate the failure up and restart the |
812 | * transaction in the top-level truncate loop. --sct | 811 | * transaction in the top-level truncate loop. --sct |
813 | */ | ||
814 | static handle_t *start_transaction(struct inode *inode) | ||
815 | { | ||
816 | handle_t *result; | ||
817 | |||
818 | result = ext4_journal_start(inode, EXT4_HT_TRUNCATE, | ||
819 | ext4_blocks_for_truncate(inode)); | ||
820 | if (!IS_ERR(result)) | ||
821 | return result; | ||
822 | |||
823 | ext4_std_error(inode->i_sb, PTR_ERR(result)); | ||
824 | return result; | ||
825 | } | ||
826 | |||
827 | /* | ||
828 | * Try to extend this transaction for the purposes of truncation. | ||
829 | * | 812 | * |
830 | * Returns 0 if we managed to create more room. If we can't create more | 813 | * Returns 0 if we managed to create more room. If we can't create more |
831 | * room, and the transaction must be restarted we return 1. | 814 | * room, and the transaction must be restarted we return 1. |
@@ -1218,68 +1201,30 @@ static void ext4_free_branches(handle_t *handle, struct inode *inode, | |||
1218 | } | 1201 | } |
1219 | } | 1202 | } |
1220 | 1203 | ||
1221 | void ext4_ind_truncate(struct inode *inode) | 1204 | void ext4_ind_truncate(handle_t *handle, struct inode *inode) |
1222 | { | 1205 | { |
1223 | handle_t *handle; | ||
1224 | struct ext4_inode_info *ei = EXT4_I(inode); | 1206 | struct ext4_inode_info *ei = EXT4_I(inode); |
1225 | __le32 *i_data = ei->i_data; | 1207 | __le32 *i_data = ei->i_data; |
1226 | int addr_per_block = EXT4_ADDR_PER_BLOCK(inode->i_sb); | 1208 | int addr_per_block = EXT4_ADDR_PER_BLOCK(inode->i_sb); |
1227 | struct address_space *mapping = inode->i_mapping; | ||
1228 | ext4_lblk_t offsets[4]; | 1209 | ext4_lblk_t offsets[4]; |
1229 | Indirect chain[4]; | 1210 | Indirect chain[4]; |
1230 | Indirect *partial; | 1211 | Indirect *partial; |
1231 | __le32 nr = 0; | 1212 | __le32 nr = 0; |
1232 | int n = 0; | 1213 | int n = 0; |
1233 | ext4_lblk_t last_block, max_block; | 1214 | ext4_lblk_t last_block, max_block; |
1234 | loff_t page_len; | ||
1235 | unsigned blocksize = inode->i_sb->s_blocksize; | 1215 | unsigned blocksize = inode->i_sb->s_blocksize; |
1236 | int err; | ||
1237 | |||
1238 | handle = start_transaction(inode); | ||
1239 | if (IS_ERR(handle)) | ||
1240 | return; /* AKPM: return what? */ | ||
1241 | 1216 | ||
1242 | last_block = (inode->i_size + blocksize-1) | 1217 | last_block = (inode->i_size + blocksize-1) |
1243 | >> EXT4_BLOCK_SIZE_BITS(inode->i_sb); | 1218 | >> EXT4_BLOCK_SIZE_BITS(inode->i_sb); |
1244 | max_block = (EXT4_SB(inode->i_sb)->s_bitmap_maxbytes + blocksize-1) | 1219 | max_block = (EXT4_SB(inode->i_sb)->s_bitmap_maxbytes + blocksize-1) |
1245 | >> EXT4_BLOCK_SIZE_BITS(inode->i_sb); | 1220 | >> EXT4_BLOCK_SIZE_BITS(inode->i_sb); |
1246 | 1221 | ||
1247 | if (inode->i_size % PAGE_CACHE_SIZE != 0) { | ||
1248 | page_len = PAGE_CACHE_SIZE - | ||
1249 | (inode->i_size & (PAGE_CACHE_SIZE - 1)); | ||
1250 | |||
1251 | err = ext4_discard_partial_page_buffers(handle, | ||
1252 | mapping, inode->i_size, page_len, 0); | ||
1253 | |||
1254 | if (err) | ||
1255 | goto out_stop; | ||
1256 | } | ||
1257 | |||
1258 | if (last_block != max_block) { | 1222 | if (last_block != max_block) { |
1259 | n = ext4_block_to_path(inode, last_block, offsets, NULL); | 1223 | n = ext4_block_to_path(inode, last_block, offsets, NULL); |
1260 | if (n == 0) | 1224 | if (n == 0) |
1261 | goto out_stop; /* error */ | 1225 | return; |
1262 | } | 1226 | } |
1263 | 1227 | ||
1264 | /* | ||
1265 | * OK. This truncate is going to happen. We add the inode to the | ||
1266 | * orphan list, so that if this truncate spans multiple transactions, | ||
1267 | * and we crash, we will resume the truncate when the filesystem | ||
1268 | * recovers. It also marks the inode dirty, to catch the new size. | ||
1269 | * | ||
1270 | * Implication: the file must always be in a sane, consistent | ||
1271 | * truncatable state while each transaction commits. | ||
1272 | */ | ||
1273 | if (ext4_orphan_add(handle, inode)) | ||
1274 | goto out_stop; | ||
1275 | |||
1276 | /* | ||
1277 | * From here we block out all ext4_get_block() callers who want to | ||
1278 | * modify the block allocation tree. | ||
1279 | */ | ||
1280 | down_write(&ei->i_data_sem); | ||
1281 | |||
1282 | ext4_discard_preallocations(inode); | ||
1283 | ext4_es_remove_extent(inode, last_block, EXT_MAX_BLOCKS - last_block); | 1228 | ext4_es_remove_extent(inode, last_block, EXT_MAX_BLOCKS - last_block); |
1284 | 1229 | ||
1285 | /* | 1230 | /* |
@@ -1296,7 +1241,7 @@ void ext4_ind_truncate(struct inode *inode) | |||
1296 | * It is unnecessary to free any data blocks if last_block is | 1241 | * It is unnecessary to free any data blocks if last_block is |
1297 | * equal to the indirect block limit. | 1242 | * equal to the indirect block limit. |
1298 | */ | 1243 | */ |
1299 | goto out_unlock; | 1244 | return; |
1300 | } else if (n == 1) { /* direct blocks */ | 1245 | } else if (n == 1) { /* direct blocks */ |
1301 | ext4_free_data(handle, inode, NULL, i_data+offsets[0], | 1246 | ext4_free_data(handle, inode, NULL, i_data+offsets[0], |
1302 | i_data + EXT4_NDIR_BLOCKS); | 1247 | i_data + EXT4_NDIR_BLOCKS); |
@@ -1356,31 +1301,6 @@ do_indirects: | |||
1356 | case EXT4_TIND_BLOCK: | 1301 | case EXT4_TIND_BLOCK: |
1357 | ; | 1302 | ; |
1358 | } | 1303 | } |
1359 | |||
1360 | out_unlock: | ||
1361 | up_write(&ei->i_data_sem); | ||
1362 | inode->i_mtime = inode->i_ctime = ext4_current_time(inode); | ||
1363 | ext4_mark_inode_dirty(handle, inode); | ||
1364 | |||
1365 | /* | ||
1366 | * In a multi-transaction truncate, we only make the final transaction | ||
1367 | * synchronous | ||
1368 | */ | ||
1369 | if (IS_SYNC(inode)) | ||
1370 | ext4_handle_sync(handle); | ||
1371 | out_stop: | ||
1372 | /* | ||
1373 | * If this was a simple ftruncate(), and the file will remain alive | ||
1374 | * then we need to clear up the orphan record which we created above. | ||
1375 | * However, if this was a real unlink then we were called by | ||
1376 | * ext4_delete_inode(), and we allow that function to clean up the | ||
1377 | * orphan info for us. | ||
1378 | */ | ||
1379 | if (inode->i_nlink) | ||
1380 | ext4_orphan_del(handle, inode); | ||
1381 | |||
1382 | ext4_journal_stop(handle); | ||
1383 | trace_ext4_truncate_exit(inode); | ||
1384 | } | 1304 | } |
1385 | 1305 | ||
1386 | static int free_hole_blocks(handle_t *handle, struct inode *inode, | 1306 | static int free_hole_blocks(handle_t *handle, struct inode *inode, |
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 9bda50aa34e2..49c80e4ac5ac 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -3738,9 +3738,9 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length) | |||
3738 | stop_block); | 3738 | stop_block); |
3739 | 3739 | ||
3740 | ext4_discard_preallocations(inode); | 3740 | ext4_discard_preallocations(inode); |
3741 | up_write(&EXT4_I(inode)->i_data_sem); | ||
3741 | if (IS_SYNC(inode)) | 3742 | if (IS_SYNC(inode)) |
3742 | ext4_handle_sync(handle); | 3743 | ext4_handle_sync(handle); |
3743 | up_write(&EXT4_I(inode)->i_data_sem); | ||
3744 | inode->i_mtime = inode->i_ctime = ext4_current_time(inode); | 3744 | inode->i_mtime = inode->i_ctime = ext4_current_time(inode); |
3745 | ext4_mark_inode_dirty(handle, inode); | 3745 | ext4_mark_inode_dirty(handle, inode); |
3746 | out_stop: | 3746 | out_stop: |
@@ -3782,6 +3782,12 @@ out_mutex: | |||
3782 | */ | 3782 | */ |
3783 | void ext4_truncate(struct inode *inode) | 3783 | void ext4_truncate(struct inode *inode) |
3784 | { | 3784 | { |
3785 | struct ext4_inode_info *ei = EXT4_I(inode); | ||
3786 | unsigned int credits; | ||
3787 | handle_t *handle; | ||
3788 | struct address_space *mapping = inode->i_mapping; | ||
3789 | loff_t page_len; | ||
3790 | |||
3785 | trace_ext4_truncate_enter(inode); | 3791 | trace_ext4_truncate_enter(inode); |
3786 | 3792 | ||
3787 | if (!ext4_can_truncate(inode)) | 3793 | if (!ext4_can_truncate(inode)) |
@@ -3800,10 +3806,72 @@ void ext4_truncate(struct inode *inode) | |||
3800 | return; | 3806 | return; |
3801 | } | 3807 | } |
3802 | 3808 | ||
3809 | /* | ||
3810 | * finish any pending end_io work so we won't run the risk of | ||
3811 | * converting any truncated blocks to initialized later | ||
3812 | */ | ||
3813 | ext4_flush_unwritten_io(inode); | ||
3814 | |||
3815 | if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) | ||
3816 | credits = ext4_writepage_trans_blocks(inode); | ||
3817 | else | ||
3818 | credits = ext4_blocks_for_truncate(inode); | ||
3819 | |||
3820 | handle = ext4_journal_start(inode, EXT4_HT_TRUNCATE, credits); | ||
3821 | if (IS_ERR(handle)) { | ||
3822 | ext4_std_error(inode->i_sb, PTR_ERR(handle)); | ||
3823 | return; | ||
3824 | } | ||
3825 | |||
3826 | if (inode->i_size % PAGE_CACHE_SIZE != 0) { | ||
3827 | page_len = PAGE_CACHE_SIZE - | ||
3828 | (inode->i_size & (PAGE_CACHE_SIZE - 1)); | ||
3829 | |||
3830 | if (ext4_discard_partial_page_buffers(handle, | ||
3831 | mapping, inode->i_size, page_len, 0)) | ||
3832 | goto out_stop; | ||
3833 | } | ||
3834 | |||
3835 | /* | ||
3836 | * We add the inode to the orphan list, so that if this | ||
3837 | * truncate spans multiple transactions, and we crash, we will | ||
3838 | * resume the truncate when the filesystem recovers. It also | ||
3839 | * marks the inode dirty, to catch the new size. | ||
3840 | * | ||
3841 | * Implication: the file must always be in a sane, consistent | ||
3842 | * truncatable state while each transaction commits. | ||
3843 | */ | ||
3844 | if (ext4_orphan_add(handle, inode)) | ||
3845 | goto out_stop; | ||
3846 | |||
3847 | down_write(&EXT4_I(inode)->i_data_sem); | ||
3848 | |||
3849 | ext4_discard_preallocations(inode); | ||
3850 | |||
3803 | if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) | 3851 | if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) |
3804 | ext4_ext_truncate(inode); | 3852 | ext4_ext_truncate(handle, inode); |
3805 | else | 3853 | else |
3806 | ext4_ind_truncate(inode); | 3854 | ext4_ind_truncate(handle, inode); |
3855 | |||
3856 | up_write(&ei->i_data_sem); | ||
3857 | |||
3858 | if (IS_SYNC(inode)) | ||
3859 | ext4_handle_sync(handle); | ||
3860 | |||
3861 | out_stop: | ||
3862 | /* | ||
3863 | * If this was a simple ftruncate() and the file will remain alive, | ||
3864 | * then we need to clear up the orphan record which we created above. | ||
3865 | * However, if this was a real unlink then we were called by | ||
3866 | * ext4_delete_inode(), and we allow that function to clean up the | ||
3867 | * orphan info for us. | ||
3868 | */ | ||
3869 | if (inode->i_nlink) | ||
3870 | ext4_orphan_del(handle, inode); | ||
3871 | |||
3872 | inode->i_mtime = inode->i_ctime = ext4_current_time(inode); | ||
3873 | ext4_mark_inode_dirty(handle, inode); | ||
3874 | ext4_journal_stop(handle); | ||
3807 | 3875 | ||
3808 | trace_ext4_truncate_exit(inode); | 3876 | trace_ext4_truncate_exit(inode); |
3809 | } | 3877 | } |