diff options
author | Zheng Liu <wenqing.lz@taobao.com> | 2014-11-25 11:44:37 -0500 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2014-11-25 11:44:37 -0500 |
commit | 2f8e0a7c6c89f850ebd5d6c0b9a08317030d1b89 (patch) | |
tree | 4e03e95c516bab8be44e23861626f0c0f29a3958 /fs/ext4 | |
parent | cbd7584e6ead1b79fb0b81573f158b57fa1f0b49 (diff) |
ext4: cache extent hole in extent status tree for ext4_da_map_blocks()
Currently extent status tree doesn't cache extent hole when a write
looks up in extent tree to make sure whether a block has been allocated
or not. In this case, we don't put extent hole in extent cache because
later this extent might be removed and a new delayed extent might be
added back. But it will cause a defect when we do a lot of writes. If
we don't put extent hole in extent cache, the following writes also need
to access extent tree to look at whether or not a block has been
allocated. It brings a cache miss. This commit fixes this defect.
Also if the inode doesn't have any extent, this extent hole will be
cached as well.
Cc: Andreas Dilger <adilger.kernel@dilger.ca>
Signed-off-by: Zheng Liu <wenqing.lz@taobao.com>
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Diffstat (limited to 'fs/ext4')
-rw-r--r-- | fs/ext4/ext4.h | 4 | ||||
-rw-r--r-- | fs/ext4/extents.c | 31 | ||||
-rw-r--r-- | fs/ext4/inode.c | 6 |
3 files changed, 19 insertions, 22 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 7b3f3b1decff..98da4cda9d18 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -556,10 +556,8 @@ enum { | |||
556 | #define EXT4_GET_BLOCKS_KEEP_SIZE 0x0080 | 556 | #define EXT4_GET_BLOCKS_KEEP_SIZE 0x0080 |
557 | /* Do not take i_data_sem locking in ext4_map_blocks */ | 557 | /* Do not take i_data_sem locking in ext4_map_blocks */ |
558 | #define EXT4_GET_BLOCKS_NO_LOCK 0x0100 | 558 | #define EXT4_GET_BLOCKS_NO_LOCK 0x0100 |
559 | /* Do not put hole in extent cache */ | ||
560 | #define EXT4_GET_BLOCKS_NO_PUT_HOLE 0x0200 | ||
561 | /* Convert written extents to unwritten */ | 559 | /* Convert written extents to unwritten */ |
562 | #define EXT4_GET_BLOCKS_CONVERT_UNWRITTEN 0x0400 | 560 | #define EXT4_GET_BLOCKS_CONVERT_UNWRITTEN 0x0200 |
563 | 561 | ||
564 | /* | 562 | /* |
565 | * The bit position of these flags must not overlap with any of the | 563 | * The bit position of these flags must not overlap with any of the |
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 7ef2f11aca56..1ee24d74270f 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c | |||
@@ -2306,16 +2306,16 @@ ext4_ext_put_gap_in_cache(struct inode *inode, struct ext4_ext_path *path, | |||
2306 | ext4_lblk_t block) | 2306 | ext4_lblk_t block) |
2307 | { | 2307 | { |
2308 | int depth = ext_depth(inode); | 2308 | int depth = ext_depth(inode); |
2309 | unsigned long len = 0; | 2309 | ext4_lblk_t len; |
2310 | ext4_lblk_t lblock = 0; | 2310 | ext4_lblk_t lblock; |
2311 | struct ext4_extent *ex; | 2311 | struct ext4_extent *ex; |
2312 | struct extent_status es; | ||
2312 | 2313 | ||
2313 | ex = path[depth].p_ext; | 2314 | ex = path[depth].p_ext; |
2314 | if (ex == NULL) { | 2315 | if (ex == NULL) { |
2315 | /* | 2316 | /* there is no extent yet, so gap is [0;-] */ |
2316 | * there is no extent yet, so gap is [0;-] and we | 2317 | lblock = 0; |
2317 | * don't cache it | 2318 | len = EXT_MAX_BLOCKS; |
2318 | */ | ||
2319 | ext_debug("cache gap(whole file):"); | 2319 | ext_debug("cache gap(whole file):"); |
2320 | } else if (block < le32_to_cpu(ex->ee_block)) { | 2320 | } else if (block < le32_to_cpu(ex->ee_block)) { |
2321 | lblock = block; | 2321 | lblock = block; |
@@ -2324,9 +2324,6 @@ ext4_ext_put_gap_in_cache(struct inode *inode, struct ext4_ext_path *path, | |||
2324 | block, | 2324 | block, |
2325 | le32_to_cpu(ex->ee_block), | 2325 | le32_to_cpu(ex->ee_block), |
2326 | ext4_ext_get_actual_len(ex)); | 2326 | ext4_ext_get_actual_len(ex)); |
2327 | if (!ext4_find_delalloc_range(inode, lblock, lblock + len - 1)) | ||
2328 | ext4_es_insert_extent(inode, lblock, len, ~0, | ||
2329 | EXTENT_STATUS_HOLE); | ||
2330 | } else if (block >= le32_to_cpu(ex->ee_block) | 2327 | } else if (block >= le32_to_cpu(ex->ee_block) |
2331 | + ext4_ext_get_actual_len(ex)) { | 2328 | + ext4_ext_get_actual_len(ex)) { |
2332 | ext4_lblk_t next; | 2329 | ext4_lblk_t next; |
@@ -2340,14 +2337,19 @@ ext4_ext_put_gap_in_cache(struct inode *inode, struct ext4_ext_path *path, | |||
2340 | block); | 2337 | block); |
2341 | BUG_ON(next == lblock); | 2338 | BUG_ON(next == lblock); |
2342 | len = next - lblock; | 2339 | len = next - lblock; |
2343 | if (!ext4_find_delalloc_range(inode, lblock, lblock + len - 1)) | ||
2344 | ext4_es_insert_extent(inode, lblock, len, ~0, | ||
2345 | EXTENT_STATUS_HOLE); | ||
2346 | } else { | 2340 | } else { |
2347 | BUG(); | 2341 | BUG(); |
2348 | } | 2342 | } |
2349 | 2343 | ||
2350 | ext_debug(" -> %u:%lu\n", lblock, len); | 2344 | ext4_es_find_delayed_extent_range(inode, lblock, lblock + len - 1, &es); |
2345 | if (es.es_len) { | ||
2346 | /* There's delayed extent containing lblock? */ | ||
2347 | if (es.es_lblk <= lblock) | ||
2348 | return; | ||
2349 | len = min(es.es_lblk - lblock, len); | ||
2350 | } | ||
2351 | ext_debug(" -> %u:%u\n", lblock, len); | ||
2352 | ext4_es_insert_extent(inode, lblock, len, ~0, EXTENT_STATUS_HOLE); | ||
2351 | } | 2353 | } |
2352 | 2354 | ||
2353 | /* | 2355 | /* |
@@ -4368,8 +4370,7 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, | |||
4368 | * put just found gap into cache to speed up | 4370 | * put just found gap into cache to speed up |
4369 | * subsequent requests | 4371 | * subsequent requests |
4370 | */ | 4372 | */ |
4371 | if ((flags & EXT4_GET_BLOCKS_NO_PUT_HOLE) == 0) | 4373 | ext4_ext_put_gap_in_cache(inode, path, map->m_lblk); |
4372 | ext4_ext_put_gap_in_cache(inode, path, map->m_lblk); | ||
4373 | goto out2; | 4374 | goto out2; |
4374 | } | 4375 | } |
4375 | 4376 | ||
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 2315e45161ee..d5a46a8df70b 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -1432,11 +1432,9 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock, | |||
1432 | if (ext4_has_inline_data(inode)) | 1432 | if (ext4_has_inline_data(inode)) |
1433 | retval = 0; | 1433 | retval = 0; |
1434 | else if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) | 1434 | else if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) |
1435 | retval = ext4_ext_map_blocks(NULL, inode, map, | 1435 | retval = ext4_ext_map_blocks(NULL, inode, map, 0); |
1436 | EXT4_GET_BLOCKS_NO_PUT_HOLE); | ||
1437 | else | 1436 | else |
1438 | retval = ext4_ind_map_blocks(NULL, inode, map, | 1437 | retval = ext4_ind_map_blocks(NULL, inode, map, 0); |
1439 | EXT4_GET_BLOCKS_NO_PUT_HOLE); | ||
1440 | 1438 | ||
1441 | add_delayed: | 1439 | add_delayed: |
1442 | if (retval == 0) { | 1440 | if (retval == 0) { |