Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 296578
b: refs/heads/master
c: 5f95d21
h: refs/heads/master
v: v3
  • Loading branch information
Lukas Czerner authored and Theodore Ts'o committed Mar 20, 2012
1 parent 447851b commit fbf26d7
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 83 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 3339578f05787259917788f461f4196b7349c2a4
refs/heads/master: 5f95d21fb6f2aaa52830e5b7fb405f6c71d3ab85
170 changes: 88 additions & 82 deletions trunk/fs/ext4/extents.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,28 @@

#include <trace/events/ext4.h>

/*
* used by extent splitting.
*/
#define EXT4_EXT_MAY_ZEROOUT 0x1 /* safe to zeroout if split fails \
due to ENOSPC */
#define EXT4_EXT_MARK_UNINIT1 0x2 /* mark first half uninitialized */
#define EXT4_EXT_MARK_UNINIT2 0x4 /* mark second half uninitialized */

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_split_extent_at(handle_t *handle,
struct inode *inode,
struct ext4_ext_path *path,
ext4_lblk_t split,
int split_flag,
int flags);

static int ext4_ext_truncate_extend_restart(handle_t *handle,
struct inode *inode,
int needed)
Expand Down Expand Up @@ -2310,7 +2325,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
struct ext4_extent *ex;

/* the header must be checked already in ext4_ext_remove_space() */
ext_debug("truncate since %u in leaf\n", start);
ext_debug("truncate since %u in leaf to %u\n", start, end);
if (!path[depth].p_hdr)
path[depth].p_hdr = ext_block_hdr(path[depth].p_bh);
eh = path[depth].p_hdr;
Expand Down Expand Up @@ -2345,7 +2360,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
ext_debug(" border %u:%u\n", a, b);

/* If this extent is beyond the end of the hole, skip it */
if (end <= ex_ee_block) {
if (end < ex_ee_block) {
ex--;
ex_ee_block = le32_to_cpu(ex->ee_block);
ex_ee_len = ext4_ext_get_actual_len(ex);
Expand Down Expand Up @@ -2484,7 +2499,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);
Expand All @@ -2493,7 +2509,7 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start)
handle_t *handle;
int i, err;

ext_debug("truncate since %u\n", start);
ext_debug("truncate since %u to %u\n", start, end);

/* probably first extent we're gonna free will be last in block */
handle = ext4_journal_start(inode, depth + 1);
Expand All @@ -2505,6 +2521,61 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start)

trace_ext4_ext_remove_space(inode, start, depth);

/*
* Check if we are removing extents inside the extent tree. If that
* is the case, we are going to punch a hole inside the extent tree
* so we have to check whether we need to split the extent covering
* the last block to remove so we can easily remove the part of it
* in ext4_ext_rm_leaf().
*/
if (end < EXT_MAX_BLOCKS - 1) {
struct ext4_extent *ex;
ext4_lblk_t ee_block;

/* find extent for this block */
path = ext4_ext_find_extent(inode, end, NULL);
if (IS_ERR(path)) {
ext4_journal_stop(handle);
return PTR_ERR(path);
}
depth = ext_depth(inode);
ex = path[depth].p_ext;
if (!ex)
goto cont;

ee_block = le32_to_cpu(ex->ee_block);

/*
* See if the last block is inside the extent, if so split
* the extent at 'end' block so we can easily remove the
* tail of the first part of the split extent in
* ext4_ext_rm_leaf().
*/
if (end >= ee_block &&
end < ee_block + ext4_ext_get_actual_len(ex) - 1) {
int split_flag = 0;

if (ext4_ext_is_uninitialized(ex))
split_flag = EXT4_EXT_MARK_UNINIT1 |
EXT4_EXT_MARK_UNINIT2;

/*
* Split the extent in two so that 'end' is the last
* block in the first new extent
*/
err = ext4_split_extent_at(handle, inode, path,
end + 1, split_flag,
EXT4_GET_BLOCKS_PRE_IO |
EXT4_GET_BLOCKS_PUNCH_OUT_EXT);

if (err < 0)
goto out;
}
ext4_ext_drop_refs(path);
kfree(path);
}
cont:

/*
* We start scanning from right side, freeing all the blocks
* after i_size and walking into the tree depth-wise.
Expand All @@ -2517,6 +2588,7 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start)
}
path[0].p_depth = depth;
path[0].p_hdr = ext_inode_hdr(inode);

if (ext4_ext_check(inode, path[0].p_hdr, depth)) {
err = -EIO;
goto out;
Expand All @@ -2528,7 +2600,7 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start)
/* this is leaf block */
err = ext4_ext_rm_leaf(handle, inode, path,
&partial_cluster, start,
EXT_MAX_BLOCKS - 1);
end);
/* root level has p_bh == NULL, brelse() eats this */
brelse(path[i].p_bh);
path[i].p_bh = NULL;
Expand Down Expand Up @@ -2710,14 +2782,6 @@ static int ext4_ext_zeroout(struct inode *inode, struct ext4_extent *ex)
return ret;
}

