mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-21 23:57:36 -04:00
wifi: cfg80211: use wiphy work for socket owner autodisconnect
nl80211_netlink_notify() walks the cfg80211 wireless device list when a
NETLINK_GENERIC socket is released. If the socket owns a connection, the
notifier queues the embedded wdev->disconnect_wk work item.
That work is a plain work_struct today. NETDEV_GOING_DOWN cancels it, but a
NETLINK_URELEASE notifier that already observed conn_owner_nlportid can
queue it after that cancel returns. _cfg80211_unregister_wdev() then
removes the wdev from the list and waits for RCU readers, but
synchronize_net() does not drain work queued by such a reader.
Make the autodisconnect work a wiphy_work instead. The callback already
needs the wiphy mutex, and wiphy_work runs under that mutex. This lets
teardown cancel pending autodisconnect work while holding the mutex,
without a cancel_work_sync() vs. worker locking concern.
Also cancel the wiphy work after list_del_rcu() and synchronize_net(). Any
NETLINK_URELEASE notifier that had already reached the wdev list has then
either queued the work and it is removed, or can no longer find the wdev.
Fixes: bd2522b168 ("cfg80211: NL80211_ATTR_SOCKET_OWNER support for CMD_CONNECT")
Suggested-by: Johannes Berg <johannes@sipsolutions.net>
Assisted-by: Codex:gpt-5.5
Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
Link: https://patch.msgid.link/20260706152418.779226-1-zzzccc427@gmail.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
@@ -7228,7 +7228,7 @@ struct wireless_dev {
|
||||
enum ieee80211_bss_type conn_bss_type;
|
||||
u32 conn_owner_nlportid;
|
||||
|
||||
struct work_struct disconnect_wk;
|
||||
struct wiphy_work disconnect_wk;
|
||||
u8 disconnect_bssid[ETH_ALEN];
|
||||
|
||||
struct list_head event_list;
|
||||
|
||||
@@ -1425,6 +1425,7 @@ static void _cfg80211_unregister_wdev(struct wireless_dev *wdev,
|
||||
list_del_rcu(&wdev->list);
|
||||
synchronize_net();
|
||||
rdev->devlist_generation++;
|
||||
wiphy_work_cancel(wdev->wiphy, &wdev->disconnect_wk);
|
||||
|
||||
cfg80211_mlme_purge_registrations(wdev);
|
||||
|
||||
@@ -1638,7 +1639,7 @@ void cfg80211_init_wdev(struct wireless_dev *wdev)
|
||||
wdev->iftype == NL80211_IFTYPE_ADHOC) && !wdev->use_4addr)
|
||||
wdev->netdev->priv_flags |= IFF_DONT_BRIDGE;
|
||||
|
||||
INIT_WORK(&wdev->disconnect_wk, cfg80211_autodisconnect_wk);
|
||||
wiphy_work_init(&wdev->disconnect_wk, cfg80211_autodisconnect_wk);
|
||||
}
|
||||
|
||||
void cfg80211_register_wdev(struct cfg80211_registered_device *rdev,
|
||||
@@ -1744,10 +1745,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
|
||||
break;
|
||||
case NETDEV_GOING_DOWN:
|
||||
cfg80211_leave(rdev, wdev, -1);
|
||||
scoped_guard(wiphy, &rdev->wiphy)
|
||||
scoped_guard(wiphy, &rdev->wiphy) {
|
||||
cfg80211_remove_links(wdev);
|
||||
/* since we just did cfg80211_leave() nothing to do there */
|
||||
cancel_work_sync(&wdev->disconnect_wk);
|
||||
/* since we just did cfg80211_leave() nothing to do there */
|
||||
wiphy_work_cancel(wdev->wiphy, &wdev->disconnect_wk);
|
||||
}
|
||||
break;
|
||||
case NETDEV_DOWN:
|
||||
wiphy_lock(&rdev->wiphy);
|
||||
|
||||
@@ -428,7 +428,7 @@ void __cfg80211_port_authorized(struct wireless_dev *wdev, const u8 *peer_addr,
|
||||
const u8 *td_bitmap, u8 td_bitmap_len);
|
||||
int cfg80211_mgd_wext_connect(struct cfg80211_registered_device *rdev,
|
||||
struct wireless_dev *wdev);
|
||||
void cfg80211_autodisconnect_wk(struct work_struct *work);
|
||||
void cfg80211_autodisconnect_wk(struct wiphy *wiphy, struct wiphy_work *work);
|
||||
|
||||
/* SME implementation */
|
||||
void cfg80211_conn_work(struct work_struct *work);
|
||||
|
||||
@@ -22954,7 +22954,8 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
|
||||
wdev->nl_owner_dead = true;
|
||||
schedule_work(&rdev->destroy_work);
|
||||
} else if (wdev->conn_owner_nlportid == notify->portid) {
|
||||
schedule_work(&wdev->disconnect_wk);
|
||||
wiphy_work_queue(wdev->wiphy,
|
||||
&wdev->disconnect_wk);
|
||||
}
|
||||
|
||||
cfg80211_release_pmsr(wdev, notify->portid);
|
||||
|
||||
@@ -1578,13 +1578,11 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
|
||||
* Used to clean up after the connection / connection attempt owner socket
|
||||
* disconnects
|
||||
*/
|
||||
void cfg80211_autodisconnect_wk(struct work_struct *work)
|
||||
void cfg80211_autodisconnect_wk(struct wiphy *wiphy, struct wiphy_work *work)
|
||||
{
|
||||
struct wireless_dev *wdev =
|
||||
container_of(work, struct wireless_dev, disconnect_wk);
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
|
||||
|
||||
guard(wiphy)(wdev->wiphy);
|
||||
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
|
||||
|
||||
if (wdev->conn_owner_nlportid) {
|
||||
switch (wdev->iftype) {
|
||||
|
||||
Reference in New Issue
Block a user