|
@@ -1655,17 +1655,61 @@ static int ext4_ext_try_to_merge_right(struct inode *inode,
|
|
|
return merge_done;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * This function does a very simple check to see if we can collapse
|
|
|
+ * an extent tree with a single extent tree leaf block into the inode.
|
|
|
+ */
|
|
|
+static void ext4_ext_try_to_merge_up(handle_t *handle,
|
|
|
+ struct inode *inode,
|
|
|
+ struct ext4_ext_path *path)
|
|
|
+{
|
|
|
+ size_t s;
|
|
|
+ unsigned max_root = ext4_ext_space_root(inode, 0);
|
|
|
+ ext4_fsblk_t blk;
|
|
|
+
|
|
|
+ if ((path[0].p_depth != 1) ||
|
|
|
+ (le16_to_cpu(path[0].p_hdr->eh_entries) != 1) ||
|
|
|
+ (le16_to_cpu(path[1].p_hdr->eh_entries) > max_root))
|
|
|
+ return;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We need to modify the block allocation bitmap and the block
|
|
|
+ * group descriptor to release the extent tree block. If we
|
|
|
+ * can't get the journal credits, give up.
|
|
|
+ */
|
|
|
+ if (ext4_journal_extend(handle, 2))
|
|
|
+ return;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Copy the extent data up to the inode
|
|
|
+ */
|
|
|
+ blk = ext4_idx_pblock(path[0].p_idx);
|
|
|
+ s = le16_to_cpu(path[1].p_hdr->eh_entries) *
|
|
|
+ sizeof(struct ext4_extent_idx);
|
|
|
+ s += sizeof(struct ext4_extent_header);
|
|
|
+
|
|
|
+ memcpy(path[0].p_hdr, path[1].p_hdr, s);
|
|
|
+ path[0].p_depth = 0;
|
|
|
+ path[0].p_ext = EXT_FIRST_EXTENT(path[0].p_hdr) +
|
|
|
+ (path[1].p_ext - EXT_FIRST_EXTENT(path[1].p_hdr));
|
|
|
+ path[0].p_hdr->eh_max = cpu_to_le16(max_root);
|
|
|
+
|
|
|
+ brelse(path[1].p_bh);
|
|
|
+ ext4_free_blocks(handle, inode, NULL, blk, 1,
|
|
|
+ EXT4_FREE_BLOCKS_METADATA | EXT4_FREE_BLOCKS_FORGET);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* This function tries to merge the @ex extent to neighbours in the tree.
|
|
|
* return 1 if merge left else 0.
|
|
|
*/
|
|
|
-static int ext4_ext_try_to_merge(struct inode *inode,
|
|
|
+static void ext4_ext_try_to_merge(handle_t *handle,
|
|
|
+ struct inode *inode,
|
|
|
struct ext4_ext_path *path,
|
|
|
struct ext4_extent *ex) {
|
|
|
struct ext4_extent_header *eh;
|
|
|
unsigned int depth;
|
|
|
int merge_done = 0;
|
|
|
- int ret = 0;
|
|
|
|
|
|
depth = ext_depth(inode);
|
|
|
BUG_ON(path[depth].p_hdr == NULL);
|
|
@@ -1675,9 +1719,9 @@ static int ext4_ext_try_to_merge(struct inode *inode,
|
|
|
merge_done = ext4_ext_try_to_merge_right(inode, path, ex - 1);
|
|
|
|
|
|
if (!merge_done)
|
|
|
- ret = ext4_ext_try_to_merge_right(inode, path, ex);
|
|
|
+ (void) ext4_ext_try_to_merge_right(inode, path, ex);
|
|
|
|
|
|
- return ret;
|
|
|
+ ext4_ext_try_to_merge_up(handle, inode, path);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -1893,7 +1937,7 @@ has_space:
|
|
|
merge:
|
|
|
/* try to merge extents */
|
|
|
if (!(flag & EXT4_GET_BLOCKS_PRE_IO))
|
|
|
- ext4_ext_try_to_merge(inode, path, nearex);
|
|
|
+ ext4_ext_try_to_merge(handle, inode, path, nearex);
|
|
|
|
|
|
|
|
|
/* time to correct all indexes above */
|
|
@@ -1901,7 +1945,7 @@ merge:
|
|
|
if (err)
|
|
|
goto cleanup;
|
|
|
|
|
|
- err = ext4_ext_dirty(handle, inode, path + depth);
|
|
|
+ err = ext4_ext_dirty(handle, inode, path + path->p_depth);
|
|
|
|
|
|
cleanup:
|
|
|
if (npath) {
|
|
@@ -2924,9 +2968,9 @@ static int ext4_split_extent_at(handle_t *handle,
|
|
|
ext4_ext_mark_initialized(ex);
|
|
|
|
|
|
if (!(flags & EXT4_GET_BLOCKS_PRE_IO))
|
|
|
- ext4_ext_try_to_merge(inode, path, ex);
|
|
|
+ ext4_ext_try_to_merge(handle, inode, path, ex);
|
|
|
|
|
|
- err = ext4_ext_dirty(handle, inode, path + depth);
|
|
|
+ err = ext4_ext_dirty(handle, inode, path + path->p_depth);
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
@@ -2958,8 +3002,8 @@ static int ext4_split_extent_at(handle_t *handle,
|
|
|
goto fix_extent_len;
|
|
|
/* update the extent length and mark as initialized */
|
|
|
ex->ee_len = cpu_to_le16(ee_len);
|
|
|
- ext4_ext_try_to_merge(inode, path, ex);
|
|
|
- err = ext4_ext_dirty(handle, inode, path + depth);
|
|
|
+ ext4_ext_try_to_merge(handle, inode, path, ex);
|
|
|
+ err = ext4_ext_dirty(handle, inode, path + path->p_depth);
|
|
|
goto out;
|
|
|
} else if (err)
|
|
|
goto fix_extent_len;
|
|
@@ -3191,8 +3235,8 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
|
|
|
if (err)
|
|
|
goto out;
|
|
|
ext4_ext_mark_initialized(ex);
|
|
|
- ext4_ext_try_to_merge(inode, path, ex);
|
|
|
- err = ext4_ext_dirty(handle, inode, path + depth);
|
|
|
+ ext4_ext_try_to_merge(handle, inode, path, ex);
|
|
|
+ err = ext4_ext_dirty(handle, inode, path + path->p_depth);
|
|
|
goto out;
|
|
|
}
|
|
|
|
|
@@ -3333,10 +3377,10 @@ static int ext4_convert_unwritten_extents_endio(handle_t *handle,
|
|
|
/* note: ext4_ext_correct_indexes() isn't needed here because
|
|
|
* borders are not changed
|
|
|
*/
|
|
|
- ext4_ext_try_to_merge(inode, path, ex);
|
|
|
+ ext4_ext_try_to_merge(handle, inode, path, ex);
|
|
|
|
|
|
/* Mark modified extent as dirty */
|
|
|
- err = ext4_ext_dirty(handle, inode, path + depth);
|
|
|
+ err = ext4_ext_dirty(handle, inode, path + path->p_depth);
|
|
|
out:
|
|
|
ext4_ext_show_leaf(inode, path);
|
|
|
return err;
|