/*
* used by extent splitting.
*/
#define EXT4_EXT_MAY_ZEROOUT 0x1 /* safe to zeroout if split fails \
due to ENOSPC */
#define EXT4_EXT_MARK_UNINIT1 0x2 /* mark first half uninitialized */
#define EXT4_EXT_MARK_UNINIT2 0x4 /* mark second half uninitialized */

/*
* ext4_split_extent_at() splits an extent at given block.
*
Expand Down Expand Up @@ -4230,7 +4294,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_BLOCKS - 1);

/* In a multi-transaction truncate, we only make the final
* transaction synchronous.
Expand Down Expand Up @@ -4707,14 +4771,12 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length)
{
struct inode *inode = file->f_path.dentry->d_inode;
struct super_block *sb = inode->i_sb;
struct ext4_ext_cache cache_ex;
ext4_lblk_t first_block, last_block, num_blocks, iblock, max_blocks;
ext4_lblk_t first_block, stop_block;
struct address_space *mapping = inode->i_mapping;
struct ext4_map_blocks map;
handle_t *handle;
loff_t first_page, last_page, page_len;
loff_t first_page_offset, last_page_offset;
int ret, credits, blocks_released, err = 0;
int credits, err = 0;

/* No need to punch hole beyond i_size */
if (offset >= inode->i_size)
Expand All @@ -4730,10 +4792,6 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length)
offset;
}

first_block = (offset + sb->s_blocksize - 1) >>
EXT4_BLOCK_SIZE_BITS(sb);
last_block = (offset + length) >> EXT4_BLOCK_SIZE_BITS(sb);

first_page = (offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
last_page = (offset + length) >> PAGE_CACHE_SHIFT;

Expand Down Expand Up @@ -4812,7 +4870,6 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length)
}
}


/*
* If i_size is contained in the last page, we need to
* unmap and zero the partial page after i_size
Expand All @@ -4832,73 +4889,22 @@ int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length)
}
}

first_block = (offset + sb->s_blocksize - 1) >>
EXT4_BLOCK_SIZE_BITS(sb);
stop_block = (offset + length) >> EXT4_BLOCK_SIZE_BITS(sb);

/* If there are no blocks to remove, return now */
if (first_block >= last_block)
if (first_block >= stop_block)
goto out;

down_write(&EXT4_I(inode)->i_data_sem);
ext4_ext_invalidate_cache(inode);
ext4_discard_preallocations(inode);

/*
* Loop over all the blocks and identify blocks
* that need to be punched out
*/
iblock = first_block;
blocks_released = 0;
while (iblock < last_block) {
max_blocks = last_block - iblock;
num_blocks = 1;
memset(&map, 0, sizeof(map));
map.m_lblk = iblock;
map.m_len = max_blocks;
ret = ext4_ext_map_blocks(handle, inode, &map,
EXT4_GET_BLOCKS_PUNCH_OUT_EXT);

if (ret > 0) {
blocks_released += ret;
num_blocks = ret;
} else if (ret == 0) {
/*
* If map blocks could not find the block,
* then it is in a hole. If the hole was
* not already cached, then map blocks should
* put it in the cache. So we can get the hole
* out of the cache
*/
memset(&cache_ex, 0, sizeof(cache_ex));
if ((ext4_ext_check_cache(inode, iblock, &cache_ex)) &&
!cache_ex.ec_start) {
err = ext4_ext_remove_space(inode, first_block, stop_block - 1);

/* The hole is cached */
num_blocks = cache_ex.ec_block +
cache_ex.ec_len - iblock;

} else {
/* The block could not be identified */
err = -EIO;
break;
}
} else {
/* Map blocks error */
err = ret;
break;
}

if (num_blocks == 0) {
/* This condition should never happen */
ext_debug("Block lookup failed");
err = -EIO;
break;
}

iblock += num_blocks;
}

if (blocks_released > 0) {
ext4_ext_invalidate_cache(inode);
ext4_discard_preallocations(inode);
}
ext4_ext_invalidate_cache(inode);
ext4_discard_preallocations(inode);

if (IS_SYNC(inode))
ext4_handle_sync(handle);
Expand Down

0 comments on commit fbf26d7

Please sign in to comment.