aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorZheng Liu <wenqing.lz@taobao.com>2013-02-18 00:28:47 -0500
committerTheodore Ts'o <tytso@mit.edu>2013-02-18 00:28:47 -0500
commitf7fec032aa782d3fd7e51fbdf08aa3a296c01500 (patch)
tree3efc694cb743f6248721566f35e0f43352f9eb1f /fs
parenta25a4e1a5d5dc0f97dddbca44e695c532d8228c1 (diff)
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 <wenqing.lz@taobao.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu> Cc: Jan kara <jack@suse.cz>
Diffstat (limited to 'fs')
-rw-r--r--fs/ext4/ext4.h3
-rw-r--r--fs/ext4/extents.c20
-rw-r--r--fs/ext4/inode.c76
3 files changed, 69 insertions, 30 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index fc1c0375c9f2..5c31d6ac9500 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -2602,6 +2602,9 @@ extern struct ext4_ext_path *ext4_ext_find_extent(struct inode *, ext4_lblk_t,
2602 struct ext4_ext_path *); 2602 struct ext4_ext_path *);
2603extern void ext4_ext_drop_refs(struct ext4_ext_path *); 2603extern void ext4_ext_drop_refs(struct ext4_ext_path *);
2604extern int ext4_ext_check_inode(struct inode *inode); 2604extern int ext4_ext_check_inode(struct inode *inode);
2605extern int ext4_find_delalloc_range(struct inode *inode,
2606 ext4_lblk_t lblk_start,
2607 ext4_lblk_t lblk_end);
2605extern int ext4_find_delalloc_cluster(struct inode *inode, ext4_lblk_t lblk); 2608extern int ext4_find_delalloc_cluster(struct inode *inode, ext4_lblk_t lblk);
2606extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, 2609extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
2607 __u64 start, __u64 len); 2610 __u64 start, __u64 len);
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index cae8ae3c1746..be0b1b3eed97 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -2076,8 +2076,18 @@ static int ext4_fill_fiemap_extents(struct inode *inode,
2076 break; 2076 break;
2077 } 2077 }
2078 2078
2079 /* This is possible iff next == next_del == EXT_MAX_BLOCKS */ 2079 /*
2080 if (next == next_del) { 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) {
2081 flags |= FIEMAP_EXTENT_LAST; 2091 flags |= FIEMAP_EXTENT_LAST;
2082 if (unlikely(next_del != EXT_MAX_BLOCKS || 2092 if (unlikely(next_del != EXT_MAX_BLOCKS ||
2083 next != EXT_MAX_BLOCKS)) { 2093 next != EXT_MAX_BLOCKS)) {
@@ -3522,9 +3532,9 @@ out:
3522 * 3532 *
3523 * Return 1 if there is a delalloc block in the range, otherwise 0. 3533 * Return 1 if there is a delalloc block in the range, otherwise 0.
3524 */ 3534 */
3525static int ext4_find_delalloc_range(struct inode *inode, 3535int ext4_find_delalloc_range(struct inode *inode,
3526 ext4_lblk_t lblk_start, 3536 ext4_lblk_t lblk_start,
3527 ext4_lblk_t lblk_end) 3537 ext4_lblk_t lblk_end)
3528{ 3538{
3529 struct extent_status es; 3539 struct extent_status es;
3530 3540
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 084b8212cf95..576b586b61aa 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -526,20 +526,26 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
526 retval = ext4_ind_map_blocks(handle, inode, map, flags & 526 retval = ext4_ind_map_blocks(handle, inode, map, flags &
527 EXT4_GET_BLOCKS_KEEP_SIZE); 527 EXT4_GET_BLOCKS_KEEP_SIZE);
528 } 528 }
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 }
529 if (!(flags & EXT4_GET_BLOCKS_NO_LOCK)) 544 if (!(flags & EXT4_GET_BLOCKS_NO_LOCK))
530 up_read((&EXT4_I(inode)->i_data_sem)); 545 up_read((&EXT4_I(inode)->i_data_sem));
531 546
532 if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) { 547 if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) {
533 int ret; 548 int ret = check_block_validity(inode, map);
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);
543 if (ret != 0) 549 if (ret != 0)
544 return ret; 550 return ret;
545 } 551 }
@@ -608,18 +614,23 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode,
608 (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE)) 614 (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE))
609 ext4_da_update_reserve_space(inode, retval, 1); 615 ext4_da_update_reserve_space(inode, retval, 1);
610 } 616 }
611 if (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) { 617 if (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE)
612 ext4_clear_inode_state(inode, EXT4_STATE_DELALLOC_RESERVED); 618 ext4_clear_inode_state(inode, EXT4_STATE_DELALLOC_RESERVED);
613 619
614 if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) { 620 if (retval > 0) {
615 int ret; 621 int ret;
616delayed_mapped: 622 unsigned long long status;
617 /* delayed allocation blocks has been allocated */ 623
618 ret = ext4_es_remove_extent(inode, map->m_lblk, 624 status = map->m_flags & EXT4_MAP_UNWRITTEN ?
619 map->m_len); 625 EXTENT_STATUS_UNWRITTEN : EXTENT_STATUS_WRITTEN;
620 if (ret < 0) 626 if (!(flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE) &&
621 retval = ret; 627 ext4_find_delalloc_range(inode, map->m_lblk,
622 } 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;
623 } 634 }
624 635
625 up_write((&EXT4_I(inode)->i_data_sem)); 636 up_write((&EXT4_I(inode)->i_data_sem));
@@ -1765,6 +1776,7 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock,
1765 retval = ext4_ind_map_blocks(NULL, inode, map, 0); 1776 retval = ext4_ind_map_blocks(NULL, inode, map, 0);
1766 1777
1767 if (retval == 0) { 1778 if (retval == 0) {
1779 int ret;
1768 /* 1780 /*
1769 * XXX: __block_prepare_write() unmaps passed block, 1781 * XXX: __block_prepare_write() unmaps passed block,
1770 * is it OK? 1782 * is it OK?
@@ -1772,16 +1784,20 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock,
1772 /* If the block was allocated from previously allocated cluster, 1784 /* If the block was allocated from previously allocated cluster,
1773 * then we dont need to reserve it again. */ 1785 * then we dont need to reserve it again. */
1774 if (!(map->m_flags & EXT4_MAP_FROM_CLUSTER)) { 1786 if (!(map->m_flags & EXT4_MAP_FROM_CLUSTER)) {
1775 retval = ext4_da_reserve_space(inode, iblock); 1787 ret = ext4_da_reserve_space(inode, iblock);
1776 if (retval) 1788 if (ret) {
1777 /* not enough space to reserve */ 1789 /* not enough space to reserve */
1790 retval = ret;
1778 goto out_unlock; 1791 goto out_unlock;
1792 }
1779 } 1793 }
1780 1794
1781 retval = ext4_es_insert_extent(inode, map->m_lblk, map->m_len, 1795 ret = ext4_es_insert_extent(inode, map->m_lblk, map->m_len,
1782 ~0, EXTENT_STATUS_DELAYED); 1796 ~0, EXTENT_STATUS_DELAYED);
1783 if (retval) 1797 if (ret) {
1798 retval = ret;
1784 goto out_unlock; 1799 goto out_unlock;
1800 }
1785 1801
1786 /* Clear EXT4_MAP_FROM_CLUSTER flag since its purpose is served 1802 /* Clear EXT4_MAP_FROM_CLUSTER flag since its purpose is served
1787 * and it should not appear on the bh->b_state. 1803 * 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,
1791 map_bh(bh, inode->i_sb, invalid_block); 1807 map_bh(bh, inode->i_sb, invalid_block);
1792 set_buffer_new(bh); 1808 set_buffer_new(bh);
1793 set_buffer_delay(bh); 1809 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;
1794 } 1820 }
1795 1821
1796out_unlock: 1822out_unlock: