Repository navigation
wifi: mt7932: carry out a requested roam - #320
Conversation
RyanTheTide
left a comment
There was a problem hiding this comment.
Approving. This fixes a real wedge, and our tree has the same refusal today (aurora-silicon/linux aurora-wip still returns -EBUSY from mt_connect() for any connect while connected). I read it against cfg80211 and iwd 3.12 and the state handling holds up. mt_disconnect() flushes the disconnect worker before the join starts, the disconnect report is held back only while roam_moving is set, and a move ends in one connect result, plus a disconnect when it fails. cfg80211 leaves wdev->connected set after a failed connect result, so that trailing disconnect is needed, and the two events are processed in order.
Follow ups, none blocking:
-
The from-AP reporting. This is an iwd bug, not the driver. netdev_reassociate() sets in_reassoc, and the only code that clears it is in netdev_associate_event(), which handles NL80211_CMD_ASSOCIATE. cfg80211 never sends that for a driver that reports through cfg80211_connect_done(), so on a CMD_CONNECT driver in_reassoc stays set until the connection is freed, and netdev_disconnect_event() drops every disconnect not from the AP. The proper fix is clearing in_reassoc in netdev_connect_event() on success. I'd send that to iwd and drop roam_landed once it ships. Until then, wpa_supplicant also reassociates with prev_bssid through CMD_CONNECT, so its users get these as AP deauths too, and it handles those differently from local ones.
-
A cancelled move reports WLAN_REASON_DEAUTH_LEAVING after the failed connect result. The cancel already saved its reason in disconnect_reason, so passing
reasonthere keeps what the caller asked for. -
roam_moving is read with READ_ONCE() in mt_retire_connection() outside response_lock but written plainly. WRITE_ONCE() at the writes would match roam_landed.
-
Deferred regulatory change. mt_disconnect_work() still calls mt_reg_apply_deferred_locked() during a move, which sets reg_pending, so mt_rf_allowed() fails in mt_connect() and the move becomes a disconnect. Holding the deferral while roam_moving is set and applying it from mt_connect_work() once the move has a result would keep the move.
-
KUnit. Which event goes out for a move that lands, fails or is cancelled is small enough to pull into a helper and test, the way the connect result logging is tested in our tree (aurora-silicon/linux 4db3a5a).
On the gaps listed, from our side.
BIP and WPA3 with iwd. You're right, integration/aurora advertises only CCMP. iwd's wiphy_can_connect_sae() won't use SAE without BIP-CMAC-128, and station.c refuses any BSS that sets MFPR when it can't pick a group management cipher, and refuses 6 GHz without MFP. So on this base iwd can't join WPA3-only, WPA2 with PMF required, OWE networks that require PMF, or anything on 6 GHz. That's a gap we left. The SAE commit said iwd stays WPA2-only, and we should have closed it before iwd was the default. Our tree has since added WPA2 PMF and advertises BIP-CMAC-128, with the firmware doing the IGTK install and BIP (aurora-silicon/linux#659, 6156b0c), on top of 6 GHz station support (12010e0, SAE-H2E or OWE with required MFP). Both are on aurora-silicon/linux aurora-wip, not integration/aurora yet. Honest status, that PR passed KUnit and build gates but has not been qualified on hardware with iwd. We're running your matrix on a Neo with iwd and again with NetworkManager and wpa_supplicant: WPA2 with PMF off, optional and required, WPA2/WPA3 transition, WPA3-only, OWE, 6 GHz, and a move between two PMF-required APs. Every row that allows it has to join with PMF on, and the results go in the PR that brings it here. Once BIP is advertised, SAE and OWE moves go through this same path, so this PR is the right base for that.
2.4 GHz -110. Our tree has no fix for this. We've seen the same signature at boot, a -110 after the request reached the AP with the firmware still healthy and the next attempt connecting, which is why we log those at info level in 4db3a5a. Your 5.5 minute pattern looks more like the AP ageing the station out than the firmware, but I haven't proven that either way.
Roaming config. omacom/omarchy-mac-pkgs#84 sets the iwd roam thresholds to -100 to leave roaming to the firmware. This driver never reports a firmware roam (no cfg80211_roamed() anywhere), and your runs show the firmware holding a far AP down to -97 dBm. With this PR in I'd keep iwd roaming on for the MT7932. omacom/omarchy-mac-pkgs#85's autoconnect setting still makes sense as the fallback for joins the firmware rejects.
maralcbr
left a comment
There was a problem hiding this comment.
Looks good. I checked it against cfg80211's sme.c. A failed connect result releases the old AP's BSS but leaves wdev->connected set, so the trailing disconnect is needed. Both events are handled in the same event loop, so nothing can slip in between them. A landed move correctly takes over the old connection. I agree with Ryan's five follow-ups. A few more, none blocking:
-
Three disconnect reports skip
mt_report_disconnected(). The refused-move path atunlock:(link.c:902-907), the trailing disconnect after a failed move (link.c:750-752) and the existing cancel path (link.c:746) always report the disconnect as local. It's harmless for iwd, since a failed reassociation already ends the connection there, but routing these through the helper (and passingreason, per Ryan's point 2) would keep the from-AP decision in one place. -
A narrow race can send two disconnects. It sits between
mt_connect()droppingresponse_lock(link.c:842) andmt_disconnect()taking it again (link.c:950). An RF failure in that window makesmt_rf_fail_locked()report a disconnect right away, becauseroam_movingonly holds back the report inmt_disconnect_work().mt_connect()then seeslink_failed, returns -EIO, and theunlock:path reports a second one.__cfg80211_disconnected()has no guard, so userspace gets a duplicateNL80211_CMD_DISCONNECTand the keys are deleted twice. It's unlikely; checkingroam_movinginmt_rf_fail_locked(), or skipping theunlock:report when one has already gone out, would close it. -
A comment no longer matches the code. The connect worker's comment (link.c:680-684) says cfg80211 calls are made only after
response_lockis dropped. The newunlock:path callscfg80211_disconnected()andnetif_carrier_off()under it.mt_disconnect_work()andmt_rf_fail_locked()already do the same, so this adds no new risk, but the comment should be reworded. -
A move during a scan isn't in "Known remaining".
mt_disconnect_work()→mt_scan_quiesce()can wait up to about 7 s while carrier stays up. If the scan is still running after that, the admission check refuses onscan_requestand the move becomes a full disconnect. Worth one line in the PR. -
The harness doesn't exercise moves. It stubs
netif_carrier_off()andmt_report_disconnected()as no-ops and never setsroam, so the new branches compile but don't run. This backs up Ryan's point 5.
Process nits:
- b2f328c repairs the harness build that 079aa63 broke. Squashing them would let every commit pass CI on its own.
- Both commits carry
Co-Authored-By:alongsideAssisted-by:, which already covers it. Kernel convention isAssisted-by:alone.
On roam_landed: marking local disconnects as from the AP is visible to every nl80211 listener, and wpa_supplicant handles those differently from local ones. I'd note the iwd in_reassoc bug next to roam_landed, so it gets removed once the iwd fix ships.
RyanTheTide
left a comment
There was a problem hiding this comment.
Still approving on b2f328c. The new commit only adds stubs to the connect result harness so it builds with the move state, and the driver code is the same as what I reviewed. The follow ups from my earlier review still apply, including a harness case that actually exercises a move.
iwd roams a fullmac device by sending CMD_CONNECT with the previous BSSID. mt_connect() refused that with -EBUSY, and iwd takes a failed reassociation as the end of the connection and sends no disconnect, so cfg80211 and the firmware stayed connected. Every later connect then failed with -EALREADY, and the device stayed offline until the firmware lost the AP or iwd exited. Do what the request asks. Leave the current AP and join the one requested, keeping the carrier up so NetworkManager doesn't restart DHCP. Report the end of the move through the connect result, which is what iwd's reassociation waits for: cfg80211_roamed() would leave its connect callback installed. A move that can't start, fails or is cancelled still ends the connection, so all three agree. iwd keeps its reassociation mark after a move, and drops disconnects not reported as from the AP, so report them that way on a connection a move made. Also refuse a reassociation that arrives after the link was lost, and log moves and connects refused at admission. Teach the connect result harness about the move state; it doesn't exercise moves. Assisted-by: Claude:claude-opus-5-5 Signed-off-by: Scott Jones <scottajones@gmail.com>
b2f328c to
747e84f
Compare
|
Squashed and force-pushed as 747e84f, one commit on 1630ee1. From the reviews: the failed or cancelled move now passes the saved Not in this push: the double-disconnect race, the deferred regulatory change and KUnit for moves. Retested on a J700 with this tree. The results and a line on a move during a scan are in the description. |
maralcbr
left a comment
There was a problem hiding this comment.
Approving 747e84f. The review fixes check out: all three reports go through mt_report_disconnected() with roam_landed cleared first, a failed or cancelled move passes the saved reason, the roam_moving writes use WRITE_ONCE, and the lock comment now matches the code.
One nit for a follow-up: the early refusals of a move (RF not allowed at link.c:811-813, and the -EINVAL validation failure) go through mt_disconnect() without clearing roam_landed. On a connection a move made, those are still reported as from the AP, while a refusal at admission is reported as local. It's harmless for iwd. Clearing roam_landed there too would make them match.
The double-disconnect race, the deferred regulatory change and KUnit coverage for moves remain follow-ups.
4fdd0fc
Summary
Roaming on the MacBook Neo can leave Wi-Fi offline. iwd asks to reassociate, the driver refuses, and iwd treats that as disconnected while cfg80211 and the firmware stay connected, so every later connect fails until the firmware loses the AP or iwd restarts.
The driver now does the move: it leaves the current AP and joins the requested one. Carrier stays up, so NetworkManager has no reason to restart DHCP. A move that fails still ends the connection cleanly.
Evidence
J700, iwd 3.12, WPA2-PSK, one SSID across three APs. omacom/omarchy-mac-pkgs#85's NetworkManager setting is noted per run.
connect-failedin a row and no connection for 25m46s, until iwd restarted at 14:53:25 and connected at 14:53:26. Shorter walks were stuck 2.5 min and 1 min.-110. It took 11.9 s to the next association and about 17 s without ping replies. NetworkManager recovered with no no-secrets, the address held, and the next connect reached the driver, so cfg80211 had seen the disconnect. A retry 18 s later landed.iw reg setto CA and back to US, each on a connection a move had made. iwd loggedreason: 3, from_ap: true, went to disconnected, and was connected again in about 1.4 s. No stuck state.Known remaining
-110, and all 5 within 43 s of a previous try landed. Four of the failures were moves and one a plain connect. On 5 GHz, 2 of 111 failed. These counts are logged joins only: the log line isn't in the journal for joins at boot. On this commit, the first move to one 2.4 GHz BSS timed out and a retry 18 s later landed, but iwd's boot join to the other landed in 0.21 s, over 2 hours after its last join. We don't know yet whether the AP or the firmware is responsible.Merge Danger
Door: two-way. One commit, in a driver only J700 binds.
Blast Radius: Neo Wi-Fi.
Roaming now tears down and rejoins for real, so a target AP that doesn't answer costs a longer outage than refusing did.
🤖 Generated with Claude Code