|
@@ -46,6 +46,13 @@
|
|
|
|
|
|
#include <trace/events/ext4.h>
|
|
|
|
|
|
+static int ext4_split_extent(handle_t *handle,
|
|
|
+ struct inode *inode,
|
|
|
+ struct ext4_ext_path *path,
|
|
|
+ struct ext4_map_blocks *map,
|
|
|
+ int split_flag,
|
|
|
+ int flags);
|
|
|
+
|
|
|
static int ext4_ext_truncate_extend_restart(handle_t *handle,
|
|
|
struct inode *inode,
|
|
|
int needed)
|
|
@@ -2203,8 +2210,16 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
|
|
|
ext4_free_blocks(handle, inode, NULL, start, num, flags);
|
|
|
} else if (from == le32_to_cpu(ex->ee_block)
|
|
|
&& to <= le32_to_cpu(ex->ee_block) + ee_len - 1) {
|
|
|
- printk(KERN_INFO "strange request: removal %u-%u from %u:%u\n",
|
|
|
- from, to, le32_to_cpu(ex->ee_block), ee_len);
|
|
|
+ /* head removal */
|
|
|
+ ext4_lblk_t num;
|
|
|
+ ext4_fsblk_t start;
|
|
|
+
|
|
|
+ num = to - from;
|
|
|
+ start = ext4_ext_pblock(ex);
|
|
|
+
|
|
|
+ ext_debug("free first %u blocks starting %llu\n", num, start);
|
|
|
+ ext4_free_blocks(handle, inode, 0, start, num, flags);
|
|
|
+
|
|
|
} else {
|
|
|
printk(KERN_INFO "strange request: removal(2) "
|
|
|
"%u-%u from %u:%u\n",
|
|
@@ -2213,9 +2228,22 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+/*
|
|
|
+ * ext4_ext_rm_leaf() Removes the extents associated with the
|
|
|
+ * blocks appearing between "start" and "end", and splits the extents
|
|
|
+ * if "start" and "end" appear in the same extent
|
|
|
+ *
|
|
|
+ * @handle: The journal handle
|
|
|
+ * @inode: The files inode
|
|
|
+ * @path: The path to the leaf
|
|
|
+ * @start: The first block to remove
|
|
|
+ * @end: The last block to remove
|
|
|
+ */
|
|
|
static int
|
|
|
ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
|
|
|
- struct ext4_ext_path *path, ext4_lblk_t start)
|
|
|
+ struct ext4_ext_path *path, ext4_lblk_t start,
|
|
|
+ ext4_lblk_t end)
|
|
|
{
|
|
|
int err = 0, correct_index = 0;
|
|
|
int depth = ext_depth(inode), credits;
|
|
@@ -2226,6 +2254,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
|
|
|
unsigned short ex_ee_len;
|
|
|
unsigned uninitialized = 0;
|
|
|
struct ext4_extent *ex;
|
|
|
+ struct ext4_map_blocks map;
|
|
|
|
|
|
/* the header must be checked already in ext4_ext_remove_space() */
|
|
|
ext_debug("truncate since %u in leaf\n", start);
|
|
@@ -2255,31 +2284,95 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
|
|
|
path[depth].p_ext = ex;
|
|
|
|
|
|
a = ex_ee_block > start ? ex_ee_block : start;
|
|
|
- b = ex_ee_block + ex_ee_len - 1 < EXT_MAX_BLOCK ?
|
|
|
- ex_ee_block + ex_ee_len - 1 : EXT_MAX_BLOCK;
|
|
|
+ b = ex_ee_block+ex_ee_len - 1 < end ?
|
|
|
+ ex_ee_block+ex_ee_len - 1 : end;
|
|
|
|
|
|
ext_debug(" border %u:%u\n", a, b);
|
|
|
|
|
|
- if (a != ex_ee_block && b != ex_ee_block + ex_ee_len - 1) {
|
|
|
- block = 0;
|
|
|
- num = 0;
|
|
|
- BUG();
|
|
|
+ /* If this extent is beyond the end of the hole, skip it */
|
|
|
+ if (end <= ex_ee_block) {
|
|
|
+ ex--;
|
|
|
+ ex_ee_block = le32_to_cpu(ex->ee_block);
|
|
|
+ ex_ee_len = ext4_ext_get_actual_len(ex);
|
|
|
+ continue;
|
|
|
+ } else if (a != ex_ee_block &&
|
|
|
+ b != ex_ee_block + ex_ee_len - 1) {
|
|
|
+ /*
|
|
|
+ * If this is a truncate, then this condition should
|
|
|
+ * never happen because at least one of the end points
|
|
|
+ * needs to be on the edge of the extent.
|
|
|
+ */
|
|
|
+ if (end == EXT_MAX_BLOCK) {
|
|
|
+ ext_debug(" bad truncate %u:%u\n",
|
|
|
+ start, end);
|
|
|
+ block = 0;
|
|
|
+ num = 0;
|
|
|
+ err = -EIO;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ /*
|
|
|
+ * else this is a hole punch, so the extent needs to
|
|
|
+ * be split since neither edge of the hole is on the
|
|
|
+ * extent edge
|
|
|
+ */
|
|
|
+ else{
|
|
|
+ map.m_pblk = ext4_ext_pblock(ex);
|
|
|
+ map.m_lblk = ex_ee_block;
|
|
|
+ map.m_len = b - ex_ee_block;
|
|
|
+
|
|
|
+ err = ext4_split_extent(handle,
|
|
|
+ inode, path, &map, 0,
|
|
|
+ EXT4_GET_BLOCKS_PUNCH_OUT_EXT |
|
|
|
+ EXT4_GET_BLOCKS_PRE_IO);
|
|
|
+
|
|
|
+ if (err < 0)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ ex_ee_len = ext4_ext_get_actual_len(ex);
|
|
|
+
|
|
|
+ b = ex_ee_block+ex_ee_len - 1 < end ?
|
|
|
+ ex_ee_block+ex_ee_len - 1 : end;
|
|
|
+
|
|
|
+ /* Then remove tail of this extent */
|
|
|
+ block = ex_ee_block;
|
|
|
+ num = a - block;
|
|
|
+ }
|
|
|
} else if (a != ex_ee_block) {
|
|
|
/* remove tail of the extent */
|
|
|
block = ex_ee_block;
|
|
|
num = a - block;
|
|
|
} else if (b != ex_ee_block + ex_ee_len - 1) {
|
|
|
/* remove head of the extent */
|
|
|
- block = a;
|
|
|
- num = b - a;
|
|
|
- /* there is no "make a hole" API yet */
|
|
|
- BUG();
|
|
|
+ block = b;
|
|
|
+ num = ex_ee_block + ex_ee_len - b;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If this is a truncate, this condition
|
|
|
+ * should never happen
|
|
|
+ */
|
|
|
+ if (end == EXT_MAX_BLOCK) {
|
|
|
+ ext_debug(" bad truncate %u:%u\n",
|
|
|
+ start, end);
|
|
|
+ err = -EIO;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
} else {
|
|
|
/* remove whole extent: excellent! */
|
|
|
block = ex_ee_block;
|
|
|
num = 0;
|
|
|
- BUG_ON(a != ex_ee_block);
|
|
|
- BUG_ON(b != ex_ee_block + ex_ee_len - 1);
|
|
|
+ if (a != ex_ee_block) {
|
|
|
+ ext_debug(" bad truncate %u:%u\n",
|
|
|
+ start, end);
|
|
|
+ err = -EIO;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (b != ex_ee_block + ex_ee_len - 1) {
|
|
|
+ ext_debug(" bad truncate %u:%u\n",
|
|
|
+ start, end);
|
|
|
+ err = -EIO;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -2310,7 +2403,13 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
|
|
|
if (num == 0) {
|
|
|
/* this extent is removed; mark slot entirely unused */
|
|
|
ext4_ext_store_pblock(ex, 0);
|
|
|
- le16_add_cpu(&eh->eh_entries, -1);
|
|
|
+ } else if (block != ex_ee_block) {
|
|
|
+ /*
|
|
|
+ * If this was a head removal, then we need to update
|
|
|
+ * the physical block since it is now at a different
|
|
|
+ * location
|
|
|
+ */
|
|
|
+ ext4_ext_store_pblock(ex, ext4_ext_pblock(ex) + (b-a));
|
|
|
}
|
|
|
|
|
|
ex->ee_block = cpu_to_le32(block);
|
|
@@ -2326,6 +2425,27 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
|
|
|
if (err)
|
|
|
goto out;
|
|
|
|
|
|
+ /*
|
|
|
+ * If the extent was completely released,
|
|
|
+ * we need to remove it from the leaf
|
|
|
+ */
|
|
|
+ if (num == 0) {
|
|
|
+ if (end != EXT_MAX_BLOCK) {
|
|
|
+ /*
|
|
|
+ * For hole punching, we need to scoot all the
|
|
|
+ * extents up when an extent is removed so that
|
|
|
+ * we dont have blank extents in the middle
|
|
|
+ */
|
|
|
+ memmove(ex, ex+1, (EXT_LAST_EXTENT(eh) - ex) *
|
|
|
+ sizeof(struct ext4_extent));
|
|
|
+
|
|
|
+ /* Now get rid of the one at the end */
|
|
|
+ memset(EXT_LAST_EXTENT(eh), 0,
|
|
|
+ sizeof(struct ext4_extent));
|
|
|
+ }
|
|
|
+ le16_add_cpu(&eh->eh_entries, -1);
|
|
|
+ }
|
|
|
+
|
|
|
ext_debug("new extent: %u:%u:%llu\n", block, num,
|
|
|
ext4_ext_pblock(ex));
|
|
|
ex--;
|
|
@@ -2366,7 +2486,8 @@ ext4_ext_more_to_rm(struct ext4_ext_path *path)
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
-static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start)
|
|
|
+static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
|
|
|
+ ext4_lblk_t end)
|
|
|
{
|
|
|
struct super_block *sb = inode->i_sb;
|
|
|
int depth = ext_depth(inode);
|
|
@@ -2405,7 +2526,8 @@ again:
|
|
|
while (i >= 0 && err == 0) {
|
|
|
if (i == depth) {
|
|
|
/* this is leaf block */
|
|
|
- err = ext4_ext_rm_leaf(handle, inode, path, start);
|
|
|
+ err = ext4_ext_rm_leaf(handle, inode, path,
|
|
|
+ start, end);
|
|
|
/* root level has p_bh == NULL, brelse() eats this */
|
|
|
brelse(path[i].p_bh);
|
|
|
path[i].p_bh = NULL;
|
|
@@ -3451,7 +3573,7 @@ void ext4_ext_truncate(struct inode *inode)
|
|
|
|
|
|
last_block = (inode->i_size + sb->s_blocksize - 1)
|
|
|
>> EXT4_BLOCK_SIZE_BITS(sb);
|
|
|
- err = ext4_ext_remove_space(inode, last_block);
|
|
|
+ err = ext4_ext_remove_space(inode, last_block, EXT_MAX_BLOCK);
|
|
|
|
|
|
/* In a multi-transaction truncate, we only make the final
|
|
|
* transaction synchronous.
|