Commit e861304b authored by Allison Henderson's avatar Allison Henderson Committed by Theodore Ts'o

ext4: add "punch hole" flag to ext4_map_blocks()

This patch adds a new flag to ext4_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.

[ext4 punch hole patch series 4/5 v7]
Signed-off-by: default avatarAllison Henderson <achender@us.ibm.com>
Signed-off-by: default avatar"Theodore Ts'o" <tytso@mit.edu>
Reviewed-by: default avatarMingming Cao <cmm@us.ibm.com>
parent d583fb87
...@@ -3298,15 +3298,19 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, ...@@ -3298,15 +3298,19 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
ext4_fsblk_t newblock = 0; ext4_fsblk_t newblock = 0;
int err = 0, depth, ret; int err = 0, depth, ret;
unsigned int allocated = 0; unsigned int allocated = 0;
unsigned int punched_out = 0;
unsigned int result = 0;
struct ext4_allocation_request ar; struct ext4_allocation_request ar;
ext4_io_end_t *io = EXT4_I(inode)->cur_aio_dio; 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", ext_debug("blocks %u/%u requested for inode %lu\n",
map->m_lblk, map->m_len, inode->i_ino); map->m_lblk, map->m_len, inode->i_ino);
trace_ext4_ext_map_blocks_enter(inode, map->m_lblk, map->m_len, flags); trace_ext4_ext_map_blocks_enter(inode, map->m_lblk, map->m_len, flags);
/* check in cache */ /* 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 (!newex.ee_start_lo && !newex.ee_start_hi) {
if ((flags & EXT4_GET_BLOCKS_CREATE) == 0) { if ((flags & EXT4_GET_BLOCKS_CREATE) == 0) {
/* /*
...@@ -3371,17 +3375,85 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, ...@@ -3371,17 +3375,85 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
ext_debug("%u fit into %u:%d -> %llu\n", map->m_lblk, ext_debug("%u fit into %u:%d -> %llu\n", map->m_lblk,
ee_block, ee_len, newblock); ee_block, ee_len, newblock);
/* Do not put uninitialized extent in the cache */ if ((flags & EXT4_GET_BLOCKS_PUNCH_OUT_EXT) == 0) {
/*
* Do not put uninitialized extent
* in the cache
*/
if (!ext4_ext_is_uninitialized(ex)) { if (!ext4_ext_is_uninitialized(ex)) {
ext4_ext_put_in_cache(inode, ee_block, ext4_ext_put_in_cache(inode, ee_block,
ee_len, ee_start); ee_len, ee_start);
goto out; goto out;
} }
ret = ext4_ext_handle_uninitialized_extents(handle, ret = ext4_ext_handle_uninitialized_extents(
inode, map, path, flags, allocated, handle, inode, map, path, flags,
newblock); allocated, newblock);
return ret; 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;
}
} }
/* /*
...@@ -3525,7 +3597,11 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, ...@@ -3525,7 +3597,11 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
} }
trace_ext4_ext_map_blocks_exit(inode, map->m_lblk, trace_ext4_ext_map_blocks_exit(inode, map->m_lblk,
newblock, map->m_len, err ? err : allocated); 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) void ext4_ext_truncate(struct inode *inode)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment