Re: [PATCH ath-current] wifi: ath11k: fix locking problem in ath11k_dp_rx_tid_del_func()
"Nicolas Escande" <[email protected]> Fri, 31 Jul 2026 16:36:17 +0200
| Newsgroups | org.infradead.lists.ath11k,org.kernel.vger.linux-wireless |
|---|---|
| Message-ID | <[email protected]> |
On Fri Jul 31, 2026 at 8:49 AM CEST, Baochen Qiang wrote: > > > On 7/29/2026 4:57 PM, Nicolas Escande wrote: >> From: Maxime Bizon <[email protected]> >>=20 >> In this function, we iterate over dp->reo_cmd_cache_flush_list using >> list_for_each_entry_safe(), and for each expired entries we call >> ath11k_dp_reo_cache_flush() then free the entry. As this can be called >> from multiple CPU we protect for concurrent access using dp->reo_cmd_loc= k. >>=20 >> The lock is dropped to call ath11k_dp_reo_cache_flush() and taken again >> before keeping iterating over the loop. That is broken. >>=20 >> The list_for_each_entry_safe() protects over deleting the entry being=20 >> iterated over but does not protect for concurrent access. So another >> thread might have taken the lock in between and modified the list, leadi= ng >> to a crash (see bellow).=20 > > nit: s/bellow/below/ > will do [...] >>=20 >> Tested-on: QCN9074 PCI WLAN.HK.2.9.0.1-01977-QCAHKSWPL_SILICONZ-1 > > hardware version missing betweeen target and bus type > will change > QCN9074 hw1.0 PCI > >>=20 >> Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices"= ) >> Signed-off-by: Maxime Bizon <[email protected]> >> Signed-off-by: Nicolas Escande <[email protected]> [...] > > while this seems the minimal fix which stable team might prefer, how abou= t refactoring as > firstly detaching all the expired entries onto a local list under the loc= k and flushing > that list afterwards: > > LIST_HEAD(flush_list); > > spin_lock_bh(&dp->reo_cmd_lock); > list_for_each_entry_safe(elem, tmp, &dp->reo_cmd_cache_flush_list, list= ) { > if () { > list_move_tail(&elem->list, &flush_list); > } > } > spin_unlock_bh(&dp->reo_cmd_lock); > > list_for_each_entry_safe(elem, tmp, &flush_list, list) { > list_del(&elem->list); > ath11k_dp_reo_cache_flush(ab, &elem->data); > kfree(elem); > } > > This fully decouples the concurrency from the flushing: the detach phase = runs entirely > under the lock, and the flush phase walks a thread-private list where no = concurrency > exists. It's a single O(N) pass instead of the O(N^2) worst case of re-sc= anning from the > head, and it avoids bouncing reo_cmd_lock once per freed entry. > > Also this seems simpler and more direct =E2=80=94 its correctness is obvi= ous by construction; > while the fix of this patch is less self-evident: a reader may not easily= grasp why the > code has to rescan from the head after re-taking the lock. > > Non-blocking though =E2=80=94 either form fixes the crash. If you'd rathe= r keep the minimal goto > retry for the stable backport and do the detach-list version as a follow-= up cleanup, that > works for me too. Yes I remember that we fixed it this way with Maxime to minimize code chang= e. But doing it your way seems to be the better thing to do. So I agree I'll rework this wit the single iteration under lock + cleanup pass. But it will take me some time to do so, I do not have much time right now.= =20 So if Jeff prefers taking this one as is just to have a quick fix no object= ion either, I'll rework it later for next. You guys tell me what you prefer.