Skip to content

Commit f7fec03

Browse files
gnehzuiltytso
authored andcommitted
ext4: track all extent status in extent status tree
By recording the phycisal block and status, extent status tree is able to track the status of every extents. When we call _map_blocks functions to lookup an extent or create a new written/unwritten/delayed extent, this extent will be inserted into extent status tree. We don't load all extents from disk in alloc_inode() because it costs too much memory, and if a file is opened and closed frequently it will takes too much time to load all extent information. So currently when we create/lookup an extent, this extent will be inserted into extent status tree. Hence, the extent status tree may not comprehensively contain all of the extents found in the file. Here a condition we need to take care is that an extent might contains unwritten and delayed status simultaneously because an extent is delayed allocated and could be allocated by fallocate. At this time we need to keep delayed status because later we need to update delayed reservation space using it. Signed-off-by: Zheng Liu <[email protected]> Signed-off-by: "Theodore Ts'o" <[email protected]> Cc: Jan kara <[email protected]>
1 parent a25a4e1 commit f7fec03

File tree

3 files changed

+69
-30
lines changed

3 files changed

+69
-30
lines changed

fs/ext4/ext4.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2602,6 +2602,9 @@ extern struct ext4_ext_path *ext4_ext_find_extent(struct inode *, ext4_lblk_t,
26022602
struct ext4_ext_path *);
26032603
extern void ext4_ext_drop_refs(struct ext4_ext_path *);
26042604
extern int ext4_ext_check_inode(struct inode *inode);
2605+
extern int ext4_find_delalloc_range(struct inode *inode,
2606+
ext4_lblk_t lblk_start,
2607+
ext4_lblk_t lblk_end);
26052608
extern int ext4_find_delalloc_cluster(struct inode *inode, ext4_lblk_t lblk);
26062609
extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
26072610
__u64 start, __u64 len);

fs/ext4/extents.c

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2076,8 +2076,18 @@ static int ext4_fill_fiemap_extents(struct inode *inode,
20762076
break;
20772077
}
20782078

