Re: [syzbot] [kvm?] WARNING: locking bug in __refill_objects_node

From: Sean Christopherson

Date: Mon Nov 02 2026 - 15:27:06 EST


+Paolo

On Tue, Oct 27, 2026, Steven Rostedt wrote:
> On Tue, 27 Oct 2026 18:48:28 +0000
> Harry Yoo <harry@xxxxxxxxxx> wrote:
>
> > [+Cc RT folks]
> >
> > On Tue, Oct 27, 2026 at 07:00:16AM -0700, Sean Christopherson wrote:
> > > +slub folks
> > >
> > > On Tue, Oct 27, 2026, Hillf Danton wrote:
> > > > Ask the locking people to shed light on the warning.
> > >
> > > get_partial_node_bulk() is waiting on a non-raw spinlock while in hard IRQ context,
> > > and it looks like __refill_objects_node() would do the same later on. On PREEMPT_RT,
> > > those spinlocks become sleepable locks, which is what triggers the splat.
> > >
> > > The alloction coming from KVM-as-a-guest code is GFP_ATOMIC, so I think that wonky
> > > code is fine.
> >
> > But memory allocation w/ GFP_ATOMIC is fine only on non-RT?
>
> Correct. GFP_ATOMIC is meaningless on RT. It still cannot be called with
> preemption disabled. Most places that disable preemption in the non-RT case
> do not disable it in the RT case. For example, spin_lock() disables
> preemption on non-RT but does not in RT. Same for interrupt handlers, as in
> RT interrupt handlers are run in task context.
>
> That is, in RT only "special" places disable preemption for real.

...

> > > > > =============================
> > > > > [ BUG: Invalid wait context ]
> > > > > syzkaller #0 Tainted: G S
> > > > > -----------------------------
> > > > > syz-executor177/6041 is trying to lock:
> > > > > ffff88801d040618 (&n->list_lock){-.-.}-{3:3}, at: get_partial_node_bulk mm/slub.c:3831 [inline]
> > > > > ffff88801d040618 (&n->list_lock){-.-.}-{3:3}, at: __refill_objects_node+0x104/0xab0 mm/slub.c:7279
> > > > > other info that might help us debug this:
> > > > > context-{2:2}
> > > > > locks held by syz-executor177/6041: 1, last CPU#1:
> > > > > #0: ffffffff9090b438 (tomoyo_ss){.+.+}-{0:0}, at: srcu_lock_acquire include/linux/srcu.h:198 [inline]
> > > > > #0: ffffffff9090b438 (tomoyo_ss){.+.+}-{0:0}, at: srcu_read_lock include/linux/srcu.h:305 [inline]
> > > > > #0: ffffffff9090b438 (tomoyo_ss){.+.+}-{0:0}, at: tomoyo_read_lock security/tomoyo/common.h:1112 [inline]
> > > > > #0: ffffffff9090b438 (tomoyo_ss){.+.+}-{0:0}, at: tomoyo_check_open_permission+0x169/0x3c0 security/tomoyo/file.c:772
> > > > > stack backtrace:
> > > > > CPU: 1 UID: 0 PID: 6041 Comm: syz-executor177 Tainted: G S syzkaller #0 PREEMPT(full)
> > > > > Tainted: [S]=CPU_OUT_OF_SPEC
> > > > > Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
> > > > > Call Trace:
> > > > > <IRQ>
> > > > > __dump_stack lib/dump_stack.c:94 [inline]
> > > > > dump_stack_lvl+0x100/0x190 lib/dump_stack.c:120
> > > > > print_lock_invalid_wait_context kernel/locking/lockdep.c:4874 [inline]
> > > > > check_wait_context kernel/locking/lockdep.c:4946 [inline]
> > > > > __lock_acquire+0xfcc/0x1f40 kernel/locking/lockdep.c:5238
> > > > > lock_acquire kernel/locking/lockdep.c:5942 [inline]
> > > > > lock_acquire+0x1d1/0x380 kernel/locking/lockdep.c:5899
> > > > > __raw_spin_lock_irqsave include/linux/spinlock_api_smp.h:155 [inline]
> > > > > _raw_spin_lock_irqsave+0x3a/0x60 kernel/locking/spinlock.c:181
> > > > > get_partial_node_bulk mm/slub.c:3831 [inline]
> > > > > __refill_objects_node+0x104/0xab0 mm/slub.c:7279
> > > > > refill_objects+0x58/0x410 mm/slub.c:7405
> > > > > refill_sheaf mm/slub.c:2885 [inline]
> > > > > __pcs_replace_empty_main+0x35e/0x650 mm/slub.c:4774
> > > > > alloc_from_pcs mm/slub.c:4850 [inline]
> > > > > slab_alloc_node mm/slub.c:4984 [inline]
> > > > > __kmalloc_cache_noprof+0x4f4/0x6d0 mm/slub.c:5559
> > > > > _kmalloc_noprof include/linux/slab.h:991 [inline]
> > > > > _kzalloc_noprof include/linux/slab.h:1312 [inline]
> > > > > kvm_async_pf_task_wake arch/x86/kernel/kvm.c:224 [inline]
>
> Hmm, this part is OK:
>
> if (!dummy) {
> raw_spin_unlock(&b->lock);
> dummy = kzalloc_obj(*dummy, GFP_ATOMIC);
>
> As it releases the raw_spin_lock() before calling kzalloc_obj().
>
> So something else must have disabled preemption/interrupts.

Heh, yeah, the IRQ that kicked all of this off.

> > > > > __sysvec_kvm_asyncpf_interrupt+0x334/0x610 arch/x86/kernel/kvm.c:338
>
> DEFINE_IDTENTRY_SYSVEC(sysvec_kvm_asyncpf_interrupt)
> {
> struct pt_regs *old_regs = set_irq_regs(regs);
> u32 token;
>
> apic_eoi();
>
> inc_irq_stat(HYPERVISOR_CALLBACK);
>
> if (__this_cpu_read(async_pf_enabled)) {
> token = __this_cpu_read(apf_reason.token);
> kvm_async_pf_task_wake(token);
> __this_cpu_write(apf_reason.token, 0);
> wrmsrq(MSR_KVM_ASYNC_PF_ACK, 1);
> }
>
> set_irq_regs(old_regs);
> }
>
>
> Is that run in hard interrupt context?

Yep.

> If so, we need to make that a thread for RT kernels if you want to call
> kzalloc_obj().

Eww. I can't imagine there's a sane usecase for memory overcommitting a real-time
guest. I'd rather just disable PV async #PF support, e.g.

diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c
index 6b0a5861ccb8..b14b03720525 100644
--- a/arch/x86/kernel/kvm.c
+++ b/arch/x86/kernel/kvm.c
@@ -51,7 +51,7 @@

DEFINE_STATIC_KEY_FALSE_RO(kvm_async_pf_enabled);

-static int kvmapf = 1;
+static int kvmapf = !IS_ENABLED(CONFIG_PREEMPT_RT);

static int __init parse_no_kvmapf(char *arg)
{