This patch adds a new flag to map blocks that specifies
the given range of blocks should be punched out. Extents
are first converted to uninitialized extents before they are
punched out. Because punching a hole may require that the
extent be split, it is possible that the splitting may need
more blocks than are available. To deal with this, use of
reserved blocks are enabled to allow the split to proceed.
The routine then returns the number of blocks successfully
punched out.
Signed-off-by: Allison Henderson <[email protected]>
---
:100644 100644 6dea243... 9c3c008... M fs/ext4/extents.c
fs/ext4/extents.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++------
1 files changed, 87 insertions(+), 11 deletions(-)
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index 6dea243..9c3c008 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -3315,15 +3315,19 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
ext4_fsblk_t newblock = 0;
int err = 0, depth, ret;
unsigned int allocated = 0;
+ unsigned int punched_out = 0;
+ unsigned int result = 0;
struct ext4_allocation_request ar;
ext4_io_end_t *io = EXT4_I(inode)->cur_aio_dio;
+ struct ext4_map_blocks punch_map;
ext_debug("blocks %u/%u requested for inode %lu\n",
map->m_lblk, map->m_len, inode->i_ino);
trace_ext4_ext_map_blocks_enter(inode, map->m_lblk, map->m_len, flags);
/* check in cache */
- if (ext4_ext_in_cache(inode, map->m_lblk, &newex)) {
+ if (ext4_ext_in_cache(inode, map->m_lblk, &newex) &&
+ ((flags & EXT4_GET_BLOCKS_PUNCH_OUT_EXT) == 0)) {
if (!newex.ee_start_lo && !newex.ee_start_hi) {
if ((flags & EXT4_GET_BLOCKS_CREATE) == 0) {
/*
@@ -3388,16 +3392,84 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
ext_debug("%u fit into %u:%d -> %llu\n", map->m_lblk,
ee_block, ee_len, newblock);
- /* Do not put uninitialized extent in the cache */
- if (!ext4_ext_is_uninitialized(ex)) {
- ext4_ext_put_in_cache(inode, ee_block,
- ee_len, ee_start);
- goto out;
+ if ((flags & EXT4_GET_BLOCKS_PUNCH_OUT_EXT) == 0) {
+ /*
+ * Do not put uninitialized extent
+ * in the cache
+ */
+ if (!ext4_ext_is_uninitialized(ex)) {
+ ext4_ext_put_in_cache(inode, ee_block,
+ ee_len, ee_start);
+ goto out;
+ }
+ ret = ext4_ext_handle_uninitialized_extents(
+ handle, inode, map, path, flags,
+ allocated, newblock);
+ return ret;
}
- ret = ext4_ext_handle_uninitialized_extents(handle,
- inode, map, path, flags, allocated,
- newblock);
- return ret;
+
+ /*
+ * Punch out the map length, but only to the
+ * end of the extent
+ */
+ punched_out = allocated < map->m_len ?
+ allocated : map->m_len;
+
+ /*
+ * Sense extents need to be converted to
+ * uninitialized, they must fit in an
+ * uninitialized extent
+ */
+ if (punched_out > EXT_UNINIT_MAX_LEN)
+ punched_out = EXT_UNINIT_MAX_LEN;
+
+ punch_map.m_lblk = map->m_lblk;
+ punch_map.m_pblk = newblock;
+ punch_map.m_len = punched_out;
+ punch_map.m_flags = 0;
+
+ /* Check to see if the extent needs to be split */
+ if (punch_map.m_len != ee_len ||
+ punch_map.m_lblk != ee_block) {
+
+ ret = ext4_split_extent(handle, inode,
+ path, &punch_map, 0,
+ EXT4_GET_BLOCKS_PUNCH_OUT_EXT |
+ EXT4_GET_BLOCKS_PRE_IO);
+
+ if (ret < 0) {
+ err = ret;
+ goto out2;
+ }
+ /*
+ * find extent for the block at
+ * the start of the hole
+ */
+ ext4_ext_drop_refs(path);
+ kfree(path);
+
+ path = ext4_ext_find_extent(inode,
+ map->m_lblk, NULL);
+ if (IS_ERR(path)) {
+ err = PTR_ERR(path);
+ path = NULL;
+ goto out2;
+ }
+
+ depth = ext_depth(inode);
+ ex = path[depth].p_ext;
+ ee_len = ext4_ext_get_actual_len(ex);
+ ee_block = le32_to_cpu(ex->ee_block);
+ ee_start = ext4_ext_pblock(ex);
+
+ }
+
+ ext4_ext_mark_uninitialized(ex);
+
+ err = ext4_ext_remove_space(inode, map->m_lblk,
+ map->m_lblk + punched_out);
+
+ goto out2;
}
}
@@ -3542,7 +3614,11 @@ out2:
}
trace_ext4_ext_map_blocks_exit(inode, map->m_lblk,
newblock, map->m_len, err ? err : allocated);
- return err ? err : allocated;
+
+ result = (flags & EXT4_GET_BLOCKS_PUNCH_OUT_EXT) ?
+ punched_out : allocated;
+
+ return err ? err : result;
}
void ext4_ext_truncate(struct inode *inode)
--
1.7.1
On Sat, 2011-05-07 at 16:54 -0700, Allison Henderson wrote:
> This patch adds a new flag to map blocks that specifies
> the given range of blocks should be punched out. Extents
> are first converted to uninitialized extents before they are
> punched out. Because punching a hole may require that the
> extent be split, it is possible that the splitting may need
> more blocks than are available. To deal with this, use of
> reserved blocks are enabled to allow the split to proceed.
>
> The routine then returns the number of blocks successfully
> punched out.
>
> Signed-off-by: Allison Henderson <[email protected]>
Good to me.
Reviewed-by: Mingming Cao <[email protected]>
> ---
> :100644 100644 6dea243... 9c3c008... M fs/ext4/extents.c
> fs/ext4/extents.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++------
> 1 files changed, 87 insertions(+), 11 deletions(-)
>
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index 6dea243..9c3c008 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -3315,15 +3315,19 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
> ext4_fsblk_t newblock = 0;
> int err = 0, depth, ret;
> unsigned int allocated = 0;
> + unsigned int punched_out = 0;
> + unsigned int result = 0;
> struct ext4_allocation_request ar;
> ext4_io_end_t *io = EXT4_I(inode)->cur_aio_dio;
> + struct ext4_map_blocks punch_map;
>
> ext_debug("blocks %u/%u requested for inode %lu\n",
> map->m_lblk, map->m_len, inode->i_ino);
> trace_ext4_ext_map_blocks_enter(inode, map->m_lblk, map->m_len, flags);
>
> /* check in cache */
> - if (ext4_ext_in_cache(inode, map->m_lblk, &newex)) {
> + if (ext4_ext_in_cache(inode, map->m_lblk, &newex) &&
> + ((flags & EXT4_GET_BLOCKS_PUNCH_OUT_EXT) == 0)) {
> if (!newex.ee_start_lo && !newex.ee_start_hi) {
> if ((flags & EXT4_GET_BLOCKS_CREATE) == 0) {
> /*
> @@ -3388,16 +3392,84 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
> ext_debug("%u fit into %u:%d -> %llu\n", map->m_lblk,
> ee_block, ee_len, newblock);
>
> - /* Do not put uninitialized extent in the cache */
> - if (!ext4_ext_is_uninitialized(ex)) {
> - ext4_ext_put_in_cache(inode, ee_block,
> - ee_len, ee_start);
> - goto out;
> + if ((flags & EXT4_GET_BLOCKS_PUNCH_OUT_EXT) == 0) {
> + /*
> + * Do not put uninitialized extent
> + * in the cache
> + */
> + if (!ext4_ext_is_uninitialized(ex)) {
> + ext4_ext_put_in_cache(inode, ee_block,
> + ee_len, ee_start);
> + goto out;
> + }
> + ret = ext4_ext_handle_uninitialized_extents(
> + handle, inode, map, path, flags,
> + allocated, newblock);
> + return ret;
> }
> - ret = ext4_ext_handle_uninitialized_extents(handle,
> - inode, map, path, flags, allocated,
> - newblock);
> - return ret;
> +
> + /*
> + * Punch out the map length, but only to the
> + * end of the extent
> + */
> + punched_out = allocated < map->m_len ?
> + allocated : map->m_len;
> +
> + /*
> + * Sense extents need to be converted to
> + * uninitialized, they must fit in an
> + * uninitialized extent
> + */
> + if (punched_out > EXT_UNINIT_MAX_LEN)
> + punched_out = EXT_UNINIT_MAX_LEN;
> +
> + punch_map.m_lblk = map->m_lblk;
> + punch_map.m_pblk = newblock;
> + punch_map.m_len = punched_out;
> + punch_map.m_flags = 0;
> +
> + /* Check to see if the extent needs to be split */
> + if (punch_map.m_len != ee_len ||
> + punch_map.m_lblk != ee_block) {
> +
> + ret = ext4_split_extent(handle, inode,
> + path, &punch_map, 0,
> + EXT4_GET_BLOCKS_PUNCH_OUT_EXT |
> + EXT4_GET_BLOCKS_PRE_IO);
> +
> + if (ret < 0) {
> + err = ret;
> + goto out2;
> + }
> + /*
> + * find extent for the block at
> + * the start of the hole
> + */
> + ext4_ext_drop_refs(path);
> + kfree(path);
> +
> + path = ext4_ext_find_extent(inode,
> + map->m_lblk, NULL);
> + if (IS_ERR(path)) {
> + err = PTR_ERR(path);
> + path = NULL;
> + goto out2;
> + }
> +
> + depth = ext_depth(inode);
> + ex = path[depth].p_ext;
> + ee_len = ext4_ext_get_actual_len(ex);
> + ee_block = le32_to_cpu(ex->ee_block);
> + ee_start = ext4_ext_pblock(ex);
> +
> + }
> +
> + ext4_ext_mark_uninitialized(ex);
> +
> + err = ext4_ext_remove_space(inode, map->m_lblk,
> + map->m_lblk + punched_out);
> +
> + goto out2;
> }
> }
>
> @@ -3542,7 +3614,11 @@ out2:
> }
> trace_ext4_ext_map_blocks_exit(inode, map->m_lblk,
> newblock, map->m_len, err ? err : allocated);
> - return err ? err : allocated;
> +
> + result = (flags & EXT4_GET_BLOCKS_PUNCH_OUT_EXT) ?
> + punched_out : allocated;
> +
> + return err ? err : result;
> }
>
> void ext4_ext_truncate(struct inode *inode)