2079-
/* This is possible iff next == next_del == EXT_MAX_BLOCKS */
2080-
if (next == next_del) {
2079+
/*
2080+
* This is possible iff next == next_del == EXT_MAX_BLOCKS.
2081+
* we need to check next == EXT_MAX_BLOCKS because it is
2082+
* possible that an extent is with unwritten and delayed
2083+
* status due to when an extent is delayed allocated and
2084+
* is allocated by fallocate status tree will track both of
2085+
* them in a extent.
2086+
*
2087+
* So we could return a unwritten and delayed extent, and
2088+
* its block is equal to 'next'.
2089+
*/
2090+
if (next == next_del && next == EXT_MAX_BLOCKS) {
20812091
flags |= FIEMAP_EXTENT_LAST;
20822092
if (unlikely(next_del != EXT_MAX_BLOCKS ||
20832093
next != EXT_MAX_BLOCKS)) {
@@ -3522,9 +3532,9 @@ static int check_eofblocks_fl(handle_t *handle, struct inode *inode,
35223532
*
35233533
* Return 1 if there is a delalloc block in the range, otherwise 0.
35243534
*/
3525-
static int ext4_find_delalloc_range(struct inode *inode,
3526-
ext4_lblk_t lblk_start,
3527-
ext4_lblk_t lblk_end)
3535+
int ext4_find_delalloc_range(struct inode *inode,
3536+
ext4_lblk_t lblk_start,
3537+
ext4_lblk_t lblk_end)
35283538
{
35293539
struct extent_status es;
35303540

fs/ext4/inode.c

Lines changed: 51 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -526,20 +526,26 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
526526
retval = ext4_ind_map_blocks(handle, inode, map, flags &
527527
EXT4_GET_BLOCKS_KEEP_SIZE);
528528
}
529+
if (retval > 0) {
530+
int ret;
531+
unsigned long long status;
532+
533+
status = map->m_flags & EXT4_MAP_UNWRITTEN ?
534+
EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
535+
if (!(flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) &&
536+
ext4_find_delalloc_range(inode, map->m_lblk,
537+
map->m_lblk + map->m_len - 1))
538+
status |= EXTENT_STATUS_DELAYED;
539+
ret = ext4_es_insert_extent(inode, map->m_lblk,
540+
map->m_len, map->m_pblk, status);
541+
if (ret < 0)
542+
retval = ret;
543+
}
529544
if (!(flags & EXT4_GET_BLOCKS_NO_LOCK))
530545
up_read((&EXT4_I(inode)->i_data_sem));
531546

532547
if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) {
533-
int ret;
534-
if (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) {
535-
/* delayed alloc may be allocated by fallocate and
536-
* coverted to initialized by directIO.
537-
* we need to handle delayed extent here.
538-
*/
539-
down_write((&EXT4_I(inode)->i_data_sem));
540-
goto delayed_mapped;
541-
}
542-
ret = check_block_validity(inode, map);
548+
int ret = check_block_validity(inode, map);
543549
if (ret != 0)
544550
return ret;
545551
}
@@ -608,18 +614,23 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
608614
(flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE))
609615
ext4_da_update_reserve_space(inode, retval, 1);
610616
}
611-
if (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) {
617+
if (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE)
612618
ext4_clear_inode_state(inode, EXT4_STATE_DELALLOC_RESERVED);
613619

614-
if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) {
615-
int ret;
616-
delayed_mapped:
617-
/* delayed allocation blocks has been allocated */
618-
ret = ext4_es_remove_extent(inode, map->m_lblk,
619-
map->m_len);
620-
if (ret < 0)
621-
retval = ret;
622-
}
620+
if (retval > 0) {
621+
int ret;
622+
unsigned long long status;
623+
624+
status = map->m_flags & EXT4_MAP_UNWRITTEN ?
625+
EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
626+
if (!(flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) &&
627+
ext4_find_delalloc_range(inode, map->m_lblk,
628+
map->m_lblk + map->m_len - 1))
629+
status |= EXTENT_STATUS_DELAYED;
630+
ret = ext4_es_insert_extent(inode, map->m_lblk, map->m_len,
631+
map->m_pblk, status);
632+
if (ret < 0)
633+
retval = ret;
623634
}
624635

625636
up_write((&EXT4_I(inode)->i_data_sem));
@@ -1765,23 +1776,28 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock,
17651776
retval = ext4_ind_map_blocks(NULL, inode, map, 0);
17661777

17671778
if (retval == 0) {
1779+
int ret;
17681780
/*
17691781
* XXX: __block_prepare_write() unmaps passed block,
17701782
* is it OK?
17711783
*/
17721784
/* If the block was allocated from previously allocated cluster,
17731785
* then we dont need to reserve it again. */
17741786
if (!(map->m_flags & EXT4_MAP_FROM_CLUSTER)) {
1775-
retval = ext4_da_reserve_space(inode, iblock);
1776-
if (retval)
1787+
ret = ext4_da_reserve_space(inode, iblock);
1788+
if (ret) {
17771789
/* not enough space to reserve */
1790+
retval = ret;
17781791
goto out_unlock;
1792+
}
17791793
}
17801794

1781-
retval = ext4_es_insert_extent(inode, map->m_lblk, map->m_len,
1782-
~0, EXTENT_STATUS_DELAYED);
1783-
if (retval)
1795+
ret = ext4_es_insert_extent(inode, map->m_lblk, map->m_len,
1796+
~0, EXTENT_STATUS_DELAYED);
1797+
if (ret) {
1798+
retval = ret;
17841799
goto out_unlock;
1800+
}
17851801

17861802
/* Clear EXT4_MAP_FROM_CLUSTER flag since its purpose is served
17871803
* and it should not appear on the bh->b_state.
@@ -1791,6 +1807,16 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock,
17911807
map_bh(bh, inode->i_sb, invalid_block);
17921808
set_buffer_new(bh);
17931809
set_buffer_delay(bh);
1810+
} else if (retval > 0) {
1811+
int ret;
1812+
unsigned long long status;
1813+
1814+
status = map->m_flags & EXT4_MAP_UNWRITTEN ?
1815+
EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
1816+
ret = ext4_es_insert_extent(inode, map->m_lblk, map->m_len,
1817+
map->m_pblk, status);
1818+
if (ret != 0)
1819+
retval = ret;
17941820
}
17951821

17961822
out_unlock:

0 commit comments

Comments
 (0)