Skip to content

SBP-2: re-login/reconnect after rediscovery, not on a 100 ms timer - #216

Merged
mrmidi merged 1 commit into
mrmidi:mainfrom
mhellevang:fix/sbp2-reconnect-after-rediscovery
Oct 23, 2026
Merged

mrmidi merged 1 commit into
mrmidi:mainfrom
mhellevang:fix/sbp2-reconnect-after-rediscovery

Conversation

@mhellevang

@mhellevang mhellevang commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Split of #213 (merge order):

  1. Bound SBP-2 max_payload by link speed, log posted-write fault address #214 Standalone fixes (max_payload, posted-write, rcvPhyPkt)
  2. Read bus-reset generation from SelfIDCount, not reserved 0x06C #215 Bus-reset generation from SelfIDCount
  3. SBP-2: re-login/reconnect after rediscovery, not on a 100 ms timer #216 Reconnect after rediscovery ← this PR
  4. SBP-2: carry command data by OHCI physical DMA #217 Physical DMA, needs 2+3
  5. SBP-2: bus reset only when a logout with a command outstanding fails #218 Recovery/teardown policy, needs 4
  6. SBP-2: DMA HBA task data in place via fBufferIOVMAddr #219 Zero-copy, needs 5

Split out of #213 per review. HandleBusReset armed a 100 ms timer that called Reconnect() (or Login() if the login was in flight). Reconnect() takes the node ID from targetInfo_, which is only refreshed by RefreshTargets after rediscovery, so the timer addressed the pre-reset node ID. Both references reconnect after re-enumeration: Linux sbp2_update → sbp2_reconnect, Apple IOFireWireSBP2Login::resumeNotify. Now the session goes Suspended and RefreshTargets logs in (if the login never completed) or reconnects with the refreshed node ID.

The open question from review is whether rediscovery completes within reconnect_hold + 1 s. I'll post bus reset → Reconnect-write timing from the LS-9000 with Discovery/ROMScanner logging in this PR before marking it ready.

Host tests: LoginSessionTests updated (two tests replaced), new SessionRegistryTests.BusResetDuringLoginLogsInAgainAfterRediscovery.

@mhellevang

Copy link
Copy Markdown
Contributor Author

Timing from the LS-9000, build d50b605 (this PR + #214 + #215), three bus resets (one scanner power-cycle, two cable pulls):

Reset Bus reset Reconnect write Δ
power-cycle 11:36:28.305 11:36:28.954 649 ms
cable 1 11:42:00.571 11:42:01.220 649 ms
cable 2 11:42:22.261 11:42:22.911 650 ms

Breakdown: 100 ms discovery delay + ~550 ms ROM scan. The LS-9000 reports reconnect_hold = 0, so the window is 1 s; margin 350 ms. All three reconnects were rejected with sbpStatus=4 because the device had been off the bus for 12–14 s, and the fallback login completed 3 ms later with target 0 back each time. An accepted reconnect (reset without the device leaving) wasn't exercised; the code path is identical up to the status value.

Unrelated observation, same session: pulling the Thunderbolt adapter restarts the dext against a scanner that still holds the old login. The LS-9000 then accepts login and LUN reset but never executes an ORB until it is power-cycled while disconnected. Pre-existing (the powercycle/re-login case from #61), not touched by this PR.

@mrmidi mrmidi left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Approving as is. The change matches both references (Linux sbp2_update, Apple resumeNotify), and the 650 ms measurement settles the timing for now. Most of that time is our ROM scan, which re-reads every node's whole ROM after each reset instead of checking the bus info block against the cache, as Apple does. We'll fix that in discovery separately.

One known gap, for a follow-up PR: RefreshTargets decides between Login and Reconnect with LoginID() == 0. SBP-2 doesn't reserve login_ID 0, so a target that assigns it would get a Login after every reset while it still holds our old login. That login is rejected with "access denied" (§8.2), and the session only recovers when the target drops the old login. Could you check which login IDs the LS-9000 and the H4D report in your logs (login successful — loginID=)? If either shows 0, the follow-up below becomes urgent.

Instead of patching the sentinel, I'd like LoginSession as a std::variant state machine once #217–#219 have landed, so it covers their states too:

  1. Each state carries its own data, e.g. LoggedIn{loginID, commandBlockAgent, reconnectHold, generation, node}, and Suspended split into "login held" (reconnect) and "no login" (log in). No sentinels, no stale side fields.
  2. One transition function (state, event) → state, plus a transition table in the PR checked against Apple IOFireWireSBP2Login (suspendedNotify/resumeNotify) and Linux sbp2_login/sbp2_reconnect.
  3. Threading: state_, loginID_ and loginGeneration_ are atomics because the user client reads them from another queue (SBP2Handler.hpp:227). Keep the variant on the driver queue and publish an atomic snapshot of what observers read.
  4. LoginState stays as the reported enum, so the registry, the bridge and the user client don't change.
  5. One test per transition, including a login with login_ID 0 that reconnects after a reset.

@mrmidi
mrmidi merged commit 950bf4d into mrmidi:main Oct 23, 2026
2 checks passed
@mhellevang

Copy link
Copy Markdown
Contributor Author

Login IDs from the logs:

So the H4D hits the sentinel: with this PR it gets a Login instead of a Reconnect after every bus reset. I haven't seen the resulting "access denied" in a log yet, since that capture doesn't show the login status after the escalation reset. I'll take the std::variant state machine as the follow-up once #217–#219 have landed, with a login_ID 0 reconnect test.

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