Skip to content

trimwrite: do not terminate between a trim and its paired write - #2131

Open
Vladyyy wants to merge 1 commit into
axboe:masterfrom
Vladyyy:trimwrite-complete-pair
Open

trimwrite: do not terminate between a trim and its paired write#2131
Vladyyy wants to merge 1 commit into
axboe:masterfrom
Vladyyy:trimwrite-complete-pair

Conversation

@Vladyyy

@Vladyyy Vladyyy commented Aug 6, 2026

Copy link
Copy Markdown

In trimwrite mode each block is trimmed and then written back by two
consecutive io_us, paired via the per-file last_start markers in
set_rw_ddir(). The do_io() termination checks (runtime_exceeded and
the bytes_issued budget) run once per io_u with no awareness of the
pairing, so a time_based or byte-bounded run can stop after issuing a
trim but before its paired write: the final block is left deallocated
("dangling trim") and replay-based verification of the write_iolog
false-fails with "bad magic" on that range. Reproduces on effectively
every time_based trimwrite run.

Defer termination while a pair is open so the loop issues exactly one
more io_u (the paired write) and terminates on the next iteration.

Validated on a loop device: 10/10 time_based runs ended on a dangling
trim (trims == writes+1, iolog replay failing "bad magic") before,
0/10 after with replay passing; loops-bounded runs issue identical op
counts (already pair-complete), so their behavior is unchanged.

Fixes: #2122

Signed-off-by: Vlad Tudose tudosevt@amazon.com

In trimwrite mode each block is trimmed and then written back by two
consecutive io_us, paired via the per-file last_start markers in
set_rw_ddir(). The do_io() termination checks (runtime_exceeded and
the bytes_issued budget) run once per io_u with no awareness of the
pairing, so a time_based or byte-bounded run can stop after issuing a
trim but before its paired write: the final block is left deallocated
("dangling trim") and replay-based verification of the write_iolog
false-fails with "bad magic" on that range. Reproduces on effectively
every time_based trimwrite run.

Defer termination while a pair is open so the loop issues exactly one
more io_u (the paired write) and terminates on the next iteration.

Validated on a loop device: 10/10 time_based runs ended on a dangling
trim (trims == writes+1, iolog replay failing "bad magic") before,
0/10 after with replay passing; loops-bounded runs issue identical op
counts (already pair-complete), so their behavior is unchanged.

Fixes: axboe#2122

Signed-off-by: Vlad Tudose <tudosevt@amazon.com>
@Vladyyy
Vladyyy force-pushed the trimwrite-complete-pair branch from 2a6b1d4 to 3e3436c Compare August 6, 2026 23:59
Comment thread backend.c
*/
if (bytes_issued >= total_bytes &&
!td->o.read_iolog_file &&
!trimwrite_mid_pair(td) &&

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bytes_issued is adjusted near the top of do_io() for trimwrite workloads. Why is this check necessary here?

Comment thread backend.c
*
* Returns number of bytes written and trimmed.
*/
static bool trimwrite_mid_pair(struct thread_data *td)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment above this function applies to do_io()

Comment thread backend.c
{
struct fio_file *f;

if (!td_trimwrite(td) || td->o.nr_files != 1)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

td_trimwrite() just checks if the write and trim bits of the job's ddir are set. Both of the bits are set for rw=[rand]trimwrite but can also be set for iolog replay jobs. Would this check cause an iolog replay job that includes trims and writes to exceed its specified runtime?

@vincentkfu

vincentkfu commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

It would help if in addition to the prose descriptions of how to reproduce the bug, you also include specific command lines or job files.
Can you also describe in more detail how you are carrying out "replay-based verification of the write_iolog"? Are you verifying the written data after the trimwrite job has completed based on the iolog? How can this fail if the final write is not issued?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Title: rw=trimwrite: termination can split a trim+write pair, leaving the last trimmed block deallocated

2 participants