Skip to content

Commit

Permalink
cfg80211: fix locking
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Johannes Berg authored and John W. Linville committed Jul 10, 2009
1 parent 4f5dadc commit 667503d
Show file tree
Hide file tree
Showing 13 changed files with 823 additions and 200 deletions.
24 changes: 19 additions & 5 deletions include/net/cfg80211.h
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,7 @@ struct cfg80211_scan_request {
/* internal */
struct wiphy *wiphy;
int ifidx;
bool aborted;
};

/**
Expand Down Expand Up @@ -998,9 +999,11 @@ struct cfg80211_ops {
int (*assoc)(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_assoc_request *req);
int (*deauth)(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_deauth_request *req);
struct cfg80211_deauth_request *req,
void *cookie);
int (*disassoc)(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_disassoc_request *req);
struct cfg80211_disassoc_request *req,
void *cookie);

int (*connect)(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_connect_params *sme);
Expand Down Expand Up @@ -1249,10 +1252,12 @@ struct wireless_dev {
struct wiphy *wiphy;
enum nl80211_iftype iftype;

/* private to the generic wireless code */
/* the remainder of this struct should be private to cfg80211 */
struct list_head list;
struct net_device *netdev;

struct mutex mtx;

/* currently used for IBSS and SME - might be rearranged later */
u8 ssid[IEEE80211_MAX_SSID_LEN];
u8 ssid_len;
Expand All @@ -1263,6 +1268,9 @@ struct wireless_dev {
} sme_state;
struct cfg80211_conn *conn;

struct list_head event_list;
spinlock_t event_lock;

struct cfg80211_internal_bss *authtry_bsses[MAX_AUTH_BSSES];
struct cfg80211_internal_bss *auth_bsses[MAX_AUTH_BSSES];
struct cfg80211_internal_bss *current_bss; /* associated / joined */
Expand Down Expand Up @@ -1765,24 +1773,30 @@ void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr);
* @dev: network device
* @buf: deauthentication frame (header + body)
* @len: length of the frame data
* @cookie: cookie from ->deauth if called within that callback,
* %NULL otherwise
*
* This function is called whenever deauthentication has been processed in
* station mode. This includes both received deauthentication frames and
* locally generated ones. This function may sleep.
*/
void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len);
void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len,
void *cookie);

/**
* cfg80211_send_disassoc - notification of processed disassociation
* @dev: network device
* @buf: disassociation response frame (header + body)
* @len: length of the frame data
* @cookie: cookie from ->disassoc if called within that callback,
* %NULL otherwise
*
* This function is called whenever disassociation has been processed in
* station mode. This includes both received disassociation frames and locally
* generated ones. This function may sleep.
*/
void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len);
void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len,
void *cookie);

/**
* cfg80211_michael_mic_failure - notification of Michael MIC failure (TKIP)
Expand Down
12 changes: 8 additions & 4 deletions net/mac80211/cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -1182,15 +1182,19 @@ static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev,
}

static int ieee80211_deauth(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_deauth_request *req)
struct cfg80211_deauth_request *req,
void *cookie)
{
return ieee80211_mgd_deauth(IEEE80211_DEV_TO_SUB_IF(dev), req);
return ieee80211_mgd_deauth(IEEE80211_DEV_TO_SUB_IF(dev),
req, cookie);
}

static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_disassoc_request *req)
struct cfg80211_disassoc_request *req,
void *cookie)
{
return ieee80211_mgd_disassoc(IEEE80211_DEV_TO_SUB_IF(dev), req);
return ieee80211_mgd_disassoc(IEEE80211_DEV_TO_SUB_IF(dev),
req, cookie);
}

static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
Expand Down
6 changes: 4 additions & 2 deletions net/mac80211/ieee80211_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -918,9 +918,11 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
struct cfg80211_assoc_request *req);
int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
struct cfg80211_deauth_request *req);
struct cfg80211_deauth_request *req,
void *cookie);
int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
struct cfg80211_disassoc_request *req);
struct cfg80211_disassoc_request *req,
void *cookie);
ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb);
void ieee80211_send_pspoll(struct ieee80211_local *local,
Expand Down
25 changes: 16 additions & 9 deletions net/mac80211/mlme.c
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,


static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
const u8 *bssid, u16 stype, u16 reason)
const u8 *bssid, u16 stype, u16 reason,
void *cookie)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
Expand All @@ -412,9 +413,9 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
mgmt->u.deauth.reason_code = cpu_to_le16(reason);

if (stype == IEEE80211_STYPE_DEAUTH)
cfg80211_send_deauth(sdata->dev, (u8 *) mgmt, skb->len);
cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len, cookie);
else
cfg80211_send_disassoc(sdata->dev, (u8 *) mgmt, skb->len);
cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len, cookie);
ieee80211_tx_skb(sdata, skb, ifmgd->flags & IEEE80211_STA_MFP_ENABLED);
}

Expand Down Expand Up @@ -1837,10 +1838,12 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
/* no action */
break;
case RX_MGMT_CFG80211_DEAUTH:
cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len);
cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len,
NULL);
break;
case RX_MGMT_CFG80211_DISASSOC:
cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len);
cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len,
NULL);
break;
default:
WARN(1, "unexpected: %d", rma);
Expand Down Expand Up @@ -2273,7 +2276,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
}

