From: Kai Huang <kai.huang@intel.com>
To: dave.hansen@linux.intel.com, pbonzini@redhat.com,
seanjc@google.com, kas@kernel.org
Cc: rick.p.edgecombe@intel.com, tglx@kernel.org, bp@alien8.de,
mingo@redhat.com, x86@kernel.org, hpa@zytor.com,
linux-kernel@vger.kernel.org, Kai Huang <kai.huang@intel.com>,
stable@vger.kernel.org, Vishal Verma <vishal.l.verma@intel.com>,
Nikolay Borisov <nik.borisov@suse.com>
Subject: [PATCH v2] x86/virt/tdx: Fix lockdep assertion failure in cache flush for kexec
Date: Thu, 12 Mar 2026 23:00:09 +1300 [thread overview]
Message-ID: <20260312100009.924136-1-kai.huang@intel.com> (raw)
TDX can leave the cache in an incoherent state for the memory it uses.
During kexec the kernel does a WBINVD for each CPU before memory gets
reused in the second kernel.
There were two considerations for where this WBINVD should happen. In
order to handle cases where the cache might get into an incoherent state
while the kexec is in the initial stages, it is needed to do this later
in the kexec path, when the kexecing CPU stops all remote CPUs. However,
the later kexec process is sensitive to existing races. So to avoid
perturbing that operation, it is better to do it earlier.
The existing solution is to track the need for the kexec time WBINVD
generically (i.e., not just for TDX) in a per-cpu var. The late
invocation only happens if the earlier TDX specific logic in
tdx_cpu_flush_cache_for_kexec() didn’t take care of the work. This
earlier WBINVD logic was built into KVM’s existing syscore ops shutdown()
handler, which is called earlier in the kexec path.
However, this accidentally added it to KVM’s unload path as well (also
the "error path" when bringing up TDX during KVM module load), which
uses the same internal functions. This makes some sense too, though,
because if KVM is getting unloaded, TDX cache affecting operations will
likely cease. So it is a good point to do the work before KVM is
unloaded and won't have a chance to handle the shutdown operation in the
future.
Unfortunately this KVM unload invocation triggers a lockdep warning in
tdx_cpu_flush_cache_for_kexec():
IS_ENABLED(CONFIG_PREEMPT_COUNT) && __lockdep_enabled && (preempt_count() == 0 && this_cpu_read(hardirqs_enabled))
WARNING: arch/x86/virt/vmx/tdx/tdx.c:1875 at tdx_cpu_flush_cache_for_kexec+0x36/0x60, CPU#0: cpuhp/0/22
...
Call Trace:
<TASK>
vt_disable_virtualization_cpu+0x1c/0x30 [kvm_intel]
kvm_arch_disable_virtualization_cpu+0x12/0x80 [kvm]
kvm_offline_cpu+0x24/0x40 [kvm]
cpuhp_invoke_callback+0x1b0/0x740
...
Since tdx_cpu_flush_cache_for_kexec() is doing WBINVD on a specific CPU,
it has an assert for preemption being disabled. This works fine for the
kexec time invocation, but the KVM unload path calls this as part of a
CPUHP callback for which, despite always executing on the target CPU,
preemption is not disabled.
It might be better to add the earlier invocation logic to a dedicated
arch/x86 TDX syscore shutdown() handler, but to make the fix more
backport friendly just adjust the lockdep assert in the
tdx_cpu_flush_cache_for_kexec().
The real requirement is tdx_cpu_flush_cache_for_kexec() must be done on
the same CPU. It's OK that it can be preempted in the middle as long as
it won't be rescheduled to another CPU.
Remove the too strong lockdep_assert_preemption_disabled(), and change
this_cpu_{read|write}() to __this_cpu_{read|write}() which provide the
more proper check (when CONFIG_DEBUG_PREEMPT is true), which checks all
conditions that the context cannot be moved to another CPU to run in the
middle.
Fixes: 61221d07e815 ("KVM/TDX: Explicitly do WBINVD when no more TDX SEAMCALLs")
Cc: stable@vger.kernel.org
Reported-by: Vishal Verma <vishal.l.verma@intel.com>
Tested-by: Vishal Verma <vishal.l.verma@intel.com>
Acked-by: Sean Christopherson <seanjc@google.com>
Reviewed-by: Nikolay Borisov <nik.borisov@suse.com>
Reviewed-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
Signed-off-by: Kai Huang <kai.huang@intel.com>
---
v1 -> v2:
- Collect tags - Thanks Nikolay, Rick and Sean!.
- Add the actual lockdep warn splat - Rick, Sean
v1: https://lore.kernel.org/lkml/20260302102226.7459-1-kai.huang@intel.com/
---
arch/x86/virt/vmx/tdx/tdx.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index 8b8e165a2001..6f6be1df4b78 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -1872,9 +1872,7 @@ EXPORT_SYMBOL_FOR_KVM(tdh_phymem_page_wbinvd_hkid);
#ifdef CONFIG_KEXEC_CORE
void tdx_cpu_flush_cache_for_kexec(void)
{
- lockdep_assert_preemption_disabled();
-
- if (!this_cpu_read(cache_state_incoherent))
+ if (!__this_cpu_read(cache_state_incoherent))
return;
/*
@@ -1883,7 +1881,7 @@ void tdx_cpu_flush_cache_for_kexec(void)
* there should be no more SEAMCALLs on this CPU.
*/
wbinvd();
- this_cpu_write(cache_state_incoherent, false);
+ __this_cpu_write(cache_state_incoherent, false);
}
EXPORT_SYMBOL_FOR_KVM(tdx_cpu_flush_cache_for_kexec);
#endif
base-commit: 0f409eaea53e49932cf92a761de66345c9a4b4be
--
2.53.0
next reply other threads:[~2026-03-12 10:00 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-12 10:00 Kai Huang [this message]
2026-03-16 12:14 ` [PATCH v2] x86/virt/tdx: Fix lockdep assertion failure in cache flush for kexec Kiryl Shutsemau
2026-03-16 21:07 ` Huang, Kai
-- strict thread matches above, loose matches on Subject: below --
2026-03-02 10:22 [PATCH] " Kai Huang
2026-03-02 10:22 ` [PATCH v2] " Kai Huang
2026-03-02 10:26 ` Huang, Kai
2026-03-05 18:33 ` Nikolay Borisov
2026-03-05 21:35 ` Huang, Kai
2026-03-06 9:58 ` Nikolay Borisov
2026-03-08 10:12 ` Huang, Kai
2026-03-10 13:43 ` Sean Christopherson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260312100009.924136-1-kai.huang@intel.com \
--to=kai.huang@intel.com \
--cc=bp@alien8.de \
--cc=dave.hansen@linux.intel.com \
--cc=hpa@zytor.com \
--cc=kas@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=nik.borisov@suse.com \
--cc=pbonzini@redhat.com \
--cc=rick.p.edgecombe@intel.com \
--cc=seanjc@google.com \
--cc=stable@vger.kernel.org \
--cc=tglx@kernel.org \
--cc=vishal.l.verma@intel.com \
--cc=x86@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox