FelipeMdeO opened a new pull request, #20178:
URL: https://github.com/apache/nuttx/pull/20178

   ## Summary
   
   `esp_wifi_event_handler()` held `esp_wifi_lock()` across the entire
   event switch, including the `esp_wlan_*_hook()` calls
   (`WIFI_EVENT_STA_CONNECTED`/`_DISCONNECTED`, `WIFI_EVENT_AP_START`/`_STOP`).
   Those hooks reach `netdev_lower_carrier_on()`/`_off()`, which take the
   per-device `netdev_lock()`.
   
   Every other path into `esp_wifi_lock()` acquires the two locks in the
   opposite order: e.g. the netdev `ifdown` path holds `netdev_lock()`
   *around* its own call into `esp_wifi_api_stop()`, which calls
   `esp_wifi_lock()`. So:
   
   * ifdown path: `netdev_lock()` → `esp_wifi_lock()`
   * event handler (before this fix): `esp_wifi_lock()` → `netdev_lock()`
   
   An application that disconnects Wi-Fi (`wpa_driver_wext_disconnect()`
   immediately followed by `wapi_set_ifdown()`, e.g.
   `netlib_ifdown()`/`wapi_set_ifdown()` right after a disconnect ioctl)
   races the resulting `WIFI_EVENT_STA_DISCONNECTED` callback against its
   own ifdown call. The two lock orders wedge each other permanently — an
   AB-BA deadlock, confirmed on real ESP32-S3 hardware (XIAO ESP32-S3,
   `CONFIG_ESPRESSIF_WIFI` + `CONFIG_PM` + `CONFIG_SCHED_TICKLESS`).
   
   Fix: `esp_wifi_lock()` is now taken only around the specific calls that
   reach into the Wi-Fi driver API (`esp_wifi_scan_event_parse()`,
   `esp_wifi_set_ps()`), never spanning a `esp_wlan_*_hook()` call. This
   keeps `netdev_lock()` first (or absent) and `esp_wifi_lock()` last on
   every path, matching the order every other caller already uses.
   
   ## Impact
   
   * Is new feature added? No — pure bug fix.
   * Is existing feature changed? No behavior change for any config that
     worked correctly before; only removes a deadlock window that was
     always latent whenever `WIFI_EVENT_STA_DISCONNECTED`/`_CONNECTED` (or
     the AP equivalents) could race an ifdown/ifup on the same device.
   * Impact on hardware? No board/driver files touched — this is generic
     `arch/xtensa/src/common/espressif/` code shared by every Xtensa
     Espressif target that enables `CONFIG_ESPRESSIF_WIFI`.
   
   ## Testing
   
   I confirm that changes are verified on local setup and works as intended:
   
   * Build Host: Ubuntu 24.04, x86_64, `xtensa-esp-elf-gcc` (crosstool-NG
     esp-14.2.0_20241119, 14.2.0).
   * Target: Xtensa, Seeed XIAO ESP32-S3, out-of-tree defconfig with
     `CONFIG_ESPRESSIF_WIFI=y`, `CONFIG_PM=y`, `CONFIG_SCHED_TICKLESS=y`,
     `CONFIG_NETINIT_DHCPC=y`, real Wi-Fi AP (WPA2/CCMP) + DHCP.
   * Reproduction: an application task calls `wpa_driver_wext_disconnect()`
     immediately followed by `wapi_set_ifdown()` on the STA interface,
     with no delay between the two calls.
   
   Confirmed via JTAG (OpenOCD + GDB) that both wedged tasks were genuinely
   blocked (not merely slow): the disconnecting task waited on a mutex
   whose `sem.val.mholder` field named the kernel low-priority work-queue
   thread as holder, and that work-queue thread waited on a mutex naming
   the disconnecting task right back — the two lock orders above,
   confirmed live in memory, not inferred from code reading alone.
   
   Console log before the fix (`ps`, ~20s after the disconnecting call,
   no further progress ever observed):
   
   ```
     TID   PID  PPID PRI POLICY   TYPE    NPX STATE    EVENT     SIGMASK        
    STACK COMMAND
       1     0     0 224 RR       Kthread   - Waiting  Semaphore 
0000000000000000 0008104 hpwork ...
       2     0     0 100 RR       Kthread   - Waiting  Mutex:9   
0000000000000000 0004016 lpwork ...
       5     0     0 253 RR       Kthread   - Waiting  MQ empty  
0000000000000000 0006600 wifi
       9     9     0 100 RR       Task      - Waiting  Mutex:2   
0000000000000000 0008128 collar start
   ```
   
   (`Mutex:N` names the *holder's* TID — see
   `sched/sched/sched_get_stateinfo.c`. TID 9 waits on a mutex held by TID
   2 (`lpwork`); TID 2 waits on a mutex held by TID 9 — a two-task cycle.)
   
   Console log after the fix, same reproduction, no hang, all application
   worker threads alive and running:
   
   ```
     TID   PID  PPID PRI POLICY   TYPE    NPX STATE    EVENT     SIGMASK        
    STACK COMMAND
       9     9     0 100 RR       Task      - Waiting  Semaphore 
0000000000000000 0008128 collar start
      10     9     0 110 RR       pthread   - Waiting  Semaphore 
0000000000000000 0004064 collar_imu ...
      11     9     0 105 RR       pthread   - Waiting  Semaphore 
0000000000000000 0004064 collar_sd ...
      12     9     0 100 RR       pthread   - Waiting  Semaphore 
0000000000000000 0008160 collar_tx ...
   ```
   
   Reproduced 4/4 times before the fix, 0/2 after (both clean runs reached
   full application startup with the disconnect/reconnect path exercised).
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to