IBSS has never had locking, instead relying on some
memory barriers etc. That's hard to get right, and
I think we had it wrong too until the previous patch.
Since this is not performance sensitive, it doesn't
make sense to have the maintenance overhead of that,
so add proper locking.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
{
u16 auth_alg, auth_transaction, status_code;
{
u16 auth_alg, auth_transaction, status_code;
+ lockdep_assert_held(&sdata->u.ibss.mtx);
+
if (len < 24 + 6)
return;
if (len < 24 + 6)
return;
u32 bss_change;
u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
u32 bss_change;
u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
+ lockdep_assert_held(&ifibss->mtx);
+
/* Reset own TSF to allow time synchronization work. */
drv_reset_tsf(local);
/* Reset own TSF to allow time synchronization work. */
drv_reset_tsf(local);
int i, j;
u16 beacon_int = cbss->beacon_interval;
int i, j;
u16 beacon_int = cbss->beacon_interval;
+ lockdep_assert_held(&sdata->u.ibss.mtx);
+
if (beacon_int < 10)
beacon_int = 10;
if (beacon_int < 10)
beacon_int = 10;
int active = 0;
struct sta_info *sta;
int active = 0;
struct sta_info *sta;
+ lockdep_assert_held(&sdata->u.ibss.mtx);
+
rcu_read_lock();
list_for_each_entry_rcu(sta, &local->sta_list, list) {
rcu_read_lock();
list_for_each_entry_rcu(sta, &local->sta_list, list) {
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
+ lockdep_assert_held(&ifibss->mtx);
+
mod_timer(&ifibss->timer,
round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));
mod_timer(&ifibss->timer,
round_jiffies(jiffies + IEEE80211_IBSS_MERGE_INTERVAL));
+ lockdep_assert_held(&ifibss->mtx);
+
if (ifibss->fixed_bssid) {
memcpy(bssid, ifibss->bssid, ETH_ALEN);
} else {
if (ifibss->fixed_bssid) {
memcpy(bssid, ifibss->bssid, ETH_ALEN);
} else {
int active_ibss;
u16 capability;
int active_ibss;
u16 capability;
+ lockdep_assert_held(&ifibss->mtx);
+
active_ibss = ieee80211_sta_active_ibss(sdata);
#ifdef CONFIG_MAC80211_IBSS_DEBUG
printk(KERN_DEBUG "%s: sta_find_ibss (active_ibss=%d)\n",
active_ibss = ieee80211_sta_active_ibss(sdata);
#ifdef CONFIG_MAC80211_IBSS_DEBUG
printk(KERN_DEBUG "%s: sta_find_ibss (active_ibss=%d)\n",
struct ieee80211_mgmt *resp;
u8 *pos, *end;
struct ieee80211_mgmt *resp;
u8 *pos, *end;
+ lockdep_assert_held(&ifibss->mtx);
+
if (ifibss->state != IEEE80211_IBSS_MLME_JOINED ||
len < 24 + 2 || !ifibss->presp)
return;
if (ifibss->state != IEEE80211_IBSS_MLME_JOINED ||
len < 24 + 2 || !ifibss->presp)
return;
mgmt = (struct ieee80211_mgmt *) skb->data;
fc = le16_to_cpu(mgmt->frame_control);
mgmt = (struct ieee80211_mgmt *) skb->data;
fc = le16_to_cpu(mgmt->frame_control);
+ mutex_lock(&sdata->u.ibss.mtx);
+
switch (fc & IEEE80211_FCTL_STYPE) {
case IEEE80211_STYPE_PROBE_REQ:
ieee80211_rx_mgmt_probe_req(sdata, mgmt, skb->len);
switch (fc & IEEE80211_FCTL_STYPE) {
case IEEE80211_STYPE_PROBE_REQ:
ieee80211_rx_mgmt_probe_req(sdata, mgmt, skb->len);
ieee80211_rx_mgmt_auth_ibss(sdata, mgmt, skb->len);
break;
}
ieee80211_rx_mgmt_auth_ibss(sdata, mgmt, skb->len);
break;
}
+
+ mutex_unlock(&sdata->u.ibss.mtx);
}
void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
}
void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
- if (!test_and_clear_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request))
- return;
+ mutex_lock(&ifibss->mtx);
+
+ /*
+ * Work could be scheduled after scan or similar
+ * when we aren't even joined (or trying) with a
+ * network.
+ */
+ if (!ifibss->ssid_len)
+ goto out;
switch (ifibss->state) {
case IEEE80211_IBSS_MLME_SEARCH:
switch (ifibss->state) {
case IEEE80211_IBSS_MLME_SEARCH:
-static void ieee80211_queue_ibss_work(struct ieee80211_sub_if_data *sdata)
-{
- struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
- struct ieee80211_local *local = sdata->local;
-
- set_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request);
- ieee80211_queue_work(&local->hw, &sdata->work);
+ out:
+ mutex_unlock(&ifibss->mtx);
}
static void ieee80211_ibss_timer(unsigned long data)
}
static void ieee80211_ibss_timer(unsigned long data)
- ieee80211_queue_ibss_work(sdata);
+ ieee80211_queue_work(&local->hw, &sdata->work);
setup_timer(&ifibss->timer, ieee80211_ibss_timer,
(unsigned long) sdata);
setup_timer(&ifibss->timer, ieee80211_ibss_timer,
(unsigned long) sdata);
+ mutex_init(&ifibss->mtx);
}
/* scan finished notification */
}
/* scan finished notification */
continue;
if (sdata->vif.type != NL80211_IFTYPE_ADHOC)
continue;
continue;
if (sdata->vif.type != NL80211_IFTYPE_ADHOC)
continue;
- if (!sdata->u.ibss.ssid_len)
- continue;
sdata->u.ibss.last_scan_completed = jiffies;
sdata->u.ibss.last_scan_completed = jiffies;
- ieee80211_queue_ibss_work(sdata);
+ ieee80211_queue_work(&local->hw, &sdata->work);
}
mutex_unlock(&local->iflist_mtx);
}
}
mutex_unlock(&local->iflist_mtx);
}
+ skb = dev_alloc_skb(sdata->local->hw.extra_tx_headroom +
+ 36 /* bitrates */ +
+ 34 /* SSID */ +
+ 3 /* DS params */ +
+ 4 /* IBSS params */ +
+ params->ie_len);
+ if (!skb)
+ return -ENOMEM;
+
+ mutex_lock(&sdata->u.ibss.mtx);
+
if (params->bssid) {
memcpy(sdata->u.ibss.bssid, params->bssid, ETH_ALEN);
sdata->u.ibss.fixed_bssid = true;
if (params->bssid) {
memcpy(sdata->u.ibss.bssid, params->bssid, ETH_ALEN);
sdata->u.ibss.fixed_bssid = true;
sdata->u.ibss.ie_len = params->ie_len;
}
sdata->u.ibss.ie_len = params->ie_len;
}
- skb = dev_alloc_skb(sdata->local->hw.extra_tx_headroom +
- 36 /* bitrates */ +
- 34 /* SSID */ +
- 3 /* DS params */ +
- 4 /* IBSS params */ +
- params->ie_len);
- if (!skb)
- return -ENOMEM;
-
sdata->u.ibss.skb = skb;
sdata->u.ibss.state = IEEE80211_IBSS_MLME_SEARCH;
sdata->u.ibss.ibss_join_req = jiffies;
memcpy(sdata->u.ibss.ssid, params->ssid, IEEE80211_MAX_SSID_LEN);
sdata->u.ibss.skb = skb;
sdata->u.ibss.state = IEEE80211_IBSS_MLME_SEARCH;
sdata->u.ibss.ibss_join_req = jiffies;
memcpy(sdata->u.ibss.ssid, params->ssid, IEEE80211_MAX_SSID_LEN);
-
- /*
- * The ssid_len setting below is used to see whether
- * we are active, and we need all other settings
- * before that may get visible.
- */
- mb();
-
sdata->u.ibss.ssid_len = params->ssid_len;
ieee80211_recalc_idle(sdata->local);
sdata->u.ibss.ssid_len = params->ssid_len;
ieee80211_recalc_idle(sdata->local);
- set_bit(IEEE80211_IBSS_REQ_RUN, &sdata->u.ibss.request);
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
+ mutex_unlock(&sdata->u.ibss.mtx);
+
struct ieee80211_local *local = sdata->local;
struct cfg80211_bss *cbss;
u16 capability;
struct ieee80211_local *local = sdata->local;
struct cfg80211_bss *cbss;
u16 capability;
+ int active_ibss;
+
+ mutex_lock(&sdata->u.ibss.mtx);
active_ibss = ieee80211_sta_active_ibss(sdata);
active_ibss = ieee80211_sta_active_ibss(sdata);
memset(sdata->u.ibss.bssid, 0, ETH_ALEN);
sdata->u.ibss.ssid_len = 0;
memset(sdata->u.ibss.bssid, 0, ETH_ALEN);
sdata->u.ibss.ssid_len = 0;
- /*
- * ssid_len indicates active or not, so needs to be visible to
- * everybody, especially ieee80211_ibss_notify_scan_completed,
- * so it won't restart the timer after we remove it here.
- */
- mb();
-
del_timer_sync(&sdata->u.ibss.timer);
del_timer_sync(&sdata->u.ibss.timer);
- clear_bit(IEEE80211_IBSS_REQ_RUN, &sdata->u.ibss.request);
- /*
- * Since the REQ_RUN bit is clear, the work won't do
- * anything if it runs after this.
- */
+
+ mutex_unlock(&sdata->u.ibss.mtx);
ieee80211_recalc_idle(sdata->local);
ieee80211_recalc_idle(sdata->local);
int last_cqm_event_signal;
};
int last_cqm_event_signal;
};
-enum ieee80211_ibss_request {
- IEEE80211_IBSS_REQ_RUN = 0,
-};
-
struct ieee80211_if_ibss {
struct timer_list timer;
struct ieee80211_if_ibss {
struct timer_list timer;
unsigned long last_scan_completed;
u32 basic_rates;
unsigned long last_scan_completed;
u32 basic_rates;