[PATCH v8 17/21] x86/virt/seamldr: Abort updates on failure

Chao Gao
2026-04-27T15:30:09Z

TDX module update is a multi-step process, and each step can fail.

The current update flow continues to later steps after an error.
Continuing after failure leaves the TDX module in an unrecoverable
state.

One failure case must remain recoverable: update contention with an ongoing
TD build. The agreed kernel behavior for this case is to fail the update
with -EBUSY so userspace can retry later.

Abort the update on any failure. For the contention case, this provides
a recoverable failure mode because the failure occurs before any TDX
module state is changed. Use the same rule for all errors to avoid
special-casing -EBUSY.

Introduce a shared "failed" flag. When a CPU fails, set the flag and force
all CPUs to exit the update loop. A failing CPU does not acknowledge the
current step, so other CPUs remain at that step until they observe the
"failed" flag and exit.

Use READ_ONCE()/WRITE_ONCE() for the flag because it is used for lockless
communication between stop_machine workers. Also use WRITE_ONCE() for the
initial clear to keep accesses to the flag uniform and explicit.

Signed-off-by: Chao Gao <chao.gao@intel.com>
Reviewed-by: Xu Yilun <yilun.xu@linux.intel.com>
Reviewed-by: Tony Lindgren <tony.lindgren@linux.intel.com>
Reviewed-by: Kai Huang <kai.huang@intel.com>
Reviewed-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
---
v8:
- Explain why aborting updates is necessary. [Rick]
- always use READ_ONCE()/WRITE_ONCE() for the "failed" flag.
---
arch/x86/virt/vmx/tdx/seamldr.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/arch/x86/virt/vmx/tdx/seamldr.c b/arch/x86/virt/vmx/tdx/seamldr.c
index c81b26c4bac1..9b8f571eb03f 100644
--- a/arch/x86/virt/vmx/tdx/seamldr.c
+++ b/arch/x86/virt/vmx/tdx/seamldr.c
@@ -220,6 +220,7 @@ enum module_update_state {
static struct {
enum module_update_state state;
int thread_ack;
+ bool failed;
/*
* Protect update_data. Raw spinlock as it will be acquired from
* interrupt-disabled contexts.
@@ -284,12 +285,15 @@ static int do_seamldr_install_module(void *seamldr_params)
break;
}

- ack_state();
+ if (ret)
+ WRITE_ONCE(update_data.failed, true);
+ else
+ ack_state();
} else {
touch_nmi_watchdog();
rcu_momentary_eqs();
}
- } while (curstate != MODULE_UPDATE_DONE);
+ } while (curstate != MODULE_UPDATE_DONE && !READ_ONCE(update_data.failed));

return ret;
}
@@ -315,6 +319,7 @@ int seamldr_install_module(const u8 *data, u32 size)

/* Ensure a stable set of online CPUs for the update process. */
guard(cpus_read_lock)();
+ WRITE_ONCE(update_data.failed, false);
set_target_state(MODULE_UPDATE_START + 1);
ret = stop_machine_cpuslocked(do_seamldr_install_module, params, cpu_online_mask);
if (ret)
--
2.47.1