mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
wifi: cfg80211: convert pmsr_free_wk to wiphy_work to fix deadlock
When a netlink socket that owns a PMSR session is closed,
cfg80211_release_pmsr() clears the request's nl_portid and queues
pmsr_free_wk to call cfg80211_pmsr_process_abort() asynchronously.
If the interface tears down concurrently, cfg80211_pmsr_wdev_down()
is called under wiphy_lock and calls cancel_work_sync(&pmsr_free_wk)
to wait for any running work. The work function acquires wiphy_lock
via guard(wiphy) before calling process_abort.
This is a deadlock: wdev_down holds wiphy_lock and blocks inside
cancel_work_sync(); pmsr_free_wk blocks trying to acquire that same
wiphy_lock. Neither thread can proceed.
The same deadlock is reachable from cfg80211_leave_locked(), which
calls cfg80211_pmsr_wdev_down() for all interface types under
wiphy_lock.
Fix this by converting pmsr_free_wk from a plain work_struct to a
wiphy_work. The wiphy_work dispatcher holds wiphy_lock when running
work items, so the explicit guard(wiphy) in the work function is no
longer needed. wiphy_work_cancel() can be called safely while holding
wiphy_lock - since wiphy_lock prevents the work from running
concurrently, wiphy_work_cancel() never blocks, eliminating the
deadlock.
Remove the cancel_work_sync() for pmsr_free_wk from the
NETDEV_GOING_DOWN handler. cfg80211_leave(), called unconditionally
just before it, already cancels any pending work under wiphy_lock
via wiphy_work_cancel() inside cfg80211_pmsr_wdev_down().
Fixes: 6dccbc9f3e ("wifi: cfg80211: cancel pmsr_free_wk in cfg80211_pmsr_wdev_down")
Signed-off-by: Peddolla Harshavardhan Reddy <peddolla.reddy@oss.qualcomm.com>
Link: https://patch.msgid.link/20260703082523.2629324-1-peddolla.reddy@oss.qualcomm.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
74e27cd1d9
commit
2b0eab425e
|
|
@ -7265,7 +7265,7 @@ struct wireless_dev {
|
|||
|
||||
struct list_head pmsr_list;
|
||||
spinlock_t pmsr_lock;
|
||||
struct work_struct pmsr_free_wk;
|
||||
struct wiphy_work pmsr_free_wk;
|
||||
|
||||
unsigned long unprot_beacon_reported;
|
||||
|
||||
|
|
|
|||
|
|
@ -1614,7 +1614,7 @@ void cfg80211_init_wdev(struct wireless_dev *wdev)
|
|||
INIT_LIST_HEAD(&wdev->mgmt_registrations);
|
||||
INIT_LIST_HEAD(&wdev->pmsr_list);
|
||||
spin_lock_init(&wdev->pmsr_lock);
|
||||
INIT_WORK(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk);
|
||||
wiphy_work_init(&wdev->pmsr_free_wk, cfg80211_pmsr_free_wk);
|
||||
|
||||
#ifdef CONFIG_CFG80211_WEXT
|
||||
wdev->wext.default_key = -1;
|
||||
|
|
@ -1748,7 +1748,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
|
|||
cfg80211_remove_links(wdev);
|
||||
/* since we just did cfg80211_leave() nothing to do there */
|
||||
cancel_work_sync(&wdev->disconnect_wk);
|
||||
cancel_work_sync(&wdev->pmsr_free_wk);
|
||||
break;
|
||||
case NETDEV_DOWN:
|
||||
wiphy_lock(&rdev->wiphy);
|
||||
|
|
|
|||
|
|
@ -586,7 +586,7 @@ cfg80211_get_6ghz_power_type(const u8 *elems, size_t elems_len,
|
|||
|
||||
void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid);
|
||||
void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev);
|
||||
void cfg80211_pmsr_free_wk(struct work_struct *work);
|
||||
void cfg80211_pmsr_free_wk(struct wiphy *wiphy, struct wiphy_work *work);
|
||||
|
||||
void cfg80211_remove_link(struct wireless_dev *wdev, unsigned int link_id);
|
||||
void cfg80211_remove_links(struct wireless_dev *wdev);
|
||||
|
|
|
|||
|
|
@ -807,13 +807,11 @@ static void cfg80211_pmsr_process_abort(struct wireless_dev *wdev)
|
|||
}
|
||||
}
|
||||
|
||||
void cfg80211_pmsr_free_wk(struct work_struct *work)
|
||||
void cfg80211_pmsr_free_wk(struct wiphy *wiphy, struct wiphy_work *work)
|
||||
{
|
||||
struct wireless_dev *wdev = container_of(work, struct wireless_dev,
|
||||
pmsr_free_wk);
|
||||
|
||||
guard(wiphy)(wdev->wiphy);
|
||||
|
||||
cfg80211_pmsr_process_abort(wdev);
|
||||
}
|
||||
|
||||
|
|
@ -829,7 +827,7 @@ void cfg80211_pmsr_wdev_down(struct wireless_dev *wdev)
|
|||
}
|
||||
spin_unlock_bh(&wdev->pmsr_lock);
|
||||
|
||||
cancel_work_sync(&wdev->pmsr_free_wk);
|
||||
wiphy_work_cancel(wdev->wiphy, &wdev->pmsr_free_wk);
|
||||
if (found)
|
||||
cfg80211_pmsr_process_abort(wdev);
|
||||
|
||||
|
|
@ -844,7 +842,7 @@ void cfg80211_release_pmsr(struct wireless_dev *wdev, u32 portid)
|
|||
list_for_each_entry(req, &wdev->pmsr_list, list) {
|
||||
if (req->nl_portid == portid) {
|
||||
req->nl_portid = 0;
|
||||
schedule_work(&wdev->pmsr_free_wk);
|
||||
wiphy_work_queue(wdev->wiphy, &wdev->pmsr_free_wk);
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&wdev->pmsr_lock);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user