Linux EXT4 FS development
 help / color / mirror / Atom feed
* [PATCH] jbd2: don't start a fast commit while the journal is marked empty
@ 2026-10-07  0:41 Daejun Park via B4 Relay
  2026-10-07  0:49 ` sashiko-bot
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Daejun Park via B4 Relay @ 2026-10-07  0:41 UTC (permalink / raw)
  To: Theodore Ts'o, Jan Kara, Jan Kara, linux-ext4
  Cc: Andreas Dilger, Baokun Li, Ojaswin Mujoo, Ritesh Harjani (IBM),
	Zhang Yi, Zhang Yi, Harshad Shirwadkar, Li Chen, linux-kernel,
	stable, Daejun Park

From: Daejun Park <daejun7.park@samsung.com>

jbd2_journal_flush() checkpoints everything, and
jbd2_mark_journal_empty() then writes the journal superblock with
s_start == 0 and the fast commit feature cleared. It sets JBD2_FLUSHED
so that the next commit writes the log tail and the feature back, but
only jbd2_journal_commit_transaction() looks at that flag. A fast commit
started before the next full commit writes its blocks while the
superblock on disk still says the log is empty: jbd2_journal_recover()
returns at once and the fast commit is never replayed.

On ext4 with -O fast_commit this happens after ext4_freeze(), and after
a remount to read-only, which flushes the journal in
ext4_mark_recovery_complete(), once the file system is read-write again.
EXT4_IOC_CHECKPOINT and online resize flush it as well. An fsync that a
fast commit handles after one of them is lost if the system crashes
before the next full commit, which can be up to the commit interval
later:

  mount -o commit=60 (-O fast_commit file system)
  echo first > a; sync
  fsfreeze -f; fsfreeze -u      # or: remount ro, then rw
  write b; fsync b              # one fast commit, s_start still 0
  crash (kill the VM), mount
  b is missing

Refuse to start a fast commit while JBD2_FLUSHED is set, as is already
done before the first full commit. ext4 then falls back to a full
commit, which writes the log tail, and the fast commits after it are
recovered again. fc_info counts the refused fast commit as ineligible,
as it does for the existing check.

Fixes: ff780b91efe9 ("jbd2: add fast commit machinery")
Cc: stable@vger.kernel.org
Signed-off-by: Daejun Park <daejun7.park@samsung.com>
---
Tested in QEMU on ext4 dev 9091c97be340, crashing by killing QEMU: the
sequence above loses b without this change and keeps it with it, both
after fsfreeze and after a remount ro then rw. A second fsync after the
fallback is a fast commit again and is recovered. xfstests ext4/044
ext4/045 generic/455 generic/456 generic/482 with -O fast_commit, and
generic/068 generic/085 generic/390, give the same results as without
the change (generic/455 fails on both).
---
 fs/jbd2/journal.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
index 00f5a98f3d..22576af8c8 100644
--- a/fs/jbd2/journal.c
+++ b/fs/jbd2/journal.c
@@ -695,7 +695,8 @@ int jbd2_log_wait_commit(journal_t *journal, tid_t tid)
  * it to complete. Returns 0 if a new fast commit was started. Returns -EALREADY
  * if a fast commit is not needed, either because there's an already a commit
  * going on or this tid has already been committed. Returns -EINVAL if no jbd2
- * commit has yet been performed.
+ * commit has yet been performed, or none since the journal was last marked
+ * empty.
  */
 int jbd2_fc_begin_commit(journal_t *journal, tid_t tid)
 {
@@ -714,6 +715,16 @@ int jbd2_fc_begin_commit(journal_t *journal, tid_t tid)
 		return -EALREADY;
 	}
 
+	/*
+	 * The log was emptied and the superblock on disk says so, which makes
+	 * recovery skip the fast commit area as well. Only a full commit
+	 * records the log tail again.
+	 */
+	if (journal->j_flags & JBD2_FLUSHED) {
+		write_unlock(&journal->j_state_lock);
+		return -EINVAL;
+	}
+
 	if (journal->j_flags & JBD2_FULL_COMMIT_ONGOING ||
 	    (journal->j_flags & JBD2_FAST_COMMIT_ONGOING)) {
 		DEFINE_WAIT(wait);

---
base-commit: 9091c97be34083587a75db174aab51551d8e8543
change-id: 20261006-jbd2-fc-after-flush-2ef9bbc32cea

Best regards,
-- 
Daejun Park <daejun7.park@samsung.com>



^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH] jbd2: don't start a fast commit while the journal is marked empty
  2026-10-07  0:41 [PATCH] jbd2: don't start a fast commit while the journal is marked empty Daejun Park via B4 Relay
@ 2026-10-07  0:49 ` sashiko-bot
  2026-10-07 12:44 ` Jan Kara
  2026-10-08  6:35 ` Zhang Yi
  2 siblings, 0 replies; 4+ messages in thread