int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
struct cfg80211_deauth_request *req)
struct cfg80211_deauth_request *req,
void *cookie)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_mgd_work *wk;
Expand Down Expand Up @@ -2305,13 +2309,15 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
mutex_unlock(&ifmgd->mtx);

ieee80211_send_deauth_disassoc(sdata, bssid,
IEEE80211_STYPE_DEAUTH, req->reason_code);
IEEE80211_STYPE_DEAUTH, req->reason_code,
cookie);

return 0;
}

int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
struct cfg80211_disassoc_request *req)
struct cfg80211_disassoc_request *req,
void *cookie)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;

Expand All @@ -2331,6 +2337,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
mutex_unlock(&ifmgd->mtx);

ieee80211_send_deauth_disassoc(sdata, req->bss->bssid,
IEEE80211_STYPE_DISASSOC, req->reason_code);
IEEE80211_STYPE_DISASSOC, req->reason_code,
cookie);
return 0;
}
92 changes: 86 additions & 6 deletions net/wireless/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,71 @@ static void cfg80211_rfkill_sync_work(struct work_struct *work)
cfg80211_rfkill_set_block(drv, rfkill_blocked(drv->rfkill));
}

static void cfg80211_process_events(struct wireless_dev *wdev)
{
struct cfg80211_event *ev;
unsigned long flags;

spin_lock_irqsave(&wdev->event_lock, flags);
while (!list_empty(&wdev->event_list)) {
ev = list_first_entry(&wdev->event_list,
struct cfg80211_event, list);
list_del(&ev->list);
spin_unlock_irqrestore(&wdev->event_lock, flags);

wdev_lock(wdev);
switch (ev->type) {
case EVENT_CONNECT_RESULT:
__cfg80211_connect_result(
wdev->netdev, ev->cr.bssid,
ev->cr.req_ie, ev->cr.req_ie_len,
ev->cr.resp_ie, ev->cr.resp_ie_len,
ev->cr.status,
ev->cr.status == WLAN_STATUS_SUCCESS);
break;
case EVENT_ROAMED:
__cfg80211_roamed(wdev, ev->rm.bssid,
ev->rm.req_ie, ev->rm.req_ie_len,
ev->rm.resp_ie, ev->rm.resp_ie_len);
break;
case EVENT_DISCONNECTED:
__cfg80211_disconnected(wdev->netdev,
ev->dc.ie, ev->dc.ie_len,
ev->dc.reason, true);
break;
case EVENT_IBSS_JOINED:
__cfg80211_ibss_joined(wdev->netdev, ev->ij.bssid);
break;
}
wdev_unlock(wdev);

kfree(ev);

spin_lock_irqsave(&wdev->event_lock, flags);
}
spin_unlock_irqrestore(&wdev->event_lock, flags);
}

static void cfg80211_event_work(struct work_struct *work)
{
struct cfg80211_registered_device *rdev;
struct wireless_dev *wdev;

rdev = container_of(work, struct cfg80211_registered_device,
event_work);

rtnl_lock();
cfg80211_lock_rdev(rdev);
mutex_lock(&rdev->devlist_mtx);

list_for_each_entry(wdev, &rdev->netdev_list, list)
cfg80211_process_events(wdev);

mutex_unlock(&rdev->devlist_mtx);
cfg80211_unlock_rdev(rdev);
rtnl_unlock();
}

/* exported functions */

struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
Expand Down Expand Up @@ -299,6 +364,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
INIT_LIST_HEAD(&drv->netdev_list);
spin_lock_init(&drv->bss_lock);
INIT_LIST_HEAD(&drv->bss_list);
INIT_WORK(&drv->scan_done_wk, __cfg80211_scan_done);

device_initialize(&drv->wiphy.dev);
drv->wiphy.dev.class = &ieee80211_class;
Expand All @@ -316,6 +382,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)

INIT_WORK(&drv->rfkill_sync, cfg80211_rfkill_sync_work);
INIT_WORK(&drv->conn_work, cfg80211_conn_work);
INIT_WORK(&drv->event_work, cfg80211_event_work);

/*
* Initialize wiphy parameters to IEEE 802.11 MIB default values.
Expand Down Expand Up @@ -477,6 +544,9 @@ void wiphy_unregister(struct wiphy *wiphy)
mutex_unlock(&drv->mtx);

cancel_work_sync(&drv->conn_work);
cancel_work_sync(&drv->scan_done_wk);
kfree(drv->scan_req);
flush_work(&drv->event_work);

cfg80211_debugfs_drv_del(drv);

Expand Down Expand Up @@ -535,6 +605,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,

switch (state) {
case NETDEV_REGISTER:
mutex_init(&wdev->mtx);
INIT_LIST_HEAD(&wdev->event_list);
spin_lock_init(&wdev->event_lock);
mutex_lock(&rdev->devlist_mtx);
list_add(&wdev->list, &rdev->netdev_list);
if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj,
Expand Down Expand Up @@ -566,36 +639,42 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
cfg80211_leave_ibss(rdev, dev, true);
break;
case NL80211_IFTYPE_STATION:
wdev_lock(wdev);
#ifdef CONFIG_WIRELESS_EXT
kfree(wdev->wext.ie);
wdev->wext.ie = NULL;
wdev->wext.ie_len = 0;
wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
#endif
cfg80211_disconnect(rdev, dev,
WLAN_REASON_DEAUTH_LEAVING, true);
__cfg80211_disconnect(rdev, dev,
WLAN_REASON_DEAUTH_LEAVING, true);
cfg80211_mlme_down(rdev, dev);
wdev_unlock(wdev);
break;
default:
break;
}
break;
case NETDEV_UP:
#ifdef CONFIG_WIRELESS_EXT
cfg80211_lock_rdev(rdev);
wdev_lock(wdev);
switch (wdev->iftype) {
case NL80211_IFTYPE_ADHOC:
if (wdev->wext.ibss.ssid_len)
cfg80211_join_ibss(rdev, dev,
&wdev->wext.ibss);
__cfg80211_join_ibss(rdev, dev,
&wdev->wext.ibss);
break;
case NL80211_IFTYPE_STATION:
if (wdev->wext.connect.ssid_len)
cfg80211_connect(rdev, dev,
&wdev->wext.connect);
__cfg80211_connect(rdev, dev,
&wdev->wext.connect);
break;
default:
break;
}
wdev_unlock(wdev);
cfg80211_unlock_rdev(rdev);
#endif
break;
case NETDEV_UNREGISTER:
Expand All @@ -605,6 +684,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
list_del_init(&wdev->list);
}
mutex_unlock(&rdev->devlist_mtx);
mutex_destroy(&wdev->mtx);
break;
case NETDEV_PRE_UP:
if (rfkill_blocked(rdev->rfkill))
Expand Down
Loading

0 comments on commit 667503d

Please sign in to comment.