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 */ |