From: sashiko-bot @ 2026-10-07  0:49 UTC (permalink / raw)
  To: Daejun Park; +Cc: tytso, linux-ext4

> From: Daejun Park <daejun7.park@samsung.com>
> 
> jbd2_journal_flush() checkpoints everything, and
> jbd2_mark_journal_empty() then writes the journal superblock with
> s_start == 0 and the fast commit feature cleared. It sets JBD2_FLUSHED
> so that the next commit writes the log tail and the feature back, but
> only jbd2_journal_commit_transaction() looks at that flag. A fast commit
> started before the next full commit writes its blocks while the
> superblock on disk still says the log is empty: jbd2_journal_recover()
> returns at once and the fast commit is never replayed.
> 
> On ext4 with -O fast_commit this happens after ext4_freeze(), and after
> a remount to read-only, which flushes the journal in
> ext4_mark_recovery_complete(), once the file system is read-write again.
> EXT4_IOC_CHECKPOINT and online resize flush it as well. An fsync that a
> [ ... ]
> as it does for the existing check.
> 
> Fixes: ff780b91efe9 ("jbd2: add fast commit machinery")
> Cc: stable@vger.kernel.org
> Signed-off-by: Daejun Park <daejun7.park@samsung.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261007-jbd2-fc-after-flush-v1-1-548bd91cd959@samsung.com?part=1


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] jbd2: don't start a fast commit while the journal is marked empty
  2026-10-07  0:41 [PATCH] jbd2: don't start a fast commit while the journal is marked empty Daejun Park via B4 Relay
  2026-10-07  0:49 ` sashiko-bot
@ 2026-10-07 12:44 ` Jan Kara
  2026-10-08  6:35 ` Zhang Yi
  2 siblings, 0 replies; 4+ messages in thread
From: Jan Kara @ 2026-10-07 12:44 UTC (permalink / raw)
  To: daejun7.park
  Cc: Theodore Ts'o, Jan Kara, Jan Kara, linux-ext4, Andreas Dilger,
	Baokun Li, Ojaswin Mujoo, Ritesh Harjani (IBM), Zhang Yi,
	Zhang Yi, Harshad Shirwadkar, Li Chen, linux-kernel, stable

On Wed 07-10-26 09:41:09, Daejun Park via B4 Relay wrote:
> From: Daejun Park <daejun7.park@samsung.com>
> 
> jbd2_journal_flush() checkpoints everything, and
> jbd2_mark_journal_empty() then writes the journal superblock with
> s_start == 0 and the fast commit feature cleared. It sets JBD2_FLUSHED
> so that the next commit writes the log tail and the feature back, but
> only jbd2_journal_commit_transaction() looks at that flag. A fast commit
> started before the next full commit writes its blocks while the
> superblock on disk still says the log is empty: jbd2_journal_recover()
> returns at once and the fast commit is never replayed.
> 
> On ext4 with -O fast_commit this happens after ext4_freeze(), and after
> a remount to read-only, which flushes the journal in
> ext4_mark_recovery_complete(), once the file system is read-write again.
> EXT4_IOC_CHECKPOINT and online resize flush it as well. An fsync that a
> fast commit handles after one of them is lost if the system crashes
> before the next full commit, which can be up to the commit interval
> later:
> 
>   mount -o commit=60 (-O fast_commit file system)
>   echo first > a; sync
>   fsfreeze -f; fsfreeze -u      # or: remount ro, then rw
>   write b; fsync b              # one fast commit, s_start still 0
>   crash (kill the VM), mount
>   b is missing
> 
> Refuse to start a fast commit while JBD2_FLUSHED is set, as is already
> done before the first full commit. ext4 then falls back to a full
> commit, which writes the log tail, and the fast commits after it are
> recovered again. fc_info counts the refused fast commit as ineligible,
> as it does for the existing check.
> 
> Fixes: ff780b91efe9 ("jbd2: add fast commit machinery")
> Cc: stable@vger.kernel.org
> Signed-off-by: Daejun Park <daejun7.park@samsung.com>

Yeah, good catch! Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
> Tested in QEMU on ext4 dev 9091c97be340, crashing by killing QEMU: the
> sequence above loses b without this change and keeps it with it, both
> after fsfreeze and after a remount ro then rw. A second fsync after the
> fallback is a fast commit again and is recovered. xfstests ext4/044
> ext4/045 generic/455 generic/456 generic/482 with -O fast_commit, and
> generic/068 generic/085 generic/390, give the same results as without
> the change (generic/455 fails on both).
> ---
>  fs/jbd2/journal.c | 13 ++++++++++++-
>  1 file changed, 12 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
> index 00f5a98f3d..22576af8c8 100644
> --- a/fs/jbd2/journal.c
> +++ b/fs/jbd2/journal.c
> @@ -695,7 +695,8 @@ int jbd2_log_wait_commit(journal_t *journal, tid_t tid)
>   * it to complete. Returns 0 if a new fast commit was started. Returns -EALREADY
>   * if a fast commit is not needed, either because there's an already a commit
>   * going on or this tid has already been committed. Returns -EINVAL if no jbd2
> - * commit has yet been performed.
> + * commit has yet been performed, or none since the journal was last marked
> + * empty.
>   */
>  int jbd2_fc_begin_commit(journal_t *journal, tid_t tid)
>  {
> @@ -714,6 +715,16 @@ int jbd2_fc_begin_commit(journal_t *journal, tid_t tid)
>  		return -EALREADY;
>  	}
>  
> +	/*
> +	 * The log was emptied and the superblock on disk says so, which makes
> +	 * recovery skip the fast commit area as well. Only a full commit
> +	 * records the log tail again.
> +	 */
> +	if (journal->j_flags & JBD2_FLUSHED) {
> +		write_unlock(&journal->j_state_lock);
> +		return -EINVAL;
> +	}
> +
>  	if (journal->j_flags & JBD2_FULL_COMMIT_ONGOING ||
>  	    (journal->j_flags & JBD2_FAST_COMMIT_ONGOING)) {
>  		DEFINE_WAIT(wait);
> 
> ---
> base-commit: 9091c97be34083587a75db174aab51551d8e8543
> change-id: 20261006-jbd2-fc-after-flush-2ef9bbc32cea
> 
> Best regards,
> -- 
> Daejun Park <daejun7.park@samsung.com>
> 
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] jbd2: don't start a fast commit while the journal is marked empty
  2026-10-07  0:41 [PATCH] jbd2: don't start a fast commit while the journal is marked empty Daejun Park via B4 Relay
  2026-10-07  0:49 ` sashiko-bot
  2026-10-07 12:44 ` Jan Kara
@ 2026-10-08  6:35 ` Zhang Yi
  2 siblings, 0 replies; 4+ messages in thread
