aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2012-08-17 09:44:17 -0400
committerTheodore Ts'o <tytso@mit.edu>2012-08-17 09:44:17 -0400
commitecb94f5fdf4b72547fca022421a9dca1672bddd4 (patch)
treec0168cfff5ecd338a06bee9f963b0e23e00d3037 /fs/ext4
parent89a4e48f8479f8145eca9698f39fe188c982212f (diff)
ext4: collapse a single extent tree block into the inode if possible
If an inode has more than 4 extents, but then later some of the extents are merged together, we can optimize the file system by moving the extents up into the inode, and discarding the extent tree block. This is important, because if there are a large number of inodes with an external extent tree blocks where the contents could fit in the inode, this can significantly increase the fsck time of the file system. Google-Bug-Id: 6801242 Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs/ext4')
-rw-r--r--fs/ext4/extents.c72
1 files changed, 58 insertions, 14 deletions
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index aabbb3f53683..e8755c21f4b9 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -1656,16 +1656,60 @@ static int ext4_ext_try_to_merge_right(struct inode *inode,
1656} 1656}
1657 1657
1658/* 1658/*
1659 * This function does a very simple check to see if we can collapse
1660 * an extent tree with a single extent tree leaf block into the inode.
1661 */
1662static void ext4_ext_try_to_merge_up(handle_t *handle,
1663 struct inode *inode,
1664 struct ext4_ext_path *path)
1665{
1666 size_t s;
1667 unsigned max_root = ext4_ext_space_root(inode, 0);
1668 ext4_fsblk_t blk;
1669
1670 if ((path[0].p_depth != 1) ||
1671 (le16_to_cpu(path[0].p_hdr->eh_entries) != 1) ||
1672 (le16_to_cpu(path[1].p_hdr->eh_entries) > max_root))
1673 return;
1674
1675 /*
1676 * We need to modify the block allocation bitmap and the block
1677 * group descriptor to release the extent tree block. If we
1678 * can't get the journal credits, give up.
1679 */
1680 if (ext4_journal_extend(handle, 2))
1681 return;
1682
1683 /*
1684 * Copy the extent data up to the inode
1685 */
1686 blk = ext4_idx_pblock(path[0].p_idx);
1687 s = le16_to_cpu(path[1].p_hdr->eh_entries) *
1688 sizeof(struct ext4_extent_idx);
1689 s += sizeof(struct ext4_extent_header);
1690
1691 memcpy(path[0].p_hdr, path[1].p_hdr, s);
1692 path[0].p_depth = 0;
1693 path[0].p_ext = EXT_FIRST_EXTENT(path[0].p_hdr) +
1694 (path[1].p_ext - EXT_FIRST_EXTENT(path[1].p_hdr));
1695 path[0].p_hdr->eh_max = cpu_to_le16(max_root);
1696
1697 brelse(path[1].p_bh);
1698 ext4_free_blocks(handle, inode, NULL, blk, 1,
1699 EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET);
1700}
1701
1702/*
1659 * This function tries to merge the @ex extent to neighbours in the tree. 1703 * This function tries to merge the @ex extent to neighbours in the tree.
1660 * return 1 if merge left else 0. 1704 * return 1 if merge left else 0.
1661 */ 1705 */
1662static int ext4_ext_try_to_merge(struct inode *inode, 1706static void ext4_ext_try_to_merge(handle_t *handle,
1707 struct inode *inode,
1663 struct ext4_ext_path *path, 1708 struct ext4_ext_path *path,
1664 struct ext4_extent *ex) { 1709 struct ext4_extent *ex) {
1665 struct ext4_extent_header *eh; 1710 struct ext4_extent_header *eh;
1666 unsigned int depth; 1711 unsigned int depth;
1667 int merge_done = 0; 1712 int merge_done = 0;
1668 int ret = 0;
1669 1713
1670 depth = ext_depth(inode); 1714 depth = ext_depth(inode);
1671 BUG_ON(path[depth].p_hdr == NULL); 1715 BUG_ON(path[depth].p_hdr == NULL);
@@ -1675,9 +1719,9 @@ static int ext4_ext_try_to_merge(struct inode *inode,
1675 merge_done = ext4_ext_try_to_merge_right(inode, path, ex - 1); 1719 merge_done = ext4_ext_try_to_merge_right(inode, path, ex - 1);
1676 1720
1677 if (!merge_done) 1721 if (!merge_done)
1678 ret = ext4_ext_try_to_merge_right(inode, path, ex); 1722 (void) ext4_ext_try_to_merge_right(inode, path, ex);
1679 1723
1680 return ret; 1724 ext4_ext_try_to_merge_up(handle, inode, path);
1681} 1725}
1682 1726
1683/* 1727/*
@@ -1893,7 +1937,7 @@ has_space:
1893merge: 1937merge:
1894 /* try to merge extents */ 1938 /* try to merge extents */
1895 if (!(flag & EXT4_GET_BLOCKS_PRE_IO)) 1939 if (!(flag & EXT4_GET_BLOCKS_PRE_IO))
1896 ext4_ext_try_to_merge(inode, path, nearex); 1940 ext4_ext_try_to_merge(handle, inode, path, nearex);
1897 1941
1898 1942
1899 /* time to correct all indexes above */ 1943 /* time to correct all indexes above */
@@ -1901,7 +1945,7 @@ merge:
1901 if (err) 1945 if (err)
1902 goto cleanup; 1946 goto cleanup;
1903 1947
1904 err = ext4_ext_dirty(handle, inode, path + depth); 1948 err = ext4_ext_dirty(handle, inode, path + path->p_depth);
1905 1949
1906cleanup: 1950cleanup:
1907 if (npath) { 1951 if (npath) {
@@ -2924,9 +2968,9 @@ static int ext4_split_extent_at(handle_t *handle,
2924 ext4_ext_mark_initialized(ex); 2968 ext4_ext_mark_initialized(ex);
2925 2969
2926 if (!(flags & EXT4_GET_BLOCKS_PRE_IO)) 2970 if (!(flags & EXT4_GET_BLOCKS_PRE_IO))
2927 ext4_ext_try_to_merge(inode, path, ex); 2971 ext4_ext_try_to_merge(handle, inode, path, ex);
2928 2972
2929 err = ext4_ext_dirty(handle, inode, path + depth); 2973 err = ext4_ext_dirty(handle, inode, path + path->p_depth);
2930 goto out; 2974 goto out;
2931 } 2975 }
2932 2976
@@ -2958,8 +3002,8 @@ static int ext4_split_extent_at(handle_t *handle,
2958 goto fix_extent_len; 3002 goto fix_extent_len;
2959 /* update the extent length and mark as initialized */ 3003 /* update the extent length and mark as initialized */
2960 ex->ee_len = cpu_to_le16(ee_len); 3004 ex->ee_len = cpu_to_le16(ee_len);
2961 ext4_ext_try_to_merge(inode, path, ex); 3005 ext4_ext_try_to_merge(handle, inode, path, ex);
2962 err = ext4_ext_dirty(handle, inode, path + depth); 3006 err = ext4_ext_dirty(handle, inode, path + path->p_depth);
2963 goto out; 3007 goto out;
2964 } else if (err) 3008 } else if (err)
2965 goto fix_extent_len; 3009 goto fix_extent_len;
@@ -3191,8 +3235,8 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
3191 if (err) 3235 if (err)
3192 goto out; 3236 goto out;
3193 ext4_ext_mark_initialized(ex); 3237 ext4_ext_mark_initialized(ex);
3194 ext4_ext_try_to_merge(inode, path, ex); 3238 ext4_ext_try_to_merge(handle, inode, path, ex);
3195 err = ext4_ext_dirty(handle, inode, path + depth); 3239 err = ext4_ext_dirty(handle, inode, path + path->p_depth);
3196 goto out; 3240 goto out;
3197 } 3241 }
3198 3242
@@ -3333,10 +3377,10 @@ static int ext4_convert_unwritten_extents_endio(handle_t *handle,
3333 /* note: ext4_ext_correct_indexes() isn't needed here because 3377 /* note: ext4_ext_correct_indexes() isn't needed here because
3334 * borders are not changed 3378 * borders are not changed
3335 */ 3379 */
3336 ext4_ext_try_to_merge(inode, path, ex); 3380 ext4_ext_try_to_merge(handle, inode, path, ex);
3337 3381
3338 /* Mark modified extent as dirty */ 3382 /* Mark modified extent as dirty */
3339 err = ext4_ext_dirty(handle, inode, path + depth); 3383 err = ext4_ext_dirty(handle, inode, path + path->p_depth);
3340out: 3384out:
3341 ext4_ext_show_leaf(inode, path); 3385 ext4_ext_show_leaf(inode, path);
3342 return err; 3386 return err;