Repository navigation
kestrel: ACK responder on its own macid / ADDR_CAM entry - #488
Conversation
Follow-up to OpenIPC#487 (review point 5). The responder registered its station at macid 0 / ADDR_CAM entry 0, the context InitWrite programs for injection and that every injected frame's WD points at; arming rewrote it and clearing re-ran the bring-up registration over it. The station now lives at macid 1 / ADDR_CAM entry 1 (BSSID CAM 1), and the injection context is never touched: entry 0 reads back byte-identical before the arm, while armed and after the clear. Clearing changed with it. The 8852B firmware leaves a disconnected station's entry VALID: join_info(disconnect) + a NO_LINK addr_cam, a role REMOVE, and role CREATE + NO_LINK all left entry 1 valid with the responder's address (the old layout hid this, because the bring-up re-registration rewrote entry 0 wholesale). Clear now disconnects the station in the firmware and clears VALID from the host, the way the vendor's no-firmware mac_upd_addr_cam path writes the CAM, repeating the clear until the entry has read back empty for 25 ms so a late firmware rewrite cannot re-validate it. Measured, TX20U Nano (8852B) responder, TX50UH injector counting ACKs, ch161, MCS1, one attempt per frame, 3 runs per row (AdapterCaps.h): 14400/14415 armed at Init, 14369/14427 armed at InitWrite, 14363/14421 retargeted, 0/6485 at the old address, 0/6449 never armed, 0/6519 after a live disarm; arm -> retarget -> clear -> re-arm in one session, 3 runs, every phase as expected. Injection delivery is unchanged armed vs unarmed (>= 2900/3000 at a witness, on this layout and the old one), so this is about the injection context, not a delivery fix. Adversarial: one armed cell in about 55 across development read entry 1 back committed and answered no ACKs; not reproduced in the following cells (base layout: 0 such cells in about 40). The responder-transmitting row read 80.6% once, with the injector's own USB sends stalling (up to 1 s, 7 failures); 98.7% and 99.2% in the other two runs. The 8852C path is unchanged: its arm still refuses.
|
/review |
Code Review by Qodo
1.
|
Review follow-ups on the own-macid responder: - Peer stations take macid 1 / ADDR_CAM entry 1 by convention (tests/ul_trigger_ap.cpp, kestrelprobe trig), the slot the responder had just moved to, so a peer registration and the responder could replace each other and a clear could invalidate the peer's entry. The responder now uses macid 127 / ADDR_CAM entry 127, the last entry of the 0x7f search range, and RegisterPeerSta / RegisterBeamformee refuse that slot (logged). Entry 127 commits in the same ~2.2 ms. - The clear's 150 ms deadline could end the loop on an empty read that came right after re-clearing a late firmware rewrite, and an empty read was taken as success. Success now requires the entry to have read back empty for the full 25 ms; on a timeout the clear is reported as not taken and the last address the entry held stays recorded for a retry. Re-measured on this version, same bench and rows as before (TX20U Nano responder, TX50UH injector, ch161, MCS1, one attempt per frame, 3 runs): 14414/14422 armed at Init, 14376/14417 armed at InitWrite, 14419/14432 retargeted, 0/6597 at the old address, 0/6575 never armed, 0/6561 after a live disarm, responder transmitting 14308/14437; arm -> retarget -> clear -> re-arm 3/3 runs as expected; injection delivery >= 2900/3000 armed and unarmed. One unarmed delivery cell heard none of the 3179 injected frames at the witness (txdemo sent them all, code path untouched by this change); three re-runs read >= 2900/3000. Full MSVC build and ctest pass (78, 4 skipped as on master).
josephnef
left a comment
There was a problem hiding this comment.
Reviewed at 60d79c7. Approving: the slot move and the host-side clear are what the measurements say they are, and the 8852C path is unchanged on the bench.
Bench (this host, no 8852B plugged — two TX50UH / 8832CU and an 8812CU witness only):
- Refusal path on the 8852C (bus 9, ch6), PR build with
DEVOURER_ACK_RESPONDER:join_info+upd_addr_cam_stago out at macid 127 / a_idx 127 / b_idx 1, the read-back refuses ("accepted but did not commit"), the rollback sendsjoin_info(disconnect)+ NO_LINK addr_cam at the same slot and reportsdisarmed (ADDR_CAM entry 127 no longer holds ...), then the configured-arm throw as on master. - Plain RX A/B, master 036a860 vs PR, 2 × 12 s each on the 8852C: 300/200 vs 300/200
rx.pkt, the same twocoex: LTE interface not readylines on both. - Injection A/B, 8852C txdemo 3000 frames → 8812CU witness, 2 runs each: master 2907 / 2947, PR 2965 / 2945 of 3000. Inside probe noise, as expected for a path the diff does not touch on the C die.
Nothing here exercises the committed-entry path or the host clear against a firmware that rewrites the entry; those are the author's 8852B rows.
Two low notes on the clear loop, neither blocking:
disarm_ack_lockeddeclares success after the entry has read empty for 25 ms since the last clear. The arm's own commit latency was measured 2–20 ms, so the margin over the observed tail is ~5 ms. If a late NO_LINK rewrite lands after the window closes, the clear is reported taken,_ack_respondergoesnullopt, and nothing retries — and whether a VALID NO_LINK entry at 127 still elicits ACKs is not measured. A wider window (50 ms costs nothing on a 150 ms bound) would put the margin past the tail rather than on it.- The 150 ms bound is checked after the stability check, so an entry that first reads empty after ~125 ms is reported "did not stay clear of" even though it is clean. The failure direction is the safe one (it stays on record and retries), so this is only a spurious warning on a slow host.
Doc nit: "it and RegisterBeamformee now refuse 127" in docs/kestrel-ack-responder.md reads as changelog; the docs here are current-state only, so just "refuse 127".
…held Maintainer review notes on 60d79c7: - The clear counted as taken after 25 ms of empty read-backs, while the arm's firmware commit was measured at 2-20 ms: ~5 ms of margin over the tail. A late NO_LINK rewrite after the window would leave the entry valid with nothing on record to retry. The window is now 50 ms. - The 150 ms bound was checked after the stability check, so an entry that first read empty late could be reported "did not stay clear" while clean. The bound now ends the wait only while the entry is still held; an entry that reads empty always gets its full window (worst case about 200 ms), and a timeout always means the entry still holds the address. - docs: "refuse 127", current state rather than changelog wording. TX20U Nano responder, TX50UH injector, ch161, MCS1: 3 live disarms 0 ACKs each (0/2215, 0/2217, 0/2223) with "disarmed (ADDR_CAM entry 127 ...)"; 2 arm -> retarget -> clear -> re-arm cycles, every phase as expected; armed 4809/4811. Full MSVC build and ctest pass (78, 4 skipped).
Head branch was pushed to by a user without write access
Thanks for the review and for running the 8852C side. All three notes are addressed in 9d9d9b8: Re-checked on the 8852B: 3 live disarms at 0 ACKs each (0/2215, 0/2217, 0/2223) with the entry-127 read-back, 2 arm -> retarget -> clear -> re-arm cycles with every phase as expected, armed 4809/4811. Full MSVC build and ctest pass. You're right that whether a VALID NO_LINK entry at 127 would still elicit ACKs isn't measured; the wider window makes that case much less likely to be reached, but it doesn't measure it. |
josephnef
left a comment
There was a problem hiding this comment.
Re-approving at 9d9d9b8: the 50 ms stable window, the deadline applying only while the entry is still held, and the doc wording cover the three notes. Re-ran the 8852C refusal cell on this head: arm at macid 127 / entry 127 / BSSID CAM 1 refused on the read-back, rollback reported entry 127 clean, same as 60d79c7.
Follow-up to #487, review point 5 ("Arming changes injected TX context").
The problem
The 8852B ACK responder registered its station at macid 0 / ADDR_CAM entry 0, which is the context
InitWriteprograms for injection and that every injected frame's WD points at. Arming rewrote that context and clearing re-ran the bring-up registration over it.In practice this had no measurable cost: a witness counted the same share of injected frames with the responder armed and unarmed. So this PR is about keeping the injection context intact, not about fixing delivery.
The change
tests/ul_trigger_ap.cppandkestrelprobe trigregister their peer at macid 1 / entry 1).RegisterPeerStaandRegisterBeamformeerefuse slot 127 (logged), so a peer and the responder cannot replace each other, and clearing the responder cannot invalidate a peer's entry.join_info(disconnect)plus a NO_LINK addr_cam, a role REMOVE, and role CREATE plus NO_LINK all left the entry valid with the responder's address. The old layout never showed this, because the bring-up re-registration rewrote entry 0 wholesale. Clear now disconnects the station in the firmware and clears VALID from the host, the way the vendor's no-firmwaremac_upd_addr_campath writes the CAM. It repeats the clear until the entry has read back empty for 25 ms, and only that counts as disarmed: on a timeout the clear is reported as not taken and the last address the entry held stays recorded for a retry.HalKestrel:register_infra_statakes the macid and CAM indices; newunregister_infra_sta,write_mac_mem32andclear_addr_cam_entry.docs/kestrel-ack-responder.mdand theAdapterCaps.hevidence are updated.The 8852C path is unchanged: its firmware still does not commit the entry, and the arm still refuses.
Measured
TX20U Nano (RTL8852BU) as responder, TX50UH (RTL8832CU) injecting and counting the ACKs it receives, ch161, MCS1, one attempt per frame, 10 s per cell, 3 runs per row, on the final version (slot 127):
Arm A, retarget to B, clear, re-arm A in one session, 3 runs: A 99.9-100% armed, then A 0% and B 99.8-99.9% after the retarget, both 0% after the clear, and A 99.9-100% (B 0%) after the re-arm.
Injection delivery, TX20U txdemo injecting 3000 frames to a TX50UH witness, armed and unarmed: at least 2900/3000 in every cell on this branch and on master.
Full MSVC build and
ctestpass (78 tests, 4 skipped as on master).What this does not show: