Files
ps5-linux-mwifiex/ps5-iw620-rtnl-bounded-wait.patch
masterzoragandNikolai Shcheglov c4431c6686 Update for 7.2.y, and fixes (#8)
* Stop a firmware lockup from deadlocking the whole system

A wedged IW620 firmware used to freeze the entire machine, not just wifi.
woal_cfg80211_dump_station_info() runs while cfg80211 holds the wiphy mutex
and nl80211_pre_doit() holds RTNL. MOAL_IOCTL_WAIT has no timeout, so once
the command queue wedged NetworkManager blocked there forever, RTNL stayed
held, and every network-touching syscall on the box blocked in D state --
no desktop icons, nothing responsive, hard power-off the only way out:

  NetworkManager (S)  woal_wait_ioctl_complete [moal]   <- holds WIPHY mutex
  wpa_supplicant (D)  nl80211_pre_doit -> mutex_lock    <- holds RTNL
  geoclue        (D)  rtnl_dumpit -> rtnl_lock          <- waits RTNL

Two independent causes, so two patches.

1. The command queue wedged. On a timeout the driver only released curr_cmd
   for a small whitelist of commands; anything else (MAC_MULTICAST_ADR, in
   the observed failure) left curr_cmd set and cmd_sent MTRUE, so every
   later ioctl queued behind a command that would never be sent. Releasing
   the command and completing its ioctl now happens on every timeout; the
   whitelist only chooses between carrying on and declaring a hang.

   The retry cap gets its own soft_cmd_timeout counter, cleared whenever a
   response arrives so it really counts consecutive timeouts. It cannot
   reuse num_cmd_timeout: wlan_process_cmdresp() discards every command
   response while that is non-zero, so it must keep being cleared after a
   recovery.

2. The wait was unbounded. The four firmware round-trips in
   woal_cfg80211_dump_station_info() (signal, stats, bss_info, dtim_period)
   now use MOAL_IOCTL_WAIT_TIMEOUT, capping each at 20s so a dead firmware
   can no longer strand RTNL. That function backs both .get_station and
   .dump_station.

Also in install.sh:

- Reset the driver tree to a pristine $REF before patching. Without it a
  tree left from a previous run still carries the old version of a patch,
  which then matches neither the apply nor the reverse-apply check, and the
  run dies with "does not apply cleanly".
- Switch the default tuning from ps_mode=0 auto_ds=0 amsdu_disable=0 to
  2 2 1, verified on a cold boot on 7.1.3-hdmifix with this patch stack.
  This also matches the linux-ps5 package's moal.conf, which previously
  disagreed -- and since ps5-iw620.conf sorts after moal.conf, the value
  written here silently wins.
- Add FW_RESET_OPTIONS for opting into firmware auto-reset. Off by default:
  woal_request_fw_reload() rejects every mode with -EINVAL unless the low
  byte of indrstcfg is 1 or 2 (module default 0xffffffff), so auto_fw_reload
  alone is a no-op, and neither is tested on PS5 hardware.

Verified: patches apply cleanly to a pristine lf-6.18.2_1.0.0 checkout in
install.sh order and build without warnings; a cold boot onto the result
downloads firmware, associates, and runs clean. The lockup is sporadic, so
the recovery paths themselves are not yet exercised in the field.

Assisted-by: Claude <noreply@anthropic.com>

* Explain in the generated modprobe config that it overrides moal.conf

install.sh writes /etc/modprobe.d/ps5-iw620.conf, and the linux-ps5 package
ships /etc/modprobe.d/moal.conf. Both set "options moal", and modprobe
concatenates those lines across every *.conf rather than letting one file
replace another, so the module receives the whole list twice and a repeated
scalar resolves to the last one read. ps5-iw620.conf sorts after moal.conf,
so it silently wins and edits to moal.conf appear to do nothing.

Nothing functional changes; the generated options line is byte-identical.
The header just makes the precedence discoverable from the file itself,
which a hand-added comment could not do because the next run overwrites it.

Assisted-by: Claude <noreply@anthropic.com>

* changes for 7.2.y: add separate patch file

---------

Co-authored-by: Nikolai Shcheglov <ndnd@mail.ru>
2026-09-28 16:30:49 +02:00

53 lines
2.4 KiB
Diff

diff --git a/mlinux/moal_sta_cfg80211.c b/mlinux/moal_sta_cfg80211.c
index 05e820f..0d5942c 100644
--- a/mlinux/moal_sta_cfg80211.c
+++ b/mlinux/moal_sta_cfg80211.c
@@ -4675,10 +4675,17 @@ static mlan_status woal_cfg80211_dump_station_info(moal_private *priv,
sinfo->filled |= MBIT64(NL80211_STA_INFO_FCS_ERROR_COUNT);
#endif
+ /* Every firmware round-trip below runs while cfg80211 holds the wiphy
+ * mutex, and nl80211_pre_doit() takes RTNL before that. MOAL_IOCTL_WAIT
+ * has no timeout, so a firmware that stops answering strands RTNL and
+ * every network-touching syscall on the machine blocks in D state.
+ * MOAL_IOCTL_WAIT_TIMEOUT bounds each one at MOAL_IOCTL_TIMEOUT (20s).
+ */
+
/* Get signal information from the firmware */
memset(&signal, 0, sizeof(mlan_ds_get_signal));
if (MLAN_STATUS_SUCCESS !=
- woal_get_signal_info(priv, MOAL_IOCTL_WAIT, &signal)) {
+ woal_get_signal_info(priv, MOAL_IOCTL_WAIT_TIMEOUT, &signal)) {
PRINTM(MERROR, "Error getting signal information\n");
ret = MLAN_STATUS_FAILURE;
goto done;
@@ -4687,7 +4694,7 @@ static mlan_status woal_cfg80211_dump_station_info(moal_private *priv,
/* Get stats information from the firmware */
memset(&stats, 0, sizeof(mlan_ds_get_stats));
if (MLAN_STATUS_SUCCESS !=
- woal_get_stats_info(priv, MOAL_IOCTL_WAIT, &stats)) {
+ woal_get_stats_info(priv, MOAL_IOCTL_WAIT_TIMEOUT, &stats)) {
PRINTM(MERROR, "Error getting stats information\n");
ret = MLAN_STATUS_FAILURE;
goto done;
@@ -4717,7 +4724,7 @@ static mlan_status woal_cfg80211_dump_station_info(moal_private *priv,
sinfo->filled |= STATION_INFO_BSS_PARAM;
#endif
sinfo->bss_param.flags = 0;
- ret = woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info);
+ ret = woal_get_bss_info(priv, MOAL_IOCTL_WAIT_TIMEOUT, &bss_info);
if (ret)
goto done;
if (bss_info.capability_info & WLAN_CAPABILITY_SHORT_PREAMBLE)
@@ -4726,8 +4733,8 @@ static mlan_status woal_cfg80211_dump_station_info(moal_private *priv,
sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
sinfo->bss_param.beacon_interval = bss_info.beacon_interval;
/* Get DTIM period */
- ret = woal_set_get_dtim_period(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT,
- &dtim_period);
+ ret = woal_set_get_dtim_period(priv, MLAN_ACT_GET,
+ MOAL_IOCTL_WAIT_TIMEOUT, &dtim_period);
if (ret) {
PRINTM(MERROR, "Get DTIM period failed\n");
goto done;