Skip to content

Commit

Permalink
ext4: update extent status tree after an extent is zeroed out
Browse files Browse the repository at this point in the history
When we try to split an extent, this extent could be zeroed out and mark
as initialized.  But we don't know this in ext4_map_blocks because it
only returns a length of allocated extent.  Meanwhile we will mark this
extent as uninitialized because we only check m_flags.

This commit update extent status tree when we try to split an unwritten
extent.  We don't need to worry about the status of this extent because
we always mark it as initialized.

Signed-off-by: Zheng Liu <wenqing.lz@taobao.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Cc: Dmitry Monakhov <dmonakhov@openvz.org>
  • Loading branch information
Zheng Liu authored and Theodore Ts'o committed Mar 11, 2013
1 parent cdee784 commit adb2355
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 4 deletions.
35 changes: 31 additions & 4 deletions fs/ext4/extents.c
Original file line number Diff line number Diff line change
Expand Up @@ -2925,7 +2925,7 @@ static int ext4_split_extent_at(handle_t *handle,
{
ext4_fsblk_t newblock;
ext4_lblk_t ee_block;
struct ext4_extent *ex, newex, orig_ex;
struct ext4_extent *ex, newex, orig_ex, zero_ex;
struct ext4_extent *ex2 = NULL;
unsigned int ee_len, depth;
int err = 0;
Expand Down Expand Up @@ -2996,19 +2996,39 @@ static int ext4_split_extent_at(handle_t *handle,
err = ext4_ext_insert_extent(handle, inode, path, &newex, flags);
if (err == -ENOSPC && (EXT4_EXT_MAY_ZEROOUT & split_flag)) {
if (split_flag & (EXT4_EXT_DATA_VALID1|EXT4_EXT_DATA_VALID2)) {
if (split_flag & EXT4_EXT_DATA_VALID1)
if (split_flag & EXT4_EXT_DATA_VALID1) {
err = ext4_ext_zeroout(inode, ex2);
else
zero_ex.ee_block = ex2->ee_block;
zero_ex.ee_len = ext4_ext_get_actual_len(ex2);
ext4_ext_store_pblock(&zero_ex,
ext4_ext_pblock(ex2));
} else {
err = ext4_ext_zeroout(inode, ex);
} else
zero_ex.ee_block = ex->ee_block;
zero_ex.ee_len = ext4_ext_get_actual_len(ex);
ext4_ext_store_pblock(&zero_ex,
ext4_ext_pblock(ex));
}
} else {
err = ext4_ext_zeroout(inode, &orig_ex);
zero_ex.ee_block = orig_ex.ee_block;
zero_ex.ee_len = ext4_ext_get_actual_len(&orig_ex);
ext4_ext_store_pblock(&zero_ex,
ext4_ext_pblock(&orig_ex));
}

if (err)
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(handle, inode, path, ex);
err = ext4_ext_dirty(handle, inode, path + path->p_depth);
if (err)
goto fix_extent_len;

/* update extent status tree */
err = ext4_es_zeroout(inode, &zero_ex);

goto out;
} else if (err)
goto fix_extent_len;
Expand Down Expand Up @@ -3150,6 +3170,7 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
ee_block = le32_to_cpu(ex->ee_block);
ee_len = ext4_ext_get_actual_len(ex);
allocated = ee_len - (map->m_lblk - ee_block);
zero_ex.ee_len = 0;

trace_ext4_ext_convert_to_initialized_enter(inode, map, ex);

Expand Down Expand Up @@ -3247,6 +3268,9 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
err = ext4_ext_zeroout(inode, ex);
if (err)
goto out;
zero_ex.ee_block = ex->ee_block;
zero_ex.ee_len = ext4_ext_get_actual_len(ex);
ext4_ext_store_pblock(&zero_ex, ext4_ext_pblock(ex));

err = ext4_ext_get_access(handle, inode, path + depth);
if (err)
Expand Down Expand Up @@ -3305,6 +3329,9 @@ static int ext4_ext_convert_to_initialized(handle_t *handle,
err = allocated;

out:
/* If we have gotten a failure, don't zero out status tree */
if (!err)
err = ext4_es_zeroout(inode, &zero_ex);
return err ? err : allocated;
}

Expand Down
17 changes: 17 additions & 0 deletions fs/ext4/extents_status.c
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,23 @@ int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk,
return err;
}

int ext4_es_zeroout(struct inode *inode, struct ext4_extent *ex)
{
ext4_lblk_t ee_block;
ext4_fsblk_t ee_pblock;
unsigned int ee_len;

ee_block = le32_to_cpu(ex->ee_block);
ee_len = ext4_ext_get_actual_len(ex);
ee_pblock = ext4_ext_pblock(ex);

if (ee_len == 0)
return 0;

return ext4_es_insert_extent(inode, ee_block, ee_len, ee_pblock,
EXTENT_STATUS_WRITTEN);
}

static int ext4_es_shrink(struct shrinker *shrink, struct shrink_control *sc)
{
struct ext4_sb_info *sbi = container_of(shrink,
Expand Down
3 changes: 3 additions & 0 deletions fs/ext4/extents_status.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
EXTENT_STATUS_DELAYED | \
EXTENT_STATUS_HOLE)

struct ext4_extent;

struct extent_status {
struct rb_node rb_node;
ext4_lblk_t es_lblk; /* first logical block extent covers */
Expand All @@ -64,6 +66,7 @@ extern void ext4_es_find_delayed_extent(struct inode *inode, ext4_lblk_t lblk,
struct extent_status *es);
extern int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk,
struct extent_status *es);
extern int ext4_es_zeroout(struct inode *inode, struct ext4_extent *ex);

static inline int ext4_es_is_written(struct extent_status *es)
{
Expand Down
10 changes: 10 additions & 0 deletions fs/ext4/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,15 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
}
#endif

/*
* If the extent has been zeroed out, we don't need to update
* extent status tree.
*/
if ((flags & EXT4_GET_BLOCKS_PRE_IO) &&
ext4_es_lookup_extent(inode, map->m_lblk, &es)) {
if (ext4_es_is_written(&es))
goto has_zeroout;
}
status = map->m_flags & EXT4_MAP_UNWRITTEN ?
EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
if (!(flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) &&
Expand All @@ -734,6 +743,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
retval = ret;
}

has_zeroout:
up_write((&EXT4_I(inode)->i_data_sem));
if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) {
int ret = check_block_validity(inode, map);
Expand Down

0 comments on commit adb2355

Please sign in to comment.