* [PATCH 0/2] ext4: reduce fast-commit write amplification for scattered writes [not found] <CGME20260611044733epcms2p38013ae683a283555526f70e4eab6d2a9@epcms2p3> @ 2026-06-11 4:47 ` Daejun Park 2026-06-11 4:48 ` [PATCH 1/2] ext4: track multiple disjoint fast-commit ranges per inode Daejun Park 2026-07-21 14:44 ` [PATCH 0/2] ext4: reduce fast-commit write amplification for scattered writes Theodore Tso 0 siblings, 2 replies; 5+ messages in thread From: Daejun Park @ 2026-06-11 4:47 UTC (permalink / raw) To: tytso@mit.edu, adilger.kernel@dilger.ca Cc: linux-ext4@vger.kernel.org, linux-kernel@vger.kernel.org, Daejun Park ext4 fast commit tracks a single coalesced logical range per inode. When an inode is dirtied at several disjoint offsets between two commits (e.g. sparse/scattered random writes), that range is widened to span [min, max] of all the touched offsets, and ext4_fc_write_inode_data() then re-logs every extent inside that span -- including the unmodified ones. On sparse allocation this inflates fast-commit traffic and frequently overflows the fast-commit area, forcing a fallback to a full jbd2 commit. This series replaces the single range with a small, bounded set of disjoint ranges so that only the actually-modified regions are logged, while keeping the per-inode memory cost negligible: 1/2 tracks up to EXT4_FC_MAX_RANGES (16) disjoint ranges, merging the two closest ranges when the set would overflow -- so the worst case degrades gracefully to the old single-span behaviour. The on-disk fast-commit (TLV) format is unchanged. 2/2 allocates that array lazily: the first range is kept inline, the array is allocated only when a second disjoint range appears, and on an allocation failure we fall back to the inline single range. The per-inode fast-commit footprint drops from ~140 to 20 bytes. Measured on a sparse random-write workload (1 GiB span, R disjoint dirty regions per fsync, 300 fsyncs, bare-metal NVMe): - fast-commit blocks per commit (R=16): 18.6 -> 1.0 - full-commit fallback rate (R=16): 22% -> 2% (on a small fs) - mean fsync latency: R=16 -10%, R=64 -14% - p99 fsync latency: R=16 -31% The p99 improvement comes from eliminating the full-commit fallback spikes. Testing: crash recovery (power loss -> fast-commit replay -> verify every fsync'd block, then e2fsck) is clean; the ext4/generic fast-commit xfstests show no regression; the unchanged on-disk format means e2fsprogs needs no update. Both patches are checkpatch --strict clean. Based on v6.17-rc3. Daejun Park (2): ext4: track multiple disjoint fast-commit ranges per inode ext4: allocate the fast-commit range array lazily fs/ext4/ext4.h | 40 +++++++-- fs/ext4/fast_commit.c | 196 +++++++++++++++++++++++++++++++++++------- fs/ext4/super.c | 1 + 3 files changed, 199 insertions(+), 38 deletions(-) -- 2.43.0 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/2] ext4: track multiple disjoint fast-commit ranges per inode 2026-06-11 4:47 ` [PATCH 0/2] ext4: reduce fast-commit write amplification for scattered writes Daejun Park @ 2026-06-11 4:48 ` Daejun Park 2026-06-11 4:49 ` [PATCH 2/2] ext4: allocate the fast-commit range array lazily Daejun Park 2026-07-21 14:44 ` [PATCH 0/2] ext4: reduce fast-commit write amplification for scattered writes Theodore Tso 1 sibling, 1 reply; 5+ messages in thread From: Daejun Park @ 2026-06-11 4:48 UTC (permalink / raw) To: tytso@mit.edu, adilger.kernel@dilger.ca Cc: linux-ext4@vger.kernel.org, linux-kernel@vger.kernel.org, Daejun Park Fast commit tracks a single coalesced logical range per inode (i_fc_lblk_start .. i_fc_lblk_len). When an inode is modified at several disjoint offsets between two commits (e.g. sparse random writes), the range is widened to span [min, max] of all touched offsets, and at commit time ext4_fc_write_inode_data() re-logs every extent inside that span, including the unmodified ones. On sparse allocation this inflates fast-commit traffic and often overflows the fast-commit area, forcing a fallback to a full jbd2 commit. Replace the single range with a bounded array of up to EXT4_FC_MAX_RANGES (16) disjoint ranges. __track_range inserts and merges into it; on overflow the two ranges separated by the smallest gap are coalesced, so it degrades to the old single-span behaviour in the worst case. ext4_fc_write_inode_data() now walks only the tracked ranges. The on-disk fast-commit (TLV) format is unchanged. The number of disjoint dirty regions an inode accumulates per fsync -- how scattered the writes are -- controls how badly the single-span tracking over-logs. On a sparse random-write workload (1 GiB span, 300 fsyncs, NVMe): 16 regions 64 regions fast-commit blocks/cmt 19.1 -> 1.0 76.3 -> 31.6 mean fsync latency (us) 2537 -> 2280 3398 -> 2937 p99 fsync latency (us) 3698 -> 2545 4492 -> 4291 With 16 dirty regions per fsync everything fits within the 16-range cap and each region is tracked exactly; 64 regions exceeds the cap and exercises the overflow-merge path, which still roughly halves the logged blocks. On a small filesystem whose fast-commit area is easily exhausted, the reduced traffic also cuts the full-commit fallback rate (e.g. 22% -> 2% at 16 regions on an 8 GiB fs). Crash recovery (online replay + offline e2fsck) and the ext4/generic fast-commit xfstests show no regression; the unchanged on-disk format means e2fsprogs needs no update. Signed-off-by: Daejun Park <pdaejun@gmail.com> --- fs/ext4/ext4.h | 31 ++++++++-- fs/ext4/fast_commit.c | 138 ++++++++++++++++++++++++++++++++---------- 2 files changed, 130 insertions(+), 39 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 01a6e2de7fc3..314a1c90075b 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1017,6 +1017,20 @@ enum { }; +/* + * Maximum number of disjoint logical-block ranges tracked per inode for a + * single fast commit. Scattered allocations that exceed this get their two + * closest ranges merged (see ext4_fc_range_add()), degrading gracefully to + * the old single coalesced-range behaviour. + */ +#define EXT4_FC_MAX_RANGES 16 + +/* In-memory record of an lblk range modified in the current fast commit. */ +struct ext4_fc_lblk_range { + ext4_lblk_t start; + ext4_lblk_t len; +}; + /* * fourth extended file system inode data in memory */ @@ -1066,11 +1080,16 @@ struct ext4_inode_info { * protected by sbi->s_fc_lock. */ - /* Start of lblk range that needs to be committed in this fast commit */ - ext4_lblk_t i_fc_lblk_start; - - /* End of lblk range that needs to be committed in this fast commit */ - ext4_lblk_t i_fc_lblk_len; + /* + * Disjoint lblk ranges modified in this fast commit. Tracking the + * actual modified ranges (instead of one coalesced [min,max]) avoids + * re-logging the whole spanned extent map for scattered allocations. + * Sorted by start, mutually disjoint. Bounded by EXT4_FC_MAX_RANGES; + * the extra slot is transient room used while inserting before an + * overflow merge. Protected by i_fc_lock. + */ + struct ext4_fc_lblk_range i_fc_ranges[EXT4_FC_MAX_RANGES + 1]; + unsigned int i_fc_nr_ranges; spinlock_t i_raw_lock; /* protects updates to the raw inode */ @@ -1078,7 +1097,7 @@ struct ext4_inode_info { wait_queue_head_t i_fc_wait; /* - * Protect concurrent accesses on i_fc_lblk_start, i_fc_lblk_len + * Protect concurrent accesses on i_fc_ranges, i_fc_nr_ranges * and inode's EXT4_FC_STATE_COMMITTING state bit. */ spinlock_t i_fc_lock; diff --git a/fs/ext4/fast_commit.c b/fs/ext4/fast_commit.c index 42bee1d4f9f9..ab9ab50ad0b5 100644 --- a/fs/ext4/fast_commit.c +++ b/fs/ext4/fast_commit.c @@ -203,8 +203,7 @@ static inline void ext4_fc_reset_inode(struct inode *inode) { struct ext4_inode_info *ei = EXT4_I(inode); - ei->i_fc_lblk_start = 0; - ei->i_fc_lblk_len = 0; + ei->i_fc_nr_ranges = 0; } void ext4_fc_init_inode(struct inode *inode) @@ -540,7 +539,7 @@ static int __track_inode(handle_t *handle, struct inode *inode, void *arg, if (update) return -EEXIST; - EXT4_I(inode)->i_fc_lblk_len = 0; + EXT4_I(inode)->i_fc_nr_ranges = 0; return 0; } @@ -603,12 +602,73 @@ struct __track_range_args { ext4_lblk_t start, end; }; +/* + * Record that logical block range [start, end] was modified in the current + * fast commit. Maintains a small, bounded set of sorted, mutually disjoint + * ranges, merging the new range with any it overlaps or is adjacent to. When + * the set would exceed EXT4_FC_MAX_RANGES, the consecutive pair separated by + * the smallest gap is merged (absorbing that gap), so the worst case degrades + * gracefully to the old single coalesced-range behaviour. Tracking the actual + * modified ranges (rather than one [min,max] span) keeps ext4_fc_write_inode_data + * from re-logging the whole spanned extent map on scattered allocations. + * Caller holds ei->i_fc_lock. + */ +static void ext4_fc_range_add(struct ext4_inode_info *ei, + ext4_lblk_t start, ext4_lblk_t end) +{ + struct ext4_fc_lblk_range *r = ei->i_fc_ranges; + unsigned int n = ei->i_fc_nr_ranges; + unsigned int i, j; + + /* Skip ranges lying entirely before [start - 1] (no overlap/adjacency). */ + i = 0; + while (i < n && r[i].start + r[i].len < start) + i++; + + /* Absorb every range overlapping or adjacent to the growing [start,end]. */ + j = i; + while (j < n && r[j].start <= end + 1) { + if (r[j].start < start) + start = r[j].start; + if (r[j].start + r[j].len - 1 > end) + end = r[j].start + r[j].len - 1; + j++; + } + + /* Replace r[i..j-1] with the merged range (j == i is a plain insert). */ + if (j != i + 1) + memmove(&r[i + 1], &r[j], (n - j) * sizeof(*r)); + r[i].start = start; + r[i].len = end - start + 1; + ei->i_fc_nr_ranges = n - (j - i) + 1; + + /* Overflow: merge the consecutive pair separated by the smallest gap. */ + while (ei->i_fc_nr_ranges > EXT4_FC_MAX_RANGES) { + ext4_lblk_t best_gap = ~0U; + unsigned int best = 0; + + n = ei->i_fc_nr_ranges; + for (i = 0; i + 1 < n; i++) { + ext4_lblk_t gap = r[i + 1].start - + (r[i].start + r[i].len); + + if (gap < best_gap) { + best_gap = gap; + best = i; + } + } + r[best].len = r[best + 1].start + r[best + 1].len - r[best].start; + memmove(&r[best + 1], &r[best + 2], + (n - best - 2) * sizeof(*r)); + ei->i_fc_nr_ranges = n - 1; + } +} + /* __track_fn for tracking data updates */ static int __track_range(handle_t *handle, struct inode *inode, void *arg, bool update) { struct ext4_inode_info *ei = EXT4_I(inode); - ext4_lblk_t oldstart; struct __track_range_args *__arg = (struct __track_range_args *)arg; @@ -617,17 +677,11 @@ static int __track_range(handle_t *handle, struct inode *inode, void *arg, return -ECANCELED; } - oldstart = ei->i_fc_lblk_start; + /* A new transaction (update == false) starts a fresh range set. */ + if (!update) + ei->i_fc_nr_ranges = 0; - if (update && ei->i_fc_lblk_len > 0) { - ei->i_fc_lblk_start = min(ei->i_fc_lblk_start, __arg->start); - ei->i_fc_lblk_len = - max(oldstart + ei->i_fc_lblk_len - 1, __arg->end) - - ei->i_fc_lblk_start + 1; - } else { - ei->i_fc_lblk_start = __arg->start; - ei->i_fc_lblk_len = __arg->end - __arg->start + 1; - } + ext4_fc_range_add(ei, __arg->start, __arg->end); return 0; } @@ -890,33 +944,20 @@ static int ext4_fc_write_inode(struct inode *inode, u32 *crc) * Writes updated data ranges for the inode in question. Updates CRC. * Returns 0 on success, error otherwise. */ -static int ext4_fc_write_inode_data(struct inode *inode, u32 *crc) +/* Write the fast commit TLVs for one modified lblk range [start, end]. */ +static int ext4_fc_write_lblk_range(struct inode *inode, ext4_lblk_t start, + ext4_lblk_t end, u32 *crc) { - ext4_lblk_t old_blk_size, cur_lblk_off, new_blk_size; - struct ext4_inode_info *ei = EXT4_I(inode); + ext4_lblk_t cur_lblk_off = start; struct ext4_map_blocks map; struct ext4_fc_add_range fc_ext; struct ext4_fc_del_range lrange; struct ext4_extent *ex; int ret; - spin_lock(&ei->i_fc_lock); - if (ei->i_fc_lblk_len == 0) { - spin_unlock(&ei->i_fc_lock); - return 0; - } - old_blk_size = ei->i_fc_lblk_start; - new_blk_size = ei->i_fc_lblk_start + ei->i_fc_lblk_len - 1; - ei->i_fc_lblk_len = 0; - spin_unlock(&ei->i_fc_lock); - - cur_lblk_off = old_blk_size; - ext4_debug("will try writing %d to %d for inode %ld\n", - cur_lblk_off, new_blk_size, inode->i_ino); - - while (cur_lblk_off <= new_blk_size) { + while (cur_lblk_off <= end) { map.m_lblk = cur_lblk_off; - map.m_len = new_blk_size - cur_lblk_off + 1; + map.m_len = end - cur_lblk_off + 1; ret = ext4_map_blocks(NULL, inode, &map, EXT4_GET_BLOCKS_IO_SUBMIT | EXT4_EX_NOCACHE); @@ -962,6 +1003,37 @@ static int ext4_fc_write_inode_data(struct inode *inode, u32 *crc) return 0; } +static int ext4_fc_write_inode_data(struct inode *inode, u32 *crc) +{ + struct ext4_inode_info *ei = EXT4_I(inode); + struct ext4_fc_lblk_range ranges[EXT4_FC_MAX_RANGES + 1]; + unsigned int nr, i; + int ret; + + spin_lock(&ei->i_fc_lock); + nr = ei->i_fc_nr_ranges; + if (nr == 0) { + spin_unlock(&ei->i_fc_lock); + return 0; + } + memcpy(ranges, ei->i_fc_ranges, nr * sizeof(ranges[0])); + ei->i_fc_nr_ranges = 0; + spin_unlock(&ei->i_fc_lock); + + for (i = 0; i < nr; i++) { + ext4_lblk_t start = ranges[i].start; + ext4_lblk_t end = ranges[i].start + ranges[i].len - 1; + + ext4_debug("will try writing %u to %u for inode %ld\n", + start, end, inode->i_ino); + ret = ext4_fc_write_lblk_range(inode, start, end, crc); + if (ret) + return ret; + } + + return 0; +} + /* Flushes data of all the inodes in the commit queue. */ static int ext4_fc_flush_data(journal_t *journal) -- 2.43.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/2] ext4: allocate the fast-commit range array lazily 2026-06-11 4:48 ` [PATCH 1/2] ext4: track multiple disjoint fast-commit ranges per inode Daejun Park @ 2026-06-11 4:49 ` Daejun Park 0 siblings, 0 replies; 5+ messages in thread From: Daejun Park @ 2026-06-11 4:49 UTC (permalink / raw) To: tytso@mit.edu, adilger.kernel@dilger.ca Cc: linux-ext4@vger.kernel.org, linux-kernel@vger.kernel.org, Daejun Park The multi-interval tracker added a fixed array of EXT4_FC_MAX_RANGES + 1 entries to every ext4_inode_info -- ~136 bytes that is wasted on inodes that never use fast commit (read-only files, directories, ...). Shrink it to the common case: - Keep the first range inline in i_fc_range, so a single contiguous dirty region (the common case) needs no allocation at all. - Allocate the i_fc_ranges array only when a second disjoint range appears, and free it when the inode is evicted. - The tracking path runs under i_fc_lock and so cannot sleep, so the array is allocated with GFP_ATOMIC. On failure, fall back to coalescing the new range into the inline i_fc_range -- exactly the original single coalesced-range behaviour -- so no full-commit fallback or fast-commit ineligibility is needed. The per-inode fast-commit footprint drops from ~140 bytes (the embedded array) to 20 bytes (inline range + array pointer + count); the array is allocated only while two or more disjoint ranges are tracked. No on-disk format change. Crash recovery (online replay + offline e2fsck) and the fast-commit xfstests are unaffected. While rewriting __track_range, also skip degenerate ranges (a sub-block punch hole rounds the start up past the end, passing end == start - 1, so no whole block changed) instead of storing an empty range, and drop the redundant per-transaction reset here -- ext4_fc_track_template() already resets the range set under i_fc_lock before calling the tracker. Signed-off-by: Daejun Park <pdaejun@gmail.com> --- fs/ext4/ext4.h | 19 ++++++++---- fs/ext4/fast_commit.c | 70 +++++++++++++++++++++++++++++++++++++++---- fs/ext4/super.c | 1 + 3 files changed, 80 insertions(+), 10 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 314a1c90075b..6c6ac19e86b6 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1081,14 +1081,23 @@ struct ext4_inode_info { */ /* - * Disjoint lblk ranges modified in this fast commit. Tracking the + * Logical block ranges modified in this fast commit. Tracking the * actual modified ranges (instead of one coalesced [min,max]) avoids * re-logging the whole spanned extent map for scattered allocations. - * Sorted by start, mutually disjoint. Bounded by EXT4_FC_MAX_RANGES; - * the extra slot is transient room used while inserting before an - * overflow merge. Protected by i_fc_lock. + * + * The first range is kept inline in i_fc_range, so the common case of a + * single contiguous dirty region needs no allocation. When a second + * disjoint range appears the inode is upgraded to the i_fc_ranges array + * (EXT4_FC_MAX_RANGES + 1 entries, sorted and mutually disjoint; the + * extra slot is transient room used while inserting before an overflow + * merge), allocated then and freed when the inode is evicted. If that + * allocation fails we fall back to coalescing into i_fc_range, i.e. the + * original single coalesced-range behaviour. i_fc_nr_ranges counts the + * valid ranges; while i_fc_ranges is NULL it is 0 or 1. Protected by + * i_fc_lock. */ - struct ext4_fc_lblk_range i_fc_ranges[EXT4_FC_MAX_RANGES + 1]; + struct ext4_fc_lblk_range i_fc_range; + struct ext4_fc_lblk_range *i_fc_ranges; unsigned int i_fc_nr_ranges; spinlock_t i_raw_lock; /* protects updates to the raw inode */ diff --git a/fs/ext4/fast_commit.c b/fs/ext4/fast_commit.c index ab9ab50ad0b5..786b79a9c573 100644 --- a/fs/ext4/fast_commit.c +++ b/fs/ext4/fast_commit.c @@ -211,6 +211,7 @@ void ext4_fc_init_inode(struct inode *inode) struct ext4_inode_info *ei = EXT4_I(inode); ext4_fc_reset_inode(inode); + ei->i_fc_ranges = NULL; ext4_clear_inode_state(inode, EXT4_STATE_FC_COMMITTING); INIT_LIST_HEAD(&ei->i_fc_list); INIT_LIST_HEAD(&ei->i_fc_dilist); @@ -671,17 +672,73 @@ static int __track_range(handle_t *handle, struct inode *inode, void *arg, struct ext4_inode_info *ei = EXT4_I(inode); struct __track_range_args *__arg = (struct __track_range_args *)arg; + ext4_lblk_t start = __arg->start, end = __arg->end; + ext4_lblk_t s0, e0; if (inode->i_ino < EXT4_FIRST_INO(inode->i_sb)) { ext4_debug("Special inode %ld being modified\n", inode->i_ino); return -ECANCELED; } - /* A new transaction (update == false) starts a fresh range set. */ - if (!update) - ei->i_fc_nr_ranges = 0; + /* + * A sub-block punch hole rounds up the start and down the end, passing + * end == start - 1: no whole block changed, so there is nothing to + * track. (ext4_fc_track_template has already reset the range set for a + * new transaction, so we need not do it here.) + */ + if (end < start) + return 0; + + /* Already upgraded to the heap array: full multi-interval tracking. */ + if (ei->i_fc_ranges) { + ext4_fc_range_add(ei, start, end); + return 0; + } + + /* First range of this commit stays inline, no allocation needed. */ + if (ei->i_fc_nr_ranges == 0) { + ei->i_fc_range.start = start; + ei->i_fc_range.len = end - start + 1; + ei->i_fc_nr_ranges = 1; + return 0; + } + + /* One inline range so far. */ + s0 = ei->i_fc_range.start; + e0 = s0 + ei->i_fc_range.len - 1; - ext4_fc_range_add(ei, __arg->start, __arg->end); + /* Disjoint from it: try to upgrade to the array for exact tracking. */ + if (start > e0 + 1 || end + 1 < s0) { + struct ext4_fc_lblk_range *heap; + + /* + * GFP_ATOMIC: we hold i_fc_lock. __GFP_NOWARN: failure is not + * fatal -- we fall back to the single coalesced range below -- + * so it must not splat under memory pressure. + */ + heap = kmalloc_array(EXT4_FC_MAX_RANGES + 1, sizeof(*heap), + GFP_ATOMIC | __GFP_NOWARN); + if (heap) { + heap[0] = ei->i_fc_range; + ei->i_fc_ranges = heap; + ext4_fc_range_add(ei, start, end); + return 0; + } + /* + * Out of memory: fall back to the original single coalesced + * range by absorbing the gap below. This over-logs the spanned + * extents but stays a valid fast commit (no full-commit + * fallback), so there is nothing to mark ineligible. + */ + } + + /* Overlapping/adjacent, or array allocation failed: coalesce inline. */ + if (start < s0) + s0 = start; + if (end > e0) + e0 = end; + ei->i_fc_range.start = s0; + ei->i_fc_range.len = e0 - s0 + 1; return 0; } @@ -1016,7 +1073,10 @@ static int ext4_fc_write_inode_data(struct inode *inode, u32 *crc) spin_unlock(&ei->i_fc_lock); return 0; } - memcpy(ranges, ei->i_fc_ranges, nr * sizeof(ranges[0])); + if (ei->i_fc_ranges) + memcpy(ranges, ei->i_fc_ranges, nr * sizeof(ranges[0])); + else + ranges[0] = ei->i_fc_range; /* inline single-range mode */ ei->i_fc_nr_ranges = 0; spin_unlock(&ei->i_fc_lock); diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 699c15db28a8..93d495cad0ba 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1433,6 +1433,7 @@ static void ext4_free_in_core_inode(struct inode *inode) pr_warn("%s: inode %ld still in fc list", __func__, inode->i_ino); } + kfree(EXT4_I(inode)->i_fc_ranges); kmem_cache_free(ext4_inode_cachep, EXT4_I(inode)); } -- 2.43.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 0/2] ext4: reduce fast-commit write amplification for scattered writes 2026-06-11 4:47 ` [PATCH 0/2] ext4: reduce fast-commit write amplification for scattered writes Daejun Park 2026-06-11 4:48 ` [PATCH 1/2] ext4: track multiple disjoint fast-commit ranges per inode Daejun Park @ 2026-07-21 14:44 ` Theodore Tso 2026-07-22 2:53 ` Daejun Park 1 sibling, 1 reply; 5+ messages in thread From: Theodore Tso @ 2026-07-21 14:44 UTC (permalink / raw) To: Daejun Park Cc: adilger.kernel@dilger.ca, linux-ext4@vger.kernel.org, linux-kernel@vger.kernel.org On Thu, Jun 11, 2026 at 01:47:33PM -0500, Daejun Park wrote: > ext4 fast commit tracks a single coalesced logical range per inode. When > an inode is dirtied at several disjoint offsets between two commits (e.g. > sparse/scattered random writes), that range is widened to span [min, max] > of all the touched offsets, and ext4_fc_write_inode_data() then re-logs > every extent inside that span -- including the unmodified ones. On sparse > allocation this inflates fast-commit traffic and frequently overflows the > fast-commit area, forcing a fallback to a full jbd2 commit. > > This series replaces the single range with a small, bounded set of disjoint > ranges so that only the actually-modified regions are logged, while keeping > the per-inode memory cost negligible: > > Based on v6.17-rc3. Hi Daejun, Unfortunately, this patch series doesn't apply on top of the latest development branch of ext4. Li Chen has also been submitting changes to the fast commit code and your changes are conflicting with his work. Could you look at rebasing your patches against the mainline development of the Linux kernel? Many thanks, - Ted ^ permalink raw reply [flat|nested] 5+ messages in thread
* RE:(2) [PATCH 0/2] ext4: reduce fast-commit write amplification for scattered writes 2026-07-21 14:44 ` [PATCH 0/2] ext4: reduce fast-commit write amplification for scattered writes Theodore Tso @ 2026-07-22 2:53 ` Daejun Park 0 siblings, 0 replies; 5+ messages in thread From: Daejun Park @ 2026-07-22 2:53 UTC (permalink / raw) To: Theodore Tso Cc: adilger.kernel@dilger.ca, linux-ext4@vger.kernel.org, linux-kernel@vger.kernel.org, Daejun Park --------------------------------------------------------------------------- On Tue, Jul 21, 2026 at 10:44:49AM -0400, Theodore Tso wrote: > Unfortunately, this patch series doesn't apply on top of the latest > development branch of ext4. Li Chen has also been submitting changes > to the fast commit code and your changes are conflicting with his > work. Could you look at rebasing your patches against the mainline > development of the Linux kernel? Hi Ted, You are right, thanks for taking a look. I did rebase the series, but I posted the follow-up as a new thread rather than replying here, so it was easy to miss. Sorry about that. v3 is here: https://lore.kernel.org/linux-ext4/20260722005922epcms2p566d8027c8d1dbb8d9c46e3d3b35b6fd0@epcms2p5/ It is based on dev at c143957520c6 and applies and builds cleanly there. Li Chen's snapshot work replaced the commit path the old series was patching, so it is a re-implementation on top of his work rather than a plain rebase. The on-disk format is unchanged. Thanks, Daejun ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-07-22 2:53 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <CGME20260611044733epcms2p38013ae683a283555526f70e4eab6d2a9@epcms2p3>
2026-06-11 4:47 ` [PATCH 0/2] ext4: reduce fast-commit write amplification for scattered writes Daejun Park
2026-06-11 4:48 ` [PATCH 1/2] ext4: track multiple disjoint fast-commit ranges per inode Daejun Park
2026-06-11 4:49 ` [PATCH 2/2] ext4: allocate the fast-commit range array lazily Daejun Park
2026-07-21 14:44 ` [PATCH 0/2] ext4: reduce fast-commit write amplification for scattered writes Theodore Tso
2026-07-22 2:53 ` Daejun Park
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox