All of lore.kernel.org
 help / color / mirror / Atom feed
* Btrfs PREEMPT_RT transaction-waiter livelock: experimental mitigation in kernel 6.6.78
@ 2026-10-09 15:34 Naulls, Peter
  2026-10-13 14:41 ` Sebastian Andrzej Siewior
       [not found] ` <SJ0PR17MB4288FC2E7FFB577F5D6C6D9EBB9E2@SJ0PR17MB4288.namprd17.prod.outlook.com>
  0 siblings, 2 replies; 7+ messages in thread
From: Naulls, Peter @ 2026-10-09 15:34 UTC (permalink / raw)
  To: linux-rt-users@vger.kernel.org

Long term kernel hacker, but very infrequent contributor. 

We are using the RT kernel patch on x86 under kernel 6.6.78-rt51. Specifically, this is the kernel from meta-intel in Yocto Scarthgap in the intel-kernel-rt recipe. 

It appears under some high load conditions, and especially triggered by calls to sync(), there is a live lock condition in the btrfs code. I used copilot for this analysis, with apologies due here where relevant. I'm familiar with kernel code, and I'm well aware how complex filesystem interactions can be. 

Here is the lock we observe:


sync (PID 52371)
  btrfs_commit_transaction+0xc48
    [return address immediately after rt_spin_lock(&fs_info->trans_lock)]
  btrfs_sync_fs
  sync_fs_one_sb
  ksys_sync

ordered-I/O completion workers
  join_transaction+0x2b
    [return address immediately after rt_spin_lock(&fs_info->trans_lock)]
  start_transaction
  btrfs_join_transaction
  btrfs_finish_one_ordered
  btrfs_work_helper

journal/fsync callers
  wait_current_trans+0x33
    [return address immediately after rt_spin_lock(&fs_info->trans_lock)]
  start_transaction
  btrfs_start_transaction
  btrfs_sync_file
  vfs_fsync_range
  fsync

btrfs-transaction thread
  transaction_kthread+0x76
    [return address immediately after rt_spin_lock(&fs_info->trans_lock)]
  kthread


The processes are then not killable, indicting a kernel thread problem. 

This was largely found because our userspace code calls sync() excessively (being separately addressed), although that's not the proximate cause. We ended up writing shell script which writes blocks of data to a brtfs partition over several threads and also calls sync and were able to repeat it within 10s of seconds. It is not repeatable in the non-RT kernel.

To add:

I have been extensively through the later kernels in 6.6, and later series and have not found any direct fixe or references for this. I've also been through the latest RT changes and btrfs mailing lists/changes (and this post might be served better there) and cannot find any direct reference to this. The only close match is:

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=99da896614d17e8a84aeb2b2d464ac046cc8633d

This is in related code. I put this patch into our 6.6.78 kernel and it did not resolve the problem. 

With that in mind, I had copilot (again, apologies) have an attempt. It came up with this:

# Experimental mitigation for a suspected PREEMPT_RT Btrfs transaction-waiter
# livelock. wait_for_commit() can reselect a completed transaction that remains
# on trans_list after its waiters are woken. Skip completed entries while still
# waiting for older incomplete transactions. This is a diagnostic hypothesis,
# not a confirmed fix for the observed lockup.
# Review notes: the scan is O(n) under trans_lock. Completion state is published
# locklessly elsewhere; READ_ONCE provides a single load, not acquire ordering.
# A multi-hour run did not reproduce the issue, but the lock owner and root cause
# remain unknown.
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index e15e1855fa0a..0f05fabe451c 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -865,6 +865,7 @@ static noinline void wait_for_commit(struct btrfs_transaction *commit,
                                     const enum btrfs_trans_state min_state)
 {
        struct btrfs_fs_info *fs_info = commit->fs_info;
+       struct btrfs_transaction *next;
        u64 transid = commit->transid;
        bool put = false;

@@ -893,10 +894,16 @@ static noinline void wait_for_commit(struct btrfs_transaction *commit,
                 */

                spin_lock(&fs_info->trans_lock);
-               commit = list_first_entry_or_null(&fs_info->trans_list,
-                                                 struct btrfs_transaction,
-                                                 list);
-               if (!commit || commit->transid > transid) {
+               commit = NULL;
+               list_for_each_entry(next, &fs_info->trans_list, list) {
+                       if (next->transid > transid)
+                               break;
+                       if (READ_ONCE(next->state) == TRANS_STATE_COMPLETED)
+                               continue;
+                       commit = next;
+                       break;
+               }
+               if (!commit) {
                        spin_unlock(&fs_info->trans_lock);
                        break;
                }


Note the O(N) time here. To that end, this is very unlikely to be the correct fix, but I hope it highlights the core problem. I ran this patch for several hours with no apparent problem (versus seconds of a lockup in previous testing). 

If there's a later kernel patch I've missed, or something else I ought to be looking at or test or need more info, please let me know. 



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

end of thread, other threads:[~2026-10-14 14:33 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-09 15:34 Btrfs PREEMPT_RT transaction-waiter livelock: experimental mitigation in kernel 6.6.78 Naulls, Peter
2026-10-13 14:41 ` Sebastian Andrzej Siewior
2026-10-13 15:29   ` [EXTERNAL] " Naulls, Peter
2026-10-13 15:52     ` Sebastian Andrzej Siewior
     [not found] ` <SJ0PR17MB4288FC2E7FFB577F5D6C6D9EBB9E2@SJ0PR17MB4288.namprd17.prod.outlook.com>
2026-10-13 17:33   ` Naulls, Peter
2026-10-14 14:23     ` Daniel Vacek
2026-10-14 14:33     ` Daniel Vacek

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.