[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250523095322.88774-11-chao.gao@intel.com>
Date: Fri, 23 May 2025 02:52:33 -0700
From: Chao Gao <chao.gao@...el.com>
To: linux-coco@...ts.linux.dev,
x86@...nel.org,
kvm@...r.kernel.org
Cc: seanjc@...gle.com,
pbonzini@...hat.com,
eddie.dong@...el.com,
kirill.shutemov@...el.com,
dave.hansen@...el.com,
dan.j.williams@...el.com,
kai.huang@...el.com,
isaku.yamahata@...el.com,
elena.reshetova@...el.com,
rick.p.edgecombe@...el.com,
Chao Gao <chao.gao@...el.com>,
Farrah Chen <farrah.chen@...el.com>,
"Kirill A. Shutemov" <kirill.shutemov@...ux.intel.com>,
Dave Hansen <dave.hansen@...ux.intel.com>,
Thomas Gleixner <tglx@...utronix.de>,
Ingo Molnar <mingo@...hat.com>,
Borislav Petkov <bp@...en8.de>,
"H. Peter Anvin" <hpa@...or.com>,
linux-kernel@...r.kernel.org
Subject: [RFC PATCH 10/20] x86/virt/seamldr: Introduce skeleton for TD-Preserving updates
To perform TD-Preserving updates, the kernel must stop invoking any TDX
module SEAMCALLs. Currently, these SEAMCALLs can be invoked in various
contexts and in parallel across CPUs. Additionally, considering the need to
force all vCPUs out of guest mode, no single lock primitive, except for
stop_machine(), can meet this requirement.
A failed attempt is to lock all KVM entry points and kick all vCPUs. But it
cannot be done within KVM TDX code. And it needs to introduce new
infrastructure and maintenance burden outside of tdx for questionable
benefits.
Perform TD-Preserving updates within stop_machine() as it achieves the
seamldr requirements and is an existing well understood mechanism.
TD-Preserving updates consist of several steps: shutting down the old
module, installing the new module, and initializing the new one and etc.
Some steps must be executed on a single CPU, others serially across all
CPUs, and some can be performed concurrently on all CPUs and there are
ordering requirements between steps. So, all CPUs need to perform the work
in a step-locked manner.
In preparation for adding concrete steps for TD-Preserving updates,
establish the framework by mimicking multi_cpu_stop(). Specifically, use a
global state machine to control the work done on each CPU and require all
CPUs to acknowledge completion before proceeding to the next stage.
Signed-off-by: Chao Gao <chao.gao@...el.com>
Tested-by: Farrah Chen <farrah.chen@...el.com>
---
instead of copy-pasting multi_cpu_stop(), would it be better to
abstract a common function and adapt it for TD-Preserving updates?
---
arch/x86/virt/vmx/tdx/seamldr.c | 63 +++++++++++++++++++++++++++++++--
1 file changed, 60 insertions(+), 3 deletions(-)
diff --git a/arch/x86/virt/vmx/tdx/seamldr.c b/arch/x86/virt/vmx/tdx/seamldr.c
index cdf85dff6d69..01dc2b0bc4a5 100644
--- a/arch/x86/virt/vmx/tdx/seamldr.c
+++ b/arch/x86/virt/vmx/tdx/seamldr.c
@@ -12,7 +12,9 @@
#include <linux/gfp.h>
#include <linux/kobject.h>
#include <linux/mm.h>
+#include <linux/nmi.h>
#include <linux/slab.h>
+#include <linux/stop_machine.h>
#include <linux/sysfs.h>
#include "tdx.h"
@@ -237,6 +239,62 @@ static struct seamldr_params *init_seamldr_params(const u8 *data, u32 size)
return alloc_seamldr_params(module, module_size, sig, sig_size);
}
+enum tdp_state {
+ TDP_START,
+ TDP_DONE,
+};
+
+static struct {
+ enum tdp_state state;
+ atomic_t thread_ack;
+} tdp_data;
+
+static void set_state(enum tdp_state state)
+{
+ /* Reset ack counter. */
+ atomic_set(&tdp_data.thread_ack, num_online_cpus());
+ /* Ensure thread_ack is updated before the new state */
+ smp_wmb();
+ WRITE_ONCE(tdp_data.state, state);
+}
+
+/* Last one to ack a state moves to the next state. */
+static void ack_state(void)
+{
+ if (atomic_dec_and_test(&tdp_data.thread_ack))
+ set_state(tdp_data.state + 1);
+}
+
+/*
+ * See multi_cpu_stop() from where this multi-cpu state-machine was
+ * adopted, and the rationale for touch_nmi_watchdog()
+ */
+static int do_seamldr_install_module(void *params)
+{
+ enum tdp_state newstate, curstate = TDP_START;
+ int ret = 0;
+
+ do {
+ /* Chill out and ensure we re-read tdp_data. */
+ cpu_relax();
+ newstate = READ_ONCE(tdp_data.state);
+
+ if (newstate != curstate) {
+ curstate = newstate;
+ switch (curstate) {
+ default:
+ break;
+ }
+ ack_state();
+ } else {
+ touch_nmi_watchdog();
+ }
+ rcu_momentary_eqs();
+ } while (curstate != TDP_DONE);
+
+ return ret;
+}
+
/*
* Temporary flag to guard TD-Preserving updates. This will be removed once
* all necessary components for its support are integrated.
@@ -256,9 +314,8 @@ static int seamldr_install_module(const u8 *data, u32 size)
if (IS_ERR(params))
return PTR_ERR(params);
- /* TODO: Install and initialize the new TDX module */
-
- return 0;
+ set_state(TDP_START + 1);
+ return stop_machine(do_seamldr_install_module, params, cpu_online_mask);
}
static enum fw_upload_err tdx_fw_prepare(struct fw_upload *fwl,
--
2.47.1
Powered by blists - more mailing lists