From: Zhang Yi @ 2026-10-08  6:35 UTC (permalink / raw)
  To: daejun7.park
  Cc: linux-ext4, Theodore Ts'o, Jan Kara, Jan Kara, Andreas Dilger,
	Baokun Li, Ojaswin Mujoo, Ritesh Harjani (IBM), Zhang Yi,
	Harshad Shirwadkar, Li Chen, linux-kernel, stable

On 10/7/2026 8:41 AM, Daejun Park via B4 Relay wrote:
> From: Daejun Park <daejun7.park@samsung.com>
> 
> jbd2_journal_flush() checkpoints everything, and
> jbd2_mark_journal_empty() then writes the journal superblock with
> s_start == 0 and the fast commit feature cleared. It sets JBD2_FLUSHED
> so that the next commit writes the log tail and the feature back, but
> only jbd2_journal_commit_transaction() looks at that flag. A fast commit
> started before the next full commit writes its blocks while the
> superblock on disk still says the log is empty: jbd2_journal_recover()
> returns at once and the fast commit is never replayed.
> 
> On ext4 with -O fast_commit this happens after ext4_freeze(), and after
> a remount to read-only, which flushes the journal in
> ext4_mark_recovery_complete(), once the file system is read-write again.
> EXT4_IOC_CHECKPOINT and online resize flush it as well. An fsync that a
> fast commit handles after one of them is lost if the system crashes
> before the next full commit, which can be up to the commit interval
> later:
> 
>   mount -o commit=60 (-O fast_commit file system)
>   echo first > a; sync
>   fsfreeze -f; fsfreeze -u      # or: remount ro, then rw
>   write b; fsync b              # one fast commit, s_start still 0
>   crash (kill the VM), mount
>   b is missing
> 
> Refuse to start a fast commit while JBD2_FLUSHED is set, as is already
> done before the first full commit. ext4 then falls back to a full
> commit, which writes the log tail, and the fast commits after it are
> recovered again. fc_info counts the refused fast commit as ineligible,
> as it does for the existing check.
> 
> Fixes: ff780b91efe9 ("jbd2: add fast commit machinery")
> Cc: stable@vger.kernel.org
> Signed-off-by: Daejun Park <daejun7.park@samsung.com>

