From 9d6b106b54e02ad6a30c8bd949c8ea581a47d92b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 24 Feb 2015 00:28:18 +0100 Subject: mac80211: don't look up stations for multicast addresses Since multicast addresses don't exist as stations, don't attempt to look them up in the hashtable on TX. Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/mac80211') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 88a18ffe2975..0e56ac5729ef 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1172,7 +1172,7 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, tx->sdata->control_port_protocol == tx->skb->protocol) { tx->sta = sta_info_get_bss(sdata, hdr->addr1); } - if (!tx->sta) + if (!tx->sta && !is_multicast_ether_addr(hdr->addr1)) tx->sta = sta_info_get(sdata, hdr->addr1); if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) && -- cgit v1.2.3 From 7d9bb2f0653c7830e676f1012051fe9349ce2669 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 13 Feb 2015 21:31:07 +0100 Subject: mac80211: iterate using station list in AP SMPS When changing AP SMPS, we need to look up all the stations for this interface, so there's no reason to iterate over hash chains rather than doing the simpler iteration over the station list. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 69 ++++++++++++++++++++++++------------------------------ 1 file changed, 30 insertions(+), 39 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index dd4ff36c557a..06557e4f9588 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2273,7 +2273,6 @@ int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata, { struct sta_info *sta; enum ieee80211_smps_mode old_req; - int i; if (WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP)) return -EINVAL; @@ -2301,48 +2300,40 @@ int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata, smps_mode, atomic_read(&sdata->u.ap.num_mcast_sta)); mutex_lock(&sdata->local->sta_mtx); - for (i = 0; i < STA_HASH_SIZE; i++) { - for (sta = rcu_dereference_protected(sdata->local->sta_hash[i], - lockdep_is_held(&sdata->local->sta_mtx)); - sta; - sta = rcu_dereference_protected(sta->hnext, - lockdep_is_held(&sdata->local->sta_mtx))) { - /* - * Only stations associated to our AP and - * associated VLANs - */ - if (sta->sdata->bss != &sdata->u.ap) - continue; + list_for_each_entry(sta, &sdata->local->sta_list, list) { + /* + * Only stations associated to our AP and + * associated VLANs + */ + if (sta->sdata->bss != &sdata->u.ap) + continue; - /* This station doesn't support MIMO - skip it */ - if (sta_info_tx_streams(sta) == 1) - continue; + /* This station doesn't support MIMO - skip it */ + if (sta_info_tx_streams(sta) == 1) + continue; - /* - * Don't wake up a STA just to send the action frame - * unless we are getting more restrictive. - */ - if (test_sta_flag(sta, WLAN_STA_PS_STA) && - !ieee80211_smps_is_restrictive(sta->known_smps_mode, - smps_mode)) { - ht_dbg(sdata, - "Won't send SMPS to sleeping STA %pM\n", - sta->sta.addr); - continue; - } + /* + * Don't wake up a STA just to send the action frame + * unless we are getting more restrictive. + */ + if (test_sta_flag(sta, WLAN_STA_PS_STA) && + !ieee80211_smps_is_restrictive(sta->known_smps_mode, + smps_mode)) { + ht_dbg(sdata, "Won't send SMPS to sleeping STA %pM\n", + sta->sta.addr); + continue; + } - /* - * If the STA is not authorized, wait until it gets - * authorized and the action frame will be sent then. - */ - if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED)) - continue; + /* + * If the STA is not authorized, wait until it gets + * authorized and the action frame will be sent then. + */ + if (!test_sta_flag(sta, WLAN_STA_AUTHORIZED)) + continue; - ht_dbg(sdata, "Sending SMPS to %pM\n", sta->sta.addr); - ieee80211_send_smps_action(sdata, smps_mode, - sta->sta.addr, - sdata->vif.bss_conf.bssid); - } + ht_dbg(sdata, "Sending SMPS to %pM\n", sta->sta.addr); + ieee80211_send_smps_action(sdata, smps_mode, sta->sta.addr, + sdata->vif.bss_conf.bssid); } mutex_unlock(&sdata->local->sta_mtx); -- cgit v1.2.3 From 31f909a2c0abfc1a1a76b2981d28ac85d33210e7 Mon Sep 17 00:00:00 2001 From: Masashi Honma Date: Tue, 24 Feb 2015 22:42:16 +0900 Subject: nl/mac80211: allow zero plink timeout to disable STA expiration Both wpa_supplicant and mac80211 have and inactivity timer. By default wpa_supplicant will be timed out in 5 minutes and mac80211's it is 30 minutes. If wpa_supplicant uses a longer timer than mac80211, it will get unexpected disconnection by mac80211. Using 0xffffffff instead as the configured value could solve this w/o changing the code, but due to integer overflow in the expression used this doesn't work. The expression is: (current jiffies) > (frame Rx jiffies + NL80211_MESHCONF_PLINK_TIMEOUT * 250) On 32bit system, the right side would overflow and be a very small value if NL80211_MESHCONF_PLINK_TIMEOUT is sufficiently large, causing unexpectedly early disconnections. Instead allow disabling the inactivity timer to avoid this situation, by passing the (previously invalid and useless) value 0. Signed-off-by: Masashi Honma [reword/rewrap commit log] Signed-off-by: Johannes Berg --- net/mac80211/mesh.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net/mac80211') diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 0c8b2a77d312..acf441ff9f4a 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -574,7 +574,8 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata) struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; u32 changed; - ieee80211_sta_expire(sdata, ifmsh->mshcfg.plink_timeout * HZ); + if (ifmsh->mshcfg.plink_timeout > 0) + ieee80211_sta_expire(sdata, ifmsh->mshcfg.plink_timeout * HZ); mesh_path_expire(sdata); changed = mesh_accept_plinks_update(sdata); -- cgit v1.2.3 From abfbc3af57b1b92ff976ce7f1c776c169d14ed8a Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 25 Feb 2015 10:03:25 +0100 Subject: mac80211: remove TX latency measurement code Revert commit ad38bfc916da ("mac80211: Tx frame latency statistics") (along with some follow-up fixes). This code turned out not to be as useful in the current form as we thought, and we've internally hacked it up more, but that's not very suitable for upstream (for now), and we might just do that with tracing instead. Therefore, for now at least, remove this code. We might also need to use the skb->tstamp field for the TCP performance issue, which is more important than the debugging. Signed-off-by: Johannes Berg --- net/mac80211/debugfs.c | 168 --------------------------------------------- net/mac80211/debugfs_sta.c | 134 ------------------------------------ net/mac80211/ieee80211_i.h | 24 ------- net/mac80211/main.c | 2 - net/mac80211/sta_info.c | 54 ++------------- net/mac80211/sta_info.h | 22 ------ net/mac80211/status.c | 74 -------------------- net/mac80211/tx.c | 22 ------ 8 files changed, 5 insertions(+), 495 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index eeb0bbd69d98..74830ce25e74 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -18,172 +18,6 @@ #define DEBUGFS_FORMAT_BUFFER_SIZE 100 -#define TX_LATENCY_BIN_DELIMTER_C ',' -#define TX_LATENCY_BIN_DELIMTER_S "," -#define TX_LATENCY_BINS_DISABLED "enable(bins disabled)\n" -#define TX_LATENCY_DISABLED "disable\n" - - -/* - * Display if Tx latency statistics & bins are enabled/disabled - */ -static ssize_t sta_tx_latency_stat_read(struct file *file, - char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct ieee80211_local *local = file->private_data; - struct ieee80211_tx_latency_bin_ranges *tx_latency; - char *buf; - int bufsz, i, ret; - int pos = 0; - - rcu_read_lock(); - - tx_latency = rcu_dereference(local->tx_latency); - - if (tx_latency && tx_latency->n_ranges) { - bufsz = tx_latency->n_ranges * 15; - buf = kzalloc(bufsz, GFP_ATOMIC); - if (!buf) - goto err; - - for (i = 0; i < tx_latency->n_ranges; i++) - pos += scnprintf(buf + pos, bufsz - pos, "%d,", - tx_latency->ranges[i]); - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - } else if (tx_latency) { - bufsz = sizeof(TX_LATENCY_BINS_DISABLED) + 1; - buf = kzalloc(bufsz, GFP_ATOMIC); - if (!buf) - goto err; - - pos += scnprintf(buf + pos, bufsz - pos, "%s\n", - TX_LATENCY_BINS_DISABLED); - } else { - bufsz = sizeof(TX_LATENCY_DISABLED) + 1; - buf = kzalloc(bufsz, GFP_ATOMIC); - if (!buf) - goto err; - - pos += scnprintf(buf + pos, bufsz - pos, "%s\n", - TX_LATENCY_DISABLED); - } - - rcu_read_unlock(); - - ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); - kfree(buf); - - return ret; -err: - rcu_read_unlock(); - return -ENOMEM; -} - -/* - * Receive input from user regarding Tx latency statistics - * The input should indicate if Tx latency statistics and bins are - * enabled/disabled. - * If bins are enabled input should indicate the amount of different bins and - * their ranges. Each bin will count how many Tx frames transmitted within the - * appropriate latency. - * Legal input is: - * a) "enable(bins disabled)" - to enable only general statistics - * b) "a,b,c,d,...z" - to enable general statistics and bins, where all are - * numbers and a < b < c < d.. < z - * c) "disable" - disable all statistics - * NOTE: must configure Tx latency statistics bins before stations connected. - */ - -static ssize_t sta_tx_latency_stat_write(struct file *file, - const char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct ieee80211_local *local = file->private_data; - char buf[128] = {}; - char *bins = buf; - char *token; - int buf_size, i, alloc_size; - int prev_bin = 0; - int n_ranges = 0; - int ret = count; - struct ieee80211_tx_latency_bin_ranges *tx_latency; - - if (sizeof(buf) <= count) - return -EINVAL; - buf_size = count; - if (copy_from_user(buf, userbuf, buf_size)) - return -EFAULT; - - mutex_lock(&local->sta_mtx); - - /* cannot change config once we have stations */ - if (local->num_sta) - goto unlock; - - tx_latency = - rcu_dereference_protected(local->tx_latency, - lockdep_is_held(&local->sta_mtx)); - - /* disable Tx statistics */ - if (!strcmp(buf, TX_LATENCY_DISABLED)) { - if (!tx_latency) - goto unlock; - RCU_INIT_POINTER(local->tx_latency, NULL); - synchronize_rcu(); - kfree(tx_latency); - goto unlock; - } - - /* Tx latency already enabled */ - if (tx_latency) - goto unlock; - - if (strcmp(TX_LATENCY_BINS_DISABLED, buf)) { - /* check how many bins and between what ranges user requested */ - token = buf; - while (*token != '\0') { - if (*token == TX_LATENCY_BIN_DELIMTER_C) - n_ranges++; - token++; - } - n_ranges++; - } - - alloc_size = sizeof(struct ieee80211_tx_latency_bin_ranges) + - n_ranges * sizeof(u32); - tx_latency = kzalloc(alloc_size, GFP_ATOMIC); - if (!tx_latency) { - ret = -ENOMEM; - goto unlock; - } - tx_latency->n_ranges = n_ranges; - for (i = 0; i < n_ranges; i++) { /* setting bin ranges */ - token = strsep(&bins, TX_LATENCY_BIN_DELIMTER_S); - sscanf(token, "%d", &tx_latency->ranges[i]); - /* bins values should be in ascending order */ - if (prev_bin >= tx_latency->ranges[i]) { - ret = -EINVAL; - kfree(tx_latency); - goto unlock; - } - prev_bin = tx_latency->ranges[i]; - } - rcu_assign_pointer(local->tx_latency, tx_latency); - -unlock: - mutex_unlock(&local->sta_mtx); - - return ret; -} - -static const struct file_operations stats_tx_latency_ops = { - .write = sta_tx_latency_stat_write, - .read = sta_tx_latency_stat_read, - .open = simple_open, - .llseek = generic_file_llseek, -}; - int mac80211_format_buffer(char __user *userbuf, size_t count, loff_t *ppos, char *fmt, ...) { @@ -475,6 +309,4 @@ void debugfs_hw_add(struct ieee80211_local *local) DEBUGFS_DEVSTATS_ADD(dot11RTSFailureCount); DEBUGFS_DEVSTATS_ADD(dot11FCSErrorCount); DEBUGFS_DEVSTATS_ADD(dot11RTSSuccessCount); - - DEBUGFS_DEVSTATS_ADD(tx_latency); } diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 94c70091bbd7..252859e90e8a 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -39,13 +39,6 @@ static const struct file_operations sta_ ##name## _ops = { \ .llseek = generic_file_llseek, \ } -#define STA_OPS_W(name) \ -static const struct file_operations sta_ ##name## _ops = { \ - .write = sta_##name##_write, \ - .open = simple_open, \ - .llseek = generic_file_llseek, \ -} - #define STA_OPS_RW(name) \ static const struct file_operations sta_ ##name## _ops = { \ .read = sta_##name##_read, \ @@ -398,131 +391,6 @@ static ssize_t sta_last_rx_rate_read(struct file *file, char __user *userbuf, } STA_OPS(last_rx_rate); -static int -sta_tx_latency_stat_header(struct ieee80211_tx_latency_bin_ranges *tx_latency, - char *buf, int pos, int bufsz) -{ - int i; - int range_count = tx_latency->n_ranges; - u32 *bin_ranges = tx_latency->ranges; - - pos += scnprintf(buf + pos, bufsz - pos, - "Station\t\t\tTID\tMax\tAvg"); - if (range_count) { - pos += scnprintf(buf + pos, bufsz - pos, - "\t<=%d", bin_ranges[0]); - for (i = 0; i < range_count - 1; i++) - pos += scnprintf(buf + pos, bufsz - pos, "\t%d-%d", - bin_ranges[i], bin_ranges[i+1]); - pos += scnprintf(buf + pos, bufsz - pos, - "\t%d<", bin_ranges[range_count - 1]); - } - - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - - return pos; -} - -static int -sta_tx_latency_stat_table(struct ieee80211_tx_latency_bin_ranges *tx_lat_range, - struct ieee80211_tx_latency_stat *tx_lat, - char *buf, int pos, int bufsz, int tid) -{ - u32 avg = 0; - int j; - int bin_count = tx_lat->bin_count; - - pos += scnprintf(buf + pos, bufsz - pos, "\t\t\t%d", tid); - /* make sure you don't divide in 0 */ - if (tx_lat->counter) - avg = tx_lat->sum / tx_lat->counter; - - pos += scnprintf(buf + pos, bufsz - pos, "\t%d\t%d", - tx_lat->max, avg); - - if (tx_lat_range->n_ranges && tx_lat->bins) - for (j = 0; j < bin_count; j++) - pos += scnprintf(buf + pos, bufsz - pos, - "\t%d", tx_lat->bins[j]); - pos += scnprintf(buf + pos, bufsz - pos, "\n"); - - return pos; -} - -/* - * Output Tx latency statistics station && restart all statistics information - */ -static ssize_t sta_tx_latency_stat_read(struct file *file, - char __user *userbuf, - size_t count, loff_t *ppos) -{ - struct sta_info *sta = file->private_data; - struct ieee80211_local *local = sta->local; - struct ieee80211_tx_latency_bin_ranges *tx_latency; - char *buf; - int bufsz, ret, i; - int pos = 0; - - bufsz = 20 * IEEE80211_NUM_TIDS * - sizeof(struct ieee80211_tx_latency_stat); - buf = kzalloc(bufsz, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - rcu_read_lock(); - - tx_latency = rcu_dereference(local->tx_latency); - - if (!sta->tx_lat) { - pos += scnprintf(buf + pos, bufsz - pos, - "Tx latency statistics are not enabled\n"); - goto unlock; - } - - pos = sta_tx_latency_stat_header(tx_latency, buf, pos, bufsz); - - pos += scnprintf(buf + pos, bufsz - pos, "%pM\n", sta->sta.addr); - for (i = 0; i < IEEE80211_NUM_TIDS; i++) - pos = sta_tx_latency_stat_table(tx_latency, &sta->tx_lat[i], - buf, pos, bufsz, i); -unlock: - rcu_read_unlock(); - - ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); - kfree(buf); - - return ret; -} -STA_OPS(tx_latency_stat); - -static ssize_t sta_tx_latency_stat_reset_write(struct file *file, - const char __user *userbuf, - size_t count, loff_t *ppos) -{ - u32 *bins; - int bin_count; - struct sta_info *sta = file->private_data; - int i; - - if (!sta->tx_lat) - return -EINVAL; - - for (i = 0; i < IEEE80211_NUM_TIDS; i++) { - bins = sta->tx_lat[i].bins; - bin_count = sta->tx_lat[i].bin_count; - - sta->tx_lat[i].max = 0; - sta->tx_lat[i].sum = 0; - sta->tx_lat[i].counter = 0; - - if (bin_count) - memset(bins, 0, bin_count * sizeof(u32)); - } - - return count; -} -STA_OPS_W(tx_latency_stat_reset); - #define DEBUGFS_ADD(name) \ debugfs_create_file(#name, 0400, \ sta->debugfs.dir, sta, &sta_ ##name## _ops); @@ -576,8 +444,6 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta) DEBUGFS_ADD(last_ack_signal); DEBUGFS_ADD(current_tx_rate); DEBUGFS_ADD(last_rx_rate); - DEBUGFS_ADD(tx_latency_stat); - DEBUGFS_ADD(tx_latency_stat_reset); DEBUGFS_ADD_COUNTER(rx_packets, rx_packets); DEBUGFS_ADD_COUNTER(tx_packets, tx_packets); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 3afe36824703..2c14447e1f4c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1030,24 +1030,6 @@ struct tpt_led_trigger { }; #endif -/* - * struct ieee80211_tx_latency_bin_ranges - Tx latency statistics bins ranges - * - * Measuring Tx latency statistics. Counts how many Tx frames transmitted in a - * certain latency range (in Milliseconds). Each station that uses these - * ranges will have bins to count the amount of frames received in that range. - * The user can configure the ranges via debugfs. - * If ranges is NULL then Tx latency statistics bins are disabled for all - * stations. - * - * @n_ranges: number of ranges that are taken in account - * @ranges: the ranges that the user requested or NULL if disabled. - */ -struct ieee80211_tx_latency_bin_ranges { - int n_ranges; - u32 ranges[]; -}; - /** * mac80211 scan flags - currently active scan mode * @@ -1199,12 +1181,6 @@ struct ieee80211_local { struct timer_list sta_cleanup; int sta_generation; - /* - * Tx latency statistics parameters for all stations. - * Can enable via debugfs (NULL when disabled). - */ - struct ieee80211_tx_latency_bin_ranges __rcu *tx_latency; - struct sk_buff_head pending[IEEE80211_MAX_QUEUES]; struct tasklet_struct tx_pending_tasklet; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 5e09d354c5a5..2f51e6d1f2b3 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1201,8 +1201,6 @@ void ieee80211_free_hw(struct ieee80211_hw *hw) ieee80211_free_ack_frame, NULL); idr_destroy(&local->ack_status_frames); - kfree(rcu_access_pointer(local->tx_latency)); - sta_info_stop(local); wiphy_free(local->hw.wiphy); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 00ca8dcc2bcf..d94004e7ce37 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -229,17 +229,9 @@ struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata, */ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta) { - int i; - if (sta->rate_ctrl) rate_control_free_sta(sta); - if (sta->tx_lat) { - for (i = 0; i < IEEE80211_NUM_TIDS; i++) - kfree(sta->tx_lat[i].bins); - kfree(sta->tx_lat); - } - sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr); kfree(rcu_dereference_raw(sta->sta.rates)); @@ -295,42 +287,12 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct sta_info *sta; struct timespec uptime; - struct ieee80211_tx_latency_bin_ranges *tx_latency; int i; sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp); if (!sta) return NULL; - rcu_read_lock(); - tx_latency = rcu_dereference(local->tx_latency); - /* init stations Tx latency statistics && TID bins */ - if (tx_latency) { - sta->tx_lat = kzalloc(IEEE80211_NUM_TIDS * - sizeof(struct ieee80211_tx_latency_stat), - GFP_ATOMIC); - if (!sta->tx_lat) { - rcu_read_unlock(); - goto free; - } - - if (tx_latency->n_ranges) { - for (i = 0; i < IEEE80211_NUM_TIDS; i++) { - /* size of bins is size of the ranges +1 */ - sta->tx_lat[i].bin_count = - tx_latency->n_ranges + 1; - sta->tx_lat[i].bins = - kcalloc(sta->tx_lat[i].bin_count, - sizeof(u32), GFP_ATOMIC); - if (!sta->tx_lat[i].bins) { - rcu_read_unlock(); - goto free; - } - } - } - } - rcu_read_unlock(); - spin_lock_init(&sta->lock); spin_lock_init(&sta->ps_lock); INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames); @@ -359,8 +321,10 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++) ewma_init(&sta->chain_signal_avg[i], 1024, 8); - if (sta_prepare_rate_control(local, sta, gfp)) - goto free; + if (sta_prepare_rate_control(local, sta, gfp)) { + kfree(sta); + return NULL; + } for (i = 0; i < IEEE80211_NUM_TIDS; i++) { /* @@ -405,16 +369,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, } sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr); - return sta; -free: - if (sta->tx_lat) { - for (i = 0; i < IEEE80211_NUM_TIDS; i++) - kfree(sta->tx_lat[i].bins); - kfree(sta->tx_lat); - } - kfree(sta); - return NULL; + return sta; } static int sta_info_insert_check(struct sta_info *sta) diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 925e68fe64c7..248f56e59ebc 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -234,25 +234,6 @@ struct sta_ampdu_mlme { u8 dialog_token_allocator; }; -/* - * struct ieee80211_tx_latency_stat - Tx latency statistics - * - * Measures TX latency and jitter for a station per TID. - * - * @max: worst case latency - * @sum: sum of all latencies - * @counter: amount of Tx frames sent from interface - * @bins: each bin counts how many frames transmitted within a certain - * latency range. when disabled it is NULL. - * @bin_count: amount of bins. - */ -struct ieee80211_tx_latency_stat { - u32 max; - u32 sum; - u32 counter; - u32 *bins; - u32 bin_count; -}; /* Value to indicate no TID reservation */ #define IEEE80211_TID_UNRESERVED 0xff @@ -314,7 +295,6 @@ struct ieee80211_tx_latency_stat { * @tid_seq: per-TID sequence numbers for sending to this STA * @ampdu_mlme: A-MPDU state machine state * @timer_to_tid: identity mapping to ID timers - * @tx_lat: Tx latency statistics * @llid: Local link ID * @plid: Peer link ID * @reason: Cancel reason on PLINK_HOLDING state @@ -435,8 +415,6 @@ struct sta_info { struct sta_ampdu_mlme ampdu_mlme; u8 timer_to_tid[IEEE80211_NUM_TIDS]; - struct ieee80211_tx_latency_stat *tx_lat; - #ifdef CONFIG_MAC80211_MESH /* * Mesh peer link attributes diff --git a/net/mac80211/status.c b/net/mac80211/status.c index e679b7c9b160..2c51742428d5 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -12,7 +12,6 @@ #include #include -#include #include #include #include "ieee80211_i.h" @@ -514,73 +513,6 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local, } } -/* - * Measure Tx frame completion and removal time for Tx latency statistics - * calculation. A single Tx frame latency should be measured from when it - * is entering the Kernel until we receive Tx complete confirmation indication - * and remove the skb. - */ -static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local, - struct sk_buff *skb, - struct sta_info *sta, - struct ieee80211_hdr *hdr) -{ - u32 msrmnt; - u16 tid; - u8 *qc; - int i, bin_range_count; - u32 *bin_ranges; - __le16 fc; - struct ieee80211_tx_latency_stat *tx_lat; - struct ieee80211_tx_latency_bin_ranges *tx_latency; - ktime_t skb_arv = skb->tstamp; - - tx_latency = rcu_dereference(local->tx_latency); - - /* assert Tx latency stats are enabled & frame arrived when enabled */ - if (!tx_latency || !ktime_to_ns(skb_arv)) - return; - - fc = hdr->frame_control; - - if (!ieee80211_is_data(fc)) /* make sure it is a data frame */ - return; - - /* get frame tid */ - if (ieee80211_is_data_qos(hdr->frame_control)) { - qc = ieee80211_get_qos_ctl(hdr); - tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; - } else { - tid = 0; - } - - tx_lat = &sta->tx_lat[tid]; - - /* Calculate the latency */ - msrmnt = ktime_to_ms(ktime_sub(ktime_get(), skb_arv)); - - if (tx_lat->max < msrmnt) /* update stats */ - tx_lat->max = msrmnt; - tx_lat->counter++; - tx_lat->sum += msrmnt; - - if (!tx_lat->bins) /* bins not activated */ - return; - - /* count how many Tx frames transmitted with the appropriate latency */ - bin_range_count = tx_latency->n_ranges; - bin_ranges = tx_latency->ranges; - - for (i = 0; i < bin_range_count; i++) { - if (msrmnt <= bin_ranges[i]) { - tx_lat->bins[i]++; - break; - } - } - if (i == bin_range_count) /* msrmnt is bigger than the biggest range */ - tx_lat->bins[i]++; -} - /* * Use a static threshold for now, best value to be determined * by testing ... @@ -853,12 +785,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) if (acked) sta->last_ack_signal = info->status.ack_signal; - - /* - * Measure frame removal for tx latency - * statistics calculation - */ - ieee80211_tx_latency_end_msrmnt(local, skb, sta, hdr); } rcu_read_unlock(); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 0e56ac5729ef..a23a84e93e80 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -1787,23 +1786,6 @@ fail: return NETDEV_TX_OK; /* meaning, we dealt with the skb */ } -/* - * Measure Tx frame arrival time for Tx latency statistics calculation - * A single Tx frame latency should be measured from when it is entering the - * Kernel until we receive Tx complete confirmation indication and the skb is - * freed. - */ -static void ieee80211_tx_latency_start_msrmnt(struct ieee80211_local *local, - struct sk_buff *skb) -{ - struct ieee80211_tx_latency_bin_ranges *tx_latency; - - tx_latency = rcu_dereference(local->tx_latency); - if (!tx_latency) - return; - skb->tstamp = ktime_get(); -} - /** * ieee80211_build_hdr - build 802.11 header in the given frame * @sdata: virtual interface to build the header for @@ -2259,7 +2241,6 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb, u32 info_flags) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - struct ieee80211_local *local = sdata->local; if (unlikely(skb->len < ETH_HLEN)) { kfree_skb(skb); @@ -2268,9 +2249,6 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb, rcu_read_lock(); - /* Measure frame arrival for Tx latency statistics calculation */ - ieee80211_tx_latency_start_msrmnt(local, skb); - skb = ieee80211_build_hdr(sdata, skb, info_flags); if (IS_ERR(skb)) goto out; -- cgit v1.2.3 From 6eb18137643fee5f182d85c818062b4feddfb76b Mon Sep 17 00:00:00 2001 From: Dedy Lansky Date: Sun, 8 Feb 2015 15:52:03 +0200 Subject: cfg80211: add bss_type and privacy arguments in cfg80211_get_bss() 802.11ad adds new a network type (PBSS) and changes the capability field interpretation for the DMG (60G) band. The same 2 bits that were interpreted as "ESS" and "IBSS" before are re-used as a 2-bit field with 3 valid values (and 1 reserved). Valid values are: "IBSS", "PBSS" (new) and "AP". In order to get the BSS struct for the new PBSS networks, change the cfg80211_get_bss() function to take a new enum ieee80211_bss_type argument with the valid network types, as "capa_mask" and "capa_val" no longer work correctly (the search must be band-aware now.) The remaining bits in "capa_mask" and "capa_val" are used only for privacy matching so replace those two with a privacy enum as well. Signed-off-by: Dedy Lansky [rewrite commit log, tiny fixes] Signed-off-by: Johannes Berg --- net/mac80211/ibss.c | 43 ++++++++++++------------------------------- 1 file changed, 12 insertions(+), 31 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index b606b53a49a7..ee93d7d9aa4b 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -470,22 +470,19 @@ int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata, struct beacon_data *presp, *old_presp; struct cfg80211_bss *cbss; const struct cfg80211_bss_ies *ies; - u16 capability; + u16 capability = 0; u64 tsf; int ret = 0; sdata_assert_lock(sdata); - capability = WLAN_CAPABILITY_IBSS; - if (ifibss->privacy) - capability |= WLAN_CAPABILITY_PRIVACY; + capability = WLAN_CAPABILITY_PRIVACY; cbss = cfg80211_get_bss(sdata->local->hw.wiphy, ifibss->chandef.chan, ifibss->bssid, ifibss->ssid, - ifibss->ssid_len, WLAN_CAPABILITY_IBSS | - WLAN_CAPABILITY_PRIVACY, - capability); + ifibss->ssid_len, IEEE80211_BSS_TYPE_IBSS, + IEEE80211_PRIVACY(ifibss->privacy)); if (WARN_ON(!cbss)) { ret = -EINVAL; @@ -525,23 +522,17 @@ int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata) struct ieee80211_if_ibss *ifibss = &sdata->u.ibss; struct cfg80211_bss *cbss; int err, changed = 0; - u16 capability; sdata_assert_lock(sdata); /* update cfg80211 bss information with the new channel */ if (!is_zero_ether_addr(ifibss->bssid)) { - capability = WLAN_CAPABILITY_IBSS; - - if (ifibss->privacy) - capability |= WLAN_CAPABILITY_PRIVACY; - cbss = cfg80211_get_bss(sdata->local->hw.wiphy, ifibss->chandef.chan, ifibss->bssid, ifibss->ssid, - ifibss->ssid_len, WLAN_CAPABILITY_IBSS | - WLAN_CAPABILITY_PRIVACY, - capability); + ifibss->ssid_len, + IEEE80211_BSS_TYPE_IBSS, + IEEE80211_PRIVACY(ifibss->privacy)); /* XXX: should not really modify cfg80211 data */ if (cbss) { cbss->channel = sdata->csa_chandef.chan; @@ -682,19 +673,13 @@ static void ieee80211_ibss_disconnect(struct ieee80211_sub_if_data *sdata) struct cfg80211_bss *cbss; struct beacon_data *presp; struct sta_info *sta; - u16 capability; if (!is_zero_ether_addr(ifibss->bssid)) { - capability = WLAN_CAPABILITY_IBSS; - - if (ifibss->privacy) - capability |= WLAN_CAPABILITY_PRIVACY; - cbss = cfg80211_get_bss(local->hw.wiphy, ifibss->chandef.chan, ifibss->bssid, ifibss->ssid, - ifibss->ssid_len, WLAN_CAPABILITY_IBSS | - WLAN_CAPABILITY_PRIVACY, - capability); + ifibss->ssid_len, + IEEE80211_BSS_TYPE_IBSS, + IEEE80211_PRIVACY(ifibss->privacy)); if (cbss) { cfg80211_unlink_bss(local->hw.wiphy, cbss); @@ -1325,7 +1310,6 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) const u8 *bssid = NULL; enum nl80211_bss_scan_width scan_width; int active_ibss; - u16 capability; sdata_assert_lock(sdata); @@ -1335,9 +1319,6 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) if (active_ibss) return; - capability = WLAN_CAPABILITY_IBSS; - if (ifibss->privacy) - capability |= WLAN_CAPABILITY_PRIVACY; if (ifibss->fixed_bssid) bssid = ifibss->bssid; if (ifibss->fixed_channel) @@ -1346,8 +1327,8 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) bssid = ifibss->bssid; cbss = cfg80211_get_bss(local->hw.wiphy, chan, bssid, ifibss->ssid, ifibss->ssid_len, - WLAN_CAPABILITY_IBSS | WLAN_CAPABILITY_PRIVACY, - capability); + IEEE80211_BSS_TYPE_IBSS, + IEEE80211_PRIVACY(ifibss->privacy)); if (cbss) { struct ieee80211_bss *bss; -- cgit v1.2.3 From 2ecc3905e6c51f545a44cc621216b5dfd7f94c50 Mon Sep 17 00:00:00 2001 From: Alexander Bondar Date: Sun, 1 Mar 2015 09:10:00 +0200 Subject: mac80211: Update beacon's timing and DTIM count on every beacon Beacon's timestamp, device system time associated with this beacon and DTIM count parameters are not updated in the associated vif context if the latest beacon's content is identical to the previously received. It make sense to update these changing parameters on every beacon so the driver can get most updated values. This may be necessary, for example, to avoid either beacons' drift effect or device time stamp overrun. IMPORTANT: Three sync_* parameters - sync_ts, sync_device_ts and sync_dtim_count would possibly be out of sync by the time the driver will use them. The synchronized view is currently guaranteed only in certain callbacks. Signed-off-by: Alexander Bondar Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 10ac6324c1d0..cf3ae9348a9d 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3419,6 +3419,26 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, if (ifmgd->csa_waiting_bcn) ieee80211_chswitch_post_beacon(sdata); + /* + * Update beacon timing and dtim count on every beacon appearance. This + * will allow the driver to use the most updated values. Do it before + * comparing this one with last received beacon. + * IMPORTANT: These parameters would possibly be out of sync by the time + * the driver will use them. The synchronized view is currently + * guaranteed only in certain callbacks. + */ + if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) { + sdata->vif.bss_conf.sync_tsf = + le64_to_cpu(mgmt->u.beacon.timestamp); + sdata->vif.bss_conf.sync_device_ts = + rx_status->device_timestamp; + if (elems.tim) + sdata->vif.bss_conf.sync_dtim_count = + elems.tim->dtim_count; + else + sdata->vif.bss_conf.sync_dtim_count = 0; + } + if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid) return; ifmgd->beacon_crc = ncrc; @@ -3446,18 +3466,6 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, else bss_conf->dtim_period = 1; - if (local->hw.flags & IEEE80211_HW_TIMING_BEACON_ONLY) { - sdata->vif.bss_conf.sync_tsf = - le64_to_cpu(mgmt->u.beacon.timestamp); - sdata->vif.bss_conf.sync_device_ts = - rx_status->device_timestamp; - if (elems.tim) - sdata->vif.bss_conf.sync_dtim_count = - elems.tim->dtim_count; - else - sdata->vif.bss_conf.sync_dtim_count = 0; - } - changed |= BSS_CHANGED_BEACON_INFO; ifmgd->have_beacon = true; -- cgit v1.2.3 From fb28ec0ce4acd54cb1972d224e2caf287b60d9c9 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sun, 1 Mar 2015 09:10:02 +0200 Subject: mac80211: TDLS: support VHT between peers Add the AID and VHT-cap/operation IEs during TDLS setup. Remove the block of TDLS peers when setting HT-caps of the peer station. Signed-off-by: Arik Nemtsov Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 2 ++ net/mac80211/tdls.c | 86 +++++++++++++++++++++++++++++++++++++++++++--- net/mac80211/util.c | 35 +++++++++++++++++++ net/mac80211/vht.c | 4 --- 4 files changed, 118 insertions(+), 9 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 2c14447e1f4c..0266c573c366 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1943,6 +1943,8 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, u16 prot_mode); u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, u32 cap); +u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, + const struct cfg80211_chan_def *chandef); int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef, const struct ieee80211_supported_band *sband, const u8 *srates, int srates_len, u32 *rates); diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index c9f9752217ac..5bcd542e4933 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -193,6 +193,17 @@ static void ieee80211_tdls_add_link_ie(struct ieee80211_sub_if_data *sdata, memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN); } +static void +ieee80211_tdls_add_aid(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) +{ + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + u8 *pos = (void *)skb_put(skb, 4); + + *pos++ = WLAN_EID_AID; + *pos++ = 2; /* len */ + put_unaligned_le16(ifmgd->aid, pos); +} + /* translate numbering in the WMM parameter IE to the mac80211 notation */ static enum ieee80211_ac_numbers ieee80211_ac_from_wmm(int ac) { @@ -271,6 +282,7 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband; struct ieee80211_sta_ht_cap ht_cap; + struct ieee80211_sta_vht_cap vht_cap; struct sta_info *sta = NULL; size_t offset = 0, noffset; u8 *pos; @@ -368,12 +380,60 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap); } - rcu_read_unlock(); - if (ht_cap.ht_supported && (ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) ieee80211_tdls_add_bss_coex_ie(skb); + ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); + + /* add any custom IEs that go before VHT capabilities */ + if (extra_ies_len) { + static const u8 before_vht_cap[] = { + WLAN_EID_SUPP_RATES, + WLAN_EID_COUNTRY, + WLAN_EID_EXT_SUPP_RATES, + WLAN_EID_SUPPORTED_CHANNELS, + WLAN_EID_RSN, + WLAN_EID_EXT_CAPABILITY, + WLAN_EID_QOS_CAPA, + WLAN_EID_FAST_BSS_TRANSITION, + WLAN_EID_TIMEOUT_INTERVAL, + WLAN_EID_SUPPORTED_REGULATORY_CLASSES, + WLAN_EID_MULTI_BAND, + }; + noffset = ieee80211_ie_split(extra_ies, extra_ies_len, + before_vht_cap, + ARRAY_SIZE(before_vht_cap), + offset); + pos = skb_put(skb, noffset - offset); + memcpy(pos, extra_ies + offset, noffset - offset); + offset = noffset; + } + + /* build the VHT-cap similarly to the HT-cap */ + memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap)); + if (action_code == WLAN_TDLS_SETUP_REQUEST && vht_cap.vht_supported) { + ieee80211_apply_vhtcap_overrides(sdata, &vht_cap); + + /* the AID is present only when VHT is implemented */ + ieee80211_tdls_add_aid(sdata, skb); + + pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); + ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap); + } else if (action_code == WLAN_TDLS_SETUP_RESPONSE && + vht_cap.vht_supported && sta->sta.vht_cap.vht_supported) { + /* the peer caps are already intersected with our own */ + memcpy(&vht_cap, &sta->sta.vht_cap, sizeof(vht_cap)); + + /* the AID is present only when VHT is implemented */ + ieee80211_tdls_add_aid(sdata, skb); + + pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); + ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap); + } + + rcu_read_unlock(); + /* add any remaining IEs */ if (extra_ies_len) { noffset = extra_ies_len; @@ -381,7 +441,6 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, memcpy(pos, extra_ies + offset, noffset - offset); } - ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); } static void @@ -394,6 +453,7 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; size_t offset = 0, noffset; struct sta_info *sta, *ap_sta; + enum ieee80211_band band = ieee80211_get_sdata_band(sdata); u8 *pos; rcu_read_lock(); @@ -453,6 +513,21 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata, } } + ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); + + /* only include VHT-operation if not on the 2.4GHz band */ + if (band != IEEE80211_BAND_2GHZ && !ap_sta->sta.vht_cap.vht_supported && + sta->sta.vht_cap.vht_supported) { + struct ieee80211_chanctx_conf *chanctx_conf = + rcu_dereference(sdata->vif.chanctx_conf); + if (!WARN_ON(!chanctx_conf)) { + pos = skb_put(skb, 2 + + sizeof(struct ieee80211_vht_operation)); + ieee80211_ie_build_vht_oper(pos, &sta->sta.vht_cap, + &chanctx_conf->def); + } + } + rcu_read_unlock(); /* add any remaining IEs */ @@ -461,8 +536,6 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata, pos = skb_put(skb, noffset - offset); memcpy(pos, extra_ies + offset, noffset - offset); } - - ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); } static void @@ -708,8 +781,11 @@ ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata, 26 + /* max(WMM-info, WMM-param) */ 2 + max(sizeof(struct ieee80211_ht_cap), sizeof(struct ieee80211_ht_operation)) + + 2 + max(sizeof(struct ieee80211_vht_cap), + sizeof(struct ieee80211_vht_operation)) + 50 + /* supported channels */ 3 + /* 40/20 BSS coex */ + 4 + /* AID */ extra_ies_len + sizeof(struct ieee80211_tdls_lnkie)); if (!skb) diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 8428f4a95479..eec94447eef7 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2344,6 +2344,41 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, return pos + sizeof(struct ieee80211_ht_operation); } +u8 *ieee80211_ie_build_vht_oper(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, + const struct cfg80211_chan_def *chandef) +{ + struct ieee80211_vht_operation *vht_oper; + + *pos++ = WLAN_EID_VHT_OPERATION; + *pos++ = sizeof(struct ieee80211_vht_operation); + vht_oper = (struct ieee80211_vht_operation *)pos; + vht_oper->center_freq_seg1_idx = ieee80211_frequency_to_channel( + chandef->center_freq1); + if (chandef->center_freq2) + vht_oper->center_freq_seg2_idx = + ieee80211_frequency_to_channel(chandef->center_freq2); + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_160: + vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_160MHZ; + break; + case NL80211_CHAN_WIDTH_80P80: + vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80P80MHZ; + break; + case NL80211_CHAN_WIDTH_80: + vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; + break; + default: + vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT; + break; + } + + /* don't require special VHT peer rates */ + vht_oper->basic_mcs_set = cpu_to_le16(0xffff); + + return pos + sizeof(struct ieee80211_vht_operation); +} + void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan, const struct ieee80211_ht_operation *ht_oper, struct cfg80211_chan_def *chandef) diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index 85f9596da07b..80694d55db74 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -129,10 +129,6 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, if (!vht_cap_ie || !sband->vht_cap.vht_supported) return; - /* don't support VHT for TDLS peers for now */ - if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) - return; - /* * A VHT STA must support 40 MHz, but if we verify that here * then we break a few things - some APs (e.g. Netgear R6300v2 -- cgit v1.2.3 From be72afe0a43825ea357bf61f01ff8fe9938fe7d4 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 1 Mar 2015 09:10:03 +0200 Subject: mac80211: fix another suspend vs. association race Since cfg80211 disconnects, but has no insight into the association process, it can happen that it disconnects while association is in progress. We then try to abort association in mac80211, but this is only later so the association can complete between the two. This results in removing an interface from the driver while bound to the channel context, obviously causing confusion and issues. Solve this by also checking if we're associated during quiesce and if so deauthenticating. The frame will no longer go out to the AP which is a bit unfortunate, but it'll resolve the crash (and before we would have suspended without telling the AP as well.) I'm working on a better, but more complex solution as well, which should avoid that problem. Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'net/mac80211') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index cf3ae9348a9d..c5f3bd6ac99e 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3985,6 +3985,34 @@ void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata) IEEE80211_DEAUTH_FRAME_LEN); } + /* This is a bit of a hack - we should find a better and more generic + * solution to this. Normally when suspending, cfg80211 will in fact + * deauthenticate. However, it doesn't (and cannot) stop an ongoing + * auth (not so important) or assoc (this is the problem) process. + * + * As a consequence, it can happen that we are in the process of both + * associating and suspending, and receive an association response + * after cfg80211 has checked if it needs to disconnect, but before + * we actually set the flag to drop incoming frames. This will then + * cause the workqueue flush to process the association response in + * the suspend, resulting in a successful association just before it + * tries to remove the interface from the driver, which now though + * has a channel context assigned ... this results in issues. + * + * To work around this (for now) simply deauth here again if we're + * now connected. + */ + if (ifmgd->associated && !sdata->local->wowlan) { + u8 bssid[ETH_ALEN]; + struct cfg80211_deauth_request req = { + .reason_code = WLAN_REASON_DEAUTH_LEAVING, + .bssid = bssid, + }; + + memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN); + ieee80211_mgd_deauth(sdata, &req); + } + sdata_unlock(sdata); } -- cgit v1.2.3 From c8fff3dc72817d476e083c3f9bc59a11901ecfeb Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Sun, 1 Mar 2015 09:10:04 +0200 Subject: mac80211: handle drv_add_interface failures properly during reconfig If any interface fails to be added to the driver in during reconfig, we should remove all the successfully added interfaces and report reconfig failure, so things can be cleaned up properly. Failing to do so can lead to subsequent failures and leave the drivers in a messed up state. Signed-off-by: Luciano Coelho Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/util.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'net/mac80211') diff --git a/net/mac80211/util.c b/net/mac80211/util.c index eec94447eef7..327886748a1d 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1811,8 +1811,25 @@ int ieee80211_reconfig(struct ieee80211_local *local) list_for_each_entry(sdata, &local->interfaces, list) { if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && sdata->vif.type != NL80211_IFTYPE_MONITOR && - ieee80211_sdata_running(sdata)) + ieee80211_sdata_running(sdata)) { res = drv_add_interface(local, sdata); + if (WARN_ON(res)) + break; + } + } + + /* If adding any of the interfaces failed above, roll back and + * report failure. + */ + if (res) { + list_for_each_entry_continue_reverse(sdata, &local->interfaces, + list) + if (sdata->vif.type != NL80211_IFTYPE_AP_VLAN && + sdata->vif.type != NL80211_IFTYPE_MONITOR && + ieee80211_sdata_running(sdata)) + drv_remove_interface(local, sdata); + ieee80211_handle_reconfig_failure(local); + return res; } /* add channel contexts */ -- cgit v1.2.3 From c84a67a2fc70841e642fe2f0429abb1d9b4d2167 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 2 Mar 2015 19:54:57 -0800 Subject: mac80211: Use eth__addr instead of memset Use the built-in function instead of memset. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- net/mac80211/cfg.c | 2 +- net/mac80211/ibss.c | 2 +- net/mac80211/mesh.c | 2 +- net/mac80211/mlme.c | 10 +++++----- 4 files changed, 8 insertions(+), 8 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index dd4ff36c557a..74f509c500f2 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1488,7 +1488,7 @@ static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop, if (next_hop_sta) memcpy(next_hop, next_hop_sta->sta.addr, ETH_ALEN); else - memset(next_hop, 0, ETH_ALEN); + eth_zero_addr(next_hop); memset(pinfo, 0, sizeof(*pinfo)); diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index b606b53a49a7..f9b07588baf5 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -1742,7 +1742,7 @@ int ieee80211_ibss_leave(struct ieee80211_sub_if_data *sdata) ieee80211_ibss_disconnect(sdata); ifibss->ssid_len = 0; - memset(ifibss->bssid, 0, ETH_ALEN); + eth_zero_addr(ifibss->bssid); /* remove beacon */ kfree(sdata->u.ibss.ie); diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index 0c8b2a77d312..49a44bcd8aba 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -520,7 +520,7 @@ int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc, } else { *fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); /* RA TA DA SA */ - memset(hdr->addr1, 0, ETH_ALEN); /* RA is resolved later */ + eth_zero_addr(hdr->addr1); /* RA is resolved later */ memcpy(hdr->addr2, meshsa, ETH_ALEN); memcpy(hdr->addr3, meshda, ETH_ALEN); memcpy(hdr->addr4, meshsa, ETH_ALEN); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 10ac6324c1d0..9f6f3562396a 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2033,7 +2033,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, ieee80211_flush_queues(local, sdata, false); /* clear bssid only after building the needed mgmt frames */ - memset(ifmgd->bssid, 0, ETH_ALEN); + eth_zero_addr(ifmgd->bssid); /* remove AP and TDLS peers */ sta_info_flush(sdata); @@ -2464,7 +2464,7 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata, del_timer_sync(&sdata->u.mgd.timer); sta_info_destroy_addr(sdata, auth_data->bss->bssid); - memset(sdata->u.mgd.bssid, 0, ETH_ALEN); + eth_zero_addr(sdata->u.mgd.bssid); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); sdata->u.mgd.flags = 0; mutex_lock(&sdata->local->mtx); @@ -2777,7 +2777,7 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata, del_timer_sync(&sdata->u.mgd.timer); sta_info_destroy_addr(sdata, assoc_data->bss->bssid); - memset(sdata->u.mgd.bssid, 0, ETH_ALEN); + eth_zero_addr(sdata->u.mgd.bssid); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); sdata->u.mgd.flags = 0; mutex_lock(&sdata->local->mtx); @@ -4474,7 +4474,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, return 0; err_clear: - memset(ifmgd->bssid, 0, ETH_ALEN); + eth_zero_addr(ifmgd->bssid); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); ifmgd->auth_data = NULL; err_free: @@ -4817,7 +4817,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, return 0; err_clear: - memset(ifmgd->bssid, 0, ETH_ALEN); + eth_zero_addr(ifmgd->bssid); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); ifmgd->assoc_data = NULL; err_free: -- cgit v1.2.3 From ef7c67257c9a0765bd1a53f83fd63d55a0f7b224 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 1 Mar 2015 09:10:06 +0200 Subject: mac80211: don't do driver suspend with auth/assoc in progress Drivers can't really be expected to suspend properly while auth or assoc is in progress since then they don't have any state they could keep with WoWLAN, nor can they actually finish the authentication or association. In fact, keeping this can cause subtle issues with drivers like iwlwifi that refuse WoWLAN if not associated, but have trouble figuring out what's going on in the middle of association. In any case, regardless of possible driver issues in this area, it doesn't make sense for mac80211 to try to WoWLAN-suspend in the middle of such operations, so stop them before. Signed-off-by: Johannes Berg --- net/mac80211/pm.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'net/mac80211') diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index ca405b6b686d..bd185f38def1 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -61,7 +61,24 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) local->wowlan = wowlan && local->open_count; if (local->wowlan) { - int err = drv_suspend(local, wowlan); + int err; + + /* Drivers don't expect to suspend while some operations like + * authenticating or associating are in progress. It doesn't + * make sense anyway to accept that, since the authentication + * or association would never finish since the driver can't do + * that on its own. + * Thus, clean up in-progress auth/assoc first. + */ + list_for_each_entry(sdata, &local->interfaces, list) { + if (!ieee80211_sdata_running(sdata)) + continue; + if (sdata->vif.type != NL80211_IFTYPE_STATION) + continue; + ieee80211_mgd_quiesce(sdata); + } + + err = drv_suspend(local, wowlan); if (err < 0) { local->quiescing = false; local->wowlan = false; -- cgit v1.2.3 From 8bb6f4b9c50eef838931e0e667f1a451f34a76ae Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Sun, 1 Mar 2015 09:10:07 +0200 Subject: mac80211: remove useless double check for open_count in __ieee80211_suspend() We check local->open_count at the top of the __ieee80211_suspend(), so there's no need to check for it again. open_count is protected by the rtnl, so there's no chance for it to have change between the two calls. Signed-off-by: Luciano Coelho Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/pm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/mac80211') diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index bd185f38def1..46ffe3ad7c46 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -59,7 +59,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) cancel_work_sync(&local->dynamic_ps_enable_work); del_timer_sync(&local->dynamic_ps_timer); - local->wowlan = wowlan && local->open_count; + local->wowlan = wowlan; if (local->wowlan) { int err; -- cgit v1.2.3 From 23e370989c3fe6b9b2062d9d511feda800434de3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 1 Mar 2015 09:10:08 +0200 Subject: mac80211: start queues if driver rejected wowlan If the driver rejects WoWLAN, restart the queues before returning to cfg80211. cfg80211 will return to mac80211, but not before it disconnects all interfaces. If we don't start the queues, any of the packets needed for disconnecting won't be transmitted, which is strange. Fix that. Signed-off-by: Johannes Berg --- net/mac80211/pm.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'net/mac80211') diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 46ffe3ad7c46..ac6ad6238e3a 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -97,6 +97,13 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) return err; } else if (err > 0) { WARN_ON(err != 1); + /* cfg80211 will call back into mac80211 to disconnect + * all interfaces, allow that to proceed properly + */ + ieee80211_wake_queues_by_reason(hw, + IEEE80211_MAX_QUEUE_MAP, + IEEE80211_QUEUE_STOP_REASON_SUSPEND, + false); return err; } else { goto suspend; -- cgit v1.2.3 From ae2e9fba85f69e0b53b1ef15e3011ce09ac1b9f2 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sun, 1 Mar 2015 09:10:09 +0200 Subject: mac80211: allow TDLS setup code to take wdev lock TDLS off-channel can be allowed in channels marked with GO_CONCURRENT, provided the device is connected to an AP on the same UNII. When relaxing the NO-IR requirements for TDLS, we might hit flows in cfg80211_reg_can_beacon that acquire the wdev lock. Take some measures to allow this during TDLS setup. Acquire the RCU read lock later in the flow that invokes cfg80211_reg_can_beacon. Avoid taking local->mtx when preparing the setup packet to avoid circular deadlocks with mac80211 code that is invoked with wdev-mtx held. Signed-off-by: Arik Nemtsov Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/tdls.c | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 5bcd542e4933..bc7e4049896f 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -287,17 +287,6 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, size_t offset = 0, noffset; u8 *pos; - rcu_read_lock(); - - /* we should have the peer STA if we're already responding */ - if (action_code == WLAN_TDLS_SETUP_RESPONSE) { - sta = sta_info_get(sdata, peer); - if (WARN_ON_ONCE(!sta)) { - rcu_read_unlock(); - return; - } - } - ieee80211_add_srates_ie(sdata, skb, false, band); ieee80211_add_ext_srates_ie(sdata, skb, false, band); ieee80211_tdls_add_supp_channels(sdata, skb); @@ -350,6 +339,17 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, offset = noffset; } + rcu_read_lock(); + + /* we should have the peer STA if we're already responding */ + if (action_code == WLAN_TDLS_SETUP_RESPONSE) { + sta = sta_info_get(sdata, peer); + if (WARN_ON_ONCE(!sta)) { + rcu_read_unlock(); + return; + } + } + /* * with TDLS we can switch channels, and HT-caps are not necessarily * the same on all bands. The specification limits the setup to a @@ -983,7 +983,7 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev, if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer) && !ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) { ret = -EBUSY; - goto exit; + goto out_unlock; } /* @@ -998,27 +998,34 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev, if (!sta_info_get(sdata, peer)) { rcu_read_unlock(); ret = -ENOLINK; - goto exit; + goto out_unlock; } rcu_read_unlock(); } ieee80211_flush_queues(local, sdata, false); + memcpy(sdata->u.mgd.tdls_peer, peer, ETH_ALEN); + mutex_unlock(&local->mtx); + /* we cannot take the mutex while preparing the setup packet */ ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code, dialog_token, status_code, peer_capability, initiator, extra_ies, extra_ies_len, 0, NULL); - if (ret < 0) - goto exit; + if (ret < 0) { + mutex_lock(&local->mtx); + eth_zero_addr(sdata->u.mgd.tdls_peer); + mutex_unlock(&local->mtx); + return ret; + } - memcpy(sdata->u.mgd.tdls_peer, peer, ETH_ALEN); ieee80211_queue_delayed_work(&sdata->local->hw, &sdata->u.mgd.tdls_peer_del_work, TDLS_PEER_SETUP_TIMEOUT); + return 0; -exit: +out_unlock: mutex_unlock(&local->mtx); return ret; } -- cgit v1.2.3 From 0b4e11074a933937ee75be371d32bf91a0051419 Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Sun, 1 Mar 2015 09:10:10 +0200 Subject: mac80211: remove duplicate check for quiescing when queueing work In ieee80211_queue_work() we check if we're quiescing or suspended, so it's not necessary to check for quiescing before calling this function. Remove duplicate checks. Signed-off-by: Luciano Coelho Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index c5f3bd6ac99e..bc2975e91272 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1621,9 +1621,6 @@ void ieee80211_dynamic_ps_timer(unsigned long data) { struct ieee80211_local *local = (void *) data; - if (local->quiescing || local->suspended) - return; - ieee80211_queue_work(&local->hw, &local->dynamic_ps_enable_work); } @@ -3899,12 +3896,8 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data) { struct ieee80211_sub_if_data *sdata = (struct ieee80211_sub_if_data *) data; - struct ieee80211_local *local = sdata->local; struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; - if (local->quiescing) - return; - if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn) return; @@ -3920,9 +3913,6 @@ static void ieee80211_sta_conn_mon_timer(unsigned long data) struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_local *local = sdata->local; - if (local->quiescing) - return; - if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn) return; -- cgit v1.2.3 From 88724a81b4ac2a755bf9c532cdbee08a52803852 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sun, 1 Mar 2015 09:10:12 +0200 Subject: mac80211: check and dequeue skb in ieee80211_tx_prepare_skb() The ieee80211_tx_prepare_skb() function currently entirely ignores the fact that the SKB that is passed in might be split into more than one due to fragmentation and doesn't check the list of skbs that the TX handlers may create. In case this happens, it would leak them. Fix this and also don't leave the skb next/prev pointers dangling pointing to the on-stack sk_buff_head. Reported-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'net/mac80211') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index a23a84e93e80..7e7de811e6ad 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1420,6 +1420,7 @@ bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw, struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_tx_data tx; + struct sk_buff *skb2; if (ieee80211_tx_prepare(sdata, &tx, skb) == TX_DROP) return false; @@ -1438,6 +1439,14 @@ bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw, *sta = NULL; } + /* this function isn't suitable for fragmented data frames */ + skb2 = __skb_dequeue(&tx.skbs); + if (WARN_ON(skb2 != skb || !skb_queue_empty(&tx.skbs))) { + ieee80211_free_txskb(hw, skb2); + ieee80211_purge_tx_queue(hw, &tx.skbs); + return false; + } + return true; } EXPORT_SYMBOL(ieee80211_tx_prepare_skb); -- cgit v1.2.3 From 3384d757d41521a3dee274ed2802bcd285ed8e62 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Sun, 1 Mar 2015 09:10:15 +0200 Subject: mac80211: allow iterating inactive interfaces Sometimes the driver might want to modify private data in interfaces that are down. One possible use-case is cleaning up interface state after HW recovery. Some interfaces that were up before the recovery took place might be down now, but they might still be "dirty". Introduce a new iterate_interfaces() API and a new ACTIVE iterator flag. This way the internal implementation of the both active and inactive APIs remains the same. Signed-off-by: Arik Nemtsov Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/util.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 327886748a1d..37d85d36dd2c 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -625,13 +625,14 @@ void ieee80211_wake_vif_queues(struct ieee80211_local *local, reason, true); } -static void __iterate_active_interfaces(struct ieee80211_local *local, - u32 iter_flags, - void (*iterator)(void *data, u8 *mac, - struct ieee80211_vif *vif), - void *data) +static void __iterate_interfaces(struct ieee80211_local *local, + u32 iter_flags, + void (*iterator)(void *data, u8 *mac, + struct ieee80211_vif *vif), + void *data) { struct ieee80211_sub_if_data *sdata; + bool active_only = iter_flags & IEEE80211_IFACE_ITER_ACTIVE; list_for_each_entry_rcu(sdata, &local->interfaces, list) { switch (sdata->vif.type) { @@ -645,9 +646,9 @@ static void __iterate_active_interfaces(struct ieee80211_local *local, break; } if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) && - !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) + active_only && !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) continue; - if (ieee80211_sdata_running(sdata)) + if (ieee80211_sdata_running(sdata) || !active_only) iterator(data, sdata->vif.addr, &sdata->vif); } @@ -656,12 +657,12 @@ static void __iterate_active_interfaces(struct ieee80211_local *local, lockdep_is_held(&local->iflist_mtx) || lockdep_rtnl_is_held()); if (sdata && - (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || + (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || !active_only || sdata->flags & IEEE80211_SDATA_IN_DRIVER)) iterator(data, sdata->vif.addr, &sdata->vif); } -void ieee80211_iterate_active_interfaces( +void ieee80211_iterate_interfaces( struct ieee80211_hw *hw, u32 iter_flags, void (*iterator)(void *data, u8 *mac, struct ieee80211_vif *vif), @@ -670,10 +671,10 @@ void ieee80211_iterate_active_interfaces( struct ieee80211_local *local = hw_to_local(hw); mutex_lock(&local->iflist_mtx); - __iterate_active_interfaces(local, iter_flags, iterator, data); + __iterate_interfaces(local, iter_flags, iterator, data); mutex_unlock(&local->iflist_mtx); } -EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces); +EXPORT_SYMBOL_GPL(ieee80211_iterate_interfaces); void ieee80211_iterate_active_interfaces_atomic( struct ieee80211_hw *hw, u32 iter_flags, @@ -684,7 +685,8 @@ void ieee80211_iterate_active_interfaces_atomic( struct ieee80211_local *local = hw_to_local(hw); rcu_read_lock(); - __iterate_active_interfaces(local, iter_flags, iterator, data); + __iterate_interfaces(local, iter_flags | IEEE80211_IFACE_ITER_ACTIVE, + iterator, data); rcu_read_unlock(); } EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic); @@ -699,7 +701,8 @@ void ieee80211_iterate_active_interfaces_rtnl( ASSERT_RTNL(); - __iterate_active_interfaces(local, iter_flags, iterator, data); + __iterate_interfaces(local, iter_flags | IEEE80211_IFACE_ITER_ACTIVE, + iterator, data); } EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl); -- cgit v1.2.3 From 64a8cef41a8ce694b59ec75ae52688f58925693c Mon Sep 17 00:00:00 2001 From: SenthilKumar Jegadeesan Date: Mon, 2 Mar 2015 13:29:40 +0530 Subject: mac80211: provide station PMF configuration to driver Some device drivers offload part of aggregation including AddBA/DelBA negotiations to firmware. In such scenario, the PMF configuration of the station needs to be provided to driver to enable encryption of AddBA/DelBA action frames. Signed-off-by: SenthilKumar Jegadeesan [fix commit log, documentation] Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 1 + net/mac80211/mlme.c | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'net/mac80211') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 06557e4f9588..94889def2ef5 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1068,6 +1068,7 @@ static int sta_apply_parameters(struct ieee80211_local *local, sta->sta.wme = set & BIT(NL80211_STA_FLAG_WME); if (mask & BIT(NL80211_STA_FLAG_MFP)) { + sta->sta.mfp = !!(set & BIT(NL80211_STA_FLAG_MFP)); if (set & BIT(NL80211_STA_FLAG_MFP)) set_sta_flag(sta, WLAN_STA_MFP); else diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index bc2975e91272..539d6a976cbf 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2966,8 +2966,12 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, rate_control_rate_init(sta); - if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) + if (ifmgd->flags & IEEE80211_STA_MFP_ENABLED) { set_sta_flag(sta, WLAN_STA_MFP); + sta->sta.mfp = true; + } else { + sta->sta.mfp = false; + } sta->sta.wme = elems.wmm_param; -- cgit v1.2.3 From cc57ac536a88604824aca1d3c874fd5252f1e867 Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Mon, 2 Mar 2015 04:54:13 -0500 Subject: mesh_plink: use msecs_to_jiffies for proper time conversion This is primarily an API consolidation and should make things more readable it replaces var * HZ / 1000 by msecs_to_jiffies(var) which also handles corner cases correctly. There is a change of behavior as e.g. for HZ 100, t * HZ / 1000 will return 0 for t < 10 but msecs_to_jiffies will return at least 1 always. Signed-off-by: Nicholas Mc Guire Signed-off-by: Johannes Berg --- net/mac80211/mesh_plink.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index b488e1859b18..4eefd5df5b05 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -17,7 +17,7 @@ #define PLINK_GET_PLID(p) (p + 4) #define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \ - jiffies + HZ * t / 1000)) + jiffies + msecs_to_jiffies(t))) enum plink_event { PLINK_UNDEFINED, @@ -623,7 +623,7 @@ static void mesh_plink_timer(unsigned long data) static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout) { - sta->plink_timer.expires = jiffies + (HZ * timeout / 1000); + sta->plink_timer.expires = jiffies + msecs_to_jiffies(timeout); sta->plink_timer.data = (unsigned long) sta; sta->plink_timer.function = mesh_plink_timer; sta->plink_timeout = timeout; -- cgit v1.2.3 From 0df2f6c11813a3e522f72f6998b00ae8e17dd4df Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Mon, 2 Mar 2015 04:54:14 -0500 Subject: mesh_plink: fixup type of timeout to match usage timeout was being passed as int but assigned from u32/u16 values and used as unsigned type. This is really only for better readability. Signed-off-by: Nicholas Mc Guire Signed-off-by: Johannes Berg --- net/mac80211/mesh_plink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/mac80211') diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 4eefd5df5b05..8465c055a371 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -621,7 +621,7 @@ static void mesh_plink_timer(unsigned long data) sta->llid, sta->plid, reason); } -static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout) +static inline void mesh_plink_timer_set(struct sta_info *sta, u32 timeout) { sta->plink_timer.expires = jiffies + msecs_to_jiffies(timeout); sta->plink_timer.data = (unsigned long) sta; -- cgit v1.2.3 From 4cc0dba95aacfcf235bcf2f8dee9cdd1568aacbf Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 16 Mar 2015 09:05:23 +0100 Subject: mac80211: move netdev stats to common function Move the netdev stats accounting into the common function ieee80211_deliver_skb() that is called in both places. Signed-off-by: Johannes Berg --- net/mac80211/rx.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 1101563357ea..bdabf349c6ee 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2043,6 +2043,9 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx) struct sta_info *dsta; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); + dev->stats.rx_packets++; + dev->stats.rx_bytes += rx->skb->len; + skb = rx->skb; xmit_skb = NULL; @@ -2173,8 +2176,6 @@ ieee80211_rx_h_amsdu(struct ieee80211_rx_data *rx) dev_kfree_skb(rx->skb); continue; } - dev->stats.rx_packets++; - dev->stats.rx_bytes += rx->skb->len; ieee80211_deliver_skb(rx); } @@ -2397,9 +2398,6 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx) rx->skb->dev = dev; - dev->stats.rx_packets++; - dev->stats.rx_bytes += rx->skb->len; - if (local->ps_sdata && local->hw.conf.dynamic_ps_timeout > 0 && !is_multicast_ether_addr( ((struct ethhdr *)rx->skb->data)->h_dest) && -- cgit v1.2.3 From 45ceeee81ecdd437f7ecac77ae79263486c755e8 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 16 Mar 2015 09:08:20 +0100 Subject: mac80211: add comment for rx_path_lock Add a comment explaining how the RX path lock is used. Signed-off-by: Johannes Berg --- net/mac80211/rx.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'net/mac80211') diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index bdabf349c6ee..6accd61ee54b 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -3123,6 +3123,12 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx, goto rxh_next; \ } while (0); + /* Lock here to avoid hitting all of the data used in the RX + * path (e.g. key data, station data, ...) concurrently when + * a frame is released from the reorder buffer due to timeout + * from the timer, potentially concurrently with RX from the + * driver. + */ spin_lock_bh(&rx->local->rx_path_lock); while ((skb = __skb_dequeue(frames))) { -- cgit v1.2.3 From dc5a1ad7bd830b7789ba2950342bdecfe4787945 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 12 Mar 2015 08:53:24 +0200 Subject: mac80211: allow to get wireless_dev structure from ieee80211_vif This will allow mac80211 drivers to call cfg80211 APIs with the right handle. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/util.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'net/mac80211') diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 37d85d36dd2c..e664b28821a2 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -745,6 +745,18 @@ struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev) } EXPORT_SYMBOL_GPL(wdev_to_ieee80211_vif); +struct wireless_dev *ieee80211_vif_to_wdev(struct ieee80211_vif *vif) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + + if (!ieee80211_sdata_running(sdata) || + !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) + return NULL; + + return &sdata->wdev; +} +EXPORT_SYMBOL_GPL(ieee80211_vif_to_wdev); + /* * Nothing should have been stuffed into the workqueue during * the suspend->resume cycle. Since we can't check each caller -- cgit v1.2.3 From f3b0bbb35dac575c571dadeace59bd23ce797d31 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 12 Mar 2015 08:53:25 +0200 Subject: mac80211: refactor drop connection/unlock in CSA processing The schedule_work()/mutex unlocking code is duplicated many times, refactor that to a common place in the function. Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 539d6a976cbf..1999bc08fdcc 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1157,11 +1157,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, if (!conf) { sdata_info(sdata, "no channel context assigned to vif?, disconnecting\n"); - ieee80211_queue_work(&local->hw, - &ifmgd->csa_connection_drop_work); - mutex_unlock(&local->chanctx_mtx); - mutex_unlock(&local->mtx); - return; + goto drop_connection; } chanctx = container_of(conf, struct ieee80211_chanctx, conf); @@ -1170,11 +1166,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) { sdata_info(sdata, "driver doesn't support chan-switch with channel contexts\n"); - ieee80211_queue_work(&local->hw, - &ifmgd->csa_connection_drop_work); - mutex_unlock(&local->chanctx_mtx); - mutex_unlock(&local->mtx); - return; + goto drop_connection; } ch_switch.timestamp = timestamp; @@ -1186,11 +1178,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, if (drv_pre_channel_switch(sdata, &ch_switch)) { sdata_info(sdata, "preparing for channel switch failed, disconnecting\n"); - ieee80211_queue_work(&local->hw, - &ifmgd->csa_connection_drop_work); - mutex_unlock(&local->chanctx_mtx); - mutex_unlock(&local->mtx); - return; + goto drop_connection; } res = ieee80211_vif_reserve_chanctx(sdata, &csa_ie.chandef, @@ -1199,11 +1187,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, sdata_info(sdata, "failed to reserve channel context for channel switch, disconnecting (err=%d)\n", res); - ieee80211_queue_work(&local->hw, - &ifmgd->csa_connection_drop_work); - mutex_unlock(&local->chanctx_mtx); - mutex_unlock(&local->mtx); - return; + goto drop_connection; } mutex_unlock(&local->chanctx_mtx); @@ -1232,6 +1216,11 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, mod_timer(&ifmgd->chswitch_timer, TU_TO_EXP_TIME((csa_ie.count - 1) * cbss->beacon_interval)); + return; + drop_connection: + ieee80211_queue_work(&local->hw, &ifmgd->csa_connection_drop_work); + mutex_unlock(&local->chanctx_mtx); + mutex_unlock(&local->mtx); } static bool -- cgit v1.2.3 From c23e31cf7b55e4d2e462b61dadb4c2d9538d3781 Mon Sep 17 00:00:00 2001 From: Marek Puzyniak Date: Sun, 8 Mar 2015 18:04:22 +0200 Subject: mac80211: initialize rate control earlier for tdls station Currently when TDLS station in driver goes from authenticated to associated state it can not use rate control parameters because rate control is not initialized yet. Some drivers require parameters already initialized by rate control when entering associated state. It can be done by initializing rate control after station transition to associated state but before notifying driver about that. Signed-off-by: Marek Puzyniak Signed-off-by: Arik Nemtsov [fix comment to say 'associated' instead of 'authorized'] Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 94889def2ef5..57b71432ec65 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -977,6 +977,14 @@ static int sta_apply_auth_flags(struct ieee80211_local *local, if (mask & BIT(NL80211_STA_FLAG_ASSOCIATED) && set & BIT(NL80211_STA_FLAG_ASSOCIATED) && !test_sta_flag(sta, WLAN_STA_ASSOC)) { + /* + * When peer becomes associated, init rate control as + * well. Some drivers require rate control initialized + * before drv_sta_state() is called. + */ + if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) + rate_control_rate_init(sta); + ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC); if (ret) return ret; @@ -1378,11 +1386,6 @@ static int ieee80211_change_station(struct wiphy *wiphy, if (err) goto out_err; - /* When peer becomes authorized, init rate control as well */ - if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) && - test_sta_flag(sta, WLAN_STA_AUTHORIZED)) - rate_control_rate_init(sta); - mutex_unlock(&local->sta_mtx); if ((sdata->vif.type == NL80211_IFTYPE_AP || -- cgit v1.2.3 From d66c258278fde25b0454ba0f9ec24874237d6ac5 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 13 Mar 2015 10:54:44 +0100 Subject: mac80211: minstrel_ht: fix rounding issue in MCS duration calculation On very high MCS bitrates, the calculated duration of rates that are next to each other can be very imprecise, due to the small packet size used as reference (1200 bytes). This is most visible in VHT80 nss=2 MCS8/9, for which minstrel shows the same throughput when the probability is also the same. This leads to a bad rate selection for such rates. Fix this issue by introducing an average A-MPDU size factor into the calculation. Signed-off-by: Felix Fietkau Signed-off-by: Johannes Berg --- net/mac80211/rc80211_minstrel_ht.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 80452cfd2dc5..60698fc7042e 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -17,10 +17,11 @@ #include "rc80211_minstrel.h" #include "rc80211_minstrel_ht.h" +#define AVG_AMPDU_SIZE 16 #define AVG_PKT_SIZE 1200 /* Number of bits for an average sized packet */ -#define MCS_NBITS (AVG_PKT_SIZE << 3) +#define MCS_NBITS ((AVG_PKT_SIZE * AVG_AMPDU_SIZE) << 3) /* Number of symbols for a packet with (bps) bits per symbol */ #define MCS_NSYMS(bps) DIV_ROUND_UP(MCS_NBITS, (bps)) @@ -33,7 +34,8 @@ ) /* Transmit duration for the raw data part of an average sized packet */ -#define MCS_DURATION(streams, sgi, bps) MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps))) +#define MCS_DURATION(streams, sgi, bps) \ + (MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps))) / AVG_AMPDU_SIZE) #define BW_20 0 #define BW_40 1 -- cgit v1.2.3 From 8a4988d13734f9340ba8b34467d8ea09347b55d5 Mon Sep 17 00:00:00 2001 From: "Janusz.Dziedzic@tieto.com" Date: Mon, 9 Mar 2015 07:58:15 +0100 Subject: mac80211: IBSS: refactor ieee80211_rx_bss_info Put station specific code in ieee80211_update_sta_info function. Signed-off-by: Janusz Dziedzic Signed-off-by: Johannes Berg --- net/mac80211/ibss.c | 184 ++++++++++++++++++++++++++++------------------------ 1 file changed, 99 insertions(+), 85 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index ee93d7d9aa4b..dec24d436e73 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -965,110 +965,124 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, mgmt->sa, sdata->u.ibss.bssid, NULL, 0, 0, 0); } -static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, - struct ieee80211_mgmt *mgmt, size_t len, - struct ieee80211_rx_status *rx_status, - struct ieee802_11_elems *elems) +static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len, + struct ieee80211_rx_status *rx_status, + struct ieee802_11_elems *elems, + struct ieee80211_channel *channel) { - struct ieee80211_local *local = sdata->local; - struct cfg80211_bss *cbss; - struct ieee80211_bss *bss; struct sta_info *sta; - struct ieee80211_channel *channel; - u64 beacon_timestamp, rx_timestamp; - u32 supp_rates = 0; enum ieee80211_band band = rx_status->band; enum nl80211_bss_scan_width scan_width; + struct ieee80211_local *local = sdata->local; struct ieee80211_supported_band *sband = local->hw.wiphy->bands[band]; bool rates_updated = false; + u32 supp_rates = 0; - channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq); - if (!channel) + if (sdata->vif.type != NL80211_IFTYPE_ADHOC) return; - if (sdata->vif.type == NL80211_IFTYPE_ADHOC && - ether_addr_equal(mgmt->bssid, sdata->u.ibss.bssid)) { + if (!ether_addr_equal(mgmt->bssid, sdata->u.ibss.bssid)) + return; - rcu_read_lock(); - sta = sta_info_get(sdata, mgmt->sa); - - if (elems->supp_rates) { - supp_rates = ieee80211_sta_get_rates(sdata, elems, - band, NULL); - if (sta) { - u32 prev_rates; - - prev_rates = sta->sta.supp_rates[band]; - /* make sure mandatory rates are always added */ - scan_width = NL80211_BSS_CHAN_WIDTH_20; - if (rx_status->flag & RX_FLAG_5MHZ) - scan_width = NL80211_BSS_CHAN_WIDTH_5; - if (rx_status->flag & RX_FLAG_10MHZ) - scan_width = NL80211_BSS_CHAN_WIDTH_10; - - sta->sta.supp_rates[band] = supp_rates | - ieee80211_mandatory_rates(sband, - scan_width); - if (sta->sta.supp_rates[band] != prev_rates) { - ibss_dbg(sdata, - "updated supp_rates set for %pM based on beacon/probe_resp (0x%x -> 0x%x)\n", - sta->sta.addr, prev_rates, - sta->sta.supp_rates[band]); - rates_updated = true; - } - } else { - rcu_read_unlock(); - sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid, - mgmt->sa, supp_rates); + rcu_read_lock(); + sta = sta_info_get(sdata, mgmt->sa); + + if (elems->supp_rates) { + supp_rates = ieee80211_sta_get_rates(sdata, elems, + band, NULL); + if (sta) { + u32 prev_rates; + + prev_rates = sta->sta.supp_rates[band]; + /* make sure mandatory rates are always added */ + scan_width = NL80211_BSS_CHAN_WIDTH_20; + if (rx_status->flag & RX_FLAG_5MHZ) + scan_width = NL80211_BSS_CHAN_WIDTH_5; + if (rx_status->flag & RX_FLAG_10MHZ) + scan_width = NL80211_BSS_CHAN_WIDTH_10; + + sta->sta.supp_rates[band] = supp_rates | + ieee80211_mandatory_rates(sband, scan_width); + if (sta->sta.supp_rates[band] != prev_rates) { + ibss_dbg(sdata, + "updated supp_rates set for %pM based on beacon/probe_resp (0x%x -> 0x%x)\n", + sta->sta.addr, prev_rates, + sta->sta.supp_rates[band]); + rates_updated = true; } + } else { + rcu_read_unlock(); + sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid, + mgmt->sa, supp_rates); } + } - if (sta && elems->wmm_info) - sta->sta.wme = true; - - if (sta && elems->ht_operation && elems->ht_cap_elem && - sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT && - sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_5 && - sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_10) { - /* we both use HT */ - struct ieee80211_ht_cap htcap_ie; - struct cfg80211_chan_def chandef; - - ieee80211_ht_oper_to_chandef(channel, - elems->ht_operation, - &chandef); - - memcpy(&htcap_ie, elems->ht_cap_elem, sizeof(htcap_ie)); - - /* - * fall back to HT20 if we don't use or use - * the other extension channel - */ - if (chandef.center_freq1 != - sdata->u.ibss.chandef.center_freq1) - htcap_ie.cap_info &= - cpu_to_le16(~IEEE80211_HT_CAP_SUP_WIDTH_20_40); - - rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap( - sdata, sband, &htcap_ie, sta); - } + if (sta && elems->wmm_info) + sta->sta.wme = true; - if (sta && rates_updated) { - u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED; - u8 rx_nss = sta->sta.rx_nss; + if (sta && elems->ht_operation && elems->ht_cap_elem && + sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT && + sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_5 && + sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_10) { + /* we both use HT */ + struct ieee80211_ht_cap htcap_ie; + struct cfg80211_chan_def chandef; - /* Force rx_nss recalculation */ - sta->sta.rx_nss = 0; - rate_control_rate_init(sta); - if (sta->sta.rx_nss != rx_nss) - changed |= IEEE80211_RC_NSS_CHANGED; + ieee80211_ht_oper_to_chandef(channel, + elems->ht_operation, + &chandef); - drv_sta_rc_update(local, sdata, &sta->sta, changed); - } + memcpy(&htcap_ie, elems->ht_cap_elem, sizeof(htcap_ie)); - rcu_read_unlock(); + /* + * fall back to HT20 if we don't use or use + * the other extension channel + */ + if (chandef.center_freq1 != sdata->u.ibss.chandef.center_freq1) + htcap_ie.cap_info &= + cpu_to_le16(~IEEE80211_HT_CAP_SUP_WIDTH_20_40); + + rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, + &htcap_ie, + sta); } + if (sta && rates_updated) { + u32 changed = IEEE80211_RC_SUPP_RATES_CHANGED; + u8 rx_nss = sta->sta.rx_nss; + + /* Force rx_nss recalculation */ + sta->sta.rx_nss = 0; + rate_control_rate_init(sta); + if (sta->sta.rx_nss != rx_nss) + changed |= IEEE80211_RC_NSS_CHANGED; + + drv_sta_rc_update(local, sdata, &sta->sta, changed); + } + + rcu_read_unlock(); +} + +static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, + struct ieee80211_mgmt *mgmt, size_t len, + struct ieee80211_rx_status *rx_status, + struct ieee802_11_elems *elems) +{ + struct ieee80211_local *local = sdata->local; + struct cfg80211_bss *cbss; + struct ieee80211_bss *bss; + struct ieee80211_channel *channel; + u64 beacon_timestamp, rx_timestamp; + u32 supp_rates = 0; + enum ieee80211_band band = rx_status->band; + + channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq); + if (!channel) + return; + + ieee80211_update_sta_info(sdata, mgmt, len, rx_status, elems, channel); + bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems, channel); if (!bss) -- cgit v1.2.3 From c7ef38e0ccca25050efed56a35df2e1e93c0b469 Mon Sep 17 00:00:00 2001 From: Cedric Izoard Date: Tue, 17 Mar 2015 10:47:33 +0000 Subject: mac80211: Get IV len from key conf and not cipher scheme When a key is installed using a cipher scheme, set a new internal key flag (KEY_FLAG_CIPHER_SCHEME) on it, to allow distinguishing such keys more easily. In particular, use this flag on the TX path instead of testing the sta->cipher_scheme pointer, as the station is NULL for broad-/multicast message, and use the key's iv_len instead of the cipher scheme information. Signed-off-by: Cedric Izoard [add missing documentation, rewrite commit message] Signed-off-by: Johannes Berg --- net/mac80211/key.c | 1 + net/mac80211/key.h | 2 ++ net/mac80211/wpa.c | 13 ++++++------- 3 files changed, 9 insertions(+), 7 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 0825d76edcfc..2291cd730091 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -492,6 +492,7 @@ ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, for (j = 0; j < len; j++) key->u.gen.rx_pn[i][j] = seq[len - j - 1]; + key->flags |= KEY_FLAG_CIPHER_SCHEME; } } memcpy(key->conf.key, key_data, key_len); diff --git a/net/mac80211/key.h b/net/mac80211/key.h index d57a9915494f..c5a31835be0e 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h @@ -30,10 +30,12 @@ struct sta_info; * @KEY_FLAG_UPLOADED_TO_HARDWARE: Indicates that this key is present * in the hardware for TX crypto hardware acceleration. * @KEY_FLAG_TAINTED: Key is tainted and packets should be dropped. + * @KEY_FLAG_CIPHER_SCHEME: This key is for a hardware cipher scheme */ enum ieee80211_internal_key_flags { KEY_FLAG_UPLOADED_TO_HARDWARE = BIT(0), KEY_FLAG_TAINTED = BIT(1), + KEY_FLAG_CIPHER_SCHEME = BIT(2), }; enum ieee80211_internal_tkip_state { diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index 75de6fac40d1..9d63d93c836e 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -780,9 +780,8 @@ ieee80211_crypto_cs_encrypt(struct ieee80211_tx_data *tx, struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_key *key = tx->key; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - const struct ieee80211_cipher_scheme *cs = key->sta->cipher_scheme; int hdrlen; - u8 *pos; + u8 *pos, iv_len = key->conf.iv_len; if (info->control.hw_key && !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) { @@ -790,14 +789,14 @@ ieee80211_crypto_cs_encrypt(struct ieee80211_tx_data *tx, return TX_CONTINUE; } - if (unlikely(skb_headroom(skb) < cs->hdr_len && - pskb_expand_head(skb, cs->hdr_len, 0, GFP_ATOMIC))) + if (unlikely(skb_headroom(skb) < iv_len && + pskb_expand_head(skb, iv_len, 0, GFP_ATOMIC))) return TX_DROP; hdrlen = ieee80211_hdrlen(hdr->frame_control); - pos = skb_push(skb, cs->hdr_len); - memmove(pos, pos + cs->hdr_len, hdrlen); + pos = skb_push(skb, iv_len); + memmove(pos, pos + iv_len, hdrlen); return TX_CONTINUE; } @@ -1217,7 +1216,7 @@ ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx) if (!info->control.hw_key) return TX_DROP; - if (tx->key->sta->cipher_scheme) { + if (tx->key->flags & KEY_FLAG_CIPHER_SCHEME) { res = ieee80211_crypto_cs_encrypt(tx, skb); if (res != TX_CONTINUE) return res; -- cgit v1.2.3 From e8f4fb7c7c6b25dc0495f1fd43b03444f0a5c6e3 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 20 Mar 2015 11:37:36 +0100 Subject: mac80211: remove drop_unencrypted code This mechanism was historic, and only ever used by IBSS, which also doesn't need to have it as it properly manages station's 802.1X PAE state (or, with WEP, always has a key.) Remove the mechanism to clean up the code. Signed-off-by: Johannes Berg --- net/mac80211/debugfs.c | 2 -- net/mac80211/debugfs_netdev.c | 2 -- net/mac80211/ibss.c | 4 ---- net/mac80211/ieee80211_i.h | 3 --- net/mac80211/iface.c | 1 - net/mac80211/rx.c | 3 +-- net/mac80211/tx.c | 17 +---------------- 7 files changed, 2 insertions(+), 30 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index 74830ce25e74..23813ebb349c 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -274,8 +274,6 @@ void debugfs_hw_add(struct ieee80211_local *local) #ifdef CONFIG_MAC80211_DEBUG_COUNTERS DEBUGFS_STATS_ADD(tx_handlers_drop, local->tx_handlers_drop); DEBUGFS_STATS_ADD(tx_handlers_queued, local->tx_handlers_queued); - DEBUGFS_STATS_ADD(tx_handlers_drop_unencrypted, - local->tx_handlers_drop_unencrypted); DEBUGFS_STATS_ADD(tx_handlers_drop_fragment, local->tx_handlers_drop_fragment); DEBUGFS_STATS_ADD(tx_handlers_drop_wep, diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index c68896adfa96..29236e832e44 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -177,7 +177,6 @@ static ssize_t ieee80211_if_write_##name(struct file *file, \ IEEE80211_IF_FILE_R(name) /* common attributes */ -IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC); IEEE80211_IF_FILE(rc_rateidx_mask_2ghz, rc_rateidx_mask[IEEE80211_BAND_2GHZ], HEX); IEEE80211_IF_FILE(rc_rateidx_mask_5ghz, rc_rateidx_mask[IEEE80211_BAND_5GHZ], @@ -562,7 +561,6 @@ IEEE80211_IF_FILE(dot11MeshAwakeWindowDuration, static void add_common_files(struct ieee80211_sub_if_data *sdata) { - DEBUGFS_ADD(drop_unencrypted); DEBUGFS_ADD(rc_rateidx_mask_2ghz); DEBUGFS_ADD(rc_rateidx_mask_5ghz); DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz); diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index dec24d436e73..17eda3575d7d 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -249,8 +249,6 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, if (presp) kfree_rcu(presp, rcu_head); - sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0; - /* make a copy of the chandef, it could be modified below. */ chandef = *req_chandef; chan = chandef.chan; @@ -1303,8 +1301,6 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) if (ifibss->privacy) capability |= WLAN_CAPABILITY_PRIVACY; - else - sdata->drop_unencrypted = 0; __ieee80211_sta_join_ibss(sdata, bssid, sdata->vif.bss_conf.beacon_int, &ifibss->chandef, ifibss->basic_rates, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 0266c573c366..4f4bcc6c5dfe 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -818,8 +818,6 @@ struct ieee80211_sub_if_data { unsigned long state; - int drop_unencrypted; - char name[IFNAMSIZ]; /* Fragment table for host-based reassembly */ @@ -1262,7 +1260,6 @@ struct ieee80211_local { /* TX/RX handler statistics */ unsigned int tx_handlers_drop; unsigned int tx_handlers_queued; - unsigned int tx_handlers_drop_unencrypted; unsigned int tx_handlers_drop_fragment; unsigned int tx_handlers_drop_wep; unsigned int tx_handlers_drop_not_assoc; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 81a27516813e..9689d3b1133b 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1508,7 +1508,6 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata, } /* reset some values that shouldn't be kept across type changes */ - sdata->drop_unencrypted = 0; if (type == NL80211_IFTYPE_STATION) sdata->u.mgd.use_4addr = false; diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 6accd61ee54b..9eab44317c87 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1912,8 +1912,7 @@ static int ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc) /* Drop unencrypted frames if key is set. */ if (unlikely(!ieee80211_has_protected(fc) && !ieee80211_is_nullfunc(fc) && - ieee80211_is_data(fc) && - (rx->key || rx->sdata->drop_unencrypted))) + ieee80211_is_data(fc) && rx->key)) return -EACCES; return 0; diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 7e7de811e6ad..0bae03bca49e 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -593,23 +593,8 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx) else if (!is_multicast_ether_addr(hdr->addr1) && (key = rcu_dereference(tx->sdata->default_unicast_key))) tx->key = key; - else if (info->flags & IEEE80211_TX_CTL_INJECTED) - tx->key = NULL; - else if (!tx->sdata->drop_unencrypted) - tx->key = NULL; - else if (tx->skb->protocol == tx->sdata->control_port_protocol) - tx->key = NULL; - else if (ieee80211_is_robust_mgmt_frame(tx->skb) && - !(ieee80211_is_action(hdr->frame_control) && - tx->sta && test_sta_flag(tx->sta, WLAN_STA_MFP))) - tx->key = NULL; - else if (ieee80211_is_mgmt(hdr->frame_control) && - !ieee80211_is_robust_mgmt_frame(tx->skb)) + else tx->key = NULL; - else { - I802_DEBUG_INC(tx->local->tx_handlers_drop_unencrypted); - return TX_DROP; - } if (tx->key) { bool skip_hw = false; -- cgit v1.2.3 From 5041006c42537cce1d3619521f50c29b3bf0a633 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 20 Mar 2015 13:29:29 +0100 Subject: mac80211: don't look up destination station twice There's no need to look up the destination station twice while building the 802.11 header for a given frame if the frame will actually be transmitted to the station we initially looked up. This happens for 4-addr VLAN interfaces and TDLS connections, which both directly send the frame to the station they looked up, though in the case of TDLS some station conditions need to be checked. To avoid that, add a variable indicating that we've looked up the station that the frame is going to be transmitted to, and avoid the lookup/flag checking if it already has been done. In the TDLS case, also move the authorized/wme_sta flag assignment to the correct place, i.e. only when that station is really used. Before this change, the new lookup should always have succeeded so that the potentially erroneous data would be overwritten. Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 0bae03bca49e..dcf60ee38b93 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1816,6 +1816,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, bool wme_sta = false, authorized = false, tdls_auth = false; bool tdls_peer = false, tdls_setup_frame = false; bool multicast; + bool have_station = false; u16 info_id = 0; struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_sub_if_data *ap_sdata; @@ -1840,6 +1841,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, hdrlen = 30; authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); wme_sta = sta->sta.wme; + have_station = true; } ap_sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); @@ -1956,9 +1958,6 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) { sta = sta_info_get(sdata, skb->data); if (sta) { - authorized = test_sta_flag(sta, - WLAN_STA_AUTHORIZED); - wme_sta = sta->sta.wme; tdls_peer = test_sta_flag(sta, WLAN_STA_TDLS_PEER); tdls_auth = test_sta_flag(sta, @@ -1990,6 +1989,9 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); memcpy(hdr.addr3, sdata->u.mgd.bssid, ETH_ALEN); hdrlen = 24; + have_station = true; + authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); + wme_sta = sta->sta.wme; } else if (sdata->u.mgd.use_4addr && cpu_to_be16(ethertype) != sdata->control_port_protocol) { fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | @@ -2052,7 +2054,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, * in AP mode) */ multicast = is_multicast_ether_addr(hdr.addr1); - if (!multicast) { + if (!multicast && !have_station) { sta = sta_info_get(sdata, hdr.addr1); if (sta) { authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); -- cgit v1.2.3 From a8d15ff0050aef96a14a185b5138f01f98109b40 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 20 Mar 2015 14:05:02 +0100 Subject: mac80211: drop 4-addr VLAN frames earlier if not connected If there's no station on the 4-addr VLAN interface, then frames cannot be transmitted. Drop such frames earlier, before setting up all the information for them. We should keep the old check though since that code might be used for other internally-generated frames. Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'net/mac80211') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index dcf60ee38b93..1d6344679bf1 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1842,6 +1842,9 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); wme_sta = sta->sta.wme; have_station = true; + } else if (sdata->wdev.use_4addr) { + ret = -ENOLINK; + goto free; } ap_sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); -- cgit v1.2.3 From e33f5569aafadfa68aea32457a9246818d3dfe48 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 20 Mar 2015 16:01:52 +0100 Subject: mac80211: mesh: avoid pointless station lookup In ieee80211_build_hdr(), the station is looked up to build the header correctly (QoS field) and to check for authorization. For mesh, authorization isn't checked here, and QoS capability is mandatory, so the station lookup can be avoided. Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 1d6344679bf1..5af35ef35322 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2052,12 +2052,14 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, } /* - * There's no need to try to look up the destination - * if it is a multicast address (which can only happen - * in AP mode) + * There's no need to try to look up the destination station + * if it is a multicast address. In mesh, there's no need to + * look up the station at all as it always must be QoS capable + * and mesh mode checks authorization later. */ multicast = is_multicast_ether_addr(hdr.addr1); - if (!multicast && !have_station) { + if (!multicast && !have_station && + !ieee80211_vif_is_mesh(&sdata->vif)) { sta = sta_info_get(sdata, hdr.addr1); if (sta) { authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); -- cgit v1.2.3 From 7c10770f995820ec1ed26c761b1583bfc4d88872 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 20 Mar 2015 14:18:27 +0100 Subject: mac80211: avoid duplicate TX path station lookup Instead of looking up the destination station twice in the TX path (first to build the header, and then for control processing), save it when building the header and use it later in the TX path. To avoid having to look up the station in the many callers, allow those to pass %NULL which keeps the existing lookup. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 2 +- net/mac80211/ieee80211_i.h | 3 +- net/mac80211/sta_info.c | 2 +- net/mac80211/tx.c | 73 +++++++++++++++++++++++++++++----------------- 4 files changed, 50 insertions(+), 30 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 57b71432ec65..0aaf8b04a738 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -3576,7 +3576,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, nullfunc->qos_ctrl = cpu_to_le16(7); local_bh_disable(); - ieee80211_xmit(sdata, skb); + ieee80211_xmit(sdata, sta, skb); local_bh_enable(); rcu_read_unlock(); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 4f4bcc6c5dfe..ebc8135e0aaa 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1745,7 +1745,8 @@ 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, bool bss_notify); -void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb); +void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, + struct sta_info *sta, struct sk_buff *skb); void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, int tid, diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index d94004e7ce37..aacaa1a85e63 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -1231,7 +1231,7 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, } info->band = chanctx_conf->def.chan->band; - ieee80211_xmit(sdata, skb); + ieee80211_xmit(sdata, sta, skb); rcu_read_unlock(); } diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 5af35ef35322..34b0e7545cc5 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1120,11 +1120,13 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx, /* * initialises @tx + * pass %NULL for the station if unknown, a valid pointer if known + * or an ERR_PTR() if the station is known not to exist */ static ieee80211_tx_result ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, struct ieee80211_tx_data *tx, - struct sk_buff *skb) + struct sta_info *sta, struct sk_buff *skb) { struct ieee80211_local *local = sdata->local; struct ieee80211_hdr *hdr; @@ -1147,17 +1149,22 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, hdr = (struct ieee80211_hdr *) skb->data; - if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { - tx->sta = rcu_dereference(sdata->u.vlan.sta); - if (!tx->sta && sdata->dev->ieee80211_ptr->use_4addr) - return TX_DROP; - } else if (info->flags & (IEEE80211_TX_CTL_INJECTED | - IEEE80211_TX_INTFL_NL80211_FRAME_TX) || - tx->sdata->control_port_protocol == tx->skb->protocol) { - tx->sta = sta_info_get_bss(sdata, hdr->addr1); + if (likely(sta)) { + if (!IS_ERR(sta)) + tx->sta = sta; + } else { + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { + tx->sta = rcu_dereference(sdata->u.vlan.sta); + if (!tx->sta && sdata->wdev.use_4addr) + return TX_DROP; + } else if (info->flags & (IEEE80211_TX_INTFL_NL80211_FRAME_TX | + IEEE80211_TX_CTL_INJECTED) || + tx->sdata->control_port_protocol == tx->skb->protocol) { + tx->sta = sta_info_get_bss(sdata, hdr->addr1); + } + if (!tx->sta && !is_multicast_ether_addr(hdr->addr1)) + tx->sta = sta_info_get(sdata, hdr->addr1); } - if (!tx->sta && !is_multicast_ether_addr(hdr->addr1)) - tx->sta = sta_info_get(sdata, hdr->addr1); if (tx->sta && ieee80211_is_data_qos(hdr->frame_control) && !ieee80211_is_qos_nullfunc(hdr->frame_control) && @@ -1407,7 +1414,7 @@ bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw, struct ieee80211_tx_data tx; struct sk_buff *skb2; - if (ieee80211_tx_prepare(sdata, &tx, skb) == TX_DROP) + if (ieee80211_tx_prepare(sdata, &tx, NULL, skb) == TX_DROP) return false; info->band = band; @@ -1440,7 +1447,8 @@ EXPORT_SYMBOL(ieee80211_tx_prepare_skb); * Returns false if the frame couldn't be transmitted but was queued instead. */ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, bool txpending) + struct sta_info *sta, struct sk_buff *skb, + bool txpending) { struct ieee80211_local *local = sdata->local; struct ieee80211_tx_data tx; @@ -1456,7 +1464,7 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata, /* initialises tx */ led_len = skb->len; - res_prepare = ieee80211_tx_prepare(sdata, &tx, skb); + res_prepare = ieee80211_tx_prepare(sdata, &tx, sta, skb); if (unlikely(res_prepare == TX_DROP)) { ieee80211_free_txskb(&local->hw, skb); @@ -1512,7 +1520,8 @@ static int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata, return 0; } -void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) +void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, + struct sta_info *sta, struct sk_buff *skb) { struct ieee80211_local *local = sdata->local; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -1547,7 +1556,7 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) } ieee80211_set_qos_hdr(sdata, skb); - ieee80211_tx(sdata, skb, false); + ieee80211_tx(sdata, sta, skb, false); } static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb) @@ -1768,7 +1777,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, goto fail_rcu; info->band = chandef->chan->band; - ieee80211_xmit(sdata, skb); + ieee80211_xmit(sdata, NULL, skb); rcu_read_unlock(); return NETDEV_TX_OK; @@ -1799,7 +1808,8 @@ fail: * Returns: the (possibly reallocated) skb or an ERR_PTR() code */ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, - struct sk_buff *skb, u32 info_flags) + struct sk_buff *skb, u32 info_flags, + struct sta_info **sta_out) { struct ieee80211_local *local = sdata->local; struct ieee80211_tx_info *info; @@ -1842,6 +1852,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); wme_sta = sta->sta.wme; have_station = true; + *sta_out = sta; } else if (sdata->wdev.use_4addr) { ret = -ENOLINK; goto free; @@ -1995,6 +2006,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, have_station = true; authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); wme_sta = sta->sta.wme; + *sta_out = sta; } else if (sdata->u.mgd.use_4addr && cpu_to_be16(ethertype) != sdata->control_port_protocol) { fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | @@ -2058,13 +2070,18 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, * and mesh mode checks authorization later. */ multicast = is_multicast_ether_addr(hdr.addr1); - if (!multicast && !have_station && - !ieee80211_vif_is_mesh(&sdata->vif)) { - sta = sta_info_get(sdata, hdr.addr1); + if (multicast) { + *sta_out = ERR_PTR(-ENOENT); + } else if (!have_station && !ieee80211_vif_is_mesh(&sdata->vif)) { + if (sdata->control_port_protocol == skb->protocol) + sta = sta_info_get_bss(sdata, hdr.addr1); + else + sta = sta_info_get(sdata, hdr.addr1); if (sta) { authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); wme_sta = sta->sta.wme; } + *sta_out = sta ?: ERR_PTR(-ENOENT); } /* For mesh, the use of the QoS header is mandatory */ @@ -2242,6 +2259,7 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb, u32 info_flags) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct sta_info *sta = NULL; if (unlikely(skb->len < ETH_HLEN)) { kfree_skb(skb); @@ -2250,7 +2268,7 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb, rcu_read_lock(); - skb = ieee80211_build_hdr(sdata, skb, info_flags); + skb = ieee80211_build_hdr(sdata, skb, info_flags, &sta); if (IS_ERR(skb)) goto out; @@ -2258,7 +2276,7 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb, dev->stats.tx_bytes += skb->len; dev->trans_start = jiffies; - ieee80211_xmit(sdata, skb); + ieee80211_xmit(sdata, sta, skb); out: rcu_read_unlock(); } @@ -2286,10 +2304,11 @@ ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata, .local = sdata->local, .sdata = sdata, }; + struct sta_info *sta_ignore; rcu_read_lock(); - skb = ieee80211_build_hdr(sdata, skb, info_flags); + skb = ieee80211_build_hdr(sdata, skb, info_flags, &sta_ignore); if (IS_ERR(skb)) goto out; @@ -2347,7 +2366,7 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local, return true; } info->band = chanctx_conf->def.chan->band; - result = ieee80211_tx(sdata, skb, true); + result = ieee80211_tx(sdata, NULL, skb, true); } else { struct sk_buff_head skbs; @@ -3085,7 +3104,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, if (sdata->vif.type == NL80211_IFTYPE_AP) sdata = IEEE80211_DEV_TO_SUB_IF(skb->dev); - if (!ieee80211_tx_prepare(sdata, &tx, skb)) + if (!ieee80211_tx_prepare(sdata, &tx, NULL, skb)) break; dev_kfree_skb_any(skb); } @@ -3217,6 +3236,6 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata, */ local_bh_disable(); IEEE80211_SKB_CB(skb)->band = band; - ieee80211_xmit(sdata, skb); + ieee80211_xmit(sdata, NULL, skb); local_bh_enable(); } -- cgit v1.2.3 From 2c158887f1185e04b3763ae346da9f71fcbc4429 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 12 Mar 2015 19:28:31 +0100 Subject: mac80211: agg-tx: avoid sending DelBA with sta->lock held The rate control locking caused a potential deadlock here due to the locks being acquired in different orders, so that change cannot yet be applied. However, there's no fundamental reason for this code to hold the sta->lock while transmitting frames. Clearly it's better not to hold the lock for longer periods of time, which can happen here since we call all the way down to the driver. Change the code a bit to not hold it while doing that. Signed-off-by: Johannes Berg --- net/mac80211/agg-tx.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index a360c15cc978..668524aeb0b2 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -793,6 +793,7 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) struct ieee80211_local *local = sdata->local; struct sta_info *sta; struct tid_ampdu_tx *tid_tx; + bool send_delba = false; trace_api_stop_tx_ba_cb(sdata, ra, tid); @@ -824,13 +825,17 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) } if (tid_tx->stop_initiator == WLAN_BACK_INITIATOR && tid_tx->tx_stop) - ieee80211_send_delba(sta->sdata, ra, tid, - WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE); + send_delba = true; ieee80211_remove_tid_tx(sta, tid); unlock_sta: spin_unlock_bh(&sta->lock); + + if (send_delba) + ieee80211_send_delba(sdata, ra, tid, + WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE); + mutex_unlock(&sta->ampdu_mlme.mtx); unlock: mutex_unlock(&local->sta_mtx); -- cgit v1.2.3 From a818292952bbfad12ec5a32ab01330cb1ceed013 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 16 Mar 2015 23:23:34 +0200 Subject: mac80211: convert rssi_callback() to event_callback() We will be able to add more events, such as MLME events and others. The low level driver may be interested in knowing about these events to dump firmware data upon failures, or to change parameters in case connection attempts fail etc... Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/driver-ops.h | 12 ++++++------ net/mac80211/mlme.c | 9 +++++++-- net/mac80211/trace.h | 14 +++++++------- 3 files changed, 20 insertions(+), 15 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index fdeda17b8dd2..0a39d3db951a 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -941,13 +941,13 @@ static inline void drv_set_rekey_data(struct ieee80211_local *local, trace_drv_return_void(local); } -static inline void drv_rssi_callback(struct ieee80211_local *local, - struct ieee80211_sub_if_data *sdata, - const enum ieee80211_rssi_event event) +static inline void drv_event_callback(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + const struct ieee80211_event *event) { - trace_drv_rssi_callback(local, sdata, event); - if (local->ops->rssi_callback) - local->ops->rssi_callback(&local->hw, &sdata->vif, event); + trace_drv_event_callback(local, sdata, event); + if (local->ops->event_callback) + local->ops->event_callback(&local->hw, &sdata->vif, event); trace_drv_return_void(local); } diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 1999bc08fdcc..a8c8fe4c9f49 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3291,6 +3291,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ifmgd->count_beacon_signal >= IEEE80211_SIGNAL_AVE_MIN_COUNT) { int sig = ifmgd->ave_beacon_signal; int last_sig = ifmgd->last_ave_beacon_signal; + struct ieee80211_event event = { + .type = RSSI_EVENT, + }; /* * if signal crosses either of the boundaries, invoke callback @@ -3299,12 +3302,14 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, if (sig > ifmgd->rssi_max_thold && (last_sig <= ifmgd->rssi_min_thold || last_sig == 0)) { ifmgd->last_ave_beacon_signal = sig; - drv_rssi_callback(local, sdata, RSSI_EVENT_HIGH); + event.u.rssi.data = RSSI_EVENT_HIGH; + drv_event_callback(local, sdata, &event); } else if (sig < ifmgd->rssi_min_thold && (last_sig >= ifmgd->rssi_max_thold || last_sig == 0)) { ifmgd->last_ave_beacon_signal = sig; - drv_rssi_callback(local, sdata, RSSI_EVENT_LOW); + event.u.rssi.data = RSSI_EVENT_LOW; + drv_event_callback(local, sdata, &event); } } diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 263a9561eb26..e9e462b349e5 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -1256,28 +1256,28 @@ TRACE_EVENT(drv_set_rekey_data, LOCAL_PR_ARG, VIF_PR_ARG) ); -TRACE_EVENT(drv_rssi_callback, +TRACE_EVENT(drv_event_callback, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, - enum ieee80211_rssi_event rssi_event), + const struct ieee80211_event *_event), - TP_ARGS(local, sdata, rssi_event), + TP_ARGS(local, sdata, _event), TP_STRUCT__entry( LOCAL_ENTRY VIF_ENTRY - __field(u32, rssi_event) + __field(u32, type) ), TP_fast_assign( LOCAL_ASSIGN; VIF_ASSIGN; - __entry->rssi_event = rssi_event; + __entry->type = _event->type; ), TP_printk( - LOCAL_PR_FMT VIF_PR_FMT " rssi_event:%d", - LOCAL_PR_ARG, VIF_PR_ARG, __entry->rssi_event + LOCAL_PR_FMT VIF_PR_FMT " event:%d", + LOCAL_PR_ARG, VIF_PR_ARG, __entry->type ) ); -- cgit v1.2.3 From a9409093d23c822d13a73f8d2df7e6fa987ae485 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 16 Mar 2015 23:23:35 +0200 Subject: mac80211: notify the driver about authentication status This can allow the driver to take action based on the success / failure of the authentication. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'net/mac80211') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index a8c8fe4c9f49..7865998d69dd 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2495,6 +2495,10 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, u8 bssid[ETH_ALEN]; u16 auth_alg, auth_transaction, status_code; struct sta_info *sta; + struct ieee80211_event event = { + .type = MLME_EVENT, + .u.mlme.data = AUTH_EVENT, + }; sdata_assert_lock(sdata); @@ -2527,6 +2531,9 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, mgmt->sa, status_code); ieee80211_destroy_auth_data(sdata, false); cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len); + event.u.mlme.status = MLME_DENIED; + event.u.mlme.reason = status_code; + drv_event_callback(sdata->local, sdata, &event); return; } @@ -2549,6 +2556,8 @@ static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata, return; } + event.u.mlme.status = MLME_SUCCESS; + drv_event_callback(sdata->local, sdata, &event); sdata_info(sdata, "authenticated\n"); ifmgd->auth_data->done = true; ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC; @@ -3805,12 +3814,18 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) ieee80211_destroy_auth_data(sdata, false); } else if (ieee80211_probe_auth(sdata)) { u8 bssid[ETH_ALEN]; + struct ieee80211_event event = { + .type = MLME_EVENT, + .u.mlme.data = AUTH_EVENT, + .u.mlme.status = MLME_TIMEOUT, + }; memcpy(bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN); ieee80211_destroy_auth_data(sdata, false); cfg80211_auth_timeout(sdata->dev, bssid); + drv_event_callback(sdata->local, sdata, &event); } } else if (ifmgd->auth_data && ifmgd->auth_data->timeout_started) run_again(sdata, ifmgd->auth_data->timeout); -- cgit v1.2.3 From d0d1a12f9cc665f3fcac5288ab84429a91abd4e9 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 16 Mar 2015 23:23:36 +0200 Subject: mac80211: notify the driver about association status This can allow the driver to take action based on the success / failure of the association. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'net/mac80211') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 7865998d69dd..b6817c4c4449 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3041,6 +3041,10 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, u8 *pos; bool reassoc; struct cfg80211_bss *bss; + struct ieee80211_event event = { + .type = MLME_EVENT, + .u.mlme.data = ASSOC_EVENT, + }; sdata_assert_lock(sdata); @@ -3092,6 +3096,9 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, sdata_info(sdata, "%pM denied association (code=%d)\n", mgmt->sa, status_code); ieee80211_destroy_assoc_data(sdata, false); + event.u.mlme.status = MLME_DENIED; + event.u.mlme.reason = status_code; + drv_event_callback(sdata->local, sdata, &event); } else { if (!ieee80211_assoc_success(sdata, bss, mgmt, len)) { /* oops -- internal error -- send timeout for now */ @@ -3099,6 +3106,8 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata, cfg80211_assoc_timeout(sdata->dev, bss); return; } + event.u.mlme.status = MLME_SUCCESS; + drv_event_callback(sdata->local, sdata, &event); sdata_info(sdata, "associated\n"); /* @@ -3835,9 +3844,15 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata) if ((ifmgd->assoc_data->need_beacon && !ifmgd->have_beacon) || ieee80211_do_assoc(sdata)) { struct cfg80211_bss *bss = ifmgd->assoc_data->bss; + struct ieee80211_event event = { + .type = MLME_EVENT, + .u.mlme.data = ASSOC_EVENT, + .u.mlme.status = MLME_TIMEOUT, + }; ieee80211_destroy_assoc_data(sdata, false); cfg80211_assoc_timeout(sdata->dev, bss); + drv_event_callback(sdata->local, sdata, &event); } } else if (ifmgd->assoc_data && ifmgd->assoc_data->timeout_started) run_again(sdata, ifmgd->assoc_data->timeout); -- cgit v1.2.3 From a90faa9d6449f3861428bafeaf4d1fcf2be4ba7f Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 16 Mar 2015 23:23:37 +0200 Subject: mac80211: notify the driver about deauth This can allow the driver to take action based on the reason of the deauth. Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 60 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 18 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index b6817c4c4449..22b125913661 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2345,6 +2345,24 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ieee80211_ap_probereq_get); +static void ieee80211_report_disconnect(struct ieee80211_sub_if_data *sdata, + const u8 *buf, size_t len, bool tx, + u16 reason) +{ + struct ieee80211_event event = { + .type = MLME_EVENT, + .u.mlme.data = tx ? DEAUTH_TX_EVENT : DEAUTH_RX_EVENT, + .u.mlme.reason = reason, + }; + + if (tx) + cfg80211_tx_mlme_mgmt(sdata->dev, buf, len); + else + cfg80211_rx_mlme_mgmt(sdata->dev, buf, len); + + drv_event_callback(sdata->local, sdata, &event); +} + static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) { struct ieee80211_local *local = sdata->local; @@ -2370,8 +2388,9 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) } mutex_unlock(&local->mtx); - cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, - IEEE80211_DEAUTH_FRAME_LEN); + ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true, + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); + sdata_unlock(sdata); } @@ -2676,7 +2695,7 @@ static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, ieee80211_set_disassoc(sdata, 0, 0, false, NULL); - cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len); + ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false, reason_code); } @@ -2702,7 +2721,7 @@ static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata, ieee80211_set_disassoc(sdata, 0, 0, false, NULL); - cfg80211_rx_mlme_mgmt(sdata->dev, (u8 *)mgmt, len); + ieee80211_report_disconnect(sdata, (u8 *)mgmt, len, false, reason_code); } static void ieee80211_get_rates(struct ieee80211_supported_band *sband, @@ -3509,8 +3528,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, WLAN_REASON_DEAUTH_LEAVING, true, deauth_buf); - cfg80211_tx_mlme_mgmt(sdata->dev, deauth_buf, - sizeof(deauth_buf)); + ieee80211_report_disconnect(sdata, deauth_buf, + sizeof(deauth_buf), true, + WLAN_REASON_DEAUTH_LEAVING); return; } @@ -3628,8 +3648,8 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata, ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason, tx, frame_buf); - cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, - IEEE80211_DEAUTH_FRAME_LEN); + ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true, + reason); } static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata) @@ -4507,8 +4527,9 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, WLAN_REASON_UNSPECIFIED, false, frame_buf); - cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, - sizeof(frame_buf)); + ieee80211_report_disconnect(sdata, frame_buf, + sizeof(frame_buf), true, + WLAN_REASON_UNSPECIFIED); } sdata_info(sdata, "authenticate with %pM\n", req->bss->bssid); @@ -4608,8 +4629,9 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, WLAN_REASON_UNSPECIFIED, false, frame_buf); - cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, - sizeof(frame_buf)); + ieee80211_report_disconnect(sdata, frame_buf, + sizeof(frame_buf), true, + WLAN_REASON_UNSPECIFIED); } if (ifmgd->auth_data && !ifmgd->auth_data->done) { @@ -4899,8 +4921,9 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, req->reason_code, tx, frame_buf); ieee80211_destroy_auth_data(sdata, false); - cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, - IEEE80211_DEAUTH_FRAME_LEN); + ieee80211_report_disconnect(sdata, frame_buf, + sizeof(frame_buf), true, + req->reason_code); return 0; } @@ -4914,8 +4937,9 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, req->reason_code, tx, frame_buf); - cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, - IEEE80211_DEAUTH_FRAME_LEN); + ieee80211_report_disconnect(sdata, frame_buf, + sizeof(frame_buf), true, + req->reason_code); return 0; } @@ -4947,8 +4971,8 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata, req->reason_code, !req->local_state_change, frame_buf); - cfg80211_tx_mlme_mgmt(sdata->dev, frame_buf, - IEEE80211_DEAUTH_FRAME_LEN); + ieee80211_report_disconnect(sdata, frame_buf, sizeof(frame_buf), true, + req->reason_code); return 0; } -- cgit v1.2.3 From 7d830a198689bd33057923e0370147d43241c7f0 Mon Sep 17 00:00:00 2001 From: David Spinadel Date: Tue, 17 Mar 2015 19:58:38 +0200 Subject: mac80211: stop scan before connection Stop scan before authentication or association to make sure that nothing interferes with connection flow. Currently mac80211 defers RX auth and assoc packets (among other ones) until after the scan is complete, so auth during scan is likely to fail if scan took too much time. Signed-off-by: David Spinadel Reviewed-by: Luciano Coelho Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net/mac80211') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 22b125913661..4a01a836f870 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -4439,6 +4439,10 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, } else WARN_ON_ONCE(!ether_addr_equal(ifmgd->bssid, cbss->bssid)); + /* Cancel scan to ensure that nothing interferes with connection */ + if (local->scanning) + ieee80211_scan_cancel(local); + return 0; } -- cgit v1.2.3 From a38700dd486f3def34cef47d00e2d360a04a7bc8 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Wed, 18 Mar 2015 08:46:08 +0200 Subject: cfg/mac80211: add regulatory classes IE during TDLS setup Seems Broadcom TDLS peers (Nexus 5, Xperia Z3) refuse to allow TDLS connection when channel-switching is supported but the regulatory classes IE is missing from the setup request. Add a chandef to reg-class translation function to cfg80211 and use it to add the required IE during setup. For now add only the current regulatory class as supported - it is enough to resolve the compatibility issue. Signed-off-by: Arik Nemtsov Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/tdls.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'net/mac80211') diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index bc7e4049896f..79ed59acf0d4 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -136,6 +136,24 @@ ieee80211_tdls_add_supp_channels(struct ieee80211_sub_if_data *sdata, *pos = 2 * subband_cnt; } +static void ieee80211_tdls_add_oper_classes(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) +{ + u8 *pos; + u8 op_class; + + if (!ieee80211_chandef_to_operating_class(&sdata->vif.bss_conf.chandef, + &op_class)) + return; + + pos = skb_put(skb, 4); + *pos++ = WLAN_EID_SUPPORTED_REGULATORY_CLASSES; + *pos++ = 2; /* len */ + + *pos++ = op_class; + *pos++ = op_class; /* give current operating class as alternate too */ +} + static void ieee80211_tdls_add_bss_coex_ie(struct sk_buff *skb) { u8 *pos = (void *)skb_put(skb, 3); @@ -350,6 +368,8 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, } } + ieee80211_tdls_add_oper_classes(sdata, skb); + /* * with TDLS we can switch channels, and HT-caps are not necessarily * the same on all bands. The specification limits the setup to a @@ -786,6 +806,7 @@ ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata, 50 + /* supported channels */ 3 + /* 40/20 BSS coex */ 4 + /* AID */ + 4 + /* oper classes */ extra_ies_len + sizeof(struct ieee80211_tdls_lnkie)); if (!skb) -- cgit v1.2.3 From 8f9c77fc1e8d4dfc1e7dc631d193162c75feef2e Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 9 Mar 2015 11:13:04 +0100 Subject: mac80211: reject aggregation sessions with non-HT peers If a peer or some local agent (rate control, ...) decides to start an aggregation session but doesn't support HT (which also implies QoS), reject it. This is mostly a corner case as such peers normally won't try to use block-ack sessions and rate control wouldn't start them, but technically QoS stations could request it according to the spec. However, since drivers don't really support such non-HT sessions it's better to reject them. Also, while at it, move the tracing for TX sessions earlier so it captures the error cases as well. Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/agg-rx.c | 8 ++++++++ net/mac80211/agg-tx.c | 5 ++++- 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'net/mac80211') diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index a48bad468880..2c090c507391 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -234,6 +234,14 @@ void __ieee80211_start_rx_ba_session(struct sta_info *sta, int i, ret = -EOPNOTSUPP; u16 status = WLAN_STATUS_REQUEST_DECLINED; + if (!sta->sta.ht_cap.ht_supported) { + ht_dbg(sta->sdata, + "STA %pM erroneously requests BA session on tid %d w/o QoS\n", + sta->sta.addr, tid); + /* send a response anyway, it's an error case if we get here */ + goto end_no_lock; + } + if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) { ht_dbg(sta->sdata, "Suspend in progress - Denying ADDBA request (%pM tid %d)\n", diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 668524aeb0b2..20522492d8cc 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -509,11 +509,14 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, struct tid_ampdu_tx *tid_tx; int ret = 0; + trace_api_start_tx_ba_session(pubsta, tid); + if (WARN(sta->reserved_tid == tid, "Requested to start BA session on reserved tid=%d", tid)) return -EINVAL; - trace_api_start_tx_ba_session(pubsta, tid); + if (!pubsta->ht_cap.ht_supported) + return -EINVAL; if (WARN_ON_ONCE(!local->ops->ampdu_action)) return -EINVAL; -- cgit v1.2.3 From 6a8b4adb47aea322e376cd1be62781c50dc1a9b5 Mon Sep 17 00:00:00 2001 From: Michael Braun Date: Wed, 18 Mar 2015 07:17:37 +0100 Subject: mac80211: fix typo in debug output Signed-off-by: Michael Braun Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/mac80211') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 0aaf8b04a738..e01cea3b9043 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -2300,7 +2300,7 @@ int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata, } ht_dbg(sdata, - "SMSP %d requested in AP mode, sending Action frame to %d stations\n", + "SMPS %d requested in AP mode, sending Action frame to %d stations\n", smps_mode, atomic_read(&sdata->u.ap.num_mcast_sta)); mutex_lock(&sdata->local->sta_mtx); -- cgit v1.2.3 From 6bab2e19c5ffd1f21b28c2cabb3801a37b77ae69 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Wed, 18 Mar 2015 11:13:39 +0100 Subject: cfg80211: pass name_assign_type to rdev_add_virtual_intf() This will expose in /sys whether the ifname of a device is set by userspace or generated by the kernel. The latter kind (wlanX, etc) is not deterministic, so userspace needs to rename these devices to names that are guaranteed to stay the same between reboots. The former, however should never be renamed, so userspace needs to be able to reliably tell the difference. Similar functionality was introduced for the rtnetlink core in commit 5517750f058e ("net: rtnetlink - make create_link take name_assign_type") Signed-off-by: Tom Gundersen Cc: Kalle Valo Cc: Brett Rudley Cc: Arend van Spriel Cc: Franky (Zhenhui) Lin Cc: Hante Meuleman Cc: Johannes Berg [reformat changelog to fit 72 cols] Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 3 ++- net/mac80211/ieee80211_i.h | 1 + net/mac80211/iface.c | 3 ++- net/mac80211/main.c | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index e01cea3b9043..49401238ac39 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -24,6 +24,7 @@ static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy, const char *name, + unsigned char name_assign_type, enum nl80211_iftype type, u32 *flags, struct vif_params *params) @@ -33,7 +34,7 @@ static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy, struct ieee80211_sub_if_data *sdata; int err; - err = ieee80211_if_add(local, name, &wdev, type, params); + err = ieee80211_if_add(local, name, name_assign_type, &wdev, type, params); if (err) return ERR_PTR(err); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index ebc8135e0aaa..3e3cfe8da4ef 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1578,6 +1578,7 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, int ieee80211_iface_init(void); void ieee80211_iface_exit(void); int ieee80211_if_add(struct ieee80211_local *local, const char *name, + unsigned char name_assign_type, struct wireless_dev **new_wdev, enum nl80211_iftype type, struct vif_params *params); int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 9689d3b1133b..a0cd97fd0c49 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1648,6 +1648,7 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local, } int ieee80211_if_add(struct ieee80211_local *local, const char *name, + unsigned char name_assign_type, struct wireless_dev **new_wdev, enum nl80211_iftype type, struct vif_params *params) { @@ -1676,7 +1677,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, txqs = IEEE80211_NUM_ACS; ndev = alloc_netdev_mqs(sizeof(*sdata) + local->hw.vif_data_size, - name, NET_NAME_UNKNOWN, + name, name_assign_type, ieee80211_if_setup, txqs, 1); if (!ndev) return -ENOMEM; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 2f51e6d1f2b3..4977967c8b00 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1057,7 +1057,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) /* add one default STA interface if supported */ if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION) && !(hw->flags & IEEE80211_HW_NO_AUTO_VIF)) { - result = ieee80211_if_add(local, "wlan%d", NULL, + result = ieee80211_if_add(local, "wlan%d", NET_NAME_ENUM, NULL, NL80211_IFTYPE_STATION, NULL); if (result) wiphy_warn(local->hw.wiphy, -- cgit v1.2.3 From 82ca6ef686f0fcefe2c1ad4ab74d5caf41be03a9 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 23 Mar 2015 15:41:15 +0200 Subject: mac80211: Fix misplaced return in AES-GMAC key setup Commit 8ade538bf39b ("mac80111: Add BIP-GMAC-128 and BIP-GMAC-256 ciphers") had the success return in incorrect place before the crypto_aead_setauthsize() call which practically ended up skipping that call unconditionally. The missing call did not actually change any functionality since GMAC_MIC_LEN (16) is identical to the maxauthsize in gcm(aes) and as such, the default value used for the authsize parameter. Reported-by: Dan Carpenter Signed-off-by: Jouni Malinen Signed-off-by: Johannes Berg --- net/mac80211/aes_gmac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/aes_gmac.c b/net/mac80211/aes_gmac.c index 1c72edcb0083..f1321b7d6506 100644 --- a/net/mac80211/aes_gmac.c +++ b/net/mac80211/aes_gmac.c @@ -69,10 +69,10 @@ struct crypto_aead *ieee80211_aes_gmac_key_setup(const u8 key[], return tfm; err = crypto_aead_setkey(tfm, key, key_len); - if (!err) - return tfm; if (!err) err = crypto_aead_setauthsize(tfm, GMAC_MIC_LEN); + if (!err) + return tfm; crypto_free_aead(tfm); return ERR_PTR(err); -- cgit v1.2.3 From 45fd63293a214c48d6d3856bb39cf207a805dfda Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 23 Mar 2015 17:08:14 +0300 Subject: mac80111: aes_ccm: cleanup ieee80211_aes_key_setup_encrypt() This code is written using an anti-pattern called "success handling" which makes it hard to read, especially if you are used to normal kernel style. It should instead be written as a list of directives in a row with branches for error handling. Signed-off-by: Dan Carpenter Acked-by: Ard Biesheuvel Signed-off-by: Johannes Berg --- net/mac80211/aes_ccm.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/aes_ccm.c b/net/mac80211/aes_ccm.c index 7869bb40acaa..208df7c0b6ea 100644 --- a/net/mac80211/aes_ccm.c +++ b/net/mac80211/aes_ccm.c @@ -85,11 +85,15 @@ struct crypto_aead *ieee80211_aes_key_setup_encrypt(const u8 key[], return tfm; err = crypto_aead_setkey(tfm, key, key_len); - if (!err) - err = crypto_aead_setauthsize(tfm, mic_len); - if (!err) - return tfm; + if (err) + goto free_aead; + err = crypto_aead_setauthsize(tfm, mic_len); + if (err) + goto free_aead; + + return tfm; +free_aead: crypto_free_aead(tfm); return ERR_PTR(err); } -- cgit v1.2.3 From 07862e13e60305e3026aaff88c764b4bafa02a64 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 23 Mar 2015 17:08:14 +0300 Subject: mac80111: aes_gcm: clean up ieee80211_aes_gcm_key_setup_encrypt() This code is written using an anti-pattern called "success handling" which makes it hard to read, especially if you are used to normal kernel style. It should instead be written as a list of directives in a row with branches for error handling. (Basically copied from Dan's previous patch for CCM) Signed-off-by: Johannes Berg --- net/mac80211/aes_gcm.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/aes_gcm.c b/net/mac80211/aes_gcm.c index c2bf6698d738..fd278bbe1b0d 100644 --- a/net/mac80211/aes_gcm.c +++ b/net/mac80211/aes_gcm.c @@ -80,11 +80,15 @@ struct crypto_aead *ieee80211_aes_gcm_key_setup_encrypt(const u8 key[], return tfm; err = crypto_aead_setkey(tfm, key, key_len); - if (!err) - err = crypto_aead_setauthsize(tfm, IEEE80211_GCMP_MIC_LEN); - if (!err) - return tfm; + if (err) + goto free_aead; + err = crypto_aead_setauthsize(tfm, IEEE80211_GCMP_MIC_LEN); + if (err) + goto free_aead; + + return tfm; +free_aead: crypto_free_aead(tfm); return ERR_PTR(err); } -- cgit v1.2.3 From a73f8e21f3f93159bc19e154e8f50891c22c11db Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 21 Mar 2015 07:41:04 +0100 Subject: mac80211: send AP probe as unicast again Louis reported that a static checker was complaining that the 'dst' variable was set (multiple times) but not used. This is due to a previous commit having removed the usage (apparently erroneously), so add it back. Fixes: a344d6778a98 ("mac80211: allow drivers to support NL80211_SCAN_FLAG_RANDOM_ADDR") Reported-by: Louis Langholtz Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/mac80211') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 4a01a836f870..8a06b1abc47a 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2233,7 +2233,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata) else ssid_len = ssid[1]; - ieee80211_send_probe_req(sdata, sdata->vif.addr, NULL, + ieee80211_send_probe_req(sdata, sdata->vif.addr, dst, ssid + 2, ssid_len, NULL, 0, (u32) -1, true, 0, ifmgd->associated->channel, false); -- cgit v1.2.3 From 527871d7206dac2733d0bae52f5a328811d299ee Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 21 Mar 2015 08:09:55 +0100 Subject: mac80211: make sta.wme indicate whether QoS is used Indicating just the peer's capability is fairly pointless if the local device doesn't support it. Make the variable track both combined, and remove the 'local support' check in the TX path. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 3 ++- net/mac80211/ibss.c | 2 +- net/mac80211/mlme.c | 2 +- net/mac80211/tx.c | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 49401238ac39..301215be59ef 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1073,7 +1073,8 @@ static int sta_apply_parameters(struct ieee80211_local *local, clear_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE); } - if (mask & BIT(NL80211_STA_FLAG_WME)) + if (mask & BIT(NL80211_STA_FLAG_WME) && + local->hw.queues >= IEEE80211_NUM_ACS) sta->sta.wme = set & BIT(NL80211_STA_FLAG_WME); if (mask & BIT(NL80211_STA_FLAG_MFP)) { diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 17eda3575d7d..6da4e72f8178 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -1016,7 +1016,7 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata, } } - if (sta && elems->wmm_info) + if (sta && elems->wmm_info && local->hw.queues >= IEEE80211_NUM_ACS) sta->sta.wme = true; if (sta && elems->ht_operation && elems->ht_cap_elem && diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 8a06b1abc47a..0cbcde11fae3 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2990,7 +2990,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, sta->sta.mfp = false; } - sta->sta.wme = elems.wmm_param; + sta->sta.wme = elems.wmm_param && local->hw.queues >= IEEE80211_NUM_ACS; err = sta_info_move_state(sta, IEEE80211_STA_ASSOC); if (!err && !(ifmgd->flags & IEEE80211_STA_CONTROL_PORT)) diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 34b0e7545cc5..018f029d0c95 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2088,8 +2088,8 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, if (ieee80211_vif_is_mesh(&sdata->vif)) wme_sta = true; - /* receiver and we are QoS enabled, use a QoS type frame */ - if (wme_sta && local->hw.queues >= IEEE80211_NUM_ACS) { + /* receiver does QoS (which also means we do) use it */ + if (wme_sta) { fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA); hdrlen += 2; } -- cgit v1.2.3 From 97ffe75791b3e9c0797d5891070e41321c3beccb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Sat, 21 Mar 2015 09:13:45 +0100 Subject: mac80211: factor out station lookup from ieee80211_build_hdr() In order to look up the RA station earlier to implement a TX fastpath, factor out the lookup from ieee80211_build_hdr(). To always have a valid station pointer, also move some of the checks into the new function. Signed-off-by: Johannes Berg --- net/mac80211/tx.c | 188 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 118 insertions(+), 70 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 018f029d0c95..e5d679f38cc1 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1789,6 +1789,91 @@ fail: return NETDEV_TX_OK; /* meaning, we dealt with the skb */ } +static inline bool ieee80211_is_tdls_setup(struct sk_buff *skb) +{ + u16 ethertype = (skb->data[12] << 8) | skb->data[13]; + + return ethertype == ETH_P_TDLS && + skb->len > 14 && + skb->data[14] == WLAN_TDLS_SNAP_RFTYPE; +} + +static int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, + struct sta_info **sta_out) +{ + struct sta_info *sta; + + switch (sdata->vif.type) { + case NL80211_IFTYPE_AP_VLAN: + sta = rcu_dereference(sdata->u.vlan.sta); + if (sta) { + *sta_out = sta; + return 0; + } else if (sdata->wdev.use_4addr) { + return -ENOLINK; + } + /* fall through */ + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_OCB: + case NL80211_IFTYPE_ADHOC: + if (is_multicast_ether_addr(skb->data)) { + *sta_out = ERR_PTR(-ENOENT); + return 0; + } + sta = sta_info_get_bss(sdata, skb->data); + break; + case NL80211_IFTYPE_WDS: + sta = sta_info_get(sdata, sdata->u.wds.remote_addr); + break; +#ifdef CONFIG_MAC80211_MESH + case NL80211_IFTYPE_MESH_POINT: + /* determined much later */ + *sta_out = NULL; + return 0; +#endif + case NL80211_IFTYPE_STATION: + if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) { + sta = sta_info_get(sdata, skb->data); + if (sta) { + bool tdls_peer, tdls_auth; + + tdls_peer = test_sta_flag(sta, + WLAN_STA_TDLS_PEER); + tdls_auth = test_sta_flag(sta, + WLAN_STA_TDLS_PEER_AUTH); + + if (tdls_peer && tdls_auth) { + *sta_out = sta; + return 0; + } + + /* + * TDLS link during setup - throw out frames to + * peer. Allow TDLS-setup frames to unauthorized + * peers for the special case of a link teardown + * after a TDLS sta is removed due to being + * unreachable. + */ + if (tdls_peer && !tdls_auth && + !ieee80211_is_tdls_setup(skb)) + return -EINVAL; + } + + } + + sta = sta_info_get(sdata, sdata->u.mgd.bssid); + if (!sta) + return -ENOLINK; + break; + default: + return -EINVAL; + } + + *sta_out = sta ?: ERR_PTR(-ENOENT); + return 0; +} + /** * ieee80211_build_hdr - build 802.11 header in the given frame * @sdata: virtual interface to build the header for @@ -1809,7 +1894,7 @@ fail: */ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, u32 info_flags, - struct sta_info **sta_out) + struct sta_info *sta) { struct ieee80211_local *local = sdata->local; struct ieee80211_tx_info *info; @@ -1822,17 +1907,18 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, const u8 *encaps_data; int encaps_len, skip_header_bytes; int nh_pos, h_pos; - struct sta_info *sta = NULL; - bool wme_sta = false, authorized = false, tdls_auth = false; - bool tdls_peer = false, tdls_setup_frame = false; + bool wme_sta = false, authorized = false; + bool tdls_peer; bool multicast; - bool have_station = false; u16 info_id = 0; struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_sub_if_data *ap_sdata; enum ieee80211_band band; int ret; + if (IS_ERR(sta)) + sta = NULL; + /* convert Ethernet header to proper 802.11 header (based on * operation mode) */ ethertype = (skb->data[12] << 8) | skb->data[13]; @@ -1840,8 +1926,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, switch (sdata->vif.type) { case NL80211_IFTYPE_AP_VLAN: - sta = rcu_dereference(sdata->u.vlan.sta); - if (sta) { + if (sdata->wdev.use_4addr) { fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); /* RA TA DA SA */ memcpy(hdr.addr1, sta->sta.addr, ETH_ALEN); @@ -1851,11 +1936,6 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, hdrlen = 30; authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); wme_sta = sta->sta.wme; - have_station = true; - *sta_out = sta; - } else if (sdata->wdev.use_4addr) { - ret = -ENOLINK; - goto free; } ap_sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap); @@ -1865,7 +1945,7 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, goto free; } band = chanctx_conf->def.chan->band; - if (sta) + if (sdata->wdev.use_4addr) break; /* fall through */ case NL80211_IFTYPE_AP: @@ -1969,44 +2049,15 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, break; #endif case NL80211_IFTYPE_STATION: - if (sdata->wdev.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) { - sta = sta_info_get(sdata, skb->data); - if (sta) { - tdls_peer = test_sta_flag(sta, - WLAN_STA_TDLS_PEER); - tdls_auth = test_sta_flag(sta, - WLAN_STA_TDLS_PEER_AUTH); - } - - if (tdls_peer) - tdls_setup_frame = - ethertype == ETH_P_TDLS && - skb->len > 14 && - skb->data[14] == WLAN_TDLS_SNAP_RFTYPE; - } - - /* - * TDLS link during setup - throw out frames to peer. We allow - * TDLS-setup frames to unauthorized peers for the special case - * of a link teardown after a TDLS sta is removed due to being - * unreachable. - */ - if (tdls_peer && !tdls_auth && !tdls_setup_frame) { - ret = -EINVAL; - goto free; - } + /* we already did checks when looking up the RA STA */ + tdls_peer = test_sta_flag(sta, WLAN_STA_TDLS_PEER); - /* send direct packets to authorized TDLS peers */ - if (tdls_peer && tdls_auth) { + if (tdls_peer) { /* DA SA BSSID */ memcpy(hdr.addr1, skb->data, ETH_ALEN); memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); memcpy(hdr.addr3, sdata->u.mgd.bssid, ETH_ALEN); hdrlen = 24; - have_station = true; - authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); - wme_sta = sta->sta.wme; - *sta_out = sta; } else if (sdata->u.mgd.use_4addr && cpu_to_be16(ethertype) != sdata->control_port_protocol) { fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | @@ -2063,30 +2114,16 @@ static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata, goto free; } - /* - * There's no need to try to look up the destination station - * if it is a multicast address. In mesh, there's no need to - * look up the station at all as it always must be QoS capable - * and mesh mode checks authorization later. - */ multicast = is_multicast_ether_addr(hdr.addr1); - if (multicast) { - *sta_out = ERR_PTR(-ENOENT); - } else if (!have_station && !ieee80211_vif_is_mesh(&sdata->vif)) { - if (sdata->control_port_protocol == skb->protocol) - sta = sta_info_get_bss(sdata, hdr.addr1); - else - sta = sta_info_get(sdata, hdr.addr1); - if (sta) { - authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); - wme_sta = sta->sta.wme; - } - *sta_out = sta ?: ERR_PTR(-ENOENT); - } - /* For mesh, the use of the QoS header is mandatory */ - if (ieee80211_vif_is_mesh(&sdata->vif)) + /* sta is always NULL for mesh */ + if (sta) { + authorized = test_sta_flag(sta, WLAN_STA_AUTHORIZED); + wme_sta = sta->sta.wme; + } else if (ieee80211_vif_is_mesh(&sdata->vif)) { + /* For mesh, the use of the QoS header is mandatory */ wme_sta = true; + } /* receiver does QoS (which also means we do) use it */ if (wme_sta) { @@ -2259,7 +2296,7 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb, u32 info_flags) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - struct sta_info *sta = NULL; + struct sta_info *sta; if (unlikely(skb->len < ETH_HLEN)) { kfree_skb(skb); @@ -2268,7 +2305,12 @@ void __ieee80211_subif_start_xmit(struct sk_buff *skb, rcu_read_lock(); - skb = ieee80211_build_hdr(sdata, skb, info_flags, &sta); + if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) { + kfree_skb(skb); + goto out; + } + + skb = ieee80211_build_hdr(sdata, skb, info_flags, sta); if (IS_ERR(skb)) goto out; @@ -2304,11 +2346,17 @@ ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata, .local = sdata->local, .sdata = sdata, }; - struct sta_info *sta_ignore; + struct sta_info *sta; rcu_read_lock(); - skb = ieee80211_build_hdr(sdata, skb, info_flags, &sta_ignore); + if (ieee80211_lookup_ra_sta(sdata, skb, &sta)) { + kfree_skb(skb); + skb = ERR_PTR(-EINVAL); + goto out; + } + + skb = ieee80211_build_hdr(sdata, skb, info_flags, sta); if (IS_ERR(skb)) goto out; -- cgit v1.2.3 From 76bed0f43b27d37cbe5fe9f3c27362db59451dea Mon Sep 17 00:00:00 2001 From: "Janusz.Dziedzic@tieto.com" Date: Fri, 20 Mar 2015 06:37:00 +0100 Subject: mac80211: IBSS fix scan request In case of wide bandwidth (wider than 20MHz) used by IBSS, scan all channels in chandef to be able to find neighboring IBSS netwqworks that use the same overall channels but a different control channel. Signed-off-by: Janusz Dziedzic Signed-off-by: Johannes Berg --- net/mac80211/ibss.c | 81 ++++++++++++++++++++++++++++++++++++++++++++-- net/mac80211/ieee80211_i.h | 3 +- net/mac80211/scan.c | 25 ++++++++------ 3 files changed, 97 insertions(+), 12 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 6da4e72f8178..8f8391e008ed 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -1270,7 +1270,7 @@ static void ieee80211_sta_merge_ibss(struct ieee80211_sub_if_data *sdata) scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef); ieee80211_request_ibss_scan(sdata, ifibss->ssid, ifibss->ssid_len, - NULL, scan_width); + NULL, 0, scan_width); } static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) @@ -1307,6 +1307,76 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata) capability, 0, true); } +static unsigned ibss_setup_channels(struct wiphy *wiphy, + struct ieee80211_channel **channels, + unsigned int channels_max, + u32 center_freq, u32 width) +{ + struct ieee80211_channel *chan = NULL; + unsigned int n_chan = 0; + u32 start_freq, end_freq, freq; + + if (width <= 20) { + start_freq = center_freq; + end_freq = center_freq; + } else { + start_freq = center_freq - width / 2 + 10; + end_freq = center_freq + width / 2 - 10; + } + + for (freq = start_freq; freq <= end_freq; freq += 20) { + chan = ieee80211_get_channel(wiphy, freq); + if (!chan) + continue; + if (n_chan >= channels_max) + return n_chan; + + channels[n_chan] = chan; + n_chan++; + } + + return n_chan; +} + +static unsigned int +ieee80211_ibss_setup_scan_channels(struct wiphy *wiphy, + const struct cfg80211_chan_def *chandef, + struct ieee80211_channel **channels, + unsigned int channels_max) +{ + unsigned int n_chan = 0; + u32 width, cf1, cf2 = 0; + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_40: + width = 40; + break; + case NL80211_CHAN_WIDTH_80P80: + cf2 = chandef->center_freq2; + /* fall through */ + case NL80211_CHAN_WIDTH_80: + width = 80; + break; + case NL80211_CHAN_WIDTH_160: + width = 160; + break; + default: + width = 20; + break; + } + + cf1 = chandef->center_freq1; + + n_chan = ibss_setup_channels(wiphy, channels, channels_max, cf1, width); + + if (cf2) + n_chan += ibss_setup_channels(wiphy, &channels[n_chan], + channels_max - n_chan, cf2, + width); + + return n_chan; +} + /* * This function is called with state == IEEE80211_IBSS_MLME_SEARCH */ @@ -1372,11 +1442,18 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata) /* Selected IBSS not found in current scan results - try to scan */ if (time_after(jiffies, ifibss->last_scan_completed + IEEE80211_SCAN_INTERVAL)) { + struct ieee80211_channel *channels[8]; + unsigned int num; + sdata_info(sdata, "Trigger new scan to find an IBSS to join\n"); + num = ieee80211_ibss_setup_scan_channels(local->hw.wiphy, + &ifibss->chandef, + channels, + ARRAY_SIZE(channels)); scan_width = cfg80211_chandef_to_scan_width(&ifibss->chandef); ieee80211_request_ibss_scan(sdata, ifibss->ssid, - ifibss->ssid_len, chan, + ifibss->ssid_len, channels, num, scan_width); } else { int interval = IEEE80211_SCAN_INTERVAL; diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 3e3cfe8da4ef..3331c62b4433 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1529,7 +1529,8 @@ int ieee80211_mesh_finish_csa(struct ieee80211_sub_if_data *sdata); void ieee80211_scan_work(struct work_struct *work); int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata, const u8 *ssid, u8 ssid_len, - struct ieee80211_channel *chan, + struct ieee80211_channel **channels, + unsigned int n_channels, enum nl80211_bss_scan_width scan_width); int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, struct cfg80211_scan_request *req); diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 05f0d711b6d8..7bb6a9383f58 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -928,11 +928,12 @@ int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata, int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata, const u8 *ssid, u8 ssid_len, - struct ieee80211_channel *chan, + struct ieee80211_channel **channels, + unsigned int n_channels, enum nl80211_bss_scan_width scan_width) { struct ieee80211_local *local = sdata->local; - int ret = -EBUSY; + int ret = -EBUSY, i, n_ch = 0; enum ieee80211_band band; mutex_lock(&local->mtx); @@ -942,9 +943,8 @@ int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata, goto unlock; /* fill internal scan request */ - if (!chan) { - int i, max_n; - int n_ch = 0; + if (!channels) { + int max_n; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (!local->hw.wiphy->bands[band]) @@ -969,12 +969,19 @@ int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata, local->int_scan_req->n_channels = n_ch; } else { - if (WARN_ON_ONCE(chan->flags & (IEEE80211_CHAN_NO_IR | - IEEE80211_CHAN_DISABLED))) + for (i = 0; i < n_channels; i++) { + if (channels[i]->flags & (IEEE80211_CHAN_NO_IR | + IEEE80211_CHAN_DISABLED)) + continue; + + local->int_scan_req->channels[n_ch] = channels[i]; + n_ch++; + } + + if (WARN_ON_ONCE(n_ch == 0)) goto unlock; - local->int_scan_req->channels[0] = chan; - local->int_scan_req->n_channels = 1; + local->int_scan_req->n_channels = n_ch; } local->int_scan_req->ssids = &local->scan_ssid; -- cgit v1.2.3 From abcff6ef01f9ffa6958e50457574132f8f786308 Mon Sep 17 00:00:00 2001 From: "Janusz.Dziedzic@tieto.com" Date: Fri, 20 Mar 2015 06:37:01 +0100 Subject: mac80211: add VHT support for IBSS Add VHT support for IBSS. Drivers could activate this feature by setting NL80211_EXT_FEATURE_VHT_IBSS flag. Signed-off-by: Janusz Dziedzic Signed-off-by: Johannes Berg --- net/mac80211/ht.c | 2 -- net/mac80211/ibss.c | 49 +++++++++++++++++++++++++++++++++++++--------- net/mac80211/ieee80211_i.h | 3 +++ net/mac80211/mesh_plink.c | 4 ++++ net/mac80211/util.c | 33 +++++++++++++++++++++++++++++++ 5 files changed, 80 insertions(+), 11 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index ff630be2ca75..7a76ce639d58 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -252,8 +252,6 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, break; } - if (bw != sta->sta.bandwidth) - changed = true; sta->sta.bandwidth = bw; sta->cur_max_bandwidth = diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 8f8391e008ed..52d629d5e797 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -188,6 +188,16 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata, */ pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap, chandef, 0); + + /* add VHT capability and information IEs */ + if (chandef->width != NL80211_CHAN_WIDTH_20 && + chandef->width != NL80211_CHAN_WIDTH_40 && + sband->vht_cap.vht_supported) { + pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap, + sband->vht_cap.cap); + pos = ieee80211_ie_build_vht_oper(pos, &sband->vht_cap, + chandef); + } } if (local->hw.queues >= IEEE80211_NUM_ACS) @@ -415,6 +425,11 @@ static void ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, NL80211_CHAN_WIDTH_20_NOHT); chandef.width = sdata->u.ibss.chandef.width; break; + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_160: + chandef = sdata->u.ibss.chandef; + chandef.chan = cbss->channel; + break; default: /* fall back to 20 MHz for unsupported modes */ cfg80211_chandef_create(&chandef, cbss->channel, @@ -1026,24 +1041,40 @@ static void ieee80211_update_sta_info(struct ieee80211_sub_if_data *sdata, /* we both use HT */ struct ieee80211_ht_cap htcap_ie; struct cfg80211_chan_def chandef; + enum ieee80211_sta_rx_bandwidth bw = sta->sta.bandwidth; ieee80211_ht_oper_to_chandef(channel, elems->ht_operation, &chandef); memcpy(&htcap_ie, elems->ht_cap_elem, sizeof(htcap_ie)); - - /* - * fall back to HT20 if we don't use or use - * the other extension channel - */ - if (chandef.center_freq1 != sdata->u.ibss.chandef.center_freq1) - htcap_ie.cap_info &= - cpu_to_le16(~IEEE80211_HT_CAP_SUP_WIDTH_20_40); - rates_updated |= ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, &htcap_ie, sta); + + if (elems->vht_operation && elems->vht_cap_elem && + sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_20 && + sdata->u.ibss.chandef.width != NL80211_CHAN_WIDTH_40) { + /* we both use VHT */ + struct ieee80211_vht_cap cap_ie; + struct ieee80211_sta_vht_cap cap = sta->sta.vht_cap; + + ieee80211_vht_oper_to_chandef(channel, + elems->vht_operation, + &chandef); + memcpy(&cap_ie, elems->vht_cap_elem, sizeof(cap_ie)); + ieee80211_vht_cap_ie_to_sta_vht_cap(sdata, sband, + &cap_ie, sta); + if (memcmp(&cap, &sta->sta.vht_cap, sizeof(cap))) + rates_updated |= true; + } + + if (bw != sta->sta.bandwidth) + rates_updated |= true; + + if (!cfg80211_chandef_compatible(&sdata->u.ibss.chandef, + &chandef)) + WARN_ON_ONCE(1); } if (sta && rates_updated) { diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 3331c62b4433..81340abb3876 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1960,6 +1960,9 @@ u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo); void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan, const struct ieee80211_ht_operation *ht_oper, struct cfg80211_chan_def *chandef); +void ieee80211_vht_oper_to_chandef(struct ieee80211_channel *control_chan, + const struct ieee80211_vht_operation *oper, + struct cfg80211_chan_def *chandef); u32 ieee80211_chandef_downgrade(struct cfg80211_chan_def *c); int __must_check diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 8465c055a371..60d737f144e3 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -382,6 +382,7 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, enum ieee80211_band band = ieee80211_get_sdata_band(sdata); struct ieee80211_supported_band *sband; u32 rates, basic_rates = 0, changed = 0; + enum ieee80211_sta_rx_bandwidth bw = sta->sta.bandwidth; sband = local->hw.wiphy->bands[band]; rates = ieee80211_sta_get_rates(sdata, elems, band, &basic_rates); @@ -401,6 +402,9 @@ static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, elems->ht_cap_elem, sta)) changed |= IEEE80211_RC_BW_CHANGED; + if (bw != sta->sta.bandwidth) + changed |= IEEE80211_RC_BW_CHANGED; + /* HT peer is operating 20MHz-only */ if (elems->ht_operation && !(elems->ht_operation->ht_param & diff --git a/net/mac80211/util.c b/net/mac80211/util.c index e664b28821a2..256647cb1d24 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2440,6 +2440,39 @@ void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan, cfg80211_chandef_create(chandef, control_chan, channel_type); } +void ieee80211_vht_oper_to_chandef(struct ieee80211_channel *control_chan, + const struct ieee80211_vht_operation *oper, + struct cfg80211_chan_def *chandef) +{ + if (!oper) + return; + + chandef->chan = control_chan; + + switch (oper->chan_width) { + case IEEE80211_VHT_CHANWIDTH_USE_HT: + break; + case IEEE80211_VHT_CHANWIDTH_80MHZ: + chandef->width = NL80211_CHAN_WIDTH_80; + break; + case IEEE80211_VHT_CHANWIDTH_160MHZ: + chandef->width = NL80211_CHAN_WIDTH_160; + break; + case IEEE80211_VHT_CHANWIDTH_80P80MHZ: + chandef->width = NL80211_CHAN_WIDTH_80P80; + break; + default: + break; + } + + chandef->center_freq1 = + ieee80211_channel_to_frequency(oper->center_freq_seg1_idx, + control_chan->band); + chandef->center_freq2 = + ieee80211_channel_to_frequency(oper->center_freq_seg2_idx, + control_chan->band); +} + int ieee80211_parse_bitrates(struct cfg80211_chan_def *chandef, const struct ieee80211_supported_band *sband, const u8 *srates, int srates_len, u32 *rates) -- cgit v1.2.3 From 070e176a758e5c96f112d6fc6c348fe4daef99a5 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Mon, 30 Mar 2015 11:16:23 +0300 Subject: mac80211: send HT/VHT IEs in TDLS discovery response These are mandated by IEEE802.11-2012 section 8.5.8.6 and IEEE802.11ac-2013 section 8.5.8.16. Signed-off-by: Arik Nemtsov Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/tdls.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 79ed59acf0d4..fff0d864adfa 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -378,7 +378,9 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, sband = local->hw.wiphy->bands[band]; memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap)); - if (action_code == WLAN_TDLS_SETUP_REQUEST && ht_cap.ht_supported) { + if ((action_code == WLAN_TDLS_SETUP_REQUEST || + action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) && + ht_cap.ht_supported) { ieee80211_apply_htcap_overrides(sdata, &ht_cap); /* disable SMPS in TDLS initiator */ @@ -432,11 +434,14 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, /* build the VHT-cap similarly to the HT-cap */ memcpy(&vht_cap, &sband->vht_cap, sizeof(vht_cap)); - if (action_code == WLAN_TDLS_SETUP_REQUEST && vht_cap.vht_supported) { + if ((action_code == WLAN_TDLS_SETUP_REQUEST || + action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) && + vht_cap.vht_supported) { ieee80211_apply_vhtcap_overrides(sdata, &vht_cap); /* the AID is present only when VHT is implemented */ - ieee80211_tdls_add_aid(sdata, skb); + if (action_code == WLAN_TDLS_SETUP_REQUEST) + ieee80211_tdls_add_aid(sdata, skb); pos = skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); ieee80211_ie_build_vht_cap(pos, &vht_cap, vht_cap.cap); -- cgit v1.2.3 From 2c44be81f0fc147eed9dc63e2601318b2c007aeb Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 30 Mar 2015 15:09:20 +0200 Subject: mac80211: set QoS capability before changing station state In the upcoming fast-xmit patch, changing station state will build a header cache based on the station's capabilities, and as the QoS capability (sta.wme) impacts the header, it needs to be set before. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 301215be59ef..e4dd2fc34de3 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1059,6 +1059,10 @@ static int sta_apply_parameters(struct ieee80211_local *local, } } + if (mask & BIT(NL80211_STA_FLAG_WME) && + local->hw.queues >= IEEE80211_NUM_ACS) + sta->sta.wme = set & BIT(NL80211_STA_FLAG_WME); + /* auth flags will be set later for TDLS stations */ if (!test_sta_flag(sta, WLAN_STA_TDLS_PEER)) { ret = sta_apply_auth_flags(local, sta, mask, set); @@ -1073,10 +1077,6 @@ static int sta_apply_parameters(struct ieee80211_local *local, clear_sta_flag(sta, WLAN_STA_SHORT_PREAMBLE); } - if (mask & BIT(NL80211_STA_FLAG_WME) && - local->hw.queues >= IEEE80211_NUM_ACS) - sta->sta.wme = set & BIT(NL80211_STA_FLAG_WME); - if (mask & BIT(NL80211_STA_FLAG_MFP)) { sta->sta.mfp = !!(set & BIT(NL80211_STA_FLAG_MFP)); if (set & BIT(NL80211_STA_FLAG_MFP)) -- cgit v1.2.3 From 7bedd0cfad4e122bc0ddaf3fc955a38c88c95d35 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 13 Feb 2015 21:55:15 +0100 Subject: mac80211: use rhashtable for station table We currently have a hand-rolled table with 256 entries and are using the last byte of the MAC address as the hash. This hash is obviously very fast, but collisions are easily created and we waste a lot of space in the common case of just connecting as a client to an AP where we just have a single station. The other common case of an AP is also suboptimal due to the size of the hash table and the ease of causing collisions. Convert all of this to use rhashtable with jhash, which gives us the advantage of a far better hash function (with random perturbation to avoid hash collision attacks) and of course that the hash table grows and shrinks dynamically with chain length, improving both cases above. Use a specialised hash function (using jhash, but with fixed length) to achieve better compiler optimisation as suggested by Sergey Ryazanov. Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 3 +- net/mac80211/main.c | 9 ++-- net/mac80211/rx.c | 9 +++- net/mac80211/sta_info.c | 103 +++++++++++++++++++++++---------------------- net/mac80211/sta_info.h | 38 ++++++----------- net/mac80211/status.c | 8 +++- 6 files changed, 85 insertions(+), 85 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 487f5e2a9283..3c1512b0442c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -1187,7 +1188,7 @@ struct ieee80211_local { spinlock_t tim_lock; unsigned long num_sta; struct list_head sta_list; - struct sta_info __rcu *sta_hash[STA_HASH_SIZE]; + struct rhashtable sta_hash; struct timer_list sta_cleanup; int sta_generation; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 4977967c8b00..51e0332a4589 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -557,6 +557,9 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, local = wiphy_priv(wiphy); + if (sta_info_init(local)) + goto err_free; + local->hw.wiphy = wiphy; local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN); @@ -629,8 +632,6 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, spin_lock_init(&local->ack_status_lock); idr_init(&local->ack_status_frames); - sta_info_init(local); - for (i = 0; i < IEEE80211_MAX_QUEUES; i++) { skb_queue_head_init(&local->pending[i]); atomic_set(&local->agg_queue_stop[i], 0); @@ -650,6 +651,9 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len, ieee80211_roc_setup(local); return &local->hw; + err_free: + wiphy_free(wiphy); + return NULL; } EXPORT_SYMBOL(ieee80211_alloc_hw_nm); @@ -1173,7 +1177,6 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw) destroy_workqueue(local->workqueue); wiphy_unregister(local->hw.wiphy); - sta_info_stop(local); ieee80211_wep_free(local); ieee80211_led_exit(local); kfree(local->int_scan_req); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 4f7b922cfda4..5b60bcf00ec3 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -3423,7 +3423,8 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, __le16 fc; struct ieee80211_rx_data rx; struct ieee80211_sub_if_data *prev; - struct sta_info *sta, *tmp, *prev_sta; + struct sta_info *sta, *prev_sta; + struct rhash_head *tmp; int err = 0; fc = ((struct ieee80211_hdr *)skb->data)->frame_control; @@ -3458,9 +3459,13 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, ieee80211_scan_rx(local, skb); if (ieee80211_is_data(fc)) { + const struct bucket_table *tbl; + prev_sta = NULL; - for_each_sta_info(local, hdr->addr2, sta, tmp) { + tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash); + + for_each_sta_info(local, tbl, hdr->addr2, sta, tmp) { if (!prev_sta) { prev_sta = sta; continue; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index aacaa1a85e63..81cc499fa4a9 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -64,32 +64,20 @@ * freed before they are done using it. */ +static const struct rhashtable_params sta_rht_params = { + .nelem_hint = 3, /* start small */ + .head_offset = offsetof(struct sta_info, hash_node), + .key_offset = offsetof(struct sta_info, sta.addr), + .key_len = ETH_ALEN, + .hashfn = sta_addr_hash, +}; + /* 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_mtx)); - if (!s) - return -ENOENT; - if (s == sta) { - rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], - s->hnext); - return 0; - } - - while (rcu_access_pointer(s->hnext) && - rcu_access_pointer(s->hnext) != sta) - s = rcu_dereference_protected(s->hnext, - lockdep_is_held(&local->sta_mtx)); - if (rcu_access_pointer(s->hnext)) { - rcu_assign_pointer(s->hnext, sta->hnext); - return 0; - } - - return -ENOENT; + return rhashtable_remove_fast(&local->sta_hash, &sta->hash_node, + sta_rht_params); } static void __cleanup_single_sta(struct sta_info *sta) @@ -159,18 +147,8 @@ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, const u8 *addr) { struct ieee80211_local *local = sdata->local; - struct sta_info *sta; - sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)], - lockdep_is_held(&local->sta_mtx)); - while (sta) { - if (sta->sdata == sdata && - ether_addr_equal(sta->sta.addr, addr)) - break; - sta = rcu_dereference_check(sta->hnext, - lockdep_is_held(&local->sta_mtx)); - } - return sta; + return rhashtable_lookup_fast(&local->sta_hash, addr, sta_rht_params); } /* @@ -182,18 +160,24 @@ struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata, { struct ieee80211_local *local = sdata->local; struct sta_info *sta; + struct rhash_head *tmp; + const struct bucket_table *tbl; - sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)], - lockdep_is_held(&local->sta_mtx)); - while (sta) { - if ((sta->sdata == sdata || - (sta->sdata->bss && sta->sdata->bss == sdata->bss)) && - ether_addr_equal(sta->sta.addr, addr)) - break; - sta = rcu_dereference_check(sta->hnext, - lockdep_is_held(&local->sta_mtx)); + rcu_read_lock(); + tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash); + + for_each_sta_info(local, tbl, addr, sta, tmp) { + if (sta->sdata == sdata || + (sta->sdata->bss && sta->sdata->bss == sdata->bss)) { + rcu_read_unlock(); + /* this is safe as the caller must already hold + * another rcu read section or the mutex + */ + return sta; + } } - return sta; + rcu_read_unlock(); + return NULL; } struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata, @@ -242,9 +226,8 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta) 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_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta); + rhashtable_insert_fast(&local->sta_hash, &sta->hash_node, + sta_rht_params); } static void sta_deliver_ps_frames(struct work_struct *wk) @@ -948,19 +931,32 @@ static void sta_info_cleanup(unsigned long data) round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL)); } -void sta_info_init(struct ieee80211_local *local) +u32 sta_addr_hash(const void *key, u32 length, u32 seed) +{ + return jhash(key, ETH_ALEN, seed); +} + +int sta_info_init(struct ieee80211_local *local) { + int err; + + err = rhashtable_init(&local->sta_hash, &sta_rht_params); + if (err) + return err; + spin_lock_init(&local->tim_lock); mutex_init(&local->sta_mtx); INIT_LIST_HEAD(&local->sta_list); setup_timer(&local->sta_cleanup, sta_info_cleanup, (unsigned long)local); + return 0; } void sta_info_stop(struct ieee80211_local *local) { del_timer_sync(&local->sta_cleanup); + rhashtable_destroy(&local->sta_hash); } @@ -1024,16 +1020,21 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, } struct ieee80211_sta *ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw, - const u8 *addr, - const u8 *localaddr) + const u8 *addr, + const u8 *localaddr) { - struct sta_info *sta, *nxt; + struct ieee80211_local *local = hw_to_local(hw); + struct sta_info *sta; + struct rhash_head *tmp; + const struct bucket_table *tbl; + + tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash); /* * Just return a random station if localaddr is NULL * ... first in list. */ - for_each_sta_info(hw_to_local(hw), addr, sta, nxt) { + for_each_sta_info(local, tbl, addr, sta, tmp) { if (localaddr && !ether_addr_equal(sta->sdata->vif.addr, localaddr)) continue; diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 248f56e59ebc..97f25b9e52be 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -16,6 +16,7 @@ #include #include #include +#include #include "key.h" /** @@ -246,7 +247,7 @@ struct sta_ampdu_mlme { * * @list: global linked list entry * @free_list: list entry for keeping track of stations to free - * @hnext: hash table linked list pointer + * @hash_node: hash node for rhashtable * @local: pointer to the global information * @sdata: virtual interface this station belongs to * @ptk: peer keys negotiated with this station, if any @@ -339,7 +340,7 @@ struct sta_info { /* General information, mostly static */ struct list_head list, free_list; struct rcu_head rcu_head; - struct sta_info __rcu *hnext; + struct rhash_head hash_node; struct ieee80211_local *local; struct ieee80211_sub_if_data *sdata; struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS]; @@ -535,10 +536,6 @@ rcu_dereference_protected_tid_tx(struct sta_info *sta, int tid) lockdep_is_held(&sta->ampdu_mlme.mtx)); } -#define STA_HASH_SIZE 256 -#define STA_HASH(sta) (sta[5]) - - /* Maximum number of frames to buffer per power saving station per AC */ #define STA_MAX_TX_BUFFER 64 @@ -559,26 +556,15 @@ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata, const u8 *addr); -static inline -void for_each_sta_info_type_check(struct ieee80211_local *local, - const u8 *addr, - struct sta_info *sta, - struct sta_info *nxt) -{ -} +u32 sta_addr_hash(const void *key, u32 length, u32 seed); + +#define _sta_bucket_idx(_tbl, _a) \ + rht_bucket_index(_tbl, sta_addr_hash(_a, ETH_ALEN, (_tbl)->hash_rnd)) -#define for_each_sta_info(local, _addr, _sta, nxt) \ - for ( /* initialise loop */ \ - _sta = rcu_dereference(local->sta_hash[STA_HASH(_addr)]),\ - nxt = _sta ? rcu_dereference(_sta->hnext) : NULL; \ - /* typecheck */ \ - for_each_sta_info_type_check(local, (_addr), _sta, nxt),\ - /* continue condition */ \ - _sta; \ - /* advance loop */ \ - _sta = nxt, \ - nxt = _sta ? rcu_dereference(_sta->hnext) : NULL \ - ) \ +#define for_each_sta_info(local, tbl, _addr, _sta, _tmp) \ + rht_for_each_entry_rcu(_sta, _tmp, tbl, \ + _sta_bucket_idx(tbl, _addr), \ + hash_node) \ /* compare address and run code only if it matches */ \ if (ether_addr_equal(_sta->sta.addr, (_addr))) @@ -615,7 +601,7 @@ int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata, void sta_info_recalc_tim(struct sta_info *sta); -void sta_info_init(struct ieee80211_local *local); +int sta_info_init(struct ieee80211_local *local); void sta_info_stop(struct ieee80211_local *local); /** diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 2c51742428d5..005fdbe39a8b 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -654,7 +654,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) struct ieee80211_supported_band *sband; struct ieee80211_sub_if_data *sdata; struct net_device *prev_dev = NULL; - struct sta_info *sta, *tmp; + struct sta_info *sta; + struct rhash_head *tmp; int retry_count; int rates_idx; bool send_to_cooked; @@ -663,6 +664,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) int rtap_len; int shift = 0; int tid = IEEE80211_NUM_TIDS; + const struct bucket_table *tbl; rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count); @@ -671,7 +673,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) sband = local->hw.wiphy->bands[info->band]; fc = hdr->frame_control; - for_each_sta_info(local, hdr->addr1, sta, tmp) { + tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash); + + for_each_sta_info(local, tbl, hdr->addr1, sta, tmp) { /* skip wrong virtual interface */ if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr)) continue; -- cgit v1.2.3 From e161f7f6c4143ef0d945e98b2d1032cf82939fb6 Mon Sep 17 00:00:00 2001 From: Thomas Huehn Date: Tue, 24 Mar 2015 21:09:34 +0100 Subject: mac80211: enhance readability of Minstrels rc_stats output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch restructures the rc_stats debugfs table of Minstrel in order to achieve better human readability. A new layout of the statistics and a new header is added. In addition to the old layout there are two new columns of information added: idx - representing the rate index of each rate in mac80211 which can be used to set specific rates as fixed rate via debugfs airtime - the tx-time in micro seconds that a 1200 Byte packet takes to be transmitted over the air at the given rate The old layout of rc_stats: rate tpt eprob *prob ret *ok(*cum) ok( cum) DP 1 0.9 93.5 100.0 1 0( 0) 2( 2) 2 0.4 40.0 100.0 0 0( 0) 4( 10) 5.5 0.0 0.0 0.0 0 0( 0) 0( 0) ... is changed into this new layout: best _______rate_____ __statistics__ ________last_______ ______sum-of________ rate [name idx tx-time] [ ø(tp) ø(prob)] [prob.|retry|suc|att] [#success | #attempts] DP 1 0 9738 0.9 93.5 100.0 1 1 1 2 2 2 1 4922 0.4 40.0 100.0 1 0 0 4 10 5.5 2 1858 0.0 0.0 0.0 2 0 0 0 0 ... Signed-off-by: Thomas Huehn Signed-off-by: Stefan Venz Acked-by: Felix Fietkau Signed-off-by: Johannes Berg --- net/mac80211/rc80211_minstrel_debugfs.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/rc80211_minstrel_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c index 2acab1bcaa4b..2d70081a77eb 100644 --- a/net/mac80211/rc80211_minstrel_debugfs.c +++ b/net/mac80211/rc80211_minstrel_debugfs.c @@ -68,8 +68,12 @@ minstrel_stats_open(struct inode *inode, struct file *file) file->private_data = ms; p = ms->buf; - p += sprintf(p, "rate tpt eprob *prob" - " *ok(*cum) ok( cum)\n"); + p += sprintf(p, "\n"); + p += sprintf(p, "best _______rate_____ __statistics__ " + "________last_______ ______sum-of________\n"); + p += sprintf(p, "rate [name idx airtime] [ ø(tp) ø(prob)] " + "[prob.|retry|suc|att] [#success | #attempts]\n"); + for (i = 0; i < mi->n_rates; i++) { struct minstrel_rate *mr = &mi->r[i]; struct minstrel_rate_stats *mrs = &mi->r[i].stats; @@ -79,18 +83,22 @@ minstrel_stats_open(struct inode *inode, struct file *file) *(p++) = (i == mi->max_tp_rate[2]) ? 'C' : ' '; *(p++) = (i == mi->max_tp_rate[3]) ? 'D' : ' '; *(p++) = (i == mi->max_prob_rate) ? 'P' : ' '; - p += sprintf(p, "%3u%s", mr->bitrate / 2, + + p += sprintf(p, " %3u%s ", mr->bitrate / 2, (mr->bitrate & 1 ? ".5" : " ")); + p += sprintf(p, "%3u ", i); + p += sprintf(p, "%6u ", mr->perfect_tx_time); tp = MINSTREL_TRUNC(mrs->cur_tp / 10); prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); eprob = MINSTREL_TRUNC(mrs->probability * 1000); - p += sprintf(p, " %4u.%1u %3u.%1u %3u.%1u" - " %4u(%4u) %9llu(%9llu)\n", + p += sprintf(p, " %4u.%1u %3u.%1u %3u.%1u %3u" + " %3u %-3u %9llu %-9llu\n", tp / 10, tp % 10, eprob / 10, eprob % 10, prob / 10, prob % 10, + mrs->retry_count, mrs->last_success, mrs->last_attempts, (unsigned long long)mrs->succ_hist, -- cgit v1.2.3 From 9c00bb7210e5bc0691c93b05a0edfab0e93b3ec9 Mon Sep 17 00:00:00 2001 From: Thomas Huehn Date: Tue, 24 Mar 2015 21:09:35 +0100 Subject: mac80211: enhance readability of Minstrel-HTs rc_stats output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch restructures the rc_stats debugfs table of Minstrel-HT in order to achieve better human readability. A new layout of the statistics and a new header is added. In addition to the old layout there are two new columns of information added: idx - representing the rate index of each rate in mac80211 which can be used to set specific rates as fixed rate via debugfs airtime - the tx-time in micro seconds that a 1200 Byte packet takes to be transmitted over the air at the given rate The old layout of rc_stats: type rate tpt eprob *prob ret *ok(*cum) ok( cum) HT20/LGI MCS0 5.6 100.0 100.0 1 0( 0) 1( 1) HT20/LGI B MCS1 10.5 100.0 100.0 0 0( 0) 1( 1) HT20/LGI A MCS2 14.8 100.0 100.0 0 0( 0) 1( 1) ... is changed into this new layout: best ________rate______ __statistics__ ________last_______ ______sum-of________ mode guard # rate [name idx airtime] [ ø(tp) ø(prob)] [prob.|retry|suc|att] [#success | #attempts] HT20 LGI 1 MCS0 0 1480 0.0 0.0 0.0 1 0 0 0 0 HT20 LGI 1 B MCS1 1 740 10.5 100.0 100.0 0 0 0 1 1 HT20 LGI 1 A MCS2 2 496 14.8 100.0 100.0 0 0 0 1 1 ... Signed-off-by: Thomas Huehn Signed-off-by: Stefan Venz Acked-by: Felix Fietkau Signed-off-by: Johannes Berg --- net/mac80211/rc80211_minstrel_ht_debugfs.c | 49 +++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 15 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c index 20c676b8e5b6..7fc690ffd85a 100644 --- a/net/mac80211/rc80211_minstrel_ht_debugfs.c +++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c @@ -19,7 +19,7 @@ static char * minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) { const struct mcs_group *mg; - unsigned int j, tp, prob, eprob; + unsigned int j, tp, prob, eprob, tx_time; char htmode = '2'; char gimode = 'L'; u32 gflags; @@ -45,12 +45,19 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) if (!(mi->groups[i].supported & BIT(j))) continue; - if (gflags & IEEE80211_TX_RC_MCS) - p += sprintf(p, " HT%c0/%cGI ", htmode, gimode); - else if (gflags & IEEE80211_TX_RC_VHT_MCS) - p += sprintf(p, "VHT%c0/%cGI ", htmode, gimode); - else - p += sprintf(p, " CCK/%cP ", j < 4 ? 'L' : 'S'); + if (gflags & IEEE80211_TX_RC_MCS) { + p += sprintf(p, "HT%c0 ", htmode); + p += sprintf(p, "%cGI ", gimode); + p += sprintf(p, "%d ", mg->streams); + } else if (gflags & IEEE80211_TX_RC_VHT_MCS) { + p += sprintf(p, "VHT%c0 ", htmode); + p += sprintf(p, "%cGI ", gimode); + p += sprintf(p, "%d ", mg->streams); + } else { + p += sprintf(p, "CCK "); + p += sprintf(p, "%cP ", j < 4 ? 'L' : 'S'); + p += sprintf(p, "1 "); + } *(p++) = (idx == mi->max_tp_rate[0]) ? 'A' : ' '; *(p++) = (idx == mi->max_tp_rate[1]) ? 'B' : ' '; @@ -59,21 +66,27 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) *(p++) = (idx == mi->max_prob_rate) ? 'P' : ' '; if (gflags & IEEE80211_TX_RC_MCS) { - p += sprintf(p, " MCS%-2u ", (mg->streams - 1) * 8 + j); + p += sprintf(p, " MCS%-2u", (mg->streams - 1) * 8 + j); } else if (gflags & IEEE80211_TX_RC_VHT_MCS) { - p += sprintf(p, " MCS%-1u/%1u", j, mg->streams); + p += sprintf(p, " MCS%-1u/%1u", j, mg->streams); } else { int r = bitrates[j % 4]; - p += sprintf(p, " %2u.%1uM ", r / 10, r % 10); + p += sprintf(p, " %2u.%1uM", r / 10, r % 10); } + p += sprintf(p, " %3u ", idx); + + /* tx_time[rate(i)] in usec */ + tx_time = DIV_ROUND_CLOSEST(mg->duration[j], 1000); + p += sprintf(p, "%6u ", tx_time); + tp = mr->cur_tp / 10; prob = MINSTREL_TRUNC(mr->cur_prob * 1000); eprob = MINSTREL_TRUNC(mr->probability * 1000); - p += sprintf(p, " %4u.%1u %3u.%1u %3u.%1u " - "%3u %4u(%4u) %9llu(%9llu)\n", + p += sprintf(p, "%4u.%1u %3u.%1u %3u.%1u " + "%3u %3u %-3u %9llu %-9llu\n", tp / 10, tp % 10, eprob / 10, eprob % 10, prob / 10, prob % 10, @@ -110,8 +123,14 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file) file->private_data = ms; p = ms->buf; - p += sprintf(p, " type rate tpt eprob *prob " - "ret *ok(*cum) ok( cum)\n"); + + p += sprintf(p, "\n"); + p += sprintf(p, " best ________rate______ " + "__statistics__ ________last_______ " + "______sum-of________\n"); + p += sprintf(p, "mode guard # rate [name idx airtime] [ ø(tp) " + "ø(prob)] [prob.|retry|suc|att] [#success | " + "#attempts]\n"); p = minstrel_ht_stats_dump(mi, MINSTREL_CCK_GROUP, p); for (i = 0; i < MINSTREL_CCK_GROUP; i++) @@ -123,7 +142,7 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file) "lookaround %d\n", max(0, (int) mi->total_packets - (int) mi->sample_packets), mi->sample_packets); - p += sprintf(p, "Average A-MPDU length: %d.%d\n", + p += sprintf(p, "Average # of aggregated frames per A-MPDU: %d.%d\n", MINSTREL_TRUNC(mi->avg_ampdu_len), MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10); ms->len = p - ms->buf; -- cgit v1.2.3 From 6d4885177929b1d978babe24d61ece687782c303 Mon Sep 17 00:00:00 2001 From: Thomas Huehn Date: Tue, 24 Mar 2015 21:09:36 +0100 Subject: mac80211: add new Minstrel statistic output via csv This patch adds a new debugfs file "rc_stats_csv" to output Minstrels statistics in a common csv format that is easy to parse. Signed-off-by: Thomas Huehn Signed-off-by: Stefan Venz Acked-by: Felix Fietkau [remove printing current time of day] Signed-off-by: Johannes Berg --- net/mac80211/rc80211_minstrel.h | 6 +-- net/mac80211/rc80211_minstrel_debugfs.c | 90 +++++++++++++++++++++++++++++---- 2 files changed, 82 insertions(+), 14 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h index 410efe620c57..9613e7392c7d 100644 --- a/net/mac80211/rc80211_minstrel.h +++ b/net/mac80211/rc80211_minstrel.h @@ -13,7 +13,6 @@ #define EWMA_DIV 128 #define SAMPLE_COLUMNS 10 /* number of columns in sample table */ - /* scaled fraction values */ #define MINSTREL_SCALE 16 #define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div) @@ -24,7 +23,7 @@ /* * Perform EWMA (Exponentially Weighted Moving Average) calculation - */ + */ static inline int minstrel_ewma(int old, int new, int weight) { @@ -95,6 +94,7 @@ struct minstrel_sta_info { #ifdef CONFIG_MAC80211_DEBUGFS struct dentry *dbg_stats; + struct dentry *dbg_stats_csv; #endif }; @@ -121,7 +121,6 @@ struct minstrel_priv { u32 fixed_rate_idx; struct dentry *dbg_fixed_rate; #endif - }; struct minstrel_debugfs_info { @@ -135,6 +134,7 @@ void minstrel_remove_sta_debugfs(void *priv, void *priv_sta); /* debugfs */ int minstrel_stats_open(struct inode *inode, struct file *file); +int minstrel_stats_csv_open(struct inode *inode, struct file *file); ssize_t minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *ppos); int minstrel_stats_release(struct inode *inode, struct file *file); diff --git a/net/mac80211/rc80211_minstrel_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c index 2d70081a77eb..97d118dab5e2 100644 --- a/net/mac80211/rc80211_minstrel_debugfs.c +++ b/net/mac80211/rc80211_minstrel_debugfs.c @@ -54,6 +54,22 @@ #include #include "rc80211_minstrel.h" +ssize_t +minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *ppos) +{ + struct minstrel_debugfs_info *ms; + + ms = file->private_data; + return simple_read_from_buffer(buf, len, ppos, ms->buf, ms->len); +} + +int +minstrel_stats_release(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + return 0; +} + int minstrel_stats_open(struct inode *inode, struct file *file) { @@ -115,25 +131,72 @@ minstrel_stats_open(struct inode *inode, struct file *file) return 0; } -ssize_t -minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *ppos) +static const struct file_operations minstrel_stat_fops = { + .owner = THIS_MODULE, + .open = minstrel_stats_open, + .read = minstrel_stats_read, + .release = minstrel_stats_release, + .llseek = default_llseek, +}; + +int +minstrel_stats_csv_open(struct inode *inode, struct file *file) { + struct minstrel_sta_info *mi = inode->i_private; struct minstrel_debugfs_info *ms; + unsigned int i, tp, prob, eprob; + char *p; - ms = file->private_data; - return simple_read_from_buffer(buf, len, ppos, ms->buf, ms->len); -} + ms = kmalloc(2048, GFP_KERNEL); + if (!ms) + return -ENOMEM; + + file->private_data = ms; + p = ms->buf; + + for (i = 0; i < mi->n_rates; i++) { + struct minstrel_rate *mr = &mi->r[i]; + struct minstrel_rate_stats *mrs = &mi->r[i].stats; + + p += sprintf(p, "%s" ,((i == mi->max_tp_rate[0]) ? "A" : "")); + p += sprintf(p, "%s" ,((i == mi->max_tp_rate[1]) ? "B" : "")); + p += sprintf(p, "%s" ,((i == mi->max_tp_rate[2]) ? "C" : "")); + p += sprintf(p, "%s" ,((i == mi->max_tp_rate[3]) ? "D" : "")); + p += sprintf(p, "%s" ,((i == mi->max_prob_rate) ? "P" : "")); + + p += sprintf(p, ",%u%s", mr->bitrate / 2, + (mr->bitrate & 1 ? ".5," : ",")); + p += sprintf(p, "%u,", i); + p += sprintf(p, "%u,",mr->perfect_tx_time); + + tp = MINSTREL_TRUNC(mrs->cur_tp / 10); + prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); + eprob = MINSTREL_TRUNC(mrs->probability * 1000); + + p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u,%u,%u," + "%llu,%llu,%d,%d\n", + tp / 10, tp % 10, + eprob / 10, eprob % 10, + prob / 10, prob % 10, + mrs->retry_count, + mrs->last_success, + mrs->last_attempts, + (unsigned long long)mrs->succ_hist, + (unsigned long long)mrs->att_hist, + mi->total_packets - mi->sample_packets, + mi->sample_packets); + + } + ms->len = p - ms->buf; + + WARN_ON(ms->len + sizeof(*ms) > 2048); -int -minstrel_stats_release(struct inode *inode, struct file *file) -{ - kfree(file->private_data); return 0; } -static const struct file_operations minstrel_stat_fops = { +static const struct file_operations minstrel_stat_csv_fops = { .owner = THIS_MODULE, - .open = minstrel_stats_open, + .open = minstrel_stats_csv_open, .read = minstrel_stats_read, .release = minstrel_stats_release, .llseek = default_llseek, @@ -146,6 +209,9 @@ minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir) mi->dbg_stats = debugfs_create_file("rc_stats", S_IRUGO, dir, mi, &minstrel_stat_fops); + + mi->dbg_stats_csv = debugfs_create_file("rc_stats_csv", S_IRUGO, dir, + mi, &minstrel_stat_csv_fops); } void @@ -154,4 +220,6 @@ minstrel_remove_sta_debugfs(void *priv, void *priv_sta) struct minstrel_sta_info *mi = priv_sta; debugfs_remove(mi->dbg_stats); + + debugfs_remove(mi->dbg_stats_csv); } -- cgit v1.2.3 From 2cae0b6a70d666f8905f0f4bf788e56df1273878 Mon Sep 17 00:00:00 2001 From: Thomas Huehn Date: Tue, 24 Mar 2015 21:09:37 +0100 Subject: mac80211: add new Minstrel-HT statistic output via csv This patch adds a new debugfs file "rc_stats_csv" to output Minstrel-HTs statistics in a common csv format that is easy to parse. Signed-off-by: Thomas Huehn Signed-off-by: Stefan Venz Acked-by: Felix Fietkau [remove printing current time of day] Signed-off-by: Johannes Berg --- net/mac80211/rc80211_minstrel_ht.h | 1 + net/mac80211/rc80211_minstrel_ht_debugfs.c | 139 ++++++++++++++++++++++++++++- 2 files changed, 138 insertions(+), 2 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/rc80211_minstrel_ht.h b/net/mac80211/rc80211_minstrel_ht.h index f2217d6aa0c2..3cc30e8f0613 100644 --- a/net/mac80211/rc80211_minstrel_ht.h +++ b/net/mac80211/rc80211_minstrel_ht.h @@ -112,6 +112,7 @@ struct minstrel_ht_sta_priv { }; #ifdef CONFIG_MAC80211_DEBUGFS struct dentry *dbg_stats; + struct dentry *dbg_stats_csv; #endif void *ratelist; void *sample_table; diff --git a/net/mac80211/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c index 7fc690ffd85a..95731e639d4e 100644 --- a/net/mac80211/rc80211_minstrel_ht_debugfs.c +++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c @@ -107,8 +107,8 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file) struct minstrel_ht_sta *mi = &msp->ht; struct minstrel_debugfs_info *ms; unsigned int i; - char *p; int ret; + char *p; if (!msp->is_ht) { inode->i_private = &msp->legacy; @@ -146,7 +146,6 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file) MINSTREL_TRUNC(mi->avg_ampdu_len), MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10); ms->len = p - ms->buf; - WARN_ON(ms->len + sizeof(*ms) > 32768); return nonseekable_open(inode, file); @@ -160,6 +159,139 @@ static const struct file_operations minstrel_ht_stat_fops = { .llseek = no_llseek, }; +static char * +minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p) +{ + const struct mcs_group *mg; + unsigned int j, tp, prob, eprob, tx_time; + char htmode = '2'; + char gimode = 'L'; + u32 gflags; + + if (!mi->groups[i].supported) + return p; + + mg = &minstrel_mcs_groups[i]; + gflags = mg->flags; + + if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH) + htmode = '4'; + else if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH) + htmode = '8'; + if (gflags & IEEE80211_TX_RC_SHORT_GI) + gimode = 'S'; + + for (j = 0; j < MCS_GROUP_RATES; j++) { + struct minstrel_rate_stats *mr = &mi->groups[i].rates[j]; + static const int bitrates[4] = { 10, 20, 55, 110 }; + int idx = i * MCS_GROUP_RATES + j; + + if (!(mi->groups[i].supported & BIT(j))) + continue; + + if (gflags & IEEE80211_TX_RC_MCS) { + p += sprintf(p, "HT%c0,", htmode); + p += sprintf(p, "%cGI,", gimode); + p += sprintf(p, "%d,", mg->streams); + } else if (gflags & IEEE80211_TX_RC_VHT_MCS) { + p += sprintf(p, "VHT%c0,", htmode); + p += sprintf(p, "%cGI,", gimode); + p += sprintf(p, "%d,", mg->streams); + } else { + p += sprintf(p, "CCK,"); + p += sprintf(p, "%cP,", j < 4 ? 'L' : 'S'); + p += sprintf(p, "1,"); + } + + p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[0]) ? "A" : "")); + p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[1]) ? "B" : "")); + p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[2]) ? "C" : "")); + p += sprintf(p, "%s" ,((idx == mi->max_tp_rate[3]) ? "D" : "")); + p += sprintf(p, "%s" ,((idx == mi->max_prob_rate) ? "P" : "")); + + if (gflags & IEEE80211_TX_RC_MCS) { + p += sprintf(p, ",MCS%-2u,", (mg->streams - 1) * 8 + j); + } else if (gflags & IEEE80211_TX_RC_VHT_MCS) { + p += sprintf(p, ",MCS%-1u/%1u,", j, mg->streams); + } else { + int r = bitrates[j % 4]; + p += sprintf(p, ",%2u.%1uM,", r / 10, r % 10); + } + + p += sprintf(p, "%u,", idx); + tx_time = DIV_ROUND_CLOSEST(mg->duration[j], 1000); + p += sprintf(p, "%u,", tx_time); + + tp = mr->cur_tp / 10; + prob = MINSTREL_TRUNC(mr->cur_prob * 1000); + eprob = MINSTREL_TRUNC(mr->probability * 1000); + + p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u,%u,%u,%llu,%llu,", + tp / 10, tp % 10, + eprob / 10, eprob % 10, + prob / 10, prob % 10, + mr->retry_count, + mr->last_success, + mr->last_attempts, + (unsigned long long)mr->succ_hist, + (unsigned long long)mr->att_hist); + p += sprintf(p, "%d,%d,%d.%d\n", + max(0, (int) mi->total_packets - + (int) mi->sample_packets), + mi->sample_packets, + MINSTREL_TRUNC(mi->avg_ampdu_len), + MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10); + } + + return p; +} + +static int +minstrel_ht_stats_csv_open(struct inode *inode, struct file *file) +{ + struct minstrel_ht_sta_priv *msp = inode->i_private; + struct minstrel_ht_sta *mi = &msp->ht; + struct minstrel_debugfs_info *ms; + unsigned int i; + int ret; + char *p; + + if (!msp->is_ht) { + inode->i_private = &msp->legacy; + ret = minstrel_stats_csv_open(inode, file); + inode->i_private = msp; + return ret; + } + + ms = kmalloc(32768, GFP_KERNEL); + + if (!ms) + return -ENOMEM; + + file->private_data = ms; + + p = ms->buf; + + p = minstrel_ht_stats_csv_dump(mi, MINSTREL_CCK_GROUP, p); + for (i = 0; i < MINSTREL_CCK_GROUP; i++) + p = minstrel_ht_stats_csv_dump(mi, i, p); + for (i++; i < ARRAY_SIZE(mi->groups); i++) + p = minstrel_ht_stats_csv_dump(mi, i, p); + + ms->len = p - ms->buf; + WARN_ON(ms->len + sizeof(*ms) > 32768); + + return nonseekable_open(inode, file); +} + +static const struct file_operations minstrel_ht_stat_csv_fops = { + .owner = THIS_MODULE, + .open = minstrel_ht_stats_csv_open, + .read = minstrel_stats_read, + .release = minstrel_stats_release, + .llseek = no_llseek, +}; + void minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir) { @@ -167,6 +299,8 @@ minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir) msp->dbg_stats = debugfs_create_file("rc_stats", S_IRUGO, dir, msp, &minstrel_ht_stat_fops); + msp->dbg_stats_csv = debugfs_create_file("rc_stats_csv", S_IRUGO, + dir, msp, &minstrel_ht_stat_csv_fops); } void @@ -175,4 +309,5 @@ minstrel_ht_remove_sta_debugfs(void *priv, void *priv_sta) struct minstrel_ht_sta_priv *msp = priv_sta; debugfs_remove(msp->dbg_stats); + debugfs_remove(msp->dbg_stats_csv); } -- cgit v1.2.3 From f62838bcc5f58f580961aaf8c9fe59036b7be825 Mon Sep 17 00:00:00 2001 From: Thomas Huehn Date: Tue, 24 Mar 2015 21:09:38 +0100 Subject: mac80211: unify Minstrel & Minstrel-HTs calculation of rate statistics This patch unifies the calculation of Minstrels and Minstrel-HTs per-rate statistic. The new common function minstrel_calc_rate_stats() is called when a statistic update is performed. Signed-off-by: Thomas Huehn Acked-by: Felix Fietkau Signed-off-by: Johannes Berg --- net/mac80211/rc80211_minstrel.c | 44 ++++++++++++++++++++++++-------------- net/mac80211/rc80211_minstrel.h | 3 +++ net/mac80211/rc80211_minstrel_ht.c | 28 +----------------------- 3 files changed, 32 insertions(+), 43 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index ef6e8a6c4253..5528037021ad 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -127,6 +127,32 @@ minstrel_update_rates(struct minstrel_priv *mp, struct minstrel_sta_info *mi) rate_control_set_rates(mp->hw, mi->sta, ratetbl); } +/* +* Recalculate success probabilities and counters for a given rate using EWMA +*/ +void +minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs) +{ + if (unlikely(mrs->attempts > 0)) { + mrs->sample_skipped = 0; + mrs->cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts); + if (unlikely(!mrs->att_hist)) + mrs->probability = mrs->cur_prob; + else + mrs->probability = minstrel_ewma(mrs->probability, + mrs->cur_prob, EWMA_LEVEL); + mrs->att_hist += mrs->attempts; + mrs->succ_hist += mrs->success; + } else { + mrs->sample_skipped++; + } + + mrs->last_success = mrs->success; + mrs->last_attempts = mrs->attempts; + mrs->success = 0; + mrs->attempts = 0; +} + static void minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) { @@ -146,22 +172,8 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) if (!usecs) usecs = 1000000; - if (unlikely(mrs->attempts > 0)) { - mrs->sample_skipped = 0; - mrs->cur_prob = MINSTREL_FRAC(mrs->success, - mrs->attempts); - mrs->succ_hist += mrs->success; - mrs->att_hist += mrs->attempts; - mrs->probability = minstrel_ewma(mrs->probability, - mrs->cur_prob, - EWMA_LEVEL); - } else - mrs->sample_skipped++; - - mrs->last_success = mrs->success; - mrs->last_attempts = mrs->attempts; - mrs->success = 0; - mrs->attempts = 0; + /* Update success probabilities per rate */ + minstrel_calc_rate_stats(mrs); /* Update throughput per rate, reset thr. below 10% success */ if (mrs->probability < MINSTREL_FRAC(10, 100)) diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h index 9613e7392c7d..728144c8df3f 100644 --- a/net/mac80211/rc80211_minstrel.h +++ b/net/mac80211/rc80211_minstrel.h @@ -132,6 +132,9 @@ extern const struct rate_control_ops mac80211_minstrel; void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir); void minstrel_remove_sta_debugfs(void *priv, void *priv_sta); +/* Recalculate success probabilities and counters for a given rate using EWMA */ +void minstrel_calc_rate_stats(struct minstrel_rate_stats *mr); + /* debugfs */ int minstrel_stats_open(struct inode *inode, struct file *file); int minstrel_stats_csv_open(struct inode *inode, struct file *file); diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 60698fc7042e..7afa5623a5ee 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -313,32 +313,6 @@ minstrel_get_ratestats(struct minstrel_ht_sta *mi, int index) return &mi->groups[index / MCS_GROUP_RATES].rates[index % MCS_GROUP_RATES]; } - -/* - * Recalculate success probabilities and counters for a rate using EWMA - */ -static void -minstrel_calc_rate_ewma(struct minstrel_rate_stats *mr) -{ - if (unlikely(mr->attempts > 0)) { - mr->sample_skipped = 0; - mr->cur_prob = MINSTREL_FRAC(mr->success, mr->attempts); - if (!mr->att_hist) - mr->probability = mr->cur_prob; - else - mr->probability = minstrel_ewma(mr->probability, - mr->cur_prob, EWMA_LEVEL); - mr->att_hist += mr->attempts; - mr->succ_hist += mr->success; - } else { - mr->sample_skipped++; - } - mr->last_success = mr->success; - mr->last_attempts = mr->attempts; - mr->success = 0; - mr->attempts = 0; -} - /* * Calculate throughput based on the average A-MPDU length, taking into account * the expected number of retransmissions and their expected length @@ -567,7 +541,7 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) mr = &mg->rates[i]; mr->retry_updated = false; - minstrel_calc_rate_ewma(mr); + minstrel_calc_rate_stats(mr); minstrel_ht_calc_tp(mi, group, i); if (!mr->cur_tp) -- cgit v1.2.3 From 9134073bc693633b5e1f1a7252c93b3fb262aae4 Mon Sep 17 00:00:00 2001 From: Thomas Huehn Date: Tue, 24 Mar 2015 21:09:39 +0100 Subject: mac80211: improve Minstrel variable & function naming This patch ensures a consistent usage of variable names for type "minstrel_rate_stats" to be used as "mrs" and from type minstrel_rate as "mr" across both Minstrel & Minstrel-HT. In addition some variable and function names got changed to more meaningful ones. Signed-off-by: Thomas Huehn Acked-by: Felix Fietkau Signed-off-by: Johannes Berg --- net/mac80211/rc80211_minstrel.c | 26 +++---- net/mac80211/rc80211_minstrel.h | 13 ++-- net/mac80211/rc80211_minstrel_debugfs.c | 4 +- net/mac80211/rc80211_minstrel_ht.c | 112 ++++++++++++++--------------- net/mac80211/rc80211_minstrel_ht.h | 2 +- net/mac80211/rc80211_minstrel_ht_debugfs.c | 36 +++++----- 6 files changed, 98 insertions(+), 95 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index 5528037021ad..c86e42b67908 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -137,9 +137,9 @@ minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs) mrs->sample_skipped = 0; mrs->cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts); if (unlikely(!mrs->att_hist)) - mrs->probability = mrs->cur_prob; + mrs->prob_ewma = mrs->cur_prob; else - mrs->probability = minstrel_ewma(mrs->probability, + mrs->prob_ewma = minstrel_ewma(mrs->prob_ewma, mrs->cur_prob, EWMA_LEVEL); mrs->att_hist += mrs->attempts; mrs->succ_hist += mrs->success; @@ -176,15 +176,15 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) minstrel_calc_rate_stats(mrs); /* Update throughput per rate, reset thr. below 10% success */ - if (mrs->probability < MINSTREL_FRAC(10, 100)) + if (mrs->prob_ewma < MINSTREL_FRAC(10, 100)) mrs->cur_tp = 0; else - mrs->cur_tp = mrs->probability * (1000000 / usecs); + mrs->cur_tp = mrs->prob_ewma * (1000000 / usecs); /* Sample less often below the 10% chance of success. * Sample less often above the 95% chance of success. */ - if (mrs->probability > MINSTREL_FRAC(95, 100) || - mrs->probability < MINSTREL_FRAC(10, 100)) { + if (mrs->prob_ewma > MINSTREL_FRAC(95, 100) || + mrs->prob_ewma < MINSTREL_FRAC(10, 100)) { mr->adjusted_retry_count = mrs->retry_count >> 1; if (mr->adjusted_retry_count > 2) mr->adjusted_retry_count = 2; @@ -204,11 +204,11 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) * choose the maximum throughput rate as max_prob_rate * (2) if all success probabilities < 95%, the rate with * highest success probability is chosen as max_prob_rate */ - if (mrs->probability >= MINSTREL_FRAC(95, 100)) { + if (mrs->prob_ewma >= MINSTREL_FRAC(95, 100)) { if (mrs->cur_tp >= mi->r[tmp_prob_rate].stats.cur_tp) tmp_prob_rate = i; } else { - if (mrs->probability >= mi->r[tmp_prob_rate].stats.probability) + if (mrs->prob_ewma >= mi->r[tmp_prob_rate].stats.prob_ewma) tmp_prob_rate = i; } } @@ -227,7 +227,7 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) #endif /* Reset update timer */ - mi->stats_update = jiffies; + mi->last_stats_update = jiffies; minstrel_update_rates(mp, mi); } @@ -265,7 +265,7 @@ minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband, if (mi->sample_deferred > 0) mi->sample_deferred--; - if (time_after(jiffies, mi->stats_update + + if (time_after(jiffies, mi->last_stats_update + (mp->update_interval * HZ) / 1000)) minstrel_update_stats(mp, mi); } @@ -397,7 +397,7 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta, * has a probability of >95%, we shouldn't be attempting * to use it, as this only wastes precious airtime */ if (!mrr_capable && - (mi->r[ndx].stats.probability > MINSTREL_FRAC(95, 100))) + (mi->r[ndx].stats.prob_ewma > MINSTREL_FRAC(95, 100))) return; mi->prev_sample = true; @@ -531,7 +531,7 @@ minstrel_rate_init(void *priv, struct ieee80211_supported_band *sband, } mi->n_rates = n; - mi->stats_update = jiffies; + mi->last_stats_update = jiffies; init_sample_table(mi); minstrel_update_rates(mp, mi); @@ -565,7 +565,7 @@ minstrel_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp) if (!mi->sample_table) goto error1; - mi->stats_update = jiffies; + mi->last_stats_update = jiffies; return mi; error1: diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h index 728144c8df3f..58f287028437 100644 --- a/net/mac80211/rc80211_minstrel.h +++ b/net/mac80211/rc80211_minstrel.h @@ -38,11 +38,14 @@ struct minstrel_rate_stats { /* total attempts/success counters */ u64 att_hist, succ_hist; - /* current throughput */ + /* current EWMA of rate throughput */ unsigned int cur_tp; - /* packet delivery probabilities */ - unsigned int cur_prob, probability; + /* statistis of packet delivery probability + * cur_prob - current prob within last update intervall + * prob_ewma - exponential weighted moving average of prob */ + unsigned int cur_prob; + unsigned int prob_ewma; /* maximum retry counts */ u8 retry_count; @@ -70,7 +73,7 @@ struct minstrel_rate { struct minstrel_sta_info { struct ieee80211_sta *sta; - unsigned long stats_update; + unsigned long last_stats_update; unsigned int sp_ack_dur; unsigned int rate_avg; @@ -133,7 +136,7 @@ void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir); void minstrel_remove_sta_debugfs(void *priv, void *priv_sta); /* Recalculate success probabilities and counters for a given rate using EWMA */ -void minstrel_calc_rate_stats(struct minstrel_rate_stats *mr); +void minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs); /* debugfs */ int minstrel_stats_open(struct inode *inode, struct file *file); diff --git a/net/mac80211/rc80211_minstrel_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c index 97d118dab5e2..8a0b73eda99a 100644 --- a/net/mac80211/rc80211_minstrel_debugfs.c +++ b/net/mac80211/rc80211_minstrel_debugfs.c @@ -107,7 +107,7 @@ minstrel_stats_open(struct inode *inode, struct file *file) tp = MINSTREL_TRUNC(mrs->cur_tp / 10); prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); - eprob = MINSTREL_TRUNC(mrs->probability * 1000); + eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); p += sprintf(p, " %4u.%1u %3u.%1u %3u.%1u %3u" " %3u %-3u %9llu %-9llu\n", @@ -171,7 +171,7 @@ minstrel_stats_csv_open(struct inode *inode, struct file *file) tp = MINSTREL_TRUNC(mrs->cur_tp / 10); prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); - eprob = MINSTREL_TRUNC(mrs->probability * 1000); + eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u,%u,%u," "%llu,%llu,%d,%d\n", diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 7afa5623a5ee..aca8b642661c 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -320,16 +320,16 @@ minstrel_get_ratestats(struct minstrel_ht_sta *mi, int index) static void minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate) { - struct minstrel_rate_stats *mr; + struct minstrel_rate_stats *mrs; unsigned int nsecs = 0; - unsigned int tp; - unsigned int prob; + unsigned int tmp_prob_ewma; - mr = &mi->groups[group].rates[rate]; - prob = mr->probability; + mrs = &mi->groups[group].rates[rate]; + tmp_prob_ewma = mrs->prob_ewma; - if (prob < MINSTREL_FRAC(1, 10)) { - mr->cur_tp = 0; + /* do not account throughput if sucess prob is below 10% */ + if (mrs->prob_ewma < MINSTREL_FRAC(10, 100)) { + mrs->cur_tp = 0; return; } @@ -337,8 +337,8 @@ minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate) * For the throughput calculation, limit the probability value to 90% to * account for collision related packet error rate fluctuation */ - if (prob > MINSTREL_FRAC(9, 10)) - prob = MINSTREL_FRAC(9, 10); + if (mrs->prob_ewma > MINSTREL_FRAC(90, 100)) + tmp_prob_ewma = MINSTREL_FRAC(90, 100); if (group != MINSTREL_CCK_GROUP) nsecs = 1000 * mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len); @@ -346,8 +346,7 @@ minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate) nsecs += minstrel_mcs_groups[group].duration[rate]; /* prob is scaled - see MINSTREL_FRAC above */ - tp = 1000000 * ((prob * 1000) / nsecs); - mr->cur_tp = MINSTREL_TRUNC(tp); + mrs->cur_tp = MINSTREL_TRUNC(1000000 * ((tmp_prob_ewma * 1000) / nsecs)); } /* @@ -368,13 +367,13 @@ minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u16 index, cur_group = index / MCS_GROUP_RATES; cur_idx = index % MCS_GROUP_RATES; cur_thr = mi->groups[cur_group].rates[cur_idx].cur_tp; - cur_prob = mi->groups[cur_group].rates[cur_idx].probability; + cur_prob = mi->groups[cur_group].rates[cur_idx].prob_ewma; do { tmp_group = tp_list[j - 1] / MCS_GROUP_RATES; tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES; tmp_thr = mi->groups[tmp_group].rates[tmp_idx].cur_tp; - tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability; + tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma; if (cur_thr < tmp_thr || (cur_thr == tmp_thr && cur_prob <= tmp_prob)) break; @@ -396,16 +395,16 @@ static void minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index) { struct minstrel_mcs_group_data *mg; - struct minstrel_rate_stats *mr; + struct minstrel_rate_stats *mrs; int tmp_group, tmp_idx, tmp_tp, tmp_prob, max_tp_group; mg = &mi->groups[index / MCS_GROUP_RATES]; - mr = &mg->rates[index % MCS_GROUP_RATES]; + mrs = &mg->rates[index % MCS_GROUP_RATES]; tmp_group = mi->max_prob_rate / MCS_GROUP_RATES; tmp_idx = mi->max_prob_rate % MCS_GROUP_RATES; tmp_tp = mi->groups[tmp_group].rates[tmp_idx].cur_tp; - tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability; + tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma; /* if max_tp_rate[0] is from MCS_GROUP max_prob_rate get selected from * MCS_GROUP as well as CCK_GROUP rates do not allow aggregation */ @@ -414,15 +413,15 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index) (max_tp_group != MINSTREL_CCK_GROUP)) return; - if (mr->probability > MINSTREL_FRAC(75, 100)) { - if (mr->cur_tp > tmp_tp) + if (mrs->prob_ewma > MINSTREL_FRAC(75, 100)) { + if (mrs->cur_tp > tmp_tp) mi->max_prob_rate = index; - if (mr->cur_tp > mg->rates[mg->max_group_prob_rate].cur_tp) + if (mrs->cur_tp > mg->rates[mg->max_group_prob_rate].cur_tp) mg->max_group_prob_rate = index; } else { - if (mr->probability > tmp_prob) + if (mrs->prob_ewma > tmp_prob) mi->max_prob_rate = index; - if (mr->probability > mg->rates[mg->max_group_prob_rate].probability) + if (mrs->prob_ewma > mg->rates[mg->max_group_prob_rate].prob_ewma) mg->max_group_prob_rate = index; } } @@ -467,7 +466,7 @@ static inline void minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi) { struct minstrel_mcs_group_data *mg; - struct minstrel_rate_stats *mr; + struct minstrel_rate_stats *mrs; int tmp_max_streams, group; int tmp_tp = 0; @@ -477,11 +476,11 @@ minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi) mg = &mi->groups[group]; if (!mg->supported || group == MINSTREL_CCK_GROUP) continue; - mr = minstrel_get_ratestats(mi, mg->max_group_prob_rate); - if (tmp_tp < mr->cur_tp && + mrs = minstrel_get_ratestats(mi, mg->max_group_prob_rate); + if (tmp_tp < mrs->cur_tp && (minstrel_mcs_groups[group].streams < tmp_max_streams)) { mi->max_prob_rate = mg->max_group_prob_rate; - tmp_tp = mr->cur_tp; + tmp_tp = mrs->cur_tp; } } } @@ -499,7 +498,7 @@ static void minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) { struct minstrel_mcs_group_data *mg; - struct minstrel_rate_stats *mr; + struct minstrel_rate_stats *mrs; int group, i, j; u16 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES]; u16 tmp_cck_tp_rate[MAX_THR_RATES], index; @@ -539,12 +538,12 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) index = MCS_GROUP_RATES * group + i; - mr = &mg->rates[i]; - mr->retry_updated = false; - minstrel_calc_rate_stats(mr); + mrs = &mg->rates[i]; + mrs->retry_updated = false; + minstrel_calc_rate_stats(mrs); minstrel_ht_calc_tp(mi, group, i); - if (!mr->cur_tp) + if (!mrs->cur_tp) continue; /* Find max throughput rate set */ @@ -588,7 +587,7 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) #endif /* Reset update timer */ - mi->stats_update = jiffies; + mi->last_stats_update = jiffies; } static bool @@ -611,7 +610,7 @@ minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct ieee80211_tx_rate *rat } static void -minstrel_next_sample_idx(struct minstrel_ht_sta *mi) +minstrel_set_next_sample_idx(struct minstrel_ht_sta *mi) { struct minstrel_mcs_group_data *mg; @@ -752,7 +751,8 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband, update = true; } - if (time_after(jiffies, mi->stats_update + (mp->update_interval / 2 * HZ) / 1000)) { + if (time_after(jiffies, mi->last_stats_update + + (mp->update_interval / 2 * HZ) / 1000)) { update = true; minstrel_ht_update_stats(mp, mi); } @@ -765,7 +765,7 @@ static void minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, int index) { - struct minstrel_rate_stats *mr; + struct minstrel_rate_stats *mrs; const struct mcs_group *group; unsigned int tx_time, tx_time_rtscts, tx_time_data; unsigned int cw = mp->cw_min; @@ -774,16 +774,16 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, unsigned int ampdu_len = MINSTREL_TRUNC(mi->avg_ampdu_len); unsigned int overhead = 0, overhead_rtscts = 0; - mr = minstrel_get_ratestats(mi, index); - if (mr->probability < MINSTREL_FRAC(1, 10)) { - mr->retry_count = 1; - mr->retry_count_rtscts = 1; + mrs = minstrel_get_ratestats(mi, index); + if (mrs->prob_ewma < MINSTREL_FRAC(1, 10)) { + mrs->retry_count = 1; + mrs->retry_count_rtscts = 1; return; } - mr->retry_count = 2; - mr->retry_count_rtscts = 2; - mr->retry_updated = true; + mrs->retry_count = 2; + mrs->retry_count_rtscts = 2; + mrs->retry_updated = true; group = &minstrel_mcs_groups[index / MCS_GROUP_RATES]; tx_time_data = group->duration[index % MCS_GROUP_RATES] * ampdu_len / 1000; @@ -814,9 +814,9 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, tx_time_rtscts += ctime + overhead_rtscts + tx_time_data; if (tx_time_rtscts < mp->segment_size) - mr->retry_count_rtscts++; + mrs->retry_count_rtscts++; } while ((tx_time < mp->segment_size) && - (++mr->retry_count < mp->max_retry)); + (++mrs->retry_count < mp->max_retry)); } @@ -825,22 +825,22 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, struct ieee80211_sta_rates *ratetbl, int offset, int index) { const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES]; - struct minstrel_rate_stats *mr; + struct minstrel_rate_stats *mrs; u8 idx; u16 flags = group->flags; - mr = minstrel_get_ratestats(mi, index); - if (!mr->retry_updated) + mrs = minstrel_get_ratestats(mi, index); + if (!mrs->retry_updated) minstrel_calc_retransmit(mp, mi, index); - if (mr->probability < MINSTREL_FRAC(20, 100) || !mr->retry_count) { + if (mrs->prob_ewma < MINSTREL_FRAC(20, 100) || !mrs->retry_count) { ratetbl->rate[offset].count = 2; ratetbl->rate[offset].count_rts = 2; ratetbl->rate[offset].count_cts = 2; } else { - ratetbl->rate[offset].count = mr->retry_count; - ratetbl->rate[offset].count_cts = mr->retry_count; - ratetbl->rate[offset].count_rts = mr->retry_count_rtscts; + ratetbl->rate[offset].count = mrs->retry_count; + ratetbl->rate[offset].count_cts = mrs->retry_count; + ratetbl->rate[offset].count_rts = mrs->retry_count_rtscts; } if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) @@ -898,7 +898,7 @@ minstrel_get_duration(int index) static int minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) { - struct minstrel_rate_stats *mr; + struct minstrel_rate_stats *mrs; struct minstrel_mcs_group_data *mg; unsigned int sample_dur, sample_group, cur_max_tp_streams; int sample_idx = 0; @@ -914,12 +914,12 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) sample_group = mi->sample_group; mg = &mi->groups[sample_group]; sample_idx = sample_table[mg->column][mg->index]; - minstrel_next_sample_idx(mi); + minstrel_set_next_sample_idx(mi); if (!(mg->supported & BIT(sample_idx))) return -1; - mr = &mg->rates[sample_idx]; + mrs = &mg->rates[sample_idx]; sample_idx += sample_group * MCS_GROUP_RATES; /* @@ -936,7 +936,7 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) * Do not sample if the probability is already higher than 95% * to avoid wasting airtime. */ - if (mr->probability > MINSTREL_FRAC(95, 100)) + if (mrs->prob_ewma > MINSTREL_FRAC(95, 100)) return -1; /* @@ -951,7 +951,7 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) (cur_max_tp_streams - 1 < minstrel_mcs_groups[sample_group].streams || sample_dur >= minstrel_get_duration(mi->max_prob_rate))) { - if (mr->sample_skipped < 20) + if (mrs->sample_skipped < 20) return -1; if (mi->sample_slow++ > 2) @@ -1105,7 +1105,7 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, memset(mi, 0, sizeof(*mi)); mi->sta = sta; - mi->stats_update = jiffies; + mi->last_stats_update = jiffies; ack_dur = ieee80211_frame_duration(sband->band, 10, 60, 1, 1, 0); mi->overhead = ieee80211_frame_duration(sband->band, 0, 60, 1, 1, 0); diff --git a/net/mac80211/rc80211_minstrel_ht.h b/net/mac80211/rc80211_minstrel_ht.h index 3cc30e8f0613..fa21a82b72c4 100644 --- a/net/mac80211/rc80211_minstrel_ht.h +++ b/net/mac80211/rc80211_minstrel_ht.h @@ -78,7 +78,7 @@ struct minstrel_ht_sta { u16 max_prob_rate; /* time of last status update */ - unsigned long stats_update; + unsigned long last_stats_update; /* overhead time in usec for each frame */ unsigned int overhead; diff --git a/net/mac80211/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c index 95731e639d4e..84183dee9ef2 100644 --- a/net/mac80211/rc80211_minstrel_ht_debugfs.c +++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c @@ -38,7 +38,7 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) gimode = 'S'; for (j = 0; j < MCS_GROUP_RATES; j++) { - struct minstrel_rate_stats *mr = &mi->groups[i].rates[j]; + struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j]; static const int bitrates[4] = { 10, 20, 55, 110 }; int idx = i * MCS_GROUP_RATES + j; @@ -81,20 +81,20 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) tx_time = DIV_ROUND_CLOSEST(mg->duration[j], 1000); p += sprintf(p, "%6u ", tx_time); - tp = mr->cur_tp / 10; - prob = MINSTREL_TRUNC(mr->cur_prob * 1000); - eprob = MINSTREL_TRUNC(mr->probability * 1000); + tp = mrs->cur_tp / 10; + prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); + eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); p += sprintf(p, "%4u.%1u %3u.%1u %3u.%1u " "%3u %3u %-3u %9llu %-9llu\n", tp / 10, tp % 10, eprob / 10, eprob % 10, prob / 10, prob % 10, - mr->retry_count, - mr->last_success, - mr->last_attempts, - (unsigned long long)mr->succ_hist, - (unsigned long long)mr->att_hist); + mrs->retry_count, + mrs->last_success, + mrs->last_attempts, + (unsigned long long)mrs->succ_hist, + (unsigned long long)mrs->att_hist); } return p; @@ -182,7 +182,7 @@ minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p) gimode = 'S'; for (j = 0; j < MCS_GROUP_RATES; j++) { - struct minstrel_rate_stats *mr = &mi->groups[i].rates[j]; + struct minstrel_rate_stats *mrs = &mi->groups[i].rates[j]; static const int bitrates[4] = { 10, 20, 55, 110 }; int idx = i * MCS_GROUP_RATES + j; @@ -222,19 +222,19 @@ minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p) tx_time = DIV_ROUND_CLOSEST(mg->duration[j], 1000); p += sprintf(p, "%u,", tx_time); - tp = mr->cur_tp / 10; - prob = MINSTREL_TRUNC(mr->cur_prob * 1000); - eprob = MINSTREL_TRUNC(mr->probability * 1000); + tp = mrs->cur_tp / 10; + prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); + eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u,%u,%u,%llu,%llu,", tp / 10, tp % 10, eprob / 10, eprob % 10, prob / 10, prob % 10, - mr->retry_count, - mr->last_success, - mr->last_attempts, - (unsigned long long)mr->succ_hist, - (unsigned long long)mr->att_hist); + mrs->retry_count, + mrs->last_success, + mrs->last_attempts, + (unsigned long long)mrs->succ_hist, + (unsigned long long)mrs->att_hist); p += sprintf(p, "%d,%d,%d.%d\n", max(0, (int) mi->total_packets - (int) mi->sample_packets), -- cgit v1.2.3 From 6a27b2c40b4829e625bc1dfdd0705c5ece720ab4 Mon Sep 17 00:00:00 2001 From: Thomas Huehn Date: Tue, 24 Mar 2015 21:09:40 +0100 Subject: mac80211: restructure per-rate throughput calculation into function This patch moves Minstrels and Minstrel-HTs per-rate throughput calculation (EWMA(thr)) into a dedicated function to be called. Therefore the variable "unsigned int cur_tp" within struct "minstrel_rate_stats" becomes obsolete. and is removed to free up its space. Signed-off-by: Thomas Huehn Acked-by: Felix Fietkau Signed-off-by: Johannes Berg --- net/mac80211/rc80211_minstrel.c | 43 +++++++++++------ net/mac80211/rc80211_minstrel.h | 4 +- net/mac80211/rc80211_minstrel_debugfs.c | 12 ++--- net/mac80211/rc80211_minstrel_ht.c | 77 ++++++++++++++++++------------ net/mac80211/rc80211_minstrel_ht.h | 1 + net/mac80211/rc80211_minstrel_ht_debugfs.c | 12 ++--- 6 files changed, 88 insertions(+), 61 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index c86e42b67908..61a857bca971 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -69,14 +69,32 @@ rix_to_ndx(struct minstrel_sta_info *mi, int rix) return i; } +/* return current EMWA throughput */ +int minstrel_get_tp_avg(struct minstrel_rate *mr) +{ + int usecs; + + usecs = mr->perfect_tx_time; + if (!usecs) + usecs = 1000000; + + /* reset thr. below 10% success */ + if (mr->stats.prob_ewma < MINSTREL_FRAC(10, 100)) + return 0; + else + return MINSTREL_TRUNC(mr->stats.prob_ewma * (100000 / usecs)); +} + /* find & sort topmost throughput rates */ static inline void minstrel_sort_best_tp_rates(struct minstrel_sta_info *mi, int i, u8 *tp_list) { int j = MAX_THR_RATES; - while (j > 0 && mi->r[i].stats.cur_tp > mi->r[tp_list[j - 1]].stats.cur_tp) + while (j > 0 && (minstrel_get_tp_avg(&mi->r[i]) > + minstrel_get_tp_avg(&mi->r[tp_list[j - 1]]))) j--; + if (j < MAX_THR_RATES - 1) memmove(&tp_list[j + 1], &tp_list[j], MAX_THR_RATES - (j + 1)); if (j < MAX_THR_RATES) @@ -158,8 +176,7 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) { u8 tmp_tp_rate[MAX_THR_RATES]; u8 tmp_prob_rate = 0; - u32 usecs; - int i; + int i, tmp_cur_tp, tmp_prob_tp; for (i = 0; i < MAX_THR_RATES; i++) tmp_tp_rate[i] = 0; @@ -168,19 +185,9 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) struct minstrel_rate *mr = &mi->r[i]; struct minstrel_rate_stats *mrs = &mi->r[i].stats; - usecs = mr->perfect_tx_time; - if (!usecs) - usecs = 1000000; - /* Update success probabilities per rate */ minstrel_calc_rate_stats(mrs); - /* Update throughput per rate, reset thr. below 10% success */ - if (mrs->prob_ewma < MINSTREL_FRAC(10, 100)) - mrs->cur_tp = 0; - else - mrs->cur_tp = mrs->prob_ewma * (1000000 / usecs); - /* Sample less often below the 10% chance of success. * Sample less often above the 95% chance of success. */ if (mrs->prob_ewma > MINSTREL_FRAC(95, 100) || @@ -205,7 +212,9 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) * (2) if all success probabilities < 95%, the rate with * highest success probability is chosen as max_prob_rate */ if (mrs->prob_ewma >= MINSTREL_FRAC(95, 100)) { - if (mrs->cur_tp >= mi->r[tmp_prob_rate].stats.cur_tp) + tmp_cur_tp = minstrel_get_tp_avg(mr); + tmp_prob_tp = minstrel_get_tp_avg(&mi->r[tmp_prob_rate]); + if (tmp_cur_tp >= tmp_prob_tp) tmp_prob_rate = i; } else { if (mrs->prob_ewma >= mi->r[tmp_prob_rate].stats.prob_ewma) @@ -676,11 +685,15 @@ static u32 minstrel_get_expected_throughput(void *priv_sta) { struct minstrel_sta_info *mi = priv_sta; int idx = mi->max_tp_rate[0]; + int tmp_cur_tp; /* convert pkt per sec in kbps (1200 is the average pkt size used for * computing cur_tp */ - return MINSTREL_TRUNC(mi->r[idx].stats.cur_tp) * 1200 * 8 / 1024; + tmp_cur_tp = minstrel_get_tp_avg(&mi->r[idx]); + tmp_cur_tp = tmp_cur_tp * 1200 * 8 / 1024; + + return tmp_cur_tp; } const struct rate_control_ops mac80211_minstrel = { diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h index 58f287028437..490df3b1f3ec 100644 --- a/net/mac80211/rc80211_minstrel.h +++ b/net/mac80211/rc80211_minstrel.h @@ -38,9 +38,6 @@ struct minstrel_rate_stats { /* total attempts/success counters */ u64 att_hist, succ_hist; - /* current EWMA of rate throughput */ - unsigned int cur_tp; - /* statistis of packet delivery probability * cur_prob - current prob within last update intervall * prob_ewma - exponential weighted moving average of prob */ @@ -137,6 +134,7 @@ void minstrel_remove_sta_debugfs(void *priv, void *priv_sta); /* Recalculate success probabilities and counters for a given rate using EWMA */ void minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs); +int minstrel_get_tp_avg(struct minstrel_rate *mr); /* debugfs */ int minstrel_stats_open(struct inode *inode, struct file *file); diff --git a/net/mac80211/rc80211_minstrel_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c index 8a0b73eda99a..188e9858713f 100644 --- a/net/mac80211/rc80211_minstrel_debugfs.c +++ b/net/mac80211/rc80211_minstrel_debugfs.c @@ -75,7 +75,7 @@ minstrel_stats_open(struct inode *inode, struct file *file) { struct minstrel_sta_info *mi = inode->i_private; struct minstrel_debugfs_info *ms; - unsigned int i, tp, prob, eprob; + unsigned int i, tp_avg, prob, eprob; char *p; ms = kmalloc(2048, GFP_KERNEL); @@ -105,13 +105,13 @@ minstrel_stats_open(struct inode *inode, struct file *file) p += sprintf(p, "%3u ", i); p += sprintf(p, "%6u ", mr->perfect_tx_time); - tp = MINSTREL_TRUNC(mrs->cur_tp / 10); + tp_avg = minstrel_get_tp_avg(mr); prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); p += sprintf(p, " %4u.%1u %3u.%1u %3u.%1u %3u" " %3u %-3u %9llu %-9llu\n", - tp / 10, tp % 10, + tp_avg / 10, tp_avg % 10, eprob / 10, eprob % 10, prob / 10, prob % 10, mrs->retry_count, @@ -144,7 +144,7 @@ minstrel_stats_csv_open(struct inode *inode, struct file *file) { struct minstrel_sta_info *mi = inode->i_private; struct minstrel_debugfs_info *ms; - unsigned int i, tp, prob, eprob; + unsigned int i, tp_avg, prob, eprob; char *p; ms = kmalloc(2048, GFP_KERNEL); @@ -169,13 +169,13 @@ minstrel_stats_csv_open(struct inode *inode, struct file *file) p += sprintf(p, "%u,", i); p += sprintf(p, "%u,",mr->perfect_tx_time); - tp = MINSTREL_TRUNC(mrs->cur_tp / 10); + tp_avg = minstrel_get_tp_avg(mr); prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u,%u,%u," "%llu,%llu,%d,%d\n", - tp / 10, tp % 10, + tp_avg / 10, tp_avg % 10, eprob / 10, eprob % 10, prob / 10, prob % 10, mrs->retry_count, diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index aca8b642661c..7202e34ec04c 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -314,11 +314,11 @@ minstrel_get_ratestats(struct minstrel_ht_sta *mi, int index) } /* - * Calculate throughput based on the average A-MPDU length, taking into account - * the expected number of retransmissions and their expected length + * Return current throughput based on the average A-MPDU length, taking into + * account the expected number of retransmissions and their expected length */ -static void -minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate) +int +minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate) { struct minstrel_rate_stats *mrs; unsigned int nsecs = 0; @@ -328,10 +328,8 @@ minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate) tmp_prob_ewma = mrs->prob_ewma; /* do not account throughput if sucess prob is below 10% */ - if (mrs->prob_ewma < MINSTREL_FRAC(10, 100)) { - mrs->cur_tp = 0; - return; - } + if (mrs->prob_ewma < MINSTREL_FRAC(10, 100)) + return 0; /* * For the throughput calculation, limit the probability value to 90% to @@ -346,7 +344,7 @@ minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate) nsecs += minstrel_mcs_groups[group].duration[rate]; /* prob is scaled - see MINSTREL_FRAC above */ - mrs->cur_tp = MINSTREL_TRUNC(1000000 * ((tmp_prob_ewma * 1000) / nsecs)); + return MINSTREL_TRUNC(100000 * ((tmp_prob_ewma * 1000) / nsecs)); } /* @@ -360,22 +358,22 @@ static void minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u16 index, u16 *tp_list) { - int cur_group, cur_idx, cur_thr, cur_prob; - int tmp_group, tmp_idx, tmp_thr, tmp_prob; + int cur_group, cur_idx, cur_tp_avg, cur_prob; + int tmp_group, tmp_idx, tmp_tp_avg, tmp_prob; int j = MAX_THR_RATES; cur_group = index / MCS_GROUP_RATES; cur_idx = index % MCS_GROUP_RATES; - cur_thr = mi->groups[cur_group].rates[cur_idx].cur_tp; + cur_tp_avg = minstrel_ht_get_tp_avg(mi, cur_group, cur_idx); cur_prob = mi->groups[cur_group].rates[cur_idx].prob_ewma; do { tmp_group = tp_list[j - 1] / MCS_GROUP_RATES; tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES; - tmp_thr = mi->groups[tmp_group].rates[tmp_idx].cur_tp; + tmp_tp_avg = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx); tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma; - if (cur_thr < tmp_thr || - (cur_thr == tmp_thr && cur_prob <= tmp_prob)) + if (cur_tp_avg < tmp_tp_avg || + (cur_tp_avg == tmp_tp_avg && cur_prob <= tmp_prob)) break; j--; } while (j > 0); @@ -396,14 +394,19 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index) { struct minstrel_mcs_group_data *mg; struct minstrel_rate_stats *mrs; - int tmp_group, tmp_idx, tmp_tp, tmp_prob, max_tp_group; + int tmp_group, tmp_idx, tmp_tp_avg, tmp_prob; + int max_tp_group, cur_tp_avg, cur_group, cur_idx; + int max_group_prob_rate_group, max_group_prob_rate_idx; + int max_group_prob_rate_tp_avg; + cur_group = index / MCS_GROUP_RATES; + cur_idx = index % MCS_GROUP_RATES; mg = &mi->groups[index / MCS_GROUP_RATES]; mrs = &mg->rates[index % MCS_GROUP_RATES]; tmp_group = mi->max_prob_rate / MCS_GROUP_RATES; tmp_idx = mi->max_prob_rate % MCS_GROUP_RATES; - tmp_tp = mi->groups[tmp_group].rates[tmp_idx].cur_tp; + tmp_tp_avg = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx); tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma; /* if max_tp_rate[0] is from MCS_GROUP max_prob_rate get selected from @@ -414,9 +417,18 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index) return; if (mrs->prob_ewma > MINSTREL_FRAC(75, 100)) { - if (mrs->cur_tp > tmp_tp) + cur_tp_avg = minstrel_ht_get_tp_avg(mi, cur_group, cur_idx); + if (cur_tp_avg > tmp_tp_avg) mi->max_prob_rate = index; - if (mrs->cur_tp > mg->rates[mg->max_group_prob_rate].cur_tp) + + max_group_prob_rate_group = mg->max_group_prob_rate / + MCS_GROUP_RATES; + max_group_prob_rate_idx = mg->max_group_prob_rate % + MCS_GROUP_RATES; + max_group_prob_rate_tp_avg = minstrel_ht_get_tp_avg(mi, + max_group_prob_rate_group, + max_group_prob_rate_idx); + if (cur_tp_avg > max_group_prob_rate_tp_avg) mg->max_group_prob_rate = index; } else { if (mrs->prob_ewma > tmp_prob) @@ -443,11 +455,11 @@ minstrel_ht_assign_best_tp_rates(struct minstrel_ht_sta *mi, tmp_group = tmp_cck_tp_rate[0] / MCS_GROUP_RATES; tmp_idx = tmp_cck_tp_rate[0] % MCS_GROUP_RATES; - tmp_cck_tp = mi->groups[tmp_group].rates[tmp_idx].cur_tp; + tmp_cck_tp = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx); tmp_group = tmp_mcs_tp_rate[0] / MCS_GROUP_RATES; tmp_idx = tmp_mcs_tp_rate[0] % MCS_GROUP_RATES; - tmp_mcs_tp = mi->groups[tmp_group].rates[tmp_idx].cur_tp; + tmp_mcs_tp = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx); if (tmp_cck_tp > tmp_mcs_tp) { for(i = 0; i < MAX_THR_RATES; i++) { @@ -466,8 +478,7 @@ static inline void minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi) { struct minstrel_mcs_group_data *mg; - struct minstrel_rate_stats *mrs; - int tmp_max_streams, group; + int tmp_max_streams, group, tmp_idx; int tmp_tp = 0; tmp_max_streams = minstrel_mcs_groups[mi->max_tp_rate[0] / @@ -476,11 +487,14 @@ minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi) mg = &mi->groups[group]; if (!mg->supported || group == MINSTREL_CCK_GROUP) continue; - mrs = minstrel_get_ratestats(mi, mg->max_group_prob_rate); - if (tmp_tp < mrs->cur_tp && + + tmp_idx = mg->max_group_prob_rate % MCS_GROUP_RATES; + + if (tmp_tp < minstrel_ht_get_tp_avg(mi, group, tmp_idx) && (minstrel_mcs_groups[group].streams < tmp_max_streams)) { mi->max_prob_rate = mg->max_group_prob_rate; - tmp_tp = mrs->cur_tp; + tmp_tp = minstrel_ht_get_tp_avg(mi, group, + tmp_idx); } } } @@ -541,9 +555,8 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) mrs = &mg->rates[i]; mrs->retry_updated = false; minstrel_calc_rate_stats(mrs); - minstrel_ht_calc_tp(mi, group, i); - if (!mrs->cur_tp) + if (minstrel_ht_get_tp_avg(mi, group, i) == 0) continue; /* Find max throughput rate set */ @@ -1302,7 +1315,7 @@ static u32 minstrel_ht_get_expected_throughput(void *priv_sta) { struct minstrel_ht_sta_priv *msp = priv_sta; struct minstrel_ht_sta *mi = &msp->ht; - int i, j; + int i, j, tp_avg; if (!msp->is_ht) return mac80211_minstrel.get_expected_throughput(priv_sta); @@ -1310,8 +1323,10 @@ static u32 minstrel_ht_get_expected_throughput(void *priv_sta) i = mi->max_tp_rate[0] / MCS_GROUP_RATES; j = mi->max_tp_rate[0] % MCS_GROUP_RATES; - /* convert cur_tp from pkt per second in kbps */ - return mi->groups[i].rates[j].cur_tp * AVG_PKT_SIZE * 8 / 1024; + /* convert tp_avg from pkt per second in kbps */ + tp_avg = minstrel_ht_get_tp_avg(mi, i, j) * AVG_PKT_SIZE * 8 / 1024; + + return tp_avg; } static const struct rate_control_ops mac80211_minstrel_ht = { diff --git a/net/mac80211/rc80211_minstrel_ht.h b/net/mac80211/rc80211_minstrel_ht.h index fa21a82b72c4..68dce4f8641c 100644 --- a/net/mac80211/rc80211_minstrel_ht.h +++ b/net/mac80211/rc80211_minstrel_ht.h @@ -121,5 +121,6 @@ struct minstrel_ht_sta_priv { void minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir); void minstrel_ht_remove_sta_debugfs(void *priv, void *priv_sta); +int minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate); #endif diff --git a/net/mac80211/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c index 84183dee9ef2..721cf74ea9a6 100644 --- a/net/mac80211/rc80211_minstrel_ht_debugfs.c +++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c @@ -19,7 +19,7 @@ static char * minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) { const struct mcs_group *mg; - unsigned int j, tp, prob, eprob, tx_time; + unsigned int j, tp_avg, prob, eprob, tx_time; char htmode = '2'; char gimode = 'L'; u32 gflags; @@ -81,13 +81,13 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) tx_time = DIV_ROUND_CLOSEST(mg->duration[j], 1000); p += sprintf(p, "%6u ", tx_time); - tp = mrs->cur_tp / 10; + tp_avg = minstrel_ht_get_tp_avg(mi, i, j); prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); p += sprintf(p, "%4u.%1u %3u.%1u %3u.%1u " "%3u %3u %-3u %9llu %-9llu\n", - tp / 10, tp % 10, + tp_avg / 10, tp_avg % 10, eprob / 10, eprob % 10, prob / 10, prob % 10, mrs->retry_count, @@ -163,7 +163,7 @@ static char * minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p) { const struct mcs_group *mg; - unsigned int j, tp, prob, eprob, tx_time; + unsigned int j, tp_avg, prob, eprob, tx_time; char htmode = '2'; char gimode = 'L'; u32 gflags; @@ -222,12 +222,12 @@ minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p) tx_time = DIV_ROUND_CLOSEST(mg->duration[j], 1000); p += sprintf(p, "%u,", tx_time); - tp = mrs->cur_tp / 10; + tp_avg = minstrel_ht_get_tp_avg(mi, i, j); prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u,%u,%u,%llu,%llu,", - tp / 10, tp % 10, + tp_avg / 10, tp_avg % 10, eprob / 10, eprob % 10, prob / 10, prob % 10, mrs->retry_count, -- cgit v1.2.3 From 50e55a8ea76fb593403cc09767b6371c17364ba8 Mon Sep 17 00:00:00 2001 From: Thomas Huehn Date: Tue, 24 Mar 2015 21:09:41 +0100 Subject: mac80211: add max lossless throughput per rate This patch adds the new statistic "maximum possible lossless throughput" to Minstrels and Minstrel-HTs rc_stats (in debugfs). This enables comprehensive comparison between current per-rate throughput and max. achievable per-rate throughput. Signed-off-by: Thomas Huehn Acked-by: Felix Fietkau Signed-off-by: Johannes Berg --- net/mac80211/rc80211_minstrel.c | 27 +++++++--- net/mac80211/rc80211_minstrel.h | 2 +- net/mac80211/rc80211_minstrel_debugfs.c | 22 ++++---- net/mac80211/rc80211_minstrel_ht.c | 84 ++++++++++++++++-------------- net/mac80211/rc80211_minstrel_ht.h | 3 +- net/mac80211/rc80211_minstrel_ht_debugfs.c | 24 +++++---- 6 files changed, 93 insertions(+), 69 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index 61a857bca971..c4a3477812ee 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -70,7 +70,7 @@ rix_to_ndx(struct minstrel_sta_info *mi, int rix) } /* return current EMWA throughput */ -int minstrel_get_tp_avg(struct minstrel_rate *mr) +int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_ewma) { int usecs; @@ -81,8 +81,11 @@ int minstrel_get_tp_avg(struct minstrel_rate *mr) /* reset thr. below 10% success */ if (mr->stats.prob_ewma < MINSTREL_FRAC(10, 100)) return 0; + + if (prob_ewma > MINSTREL_FRAC(90, 100)) + return MINSTREL_TRUNC(100000 * (MINSTREL_FRAC(90, 100) / usecs)); else - return MINSTREL_TRUNC(mr->stats.prob_ewma * (100000 / usecs)); + return MINSTREL_TRUNC(100000 * (prob_ewma / usecs)); } /* find & sort topmost throughput rates */ @@ -90,10 +93,14 @@ static inline void minstrel_sort_best_tp_rates(struct minstrel_sta_info *mi, int i, u8 *tp_list) { int j = MAX_THR_RATES; + struct minstrel_rate_stats *tmp_mrs = &mi->r[j - 1].stats; + struct minstrel_rate_stats *cur_mrs = &mi->r[i].stats; - while (j > 0 && (minstrel_get_tp_avg(&mi->r[i]) > - minstrel_get_tp_avg(&mi->r[tp_list[j - 1]]))) + while (j > 0 && (minstrel_get_tp_avg(&mi->r[i], cur_mrs->prob_ewma) > + minstrel_get_tp_avg(&mi->r[tp_list[j - 1]], tmp_mrs->prob_ewma))) { j--; + tmp_mrs = &mi->r[tp_list[j - 1]].stats; + } if (j < MAX_THR_RATES - 1) memmove(&tp_list[j + 1], &tp_list[j], MAX_THR_RATES - (j + 1)); @@ -184,6 +191,7 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) for (i = 0; i < mi->n_rates; i++) { struct minstrel_rate *mr = &mi->r[i]; struct minstrel_rate_stats *mrs = &mi->r[i].stats; + struct minstrel_rate_stats *tmp_mrs = &mi->r[tmp_prob_rate].stats; /* Update success probabilities per rate */ minstrel_calc_rate_stats(mrs); @@ -212,12 +220,13 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) * (2) if all success probabilities < 95%, the rate with * highest success probability is chosen as max_prob_rate */ if (mrs->prob_ewma >= MINSTREL_FRAC(95, 100)) { - tmp_cur_tp = minstrel_get_tp_avg(mr); - tmp_prob_tp = minstrel_get_tp_avg(&mi->r[tmp_prob_rate]); + tmp_cur_tp = minstrel_get_tp_avg(mr, mrs->prob_ewma); + tmp_prob_tp = minstrel_get_tp_avg(&mi->r[tmp_prob_rate], + tmp_mrs->prob_ewma); if (tmp_cur_tp >= tmp_prob_tp) tmp_prob_rate = i; } else { - if (mrs->prob_ewma >= mi->r[tmp_prob_rate].stats.prob_ewma) + if (mrs->prob_ewma >= tmp_mrs->prob_ewma) tmp_prob_rate = i; } } @@ -684,13 +693,15 @@ minstrel_free(void *priv) static u32 minstrel_get_expected_throughput(void *priv_sta) { struct minstrel_sta_info *mi = priv_sta; + struct minstrel_rate_stats *tmp_mrs; int idx = mi->max_tp_rate[0]; int tmp_cur_tp; /* convert pkt per sec in kbps (1200 is the average pkt size used for * computing cur_tp */ - tmp_cur_tp = minstrel_get_tp_avg(&mi->r[idx]); + tmp_mrs = &mi->r[idx].stats; + tmp_cur_tp = minstrel_get_tp_avg(&mi->r[idx], tmp_mrs->prob_ewma); tmp_cur_tp = tmp_cur_tp * 1200 * 8 / 1024; return tmp_cur_tp; diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h index 490df3b1f3ec..0083036161af 100644 --- a/net/mac80211/rc80211_minstrel.h +++ b/net/mac80211/rc80211_minstrel.h @@ -134,7 +134,7 @@ void minstrel_remove_sta_debugfs(void *priv, void *priv_sta); /* Recalculate success probabilities and counters for a given rate using EWMA */ void minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs); -int minstrel_get_tp_avg(struct minstrel_rate *mr); +int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_ewma); /* debugfs */ int minstrel_stats_open(struct inode *inode, struct file *file); diff --git a/net/mac80211/rc80211_minstrel_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c index 188e9858713f..617b81f72226 100644 --- a/net/mac80211/rc80211_minstrel_debugfs.c +++ b/net/mac80211/rc80211_minstrel_debugfs.c @@ -75,7 +75,7 @@ minstrel_stats_open(struct inode *inode, struct file *file) { struct minstrel_sta_info *mi = inode->i_private; struct minstrel_debugfs_info *ms; - unsigned int i, tp_avg, prob, eprob; + unsigned int i, tp_max, tp_avg, prob, eprob; char *p; ms = kmalloc(2048, GFP_KERNEL); @@ -85,9 +85,9 @@ minstrel_stats_open(struct inode *inode, struct file *file) file->private_data = ms; p = ms->buf; p += sprintf(p, "\n"); - p += sprintf(p, "best _______rate_____ __statistics__ " + p += sprintf(p, "best __________rate_________ __statistics__ " "________last_______ ______sum-of________\n"); - p += sprintf(p, "rate [name idx airtime] [ ø(tp) ø(prob)] " + p += sprintf(p, "rate [name idx airtime max_tp] [ ø(tp) ø(prob)] " "[prob.|retry|suc|att] [#success | #attempts]\n"); for (i = 0; i < mi->n_rates; i++) { @@ -103,14 +103,16 @@ minstrel_stats_open(struct inode *inode, struct file *file) p += sprintf(p, " %3u%s ", mr->bitrate / 2, (mr->bitrate & 1 ? ".5" : " ")); p += sprintf(p, "%3u ", i); - p += sprintf(p, "%6u ", mr->perfect_tx_time); + p += sprintf(p, "%6u ", mr->perfect_tx_time); - tp_avg = minstrel_get_tp_avg(mr); + tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100)); + tp_avg = minstrel_get_tp_avg(mr, mrs->prob_ewma); prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); - p += sprintf(p, " %4u.%1u %3u.%1u %3u.%1u %3u" + p += sprintf(p, "%4u.%1u %4u.%1u %3u.%1u %3u.%1u %3u" " %3u %-3u %9llu %-9llu\n", + tp_max / 10, tp_max % 10, tp_avg / 10, tp_avg % 10, eprob / 10, eprob % 10, prob / 10, prob % 10, @@ -144,7 +146,7 @@ minstrel_stats_csv_open(struct inode *inode, struct file *file) { struct minstrel_sta_info *mi = inode->i_private; struct minstrel_debugfs_info *ms; - unsigned int i, tp_avg, prob, eprob; + unsigned int i, tp_max, tp_avg, prob, eprob; char *p; ms = kmalloc(2048, GFP_KERNEL); @@ -169,12 +171,14 @@ minstrel_stats_csv_open(struct inode *inode, struct file *file) p += sprintf(p, "%u,", i); p += sprintf(p, "%u,",mr->perfect_tx_time); - tp_avg = minstrel_get_tp_avg(mr); + tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100)); + tp_avg = minstrel_get_tp_avg(mr, mrs->prob_ewma); prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); - p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u,%u,%u," + p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u,%u,%u," "%llu,%llu,%d,%d\n", + tp_max / 10, tp_max % 10, tp_avg / 10, tp_avg % 10, eprob / 10, eprob % 10, prob / 10, prob % 10, diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index 7202e34ec04c..7430a1df2ab1 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -318,33 +318,30 @@ minstrel_get_ratestats(struct minstrel_ht_sta *mi, int index) * account the expected number of retransmissions and their expected length */ int -minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate) +minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate, + int prob_ewma) { - struct minstrel_rate_stats *mrs; unsigned int nsecs = 0; - unsigned int tmp_prob_ewma; - - mrs = &mi->groups[group].rates[rate]; - tmp_prob_ewma = mrs->prob_ewma; /* do not account throughput if sucess prob is below 10% */ - if (mrs->prob_ewma < MINSTREL_FRAC(10, 100)) + if (prob_ewma < MINSTREL_FRAC(10, 100)) return 0; - /* - * For the throughput calculation, limit the probability value to 90% to - * account for collision related packet error rate fluctuation - */ - if (mrs->prob_ewma > MINSTREL_FRAC(90, 100)) - tmp_prob_ewma = MINSTREL_FRAC(90, 100); - if (group != MINSTREL_CCK_GROUP) nsecs = 1000 * mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len); nsecs += minstrel_mcs_groups[group].duration[rate]; - /* prob is scaled - see MINSTREL_FRAC above */ - return MINSTREL_TRUNC(100000 * ((tmp_prob_ewma * 1000) / nsecs)); + /* + * For the throughput calculation, limit the probability value to 90% to + * account for collision related packet error rate fluctuation + * (prob is scaled - see MINSTREL_FRAC above) + */ + if (prob_ewma > MINSTREL_FRAC(90, 100)) + return MINSTREL_TRUNC(100000 * ((MINSTREL_FRAC(90, 100) * 1000) + / nsecs)); + else + return MINSTREL_TRUNC(100000 * ((prob_ewma * 1000) / nsecs)); } /* @@ -364,14 +361,15 @@ minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u16 index, cur_group = index / MCS_GROUP_RATES; cur_idx = index % MCS_GROUP_RATES; - cur_tp_avg = minstrel_ht_get_tp_avg(mi, cur_group, cur_idx); cur_prob = mi->groups[cur_group].rates[cur_idx].prob_ewma; + cur_tp_avg = minstrel_ht_get_tp_avg(mi, cur_group, cur_idx, cur_prob); do { tmp_group = tp_list[j - 1] / MCS_GROUP_RATES; tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES; - tmp_tp_avg = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx); tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma; + tmp_tp_avg = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, + tmp_prob); if (cur_tp_avg < tmp_tp_avg || (cur_tp_avg == tmp_tp_avg && cur_prob <= tmp_prob)) break; @@ -396,8 +394,8 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index) struct minstrel_rate_stats *mrs; int tmp_group, tmp_idx, tmp_tp_avg, tmp_prob; int max_tp_group, cur_tp_avg, cur_group, cur_idx; - int max_group_prob_rate_group, max_group_prob_rate_idx; - int max_group_prob_rate_tp_avg; + int max_gpr_group, max_gpr_idx; + int max_gpr_tp_avg, max_gpr_prob; cur_group = index / MCS_GROUP_RATES; cur_idx = index % MCS_GROUP_RATES; @@ -406,8 +404,8 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index) tmp_group = mi->max_prob_rate / MCS_GROUP_RATES; tmp_idx = mi->max_prob_rate % MCS_GROUP_RATES; - tmp_tp_avg = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx); tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma; + tmp_tp_avg = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob); /* if max_tp_rate[0] is from MCS_GROUP max_prob_rate get selected from * MCS_GROUP as well as CCK_GROUP rates do not allow aggregation */ @@ -417,18 +415,18 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index) return; if (mrs->prob_ewma > MINSTREL_FRAC(75, 100)) { - cur_tp_avg = minstrel_ht_get_tp_avg(mi, cur_group, cur_idx); + cur_tp_avg = minstrel_ht_get_tp_avg(mi, cur_group, cur_idx, + mrs->prob_ewma); if (cur_tp_avg > tmp_tp_avg) mi->max_prob_rate = index; - max_group_prob_rate_group = mg->max_group_prob_rate / - MCS_GROUP_RATES; - max_group_prob_rate_idx = mg->max_group_prob_rate % - MCS_GROUP_RATES; - max_group_prob_rate_tp_avg = minstrel_ht_get_tp_avg(mi, - max_group_prob_rate_group, - max_group_prob_rate_idx); - if (cur_tp_avg > max_group_prob_rate_tp_avg) + max_gpr_group = mg->max_group_prob_rate / MCS_GROUP_RATES; + max_gpr_idx = mg->max_group_prob_rate % MCS_GROUP_RATES; + max_gpr_prob = mi->groups[max_gpr_group].rates[max_gpr_idx].prob_ewma; + max_gpr_tp_avg = minstrel_ht_get_tp_avg(mi, max_gpr_group, + max_gpr_idx, + max_gpr_prob); + if (cur_tp_avg > max_gpr_tp_avg) mg->max_group_prob_rate = index; } else { if (mrs->prob_ewma > tmp_prob) @@ -450,16 +448,18 @@ minstrel_ht_assign_best_tp_rates(struct minstrel_ht_sta *mi, u16 tmp_mcs_tp_rate[MAX_THR_RATES], u16 tmp_cck_tp_rate[MAX_THR_RATES]) { - unsigned int tmp_group, tmp_idx, tmp_cck_tp, tmp_mcs_tp; + unsigned int tmp_group, tmp_idx, tmp_cck_tp, tmp_mcs_tp, tmp_prob; int i; tmp_group = tmp_cck_tp_rate[0] / MCS_GROUP_RATES; tmp_idx = tmp_cck_tp_rate[0] % MCS_GROUP_RATES; - tmp_cck_tp = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx); + tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma; + tmp_cck_tp = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob); tmp_group = tmp_mcs_tp_rate[0] / MCS_GROUP_RATES; tmp_idx = tmp_mcs_tp_rate[0] % MCS_GROUP_RATES; - tmp_mcs_tp = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx); + tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma; + tmp_mcs_tp = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob); if (tmp_cck_tp > tmp_mcs_tp) { for(i = 0; i < MAX_THR_RATES; i++) { @@ -478,7 +478,7 @@ static inline void minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi) { struct minstrel_mcs_group_data *mg; - int tmp_max_streams, group, tmp_idx; + int tmp_max_streams, group, tmp_idx, tmp_prob; int tmp_tp = 0; tmp_max_streams = minstrel_mcs_groups[mi->max_tp_rate[0] / @@ -489,12 +489,14 @@ minstrel_ht_prob_rate_reduce_streams(struct minstrel_ht_sta *mi) continue; tmp_idx = mg->max_group_prob_rate % MCS_GROUP_RATES; + tmp_prob = mi->groups[group].rates[tmp_idx].prob_ewma; - if (tmp_tp < minstrel_ht_get_tp_avg(mi, group, tmp_idx) && + if (tmp_tp < minstrel_ht_get_tp_avg(mi, group, tmp_idx, tmp_prob) && (minstrel_mcs_groups[group].streams < tmp_max_streams)) { mi->max_prob_rate = mg->max_group_prob_rate; tmp_tp = minstrel_ht_get_tp_avg(mi, group, - tmp_idx); + tmp_idx, + tmp_prob); } } } @@ -513,7 +515,7 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) { struct minstrel_mcs_group_data *mg; struct minstrel_rate_stats *mrs; - int group, i, j; + int group, i, j, cur_prob; u16 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES]; u16 tmp_cck_tp_rate[MAX_THR_RATES], index; @@ -555,8 +557,9 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) mrs = &mg->rates[i]; mrs->retry_updated = false; minstrel_calc_rate_stats(mrs); + cur_prob = mrs->prob_ewma; - if (minstrel_ht_get_tp_avg(mi, group, i) == 0) + if (minstrel_ht_get_tp_avg(mi, group, i, cur_prob) == 0) continue; /* Find max throughput rate set */ @@ -1315,16 +1318,17 @@ static u32 minstrel_ht_get_expected_throughput(void *priv_sta) { struct minstrel_ht_sta_priv *msp = priv_sta; struct minstrel_ht_sta *mi = &msp->ht; - int i, j, tp_avg; + int i, j, prob, tp_avg; if (!msp->is_ht) return mac80211_minstrel.get_expected_throughput(priv_sta); i = mi->max_tp_rate[0] / MCS_GROUP_RATES; j = mi->max_tp_rate[0] % MCS_GROUP_RATES; + prob = mi->groups[i].rates[j].prob_ewma; /* convert tp_avg from pkt per second in kbps */ - tp_avg = minstrel_ht_get_tp_avg(mi, i, j) * AVG_PKT_SIZE * 8 / 1024; + tp_avg = minstrel_ht_get_tp_avg(mi, i, j, prob) * AVG_PKT_SIZE * 8 / 1024; return tp_avg; } diff --git a/net/mac80211/rc80211_minstrel_ht.h b/net/mac80211/rc80211_minstrel_ht.h index 68dce4f8641c..e8b52a94d24b 100644 --- a/net/mac80211/rc80211_minstrel_ht.h +++ b/net/mac80211/rc80211_minstrel_ht.h @@ -121,6 +121,7 @@ struct minstrel_ht_sta_priv { void minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir); void minstrel_ht_remove_sta_debugfs(void *priv, void *priv_sta); -int minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate); +int minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate, + int prob_ewma); #endif diff --git a/net/mac80211/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c index 721cf74ea9a6..135ed39790c0 100644 --- a/net/mac80211/rc80211_minstrel_ht_debugfs.c +++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c @@ -19,7 +19,7 @@ static char * minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) { const struct mcs_group *mg; - unsigned int j, tp_avg, prob, eprob, tx_time; + unsigned int j, tp_max, tp_avg, prob, eprob, tx_time; char htmode = '2'; char gimode = 'L'; u32 gflags; @@ -79,14 +79,16 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) /* tx_time[rate(i)] in usec */ tx_time = DIV_ROUND_CLOSEST(mg->duration[j], 1000); - p += sprintf(p, "%6u ", tx_time); + p += sprintf(p, "%6u ", tx_time); - tp_avg = minstrel_ht_get_tp_avg(mi, i, j); + tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100)); + tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_ewma); prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); - p += sprintf(p, "%4u.%1u %3u.%1u %3u.%1u " + p += sprintf(p, "%4u.%1u %4u.%1u %3u.%1u %3u.%1u " "%3u %3u %-3u %9llu %-9llu\n", + tp_max / 10, tp_max % 10, tp_avg / 10, tp_avg % 10, eprob / 10, eprob % 10, prob / 10, prob % 10, @@ -125,11 +127,11 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file) p = ms->buf; p += sprintf(p, "\n"); - p += sprintf(p, " best ________rate______ " + p += sprintf(p, " best ____________rate__________ " "__statistics__ ________last_______ " "______sum-of________\n"); - p += sprintf(p, "mode guard # rate [name idx airtime] [ ø(tp) " - "ø(prob)] [prob.|retry|suc|att] [#success | " + p += sprintf(p, "mode guard # rate [name idx airtime max_tp] " + "[ ø(tp) ø(prob)] [prob.|retry|suc|att] [#success | " "#attempts]\n"); p = minstrel_ht_stats_dump(mi, MINSTREL_CCK_GROUP, p); @@ -163,7 +165,7 @@ static char * minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p) { const struct mcs_group *mg; - unsigned int j, tp_avg, prob, eprob, tx_time; + unsigned int j, tp_max, tp_avg, prob, eprob, tx_time; char htmode = '2'; char gimode = 'L'; u32 gflags; @@ -222,11 +224,13 @@ minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p) tx_time = DIV_ROUND_CLOSEST(mg->duration[j], 1000); p += sprintf(p, "%u,", tx_time); - tp_avg = minstrel_ht_get_tp_avg(mi, i, j); + tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100)); + tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_ewma); prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); - p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u,%u,%u,%llu,%llu,", + p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u,%u,%u,%llu,%llu,", + tp_max / 10, tp_max % 10, tp_avg / 10, tp_avg % 10, eprob / 10, eprob % 10, prob / 10, prob % 10, -- cgit v1.2.3 From ade6d4a2ec57d258bc181a155288c267dd8cf094 Mon Sep 17 00:00:00 2001 From: Thomas Huehn Date: Tue, 24 Mar 2015 21:09:42 +0100 Subject: mac80211: reduce calculation costs of EWMA This patch reduces the calculation costs of the EWMA macro from "2x multiplication and 1 addition" down to "1x multiplication and 2x additions". This slightly improves performance depending on the CPU architecture. Signed-off-by: Thomas Huehn Acked-by: Felix Fietkau Signed-off-by: Johannes Berg --- net/mac80211/rc80211_minstrel.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'net/mac80211') diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h index 0083036161af..9c85a610b57b 100644 --- a/net/mac80211/rc80211_minstrel.h +++ b/net/mac80211/rc80211_minstrel.h @@ -27,7 +27,12 @@ static inline int minstrel_ewma(int old, int new, int weight) { - return (new * (EWMA_DIV - weight) + old * weight) / EWMA_DIV; + int diff, incr; + + diff = new - old; + incr = (EWMA_DIV - weight) * diff / EWMA_DIV; + + return old + incr; } struct minstrel_rate_stats { -- cgit v1.2.3 From 5f919abc76fc3de1e5965ea03c925f7563c7fc15 Mon Sep 17 00:00:00 2001 From: Thomas Huehn Date: Tue, 24 Mar 2015 21:09:43 +0100 Subject: mac80211: add standard deviation to Minstrel stats This patch adds the statistical descriptor "standard deviation" to better describe the current properties of Minstrel and Minstrel-HTs success probability distribution. The standard deviation (SD) is calculated as exponential weighted moving standard deviation (EWMSD) and its current value is added as new column in all rc_stats (in debugfs). Signed-off-by: Thomas Huehn Acked-by: Felix Fietkau Signed-off-by: Johannes Berg --- net/mac80211/rc80211_minstrel.c | 19 ++++++++++++++----- net/mac80211/rc80211_minstrel.h | 22 +++++++++++++++++++++- net/mac80211/rc80211_minstrel_debugfs.c | 19 ++++++++++++------- net/mac80211/rc80211_minstrel_ht_debugfs.c | 14 +++++++++----- 4 files changed, 56 insertions(+), 18 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index c4a3477812ee..247552a7f6c2 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -153,7 +153,7 @@ minstrel_update_rates(struct minstrel_priv *mp, struct minstrel_sta_info *mi) } /* -* Recalculate success probabilities and counters for a given rate using EWMA +* Recalculate statistics and counters of a given rate */ void minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs) @@ -161,11 +161,20 @@ minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs) if (unlikely(mrs->attempts > 0)) { mrs->sample_skipped = 0; mrs->cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts); - if (unlikely(!mrs->att_hist)) + if (unlikely(!mrs->att_hist)) { mrs->prob_ewma = mrs->cur_prob; - else + } else { + /* update exponential weighted moving variance */ + mrs->prob_ewmsd = minstrel_ewmsd(mrs->prob_ewmsd, + mrs->cur_prob, + mrs->prob_ewma, + EWMA_LEVEL); + + /*update exponential weighted moving avarage */ mrs->prob_ewma = minstrel_ewma(mrs->prob_ewma, - mrs->cur_prob, EWMA_LEVEL); + mrs->cur_prob, + EWMA_LEVEL); + } mrs->att_hist += mrs->attempts; mrs->succ_hist += mrs->success; } else { @@ -193,7 +202,7 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi) struct minstrel_rate_stats *mrs = &mi->r[i].stats; struct minstrel_rate_stats *tmp_mrs = &mi->r[tmp_prob_rate].stats; - /* Update success probabilities per rate */ + /* Update statistics of success probability per rate */ minstrel_calc_rate_stats(mrs); /* Sample less often below the 10% chance of success. diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h index 9c85a610b57b..c230bbe93262 100644 --- a/net/mac80211/rc80211_minstrel.h +++ b/net/mac80211/rc80211_minstrel.h @@ -35,6 +35,24 @@ minstrel_ewma(int old, int new, int weight) return old + incr; } +/* + * Perform EWMSD (Exponentially Weighted Moving Standard Deviation) calculation + */ +static inline int +minstrel_ewmsd(int old_ewmsd, int cur_prob, int prob_ewma, int weight) +{ + int diff, incr, tmp_var; + + /* calculate exponential weighted moving variance */ + diff = MINSTREL_TRUNC((cur_prob - prob_ewma) * 1000000); + incr = (EWMA_DIV - weight) * diff / EWMA_DIV; + tmp_var = old_ewmsd * old_ewmsd; + tmp_var = weight * (tmp_var + diff * incr / 1000000) / EWMA_DIV; + + /* return standard deviation */ + return (u16) int_sqrt(tmp_var); +} + struct minstrel_rate_stats { /* current / last sampling period attempts/success counters */ u16 attempts, last_attempts; @@ -45,9 +63,11 @@ struct minstrel_rate_stats { /* statistis of packet delivery probability * cur_prob - current prob within last update intervall - * prob_ewma - exponential weighted moving average of prob */ + * prob_ewma - exponential weighted moving average of prob + * prob_ewmsd - exp. weighted moving standard deviation of prob */ unsigned int cur_prob; unsigned int prob_ewma; + u16 prob_ewmsd; /* maximum retry counts */ u8 retry_count; diff --git a/net/mac80211/rc80211_minstrel_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c index 617b81f72226..1db5f7c3318a 100644 --- a/net/mac80211/rc80211_minstrel_debugfs.c +++ b/net/mac80211/rc80211_minstrel_debugfs.c @@ -85,10 +85,12 @@ minstrel_stats_open(struct inode *inode, struct file *file) file->private_data = ms; p = ms->buf; p += sprintf(p, "\n"); - p += sprintf(p, "best __________rate_________ __statistics__ " - "________last_______ ______sum-of________\n"); - p += sprintf(p, "rate [name idx airtime max_tp] [ ø(tp) ø(prob)] " - "[prob.|retry|suc|att] [#success | #attempts]\n"); + p += sprintf(p, "best __________rate_________ ______" + "statistics______ ________last_______ " + "______sum-of________\n"); + p += sprintf(p, "rate [name idx airtime max_tp] [ ø(tp) ø(prob) " + "sd(prob)] [prob.|retry|suc|att] " + "[#success | #attempts]\n"); for (i = 0; i < mi->n_rates; i++) { struct minstrel_rate *mr = &mi->r[i]; @@ -110,11 +112,13 @@ minstrel_stats_open(struct inode *inode, struct file *file) prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); - p += sprintf(p, "%4u.%1u %4u.%1u %3u.%1u %3u.%1u %3u" - " %3u %-3u %9llu %-9llu\n", + p += sprintf(p, "%4u.%1u %4u.%1u %3u.%1u %3u.%1u" + " %3u.%1u %3u %3u %-3u " + "%9llu %-9llu\n", tp_max / 10, tp_max % 10, tp_avg / 10, tp_avg % 10, eprob / 10, eprob % 10, + mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10, prob / 10, prob % 10, mrs->retry_count, mrs->last_success, @@ -176,11 +180,12 @@ minstrel_stats_csv_open(struct inode *inode, struct file *file) prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); - p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u,%u,%u," + p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u.%u,%u,%u,%u," "%llu,%llu,%d,%d\n", tp_max / 10, tp_max % 10, tp_avg / 10, tp_avg % 10, eprob / 10, eprob % 10, + mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10, prob / 10, prob % 10, mrs->retry_count, mrs->last_success, diff --git a/net/mac80211/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c index 135ed39790c0..6822ce0f95e5 100644 --- a/net/mac80211/rc80211_minstrel_ht_debugfs.c +++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c @@ -86,11 +86,13 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); - p += sprintf(p, "%4u.%1u %4u.%1u %3u.%1u %3u.%1u " - "%3u %3u %-3u %9llu %-9llu\n", + p += sprintf(p, "%4u.%1u %4u.%1u %3u.%1u %3u.%1u" + " %3u.%1u %3u %3u %-3u " + "%9llu %-9llu\n", tp_max / 10, tp_max % 10, tp_avg / 10, tp_avg % 10, eprob / 10, eprob % 10, + mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10, prob / 10, prob % 10, mrs->retry_count, mrs->last_success, @@ -128,10 +130,10 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file) p += sprintf(p, "\n"); p += sprintf(p, " best ____________rate__________ " - "__statistics__ ________last_______ " + "______statistics______ ________last_______ " "______sum-of________\n"); p += sprintf(p, "mode guard # rate [name idx airtime max_tp] " - "[ ø(tp) ø(prob)] [prob.|retry|suc|att] [#success | " + "[ ø(tp) ø(prob) sd(prob)] [prob.|retry|suc|att] [#success | " "#attempts]\n"); p = minstrel_ht_stats_dump(mi, MINSTREL_CCK_GROUP, p); @@ -229,10 +231,12 @@ minstrel_ht_stats_csv_dump(struct minstrel_ht_sta *mi, int i, char *p) prob = MINSTREL_TRUNC(mrs->cur_prob * 1000); eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000); - p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u,%u,%u,%llu,%llu,", + p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u.%u,%u,%u," + "%u,%llu,%llu,", tp_max / 10, tp_max % 10, tp_avg / 10, tp_avg % 10, eprob / 10, eprob % 10, + mrs->prob_ewmsd / 10, mrs->prob_ewmsd % 10, prob / 10, prob % 10, mrs->retry_count, mrs->last_success, -- cgit v1.2.3 From cef2fc1ce4326f7f24c3cf938b94a661fbe773e3 Mon Sep 17 00:00:00 2001 From: John Linville Date: Tue, 31 Mar 2015 10:49:14 -0400 Subject: mac80211: reduce log spam from ieee80211_handle_pwr_constr This changes a couple of messages from sdata_info to sdata_dbg. This should reduce some log spam, as reported here: https://bugzilla.redhat.com/show_bug.cgi?id=1206468 Signed-off-by: John W. Linville Signed-off-by: Johannes Berg --- net/mac80211/mlme.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 00103f36dcbf..26053bf2faa8 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -1348,15 +1348,15 @@ static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, */ if (has_80211h_pwr && (!has_cisco_pwr || pwr_level_80211h <= pwr_level_cisco)) { - sdata_info(sdata, - "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n", - pwr_level_80211h, chan_pwr, pwr_reduction_80211h, - sdata->u.mgd.bssid); + sdata_dbg(sdata, + "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n", + pwr_level_80211h, chan_pwr, pwr_reduction_80211h, + sdata->u.mgd.bssid); new_ap_level = pwr_level_80211h; } else { /* has_cisco_pwr is always true here. */ - sdata_info(sdata, - "Limiting TX power to %d dBm as advertised by %pM\n", - pwr_level_cisco, sdata->u.mgd.bssid); + sdata_dbg(sdata, + "Limiting TX power to %d dBm as advertised by %pM\n", + pwr_level_cisco, sdata->u.mgd.bssid); new_ap_level = pwr_level_cisco; } -- cgit v1.2.3 From ba8c3d6f16a1f9305c23ac1d2fd3992508c5ac03 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 27 Mar 2015 21:30:37 +0100 Subject: mac80211: add an intermediate software queue implementation This allows drivers to request per-vif and per-sta-tid queues from which they can pull frames. This makes it easier to keep the hardware queues short, and to improve fairness between clients and vifs. The task of scheduling packet transmission is left up to the driver - queueing is controlled by mac80211. Drivers can only dequeue packets by calling ieee80211_tx_dequeue. This makes it possible to add active queue management later without changing drivers using this code. This can also be used as a starting point to implement A-MSDU aggregation in a way that does not add artificially induced latency. Signed-off-by: Felix Fietkau [resolved minor context conflict, minor changes, endian annotations] Signed-off-by: Johannes Berg --- net/mac80211/agg-tx.c | 44 +++++++++++++++++ net/mac80211/driver-ops.h | 12 +++++ net/mac80211/ieee80211_i.h | 21 +++++++++ net/mac80211/iface.c | 23 ++++++++- net/mac80211/main.c | 3 ++ net/mac80211/rx.c | 13 +++++ net/mac80211/sta_info.c | 83 ++++++++++++++++++++++++++++---- net/mac80211/sta_info.h | 2 + net/mac80211/trace.h | 31 ++++++++++++ net/mac80211/tx.c | 115 +++++++++++++++++++++++++++++++++++++++++---- net/mac80211/util.c | 22 +++++++++ 11 files changed, 350 insertions(+), 19 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 20522492d8cc..cce9d425c718 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -188,6 +188,43 @@ ieee80211_wake_queue_agg(struct ieee80211_sub_if_data *sdata, int tid) __release(agg_queue); } +static void +ieee80211_agg_stop_txq(struct sta_info *sta, int tid) +{ + struct ieee80211_txq *txq = sta->sta.txq[tid]; + struct txq_info *txqi; + + if (!txq) + return; + + txqi = to_txq_info(txq); + + /* Lock here to protect against further seqno updates on dequeue */ + spin_lock_bh(&txqi->queue.lock); + set_bit(IEEE80211_TXQ_STOP, &txqi->flags); + spin_unlock_bh(&txqi->queue.lock); +} + +static void +ieee80211_agg_start_txq(struct sta_info *sta, int tid, bool enable) +{ + struct ieee80211_txq *txq = sta->sta.txq[tid]; + struct txq_info *txqi; + + if (!txq) + return; + + txqi = to_txq_info(txq); + + if (enable) + set_bit(IEEE80211_TXQ_AMPDU, &txqi->flags); + else + clear_bit(IEEE80211_TXQ_AMPDU, &txqi->flags); + + clear_bit(IEEE80211_TXQ_STOP, &txqi->flags); + drv_wake_tx_queue(sta->sdata->local, txqi); +} + /* * splice packets from the STA's pending to the local pending, * requires a call to ieee80211_agg_splice_finish later @@ -247,6 +284,7 @@ static void ieee80211_remove_tid_tx(struct sta_info *sta, int tid) ieee80211_assign_tid_tx(sta, tid, NULL); ieee80211_agg_splice_finish(sta->sdata, tid); + ieee80211_agg_start_txq(sta, tid, false); kfree_rcu(tid_tx, rcu_head); } @@ -418,6 +456,8 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) */ clear_bit(HT_AGG_STATE_WANT_START, &tid_tx->state); + ieee80211_agg_stop_txq(sta, tid); + /* * Make sure no packets are being processed. This ensures that * we have a valid starting sequence number and that in-flight @@ -440,6 +480,8 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid) ieee80211_agg_splice_finish(sdata, tid); spin_unlock_bh(&sta->lock); + ieee80211_agg_start_txq(sta, tid, false); + kfree_rcu(tid_tx, rcu_head); return; } @@ -669,6 +711,8 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local, ieee80211_agg_splice_finish(sta->sdata, tid); spin_unlock_bh(&sta->lock); + + ieee80211_agg_start_txq(sta, tid, true); } void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid) diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 0a39d3db951a..26e1ca8a474a 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -1367,4 +1367,16 @@ drv_tdls_recv_channel_switch(struct ieee80211_local *local, trace_drv_return_void(local); } +static inline void drv_wake_tx_queue(struct ieee80211_local *local, + struct txq_info *txq) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->txq.vif); + + if (!check_sdata_in_driver(sdata)) + return; + + trace_drv_wake_tx_queue(local, sdata, txq); + local->ops->wake_tx_queue(&local->hw, &txq->txq); +} + #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 3c1512b0442c..04b32f3e0395 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -811,6 +811,19 @@ struct mac80211_qos_map { struct rcu_head rcu_head; }; +enum txq_info_flags { + IEEE80211_TXQ_STOP, + IEEE80211_TXQ_AMPDU, +}; + +struct txq_info { + struct sk_buff_head queue; + unsigned long flags; + + /* keep last! */ + struct ieee80211_txq txq; +}; + struct ieee80211_sub_if_data { struct list_head list; @@ -853,6 +866,7 @@ struct ieee80211_sub_if_data { bool control_port_no_encrypt; int encrypt_headroom; + atomic_t txqs_len[IEEE80211_NUM_ACS]; struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS]; struct mac80211_qos_map __rcu *qos_map; @@ -1450,6 +1464,10 @@ static inline struct ieee80211_local *hw_to_local( return container_of(hw, struct ieee80211_local, hw); } +static inline struct txq_info *to_txq_info(struct ieee80211_txq *txq) +{ + return container_of(txq, struct txq_info, txq); +} static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr) { @@ -1906,6 +1924,9 @@ static inline bool ieee80211_can_run_worker(struct ieee80211_local *local) return true; } +void ieee80211_init_tx_queue(struct ieee80211_sub_if_data *sdata, + struct sta_info *sta, + struct txq_info *txq, int tid); void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, u16 transaction, u16 auth_alg, u16 status, const u8 *extra, size_t extra_len, const u8 *bssid, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index a0cd97fd0c49..b4ac596a7cb7 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -969,6 +969,13 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, } spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); + if (sdata->vif.txq) { + struct txq_info *txqi = to_txq_info(sdata->vif.txq); + + ieee80211_purge_tx_queue(&local->hw, &txqi->queue); + atomic_set(&sdata->txqs_len[txqi->txq.ac], 0); + } + if (local->open_count == 0) ieee80211_clear_tx_pending(local); @@ -1654,6 +1661,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, { struct net_device *ndev = NULL; struct ieee80211_sub_if_data *sdata = NULL; + struct txq_info *txqi; int ret, i; int txqs = 1; @@ -1673,10 +1681,18 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, ieee80211_assign_perm_addr(local, wdev->address, type); memcpy(sdata->vif.addr, wdev->address, ETH_ALEN); } else { + int size = ALIGN(sizeof(*sdata) + local->hw.vif_data_size, + sizeof(void *)); + int txq_size = 0; + + if (local->ops->wake_tx_queue) + txq_size += sizeof(struct txq_info) + + local->hw.txq_data_size; + if (local->hw.queues >= IEEE80211_NUM_ACS) txqs = IEEE80211_NUM_ACS; - ndev = alloc_netdev_mqs(sizeof(*sdata) + local->hw.vif_data_size, + ndev = alloc_netdev_mqs(size + txq_size, name, name_assign_type, ieee80211_if_setup, txqs, 1); if (!ndev) @@ -1711,6 +1727,11 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, memcpy(sdata->vif.addr, ndev->dev_addr, ETH_ALEN); memcpy(sdata->name, ndev->name, IFNAMSIZ); + if (txq_size) { + txqi = netdev_priv(ndev) + size; + ieee80211_init_tx_queue(sdata, NULL, txqi, 0); + } + sdata->dev = ndev; } diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 51e0332a4589..df3051d96aff 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -1039,6 +1039,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) local->dynamic_ps_forced_timeout = -1; + if (!local->hw.txq_ac_max_pending) + local->hw.txq_ac_max_pending = 64; + result = ieee80211_wep_init(local); if (result < 0) wiphy_debug(local->hw.wiphy, "Failed to initialize wep: %d\n", diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 5b60bcf00ec3..bc59c8a20a39 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1184,6 +1184,7 @@ static void sta_ps_start(struct sta_info *sta) struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; struct ps_data *ps; + int tid; if (sta->sdata->vif.type == NL80211_IFTYPE_AP || sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) @@ -1197,6 +1198,18 @@ static void sta_ps_start(struct sta_info *sta) drv_sta_notify(local, sdata, STA_NOTIFY_SLEEP, &sta->sta); ps_dbg(sdata, "STA %pM aid %d enters power save mode\n", sta->sta.addr, sta->sta.aid); + + if (!sta->sta.txq[0]) + return; + + for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) { + struct txq_info *txqi = to_txq_info(sta->sta.txq[tid]); + + if (!skb_queue_len(&txqi->queue)) + set_bit(tid, &sta->txq_buffered_tids); + else + clear_bit(tid, &sta->txq_buffered_tids); + } } static void sta_ps_end(struct sta_info *sta) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 81cc499fa4a9..12971b71d0fa 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -106,6 +106,16 @@ static void __cleanup_single_sta(struct sta_info *sta) atomic_dec(&ps->num_sta_ps); } + if (sta->sta.txq[0]) { + for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { + struct txq_info *txqi = to_txq_info(sta->sta.txq[i]); + int n = skb_queue_len(&txqi->queue); + + ieee80211_purge_tx_queue(&local->hw, &txqi->queue); + atomic_sub(n, &sdata->txqs_len[txqi->txq.ac]); + } + } + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]); ieee80211_purge_tx_queue(&local->hw, &sta->ps_tx_buf[ac]); @@ -218,6 +228,8 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta) sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr); + if (sta->sta.txq[0]) + kfree(to_txq_info(sta->sta.txq[0])); kfree(rcu_dereference_raw(sta->sta.rates)); kfree(sta); } @@ -268,11 +280,12 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, const u8 *addr, gfp_t gfp) { struct ieee80211_local *local = sdata->local; + struct ieee80211_hw *hw = &local->hw; struct sta_info *sta; struct timespec uptime; int i; - sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp); + sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp); if (!sta) return NULL; @@ -304,11 +317,25 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, for (i = 0; i < ARRAY_SIZE(sta->chain_signal_avg); i++) ewma_init(&sta->chain_signal_avg[i], 1024, 8); - if (sta_prepare_rate_control(local, sta, gfp)) { - kfree(sta); - return NULL; + if (local->ops->wake_tx_queue) { + void *txq_data; + int size = sizeof(struct txq_info) + + ALIGN(hw->txq_data_size, sizeof(void *)); + + txq_data = kcalloc(ARRAY_SIZE(sta->sta.txq), size, gfp); + if (!txq_data) + goto free; + + for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { + struct txq_info *txq = txq_data + i * size; + + ieee80211_init_tx_queue(sdata, sta, txq, i); + } } + if (sta_prepare_rate_control(local, sta, gfp)) + goto free_txq; + for (i = 0; i < IEEE80211_NUM_TIDS; i++) { /* * timer_to_tid must be initialized with identity mapping @@ -329,7 +356,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, if (sdata->vif.type == NL80211_IFTYPE_AP || sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { struct ieee80211_supported_band *sband = - local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)]; + hw->wiphy->bands[ieee80211_get_sdata_band(sdata)]; u8 smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> IEEE80211_HT_CAP_SM_PS_SHIFT; /* @@ -354,6 +381,13 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr); return sta; + +free_txq: + if (sta->sta.txq[0]) + kfree(to_txq_info(sta->sta.txq[0])); +free: + kfree(sta); + return NULL; } static int sta_info_insert_check(struct sta_info *sta) @@ -623,6 +657,8 @@ static void __sta_info_recalc_tim(struct sta_info *sta, bool ignore_pending) indicate_tim |= sta->driver_buffered_tids & tids; + indicate_tim |= + sta->txq_buffered_tids & tids; } done: @@ -1072,7 +1108,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) struct ieee80211_sub_if_data *sdata = sta->sdata; struct ieee80211_local *local = sdata->local; struct sk_buff_head pending; - int filtered = 0, buffered = 0, ac; + int filtered = 0, buffered = 0, ac, i; unsigned long flags; struct ps_data *ps; @@ -1091,10 +1127,22 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) BUILD_BUG_ON(BITS_TO_LONGS(IEEE80211_NUM_TIDS) > 1); sta->driver_buffered_tids = 0; + sta->txq_buffered_tids = 0; if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS)) drv_sta_notify(local, sdata, STA_NOTIFY_AWAKE, &sta->sta); + if (sta->sta.txq[0]) { + for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { + struct txq_info *txqi = to_txq_info(sta->sta.txq[i]); + + if (!skb_queue_len(&txqi->queue)) + continue; + + drv_wake_tx_queue(local, txqi); + } + } + skb_queue_head_init(&pending); /* sync with ieee80211_tx_h_unicast_ps_buf */ @@ -1276,8 +1324,10 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, /* if we already have frames from software, then we can't also * release from hardware queues */ - if (skb_queue_empty(&frames)) + if (skb_queue_empty(&frames)) { driver_release_tids |= sta->driver_buffered_tids & tids; + driver_release_tids |= sta->txq_buffered_tids & tids; + } if (driver_release_tids) { /* If the driver has data on more than one TID then @@ -1448,6 +1498,9 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, sta_info_recalc_tim(sta); } else { + unsigned long tids = sta->txq_buffered_tids & driver_release_tids; + int tid; + /* * We need to release a frame that is buffered somewhere in the * driver ... it'll have to handle that. @@ -1467,8 +1520,22 @@ ieee80211_sta_ps_deliver_response(struct sta_info *sta, * that the TID(s) became empty before returning here from the * release function. * Either way, however, when the driver tells us that the TID(s) - * became empty we'll do the TIM recalculation. + * became empty or we find that a txq became empty, we'll do the + * TIM recalculation. */ + + if (!sta->sta.txq[0]) + return; + + for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) { + struct txq_info *txqi = to_txq_info(sta->sta.txq[tid]); + + if (!(tids & BIT(tid)) || skb_queue_len(&txqi->queue)) + continue; + + sta_info_recalc_tim(sta); + break; + } } } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 97f25b9e52be..691d8a1f94a5 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -275,6 +275,7 @@ struct sta_ampdu_mlme { * entered power saving state, these are also delivered to * the station when it leaves powersave or polls for frames * @driver_buffered_tids: bitmap of TIDs the driver has data buffered on + * @txq_buffered_tids: bitmap of TIDs that mac80211 has txq data buffered on * @rx_packets: Number of MSDUs received from this STA * @rx_bytes: Number of bytes received from this STA * @last_rx: time (in jiffies) when last frame was received from this STA @@ -369,6 +370,7 @@ struct sta_info { struct sk_buff_head ps_tx_buf[IEEE80211_NUM_ACS]; struct sk_buff_head tx_filtered[IEEE80211_NUM_ACS]; unsigned long driver_buffered_tids; + unsigned long txq_buffered_tids; /* Updated from RX path only, no locking requirements */ unsigned long rx_packets; diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index e9e462b349e5..790bd45081c4 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -2312,6 +2312,37 @@ TRACE_EVENT(drv_tdls_recv_channel_switch, ) ); +TRACE_EVENT(drv_wake_tx_queue, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct txq_info *txq), + + TP_ARGS(local, sdata, txq), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + STA_ENTRY + __field(u8, ac) + __field(u8, tid) + ), + + TP_fast_assign( + struct ieee80211_sta *sta = txq->txq.sta; + + LOCAL_ASSIGN; + VIF_ASSIGN; + STA_ASSIGN; + __entry->ac = txq->txq.ac; + __entry->tid = txq->txq.tid; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " ac:%d tid:%d", + LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->ac, __entry->tid + ) +); + #ifdef CONFIG_MAC80211_MESSAGE_TRACING #undef TRACE_SYSTEM #define TRACE_SYSTEM mac80211_msg diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 9f7fb4eec37b..667111ee6a20 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -767,12 +767,22 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) return TX_CONTINUE; } +static __le16 ieee80211_tx_next_seq(struct sta_info *sta, int tid) +{ + u16 *seq = &sta->tid_seq[tid]; + __le16 ret = cpu_to_le16(*seq); + + /* Increase the sequence number. */ + *seq = (*seq + 0x10) & IEEE80211_SCTL_SEQ; + + return ret; +} + static ieee80211_tx_result debug_noinline ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data; - u16 *seq; u8 *qc; int tid; @@ -823,13 +833,10 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx) qc = ieee80211_get_qos_ctl(hdr); tid = *qc & IEEE80211_QOS_CTL_TID_MASK; - seq = &tx->sta->tid_seq[tid]; tx->sta->tx_msdu[tid]++; - hdr->seq_ctrl = cpu_to_le16(*seq); - - /* Increase the sequence number. */ - *seq = (*seq + 0x10) & IEEE80211_SCTL_SEQ; + if (!tx->sta->sta.txq[0]) + hdr->seq_ctrl = ieee80211_tx_next_seq(tx->sta, tid); return TX_CONTINUE; } @@ -1070,7 +1077,7 @@ static bool ieee80211_tx_prep_agg(struct ieee80211_tx_data *tx, * nothing -- this aggregation session is being started * but that might still fail with the driver */ - } else { + } else if (!tx->sta->sta.txq[tid]) { spin_lock(&tx->sta->lock); /* * Need to re-check now, because we may get here @@ -1211,13 +1218,102 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, return TX_CONTINUE; } +static void ieee80211_drv_tx(struct ieee80211_local *local, + struct ieee80211_vif *vif, + struct ieee80211_sta *pubsta, + struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_tx_control control = { + .sta = pubsta, + }; + struct ieee80211_txq *txq = NULL; + struct txq_info *txqi; + u8 ac; + + if (info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE) + goto tx_normal; + + if (!ieee80211_is_data(hdr->frame_control)) + goto tx_normal; + + if (pubsta) { + u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; + + txq = pubsta->txq[tid]; + } else if (vif) { + txq = vif->txq; + } + + if (!txq) + goto tx_normal; + + ac = txq->ac; + txqi = to_txq_info(txq); + atomic_inc(&sdata->txqs_len[ac]); + if (atomic_read(&sdata->txqs_len[ac]) >= local->hw.txq_ac_max_pending) + netif_stop_subqueue(sdata->dev, ac); + + skb_queue_tail(&txqi->queue, skb); + drv_wake_tx_queue(local, txqi); + + return; + +tx_normal: + drv_tx(local, &control, skb); +} + +struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw, + struct ieee80211_txq *txq) +{ + struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_sub_if_data *sdata = vif_to_sdata(txq->vif); + struct txq_info *txqi = container_of(txq, struct txq_info, txq); + struct ieee80211_hdr *hdr; + struct sk_buff *skb = NULL; + u8 ac = txq->ac; + + spin_lock_bh(&txqi->queue.lock); + + if (test_bit(IEEE80211_TXQ_STOP, &txqi->flags)) + goto out; + + skb = __skb_dequeue(&txqi->queue); + if (!skb) + goto out; + + atomic_dec(&sdata->txqs_len[ac]); + if (__netif_subqueue_stopped(sdata->dev, ac)) + ieee80211_propagate_queue_wake(local, sdata->vif.hw_queue[ac]); + + hdr = (struct ieee80211_hdr *)skb->data; + if (txq->sta && ieee80211_is_data_qos(hdr->frame_control)) { + struct sta_info *sta = container_of(txq->sta, struct sta_info, + sta); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + hdr->seq_ctrl = ieee80211_tx_next_seq(sta, txq->tid); + if (test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags)) + info->flags |= IEEE80211_TX_CTL_AMPDU; + else + info->flags &= ~IEEE80211_TX_CTL_AMPDU; + } + +out: + spin_unlock_bh(&txqi->queue.lock); + + return skb; +} +EXPORT_SYMBOL(ieee80211_tx_dequeue); + static bool ieee80211_tx_frags(struct ieee80211_local *local, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct sk_buff_head *skbs, bool txpending) { - struct ieee80211_tx_control control; struct sk_buff *skb, *tmp; unsigned long flags; @@ -1275,10 +1371,9 @@ static bool ieee80211_tx_frags(struct ieee80211_local *local, spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); info->control.vif = vif; - control.sta = sta; __skb_unlink(skb, skbs); - drv_tx(local, &control, skb); + ieee80211_drv_tx(local, vif, sta, skb); } return true; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index d1742a7d9ea4..482b85c19a36 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -308,6 +308,11 @@ void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue) for (ac = 0; ac < n_acs; ac++) { int ac_queue = sdata->vif.hw_queue[ac]; + if (local->ops->wake_tx_queue && + (atomic_read(&sdata->txqs_len[ac]) > + local->hw.txq_ac_max_pending)) + continue; + if (ac_queue == queue || (sdata->vif.cab_queue == queue && local->queue_stop_reasons[ac_queue] == 0 && @@ -3352,3 +3357,20 @@ u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo) return buf; } + +void ieee80211_init_tx_queue(struct ieee80211_sub_if_data *sdata, + struct sta_info *sta, + struct txq_info *txqi, int tid) +{ + skb_queue_head_init(&txqi->queue); + txqi->txq.vif = &sdata->vif; + + if (sta) { + txqi->txq.sta = &sta->sta; + sta->sta.txq[tid] = &txqi->txq; + txqi->txq.ac = ieee802_1d_to_ac[tid & 7]; + } else { + sdata->vif.txq = &txqi->txq; + txqi->txq.ac = IEEE80211_AC_BE; + } +} -- cgit v1.2.3 From 29464ccc78f1e8f77b8c37ddbe95af46d13c2ec2 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 31 Mar 2015 15:36:22 +0200 Subject: cfg80211: move IE split utilities here from mac80211 As the next patch will require the IE splitting utility functions in cfg80211, move them there from mac80211. Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 4 ---- net/mac80211/util.c | 40 ---------------------------------------- 2 files changed, 44 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 04b32f3e0395..ab46ab4a7249 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1965,10 +1965,6 @@ int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata, void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata); void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata); -size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen, - const u8 *ids, int n_ids, - const u8 *after_ric, int n_after_ric, - size_t offset); size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset); u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, u16 cap); diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 482b85c19a36..79412f16b61d 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -2194,46 +2194,6 @@ void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata) mutex_unlock(&local->chanctx_mtx); } -static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id) -{ - int i; - - for (i = 0; i < n_ids; i++) - if (ids[i] == id) - return true; - return false; -} - -size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen, - const u8 *ids, int n_ids, - const u8 *after_ric, int n_after_ric, - size_t offset) -{ - size_t pos = offset; - - while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) { - if (ies[pos] == WLAN_EID_RIC_DATA && n_after_ric) { - pos += 2 + ies[pos + 1]; - - while (pos < ielen && - !ieee80211_id_in_list(after_ric, n_after_ric, - ies[pos])) - pos += 2 + ies[pos + 1]; - } else { - pos += 2 + ies[pos + 1]; - } - } - - return pos; -} - -size_t ieee80211_ie_split(const u8 *ies, size_t ielen, - const u8 *ids, int n_ids, size_t offset) -{ - return ieee80211_ie_split_ric(ies, ielen, ids, n_ids, NULL, 0, offset); -} -EXPORT_SYMBOL(ieee80211_ie_split); - size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset) { size_t pos = offset; -- cgit v1.2.3