cfg80211: fix locking

Over time, a lot of locking issues have crept into
the smarts of cfg80211, so e.g. scan completion can
race against a new scan, IBSS join can race against
leaving an IBSS, etc.

Introduce a new per-interface lock that protects
most of the per-interface data that we need to keep
track of, and sprinkle assertions about that lock
everywhere. Some things now need to be offloaded to
work structs so that we don't require being able to
sleep in functions the drivers call. The exception
to that are the MLME callbacks (rx_auth etc.) that
currently only mac80211 calls because it was easier
to do that there instead of in cfg80211, and future
drivers implementing those calls will, if they ever
exist, probably need to use a similar scheme like
mac80211 anyway...

In order to be able to handle _deauth and _disassoc
properly, introduce a cookie passed to it that will
determine locking requirements.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Johannes Berg
2009-07-07 03:56:11 +02:00
committed by John W. Linville
parent 4f5dadcebb
commit 667503ddcb
13 changed files with 823 additions and 200 deletions

View File

@@ -17,13 +17,21 @@
#define IEEE80211_SCAN_RESULT_EXPIRE (10 * HZ)
void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
void __cfg80211_scan_done(struct work_struct *wk)
{
struct cfg80211_registered_device *rdev;
struct cfg80211_scan_request *request;
struct net_device *dev;
#ifdef CONFIG_WIRELESS_EXT
union iwreq_data wrqu;
#endif
rdev = container_of(wk, struct cfg80211_registered_device,
scan_done_wk);
mutex_lock(&rdev->mtx);
request = rdev->scan_req;
dev = dev_get_by_index(&init_net, request->ifidx);
if (!dev)
goto out;
@@ -35,7 +43,7 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
*/
cfg80211_sme_scan_done(dev);
if (aborted)
if (request->aborted)
nl80211_send_scan_aborted(wiphy_to_dev(request->wiphy), dev);
else
nl80211_send_scan_done(wiphy_to_dev(request->wiphy), dev);
@@ -43,7 +51,7 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
wiphy_to_dev(request->wiphy)->scan_req = NULL;
#ifdef CONFIG_WIRELESS_EXT
if (!aborted) {
if (!request->aborted) {
memset(&wrqu, 0, sizeof(wrqu));
wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
@@ -53,8 +61,24 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
dev_put(dev);
out:
cfg80211_unlock_rdev(rdev);
kfree(request);
}
void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted)
{
struct net_device *dev = dev_get_by_index(&init_net, request->ifidx);
if (WARN_ON(!dev)) {
kfree(request);
return;
}
WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req);
request->aborted = aborted;
schedule_work(&wiphy_to_dev(request->wiphy)->scan_done_wk);
dev_put(dev);
}
EXPORT_SYMBOL(cfg80211_scan_done);
static void bss_release(struct kref *ref)