diff options
author | John W. Linville <linville@tuxdriver.com> | 2011-12-19 13:54:26 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-12-19 13:54:26 -0500 |
commit | 9f6e20cee6253c4ca5faacba8dbd09ebe70132ed (patch) | |
tree | 0dbcdabca91864daaf174bb014a5f46316880403 /net/mac80211 | |
parent | d1d182e00d72300e05b18e28372fab003d8d4a58 (diff) | |
parent | 5bd5e9a6ae5137a61d0b5c277eac61892d89fc4f (diff) | |
download | blackbird-op-linux-9f6e20cee6253c4ca5faacba8dbd09ebe70132ed.tar.gz blackbird-op-linux-9f6e20cee6253c4ca5faacba8dbd09ebe70132ed.zip |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next into for-davem
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/agg-tx.c | 6 | ||||
-rw-r--r-- | net/mac80211/cfg.c | 102 | ||||
-rw-r--r-- | net/mac80211/debugfs_netdev.c | 2 | ||||
-rw-r--r-- | net/mac80211/ht.c | 2 | ||||
-rw-r--r-- | net/mac80211/ibss.c | 156 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 31 | ||||
-rw-r--r-- | net/mac80211/iface.c | 5 | ||||
-rw-r--r-- | net/mac80211/main.c | 11 | ||||
-rw-r--r-- | net/mac80211/mesh_plink.c | 7 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 9 | ||||
-rw-r--r-- | net/mac80211/rx.c | 4 | ||||
-rw-r--r-- | net/mac80211/sta_info.c | 356 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 39 | ||||
-rw-r--r-- | net/mac80211/status.c | 15 | ||||
-rw-r--r-- | net/mac80211/tx.c | 32 | ||||
-rw-r--r-- | net/mac80211/util.c | 3 |
16 files changed, 448 insertions, 332 deletions
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 7380287f010e..e92f98d32746 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -55,6 +55,8 @@ * @ampdu_action function will be called with the action * %IEEE80211_AMPDU_TX_STOP. In this case, the call must not fail, * and the driver must later call ieee80211_stop_tx_ba_cb_irqsafe(). + * Note that the sta can get destroyed before the BA tear down is + * complete. */ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata, @@ -105,7 +107,7 @@ static void ieee80211_send_addba_request(struct ieee80211_sub_if_data *sdata, mgmt->u.action.u.addba_req.start_seq_num = cpu_to_le16(start_seq_num << 4); - ieee80211_tx_skb(sdata, skb); + ieee80211_tx_skb_tid(sdata, skb, tid); } void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn) @@ -134,7 +136,7 @@ void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn) bar->start_seq_num = cpu_to_le16(ssn); IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; - ieee80211_tx_skb(sdata, skb); + ieee80211_tx_skb_tid(sdata, skb, tid); } EXPORT_SYMBOL(ieee80211_send_bar); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 393b2a4445b8..66ad9d9af87f 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -746,10 +746,11 @@ static void ieee80211_send_layer2_update(struct sta_info *sta) netif_rx_ni(skb); } -static void sta_apply_parameters(struct ieee80211_local *local, - struct sta_info *sta, - struct station_parameters *params) +static int sta_apply_parameters(struct ieee80211_local *local, + struct sta_info *sta, + struct station_parameters *params) { + int ret = 0; u32 rates; int i, j; struct ieee80211_supported_band *sband; @@ -761,13 +762,59 @@ static void sta_apply_parameters(struct ieee80211_local *local, mask = params->sta_flags_mask; set = params->sta_flags_set; + /* + * In mesh mode, we can clear AUTHENTICATED flag but must + * also make ASSOCIATED follow appropriately for the driver + * API. See also below, after AUTHORIZED changes. + */ + if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) { + /* cfg80211 should not allow this in non-mesh modes */ + if (WARN_ON(!ieee80211_vif_is_mesh(&sdata->vif))) + return -EINVAL; + + if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED) && + !test_sta_flag(sta, WLAN_STA_AUTH)) { + ret = sta_info_move_state_checked(sta, + IEEE80211_STA_AUTH); + if (ret) + return ret; + ret = sta_info_move_state_checked(sta, + IEEE80211_STA_ASSOC); + if (ret) + return ret; + } + } + if (mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) { if (set & BIT(NL80211_STA_FLAG_AUTHORIZED)) - set_sta_flag(sta, WLAN_STA_AUTHORIZED); + ret = sta_info_move_state_checked(sta, + IEEE80211_STA_AUTHORIZED); else - clear_sta_flag(sta, WLAN_STA_AUTHORIZED); + ret = sta_info_move_state_checked(sta, + IEEE80211_STA_ASSOC); + if (ret) + return ret; } + if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) { + /* cfg80211 should not allow this in non-mesh modes */ + if (WARN_ON(!ieee80211_vif_is_mesh(&sdata->vif))) + return -EINVAL; + + if (!(set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) && + test_sta_flag(sta, WLAN_STA_AUTH)) { + ret = sta_info_move_state_checked(sta, + IEEE80211_STA_AUTH); + if (ret) + return ret; + ret = sta_info_move_state_checked(sta, + IEEE80211_STA_NONE); + if (ret) + return ret; + } + } + + if (mask & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) { if (set & BIT(NL80211_STA_FLAG_SHORT_PREAMBLE)) set_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE); @@ -792,13 +839,6 @@ static void sta_apply_parameters(struct ieee80211_local *local, clear_sta_flag(sta, WLAN_STA_MFP); } - if (mask & BIT(NL80211_STA_FLAG_AUTHENTICATED)) { - if (set & BIT(NL80211_STA_FLAG_AUTHENTICATED)) - set_sta_flag(sta, WLAN_STA_AUTH); - else - clear_sta_flag(sta, WLAN_STA_AUTH); - } - if (mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) { if (set & BIT(NL80211_STA_FLAG_TDLS_PEER)) set_sta_flag(sta, WLAN_STA_TDLS_PEER); @@ -870,6 +910,8 @@ static void sta_apply_parameters(struct ieee80211_local *local, } #endif } + + return 0; } static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, @@ -896,20 +938,18 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, if (is_multicast_ether_addr(mac)) return -EINVAL; - /* Only TDLS-supporting stations can add TDLS peers */ - if ((params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) && - !((wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) && - sdata->vif.type == NL80211_IFTYPE_STATION)) - return -ENOTSUPP; - sta = sta_info_alloc(sdata, mac, GFP_KERNEL); if (!sta) return -ENOMEM; - set_sta_flag(sta, WLAN_STA_AUTH); - set_sta_flag(sta, WLAN_STA_ASSOC); + sta_info_move_state(sta, IEEE80211_STA_AUTH); + sta_info_move_state(sta, IEEE80211_STA_ASSOC); - sta_apply_parameters(local, sta, params); + err = sta_apply_parameters(local, sta, params); + if (err) { + sta_info_free(local, sta); + return err; + } /* * for TDLS, rate control should be initialized only when supported @@ -960,19 +1000,19 @@ static int ieee80211_change_station(struct wiphy *wiphy, struct sta_info *sta; struct ieee80211_sub_if_data *vlansdata; - rcu_read_lock(); + mutex_lock(&local->sta_mtx); sta = sta_info_get_bss(sdata, mac); if (!sta) { - rcu_read_unlock(); + mutex_unlock(&local->sta_mtx); return -ENOENT; } - /* The TDLS bit cannot be toggled after the STA was added */ - if ((params->sta_flags_mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) && - !!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) != - !!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { - rcu_read_unlock(); + /* in station mode, supported rates are only valid with TDLS */ + if (sdata->vif.type == NL80211_IFTYPE_STATION && + params->supported_rates && + !test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { + mutex_unlock(&local->sta_mtx); return -EINVAL; } @@ -981,13 +1021,13 @@ static int ieee80211_change_station(struct wiphy *wiphy, if (vlansdata->vif.type != NL80211_IFTYPE_AP_VLAN && vlansdata->vif.type != NL80211_IFTYPE_AP) { - rcu_read_unlock(); + mutex_unlock(&local->sta_mtx); return -EINVAL; } if (params->vlan->ieee80211_ptr->use_4addr) { if (vlansdata->u.vlan.sta) { - rcu_read_unlock(); + mutex_unlock(&local->sta_mtx); return -EBUSY; } @@ -1003,7 +1043,7 @@ static int ieee80211_change_station(struct wiphy *wiphy, if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && params->supported_rates) rate_control_rate_init(sta); - rcu_read_unlock(); + mutex_unlock(&local->sta_mtx); if (sdata->vif.type == NL80211_IFTYPE_STATION && params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)) diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 8df28910b8ee..176c08ffb13c 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -321,6 +321,7 @@ static ssize_t ieee80211_if_parse_tkip_mic_test( __IEEE80211_IF_FILE_W(tkip_mic_test); /* AP attributes */ +IEEE80211_IF_FILE(num_sta_authorized, u.ap.num_sta_authorized, ATOMIC); IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC); IEEE80211_IF_FILE(dtim_count, u.ap.dtim_count, DEC); @@ -458,6 +459,7 @@ static void add_ap_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD(rc_rateidx_mask_2ghz); DEBUGFS_ADD(rc_rateidx_mask_5ghz); + DEBUGFS_ADD(num_sta_authorized); DEBUGFS_ADD(num_sta_ps); DEBUGFS_ADD(dtim_count); DEBUGFS_ADD(num_buffered_multicast); diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 0fd9c2a7f242..f25fff7607d8 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -300,7 +300,7 @@ void ieee80211_send_delba(struct ieee80211_sub_if_data *sdata, mgmt->u.action.u.delba.params = cpu_to_le16(params); mgmt->u.action.u.delba.reason_code = cpu_to_le16(reason_code); - ieee80211_tx_skb(sdata, skb); + ieee80211_tx_skb_tid(sdata, skb, tid); } void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 3f830ac159e5..f8a32bf98216 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -275,6 +275,80 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, cbss->tsf); } +static struct sta_info *ieee80211_ibss_finish_sta(struct sta_info *sta) + __acquires(RCU) +{ + struct ieee80211_sub_if_data *sdata = sta->sdata; + u8 addr[ETH_ALEN]; + + memcpy(addr, sta->sta.addr, ETH_ALEN); + +#ifdef CONFIG_MAC80211_VERBOSE_DEBUG + wiphy_debug(sdata->local->hw.wiphy, + "Adding new IBSS station %pM (dev=%s)\n", + addr, sdata->name); +#endif + + sta_info_move_state(sta, IEEE80211_STA_AUTH); + sta_info_move_state(sta, IEEE80211_STA_ASSOC); + sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED); + + rate_control_rate_init(sta); + + /* If it fails, maybe we raced another insertion? */ + if (sta_info_insert_rcu(sta)) + return sta_info_get(sdata, addr); + return sta; +} + +static struct sta_info * +ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, + const u8 *bssid, const u8 *addr, + u32 supp_rates) + __acquires(RCU) +{ + struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; + struct ieee80211_local *local = sdata->local; + struct sta_info *sta; + int band = local->hw.conf.channel->band; + + /* + * XXX: Consider removing the least recently used entry and + * allow new one to be added. + */ + if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) { + if (net_ratelimit()) + printk(KERN_DEBUG "%s: No room for a new IBSS STA entry %pM\n", + sdata->name, addr); + rcu_read_lock(); + return NULL; + } + + if (ifibss->state == IEEE80211_IBSS_MLME_SEARCH) { + rcu_read_lock(); + return NULL; + } + + if (compare_ether_addr(bssid, sdata->u.ibss.bssid)) { + rcu_read_lock(); + return NULL; + } + + sta = sta_info_alloc(sdata, addr, GFP_KERNEL); + if (!sta) { + rcu_read_lock(); + return NULL; + } + + sta->last_rx = jiffies; + + /* make sure mandatory rates are always added */ + sta->sta.supp_rates[band] = supp_rates | + ieee80211_mandatory_rates(local, band); + + return ieee80211_ibss_finish_sta(sta); +} + static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len, @@ -334,10 +408,11 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, #endif rates_updated = true; } - } else + } else { + rcu_read_unlock(); sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid, - mgmt->sa, supp_rates, - GFP_ATOMIC); + mgmt->sa, supp_rates); + } } if (sta && elems->wmm_info) @@ -464,21 +539,17 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, ieee80211_sta_join_ibss(sdata, bss); supp_rates = ieee80211_sta_get_rates(local, elems, band); ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, - supp_rates, GFP_KERNEL); + supp_rates); + rcu_read_unlock(); } put_bss: ieee80211_rx_bss_put(local, bss); } -/* - * Add a new IBSS station, will also be called by the RX code when, - * in IBSS mode, receiving a frame from a yet-unknown station, hence - * must be callable in atomic context. - */ -struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, - u8 *bssid, u8 *addr, u32 supp_rates, - gfp_t gfp) +void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata, + const u8 *bssid, const u8 *addr, + u32 supp_rates) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct ieee80211_local *local = sdata->local; @@ -493,37 +564,29 @@ struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, if (net_ratelimit()) printk(KERN_DEBUG "%s: No room for a new IBSS STA entry %pM\n", sdata->name, addr); - return NULL; + return; } if (ifibss->state == IEEE80211_IBSS_MLME_SEARCH) - return NULL; + return; if (compare_ether_addr(bssid, sdata->u.ibss.bssid)) - return NULL; - -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - wiphy_debug(local->hw.wiphy, "Adding new IBSS station %pM (dev=%s)\n", - addr, sdata->name); -#endif + return; - sta = sta_info_alloc(sdata, addr, gfp); + sta = sta_info_alloc(sdata, addr, GFP_ATOMIC); if (!sta) - return NULL; + return; sta->last_rx = jiffies; - set_sta_flag(sta, WLAN_STA_AUTHORIZED); /* make sure mandatory rates are always added */ sta->sta.supp_rates[band] = supp_rates | ieee80211_mandatory_rates(local, band); - rate_control_rate_init(sta); - - /* If it fails, maybe we raced another insertion? */ - if (sta_info_insert(sta)) - return sta_info_get(sdata, addr); - return sta; + spin_lock(&ifibss->incomplete_lock); + list_add(&sta->list, &ifibss->incomplete_stations); + spin_unlock(&ifibss->incomplete_lock); + ieee80211_queue_work(&local->hw, &sdata->work); } static int ieee80211_sta_active_ibss(struct ieee80211_sub_if_data *sdata) @@ -862,6 +925,7 @@ void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata, void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; + struct sta_info *sta; mutex_lock(&ifibss->mtx); @@ -873,6 +937,19 @@ void ieee80211_ibss_work(struct ieee80211_sub_if_data *sdata) if (!ifibss->ssid_len) goto out; + spin_lock_bh(&ifibss->incomplete_lock); + while (!list_empty(&ifibss->incomplete_stations)) { + sta = list_first_entry(&ifibss->incomplete_stations, + struct sta_info, list); + list_del(&sta->list); + spin_unlock_bh(&ifibss->incomplete_lock); + + ieee80211_ibss_finish_sta(sta); + rcu_read_unlock(); + spin_lock_bh(&ifibss->incomplete_lock); + } + spin_unlock_bh(&ifibss->incomplete_lock); + switch (ifibss->state) { case IEEE80211_IBSS_MLME_SEARCH: ieee80211_sta_find_ibss(sdata); @@ -931,6 +1008,8 @@ void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata) setup_timer(&ifibss->timer, ieee80211_ibss_timer, (unsigned long) sdata); mutex_init(&ifibss->mtx); + INIT_LIST_HEAD(&ifibss->incomplete_stations); + spin_lock_init(&ifibss->incomplete_lock); } /* scan finished notification */ @@ -993,8 +1072,11 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, if (params->channel_fixed) { sdata->local->oper_channel = params->channel; if (!ieee80211_set_channel_type(sdata->local, sdata, - params->channel_type)) + params->channel_type)) { + mutex_unlock(&sdata->u.ibss.mtx); + kfree_skb(skb); return -EINVAL; + } } if (params->ie) { @@ -1047,6 +1129,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) struct cfg80211_bss *cbss; u16 capability; int active_ibss; + struct sta_info *sta; mutex_lock(&sdata->u.ibss.mtx); @@ -1075,6 +1158,19 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) } sta_info_flush(sdata->local, sdata); + + spin_lock_bh(&ifibss->incomplete_lock); + while (!list_empty(&ifibss->incomplete_stations)) { + sta = list_first_entry(&ifibss->incomplete_stations, + struct sta_info, list); + list_del(&sta->list); + spin_unlock_bh(&ifibss->incomplete_lock); + + sta_info_free(local, sta); + spin_lock_bh(&ifibss->incomplete_lock); + } + spin_unlock_bh(&ifibss->incomplete_lock); + netif_carrier_off(sdata->dev); /* remove beacon */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 96fe75410bbe..c3f3e431a573 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -243,6 +243,7 @@ struct ieee80211_if_ap { u8 tim[sizeof(unsigned long) * BITS_TO_LONGS(IEEE80211_MAX_AID + 1)]; struct sk_buff_head ps_bc_buf; atomic_t num_sta_ps; /* number of stations in PS mode */ + atomic_t num_sta_authorized; /* number of authorized stations */ int dtim_count; bool dtim_bc_mc; }; @@ -481,6 +482,9 @@ struct ieee80211_if_ibss { struct sk_buff __rcu *presp; struct sk_buff *skb; + spinlock_t incomplete_lock; + struct list_head incomplete_stations; + enum { IEEE80211_IBSS_MLME_SEARCH, IEEE80211_IBSS_MLME_JOINED, @@ -851,18 +855,15 @@ struct ieee80211_local { /* Station data */ /* - * The mutex only protects the list and counter, - * reads are done in RCU. - * Additionally, the lock protects the hash table, - * the pending list and each BSS's TIM bitmap. + * The mutex only protects the list, hash table and + * counter, reads are done with RCU. */ struct mutex sta_mtx; - spinlock_t sta_lock; + spinlock_t tim_lock; unsigned long num_sta; - struct list_head sta_list, sta_pending_list; + struct list_head sta_list; struct sta_info __rcu *sta_hash[STA_HASH_SIZE]; struct timer_list sta_cleanup; - struct work_struct sta_finish_work; int sta_generation; struct sk_buff_head pending[IEEE80211_MAX_QUEUES]; @@ -1171,9 +1172,8 @@ void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata); /* IBSS code */ void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local); void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata); -struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, - u8 *bssid, u8 *addr, u32 supp_rates, - gfp_t gfp); +void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata, + const u8 *bssid, const u8 *addr, u32 supp_rates); int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, struct cfg80211_ibss_params *params); int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata); @@ -1349,7 +1349,16 @@ void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int ke gfp_t gfp); void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata); void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); -void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); + +void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, int tid); +static void inline ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) +{ + /* Send all internal mgmt frames on VO. Accordingly set TID to 7. */ + ieee80211_tx_skb_tid(sdata, skb, 7); +} + void ieee802_11_parse_elems(u8 *start, size_t len, struct ieee802_11_elems *elems); u32 ieee802_11_parse_elems_crc(u8 *start, size_t len, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 3d3bb5e9d8fa..e47768cb8cb3 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -318,8 +318,9 @@ static int ieee80211_do_open(struct net_device *dev, bool coming_up) goto err_del_interface; } - /* no atomic bitop required since STA is not live yet */ - set_sta_flag(sta, WLAN_STA_AUTHORIZED); + sta_info_move_state(sta, IEEE80211_STA_AUTH); + sta_info_move_state(sta, IEEE80211_STA_ASSOC); + sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED); res = sta_info_insert(sta); if (res) { diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 60198ac664a0..0a0d94ad9b08 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -47,7 +47,7 @@ void ieee80211_configure_filter(struct ieee80211_local *local) if (atomic_read(&local->iff_allmultis)) new_flags |= FIF_ALLMULTI; - if (local->monitors || local->scanning) + if (local->monitors || test_bit(SCAN_SW_SCANNING, &local->scanning)) new_flags |= FIF_BCN_PRBRESP_PROMISC; if (local->fif_probe_req || local->probe_req_reg) @@ -150,8 +150,8 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) changed |= IEEE80211_CONF_CHANGE_SMPS; } - if ((local->scanning & SCAN_SW_SCANNING) || - (local->scanning & SCAN_HW_SCANNING)) + if (test_bit(SCAN_SW_SCANNING, &local->scanning) || + test_bit(SCAN_HW_SCANNING, &local->scanning)) power = chan->max_power; else power = local->power_constr_level ? @@ -393,9 +393,6 @@ static int ieee80211_ifa_changed(struct notifier_block *nb, sdata = IEEE80211_DEV_TO_SUB_IF(ndev); bss_conf = &sdata->vif.bss_conf; - if (!ieee80211_sdata_running(sdata)) - return NOTIFY_DONE; - /* ARP filtering is only supported in managed mode */ if (sdata->vif.type != NL80211_IFTYPE_STATION) return NOTIFY_DONE; @@ -424,7 +421,7 @@ static int ieee80211_ifa_changed(struct notifier_block *nb, } bss_conf->arp_addr_cnt = c; - /* Configure driver only if associated */ + /* Configure driver only if associated (which also implies it is up) */ if (ifmgd->associated) { bss_conf->arp_filter_enabled = sdata->arp_filter_state; ieee80211_bss_info_change_notify(sdata, diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 7314372b12ba..41ef1b476442 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -96,9 +96,12 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata, if (!sta) return NULL; - set_sta_flag(sta, WLAN_STA_AUTH); - set_sta_flag(sta, WLAN_STA_AUTHORIZED); + sta_info_move_state(sta, IEEE80211_STA_AUTH); + sta_info_move_state(sta, IEEE80211_STA_ASSOC); + sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED); + set_sta_flag(sta, WLAN_STA_WME); + sta->sta.supp_rates[local->hw.conf.channel->band] = rates; if (elems->ht_cap_elem) ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 09019d135942..a984f1f60ddb 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -819,7 +819,7 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work) } if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) && - (!(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED))) { + !(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) { netif_tx_stop_all_queues(sdata->dev); if (drv_tx_frames_pending(local)) @@ -1577,10 +1577,10 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk, return false; } - set_sta_flag(sta, WLAN_STA_AUTH); - set_sta_flag(sta, WLAN_STA_ASSOC); + sta_info_move_state(sta, IEEE80211_STA_AUTH); + sta_info_move_state(sta, IEEE80211_STA_ASSOC); if (!(ifmgd->flags & IEEE80211_STA_CONTROL_PORT)) - set_sta_flag(sta, WLAN_STA_AUTHORIZED); + sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED); rates = 0; basic_rates = 0; @@ -2371,6 +2371,7 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) (unsigned long) sdata); ifmgd->flags = 0; + ifmgd->powersave = sdata->wdev.ps; mutex_init(&ifmgd->mtx); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 7d226417ef46..2be5b7d69ad7 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2775,8 +2775,8 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx, rate_idx = 0; /* TODO: HT rates */ else rate_idx = status->rate_idx; - rx->sta = ieee80211_ibss_add_sta(sdata, bssid, - hdr->addr2, BIT(rate_idx), GFP_ATOMIC); + ieee80211_ibss_rx_no_sta(sdata, bssid, hdr->addr2, + BIT(rate_idx)); } break; case NL80211_IFTYPE_MESH_POINT: diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index f98235262006..2db01e9541e7 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -62,14 +62,14 @@ * freed before they are done using it. */ -/* Caller must hold local->sta_lock */ +/* Caller must hold local->sta_mtx */ static int sta_info_hash_del(struct ieee80211_local *local, struct sta_info *sta) { struct sta_info *s; s = rcu_dereference_protected(local->sta_hash[STA_HASH(sta->sta.addr)], - lockdep_is_held(&local->sta_lock)); + lockdep_is_held(&local->sta_mtx)); if (!s) return -ENOENT; if (s == sta) { @@ -81,7 +81,7 @@ static int sta_info_hash_del(struct ieee80211_local *local, while (rcu_access_pointer(s->hnext) && rcu_access_pointer(s->hnext) != sta) s = rcu_dereference_protected(s->hnext, - lockdep_is_held(&local->sta_lock)); + lockdep_is_held(&local->sta_mtx)); if (rcu_access_pointer(s->hnext)) { RCU_INIT_POINTER(s->hnext, sta->hnext); return 0; @@ -98,14 +98,12 @@ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, struct sta_info *sta; sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)], - lockdep_is_held(&local->sta_lock) || lockdep_is_held(&local->sta_mtx)); while (sta) { if (sta->sdata == sdata && !sta->dummy && memcmp(sta->sta.addr, addr, ETH_ALEN) == 0) break; sta = rcu_dereference_check(sta->hnext, - lockdep_is_held(&local->sta_lock) || lockdep_is_held(&local->sta_mtx)); } return sta; @@ -119,14 +117,12 @@ struct sta_info *sta_info_get_rx(struct ieee80211_sub_if_data *sdata, struct sta_info *sta; sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)], - lockdep_is_held(&local->sta_lock) || lockdep_is_held(&local->sta_mtx)); while (sta) { if (sta->sdata == sdata && memcmp(sta->sta.addr, addr, ETH_ALEN) == 0) break; sta = rcu_dereference_check(sta->hnext, - lockdep_is_held(&local->sta_lock) || lockdep_is_held(&local->sta_mtx)); } return sta; @@ -143,7 +139,6 @@ struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata, struct sta_info *sta; sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)], - lockdep_is_held(&local->sta_lock) || lockdep_is_held(&local->sta_mtx)); while (sta) { if ((sta->sdata == sdata || @@ -152,7 +147,6 @@ struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata, memcmp(sta->sta.addr, addr, ETH_ALEN) == 0) break; sta = rcu_dereference_check(sta->hnext, - lockdep_is_held(&local->sta_lock) || lockdep_is_held(&local->sta_mtx)); } return sta; @@ -169,7 +163,6 @@ struct sta_info *sta_info_get_bss_rx(struct ieee80211_sub_if_data *sdata, struct sta_info *sta; sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)], - lockdep_is_held(&local->sta_lock) || lockdep_is_held(&local->sta_mtx)); while (sta) { if ((sta->sdata == sdata || @@ -177,7 +170,6 @@ struct sta_info *sta_info_get_bss_rx(struct ieee80211_sub_if_data *sdata, memcmp(sta->sta.addr, addr, ETH_ALEN) == 0) break; sta = rcu_dereference_check(sta->hnext, - lockdep_is_held(&local->sta_lock) || lockdep_is_held(&local->sta_mtx)); } return sta; @@ -204,16 +196,17 @@ struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata, } /** - * __sta_info_free - internal STA free helper + * sta_info_free - free STA * * @local: pointer to the global information * @sta: STA info to free * * This function must undo everything done by sta_info_alloc() - * that may happen before sta_info_insert(). + * that may happen before sta_info_insert(). It may only be + * called when sta_info_insert() has not been attempted (and + * if that fails, the station is freed anyway.) */ -static void __sta_info_free(struct ieee80211_local *local, - struct sta_info *sta) +void sta_info_free(struct ieee80211_local *local, struct sta_info *sta) { if (sta->rate_ctrl) { rate_control_free_sta(sta); @@ -227,10 +220,11 @@ static void __sta_info_free(struct ieee80211_local *local, kfree(sta); } -/* Caller must hold local->sta_lock */ +/* Caller must hold local->sta_mtx */ static void sta_info_hash_add(struct ieee80211_local *local, struct sta_info *sta) { + lockdep_assert_held(&local->sta_mtx); sta->hnext = local->sta_hash[STA_HASH(sta->sta.addr)]; RCU_INIT_POINTER(local->sta_hash[STA_HASH(sta->sta.addr)], sta); } @@ -280,7 +274,7 @@ static int sta_prepare_rate_control(struct ieee80211_local *local, } struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, - u8 *addr, gfp_t gfp) + const u8 *addr, gfp_t gfp) { struct ieee80211_local *local = sdata->local; struct sta_info *sta; @@ -338,98 +332,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, return sta; } -static int sta_info_finish_insert(struct sta_info *sta, - bool async, bool dummy_reinsert) -{ - struct ieee80211_local *local = sta->local; - struct ieee80211_sub_if_data *sdata = sta->sdata; - struct station_info sinfo; - unsigned long flags; - int err = 0; - - lockdep_assert_held(&local->sta_mtx); - - if (!sta->dummy || dummy_reinsert) { - /* notify driver */ - err = drv_sta_add(local, sdata, &sta->sta); - if (err) { - if (!async) - return err; - printk(KERN_DEBUG "%s: failed to add IBSS STA %pM to " - "driver (%d) - keeping it anyway.\n", - sdata->name, sta->sta.addr, err); - } else { - sta->uploaded = true; -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - if (async) - wiphy_debug(local->hw.wiphy, - "Finished adding IBSS STA %pM\n", - sta->sta.addr); -#endif - } - - sdata = sta->sdata; - } - - if (!dummy_reinsert) { - if (!async) { - local->num_sta++; - local->sta_generation++; - smp_mb(); - - /* make the station visible */ - spin_lock_irqsave(&local->sta_lock, flags); - sta_info_hash_add(local, sta); - spin_unlock_irqrestore(&local->sta_lock, flags); - } - - list_add(&sta->list, &local->sta_list); - } else { - sta->dummy = false; - } - - if (!sta->dummy) { - ieee80211_sta_debugfs_add(sta); - rate_control_add_sta_debugfs(sta); - - memset(&sinfo, 0, sizeof(sinfo)); - sinfo.filled = 0; - sinfo.generation = local->sta_generation; - cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL); - } - - return 0; -} - -static void sta_info_finish_pending(struct ieee80211_local *local) -{ - struct sta_info *sta; - unsigned long flags; - - spin_lock_irqsave(&local->sta_lock, flags); - while (!list_empty(&local->sta_pending_list)) { - sta = list_first_entry(&local->sta_pending_list, - struct sta_info, list); - list_del(&sta->list); - spin_unlock_irqrestore(&local->sta_lock, flags); - - sta_info_finish_insert(sta, true, false); - - spin_lock_irqsave(&local->sta_lock, flags); - } - spin_unlock_irqrestore(&local->sta_lock, flags); -} - -static void sta_info_finish_work(struct work_struct *work) -{ - struct ieee80211_local *local = - container_of(work, struct ieee80211_local, sta_finish_work); - - mutex_lock(&local->sta_mtx); - sta_info_finish_pending(local); - mutex_unlock(&local->sta_mtx); -} - static int sta_info_insert_check(struct sta_info *sta) { struct ieee80211_sub_if_data *sdata = sta->sdata; @@ -449,50 +351,15 @@ static int sta_info_insert_check(struct sta_info *sta) return 0; } -static int sta_info_insert_ibss(struct sta_info *sta) __acquires(RCU) -{ - struct ieee80211_local *local = sta->local; - struct ieee80211_sub_if_data *sdata = sta->sdata; - unsigned long flags; - - spin_lock_irqsave(&local->sta_lock, flags); - /* check if STA exists already */ - if (sta_info_get_bss_rx(sdata, sta->sta.addr)) { - spin_unlock_irqrestore(&local->sta_lock, flags); - rcu_read_lock(); - return -EEXIST; - } - - local->num_sta++; - local->sta_generation++; - smp_mb(); - sta_info_hash_add(local, sta); - - list_add_tail(&sta->list, &local->sta_pending_list); - - rcu_read_lock(); - spin_unlock_irqrestore(&local->sta_lock, flags); - -#ifdef CONFIG_MAC80211_VERBOSE_DEBUG - wiphy_debug(local->hw.wiphy, "Added IBSS STA %pM\n", - sta->sta.addr); -#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */ - - ieee80211_queue_work(&local->hw, &local->sta_finish_work); - - return 0; -} - /* * should be called with sta_mtx locked * this function replaces the mutex lock * with a RCU lock */ -static int sta_info_insert_non_ibss(struct sta_info *sta) __acquires(RCU) +static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) { struct ieee80211_local *local = sta->local; struct ieee80211_sub_if_data *sdata = sta->sdata; - unsigned long flags; struct sta_info *exist_sta; bool dummy_reinsert = false; int err = 0; @@ -500,19 +367,8 @@ static int sta_info_insert_non_ibss(struct sta_info *sta) __acquires(RCU) lockdep_assert_held(&local->sta_mtx); /* - * On first glance, this will look racy, because the code - * in this function, which inserts a station with sleeping, - * unlocks the sta_lock between checking existence in the - * hash table and inserting into it. - * - * However, it is not racy against itself because it keeps - * the mutex locked. - */ - - spin_lock_irqsave(&local->sta_lock, flags); - /* * check if STA exists already. - * only accept a scenario of a second call to sta_info_insert_non_ibss + * only accept a scenario of a second call to sta_info_insert_finish * with a dummy station entry that was inserted earlier * in that case - assume that the dummy station flag should * be removed. @@ -522,20 +378,47 @@ static int sta_info_insert_non_ibss(struct sta_info *sta) __acquires(RCU) if (exist_sta == sta && sta->dummy) { dummy_reinsert = true; } else { - spin_unlock_irqrestore(&local->sta_lock, flags); - mutex_unlock(&local->sta_mtx); - rcu_read_lock(); - return -EEXIST; + err = -EEXIST; + goto out_err; } } - spin_unlock_irqrestore(&local->sta_lock, flags); + if (!sta->dummy || dummy_reinsert) { + /* notify driver */ + err = drv_sta_add(local, sdata, &sta->sta); + if (err) { + if (sdata->vif.type != NL80211_IFTYPE_ADHOC) + goto out_err; + printk(KERN_DEBUG "%s: failed to add IBSS STA %pM to " + "driver (%d) - keeping it anyway.\n", + sdata->name, sta->sta.addr, err); + } else + sta->uploaded = true; + } - err = sta_info_finish_insert(sta, false, dummy_reinsert); - if (err) { - mutex_unlock(&local->sta_mtx); - rcu_read_lock(); - return err; + if (!dummy_reinsert) { + local->num_sta++; + local->sta_generation++; + smp_mb(); + + /* make the station visible */ + sta_info_hash_add(local, sta); + + list_add(&sta->list, &local->sta_list); + } else { + sta->dummy = false; + } + + if (!sta->dummy) { + struct station_info sinfo; + + ieee80211_sta_debugfs_add(sta); + rate_control_add_sta_debugfs(sta); + + memset(&sinfo, 0, sizeof(sinfo)); + sinfo.filled = 0; + sinfo.generation = local->sta_generation; + cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL); } #ifdef CONFIG_MAC80211_VERBOSE_DEBUG @@ -551,54 +434,35 @@ static int sta_info_insert_non_ibss(struct sta_info *sta) __acquires(RCU) mesh_accept_plinks_update(sdata); return 0; + out_err: + mutex_unlock(&local->sta_mtx); + rcu_read_lock(); + return err; } int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU) { struct ieee80211_local *local = sta->local; - struct ieee80211_sub_if_data *sdata = sta->sdata; int err = 0; + might_sleep(); + err = sta_info_insert_check(sta); if (err) { rcu_read_lock(); goto out_free; } - /* - * In ad-hoc mode, we sometimes need to insert stations - * from tasklet context from the RX path. To avoid races, - * always do so in that case -- see the comment below. - */ - if (sdata->vif.type == NL80211_IFTYPE_ADHOC) { - err = sta_info_insert_ibss(sta); - if (err) - goto out_free; - - return 0; - } - - /* - * It might seem that the function called below is in race against - * the function call above that atomically inserts the station... That, - * however, is not true because the above code can only - * be invoked for IBSS interfaces, and the below code will - * not be -- and the two do not race against each other as - * the hash table also keys off the interface. - */ - - might_sleep(); - mutex_lock(&local->sta_mtx); - err = sta_info_insert_non_ibss(sta); + err = sta_info_insert_finish(sta); if (err) goto out_free; return 0; out_free: BUG_ON(!err); - __sta_info_free(local, sta); + sta_info_free(local, sta); return err; } @@ -625,7 +489,7 @@ int sta_info_reinsert(struct sta_info *sta) might_sleep(); - err = sta_info_insert_non_ibss(sta); + err = sta_info_insert_finish(sta); rcu_read_unlock(); return err; } @@ -712,7 +576,7 @@ void sta_info_recalc_tim(struct sta_info *sta) } done: - spin_lock_irqsave(&local->sta_lock, flags); + spin_lock_irqsave(&local->tim_lock, flags); if (indicate_tim) __bss_tim_set(bss, sta->sta.aid); @@ -725,7 +589,7 @@ void sta_info_recalc_tim(struct sta_info *sta) local->tim_in_locked_section = false; } - spin_unlock_irqrestore(&local->sta_lock, flags); + spin_unlock_irqrestore(&local->tim_lock, flags); } static bool sta_info_buffer_expired(struct sta_info *sta, struct sk_buff *skb) @@ -849,8 +713,8 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) { struct ieee80211_local *local; struct ieee80211_sub_if_data *sdata; - unsigned long flags; int ret, i, ac; + struct tid_ampdu_tx *tid_tx; might_sleep(); @@ -869,15 +733,12 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) set_sta_flag(sta, WLAN_STA_BLOCK_BA); ieee80211_sta_tear_down_BA_sessions(sta, true); - spin_lock_irqsave(&local->sta_lock, flags); ret = sta_info_hash_del(local, sta); - /* this might still be the pending list ... which is fine */ - if (!ret) - list_del(&sta->list); - spin_unlock_irqrestore(&local->sta_lock, flags); if (ret) return ret; + list_del(&sta->list); + mutex_lock(&local->key_mtx); for (i = 0; i < NUM_DEFAULT_KEYS; i++) __ieee80211_key_free(key_mtx_dereference(local, sta->gtk[i])); @@ -904,6 +765,9 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) RCU_INIT_POINTER(sdata->u.vlan.sta, NULL); + while (sta->sta_state > IEEE80211_STA_NONE) + sta_info_move_state(sta, sta->sta_state - 1); + if (sta->uploaded) { if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) sdata = container_of(sdata->bss, @@ -949,7 +813,31 @@ static int __must_check __sta_info_destroy(struct sta_info *sta) } #endif - __sta_info_free(local, sta); + /* There could be some memory leaks because of ampdu tx pending queue + * not being freed before destroying the station info. + * + * Make sure that such queues are purged before freeing the station + * info. + * TODO: We have to somehow postpone the full destruction + * until the aggregation stop completes. Refer + * http://thread.gmane.org/gmane.linux.kernel.wireless.general/81936 + */ + for (i = 0; i < STA_TID_NUM; i++) { + if (!sta->ampdu_mlme.tid_tx[i]) + continue; + tid_tx = sta->ampdu_mlme.tid_tx[i]; + if (skb_queue_len(&tid_tx->pending)) { +#ifdef CONFIG_MAC80211_HT_DEBUG + wiphy_debug(local->hw.wiphy, "TX A-MPDU purging %d " + "packets for tid=%d\n", + skb_queue_len(&tid_tx->pending), i); +#endif /* CONFIG_MAC80211_HT_DEBUG */ + __skb_queue_purge(&tid_tx->pending); + } + kfree_rcu(tid_tx, rcu_head); + } + + sta_info_free(local, sta); return 0; } @@ -1005,11 +893,9 @@ static void sta_info_cleanup(unsigned long data) void sta_info_init(struct ieee80211_local *local) { - spin_lock_init(&local->sta_lock); + spin_lock_init(&local->tim_lock); mutex_init(&local->sta_mtx); INIT_LIST_HEAD(&local->sta_list); - INIT_LIST_HEAD(&local->sta_pending_list); - INIT_WORK(&local->sta_finish_work, sta_info_finish_work); setup_timer(&local->sta_cleanup, sta_info_cleanup, (unsigned long)local); @@ -1038,9 +924,6 @@ int sta_info_flush(struct ieee80211_local *local, might_sleep(); mutex_lock(&local->sta_mtx); - - sta_info_finish_pending(local); - list_for_each_entry_safe(sta, tmp, &local->sta_list, list) { if (!sdata || sdata == sta->sdata) WARN_ON(__sta_info_destroy(sta)); @@ -1513,3 +1396,56 @@ void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta, sta_info_recalc_tim(sta); } EXPORT_SYMBOL(ieee80211_sta_set_buffered); + +int sta_info_move_state_checked(struct sta_info *sta, + enum ieee80211_sta_state new_state) +{ + might_sleep(); + + if (sta->sta_state == new_state) + return 0; + + switch (new_state) { + case IEEE80211_STA_NONE: + if (sta->sta_state == IEEE80211_STA_AUTH) + clear_bit(WLAN_STA_AUTH, &sta->_flags); + else + return -EINVAL; + break; + case IEEE80211_STA_AUTH: + if (sta->sta_state == IEEE80211_STA_NONE) + set_bit(WLAN_STA_AUTH, &sta->_flags); + else if (sta->sta_state == IEEE80211_STA_ASSOC) + clear_bit(WLAN_STA_ASSOC, &sta->_flags); + else + return -EINVAL; + break; + case IEEE80211_STA_ASSOC: + if (sta->sta_state == IEEE80211_STA_AUTH) { + set_bit(WLAN_STA_ASSOC, &sta->_flags); + } else if (sta->sta_state == IEEE80211_STA_AUTHORIZED) { + if (sta->sdata->vif.type == NL80211_IFTYPE_AP) + atomic_dec(&sta->sdata->u.ap.num_sta_authorized); + clear_bit(WLAN_STA_AUTHORIZED, &sta->_flags); + } else + return -EINVAL; + break; + case IEEE80211_STA_AUTHORIZED: + if (sta->sta_state == IEEE80211_STA_ASSOC) { + if (sta->sdata->vif.type == NL80211_IFTYPE_AP) + atomic_inc(&sta->sdata->u.ap.num_sta_authorized); + set_bit(WLAN_STA_AUTHORIZED, &sta->_flags); + } else + return -EINVAL; + break; + default: + WARN(1, "invalid state %d", new_state); + return -EINVAL; + } + + printk(KERN_DEBUG "%s: moving STA %pM to state %d\n", + sta->sdata->name, sta->sta.addr, new_state); + sta->sta_state = new_state; + + return 0; +} diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 1a14fab4bc9a..15b3bb7d8629 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -73,6 +73,14 @@ enum ieee80211_sta_info_flags { WLAN_STA_4ADDR_EVENT, }; +enum ieee80211_sta_state { + /* NOTE: These need to be ordered correctly! */ + IEEE80211_STA_NONE, + IEEE80211_STA_AUTH, + IEEE80211_STA_ASSOC, + IEEE80211_STA_AUTHORIZED, +}; + #define STA_TID_NUM 16 #define ADDBA_RESP_INTERVAL HZ #define HT_AGG_MAX_RETRIES 0x3 @@ -262,6 +270,7 @@ struct sta_ampdu_mlme { * @dummy: indicate a dummy station created for receiving * EAP frames before association * @sta: station information we share with the driver + * @sta_state: duplicates information about station state (for debug) */ struct sta_info { /* General information, mostly static */ @@ -283,6 +292,8 @@ struct sta_info { bool uploaded; + enum ieee80211_sta_state sta_state; + /* use the accessors defined below */ unsigned long _flags; @@ -371,12 +382,18 @@ static inline enum nl80211_plink_state sta_plink_state(struct sta_info *sta) static inline void set_sta_flag(struct sta_info *sta, enum ieee80211_sta_info_flags flag) { + WARN_ON(flag == WLAN_STA_AUTH || + flag == WLAN_STA_ASSOC || + flag == WLAN_STA_AUTHORIZED); set_bit(flag, &sta->_flags); } static inline void clear_sta_flag(struct sta_info *sta, enum ieee80211_sta_info_flags flag) { + WARN_ON(flag == WLAN_STA_AUTH || + flag == WLAN_STA_ASSOC || + flag == WLAN_STA_AUTHORIZED); clear_bit(flag, &sta->_flags); } @@ -389,15 +406,32 @@ static inline int test_sta_flag(struct sta_info *sta, static inline int test_and_clear_sta_flag(struct sta_info *sta, enum ieee80211_sta_info_flags flag) { + WARN_ON(flag == WLAN_STA_AUTH || + flag == WLAN_STA_ASSOC || + flag == WLAN_STA_AUTHORIZED); return test_and_clear_bit(flag, &sta->_flags); } static inline int test_and_set_sta_flag(struct sta_info *sta, enum ieee80211_sta_info_flags flag) { + WARN_ON(flag == WLAN_STA_AUTH || + flag == WLAN_STA_ASSOC || + flag == WLAN_STA_AUTHORIZED); return test_and_set_bit(flag, &sta->_flags); } +int sta_info_move_state_checked(struct sta_info *sta, + enum ieee80211_sta_state new_state); + +static inline void sta_info_move_state(struct sta_info *sta, + enum ieee80211_sta_state new_state) +{ + int ret = sta_info_move_state_checked(sta, new_state); + WARN_ON_ONCE(ret); +} + + void ieee80211_assign_tid_tx(struct sta_info *sta, int tid, struct tid_ampdu_tx *tid_tx); @@ -488,7 +522,10 @@ struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata, * until sta_info_insert(). */ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, - u8 *addr, gfp_t gfp); + const u8 *addr, gfp_t gfp); + +void sta_info_free(struct ieee80211_local *local, struct sta_info *sta); + /* * Insert STA info into hash table/list, returns zero or a * -EEXIST if (if the same MAC address is already present). diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 46222ce0e5b1..30c265c98f73 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -340,7 +340,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - u16 frag, type; __le16 fc; struct ieee80211_supported_band *sband; struct ieee80211_sub_if_data *sdata; @@ -476,12 +475,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) * Fragments are passed to low-level drivers as separate skbs, so these * are actually fragments, not frames. Update frame counters only for * the first fragment of the frame. */ - - frag = le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG; - type = le16_to_cpu(hdr->frame_control) & IEEE80211_FCTL_FTYPE; - if (info->flags & IEEE80211_TX_STAT_ACK) { - if (frag == 0) { + if (ieee80211_is_first_frag(hdr->seq_ctrl)) { local->dot11TransmittedFrameCount++; if (is_multicast_ether_addr(hdr->addr1)) local->dot11MulticastTransmittedFrameCount++; @@ -496,11 +491,11 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) * with a multicast address in the address 1 field of type Data * or Management. */ if (!is_multicast_ether_addr(hdr->addr1) || - type == IEEE80211_FTYPE_DATA || - type == IEEE80211_FTYPE_MGMT) + ieee80211_is_data(fc) || + ieee80211_is_mgmt(fc)) local->dot11TransmittedFragmentCount++; } else { - if (frag == 0) + if (ieee80211_is_first_frag(hdr->seq_ctrl)) local->dot11FailedCount++; } @@ -572,7 +567,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) /* Need to make a copy before skb->cb gets cleared */ send_to_cooked = !!(info->flags & IEEE80211_TX_CTL_INJECTED) || - (type != IEEE80211_FTYPE_DATA); + !(ieee80211_is_data(fc)); /* * This is a bit racy but we can avoid a lot of work diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index e74652d38245..edcd1c7ab83f 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -295,7 +295,6 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx) if (likely(tx->flags & IEEE80211_TX_UNICAST)) { if (unlikely(!assoc && - tx->sdata->vif.type != NL80211_IFTYPE_ADHOC && ieee80211_is_data(hdr->frame_control))) { #ifdef CONFIG_MAC80211_VERBOSE_DEBUG printk(KERN_DEBUG "%s: dropped data frame to not " @@ -305,17 +304,14 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx) I802_DEBUG_INC(tx->local->tx_handlers_drop_not_assoc); return TX_DROP; } - } else { - if (unlikely(ieee80211_is_data(hdr->frame_control) && - tx->local->num_sta == 0 && - tx->sdata->vif.type != NL80211_IFTYPE_ADHOC)) { - /* - * No associated STAs - no need to send multicast - * frames. - */ - return TX_DROP; - } - return TX_CONTINUE; + } else if (unlikely(tx->sdata->vif.type == NL80211_IFTYPE_AP && + ieee80211_is_data(hdr->frame_control) && + !atomic_read(&tx->sdata->u.ap.num_sta_authorized))) { + /* + * No associated STAs - no need to send multicast + * frames. + */ + return TX_DROP; } return TX_CONTINUE; @@ -2337,9 +2333,9 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, } else { unsigned long flags; - spin_lock_irqsave(&local->sta_lock, flags); + spin_lock_irqsave(&local->tim_lock, flags); ieee80211_beacon_add_tim(ap, skb, beacon); - spin_unlock_irqrestore(&local->sta_lock, flags); + spin_unlock_irqrestore(&local->tim_lock, flags); } if (tim_offset) @@ -2700,15 +2696,15 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ieee80211_get_buffered_bc); -void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) +void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, int tid) { skb_set_mac_header(skb, 0); skb_set_network_header(skb, 0); skb_set_transport_header(skb, 0); - /* Send all internal mgmt frames on VO. Accordingly set TID to 7. */ - skb_set_queue_mapping(skb, IEEE80211_AC_VO); - skb->priority = 7; + skb_set_queue_mapping(skb, ieee802_1d_to_ac[tid]); + skb->priority = tid; /* * The other path calling ieee80211_xmit is from the tasklet, diff --git a/net/mac80211/util.c b/net/mac80211/util.c index ac7ea2949de0..eb1a5f737a9c 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1234,7 +1234,8 @@ int ieee80211_reconfig(struct ieee80211_local *local) switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: - changed |= BSS_CHANGED_ASSOC; + changed |= BSS_CHANGED_ASSOC | + BSS_CHANGED_ARP_FILTER; mutex_lock(&sdata->u.mgd.mtx); ieee80211_bss_info_change_notify(sdata, changed); mutex_unlock(&sdata->u.mgd.mtx); |