aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/ext4/extents.c93
-rw-r--r--fs/ext4/super.c1
-rw-r--r--include/trace/events/ext4.h82
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 */
2923static int ext4_ext_convert_to_initialized(handle_t *handle, 2933static 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
10struct ext4_allocation_context; 10struct ext4_allocation_context;
11struct ext4_allocation_request; 11struct ext4_allocation_request;
12struct ext4_extent;
12struct ext4_prealloc_space; 13struct ext4_prealloc_space;
13struct ext4_inode_info; 14struct ext4_inode_info;
14struct mpage_da_data; 15struct 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. */
1399TRACE_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.