Skip to content

Have everything in in sprot use the default retries - #2657

Merged
labbott merged 4 commits into
masterfrom
increase_sprot_retries
Aug 27, 2026
Merged

Have everything in in sprot use the default retries#2657
labbott merged 4 commits into
masterfrom
increase_sprot_retries

Conversation

@labbott

@labbott labbott commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

We have a constant DEFAULT_ATTEMPTS for sprot retries that is used inconsistently. A handful of functions have no retries. On at least one sidecar system we've seen sprot need at least one retry on a cold boot. If the first sprot command happens to be one with no retry this will result in a timeout. This is annoying so just make everything that was previously trying one time use DEFAULT_ATTEMPTS.

We have a constant `DEFAULT_ATTEMPTS` for sprot retries that is
used inconsistently. A handful of functions have no retries. On
at least one sidecar system we've seen sprot need at least one
retry on a cold boot. If the first sprot command happens to be
one with no retry this will result in a timeout. This is annoying
so just make everything that was previously trying one time
use `DEFAULT_ATTEMPTS`.
@lzrd

lzrd commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Some of the operations are not idempotent, the reset message being the most obvious. However, in the case of reset, it winds up being ok in practice because RoT reset takes more than 2 seconds. We expect a timeout on success for reset.

"record" is also not idempotent. If the record message included the slot number it expected to fill then it would be idempotent because the RoT could reject the message if appending the attestation log would fill a different slot.

Some of the software update related operations are also not idempotent though they all fail safe if received more than once or at inappropriate times.

We should improve the logic related to the is_recoverable() assessment. InvalicCrc, Deserialization, and Timeout errors might mean that the RoT actually performed the requested action but the reply to the SP did not succeed.

At a minimum, the cases where retry count is not the default should be using a named constant that indicates why the non-default value is being used. There might be an argument for reducing the default to be 2 instead of 3, the first try in case the RoT was not listening, and the 2nd because the RoT has no excuses at that point. But, analysis more careful that that should be done.

@lzrd lzrd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some operations are not idempotent and the logic about what is ok to retry should be improved. At a minimum, non-idempotent operations should be better identified.

@labbott

labbott commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator Author

I do see the point about idempotentcy but I also don't think we can really argue that's a property we have now. The original commit where this was introduced 195c4496c36c606258eab400b86a980d3651c36d added it for the image update functions and they currently all have retries.

We should improve the logic related to the is_recoverable() assessment. InvalicCrc, Deserialization, and Timeout errors might mean that the RoT actually performed the requested action but the reply to the SP did not succeed.

I see the argument for InvalidCrc and Deserialization but I think Timeout needs to stay because otherwise I'm not sure of the value of retrying since Timeout was the exact error I was seeing in my case because the RoT did not seem to be 'awake' the first time.

This change adjusts the following functions:

These should actually be idempotent and safe
caboose_size
cert_chain_len
cert_len
log_len
attest (lightly questionable if something would change the log but given the design of hubris this shouldn't be a concnern)
attest_len
component_caboose_size
tq_sign_len
tq_cert_chain_len
tq_cert_len
*tq_sign
enable_sp_slot_watchdog (this actually has a comment in the LPC55 swd code that it's idempotentish and the impact of running again is fine)
disable_sp_slot_watchdog (this is also fine to run multiple times)

May not actually be idempotent
reset
dump
record (we are not actually using this function at the moment)

I can drop the ones that are not technically idempotent from this commit since those seem pretty low risk but I'd like to make a case to keep reset since as you noted it's probably fine and giving an error on reset is a pretty terrible edge case vs just a retry.

@lzrd

lzrd commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

I ran a Claude session to verify what is idempotent or safely repeatable.

  • "reset" is safe in practice but "Timeout" is success. We would always retry the maximum number of times without special handling. A "reset_prep" + "reset_trigger" scheme similar to SP reset from CPA would be an improvement. For just fiddling with retry count; maybe needs 2 retries for RoT being out of sync the first time.
  • "record" has yet to be used, so we could change the message to include the presumed log slot to be filled which would make it safe to repeat. I'm not current on the plan for Gimlet measuring HBF and Cosmo may not need the RoT's attestation log for that feature.
  • "dump" affects SP operation (RoT SWD to SP). I think keeping at 1 retry is good but multiple will fail safe. task/dumper/src/main.rs:159 mentions a 15% failure rate. If that is still true, the value of having the dump data may override any downsides.

We could better track "RoT known to be in sync" state and if in question do a cs_pulse to force sync. I'd rather that than do it unilaterally. Right now, any message with only 1 attempt budgeted may spend that on syncing the RoT's sprot server's state machine.

I think that we should look at "enable_sp_slot_watchdog" having a parameter that says which slot is the recovery slot rather than ask for a toggle operation. But that would be a different PR.

DELIVERY-SEMANTICS.md

@lzrd lzrd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we're here, I think it's worth changing "retries" to "attempts" and fixing the math/comparison in the attempt loop. Otherwise it look good.

Comment thread drv/stm32h7-sprot-server/src/main.rs Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This name is misleading. See line 478 where attempts_left is the number of "retries".
Also, retries is a u16 and can be decremented below zero at the end of the loop. It is tested against zero.
So at least one attempt will always be made and the math and comparison at the end of the loop should be improved.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the name to be "attempts".

I agree there's a potential issue if we pass in zero attempts. The failure mode to trigger it would have to be passing 0 from a function that calls do_send_recv_retries and have that first call fail. Passing in 0 for attempts is arguably a driver bug but I think this code could do with some refactoring to better make it obvious what's going on. I think that's also outside the immediate scope of this change.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we also already pass overflow-checks=y to all code so the failure mode should also be a panic (ugly but arguably safe and maybe equivalent to what we want anyway)

tx_size,
TIMEOUT_QUICK,
// This is not idempotent so only retry once
1,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should rename this parameter to indicate it is the number of attempts, not the number of retries.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines 806 to +817
/// Reset the RoT
fn reset(
&mut self,
_msg: &userlib::RecvMessage,
) -> Result<(), idol_runtime::RequestError<SprotError>> {
let body = ReqBody::Update(UpdateReq::Reset);
let tx_size = Request::pack(&body, self.tx_buf);
let rsp = self.do_send_recv_retries(tx_size, TIMEOUT_QUICK, 1)?;
let rsp = self.do_send_recv_retries(
tx_size,
TIMEOUT_QUICK,
DEFAULT_ATTEMPTS,
)?;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think keeping this consistent with the other code to be DEFAULT_ATTEMPTS is cleaner than trying to optimize it to 2

@labbott
labbott enabled auto-merge (rebase) August 27, 2026 13:03
@labbott
labbott merged commit 90febb7 into master Aug 27, 2026
190 checks passed
@labbott
labbott deleted the increase_sprot_retries branch August 27, 2026 13:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants