diff options
-rw-r--r-- | fs/ext4/ext4.h | 2 | ||||
-rw-r--r-- | fs/ext4/extents.c | 9 | ||||
-rw-r--r-- | fs/ext4/extents_status.c | 60 | ||||
-rw-r--r-- | fs/ext4/extents_status.h | 2 | ||||
-rw-r--r-- | fs/ext4/inode.c | 66 | ||||
-rw-r--r-- | include/trace/events/ext4.h | 56 |
6 files changed, 192 insertions, 3 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 5c31d6ac9500..329e7fba47d6 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -579,6 +579,8 @@ enum { | |||
579 | #define EXT4_GET_BLOCKS_KEEP_SIZE 0x0080 | 579 | #define EXT4_GET_BLOCKS_KEEP_SIZE 0x0080 |
580 | /* Do not take i_data_sem locking in ext4_map_blocks */ | 580 | /* Do not take i_data_sem locking in ext4_map_blocks */ |
581 | #define EXT4_GET_BLOCKS_NO_LOCK 0x0100 | 581 | #define EXT4_GET_BLOCKS_NO_LOCK 0x0100 |
582 | /* Do not put hole in extent cache */ | ||
583 | #define EXT4_GET_BLOCKS_NO_PUT_HOLE 0x0200 | ||
582 | 584 | ||
583 | /* | 585 | /* |
584 | * Flags used by ext4_free_blocks | 586 | * Flags used by ext4_free_blocks |
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index be0b1b3eed97..b9d7a2363736 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c | |||
@@ -2167,6 +2167,9 @@ ext4_ext_put_gap_in_cache(struct inode *inode, struct ext4_ext_path *path, | |||
2167 | block, | 2167 | block, |
2168 | le32_to_cpu(ex->ee_block), | 2168 | le32_to_cpu(ex->ee_block), |
2169 | ext4_ext_get_actual_len(ex)); | 2169 | ext4_ext_get_actual_len(ex)); |
2170 | if (!ext4_find_delalloc_range(inode, lblock, lblock + len - 1)) | ||
2171 | ext4_es_insert_extent(inode, lblock, len, ~0, | ||
2172 | EXTENT_STATUS_HOLE); | ||
2170 | } else if (block >= le32_to_cpu(ex->ee_block) | 2173 | } else if (block >= le32_to_cpu(ex->ee_block) |
2171 | + ext4_ext_get_actual_len(ex)) { | 2174 | + ext4_ext_get_actual_len(ex)) { |
2172 | ext4_lblk_t next; | 2175 | ext4_lblk_t next; |
@@ -2180,6 +2183,9 @@ ext4_ext_put_gap_in_cache(struct inode *inode, struct ext4_ext_path *path, | |||
2180 | block); | 2183 | block); |
2181 | BUG_ON(next == lblock); | 2184 | BUG_ON(next == lblock); |
2182 | len = next - lblock; | 2185 | len = next - lblock; |
2186 | if (!ext4_find_delalloc_range(inode, lblock, lblock + len - 1)) | ||
2187 | ext4_es_insert_extent(inode, lblock, len, ~0, | ||
2188 | EXTENT_STATUS_HOLE); | ||
2183 | } else { | 2189 | } else { |
2184 | lblock = len = 0; | 2190 | lblock = len = 0; |
2185 | BUG(); | 2191 | BUG(); |
@@ -4018,7 +4024,8 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, | |||
4018 | * put just found gap into cache to speed up | 4024 | * put just found gap into cache to speed up |
4019 | * subsequent requests | 4025 | * subsequent requests |
4020 | */ | 4026 | */ |
4021 | ext4_ext_put_gap_in_cache(inode, path, map->m_lblk); | 4027 | if ((flags & EXT4_GET_BLOCKS_NO_PUT_HOLE) == 0) |
4028 | ext4_ext_put_gap_in_cache(inode, path, map->m_lblk); | ||
4022 | goto out2; | 4029 | goto out2; |
4023 | } | 4030 | } |
4024 | 4031 | ||
diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c index 76f4351ea821..eeb893122d8d 100644 --- a/fs/ext4/extents_status.c +++ b/fs/ext4/extents_status.c | |||
@@ -461,6 +461,66 @@ error: | |||
461 | return err; | 461 | return err; |
462 | } | 462 | } |
463 | 463 | ||
464 | /* | ||
465 | * ext4_es_lookup_extent() looks up an extent in extent status tree. | ||
466 | * | ||
467 | * ext4_es_lookup_extent is called by ext4_map_blocks/ext4_da_map_blocks. | ||
468 | * | ||
469 | * Return: 1 on found, 0 on not | ||
470 | */ | ||
471 | int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk, | ||
472 | struct extent_status *es) | ||
473 | { | ||
474 | struct ext4_es_tree *tree; | ||
475 | struct extent_status *es1 = NULL; | ||
476 | struct rb_node *node; | ||
477 | int found = 0; | ||
478 | |||
479 | trace_ext4_es_lookup_extent_enter(inode, lblk); | ||
480 | es_debug("lookup extent in block %u\n", lblk); | ||
481 | |||
482 | tree = &EXT4_I(inode)->i_es_tree; | ||
483 | read_lock(&EXT4_I(inode)->i_es_lock); | ||
484 | |||
485 | /* find extent in cache firstly */ | ||
486 | es->es_lblk = es->es_len = es->es_pblk = 0; | ||
487 | if (tree->cache_es) { | ||
488 | es1 = tree->cache_es; | ||
489 | if (in_range(lblk, es1->es_lblk, es1->es_len)) { | ||
490 | es_debug("%u cached by [%u/%u)\n", | ||
491 | lblk, es1->es_lblk, es1->es_len); | ||
492 | found = 1; | ||
493 | goto out; | ||
494 | } | ||
495 | } | ||
496 | |||
497 | node = tree->root.rb_node; | ||
498 | while (node) { | ||
499 | es1 = rb_entry(node, struct extent_status, rb_node); | ||
500 | if (lblk < es1->es_lblk) | ||
501 | node = node->rb_left; | ||
502 | else if (lblk > ext4_es_end(es1)) | ||
503 | node = node->rb_right; | ||
504 | else { | ||
505 | found = 1; | ||
506 | break; | ||
507 | } | ||
508 | } | ||
509 | |||
510 | out: | ||
511 | if (found) { | ||
512 | BUG_ON(!es1); | ||
513 | es->es_lblk = es1->es_lblk; | ||
514 | es->es_len = es1->es_len; | ||
515 | es->es_pblk = es1->es_pblk; | ||
516 | } | ||
517 | |||
518 | read_unlock(&EXT4_I(inode)->i_es_lock); | ||
519 | |||
520 | trace_ext4_es_lookup_extent_exit(inode, es, found); | ||
521 | return found; | ||
522 | } | ||
523 | |||
464 | static int __es_remove_extent(struct ext4_es_tree *tree, ext4_lblk_t lblk, | 524 | static int __es_remove_extent(struct ext4_es_tree *tree, ext4_lblk_t lblk, |
465 | ext4_lblk_t end) | 525 | ext4_lblk_t end) |
466 | { | 526 | { |
diff --git a/fs/ext4/extents_status.h b/fs/ext4/extents_status.h index 3f69d097c6e7..8ffc90c784fa 100644 --- a/fs/ext4/extents_status.h +++ b/fs/ext4/extents_status.h | |||
@@ -53,6 +53,8 @@ extern int ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk, | |||
53 | ext4_lblk_t len); | 53 | ext4_lblk_t len); |
54 | extern void ext4_es_find_delayed_extent(struct inode *inode, ext4_lblk_t lblk, | 54 | extern void ext4_es_find_delayed_extent(struct inode *inode, ext4_lblk_t lblk, |
55 | struct extent_status *es); | 55 | struct extent_status *es); |
56 | extern int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk, | ||
57 | struct extent_status *es); | ||
56 | 58 | ||
57 | static inline int ext4_es_is_written(struct extent_status *es) | 59 | static inline int ext4_es_is_written(struct extent_status *es) |
58 | { | 60 | { |
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 576b586b61aa..95a0c62c5683 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -507,12 +507,33 @@ static pgoff_t ext4_num_dirty_pages(struct inode *inode, pgoff_t idx, | |||
507 | int ext4_map_blocks(handle_t *handle, struct inode *inode, | 507 | int ext4_map_blocks(handle_t *handle, struct inode *inode, |
508 | struct ext4_map_blocks *map, int flags) | 508 | struct ext4_map_blocks *map, int flags) |
509 | { | 509 | { |
510 | struct extent_status es; | ||
510 | int retval; | 511 | int retval; |
511 | 512 | ||
512 | map->m_flags = 0; | 513 | map->m_flags = 0; |
513 | ext_debug("ext4_map_blocks(): inode %lu, flag %d, max_blocks %u," | 514 | ext_debug("ext4_map_blocks(): inode %lu, flag %d, max_blocks %u," |
514 | "logical block %lu\n", inode->i_ino, flags, map->m_len, | 515 | "logical block %lu\n", inode->i_ino, flags, map->m_len, |
515 | (unsigned long) map->m_lblk); | 516 | (unsigned long) map->m_lblk); |
517 | |||
518 | /* Lookup extent status tree firstly */ | ||
519 | if (ext4_es_lookup_extent(inode, map->m_lblk, &es)) { | ||
520 | if (ext4_es_is_written(&es) || ext4_es_is_unwritten(&es)) { | ||
521 | map->m_pblk = ext4_es_pblock(&es) + | ||
522 | map->m_lblk - es.es_lblk; | ||
523 | map->m_flags |= ext4_es_is_written(&es) ? | ||
524 | EXT4_MAP_MAPPED : EXT4_MAP_UNWRITTEN; | ||
525 | retval = es.es_len - (map->m_lblk - es.es_lblk); | ||
526 | if (retval > map->m_len) | ||
527 | retval = map->m_len; | ||
528 | map->m_len = retval; | ||
529 | } else if (ext4_es_is_delayed(&es) || ext4_es_is_hole(&es)) { | ||
530 | retval = 0; | ||
531 | } else { | ||
532 | BUG_ON(1); | ||
533 | } | ||
534 | goto found; | ||
535 | } | ||
536 | |||
516 | /* | 537 | /* |
517 | * Try to see if we can get the block without requesting a new | 538 | * Try to see if we can get the block without requesting a new |
518 | * file system block. | 539 | * file system block. |
@@ -544,6 +565,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, | |||
544 | if (!(flags & EXT4_GET_BLOCKS_NO_LOCK)) | 565 | if (!(flags & EXT4_GET_BLOCKS_NO_LOCK)) |
545 | up_read((&EXT4_I(inode)->i_data_sem)); | 566 | up_read((&EXT4_I(inode)->i_data_sem)); |
546 | 567 | ||
568 | found: | ||
547 | if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) { | 569 | if (retval > 0 && map->m_flags & EXT4_MAP_MAPPED) { |
548 | int ret = check_block_validity(inode, map); | 570 | int ret = check_block_validity(inode, map); |
549 | if (ret != 0) | 571 | if (ret != 0) |
@@ -1743,6 +1765,7 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock, | |||
1743 | struct ext4_map_blocks *map, | 1765 | struct ext4_map_blocks *map, |
1744 | struct buffer_head *bh) | 1766 | struct buffer_head *bh) |
1745 | { | 1767 | { |
1768 | struct extent_status es; | ||
1746 | int retval; | 1769 | int retval; |
1747 | sector_t invalid_block = ~((sector_t) 0xffff); | 1770 | sector_t invalid_block = ~((sector_t) 0xffff); |
1748 | 1771 | ||
@@ -1753,6 +1776,42 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock, | |||
1753 | ext_debug("ext4_da_map_blocks(): inode %lu, max_blocks %u," | 1776 | ext_debug("ext4_da_map_blocks(): inode %lu, max_blocks %u," |
1754 | "logical block %lu\n", inode->i_ino, map->m_len, | 1777 | "logical block %lu\n", inode->i_ino, map->m_len, |
1755 | (unsigned long) map->m_lblk); | 1778 | (unsigned long) map->m_lblk); |
1779 | |||
1780 | /* Lookup extent status tree firstly */ | ||
1781 | if (ext4_es_lookup_extent(inode, iblock, &es)) { | ||
1782 | |||
1783 | if (ext4_es_is_hole(&es)) { | ||
1784 | retval = 0; | ||
1785 | down_read((&EXT4_I(inode)->i_data_sem)); | ||
1786 | goto add_delayed; | ||
1787 | } | ||
1788 | |||
1789 | /* | ||
1790 | * Delayed extent could be allocated by fallocate. | ||
1791 | * So we need to check it. | ||
1792 | */ | ||
1793 | if (ext4_es_is_delayed(&es) && !ext4_es_is_unwritten(&es)) { | ||
1794 | map_bh(bh, inode->i_sb, invalid_block); | ||
1795 | set_buffer_new(bh); | ||
1796 | set_buffer_delay(bh); | ||
1797 | return 0; | ||
1798 | } | ||
1799 | |||
1800 | map->m_pblk = ext4_es_pblock(&es) + iblock - es.es_lblk; | ||
1801 | retval = es.es_len - (iblock - es.es_lblk); | ||
1802 | if (retval > map->m_len) | ||
1803 | retval = map->m_len; | ||
1804 | map->m_len = retval; | ||
1805 | if (ext4_es_is_written(&es)) | ||
1806 | map->m_flags |= EXT4_MAP_MAPPED; | ||
1807 | else if (ext4_es_is_unwritten(&es)) | ||
1808 | map->m_flags |= EXT4_MAP_UNWRITTEN; | ||
1809 | else | ||
1810 | BUG_ON(1); | ||
1811 | |||
1812 | return retval; | ||
1813 | } | ||
1814 | |||
1756 | /* | 1815 | /* |
1757 | * Try to see if we can get the block without requesting a new | 1816 | * Try to see if we can get the block without requesting a new |
1758 | * file system block. | 1817 | * file system block. |
@@ -1771,10 +1830,13 @@ static int ext4_da_map_blocks(struct inode *inode, sector_t iblock, | |||
1771 | map->m_flags |= EXT4_MAP_FROM_CLUSTER; | 1830 | map->m_flags |= EXT4_MAP_FROM_CLUSTER; |
1772 | retval = 0; | 1831 | retval = 0; |
1773 | } else if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) | 1832 | } else if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) |
1774 | retval = ext4_ext_map_blocks(NULL, inode, map, 0); | 1833 | retval = ext4_ext_map_blocks(NULL, inode, map, |
1834 | EXT4_GET_BLOCKS_NO_PUT_HOLE); | ||
1775 | else | 1835 | else |
1776 | retval = ext4_ind_map_blocks(NULL, inode, map, 0); | 1836 | retval = ext4_ind_map_blocks(NULL, inode, map, |
1837 | EXT4_GET_BLOCKS_NO_PUT_HOLE); | ||
1777 | 1838 | ||
1839 | add_delayed: | ||
1778 | if (retval == 0) { | 1840 | if (retval == 0) { |
1779 | int ret; | 1841 | int ret; |
1780 | /* | 1842 | /* |
diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h index c121cdf55ab3..1e590b68cec4 100644 --- a/include/trace/events/ext4.h +++ b/include/trace/events/ext4.h | |||
@@ -2199,6 +2199,62 @@ TRACE_EVENT(ext4_es_find_delayed_extent_exit, | |||
2199 | __entry->pblk, __entry->status) | 2199 | __entry->pblk, __entry->status) |
2200 | ); | 2200 | ); |
2201 | 2201 | ||
2202 | TRACE_EVENT(ext4_es_lookup_extent_enter, | ||
2203 | TP_PROTO(struct inode *inode, ext4_lblk_t lblk), | ||
2204 | |||
2205 | TP_ARGS(inode, lblk), | ||
2206 | |||
2207 | TP_STRUCT__entry( | ||
2208 | __field( dev_t, dev ) | ||
2209 | __field( ino_t, ino ) | ||
2210 | __field( ext4_lblk_t, lblk ) | ||
2211 | ), | ||
2212 | |||
2213 | TP_fast_assign( | ||
2214 | __entry->dev = inode->i_sb->s_dev; | ||
2215 | __entry->ino = inode->i_ino; | ||
2216 | __entry->lblk = lblk; | ||
2217 | ), | ||
2218 | |||
2219 | TP_printk("dev %d,%d ino %lu lblk %u", | ||
2220 | MAJOR(__entry->dev), MINOR(__entry->dev), | ||
2221 | (unsigned long) __entry->ino, __entry->lblk) | ||
2222 | ); | ||
2223 | |||
2224 | TRACE_EVENT(ext4_es_lookup_extent_exit, | ||
2225 | TP_PROTO(struct inode *inode, struct extent_status *es, | ||
2226 | int found), | ||
2227 | |||
2228 | TP_ARGS(inode, es, found), | ||
2229 | |||
2230 | TP_STRUCT__entry( | ||
2231 | __field( dev_t, dev ) | ||
2232 | __field( ino_t, ino ) | ||
2233 | __field( ext4_lblk_t, lblk ) | ||
2234 | __field( ext4_lblk_t, len ) | ||
2235 | __field( ext4_fsblk_t, pblk ) | ||
2236 | __field( unsigned long long, status ) | ||
2237 | __field( int, found ) | ||
2238 | ), | ||
2239 | |||
2240 | TP_fast_assign( | ||
2241 | __entry->dev = inode->i_sb->s_dev; | ||
2242 | __entry->ino = inode->i_ino; | ||
2243 | __entry->lblk = es->es_lblk; | ||
2244 | __entry->len = es->es_len; | ||
2245 | __entry->pblk = ext4_es_pblock(es); | ||
2246 | __entry->status = ext4_es_status(es); | ||
2247 | __entry->found = found; | ||
2248 | ), | ||
2249 | |||
2250 | TP_printk("dev %d,%d ino %lu found %d [%u/%u) %llu %llx", | ||
2251 | MAJOR(__entry->dev), MINOR(__entry->dev), | ||
2252 | (unsigned long) __entry->ino, __entry->found, | ||
2253 | __entry->lblk, __entry->len, | ||
2254 | __entry->found ? __entry->pblk : 0, | ||
2255 | __entry->found ? __entry->status : 0) | ||
2256 | ); | ||
2257 | |||
2202 | #endif /* _TRACE_EXT4_H */ | 2258 | #endif /* _TRACE_EXT4_H */ |
2203 | 2259 | ||
2204 | /* This part must be outside protection */ | 2260 | /* This part must be outside protection */ |