diff options
Diffstat (limited to 'net/mac80211/ieee80211.c')
-rw-r--r-- | net/mac80211/ieee80211.c | 78 |
1 files changed, 52 insertions, 26 deletions
diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c index 727af295c96..85b1391375c 100644 --- a/net/mac80211/ieee80211.c +++ b/net/mac80211/ieee80211.c @@ -375,15 +375,19 @@ static int ieee80211_stop(struct net_device *dev) sdata = IEEE80211_DEV_TO_SUB_IF(dev); - list_for_each_entry(sta, &local->sta_list, list) { - if (sta->dev == dev) + rcu_read_lock(); + + list_for_each_entry_rcu(sta, &local->sta_list, list) { + if (sta->sdata == sdata) for (i = 0; i < STA_TID_NUM; i++) - ieee80211_sta_stop_rx_ba_session(sta->dev, + ieee80211_sta_stop_rx_ba_session(sdata->dev, sta->addr, i, WLAN_BACK_RECIPIENT, WLAN_REASON_QSTA_LEAVE_QBSS); } + rcu_read_unlock(); + netif_stop_queue(dev); /* @@ -449,7 +453,7 @@ static int ieee80211_stop(struct net_device *dev) netif_tx_unlock_bh(local->mdev); break; case IEEE80211_IF_TYPE_MESH_POINT: - sta_info_flush(local, dev); + sta_info_flush(local, sdata); /* fall through */ case IEEE80211_IF_TYPE_STA: case IEEE80211_IF_TYPE_IBSS: @@ -522,9 +526,12 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) print_mac(mac, ra), tid); #endif /* CONFIG_MAC80211_HT_DEBUG */ + rcu_read_lock(); + sta = sta_info_get(local, ra); if (!sta) { printk(KERN_DEBUG "Could not find the station\n"); + rcu_read_unlock(); return -ENOENT; } @@ -564,7 +571,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) spin_unlock_bh(&local->mdev->queue_lock); goto start_ba_exit; } - sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev); + sdata = sta->sdata; /* Ok, the Addba frame hasn't been sent yet, but if the driver calls the * call back right away, it must see that the flow has begun */ @@ -601,7 +608,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) sta->ampdu_mlme.dialog_token_allocator; sta->ampdu_mlme.tid_tx[tid].ssn = start_seq_num; - ieee80211_send_addba_request(sta->dev, ra, tid, + ieee80211_send_addba_request(sta->sdata->dev, ra, tid, sta->ampdu_mlme.tid_tx[tid].dialog_token, sta->ampdu_mlme.tid_tx[tid].ssn, 0x40, 5000); @@ -614,7 +621,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid) start_ba_exit: spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx); - sta_info_put(sta); + rcu_read_unlock(); return ret; } EXPORT_SYMBOL(ieee80211_start_tx_ba_session); @@ -637,9 +644,12 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw, print_mac(mac, ra), tid); #endif /* CONFIG_MAC80211_HT_DEBUG */ + rcu_read_lock(); sta = sta_info_get(local, ra); - if (!sta) + if (!sta) { + rcu_read_unlock(); return -ENOENT; + } /* check if the TID is in aggregation */ state = &sta->ampdu_mlme.tid_tx[tid].state; @@ -673,7 +683,7 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw, stop_BA_exit: spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx); - sta_info_put(sta); + rcu_read_unlock(); return ret; } EXPORT_SYMBOL(ieee80211_stop_tx_ba_session); @@ -691,8 +701,10 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) return; } + rcu_read_lock(); sta = sta_info_get(local, ra); if (!sta) { + rcu_read_unlock(); printk(KERN_DEBUG "Could not find station: %s\n", print_mac(mac, ra)); return; @@ -705,7 +717,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) printk(KERN_DEBUG "addBA was not requested yet, state is %d\n", *state); spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx); - sta_info_put(sta); + rcu_read_unlock(); return; } @@ -718,7 +730,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid) ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]); } spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx); - sta_info_put(sta); + rcu_read_unlock(); } EXPORT_SYMBOL(ieee80211_start_tx_ba_cb); @@ -739,10 +751,12 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) printk(KERN_DEBUG "Stop a BA session requested on DA %s tid %d\n", print_mac(mac, ra), tid); + rcu_read_lock(); sta = sta_info_get(local, ra); if (!sta) { printk(KERN_DEBUG "Could not find station: %s\n", print_mac(mac, ra)); + rcu_read_unlock(); return; } state = &sta->ampdu_mlme.tid_tx[tid].state; @@ -750,13 +764,13 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) spin_lock_bh(&sta->ampdu_mlme.ampdu_tx); if ((*state & HT_AGG_STATE_REQ_STOP_BA_MSK) == 0) { printk(KERN_DEBUG "unexpected callback to A-MPDU stop\n"); - sta_info_put(sta); spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx); + rcu_read_unlock(); return; } if (*state & HT_AGG_STATE_INITIATOR_MSK) - ieee80211_send_delba(sta->dev, ra, tid, + ieee80211_send_delba(sta->sdata->dev, ra, tid, WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE); agg_queue = sta->tid_to_tx_q[tid]; @@ -777,7 +791,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid) sta->ampdu_mlme.tid_tx[tid].addba_req_num = 0; spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx); - sta_info_put(sta); + rcu_read_unlock(); } EXPORT_SYMBOL(ieee80211_stop_tx_ba_cb); @@ -887,32 +901,41 @@ int ieee80211_if_update_wds(struct net_device *dev, u8 *remote_addr) struct sta_info *sta; DECLARE_MAC_BUF(mac); + might_sleep(); + if (compare_ether_addr(remote_addr, sdata->u.wds.remote_addr) == 0) return 0; + rcu_read_lock(); + /* Create STA entry for the new peer */ - sta = sta_info_add(local, dev, remote_addr, GFP_KERNEL); - if (IS_ERR(sta)) + sta = sta_info_add(sdata, remote_addr); + if (IS_ERR(sta)) { + rcu_read_unlock(); return PTR_ERR(sta); + } sta->flags |= WLAN_STA_AUTHORIZED; - sta_info_put(sta); - /* Remove STA entry for the old peer */ sta = sta_info_get(local, sdata->u.wds.remote_addr); - if (sta) { - sta_info_free(sta); - sta_info_put(sta); - } else { + if (sta) + sta_info_unlink(&sta); + else printk(KERN_DEBUG "%s: could not find STA entry for WDS link " "peer %s\n", dev->name, print_mac(mac, sdata->u.wds.remote_addr)); - } /* Update WDS link data */ memcpy(&sdata->u.wds.remote_addr, remote_addr, ETH_ALEN); + rcu_read_unlock(); + + if (sta) { + synchronize_rcu(); + sta_info_destroy(sta); + } + return 0; } @@ -1330,6 +1353,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, return; } + rcu_read_lock(); + if (status->excessive_retries) { struct sta_info *sta; sta = sta_info_get(local, hdr->addr1); @@ -1343,10 +1368,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, status->flags |= IEEE80211_TX_STATUS_TX_FILTERED; ieee80211_handle_filtered_frame(local, sta, skb, status); - sta_info_put(sta); + rcu_read_unlock(); return; } - sta_info_put(sta); } } @@ -1356,12 +1380,14 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb, if (sta) { ieee80211_handle_filtered_frame(local, sta, skb, status); - sta_info_put(sta); + rcu_read_unlock(); return; } } else rate_control_tx_status(local->mdev, skb, status); + rcu_read_unlock(); + ieee80211_led_tx(local, 0); /* SNMP counters |