diff options
| -rw-r--r-- | fs/ext4/extents.c | 93 | ||||
| -rw-r--r-- | fs/ext4/super.c | 1 | ||||
| -rw-r--r-- | include/trace/events/ext4.h | 82 |
3 files changed, 176 insertions, 0 deletions
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index c2ac06cb2d46..8b6a17b60970 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c | |||
| @@ -2919,12 +2919,23 @@ out: | |||
| 2919 | * a> There is no split required: Entire extent should be initialized | 2919 | * a> There is no split required: Entire extent should be initialized |
| 2920 | * b> Splits in two extents: Write is happening at either end of the extent | 2920 | * b> Splits in two extents: Write is happening at either end of the extent |
| 2921 | * c> Splits in three extents: Somone is writing in middle of the extent | 2921 | * c> Splits in three extents: Somone is writing in middle of the extent |
| 2922 | * | ||
| 2923 | * Pre-conditions: | ||
| 2924 | * - The extent pointed to by 'path' is uninitialized. | ||
| 2925 | * - The extent pointed to by 'path' contains a superset | ||
| 2926 | * of the logical span [map->m_lblk, map->m_lblk + map->m_len). | ||
| 2927 | * | ||
| 2928 | * Post-conditions on success: | ||
| 2929 | * - the returned value is the number of blocks beyond map->l_lblk | ||
| 2930 | * that are allocated and initialized. | ||
| 2931 | * It is guaranteed to be >= map->m_len. | ||
| 2922 | */ | 2932 | */ |
| 2923 | static int ext4_ext_convert_to_initialized(handle_t *handle, | 2933 | static int ext4_ext_convert_to_initialized(handle_t *handle, |
| 2924 | struct inode *inode, | 2934 | struct inode *inode, |
| 2925 | struct ext4_map_blocks *map, | 2935 | struct ext4_map_blocks *map, |
| 2926 | struct ext4_ext_path *path) | 2936 | struct ext4_ext_path *path) |
| 2927 | { | 2937 | { |
| 2938 | struct ext4_extent_header *eh; | ||
| 2928 | struct ext4_map_blocks split_map; | 2939 | struct ext4_map_blocks split_map; |
| 2929 | struct ext4_extent zero_ex; | 2940 | struct ext4_extent zero_ex; |
| 2930 | struct ext4_extent *ex; | 2941 | struct ext4_extent *ex; |
| @@ -2944,11 +2955,93 @@ static int ext4_ext_convert_to_initialized(handle_t *handle, | |||
| 2944 | eof_block = map->m_lblk + map->m_len; | 2955 | eof_block = map->m_lblk + map->m_len; |
| 2945 | 2956 | ||
| 2946 | depth = ext_depth(inode); | 2957 | depth = ext_depth(inode); |
| 2958 | eh = path[depth].p_hdr; | ||
| 2947 | ex = path[depth].p_ext; | 2959 | ex = path[depth].p_ext; |
| 2948 | ee_block = le32_to_cpu(ex->ee_block); | 2960 | ee_block = le32_to_cpu(ex->ee_block); |
| 2949 | ee_len = ext4_ext_get_actual_len(ex); | 2961 | ee_len = ext4_ext_get_actual_len(ex); |
| 2950 | allocated = ee_len - (map->m_lblk - ee_block); | 2962 | allocated = ee_len - (map->m_lblk - ee_block); |
| 2951 | 2963 | ||
| 2964 | trace_ext4_ext_convert_to_initialized_enter(inode, map, ex); | ||
| 2965 | |||
| 2966 | /* Pre-conditions */ | ||
| 2967 | BUG_ON(!ext4_ext_is_uninitialized(ex)); | ||
| 2968 | BUG_ON(!in_range(map->m_lblk, ee_block, ee_len)); | ||
| 2969 | BUG_ON(map->m_lblk + map->m_len > ee_block + ee_len); | ||
| 2970 | |||
| 2971 | /* | ||
| 2972 | * Attempt to transfer newly initialized blocks from the currently | ||
| 2973 | * uninitialized extent to its left neighbor. This is much cheaper | ||
| 2974 | * than an insertion followed by a merge as those involve costly | ||
| 2975 | * memmove() calls. This is the common case in steady state for | ||
| 2976 | * workloads doing fallocate(FALLOC_FL_KEEP_SIZE) followed by append | ||
| 2977 | * writes. | ||
| 2978 | * | ||
| 2979 | * Limitations of the current logic: | ||
| 2980 | * - L1: we only deal with writes at the start of the extent. | ||
| 2981 | * The approach could be extended to writes at the end | ||
| 2982 | * of the extent but this scenario was deemed less common. | ||
| 2983 | * - L2: we do not deal with writes covering the whole extent. | ||
| 2984 | * This would require removing the extent if the transfer | ||
| 2985 | * is possible. | ||
| 2986 | * - L3: we only attempt to merge with an extent stored in the | ||
| 2987 | * same extent tree node. | ||
| 2988 | */ | ||
| 2989 | if ((map->m_lblk == ee_block) && /*L1*/ | ||
| 2990 | (map->m_len < ee_len) && /*L2*/ | ||
| 2991 | (ex > EXT_FIRST_EXTENT(eh))) { /*L3*/ | ||
| 2992 | struct ext4_extent *prev_ex; | ||
| 2993 | ext4_lblk_t prev_lblk; | ||
| 2994 | ext4_fsblk_t prev_pblk, ee_pblk; | ||
| 2995 | unsigned int prev_len, write_len; | ||
| 2996 | |||
| 2997 | prev_ex = ex - 1; | ||
| 2998 | prev_lblk = le32_to_cpu(prev_ex->ee_block); | ||
| 2999 | prev_len = ext4_ext_get_actual_len(prev_ex); | ||
| 3000 | prev_pblk = ext4_ext_pblock(prev_ex); | ||
| 3001 | ee_pblk = ext4_ext_pblock(ex); | ||
| 3002 | write_len = map->m_len; | ||
| 3003 | |||
| 3004 | /* | ||
| 3005 | * A transfer of blocks from 'ex' to 'prev_ex' is allowed | ||
| 3006 | * upon those conditions: | ||
| 3007 | * - C1: prev_ex is initialized, | ||
| 3008 | * - C2: prev_ex is logically abutting ex, | ||
| 3009 | * - C3: prev_ex is physically abutting ex, | ||
| 3010 | * - C4: prev_ex can receive the additional blocks without | ||
| 3011 | * overflowing the (initialized) length limit. | ||
| 3012 | */ | ||
| 3013 | if ((!ext4_ext_is_uninitialized(prev_ex)) && /*C1*/ | ||
| 3014 | ((prev_lblk + prev_len) == ee_block) && /*C2*/ | ||
| 3015 | ((prev_pblk + prev_len) == ee_pblk) && /*C3*/ | ||
| 3016 | (prev_len < (EXT_INIT_MAX_LEN - write_len))) { /*C4*/ | ||
| 3017 | err = ext4_ext_get_access(handle, inode, path + depth); | ||
| 3018 | if (err) | ||
| 3019 | goto out; | ||
| 3020 | |||
| 3021 | trace_ext4_ext_convert_to_initialized_fastpath(inode, | ||
| 3022 | map, ex, prev_ex); | ||
| 3023 | |||
| 3024 | /* Shift the start of ex by 'write_len' blocks */ | ||
| 3025 | ex->ee_block = cpu_to_le32(ee_block + write_len); | ||
| 3026 | ext4_ext_store_pblock(ex, ee_pblk + write_len); | ||
| 3027 | ex->ee_len = cpu_to_le16(ee_len - write_len); | ||
| 3028 | ext4_ext_mark_uninitialized(ex); /* Restore the flag */ | ||
| 3029 | |||
| 3030 | /* Extend prev_ex by 'write_len' blocks */ | ||
| 3031 | prev_ex->ee_len = cpu_to_le16(prev_len + write_len); | ||
| 3032 | |||
| 3033 | /* Mark the block containing both extents as dirty */ | ||
| 3034 | ext4_ext_dirty(handle, inode, path + depth); | ||
| 3035 | |||
| 3036 | /* Update path to point to the right extent */ | ||
| 3037 | path[depth].p_ext = prev_ex; | ||
| 3038 | |||
| 3039 | /* Result: number of initialized blocks past m_lblk */ | ||
| 3040 | allocated = write_len; | ||
| 3041 | goto out; | ||
| 3042 | } | ||
| 3043 | } | ||
| 3044 | |||
| 2952 | WARN_ON(map->m_lblk < ee_block); | 3045 | WARN_ON(map->m_lblk < ee_block); |
| 2953 | /* | 3046 | /* |
| 2954 | * It is safe to convert extent to initialized via explicit | 3047 | * It is safe to convert extent to initialized via explicit |
diff --git a/fs/ext4/super.c b/fs/ext4/super.c index dcc460537bc7..9953d80145ad 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c | |||
| @@ -45,6 +45,7 @@ | |||
| 45 | #include <linux/freezer.h> | 45 | #include <linux/freezer.h> |
| 46 | 46 | ||
| 47 | #include "ext4.h" | 47 | #include "ext4.h" |
| 48 | #include "ext4_extents.h" | ||
| 48 | #include "ext4_jbd2.h" | 49 | #include "ext4_jbd2.h" |
| 49 | #include "xattr.h" | 50 | #include "xattr.h" |
| 50 | #include "acl.h" | 51 | #include "acl.h" |
diff --git a/include/trace/events/ext4.h b/include/trace/events/ext4.h index c9a341e385a3..748ff7cbe555 100644 --- a/include/trace/events/ext4.h +++ b/include/trace/events/ext4.h | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | 9 | ||
| 10 | struct ext4_allocation_context; | 10 | struct ext4_allocation_context; |
| 11 | struct ext4_allocation_request; | 11 | struct ext4_allocation_request; |
| 12 | struct ext4_extent; | ||
| 12 | struct ext4_prealloc_space; | 13 | struct ext4_prealloc_space; |
| 13 | struct ext4_inode_info; | 14 | struct ext4_inode_info; |
| 14 | struct mpage_da_data; | 15 | struct mpage_da_data; |
| @@ -1394,6 +1395,87 @@ DEFINE_EVENT(ext4__truncate, ext4_truncate_exit, | |||
| 1394 | TP_ARGS(inode) | 1395 | TP_ARGS(inode) |
| 1395 | ); | 1396 | ); |
| 1396 | 1397 | ||
| 1398 | /* 'ux' is the uninitialized extent. */ | ||
| 1399 | TRACE_EVENT(ext4_ext_convert_to_initialized_enter, | ||
| 1400 | TP_PROTO(struct inode *inode, struct ext4_map_blocks *map, | ||
| 1401 | struct ext4_extent *ux), | ||
| 1402 | |||
| 1403 | TP_ARGS(inode, map, ux), | ||
| 1404 | |||
| 1405 | TP_STRUCT__entry( | ||
| 1406 | __field( ino_t, ino ) | ||
| 1407 | __field( dev_t, dev ) | ||
| 1408 | __field( ext4_lblk_t, m_lblk ) | ||
| 1409 | __field( unsigned, m_len ) | ||
| 1410 | __field( ext4_lblk_t, u_lblk ) | ||
| 1411 | __field( unsigned, u_len ) | ||
| 1412 | __field( ext4_fsblk_t, u_pblk ) | ||
| 1413 | ), | ||
| 1414 | |||
| 1415 | TP_fast_assign( | ||
| 1416 | __entry->ino = inode->i_ino; | ||
| 1417 | __entry->dev = inode->i_sb->s_dev; | ||
| 1418 | __entry->m_lblk = map->m_lblk; | ||
| 1419 | __entry->m_len = map->m_len; | ||
| 1420 | __entry->u_lblk = le32_to_cpu(ux->ee_block); | ||
| 1421 | __entry->u_len = ext4_ext_get_actual_len(ux); | ||
| 1422 | __entry->u_pblk = ext4_ext_pblock(ux); | ||
| 1423 | ), | ||
| 1424 | |||
| 1425 | TP_printk("dev %d,%d ino %lu m_lblk %u m_len %u u_lblk %u u_len %u " | ||
| 1426 | "u_pblk %llu", | ||
| 1427 | MAJOR(__entry->dev), MINOR(__entry->dev), | ||
| 1428 | (unsigned long) __entry->ino, | ||
| 1429 | __entry->m_lblk, __entry->m_len, | ||
| 1430 | __entry->u_lblk, __entry->u_len, __entry->u_pblk) | ||
| 1431 | ); | ||
| 1432 | |||
| 1433 | /* | ||
| 1434 | * 'ux' is the uninitialized extent. | ||