This makes sense to me!

Reviewed-by: Zhang Yi <yi.zhang@huawei.com>

> ---
> Tested in QEMU on ext4 dev 9091c97be340, crashing by killing QEMU: the
> sequence above loses b without this change and keeps it with it, both
> after fsfreeze and after a remount ro then rw. A second fsync after the
> fallback is a fast commit again and is recovered. xfstests ext4/044
> ext4/045 generic/455 generic/456 generic/482 with -O fast_commit, and
> generic/068 generic/085 generic/390, give the same results as without
> the change (generic/455 fails on both).
> ---
>  fs/jbd2/journal.c | 13 ++++++++++++-
>  1 file changed, 12 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
> index 00f5a98f3d..22576af8c8 100644
> --- a/fs/jbd2/journal.c
> +++ b/fs/jbd2/journal.c
> @@ -695,7 +695,8 @@ int jbd2_log_wait_commit(journal_t *journal, tid_t tid)
>   * it to complete. Returns 0 if a new fast commit was started. Returns -EALREADY
>   * if a fast commit is not needed, either because there's an already a commit
>   * going on or this tid has already been committed. Returns -EINVAL if no jbd2
> - * commit has yet been performed.
> + * commit has yet been performed, or none since the journal was last marked
> + * empty.
>   */
>  int jbd2_fc_begin_commit(journal_t *journal, tid_t tid)
>  {
> @@ -714,6 +715,16 @@ int jbd2_fc_begin_commit(journal_t *journal, tid_t tid)
>  		return -EALREADY;
>  	}
>  
> +	/*
> +	 * The log was emptied and the superblock on disk says so, which makes
> +	 * recovery skip the fast commit area as well. Only a full commit
> +	 * records the log tail again.
> +	 */
> +	if (journal->j_flags & JBD2_FLUSHED) {
> +		write_unlock(&journal->j_state_lock);
> +		return -EINVAL;
> +	}
> +
>  	if (journal->j_flags & JBD2_FULL_COMMIT_ONGOING ||
>  	    (journal->j_flags & JBD2_FAST_COMMIT_ONGOING)) {
>  		DEFINE_WAIT(wait);
> 
> ---
> base-commit: 9091c97be34083587a75db174aab51551d8e8543
> change-id: 20261006-jbd2-fc-after-flush-2ef9bbc32cea
> 
> Best regards,


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-10-08  6:35 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-07  0:41 [PATCH] jbd2: don't start a fast commit while the journal is marked empty Daejun Park via B4 Relay
2026-10-07  0:49 ` sashiko-bot
2026-10-07 12:44 ` Jan Kara
2026-10-08  6:35 ` Zhang Yi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox