[PATCH v9 13/23] x86/virt/seamldr: Abort updates after a failed step
From: Chao Gao
Date: Wed May 13 2026 - 11:38:23 EST
A TDX module update is a multi-step process, and any step can fail.
The current update flow continues to later steps after an error.
Continuing after a failure can leave 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 [1] is to fail the
update with -EBUSY so userspace can retry later.
Abort the update on any failure. This also makes the TD-build contention
case recoverable, because that failure occurs before any TDX module state
is changed. Apply the same rule to all errors instead of special-casing
-EBUSY.
Track per-step failures, stop the update loop once a failure is observed,
and do not advance the state machine to the next step.
Signed-off-by: Chao Gao <chao.gao@xxxxxxxxx>
Reviewed-by: Xu Yilun <yilun.xu@xxxxxxxxxxxxxxx>
Reviewed-by: Tony Lindgren <tony.lindgren@xxxxxxxxxxxxxxx>
Reviewed-by: Kai Huang <kai.huang@xxxxxxxxx>
Reviewed-by: Kiryl Shutsemau (Meta) <kas@xxxxxxxxxx>
Link: https://lore.kernel.org/linux-coco/aQFmOZCdw64z14cJ@xxxxxxxxxx/ # [1]
---
v9:
- Avoid nested if/else by deferring failure accounting to ack_state().
- Reduce indentation of the main flow.
- Convert the failed flag into a counter. This avoids a conditional
update of the flag; the counter can simply accumulate failures.
---
arch/x86/virt/vmx/tdx/seamldr.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/arch/x86/virt/vmx/tdx/seamldr.c b/arch/x86/virt/vmx/tdx/seamldr.c
index 7befe4a08f33..48fe71319fea 100644
--- a/arch/x86/virt/vmx/tdx/seamldr.c
+++ b/arch/x86/virt/vmx/tdx/seamldr.c
@@ -170,6 +170,7 @@ enum module_update_state {
static struct update_ctrl {
enum module_update_state state;
int num_ack;
+ int num_failed;
/*
* Protect update_ctrl. Raw spinlock as it will be acquired from
* interrupt-disabled contexts.
@@ -187,12 +188,13 @@ static void __set_target_state(struct update_ctrl *ctrl,
}
/* Last one to ack a state moves to the next state. */
-static void ack_state(struct update_ctrl *ctrl)
+static void ack_state(struct update_ctrl *ctrl, int result)
{
raw_spin_lock(&ctrl->lock);
+ ctrl->num_failed += !!result;
ctrl->num_ack++;
- if (ctrl->num_ack == num_online_cpus())
+ if (ctrl->num_ack == num_online_cpus() && !ctrl->num_failed)
__set_target_state(ctrl, ctrl->state + 1);
raw_spin_unlock(&ctrl->lock);
@@ -202,6 +204,7 @@ static void init_state(struct update_ctrl *ctrl)
{
raw_spin_lock_init(&ctrl->lock);
__set_target_state(ctrl, MODULE_UPDATE_START + 1);
+ ctrl->num_failed = 0;
}
/*
@@ -228,8 +231,8 @@ static int do_seamldr_install_module(void *seamldr_params)
break;
}
- ack_state(&update_ctrl);
- } while (curstate != MODULE_UPDATE_DONE);
+ ack_state(&update_ctrl, ret);
+ } while (curstate != MODULE_UPDATE_DONE && !READ_ONCE(update_ctrl.num_failed));
return ret;
}
--
2.52.0