diskfs_journal_commit_transaction promises that the caller's changes
are on disk when it returns.  A non-empty running transaction commits
only after the one in flight has finished, so its commit covers all
older changes.  But an empty running transaction is kept open and the
call returns at once, while the caller's earlier changes can be in the
transaction kjournald is committing at that moment.  This happens when
a caller opens its handle just after kjournald has locked the
transaction holding its changes.

After committing the running transaction, also wait for the one before
it.  That wait is satisfied at once after a real commit.  The caller's
handle is already released, so the wait cannot hold back a drain, and
a failed commit sets j_last_committed_tid, so it cannot wait forever.
---
 ext2fs/journal.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/ext2fs/journal.c b/ext2fs/journal.c
index 1212657b7..2ab8802ff 100644
--- a/ext2fs/journal.c
+++ b/ext2fs/journal.c
@@ -2990,6 +2990,10 @@ diskfs_journal_commit_transaction (diskfs_transaction_t 
*opaque_txn)
        JRNL_LOG_WARN
          ("Commit failed in diskfs_journal_commit_transaction: %s",
           strerror (err));
+      /* An empty transaction stays open without waiting, while the
+        caller's earlier changes can be in the one kjournald is
+        committing.  The caller asked for them to be durable too.  */
+      journal_wait_on_tid_locked (ext2_journal, tid - 1);
       goto out;
     }
   /* We missed it. Someone else (kjournald) stole it.
-- 
2.56.0


Reply via email to