Initial commit
This commit is contained in:
@@ -0,0 +1,705 @@
|
||||
--- a/net/mac80211/Makefile
|
||||
+++ b/net/mac80211/Makefile
|
||||
@@ -7,7 +7,6 @@ mac80211-y := \
|
||||
driver-ops.o \
|
||||
sta_info.o \
|
||||
wep.o \
|
||||
- aead_api.o \
|
||||
wpa.o \
|
||||
scan.o offchannel.o \
|
||||
ht.o agg-tx.o agg-rx.o \
|
||||
@@ -18,8 +17,8 @@ mac80211-y := \
|
||||
rate.o \
|
||||
michael.o \
|
||||
tkip.o \
|
||||
+ aes_ccm.o \
|
||||
aes_cmac.o \
|
||||
- aes_gmac.o \
|
||||
fils_aead.o \
|
||||
cfg.o \
|
||||
ethtool.o \
|
||||
--- a/net/mac80211/aead_api.c
|
||||
+++ /dev/null
|
||||
@@ -1,115 +0,0 @@
|
||||
-/*
|
||||
- * Copyright 2003-2004, Instant802 Networks, Inc.
|
||||
- * Copyright 2005-2006, Devicescape Software, Inc.
|
||||
- * Copyright 2014-2015, Qualcomm Atheros, Inc.
|
||||
- *
|
||||
- * Rewrite: Copyright (C) 2013 Linaro Ltd <ard.biesheuvel@linaro.org>
|
||||
- *
|
||||
- * This program is free software; you can redistribute it and/or modify
|
||||
- * it under the terms of the GNU General Public License version 2 as
|
||||
- * published by the Free Software Foundation.
|
||||
- */
|
||||
-
|
||||
-#include <linux/kernel.h>
|
||||
-#include <linux/types.h>
|
||||
-#include <linux/err.h>
|
||||
-#include <linux/scatterlist.h>
|
||||
-#include <crypto/aead.h>
|
||||
-
|
||||
-#include "aead_api.h"
|
||||
-
|
||||
-int aead_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, size_t aad_len,
|
||||
- u8 *data, size_t data_len, u8 *mic)
|
||||
-{
|
||||
- size_t mic_len = crypto_aead_authsize(tfm);
|
||||
- struct scatterlist sg[3];
|
||||
- struct aead_request *aead_req;
|
||||
- int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm);
|
||||
- u8 *__aad;
|
||||
-
|
||||
- aead_req = kzalloc(reqsize + aad_len, GFP_ATOMIC);
|
||||
- if (!aead_req)
|
||||
- return -ENOMEM;
|
||||
-
|
||||
- __aad = (u8 *)aead_req + reqsize;
|
||||
- memcpy(__aad, aad, aad_len);
|
||||
-
|
||||
- sg_init_table(sg, 3);
|
||||
- sg_set_buf(&sg[0], __aad, aad_len);
|
||||
- sg_set_buf(&sg[1], data, data_len);
|
||||
- sg_set_buf(&sg[2], mic, mic_len);
|
||||
-
|
||||
- aead_request_set_tfm(aead_req, tfm);
|
||||
- aead_request_set_crypt(aead_req, sg, sg, data_len, b_0);
|
||||
- aead_request_set_ad(aead_req, sg[0].length);
|
||||
-
|
||||
- crypto_aead_encrypt(aead_req);
|
||||
- kzfree(aead_req);
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-int aead_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad, size_t aad_len,
|
||||
- u8 *data, size_t data_len, u8 *mic)
|
||||
-{
|
||||
- size_t mic_len = crypto_aead_authsize(tfm);
|
||||
- struct scatterlist sg[3];
|
||||
- struct aead_request *aead_req;
|
||||
- int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm);
|
||||
- u8 *__aad;
|
||||
- int err;
|
||||
-
|
||||
- if (data_len == 0)
|
||||
- return -EINVAL;
|
||||
-
|
||||
- aead_req = kzalloc(reqsize + aad_len, GFP_ATOMIC);
|
||||
- if (!aead_req)
|
||||
- return -ENOMEM;
|
||||
-
|
||||
- __aad = (u8 *)aead_req + reqsize;
|
||||
- memcpy(__aad, aad, aad_len);
|
||||
-
|
||||
- sg_init_table(sg, 3);
|
||||
- sg_set_buf(&sg[0], __aad, aad_len);
|
||||
- sg_set_buf(&sg[1], data, data_len);
|
||||
- sg_set_buf(&sg[2], mic, mic_len);
|
||||
-
|
||||
- aead_request_set_tfm(aead_req, tfm);
|
||||
- aead_request_set_crypt(aead_req, sg, sg, data_len + mic_len, b_0);
|
||||
- aead_request_set_ad(aead_req, sg[0].length);
|
||||
-
|
||||
- err = crypto_aead_decrypt(aead_req);
|
||||
- kzfree(aead_req);
|
||||
-
|
||||
- return err;
|
||||
-}
|
||||
-
|
||||
-struct crypto_aead *
|
||||
-aead_key_setup_encrypt(const char *alg, const u8 key[],
|
||||
- size_t key_len, size_t mic_len)
|
||||
-{
|
||||
- struct crypto_aead *tfm;
|
||||
- int err;
|
||||
-
|
||||
- tfm = crypto_alloc_aead(alg, 0, CRYPTO_ALG_ASYNC);
|
||||
- if (IS_ERR(tfm))
|
||||
- return tfm;
|
||||
-
|
||||
- err = crypto_aead_setkey(tfm, key, key_len);
|
||||
- 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);
|
||||
-}
|
||||
-
|
||||
-void aead_key_free(struct crypto_aead *tfm)
|
||||
-{
|
||||
- crypto_free_aead(tfm);
|
||||
-}
|
||||
--- a/net/mac80211/aead_api.h
|
||||
+++ /dev/null
|
||||
@@ -1,27 +0,0 @@
|
||||
-/*
|
||||
- * This program is free software; you can redistribute it and/or modify
|
||||
- * it under the terms of the GNU General Public License version 2 as
|
||||
- * published by the Free Software Foundation.
|
||||
- */
|
||||
-
|
||||
-#ifndef _AEAD_API_H
|
||||
-#define _AEAD_API_H
|
||||
-
|
||||
-#include <crypto/aead.h>
|
||||
-#include <linux/crypto.h>
|
||||
-
|
||||
-struct crypto_aead *
|
||||
-aead_key_setup_encrypt(const char *alg, const u8 key[],
|
||||
- size_t key_len, size_t mic_len);
|
||||
-
|
||||
-int aead_encrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
|
||||
- size_t aad_len, u8 *data,
|
||||
- size_t data_len, u8 *mic);
|
||||
-
|
||||
-int aead_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
|
||||
- size_t aad_len, u8 *data,
|
||||
- size_t data_len, u8 *mic);
|
||||
-
|
||||
-void aead_key_free(struct crypto_aead *tfm);
|
||||
-
|
||||
-#endif /* _AEAD_API_H */
|
||||
--- a/net/mac80211/aes_ccm.h
|
||||
+++ b/net/mac80211/aes_ccm.h
|
||||
@@ -10,39 +10,17 @@
|
||||
#ifndef AES_CCM_H
|
||||
#define AES_CCM_H
|
||||
|
||||
-#include "aead_api.h"
|
||||
+#include <linux/crypto.h>
|
||||
|
||||
-#define CCM_AAD_LEN 32
|
||||
-
|
||||
-static inline struct crypto_aead *
|
||||
-ieee80211_aes_key_setup_encrypt(const u8 key[], size_t key_len, size_t mic_len)
|
||||
-{
|
||||
- return aead_key_setup_encrypt("ccm(aes)", key, key_len, mic_len);
|
||||
-}
|
||||
-
|
||||
-static inline int
|
||||
-ieee80211_aes_ccm_encrypt(struct crypto_aead *tfm,
|
||||
- u8 *b_0, u8 *aad, u8 *data,
|
||||
- size_t data_len, u8 *mic)
|
||||
-{
|
||||
- return aead_encrypt(tfm, b_0, aad + 2,
|
||||
- be16_to_cpup((__be16 *)aad),
|
||||
- data, data_len, mic);
|
||||
-}
|
||||
-
|
||||
-static inline int
|
||||
-ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm,
|
||||
- u8 *b_0, u8 *aad, u8 *data,
|
||||
- size_t data_len, u8 *mic)
|
||||
-{
|
||||
- return aead_decrypt(tfm, b_0, aad + 2,
|
||||
- be16_to_cpup((__be16 *)aad),
|
||||
- data, data_len, mic);
|
||||
-}
|
||||
-
|
||||
-static inline void ieee80211_aes_key_free(struct crypto_aead *tfm)
|
||||
-{
|
||||
- return aead_key_free(tfm);
|
||||
-}
|
||||
+struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[],
|
||||
+ size_t key_len,
|
||||
+ size_t mic_len);
|
||||
+void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad,
|
||||
+ u8 *data, size_t data_len, u8 *mic,
|
||||
+ size_t mic_len);
|
||||
+int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad,
|
||||
+ u8 *data, size_t data_len, u8 *mic,
|
||||
+ size_t mic_len);
|
||||
+void ieee80211_aes_key_free(struct crypto_cipher *tfm);
|
||||
|
||||
#endif /* AES_CCM_H */
|
||||
--- /dev/null
|
||||
+++ b/net/mac80211/aes_gcm.c
|
||||
@@ -0,0 +1,109 @@
|
||||
+/*
|
||||
+ * Copyright 2014-2015, Qualcomm Atheros, Inc.
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License version 2 as
|
||||
+ * published by the Free Software Foundation.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/types.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <crypto/aead.h>
|
||||
+
|
||||
+#include <net/mac80211.h>
|
||||
+#include "key.h"
|
||||
+#include "aes_gcm.h"
|
||||
+
|
||||
+int ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
|
||||
+ u8 *data, size_t data_len, u8 *mic)
|
||||
+{
|
||||
+ struct scatterlist sg[3];
|
||||
+ struct aead_request *aead_req;
|
||||
+ int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm);
|
||||
+ u8 *__aad;
|
||||
+
|
||||
+ aead_req = kzalloc(reqsize + GCM_AAD_LEN, GFP_ATOMIC);
|
||||
+ if (!aead_req)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ __aad = (u8 *)aead_req + reqsize;
|
||||
+ memcpy(__aad, aad, GCM_AAD_LEN);
|
||||
+
|
||||
+ sg_init_table(sg, 3);
|
||||
+ sg_set_buf(&sg[0], &__aad[2], be16_to_cpup((__be16 *)__aad));
|
||||
+ sg_set_buf(&sg[1], data, data_len);
|
||||
+ sg_set_buf(&sg[2], mic, IEEE80211_GCMP_MIC_LEN);
|
||||
+
|
||||
+ aead_request_set_tfm(aead_req, tfm);
|
||||
+ aead_request_set_crypt(aead_req, sg, sg, data_len, j_0);
|
||||
+ aead_request_set_ad(aead_req, sg[0].length);
|
||||
+
|
||||
+ crypto_aead_encrypt(aead_req);
|
||||
+ kzfree(aead_req);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+int ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
|
||||
+ u8 *data, size_t data_len, u8 *mic)
|
||||
+{
|
||||
+ struct scatterlist sg[3];
|
||||
+ struct aead_request *aead_req;
|
||||
+ int reqsize = sizeof(*aead_req) + crypto_aead_reqsize(tfm);
|
||||
+ u8 *__aad;
|
||||
+ int err;
|
||||
+
|
||||
+ if (data_len == 0)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ aead_req = kzalloc(reqsize + GCM_AAD_LEN, GFP_ATOMIC);
|
||||
+ if (!aead_req)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ __aad = (u8 *)aead_req + reqsize;
|
||||
+ memcpy(__aad, aad, GCM_AAD_LEN);
|
||||
+
|
||||
+ sg_init_table(sg, 3);
|
||||
+ sg_set_buf(&sg[0], &__aad[2], be16_to_cpup((__be16 *)__aad));
|
||||
+ sg_set_buf(&sg[1], data, data_len);
|
||||
+ sg_set_buf(&sg[2], mic, IEEE80211_GCMP_MIC_LEN);
|
||||
+
|
||||
+ aead_request_set_tfm(aead_req, tfm);
|
||||
+ aead_request_set_crypt(aead_req, sg, sg,
|
||||
+ data_len + IEEE80211_GCMP_MIC_LEN, j_0);
|
||||
+ aead_request_set_ad(aead_req, sg[0].length);
|
||||
+
|
||||
+ err = crypto_aead_decrypt(aead_req);
|
||||
+ kzfree(aead_req);
|
||||
+
|
||||
+ return err;
|
||||
+}
|
||||
+
|
||||
+struct crypto_aead *ieee80211_aes_gcm_key_setup_encrypt(const u8 key[],
|
||||
+ size_t key_len)
|
||||
+{
|
||||
+ struct crypto_aead *tfm;
|
||||
+ int err;
|
||||
+
|
||||
+ tfm = crypto_alloc_aead("gcm(aes)", 0, CRYPTO_ALG_ASYNC);
|
||||
+ if (IS_ERR(tfm))
|
||||
+ return tfm;
|
||||
+
|
||||
+ err = crypto_aead_setkey(tfm, key, key_len);
|
||||
+ 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);
|
||||
+}
|
||||
+
|
||||
+void ieee80211_aes_gcm_key_free(struct crypto_aead *tfm)
|
||||
+{
|
||||
+ crypto_free_aead(tfm);
|
||||
+}
|
||||
--- a/net/mac80211/aes_gcm.h
|
||||
+++ b/net/mac80211/aes_gcm.h
|
||||
@@ -9,38 +9,30 @@
|
||||
#ifndef AES_GCM_H
|
||||
#define AES_GCM_H
|
||||
|
||||
-#include "aead_api.h"
|
||||
+#include <linux/crypto.h>
|
||||
|
||||
-#define GCM_AAD_LEN 32
|
||||
-
|
||||
-static inline int ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm,
|
||||
- u8 *j_0, u8 *aad, u8 *data,
|
||||
- size_t data_len, u8 *mic)
|
||||
+static inline void
|
||||
+ieee80211_aes_gcm_encrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
|
||||
+ u8 *data, size_t data_len, u8 *mic)
|
||||
{
|
||||
- return aead_encrypt(tfm, j_0, aad + 2,
|
||||
- be16_to_cpup((__be16 *)aad),
|
||||
- data, data_len, mic);
|
||||
}
|
||||
|
||||
-static inline int ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm,
|
||||
- u8 *j_0, u8 *aad, u8 *data,
|
||||
- size_t data_len, u8 *mic)
|
||||
+static inline int
|
||||
+ieee80211_aes_gcm_decrypt(struct crypto_aead *tfm, u8 *j_0, u8 *aad,
|
||||
+ u8 *data, size_t data_len, u8 *mic)
|
||||
{
|
||||
- return aead_decrypt(tfm, j_0, aad + 2,
|
||||
- be16_to_cpup((__be16 *)aad),
|
||||
- data, data_len, mic);
|
||||
+ return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline struct crypto_aead *
|
||||
ieee80211_aes_gcm_key_setup_encrypt(const u8 key[], size_t key_len)
|
||||
{
|
||||
- return aead_key_setup_encrypt("gcm(aes)", key,
|
||||
- key_len, IEEE80211_GCMP_MIC_LEN);
|
||||
+ return NULL;
|
||||
}
|
||||
|
||||
-static inline void ieee80211_aes_gcm_key_free(struct crypto_aead *tfm)
|
||||
+static inline void
|
||||
+ieee80211_aes_gcm_key_free(struct crypto_aead *tfm)
|
||||
{
|
||||
- return aead_key_free(tfm);
|
||||
}
|
||||
|
||||
#endif /* AES_GCM_H */
|
||||
--- a/net/mac80211/wpa.c
|
||||
+++ b/net/mac80211/wpa.c
|
||||
@@ -314,7 +314,8 @@ ieee80211_crypto_tkip_decrypt(struct iee
|
||||
}
|
||||
|
||||
|
||||
-static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad)
|
||||
+static void ccmp_special_blocks(struct sk_buff *skb, u8 *pn, u8 *b_0, u8 *aad,
|
||||
+ u16 data_len)
|
||||
{
|
||||
__le16 mask_fc;
|
||||
int a4_included, mgmt;
|
||||
@@ -344,14 +345,8 @@ static void ccmp_special_blocks(struct s
|
||||
else
|
||||
qos_tid = 0;
|
||||
|
||||
- /* In CCM, the initial vectors (IV) used for CTR mode encryption and CBC
|
||||
- * mode authentication are not allowed to collide, yet both are derived
|
||||
- * from this vector b_0. We only set L := 1 here to indicate that the
|
||||
- * data size can be represented in (L+1) bytes. The CCM layer will take
|
||||
- * care of storing the data length in the top (L+1) bytes and setting
|
||||
- * and clearing the other bits as is required to derive the two IVs.
|
||||
- */
|
||||
- b_0[0] = 0x1;
|
||||
+ /* First block, b_0 */
|
||||
+ b_0[0] = 0x59; /* flags: Adata: 1, M: 011, L: 001 */
|
||||
|
||||
/* Nonce: Nonce Flags | A2 | PN
|
||||
* Nonce Flags: Priority (b0..b3) | Management (b4) | Reserved (b5..b7)
|
||||
@@ -359,6 +354,8 @@ static void ccmp_special_blocks(struct s
|
||||
b_0[1] = qos_tid | (mgmt << 4);
|
||||
memcpy(&b_0[2], hdr->addr2, ETH_ALEN);
|
||||
memcpy(&b_0[8], pn, IEEE80211_CCMP_PN_LEN);
|
||||
+ /* l(m) */
|
||||
+ put_unaligned_be16(data_len, &b_0[14]);
|
||||
|
||||
/* AAD (extra authenticate-only data) / masked 802.11 header
|
||||
* FC | A1 | A2 | A3 | SC | [A4] | [QC] */
|
||||
@@ -415,7 +412,7 @@ static int ccmp_encrypt_skb(struct ieee8
|
||||
u8 *pos;
|
||||
u8 pn[6];
|
||||
u64 pn64;
|
||||
- u8 aad[CCM_AAD_LEN];
|
||||
+ u8 aad[2 * AES_BLOCK_SIZE];
|
||||
u8 b_0[AES_BLOCK_SIZE];
|
||||
|
||||
if (info->control.hw_key &&
|
||||
@@ -470,9 +467,11 @@ static int ccmp_encrypt_skb(struct ieee8
|
||||
return 0;
|
||||
|
||||
pos += IEEE80211_CCMP_HDR_LEN;
|
||||
- ccmp_special_blocks(skb, pn, b_0, aad);
|
||||
- return ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len,
|
||||
- skb_put(skb, mic_len));
|
||||
+ ccmp_special_blocks(skb, pn, b_0, aad, len);
|
||||
+ ieee80211_aes_ccm_encrypt(key->u.ccmp.tfm, b_0, aad, pos, len,
|
||||
+ skb_put(skb, mic_len), mic_len);
|
||||
+
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -545,13 +544,13 @@ ieee80211_crypto_ccmp_decrypt(struct iee
|
||||
u8 aad[2 * AES_BLOCK_SIZE];
|
||||
u8 b_0[AES_BLOCK_SIZE];
|
||||
/* hardware didn't decrypt/verify MIC */
|
||||
- ccmp_special_blocks(skb, pn, b_0, aad);
|
||||
+ ccmp_special_blocks(skb, pn, b_0, aad, data_len);
|
||||
|
||||
if (ieee80211_aes_ccm_decrypt(
|
||||
key->u.ccmp.tfm, b_0, aad,
|
||||
skb->data + hdrlen + IEEE80211_CCMP_HDR_LEN,
|
||||
data_len,
|
||||
- skb->data + skb->len - mic_len))
|
||||
+ skb->data + skb->len - mic_len, mic_len))
|
||||
return RX_DROP_UNUSABLE;
|
||||
}
|
||||
|
||||
@@ -646,7 +645,7 @@ static int gcmp_encrypt_skb(struct ieee8
|
||||
u8 *pos;
|
||||
u8 pn[6];
|
||||
u64 pn64;
|
||||
- u8 aad[GCM_AAD_LEN];
|
||||
+ u8 aad[2 * AES_BLOCK_SIZE];
|
||||
u8 j_0[AES_BLOCK_SIZE];
|
||||
|
||||
if (info->control.hw_key &&
|
||||
@@ -703,8 +702,10 @@ static int gcmp_encrypt_skb(struct ieee8
|
||||
|
||||
pos += IEEE80211_GCMP_HDR_LEN;
|
||||
gcmp_special_blocks(skb, pn, j_0, aad);
|
||||
- return ieee80211_aes_gcm_encrypt(key->u.gcmp.tfm, j_0, aad, pos, len,
|
||||
- skb_put(skb, IEEE80211_GCMP_MIC_LEN));
|
||||
+ ieee80211_aes_gcm_encrypt(key->u.gcmp.tfm, j_0, aad, pos, len,
|
||||
+ skb_put(skb, IEEE80211_GCMP_MIC_LEN));
|
||||
+
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
ieee80211_tx_result
|
||||
@@ -1127,9 +1128,9 @@ ieee80211_crypto_aes_gmac_encrypt(struct
|
||||
struct ieee80211_key *key = tx->key;
|
||||
struct ieee80211_mmie_16 *mmie;
|
||||
struct ieee80211_hdr *hdr;
|
||||
- u8 aad[GMAC_AAD_LEN];
|
||||
+ u8 aad[20];
|
||||
u64 pn64;
|
||||
- u8 nonce[GMAC_NONCE_LEN];
|
||||
+ u8 nonce[12];
|
||||
|
||||
if (WARN_ON(skb_queue_len(&tx->skbs) != 1))
|
||||
return TX_DROP;
|
||||
@@ -1175,7 +1176,7 @@ ieee80211_crypto_aes_gmac_decrypt(struct
|
||||
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
|
||||
struct ieee80211_key *key = rx->key;
|
||||
struct ieee80211_mmie_16 *mmie;
|
||||
- u8 aad[GMAC_AAD_LEN], *mic, ipn[6], nonce[GMAC_NONCE_LEN];
|
||||
+ u8 aad[20], *mic, ipn[6], nonce[12];
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
||||
|
||||
if (!ieee80211_is_mgmt(hdr->frame_control))
|
||||
--- /dev/null
|
||||
+++ b/net/mac80211/aes_ccm.c
|
||||
@@ -0,0 +1,144 @@
|
||||
+/*
|
||||
+ * Copyright 2003-2004, Instant802 Networks, Inc.
|
||||
+ * Copyright 2005-2006, Devicescape Software, Inc.
|
||||
+ *
|
||||
+ * Rewrite: Copyright (C) 2013 Linaro Ltd <ard.biesheuvel@linaro.org>
|
||||
+ *
|
||||
+ * This program is free software; you can redistribute it and/or modify
|
||||
+ * it under the terms of the GNU General Public License version 2 as
|
||||
+ * published by the Free Software Foundation.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/types.h>
|
||||
+#include <linux/err.h>
|
||||
+#include <crypto/aead.h>
|
||||
+#include <crypto/aes.h>
|
||||
+
|
||||
+#include <net/mac80211.h>
|
||||
+#include "key.h"
|
||||
+#include "aes_ccm.h"
|
||||
+
|
||||
+static void aes_ccm_prepare(struct crypto_cipher *tfm, u8 *b_0, u8 *aad, u8 *s_0,
|
||||
+ u8 *a, u8 *b)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ crypto_cipher_encrypt_one(tfm, b, b_0);
|
||||
+
|
||||
+ /* Extra Authenticate-only data (always two AES blocks) */
|
||||
+ for (i = 0; i < AES_BLOCK_SIZE; i++)
|
||||
+ aad[i] ^= b[i];
|
||||
+ crypto_cipher_encrypt_one(tfm, b, aad);
|
||||
+
|
||||
+ aad += AES_BLOCK_SIZE;
|
||||
+
|
||||
+ for (i = 0; i < AES_BLOCK_SIZE; i++)
|
||||
+ aad[i] ^= b[i];
|
||||
+ crypto_cipher_encrypt_one(tfm, a, aad);
|
||||
+
|
||||
+ /* Mask out bits from auth-only-b_0 */
|
||||
+ b_0[0] &= 0x07;
|
||||
+
|
||||
+ /* S_0 is used to encrypt T (= MIC) */
|
||||
+ b_0[14] = 0;
|
||||
+ b_0[15] = 0;
|
||||
+ crypto_cipher_encrypt_one(tfm, s_0, b_0);
|
||||
+}
|
||||
+
|
||||
+
|
||||
+void ieee80211_aes_ccm_encrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad,
|
||||
+ u8 *data, size_t data_len, u8 *mic,
|
||||
+ size_t mic_len)
|
||||
+{
|
||||
+ int i, j, last_len, num_blocks;
|
||||
+ u8 b[AES_BLOCK_SIZE];
|
||||
+ u8 s_0[AES_BLOCK_SIZE];
|
||||
+ u8 e[AES_BLOCK_SIZE];
|
||||
+ u8 *pos, *cpos;
|
||||
+
|
||||
+ num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE);
|
||||
+ last_len = data_len % AES_BLOCK_SIZE;
|
||||
+ aes_ccm_prepare(tfm, b_0, aad, s_0, b, b);
|
||||
+
|
||||
+ /* Process payload blocks */
|
||||
+ pos = data;
|
||||
+ cpos = data;
|
||||
+ for (j = 1; j <= num_blocks; j++) {
|
||||
+ int blen = (j == num_blocks && last_len) ?
|
||||
+ last_len : AES_BLOCK_SIZE;
|
||||
+
|
||||
+ /* Authentication followed by encryption */
|
||||
+ for (i = 0; i < blen; i++)
|
||||
+ b[i] ^= pos[i];
|
||||
+ crypto_cipher_encrypt_one(tfm, b, b);
|
||||
+
|
||||
+ b_0[14] = (j >> 8) & 0xff;
|
||||
+ b_0[15] = j & 0xff;
|
||||
+ crypto_cipher_encrypt_one(tfm, e, b_0);
|
||||
+ for (i = 0; i < blen; i++)
|
||||
+ *cpos++ = *pos++ ^ e[i];
|
||||
+ }
|
||||
+
|
||||
+ for (i = 0; i < mic_len; i++)
|
||||
+ mic[i] = b[i] ^ s_0[i];
|
||||
+}
|
||||
+
|
||||
+int ieee80211_aes_ccm_decrypt(struct crypto_cipher *tfm, u8 *b_0, u8 *aad,
|
||||
+ u8 *data, size_t data_len, u8 *mic,
|
||||
+ size_t mic_len)
|
||||
+{
|
||||
+ int i, j, last_len, num_blocks;
|
||||
+ u8 *pos, *cpos;
|
||||
+ u8 a[AES_BLOCK_SIZE];
|
||||
+ u8 b[AES_BLOCK_SIZE];
|
||||
+ u8 s_0[AES_BLOCK_SIZE];
|
||||
+
|
||||
+ num_blocks = DIV_ROUND_UP(data_len, AES_BLOCK_SIZE);
|
||||
+ last_len = data_len % AES_BLOCK_SIZE;
|
||||
+ aes_ccm_prepare(tfm, b_0, aad, s_0, a, b);
|
||||
+
|
||||
+ /* Process payload blocks */
|
||||
+ cpos = data;
|
||||
+ pos = data;
|
||||
+ for (j = 1; j <= num_blocks; j++) {
|
||||
+ int blen = (j == num_blocks && last_len) ?
|
||||
+ last_len : AES_BLOCK_SIZE;
|
||||
+
|
||||
+ /* Decryption followed by authentication */
|
||||
+ b_0[14] = (j >> 8) & 0xff;
|
||||
+ b_0[15] = j & 0xff;
|
||||
+ crypto_cipher_encrypt_one(tfm, b, b_0);
|
||||
+ for (i = 0; i < blen; i++) {
|
||||
+ *pos = *cpos++ ^ b[i];
|
||||
+ a[i] ^= *pos++;
|
||||
+ }
|
||||
+ crypto_cipher_encrypt_one(tfm, a, a);
|
||||
+ }
|
||||
+
|
||||
+ for (i = 0; i < mic_len; i++) {
|
||||
+ if ((mic[i] ^ s_0[i]) != a[i])
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+struct crypto_cipher *ieee80211_aes_key_setup_encrypt(const u8 key[],
|
||||
+ size_t key_len,
|
||||
+ size_t mic_len)
|
||||
+{
|
||||
+ struct crypto_cipher *tfm;
|
||||
+
|
||||
+ tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
|
||||
+ if (!IS_ERR(tfm))
|
||||
+ crypto_cipher_setkey(tfm, key, key_len);
|
||||
+
|
||||
+ return tfm;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+void ieee80211_aes_key_free(struct crypto_cipher *tfm)
|
||||
+{
|
||||
+ crypto_free_cipher(tfm);
|
||||
+}
|
||||
--- a/net/mac80211/Kconfig
|
||||
+++ b/net/mac80211/Kconfig
|
||||
@@ -5,8 +5,6 @@ config MAC80211
|
||||
depends on CRYPTO
|
||||
depends on CRYPTO_ARC4
|
||||
depends on CRYPTO_AES
|
||||
- depends on CRYPTO_CCM
|
||||
- depends on CRYPTO_GCM
|
||||
depends on CRYPTO_CMAC
|
||||
depends on CRC32
|
||||
---help---
|
||||
--- a/net/mac80211/aes_gmac.h
|
||||
+++ b/net/mac80211/aes_gmac.h
|
||||
@@ -15,10 +15,22 @@
|
||||
#define GMAC_MIC_LEN 16
|
||||
#define GMAC_NONCE_LEN 12
|
||||
|
||||
-struct crypto_aead *ieee80211_aes_gmac_key_setup(const u8 key[],
|
||||
- size_t key_len);
|
||||
-int ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce,
|
||||
- const u8 *data, size_t data_len, u8 *mic);
|
||||
-void ieee80211_aes_gmac_key_free(struct crypto_aead *tfm);
|
||||
+static inline struct crypto_aead *
|
||||
+ieee80211_aes_gmac_key_setup(const u8 key[], size_t key_len)
|
||||
+{
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+static inline int
|
||||
+ieee80211_aes_gmac(struct crypto_aead *tfm, const u8 *aad, u8 *nonce,
|
||||
+ const u8 *data, size_t data_len, u8 *mic)
|
||||
+{
|
||||
+ return -EOPNOTSUPP;
|
||||
+}
|
||||
+
|
||||
+static inline void
|
||||
+ieee80211_aes_gmac_key_free(struct crypto_aead *tfm)
|
||||
+{
|
||||
+}
|
||||
|
||||
#endif /* AES_GMAC_H */
|
||||
--- a/net/mac80211/key.h
|
||||
+++ b/net/mac80211/key.h
|
||||
@@ -88,7 +88,7 @@ struct ieee80211_key {
|
||||
* Management frames.
|
||||
*/
|
||||
u8 rx_pn[IEEE80211_NUM_TIDS + 1][IEEE80211_CCMP_PN_LEN];
|
||||
- struct crypto_aead *tfm;
|
||||
+ struct crypto_cipher *tfm;
|
||||
u32 replays; /* dot11RSNAStatsCCMPReplays */
|
||||
} ccmp;
|
||||
struct {
|
||||
@@ -0,0 +1,12 @@
|
||||
Used for AP+STA support in OpenWrt - preserve AP mode keys across STA reconnects
|
||||
|
||||
--- a/net/mac80211/cfg.c
|
||||
+++ b/net/mac80211/cfg.c
|
||||
@@ -1068,7 +1068,6 @@ static int ieee80211_stop_ap(struct wiph
|
||||
sdata->u.ap.driver_smps_mode = IEEE80211_SMPS_OFF;
|
||||
|
||||
__sta_info_flush(sdata, true);
|
||||
- ieee80211_free_keys(sdata, true);
|
||||
|
||||
sdata->vif.bss_conf.enable_beacon = false;
|
||||
sdata->vif.bss_conf.ssid_len = 0;
|
||||
@@ -0,0 +1,43 @@
|
||||
--- a/net/wireless/sysfs.c
|
||||
+++ b/net/wireless/sysfs.c
|
||||
@@ -24,18 +24,35 @@ static inline struct cfg80211_registered
|
||||
return container_of(dev, struct cfg80211_registered_device, wiphy.dev);
|
||||
}
|
||||
|
||||
-#define SHOW_FMT(name, fmt, member) \
|
||||
+#define SHOW_FMT(name, fmt, member, mode) \
|
||||
static ssize_t name ## _show(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member); \
|
||||
} \
|
||||
-static DEVICE_ATTR_RO(name)
|
||||
+static DEVICE_ATTR_##mode(name)
|
||||
|
||||
-SHOW_FMT(index, "%d", wiphy_idx);
|
||||
-SHOW_FMT(macaddress, "%pM", wiphy.perm_addr);
|
||||
-SHOW_FMT(address_mask, "%pM", wiphy.addr_mask);
|
||||
+static ssize_t macaddress_store(struct device *dev,
|
||||
+ struct device_attribute *attr,
|
||||
+ const char *buf, size_t len)
|
||||
+{
|
||||
+ u8 mac[ETH_ALEN];
|
||||
+
|
||||
+ if (!mac_pton(buf, mac))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (buf[3 * ETH_ALEN - 1] && buf[3 * ETH_ALEN - 1] != '\n')
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ memcpy(dev_to_rdev(dev)->wiphy.perm_addr, mac, ETH_ALEN);
|
||||
+
|
||||
+ return strnlen(buf, len);
|
||||
+}
|
||||
+
|
||||
+SHOW_FMT(index, "%d", wiphy_idx, RO);
|
||||
+SHOW_FMT(macaddress, "%pM", wiphy.perm_addr, RW);
|
||||
+SHOW_FMT(address_mask, "%pM", wiphy.addr_mask, RO);
|
||||
|
||||
static ssize_t name_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
@@ -0,0 +1,32 @@
|
||||
Disable FILS support, since it pulls in crypto hash support
|
||||
|
||||
--- a/net/mac80211/fils_aead.h
|
||||
+++ b/net/mac80211/fils_aead.h
|
||||
@@ -10,7 +10,7 @@
|
||||
#ifndef FILS_AEAD_H
|
||||
#define FILS_AEAD_H
|
||||
|
||||
-#if LINUX_VERSION_IS_GEQ(4,3,0)
|
||||
+#if 0 /* LINUX_VERSION_IS_GEQ(4,3,0) */
|
||||
int fils_encrypt_assoc_req(struct sk_buff *skb,
|
||||
struct ieee80211_mgd_assoc_data *assoc_data);
|
||||
int fils_decrypt_assoc_resp(struct ieee80211_sub_if_data *sdata,
|
||||
--- a/net/mac80211/fils_aead.c
|
||||
+++ b/net/mac80211/fils_aead.c
|
||||
@@ -1,4 +1,4 @@
|
||||
-#if LINUX_VERSION_IS_GEQ(4,3,0)
|
||||
+#if 0 /* LINUX_VERSION_IS_GEQ(4,3,0) */
|
||||
/*
|
||||
* FILS AEAD for (Re)Association Request/Response frames
|
||||
* Copyright 2016, Qualcomm Atheros, Inc.
|
||||
--- a/net/mac80211/main.c
|
||||
+++ b/net/mac80211/main.c
|
||||
@@ -570,7 +570,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_
|
||||
NL80211_FEATURE_MAC_ON_CREATE |
|
||||
NL80211_FEATURE_USERSPACE_MPM |
|
||||
NL80211_FEATURE_FULL_AP_CLIENT_STATE;
|
||||
-#if LINUX_VERSION_IS_GEQ(4,3,0)
|
||||
+#if 0 /* LINUX_VERSION_IS_GEQ(4,3,0) */
|
||||
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_STA);
|
||||
#endif
|
||||
wiphy_ext_feature_set(wiphy,
|
||||
@@ -0,0 +1,199 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sat, 7 Oct 2017 09:37:28 +0200
|
||||
Subject: [PATCH] Revert "mac80211: aes-cmac: switch to shash CMAC
|
||||
driver"
|
||||
|
||||
This reverts commit 26717828b75dd5c46e97f7f4a9b937d038bb2852.
|
||||
Reduces mac80211 dependencies for LEDE
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/aes_cmac.c
|
||||
+++ b/net/mac80211/aes_cmac.c
|
||||
@@ -22,50 +22,126 @@
|
||||
#define CMAC_TLEN_256 16 /* CMAC TLen = 128 bits (16 octets) */
|
||||
#define AAD_LEN 20
|
||||
|
||||
-static const u8 zero[CMAC_TLEN_256];
|
||||
|
||||
-void ieee80211_aes_cmac(struct crypto_shash *tfm, const u8 *aad,
|
||||
- const u8 *data, size_t data_len, u8 *mic)
|
||||
+void gf_mulx(u8 *pad)
|
||||
+{
|
||||
+ int i, carry;
|
||||
+
|
||||
+ carry = pad[0] & 0x80;
|
||||
+ for (i = 0; i < AES_BLOCK_SIZE - 1; i++)
|
||||
+ pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7);
|
||||
+ pad[AES_BLOCK_SIZE - 1] <<= 1;
|
||||
+ if (carry)
|
||||
+ pad[AES_BLOCK_SIZE - 1] ^= 0x87;
|
||||
+}
|
||||
+
|
||||
+void aes_cmac_vector(struct crypto_cipher *tfm, size_t num_elem,
|
||||
+ const u8 *addr[], const size_t *len, u8 *mac,
|
||||
+ size_t mac_len)
|
||||
{
|
||||
- SHASH_DESC_ON_STACK(desc, tfm);
|
||||
- u8 out[AES_BLOCK_SIZE];
|
||||
+ u8 cbc[AES_BLOCK_SIZE], pad[AES_BLOCK_SIZE];
|
||||
+ const u8 *pos, *end;
|
||||
+ size_t i, e, left, total_len;
|
||||
+
|
||||
+ memset(cbc, 0, AES_BLOCK_SIZE);
|
||||
+
|
||||
+ total_len = 0;
|
||||
+ for (e = 0; e < num_elem; e++)
|
||||
+ total_len += len[e];
|
||||
+ left = total_len;
|
||||
+
|
||||
+ e = 0;
|
||||
+ pos = addr[0];
|
||||
+ end = pos + len[0];
|
||||
+
|
||||
+ while (left >= AES_BLOCK_SIZE) {
|
||||
+ for (i = 0; i < AES_BLOCK_SIZE; i++) {
|
||||
+ cbc[i] ^= *pos++;
|
||||
+ if (pos >= end) {
|
||||
+ e++;
|
||||
+ pos = addr[e];
|
||||
+ end = pos + len[e];
|
||||
+ }
|
||||
+ }
|
||||
+ if (left > AES_BLOCK_SIZE)
|
||||
+ crypto_cipher_encrypt_one(tfm, cbc, cbc);
|
||||
+ left -= AES_BLOCK_SIZE;
|
||||
+ }
|
||||
+
|
||||
+ memset(pad, 0, AES_BLOCK_SIZE);
|
||||
+ crypto_cipher_encrypt_one(tfm, pad, pad);
|
||||
+ gf_mulx(pad);
|
||||
+
|
||||
+ if (left || total_len == 0) {
|
||||
+ for (i = 0; i < left; i++) {
|
||||
+ cbc[i] ^= *pos++;
|
||||
+ if (pos >= end) {
|
||||
+ e++;
|
||||
+ pos = addr[e];
|
||||
+ end = pos + len[e];
|
||||
+ }
|
||||
+ }
|
||||
+ cbc[left] ^= 0x80;
|
||||
+ gf_mulx(pad);
|
||||
+ }
|
||||
+
|
||||
+ for (i = 0; i < AES_BLOCK_SIZE; i++)
|
||||
+ pad[i] ^= cbc[i];
|
||||
+ crypto_cipher_encrypt_one(tfm, pad, pad);
|
||||
+ memcpy(mac, pad, mac_len);
|
||||
+}
|
||||
|
||||
- desc->tfm = tfm;
|
||||
|
||||
- crypto_shash_init(desc);
|
||||
- crypto_shash_update(desc, aad, AAD_LEN);
|
||||
- crypto_shash_update(desc, data, data_len - CMAC_TLEN);
|
||||
- crypto_shash_finup(desc, zero, CMAC_TLEN, out);
|
||||
+void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad,
|
||||
+ const u8 *data, size_t data_len, u8 *mic)
|
||||
+{
|
||||
+ const u8 *addr[3];
|
||||
+ size_t len[3];
|
||||
+ u8 zero[CMAC_TLEN];
|
||||
+
|
||||
+ memset(zero, 0, CMAC_TLEN);
|
||||
+ addr[0] = aad;
|
||||
+ len[0] = AAD_LEN;
|
||||
+ addr[1] = data;
|
||||
+ len[1] = data_len - CMAC_TLEN;
|
||||
+ addr[2] = zero;
|
||||
+ len[2] = CMAC_TLEN;
|
||||
|
||||
- memcpy(mic, out, CMAC_TLEN);
|
||||
+ aes_cmac_vector(tfm, 3, addr, len, mic, CMAC_TLEN);
|
||||
}
|
||||
|
||||
-void ieee80211_aes_cmac_256(struct crypto_shash *tfm, const u8 *aad,
|
||||
+void ieee80211_aes_cmac_256(struct crypto_cipher *tfm, const u8 *aad,
|
||||
const u8 *data, size_t data_len, u8 *mic)
|
||||
{
|
||||
- SHASH_DESC_ON_STACK(desc, tfm);
|
||||
+ const u8 *addr[3];
|
||||
+ size_t len[3];
|
||||
+ u8 zero[CMAC_TLEN_256];
|
||||
+
|
||||
+ memset(zero, 0, CMAC_TLEN_256);
|
||||
+ addr[0] = aad;
|
||||
+ len[0] = AAD_LEN;
|
||||
+ addr[1] = data;
|
||||
+ len[1] = data_len - CMAC_TLEN_256;
|
||||
+ addr[2] = zero;
|
||||
+ len[2] = CMAC_TLEN_256;
|
||||
|
||||
- desc->tfm = tfm;
|
||||
-
|
||||
- crypto_shash_init(desc);
|
||||
- crypto_shash_update(desc, aad, AAD_LEN);
|
||||
- crypto_shash_update(desc, data, data_len - CMAC_TLEN_256);
|
||||
- crypto_shash_finup(desc, zero, CMAC_TLEN_256, mic);
|
||||
+ aes_cmac_vector(tfm, 3, addr, len, mic, CMAC_TLEN_256);
|
||||
}
|
||||
|
||||
-struct crypto_shash *ieee80211_aes_cmac_key_setup(const u8 key[],
|
||||
- size_t key_len)
|
||||
+struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[],
|
||||
+ size_t key_len)
|
||||
{
|
||||
- struct crypto_shash *tfm;
|
||||
+ struct crypto_cipher *tfm;
|
||||
|
||||
- tfm = crypto_alloc_shash("cmac(aes)", 0, 0);
|
||||
+ tfm = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
|
||||
if (!IS_ERR(tfm))
|
||||
- crypto_shash_setkey(tfm, key, key_len);
|
||||
+ crypto_cipher_setkey(tfm, key, key_len);
|
||||
|
||||
return tfm;
|
||||
}
|
||||
|
||||
-void ieee80211_aes_cmac_key_free(struct crypto_shash *tfm)
|
||||
+
|
||||
+void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm)
|
||||
{
|
||||
- crypto_free_shash(tfm);
|
||||
+ crypto_free_cipher(tfm);
|
||||
}
|
||||
--- a/net/mac80211/aes_cmac.h
|
||||
+++ b/net/mac80211/aes_cmac.h
|
||||
@@ -10,14 +10,13 @@
|
||||
#define AES_CMAC_H
|
||||
|
||||
#include <linux/crypto.h>
|
||||
-#include <crypto/hash.h>
|
||||
|
||||
-struct crypto_shash *ieee80211_aes_cmac_key_setup(const u8 key[],
|
||||
- size_t key_len);
|
||||
-void ieee80211_aes_cmac(struct crypto_shash *tfm, const u8 *aad,
|
||||
+struct crypto_cipher *ieee80211_aes_cmac_key_setup(const u8 key[],
|
||||
+ size_t key_len);
|
||||
+void ieee80211_aes_cmac(struct crypto_cipher *tfm, const u8 *aad,
|
||||
const u8 *data, size_t data_len, u8 *mic);
|
||||
-void ieee80211_aes_cmac_256(struct crypto_shash *tfm, const u8 *aad,
|
||||
+void ieee80211_aes_cmac_256(struct crypto_cipher *tfm, const u8 *aad,
|
||||
const u8 *data, size_t data_len, u8 *mic);
|
||||
-void ieee80211_aes_cmac_key_free(struct crypto_shash *tfm);
|
||||
+void ieee80211_aes_cmac_key_free(struct crypto_cipher *tfm);
|
||||
|
||||
#endif /* AES_CMAC_H */
|
||||
--- a/net/mac80211/key.h
|
||||
+++ b/net/mac80211/key.h
|
||||
@@ -93,7 +93,7 @@ struct ieee80211_key {
|
||||
} ccmp;
|
||||
struct {
|
||||
u8 rx_pn[IEEE80211_CMAC_PN_LEN];
|
||||
- struct crypto_shash *tfm;
|
||||
+ struct crypto_cipher *tfm;
|
||||
u32 replays; /* dot11RSNAStatsCMACReplays */
|
||||
u32 icverrors; /* dot11RSNAStatsCMACICVErrors */
|
||||
} aes_cmac;
|
||||
@@ -0,0 +1,10 @@
|
||||
--- a/net/mac80211/Kconfig
|
||||
+++ b/net/mac80211/Kconfig
|
||||
@@ -5,7 +5,6 @@ config MAC80211
|
||||
depends on CRYPTO
|
||||
depends on CRYPTO_ARC4
|
||||
depends on CRYPTO_AES
|
||||
- depends on CRYPTO_CMAC
|
||||
depends on CRC32
|
||||
---help---
|
||||
This option enables the hardware independent IEEE 802.11
|
||||
@@ -0,0 +1,15 @@
|
||||
--- a/net/mac80211/tx.c
|
||||
+++ b/net/mac80211/tx.c
|
||||
@@ -3833,6 +3833,12 @@ out:
|
||||
netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
struct net_device *dev)
|
||||
{
|
||||
+#if defined(sk_pacing_shift) || LINUX_VERSION_IS_GEQ(4,15,0)
|
||||
+ if (skb->sk && sk_fullsock(skb->sk) &&
|
||||
+ skb->sk->sk_pacing_shift != 6)
|
||||
+ skb->sk->sk_pacing_shift = 6;
|
||||
+#endif
|
||||
+
|
||||
if (unlikely(ieee80211_multicast_to_unicast(skb, dev))) {
|
||||
struct sk_buff_head queue;
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
--- a/net/mac80211/main.c
|
||||
+++ b/net/mac80211/main.c
|
||||
@@ -315,7 +315,7 @@ void ieee80211_restart_hw(struct ieee802
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_restart_hw);
|
||||
|
||||
-#ifdef CONFIG_INET
|
||||
+#ifdef __disabled__CONFIG_INET
|
||||
static int ieee80211_ifa_changed(struct notifier_block *nb,
|
||||
unsigned long data, void *arg)
|
||||
{
|
||||
@@ -374,7 +374,7 @@ static int ieee80211_ifa_changed(struct
|
||||
}
|
||||
#endif
|
||||
|
||||
-#if IS_ENABLED(CONFIG_IPV6)
|
||||
+#if IS_ENABLED(__disabled__CONFIG_IPV6)
|
||||
static int ieee80211_ifa6_changed(struct notifier_block *nb,
|
||||
unsigned long data, void *arg)
|
||||
{
|
||||
@@ -1168,14 +1168,14 @@ int ieee80211_register_hw(struct ieee802
|
||||
|
||||
rtnl_unlock();
|
||||
|
||||
-#ifdef CONFIG_INET
|
||||
+#ifdef __disabled__CONFIG_INET
|
||||
local->ifa_notifier.notifier_call = ieee80211_ifa_changed;
|
||||
result = register_inetaddr_notifier(&local->ifa_notifier);
|
||||
if (result)
|
||||
goto fail_ifa;
|
||||
#endif
|
||||
|
||||
-#if IS_ENABLED(CONFIG_IPV6)
|
||||
+#if IS_ENABLED(__disabled__CONFIG_IPV6)
|
||||
local->ifa6_notifier.notifier_call = ieee80211_ifa6_changed;
|
||||
result = register_inet6addr_notifier(&local->ifa6_notifier);
|
||||
if (result)
|
||||
@@ -1184,13 +1184,13 @@ int ieee80211_register_hw(struct ieee802
|
||||
|
||||
return 0;
|
||||
|
||||
-#if IS_ENABLED(CONFIG_IPV6)
|
||||
+#if IS_ENABLED(__disabled__CONFIG_IPV6)
|
||||
fail_ifa6:
|
||||
-#ifdef CONFIG_INET
|
||||
+#ifdef __disabled__CONFIG_INET
|
||||
unregister_inetaddr_notifier(&local->ifa_notifier);
|
||||
#endif
|
||||
#endif
|
||||
-#if defined(CONFIG_INET) || defined(CONFIG_IPV6)
|
||||
+#if defined(__disabled__CONFIG_INET) || defined(__disabled__CONFIG_IPV6)
|
||||
fail_ifa:
|
||||
#endif
|
||||
rtnl_lock();
|
||||
@@ -1219,10 +1219,10 @@ void ieee80211_unregister_hw(struct ieee
|
||||
tasklet_kill(&local->tx_pending_tasklet);
|
||||
tasklet_kill(&local->tasklet);
|
||||
|
||||
-#ifdef CONFIG_INET
|
||||
+#ifdef __disabled__CONFIG_INET
|
||||
unregister_inetaddr_notifier(&local->ifa_notifier);
|
||||
#endif
|
||||
-#if IS_ENABLED(CONFIG_IPV6)
|
||||
+#if IS_ENABLED(__disabled__CONFIG_IPV6)
|
||||
unregister_inet6addr_notifier(&local->ifa6_notifier);
|
||||
#endif
|
||||
|
||||
11
package/kernel/mac80211/patches/subsys/210-ap_scan.patch
Normal file
11
package/kernel/mac80211/patches/subsys/210-ap_scan.patch
Normal file
@@ -0,0 +1,11 @@
|
||||
--- a/net/mac80211/cfg.c
|
||||
+++ b/net/mac80211/cfg.c
|
||||
@@ -2190,7 +2190,7 @@ static int ieee80211_scan(struct wiphy *
|
||||
* the frames sent while scanning on other channel will be
|
||||
* lost)
|
||||
*/
|
||||
- if (sdata->u.ap.beacon &&
|
||||
+ if (0 && sdata->u.ap.beacon &&
|
||||
(!(wiphy->features & NL80211_FEATURE_AP_SCAN) ||
|
||||
!(req->flags & NL80211_SCAN_FLAG_AP)))
|
||||
return -EOPNOTSUPP;
|
||||
@@ -0,0 +1,272 @@
|
||||
From: Manikanta Pubbisetty <mpubbise@codeaurora.org>
|
||||
Date: Wed, 11 Jul 2018 00:12:53 +0530
|
||||
Subject: [PATCH] mac80211: add stop/start logic for software TXQs
|
||||
|
||||
Sometimes, it is required to stop the transmissions momentarily and
|
||||
resume it later; stopping the txqs becomes very critical in scenarios where
|
||||
the packet transmission has to be ceased completely. For example, during
|
||||
the hardware restart, during off channel operations,
|
||||
when initiating CSA(upon detecting a radar on the DFS channel), etc.
|
||||
|
||||
The TX queue stop/start logic in mac80211 works well in stopping the TX
|
||||
when drivers make use of netdev queues, i.e, when Qdiscs in network layer
|
||||
take care of traffic scheduling. Since the devices implementing
|
||||
wake_tx_queue can run without Qdiscs, packets will be handed to mac80211
|
||||
directly without queueing them in the netdev queues.
|
||||
|
||||
Also, mac80211 does not invoke any of the
|
||||
netif_stop_*/netif_wake_* APIs if wake_tx_queue is implemented.
|
||||
Since the queues are not stopped in this case, transmissions can continue
|
||||
and this will impact negatively on the operation of the wireless device.
|
||||
|
||||
For example,
|
||||
During hardware restart, we stop the netdev queues so that packets are
|
||||
not sent to the driver. Since ath10k implements wake_tx_queue,
|
||||
TX queues will not be stopped and packets might reach the hardware while
|
||||
it is restarting; this can make hardware unresponsive and the only
|
||||
possible option for recovery is to reboot the entire system.
|
||||
|
||||
There is another problem to this, it is observed that the packets
|
||||
were sent on the DFS channel for a prolonged duration after radar
|
||||
detection impacting the channel closing time.
|
||||
|
||||
We can still invoke netif stop/wake APIs when wake_tx_queue is implemented
|
||||
but this could lead to packet drops in network layer; adding stop/start
|
||||
logic for software TXQs in mac80211 instead makes more sense; the change
|
||||
proposed adds the same in mac80211.
|
||||
|
||||
Signed-off-by: Manikanta Pubbisetty <mpubbise@codeaurora.org>
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/include/net/mac80211.h
|
||||
+++ b/include/net/mac80211.h
|
||||
@@ -1504,6 +1504,8 @@ enum ieee80211_vif_flags {
|
||||
* @drv_priv: data area for driver use, will always be aligned to
|
||||
* sizeof(void \*).
|
||||
* @txq: the multicast data TX queue (if driver uses the TXQ abstraction)
|
||||
+ * @txqs_stopped: per AC flag to indicate that intermediate TXQs are stopped,
|
||||
+ * protected by fq->lock.
|
||||
*/
|
||||
struct ieee80211_vif {
|
||||
enum nl80211_iftype type;
|
||||
@@ -1528,6 +1530,8 @@ struct ieee80211_vif {
|
||||
|
||||
unsigned int probe_req_reg;
|
||||
|
||||
+ bool txqs_stopped[IEEE80211_NUM_ACS];
|
||||
+
|
||||
/* must be last */
|
||||
u8 drv_priv[0] __aligned(sizeof(void *));
|
||||
};
|
||||
--- a/net/mac80211/ieee80211_i.h
|
||||
+++ b/net/mac80211/ieee80211_i.h
|
||||
@@ -818,6 +818,7 @@ enum txq_info_flags {
|
||||
IEEE80211_TXQ_STOP,
|
||||
IEEE80211_TXQ_AMPDU,
|
||||
IEEE80211_TXQ_NO_AMSDU,
|
||||
+ IEEE80211_TXQ_STOP_NETIF_TX,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1226,6 +1227,7 @@ struct ieee80211_local {
|
||||
|
||||
struct sk_buff_head pending[IEEE80211_MAX_QUEUES];
|
||||
struct tasklet_struct tx_pending_tasklet;
|
||||
+ struct tasklet_struct wake_txqs_tasklet;
|
||||
|
||||
atomic_t agg_queue_stop[IEEE80211_MAX_QUEUES];
|
||||
|
||||
@@ -2047,6 +2049,7 @@ void ieee80211_txq_remove_vlan(struct ie
|
||||
struct ieee80211_sub_if_data *sdata);
|
||||
void ieee80211_fill_txq_stats(struct cfg80211_txq_stats *txqstats,
|
||||
struct txq_info *txqi);
|
||||
+void ieee80211_wake_txqs(unsigned long data);
|
||||
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,
|
||||
--- a/net/mac80211/main.c
|
||||
+++ b/net/mac80211/main.c
|
||||
@@ -686,6 +686,10 @@ struct ieee80211_hw *ieee80211_alloc_hw_
|
||||
tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending,
|
||||
(unsigned long)local);
|
||||
|
||||
+ if (ops->wake_tx_queue)
|
||||
+ tasklet_init(&local->wake_txqs_tasklet, ieee80211_wake_txqs,
|
||||
+ (unsigned long)local);
|
||||
+
|
||||
tasklet_init(&local->tasklet,
|
||||
ieee80211_tasklet_handler,
|
||||
(unsigned long) local);
|
||||
--- a/net/mac80211/tx.c
|
||||
+++ b/net/mac80211/tx.c
|
||||
@@ -3498,13 +3498,19 @@ struct sk_buff *ieee80211_tx_dequeue(str
|
||||
struct ieee80211_tx_info *info;
|
||||
struct ieee80211_tx_data tx;
|
||||
ieee80211_tx_result r;
|
||||
- struct ieee80211_vif *vif;
|
||||
+ struct ieee80211_vif *vif = txq->vif;
|
||||
|
||||
spin_lock_bh(&fq->lock);
|
||||
|
||||
- if (test_bit(IEEE80211_TXQ_STOP, &txqi->flags))
|
||||
+ if (test_bit(IEEE80211_TXQ_STOP, &txqi->flags) ||
|
||||
+ test_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txqi->flags))
|
||||
goto out;
|
||||
|
||||
+ if (vif->txqs_stopped[ieee80211_ac_from_tid(txq->tid)]) {
|
||||
+ set_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txqi->flags);
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
/* Make sure fragments stay together. */
|
||||
skb = __skb_dequeue(&txqi->frags);
|
||||
if (skb)
|
||||
@@ -3617,6 +3623,7 @@ begin:
|
||||
}
|
||||
|
||||
IEEE80211_SKB_CB(skb)->control.vif = vif;
|
||||
+
|
||||
out:
|
||||
spin_unlock_bh(&fq->lock);
|
||||
|
||||
--- a/net/mac80211/util.c
|
||||
+++ b/net/mac80211/util.c
|
||||
@@ -240,6 +240,99 @@ __le16 ieee80211_ctstoself_duration(stru
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_ctstoself_duration);
|
||||
|
||||
+static void __ieee80211_wake_txqs(struct ieee80211_sub_if_data *sdata, int ac)
|
||||
+{
|
||||
+ struct ieee80211_local *local = sdata->local;
|
||||
+ struct ieee80211_vif *vif = &sdata->vif;
|
||||
+ struct fq *fq = &local->fq;
|
||||
+ struct ps_data *ps = NULL;
|
||||
+ struct txq_info *txqi;
|
||||
+ struct sta_info *sta;
|
||||
+ int i;
|
||||
+
|
||||
+ spin_lock_bh(&fq->lock);
|
||||
+
|
||||
+ if (sdata->vif.type == NL80211_IFTYPE_AP)
|
||||
+ ps = &sdata->bss->ps;
|
||||
+
|
||||
+ sdata->vif.txqs_stopped[ac] = false;
|
||||
+
|
||||
+ list_for_each_entry_rcu(sta, &local->sta_list, list) {
|
||||
+ if (sdata != sta->sdata)
|
||||
+ continue;
|
||||
+
|
||||
+ for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
|
||||
+ struct ieee80211_txq *txq = sta->sta.txq[i];
|
||||
+
|
||||
+ txqi = to_txq_info(txq);
|
||||
+
|
||||
+ if (ac != txq->ac)
|
||||
+ continue;
|
||||
+
|
||||
+ if (!test_and_clear_bit(IEEE80211_TXQ_STOP_NETIF_TX,
|
||||
+ &txqi->flags))
|
||||
+ continue;
|
||||
+
|
||||
+ spin_unlock_bh(&fq->lock);
|
||||
+ drv_wake_tx_queue(local, txqi);
|
||||
+ spin_lock_bh(&fq->lock);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (!vif->txq)
|
||||
+ goto out;
|
||||
+
|
||||
+ txqi = to_txq_info(vif->txq);
|
||||
+
|
||||
+ if (!test_and_clear_bit(IEEE80211_TXQ_STOP_NETIF_TX, &txqi->flags) ||
|
||||
+ (ps && atomic_read(&ps->num_sta_ps)) || ac != vif->txq->ac)
|
||||
+ goto out;
|
||||
+
|
||||
+ spin_unlock_bh(&fq->lock);
|
||||
+
|
||||
+ drv_wake_tx_queue(local, txqi);
|
||||
+ return;
|
||||
+out:
|
||||
+ spin_unlock_bh(&fq->lock);
|
||||
+}
|
||||
+
|
||||
+void ieee80211_wake_txqs(unsigned long data)
|
||||
+{
|
||||
+ struct ieee80211_local *local = (struct ieee80211_local *)data;
|
||||
+ struct ieee80211_sub_if_data *sdata;
|
||||
+ int n_acs = IEEE80211_NUM_ACS;
|
||||
+ unsigned long flags;
|
||||
+ int i;
|
||||
+
|
||||
+ rcu_read_lock();
|
||||
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
|
||||
+
|
||||
+ if (local->hw.queues < IEEE80211_NUM_ACS)
|
||||
+ n_acs = 1;
|
||||
+
|
||||
+ for (i = 0; i < local->hw.queues; i++) {
|
||||
+ if (local->queue_stop_reasons[i])
|
||||
+ continue;
|
||||
+
|
||||
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
||||
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
||||
+ int ac;
|
||||
+
|
||||
+ for (ac = 0; ac < n_acs; ac++) {
|
||||
+ int ac_queue = sdata->vif.hw_queue[ac];
|
||||
+
|
||||
+ if (ac_queue == i ||
|
||||
+ sdata->vif.cab_queue == i)
|
||||
+ __ieee80211_wake_txqs(sdata, ac);
|
||||
+ }
|
||||
+ }
|
||||
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
|
||||
+ }
|
||||
+
|
||||
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
||||
+ rcu_read_unlock();
|
||||
+}
|
||||
+
|
||||
void ieee80211_propagate_queue_wake(struct ieee80211_local *local, int queue)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
@@ -308,6 +401,9 @@ static void __ieee80211_wake_queue(struc
|
||||
rcu_read_unlock();
|
||||
} else
|
||||
tasklet_schedule(&local->tx_pending_tasklet);
|
||||
+
|
||||
+ if (local->ops->wake_tx_queue)
|
||||
+ tasklet_schedule(&local->wake_txqs_tasklet);
|
||||
}
|
||||
|
||||
void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
|
||||
@@ -351,9 +447,6 @@ static void __ieee80211_stop_queue(struc
|
||||
if (__test_and_set_bit(reason, &local->queue_stop_reasons[queue]))
|
||||
return;
|
||||
|
||||
- if (local->ops->wake_tx_queue)
|
||||
- return;
|
||||
-
|
||||
if (local->hw.queues < IEEE80211_NUM_ACS)
|
||||
n_acs = 1;
|
||||
|
||||
@@ -366,8 +459,15 @@ static void __ieee80211_stop_queue(struc
|
||||
|
||||
for (ac = 0; ac < n_acs; ac++) {
|
||||
if (sdata->vif.hw_queue[ac] == queue ||
|
||||
- sdata->vif.cab_queue == queue)
|
||||
- netif_stop_subqueue(sdata->dev, ac);
|
||||
+ sdata->vif.cab_queue == queue) {
|
||||
+ if (!local->ops->wake_tx_queue) {
|
||||
+ netif_stop_subqueue(sdata->dev, ac);
|
||||
+ continue;
|
||||
+ }
|
||||
+ spin_lock(&local->fq.lock);
|
||||
+ sdata->vif.txqs_stopped[ac] = true;
|
||||
+ spin_unlock(&local->fq.lock);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
@@ -0,0 +1,81 @@
|
||||
From: Chaitanya T K <chaitanya.mgit@gmail.com>
|
||||
Date: Mon, 27 Jun 2016 15:23:26 +0530
|
||||
Subject: [PATCH] mac80211: minstrel: Enable STBC and LDPC for VHT Rates
|
||||
|
||||
If peer support reception of STBC and LDPC, enable them for better
|
||||
performance.
|
||||
|
||||
Signed-off-by: Chaitanya TK <chaitanya.mgit@gmail.com>
|
||||
---
|
||||
|
||||
--- a/include/linux/ieee80211.h
|
||||
+++ b/include/linux/ieee80211.h
|
||||
@@ -1668,6 +1668,7 @@ struct ieee80211_mu_edca_param_set {
|
||||
#define IEEE80211_VHT_CAP_RXSTBC_3 0x00000300
|
||||
#define IEEE80211_VHT_CAP_RXSTBC_4 0x00000400
|
||||
#define IEEE80211_VHT_CAP_RXSTBC_MASK 0x00000700
|
||||
+#define IEEE80211_VHT_CAP_RXSTBC_SHIFT 8
|
||||
#define IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE 0x00000800
|
||||
#define IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE 0x00001000
|
||||
#define IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT 13
|
||||
--- a/net/mac80211/rc80211_minstrel_ht.c
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht.c
|
||||
@@ -1136,13 +1136,14 @@ minstrel_ht_update_caps(void *priv, stru
|
||||
struct minstrel_ht_sta_priv *msp = priv_sta;
|
||||
struct minstrel_ht_sta *mi = &msp->ht;
|
||||
struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs;
|
||||
- u16 sta_cap = sta->ht_cap.cap;
|
||||
+ u16 ht_cap = sta->ht_cap.cap;
|
||||
struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
|
||||
int use_vht;
|
||||
int n_supported = 0;
|
||||
int ack_dur;
|
||||
int stbc;
|
||||
int i;
|
||||
+ bool ldpc = false;
|
||||
|
||||
/* fall back to the old minstrel for legacy stations */
|
||||
if (!sta->ht_cap.ht_supported)
|
||||
@@ -1180,16 +1181,24 @@ minstrel_ht_update_caps(void *priv, stru
|
||||
}
|
||||
mi->sample_tries = 4;
|
||||
|
||||
- /* TODO tx_flags for vht - ATM the RC API is not fine-grained enough */
|
||||
if (!use_vht) {
|
||||
- stbc = (sta_cap & IEEE80211_HT_CAP_RX_STBC) >>
|
||||
+ stbc = (ht_cap & IEEE80211_HT_CAP_RX_STBC) >>
|
||||
IEEE80211_HT_CAP_RX_STBC_SHIFT;
|
||||
- mi->tx_flags |= stbc << IEEE80211_TX_CTL_STBC_SHIFT;
|
||||
|
||||
- if (sta_cap & IEEE80211_HT_CAP_LDPC_CODING)
|
||||
- mi->tx_flags |= IEEE80211_TX_CTL_LDPC;
|
||||
+ if (ht_cap & IEEE80211_HT_CAP_LDPC_CODING)
|
||||
+ ldpc = true;
|
||||
+ } else {
|
||||
+ stbc = (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK) >>
|
||||
+ IEEE80211_VHT_CAP_RXSTBC_SHIFT;
|
||||
+
|
||||
+ if (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC)
|
||||
+ ldpc = true;
|
||||
}
|
||||
|
||||
+ mi->tx_flags |= stbc << IEEE80211_TX_CTL_STBC_SHIFT;
|
||||
+ if (ldpc)
|
||||
+ mi->tx_flags |= IEEE80211_TX_CTL_LDPC;
|
||||
+
|
||||
for (i = 0; i < ARRAY_SIZE(mi->groups); i++) {
|
||||
u32 gflags = minstrel_mcs_groups[i].flags;
|
||||
int bw, nss;
|
||||
@@ -1202,10 +1211,10 @@ minstrel_ht_update_caps(void *priv, stru
|
||||
|
||||
if (gflags & IEEE80211_TX_RC_SHORT_GI) {
|
||||
if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
|
||||
- if (!(sta_cap & IEEE80211_HT_CAP_SGI_40))
|
||||
+ if (!(ht_cap & IEEE80211_HT_CAP_SGI_40))
|
||||
continue;
|
||||
} else {
|
||||
- if (!(sta_cap & IEEE80211_HT_CAP_SGI_20))
|
||||
+ if (!(ht_cap & IEEE80211_HT_CAP_SGI_20))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sat, 10 Feb 2018 12:41:51 +0100
|
||||
Subject: [PATCH] mac80211: minstrel: remove unnecessary debugfs cleanup
|
||||
code
|
||||
|
||||
debugfs entries are cleaned up by debugfs_remove_recursive already.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/rc80211_minstrel.c
|
||||
+++ b/net/mac80211/rc80211_minstrel.c
|
||||
@@ -672,8 +672,8 @@ minstrel_alloc(struct ieee80211_hw *hw,
|
||||
|
||||
#ifdef CPTCFG_MAC80211_DEBUGFS
|
||||
mp->fixed_rate_idx = (u32) -1;
|
||||
- mp->dbg_fixed_rate = debugfs_create_u32("fixed_rate_idx",
|
||||
- 0666, debugfsdir, &mp->fixed_rate_idx);
|
||||
+ debugfs_create_u32("fixed_rate_idx", S_IRUGO | S_IWUGO, debugfsdir,
|
||||
+ &mp->fixed_rate_idx);
|
||||
#endif
|
||||
|
||||
minstrel_init_cck_rates(mp);
|
||||
@@ -684,9 +684,6 @@ minstrel_alloc(struct ieee80211_hw *hw,
|
||||
static void
|
||||
minstrel_free(void *priv)
|
||||
{
|
||||
-#ifdef CPTCFG_MAC80211_DEBUGFS
|
||||
- debugfs_remove(((struct minstrel_priv *)priv)->dbg_fixed_rate);
|
||||
-#endif
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
@@ -718,7 +715,6 @@ const struct rate_control_ops mac80211_m
|
||||
.free_sta = minstrel_free_sta,
|
||||
#ifdef CPTCFG_MAC80211_DEBUGFS
|
||||
.add_sta_debugfs = minstrel_add_sta_debugfs,
|
||||
- .remove_sta_debugfs = minstrel_remove_sta_debugfs,
|
||||
#endif
|
||||
.get_expected_throughput = minstrel_get_expected_throughput,
|
||||
};
|
||||
--- a/net/mac80211/rc80211_minstrel.h
|
||||
+++ b/net/mac80211/rc80211_minstrel.h
|
||||
@@ -108,11 +108,6 @@ struct minstrel_sta_info {
|
||||
|
||||
/* sampling table */
|
||||
u8 *sample_table;
|
||||
-
|
||||
-#ifdef CPTCFG_MAC80211_DEBUGFS
|
||||
- struct dentry *dbg_stats;
|
||||
- struct dentry *dbg_stats_csv;
|
||||
-#endif
|
||||
};
|
||||
|
||||
struct minstrel_priv {
|
||||
@@ -136,7 +131,6 @@ struct minstrel_priv {
|
||||
* - setting will be applied on next update
|
||||
*/
|
||||
u32 fixed_rate_idx;
|
||||
- struct dentry *dbg_fixed_rate;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -155,7 +149,6 @@ minstrel_get_ewmsd10(struct minstrel_rat
|
||||
|
||||
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 *mrs);
|
||||
--- a/net/mac80211/rc80211_minstrel_debugfs.c
|
||||
+++ b/net/mac80211/rc80211_minstrel_debugfs.c
|
||||
@@ -214,19 +214,7 @@ minstrel_add_sta_debugfs(void *priv, voi
|
||||
{
|
||||
struct minstrel_sta_info *mi = priv_sta;
|
||||
|
||||
- mi->dbg_stats = debugfs_create_file("rc_stats", 0444, dir, mi,
|
||||
- &minstrel_stat_fops);
|
||||
-
|
||||
- mi->dbg_stats_csv = debugfs_create_file("rc_stats_csv", 0444, dir, mi,
|
||||
- &minstrel_stat_csv_fops);
|
||||
-}
|
||||
-
|
||||
-void
|
||||
-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);
|
||||
+ debugfs_create_file("rc_stats", S_IRUGO, dir, mi, &minstrel_stat_fops);
|
||||
+ debugfs_create_file("rc_stats_csv", S_IRUGO, dir, mi,
|
||||
+ &minstrel_stat_csv_fops);
|
||||
}
|
||||
--- a/net/mac80211/rc80211_minstrel_ht.c
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht.c
|
||||
@@ -1397,7 +1397,6 @@ static const struct rate_control_ops mac
|
||||
.free = minstrel_ht_free,
|
||||
#ifdef CPTCFG_MAC80211_DEBUGFS
|
||||
.add_sta_debugfs = minstrel_ht_add_sta_debugfs,
|
||||
- .remove_sta_debugfs = minstrel_ht_remove_sta_debugfs,
|
||||
#endif
|
||||
.get_expected_throughput = minstrel_ht_get_expected_throughput,
|
||||
};
|
||||
--- a/net/mac80211/rc80211_minstrel_ht.h
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht.h
|
||||
@@ -110,17 +110,12 @@ struct minstrel_ht_sta_priv {
|
||||
struct minstrel_ht_sta ht;
|
||||
struct minstrel_sta_info legacy;
|
||||
};
|
||||
-#ifdef CPTCFG_MAC80211_DEBUGFS
|
||||
- struct dentry *dbg_stats;
|
||||
- struct dentry *dbg_stats_csv;
|
||||
-#endif
|
||||
void *ratelist;
|
||||
void *sample_table;
|
||||
bool is_ht;
|
||||
};
|
||||
|
||||
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 prob_ewma);
|
||||
|
||||
--- a/net/mac80211/rc80211_minstrel_ht_debugfs.c
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c
|
||||
@@ -303,17 +303,8 @@ minstrel_ht_add_sta_debugfs(void *priv,
|
||||
{
|
||||
struct minstrel_ht_sta_priv *msp = priv_sta;
|
||||
|
||||
- msp->dbg_stats = debugfs_create_file("rc_stats", 0444, dir, msp,
|
||||
- &minstrel_ht_stat_fops);
|
||||
- msp->dbg_stats_csv = debugfs_create_file("rc_stats_csv", 0444, dir, msp,
|
||||
- &minstrel_ht_stat_csv_fops);
|
||||
-}
|
||||
-
|
||||
-void
|
||||
-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);
|
||||
+ debugfs_create_file("rc_stats", S_IRUGO, dir, msp,
|
||||
+ &minstrel_ht_stat_fops);
|
||||
+ debugfs_create_file("rc_stats_csv", S_IRUGO, dir, msp,
|
||||
+ &minstrel_ht_stat_csv_fops);
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
From: Markus Theil <markus.theil@tu-ilmenau.de>
|
||||
Date: Wed, 18 Dec 2019 15:27:36 +0100
|
||||
Subject: [PATCH] mac80211: fix tx status for no ack cases
|
||||
|
||||
Before this patch, frames which where successfully transmitted without
|
||||
requiring acks where accounted as lost frames.
|
||||
|
||||
Signed-off-by: Markus Theil <markus.theil@tu-ilmenau.de>
|
||||
Link: https://lore.kernel.org/r/20191218142736.15843-1-markus.theil@tu-ilmenau.de
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/status.c
|
||||
+++ b/net/mac80211/status.c
|
||||
@@ -718,6 +718,7 @@ static void __ieee80211_tx_status(struct
|
||||
int rates_idx;
|
||||
bool send_to_cooked;
|
||||
bool acked;
|
||||
+ bool noack_success;
|
||||
struct ieee80211_bar *bar;
|
||||
int shift = 0;
|
||||
int tid = IEEE80211_NUM_TIDS;
|
||||
@@ -735,6 +736,8 @@ static void __ieee80211_tx_status(struct
|
||||
clear_sta_flag(sta, WLAN_STA_SP);
|
||||
|
||||
acked = !!(info->flags & IEEE80211_TX_STAT_ACK);
|
||||
+ noack_success = !!(info->flags &
|
||||
+ IEEE80211_TX_STAT_NOACK_TRANSMITTED);
|
||||
|
||||
/* mesh Peer Service Period support */
|
||||
if (ieee80211_vif_is_mesh(&sta->sdata->vif) &&
|
||||
@@ -799,12 +802,12 @@ static void __ieee80211_tx_status(struct
|
||||
ieee80211_handle_filtered_frame(local, sta, skb);
|
||||
return;
|
||||
} else {
|
||||
- if (!acked)
|
||||
+ if (!acked && !noack_success)
|
||||
sta->status_stats.retry_failed++;
|
||||
sta->status_stats.retry_count += retry_count;
|
||||
|
||||
if (ieee80211_is_data_present(fc)) {
|
||||
- if (!acked)
|
||||
+ if (!acked && !noack_success)
|
||||
sta->status_stats.msdu_failed[tid]++;
|
||||
|
||||
sta->status_stats.msdu_retries[tid] +=
|
||||
@@ -825,7 +828,7 @@ static void __ieee80211_tx_status(struct
|
||||
acked, info->status.tx_time);
|
||||
|
||||
if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
|
||||
- if (info->flags & IEEE80211_TX_STAT_ACK) {
|
||||
+ if (acked) {
|
||||
if (sta->status_stats.lost_packets)
|
||||
sta->status_stats.lost_packets = 0;
|
||||
|
||||
@@ -833,6 +836,8 @@ static void __ieee80211_tx_status(struct
|
||||
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH))
|
||||
sta->status_stats.last_tdls_pkt_time =
|
||||
jiffies;
|
||||
+ } else if (noack_success) {
|
||||
+ /* nothing to do here, do not account as lost */
|
||||
} else {
|
||||
ieee80211_lost_packet(sta, info);
|
||||
}
|
||||
@@ -958,7 +963,7 @@ void ieee80211_tx_status_ext(struct ieee
|
||||
|
||||
sta = container_of(pubsta, struct sta_info, sta);
|
||||
|
||||
- if (!acked)
|
||||
+ if (!acked && !noack_success)
|
||||
sta->status_stats.retry_failed++;
|
||||
sta->status_stats.retry_count += retry_count;
|
||||
|
||||
@@ -973,6 +978,8 @@ void ieee80211_tx_status_ext(struct ieee
|
||||
sta->status_stats.last_tdls_pkt_time = jiffies;
|
||||
} else if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
|
||||
return;
|
||||
+ } else if (noack_success) {
|
||||
+ /* nothing to do here, do not account as lost */
|
||||
} else {
|
||||
ieee80211_lost_packet(sta, info);
|
||||
}
|
||||
@@ -0,0 +1,575 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sat, 10 Feb 2018 12:43:30 +0100
|
||||
Subject: [PATCH] mac80211: minstrel: merge with minstrel_ht, always enable
|
||||
VHT support
|
||||
|
||||
Legacy-only devices are not very common and the overhead of the extra
|
||||
code for HT and VHT rates is not big enough to justify all those extra
|
||||
lines of code to make it optional.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/Kconfig
|
||||
+++ b/net/mac80211/Kconfig
|
||||
@@ -25,20 +25,6 @@ config MAC80211_RC_MINSTREL
|
||||
---help---
|
||||
This option enables the 'minstrel' TX rate control algorithm
|
||||
|
||||
-config MAC80211_RC_MINSTREL_HT
|
||||
- bool "Minstrel 802.11n support" if EXPERT
|
||||
- depends on MAC80211_RC_MINSTREL
|
||||
- default y
|
||||
- ---help---
|
||||
- This option enables the 'minstrel_ht' TX rate control algorithm
|
||||
-
|
||||
-config MAC80211_RC_MINSTREL_VHT
|
||||
- bool "Minstrel 802.11ac support" if EXPERT
|
||||
- depends on MAC80211_RC_MINSTREL_HT
|
||||
- default n
|
||||
- ---help---
|
||||
- This option enables VHT in the 'minstrel_ht' TX rate control algorithm
|
||||
-
|
||||
choice
|
||||
prompt "Default rate control algorithm"
|
||||
depends on MAC80211_HAS_RC
|
||||
@@ -60,8 +46,7 @@ endchoice
|
||||
|
||||
config MAC80211_RC_DEFAULT
|
||||
string
|
||||
- default "minstrel_ht" if MAC80211_RC_DEFAULT_MINSTREL && MAC80211_RC_MINSTREL_HT
|
||||
- default "minstrel" if MAC80211_RC_DEFAULT_MINSTREL
|
||||
+ default "minstrel_ht" if MAC80211_RC_DEFAULT_MINSTREL
|
||||
default ""
|
||||
|
||||
endif
|
||||
--- a/net/mac80211/Makefile
|
||||
+++ b/net/mac80211/Makefile
|
||||
@@ -52,13 +52,14 @@ mac80211-$(CONFIG_PM) += pm.o
|
||||
|
||||
CFLAGS_trace.o := -I$(src)
|
||||
|
||||
-rc80211_minstrel-y := rc80211_minstrel.o
|
||||
-rc80211_minstrel-$(CPTCFG_MAC80211_DEBUGFS) += rc80211_minstrel_debugfs.o
|
||||
+rc80211_minstrel-y := \
|
||||
+ rc80211_minstrel.o \
|
||||
+ rc80211_minstrel_ht.o
|
||||
|
||||
-rc80211_minstrel_ht-y := rc80211_minstrel_ht.o
|
||||
-rc80211_minstrel_ht-$(CPTCFG_MAC80211_DEBUGFS) += rc80211_minstrel_ht_debugfs.o
|
||||
+rc80211_minstrel-$(CPTCFG_MAC80211_DEBUGFS) += \
|
||||
+ rc80211_minstrel_debugfs.o \
|
||||
+ rc80211_minstrel_ht_debugfs.o
|
||||
|
||||
mac80211-$(CPTCFG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y)
|
||||
-mac80211-$(CPTCFG_MAC80211_RC_MINSTREL_HT) += $(rc80211_minstrel_ht-y)
|
||||
|
||||
ccflags-y += -DDEBUG
|
||||
--- a/net/mac80211/main.c
|
||||
+++ b/net/mac80211/main.c
|
||||
@@ -1308,18 +1308,12 @@ static int __init ieee80211_init(void)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- ret = rc80211_minstrel_ht_init();
|
||||
- if (ret)
|
||||
- goto err_minstrel;
|
||||
-
|
||||
ret = ieee80211_iface_init();
|
||||
if (ret)
|
||||
goto err_netdev;
|
||||
|
||||
return 0;
|
||||
err_netdev:
|
||||
- rc80211_minstrel_ht_exit();
|
||||
- err_minstrel:
|
||||
rc80211_minstrel_exit();
|
||||
|
||||
return ret;
|
||||
@@ -1327,7 +1321,6 @@ static int __init ieee80211_init(void)
|
||||
|
||||
static void __exit ieee80211_exit(void)
|
||||
{
|
||||
- rc80211_minstrel_ht_exit();
|
||||
rc80211_minstrel_exit();
|
||||
|
||||
ieee80211s_stop();
|
||||
--- a/net/mac80211/rate.h
|
||||
+++ b/net/mac80211/rate.h
|
||||
@@ -95,18 +95,5 @@ static inline void rc80211_minstrel_exit
|
||||
}
|
||||
#endif
|
||||
|
||||
-#ifdef CPTCFG_MAC80211_RC_MINSTREL_HT
|
||||
-int rc80211_minstrel_ht_init(void);
|
||||
-void rc80211_minstrel_ht_exit(void);
|
||||
-#else
|
||||
-static inline int rc80211_minstrel_ht_init(void)
|
||||
-{
|
||||
- return 0;
|
||||
-}
|
||||
-static inline void rc80211_minstrel_ht_exit(void)
|
||||
-{
|
||||
-}
|
||||
-#endif
|
||||
-
|
||||
|
||||
#endif /* IEEE80211_RATE_H */
|
||||
--- a/net/mac80211/rc80211_minstrel.c
|
||||
+++ b/net/mac80211/rc80211_minstrel.c
|
||||
@@ -555,138 +555,6 @@ minstrel_rate_init(void *priv, struct ie
|
||||
minstrel_update_rates(mp, mi);
|
||||
}
|
||||
|
||||
-static void *
|
||||
-minstrel_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp)
|
||||
-{
|
||||
- struct ieee80211_supported_band *sband;
|
||||
- struct minstrel_sta_info *mi;
|
||||
- struct minstrel_priv *mp = priv;
|
||||
- struct ieee80211_hw *hw = mp->hw;
|
||||
- int max_rates = 0;
|
||||
- int i;
|
||||
-
|
||||
- mi = kzalloc(sizeof(struct minstrel_sta_info), gfp);
|
||||
- if (!mi)
|
||||
- return NULL;
|
||||
-
|
||||
- for (i = 0; i < NUM_NL80211_BANDS; i++) {
|
||||
- sband = hw->wiphy->bands[i];
|
||||
- if (sband && sband->n_bitrates > max_rates)
|
||||
- max_rates = sband->n_bitrates;
|
||||
- }
|
||||
-
|
||||
- mi->r = kcalloc(max_rates, sizeof(struct minstrel_rate), gfp);
|
||||
- if (!mi->r)
|
||||
- goto error;
|
||||
-
|
||||
- mi->sample_table = kmalloc_array(max_rates, SAMPLE_COLUMNS, gfp);
|
||||
- if (!mi->sample_table)
|
||||
- goto error1;
|
||||
-
|
||||
- mi->last_stats_update = jiffies;
|
||||
- return mi;
|
||||
-
|
||||
-error1:
|
||||
- kfree(mi->r);
|
||||
-error:
|
||||
- kfree(mi);
|
||||
- return NULL;
|
||||
-}
|
||||
-
|
||||
-static void
|
||||
-minstrel_free_sta(void *priv, struct ieee80211_sta *sta, void *priv_sta)
|
||||
-{
|
||||
- struct minstrel_sta_info *mi = priv_sta;
|
||||
-
|
||||
- kfree(mi->sample_table);
|
||||
- kfree(mi->r);
|
||||
- kfree(mi);
|
||||
-}
|
||||
-
|
||||
-static void
|
||||
-minstrel_init_cck_rates(struct minstrel_priv *mp)
|
||||
-{
|
||||
- static const int bitrates[4] = { 10, 20, 55, 110 };
|
||||
- struct ieee80211_supported_band *sband;
|
||||
- u32 rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef);
|
||||
- int i, j;
|
||||
-
|
||||
- sband = mp->hw->wiphy->bands[NL80211_BAND_2GHZ];
|
||||
- if (!sband)
|
||||
- return;
|
||||
-
|
||||
- for (i = 0, j = 0; i < sband->n_bitrates; i++) {
|
||||
- struct ieee80211_rate *rate = &sband->bitrates[i];
|
||||
-
|
||||
- if (rate->flags & IEEE80211_RATE_ERP_G)
|
||||
- continue;
|
||||
-
|
||||
- if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
|
||||
- continue;
|
||||
-
|
||||
- for (j = 0; j < ARRAY_SIZE(bitrates); j++) {
|
||||
- if (rate->bitrate != bitrates[j])
|
||||
- continue;
|
||||
-
|
||||
- mp->cck_rates[j] = i;
|
||||
- break;
|
||||
- }
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-static void *
|
||||
-minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
|
||||
-{
|
||||
- struct minstrel_priv *mp;
|
||||
-
|
||||
- mp = kzalloc(sizeof(struct minstrel_priv), GFP_ATOMIC);
|
||||
- if (!mp)
|
||||
- return NULL;
|
||||
-
|
||||
- /* contention window settings
|
||||
- * Just an approximation. Using the per-queue values would complicate
|
||||
- * the calculations and is probably unnecessary */
|
||||
- mp->cw_min = 15;
|
||||
- mp->cw_max = 1023;
|
||||
-
|
||||
- /* number of packets (in %) to use for sampling other rates
|
||||
- * sample less often for non-mrr packets, because the overhead
|
||||
- * is much higher than with mrr */
|
||||
- mp->lookaround_rate = 5;
|
||||
- mp->lookaround_rate_mrr = 10;
|
||||
-
|
||||
- /* maximum time that the hw is allowed to stay in one MRR segment */
|
||||
- mp->segment_size = 6000;
|
||||
-
|
||||
- if (hw->max_rate_tries > 0)
|
||||
- mp->max_retry = hw->max_rate_tries;
|
||||
- else
|
||||
- /* safe default, does not necessarily have to match hw properties */
|
||||
- mp->max_retry = 7;
|
||||
-
|
||||
- if (hw->max_rates >= 4)
|
||||
- mp->has_mrr = true;
|
||||
-
|
||||
- mp->hw = hw;
|
||||
- mp->update_interval = 100;
|
||||
-
|
||||
-#ifdef CPTCFG_MAC80211_DEBUGFS
|
||||
- mp->fixed_rate_idx = (u32) -1;
|
||||
- debugfs_create_u32("fixed_rate_idx", S_IRUGO | S_IWUGO, debugfsdir,
|
||||
- &mp->fixed_rate_idx);
|
||||
-#endif
|
||||
-
|
||||
- minstrel_init_cck_rates(mp);
|
||||
-
|
||||
- return mp;
|
||||
-}
|
||||
-
|
||||
-static void
|
||||
-minstrel_free(void *priv)
|
||||
-{
|
||||
- kfree(priv);
|
||||
-}
|
||||
-
|
||||
static u32 minstrel_get_expected_throughput(void *priv_sta)
|
||||
{
|
||||
struct minstrel_sta_info *mi = priv_sta;
|
||||
@@ -705,28 +573,8 @@ static u32 minstrel_get_expected_through
|
||||
}
|
||||
|
||||
const struct rate_control_ops mac80211_minstrel = {
|
||||
- .name = "minstrel",
|
||||
.tx_status_ext = minstrel_tx_status,
|
||||
.get_rate = minstrel_get_rate,
|
||||
.rate_init = minstrel_rate_init,
|
||||
- .alloc = minstrel_alloc,
|
||||
- .free = minstrel_free,
|
||||
- .alloc_sta = minstrel_alloc_sta,
|
||||
- .free_sta = minstrel_free_sta,
|
||||
-#ifdef CPTCFG_MAC80211_DEBUGFS
|
||||
- .add_sta_debugfs = minstrel_add_sta_debugfs,
|
||||
-#endif
|
||||
.get_expected_throughput = minstrel_get_expected_throughput,
|
||||
};
|
||||
-
|
||||
-int __init
|
||||
-rc80211_minstrel_init(void)
|
||||
-{
|
||||
- return ieee80211_rate_control_register(&mac80211_minstrel);
|
||||
-}
|
||||
-
|
||||
-void
|
||||
-rc80211_minstrel_exit(void)
|
||||
-{
|
||||
- ieee80211_rate_control_unregister(&mac80211_minstrel);
|
||||
-}
|
||||
--- a/net/mac80211/rc80211_minstrel.h
|
||||
+++ b/net/mac80211/rc80211_minstrel.h
|
||||
@@ -157,7 +157,5 @@ int minstrel_get_tp_avg(struct minstrel_
|
||||
/* 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);
|
||||
|
||||
#endif
|
||||
--- a/net/mac80211/rc80211_minstrel_debugfs.c
|
||||
+++ b/net/mac80211/rc80211_minstrel_debugfs.c
|
||||
@@ -54,22 +54,6 @@
|
||||
#include <net/mac80211.h>
|
||||
#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)
|
||||
{
|
||||
@@ -135,14 +119,6 @@ minstrel_stats_open(struct inode *inode,
|
||||
return 0;
|
||||
}
|
||||
|
||||
-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)
|
||||
{
|
||||
@@ -200,21 +176,3 @@ minstrel_stats_csv_open(struct inode *in
|
||||
|
||||
return 0;
|
||||
}
|
||||
-
|
||||
-static const struct file_operations minstrel_stat_csv_fops = {
|
||||
- .owner = THIS_MODULE,
|
||||
- .open = minstrel_stats_csv_open,
|
||||
- .read = minstrel_stats_read,
|
||||
- .release = minstrel_stats_release,
|
||||
- .llseek = default_llseek,
|
||||
-};
|
||||
-
|
||||
-void
|
||||
-minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir)
|
||||
-{
|
||||
- struct minstrel_sta_info *mi = priv_sta;
|
||||
-
|
||||
- debugfs_create_file("rc_stats", S_IRUGO, dir, mi, &minstrel_stat_fops);
|
||||
- debugfs_create_file("rc_stats_csv", S_IRUGO, dir, mi,
|
||||
- &minstrel_stat_csv_fops);
|
||||
-}
|
||||
--- a/net/mac80211/rc80211_minstrel_ht.c
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht.c
|
||||
@@ -137,12 +137,10 @@
|
||||
} \
|
||||
}
|
||||
|
||||
-#ifdef CPTCFG_MAC80211_RC_MINSTREL_VHT
|
||||
static bool minstrel_vht_only = true;
|
||||
module_param(minstrel_vht_only, bool, 0644);
|
||||
MODULE_PARM_DESC(minstrel_vht_only,
|
||||
"Use only VHT rates when VHT is supported by sta.");
|
||||
-#endif
|
||||
|
||||
/*
|
||||
* To enable sufficiently targeted rate sampling, MCS rates are divided into
|
||||
@@ -171,7 +169,6 @@ const struct mcs_group minstrel_mcs_grou
|
||||
|
||||
CCK_GROUP,
|
||||
|
||||
-#ifdef CPTCFG_MAC80211_RC_MINSTREL_VHT
|
||||
VHT_GROUP(1, 0, BW_20),
|
||||
VHT_GROUP(2, 0, BW_20),
|
||||
VHT_GROUP(3, 0, BW_20),
|
||||
@@ -195,7 +192,6 @@ const struct mcs_group minstrel_mcs_grou
|
||||
VHT_GROUP(1, 1, BW_80),
|
||||
VHT_GROUP(2, 1, BW_80),
|
||||
VHT_GROUP(3, 1, BW_80),
|
||||
-#endif
|
||||
};
|
||||
|
||||
static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES] __read_mostly;
|
||||
@@ -1151,12 +1147,10 @@ minstrel_ht_update_caps(void *priv, stru
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) != MINSTREL_GROUPS_NB);
|
||||
|
||||
-#ifdef CPTCFG_MAC80211_RC_MINSTREL_VHT
|
||||
if (vht_cap->vht_supported)
|
||||
use_vht = vht_cap->vht_mcs.tx_mcs_map != cpu_to_le16(~0);
|
||||
else
|
||||
-#endif
|
||||
- use_vht = 0;
|
||||
+ use_vht = 0;
|
||||
|
||||
msp->is_ht = true;
|
||||
memset(mi, 0, sizeof(*mi));
|
||||
@@ -1231,10 +1225,9 @@ minstrel_ht_update_caps(void *priv, stru
|
||||
|
||||
/* HT rate */
|
||||
if (gflags & IEEE80211_TX_RC_MCS) {
|
||||
-#ifdef CPTCFG_MAC80211_RC_MINSTREL_VHT
|
||||
if (use_vht && minstrel_vht_only)
|
||||
continue;
|
||||
-#endif
|
||||
+
|
||||
mi->supported[i] = mcs->rx_mask[nss - 1];
|
||||
if (mi->supported[i])
|
||||
n_supported++;
|
||||
@@ -1353,16 +1346,88 @@ minstrel_ht_free_sta(void *priv, struct
|
||||
kfree(msp);
|
||||
}
|
||||
|
||||
+static void
|
||||
+minstrel_ht_init_cck_rates(struct minstrel_priv *mp)
|
||||
+{
|
||||
+ static const int bitrates[4] = { 10, 20, 55, 110 };
|
||||
+ struct ieee80211_supported_band *sband;
|
||||
+ u32 rate_flags = ieee80211_chandef_rate_flags(&mp->hw->conf.chandef);
|
||||
+ int i, j;
|
||||
+
|
||||
+ sband = mp->hw->wiphy->bands[NL80211_BAND_2GHZ];
|
||||
+ if (!sband)
|
||||
+ return;
|
||||
+
|
||||
+ for (i = 0, j = 0; i < sband->n_bitrates; i++) {
|
||||
+ struct ieee80211_rate *rate = &sband->bitrates[i];
|
||||
+
|
||||
+ if (rate->flags & IEEE80211_RATE_ERP_G)
|
||||
+ continue;
|
||||
+
|
||||
+ if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
|
||||
+ continue;
|
||||
+
|
||||
+ for (j = 0; j < ARRAY_SIZE(bitrates); j++) {
|
||||
+ if (rate->bitrate != bitrates[j])
|
||||
+ continue;
|
||||
+
|
||||
+ mp->cck_rates[j] = i;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
static void *
|
||||
minstrel_ht_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
|
||||
{
|
||||
- return mac80211_minstrel.alloc(hw, debugfsdir);
|
||||
+ struct minstrel_priv *mp;
|
||||
+
|
||||
+ mp = kzalloc(sizeof(struct minstrel_priv), GFP_ATOMIC);
|
||||
+ if (!mp)
|
||||
+ return NULL;
|
||||
+
|
||||
+ /* contention window settings
|
||||
+ * Just an approximation. Using the per-queue values would complicate
|
||||
+ * the calculations and is probably unnecessary */
|
||||
+ mp->cw_min = 15;
|
||||
+ mp->cw_max = 1023;
|
||||
+
|
||||
+ /* number of packets (in %) to use for sampling other rates
|
||||
+ * sample less often for non-mrr packets, because the overhead
|
||||
+ * is much higher than with mrr */
|
||||
+ mp->lookaround_rate = 5;
|
||||
+ mp->lookaround_rate_mrr = 10;
|
||||
+
|
||||
+ /* maximum time that the hw is allowed to stay in one MRR segment */
|
||||
+ mp->segment_size = 6000;
|
||||
+
|
||||
+ if (hw->max_rate_tries > 0)
|
||||
+ mp->max_retry = hw->max_rate_tries;
|
||||
+ else
|
||||
+ /* safe default, does not necessarily have to match hw properties */
|
||||
+ mp->max_retry = 7;
|
||||
+
|
||||
+ if (hw->max_rates >= 4)
|
||||
+ mp->has_mrr = true;
|
||||
+
|
||||
+ mp->hw = hw;
|
||||
+ mp->update_interval = 100;
|
||||
+
|
||||
+#ifdef CPTCFG_MAC80211_DEBUGFS
|
||||
+ mp->fixed_rate_idx = (u32) -1;
|
||||
+ debugfs_create_u32("fixed_rate_idx", S_IRUGO | S_IWUGO, debugfsdir,
|
||||
+ &mp->fixed_rate_idx);
|
||||
+#endif
|
||||
+
|
||||
+ minstrel_ht_init_cck_rates(mp);
|
||||
+
|
||||
+ return mp;
|
||||
}
|
||||
|
||||
static void
|
||||
minstrel_ht_free(void *priv)
|
||||
{
|
||||
- mac80211_minstrel.free(priv);
|
||||
+ kfree(priv);
|
||||
}
|
||||
|
||||
static u32 minstrel_ht_get_expected_throughput(void *priv_sta)
|
||||
@@ -1421,14 +1486,14 @@ static void __init init_sample_table(voi
|
||||
}
|
||||
|
||||
int __init
|
||||
-rc80211_minstrel_ht_init(void)
|
||||
+rc80211_minstrel_init(void)
|
||||
{
|
||||
init_sample_table();
|
||||
return ieee80211_rate_control_register(&mac80211_minstrel_ht);
|
||||
}
|
||||
|
||||
void
|
||||
-rc80211_minstrel_ht_exit(void)
|
||||
+rc80211_minstrel_exit(void)
|
||||
{
|
||||
ieee80211_rate_control_unregister(&mac80211_minstrel_ht);
|
||||
}
|
||||
--- a/net/mac80211/rc80211_minstrel_ht.h
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht.h
|
||||
@@ -15,11 +15,7 @@
|
||||
*/
|
||||
#define MINSTREL_MAX_STREAMS 3
|
||||
#define MINSTREL_HT_STREAM_GROUPS 4 /* BW(=2) * SGI(=2) */
|
||||
-#ifdef CPTCFG_MAC80211_RC_MINSTREL_VHT
|
||||
#define MINSTREL_VHT_STREAM_GROUPS 6 /* BW(=3) * SGI(=2) */
|
||||
-#else
|
||||
-#define MINSTREL_VHT_STREAM_GROUPS 0
|
||||
-#endif
|
||||
|
||||
#define MINSTREL_HT_GROUPS_NB (MINSTREL_MAX_STREAMS * \
|
||||
MINSTREL_HT_STREAM_GROUPS)
|
||||
@@ -34,11 +30,7 @@
|
||||
#define MINSTREL_CCK_GROUP (MINSTREL_HT_GROUP_0 + MINSTREL_HT_GROUPS_NB)
|
||||
#define MINSTREL_VHT_GROUP_0 (MINSTREL_CCK_GROUP + 1)
|
||||
|
||||
-#ifdef CPTCFG_MAC80211_RC_MINSTREL_VHT
|
||||
#define MCS_GROUP_RATES 10
|
||||
-#else
|
||||
-#define MCS_GROUP_RATES 8
|
||||
-#endif
|
||||
|
||||
struct mcs_group {
|
||||
u32 flags;
|
||||
--- a/net/mac80211/rc80211_minstrel_ht_debugfs.c
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c
|
||||
@@ -15,6 +15,22 @@
|
||||
#include "rc80211_minstrel.h"
|
||||
#include "rc80211_minstrel_ht.h"
|
||||
|
||||
+static 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);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+minstrel_stats_release(struct inode *inode, struct file *file)
|
||||
+{
|
||||
+ kfree(file->private_data);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static char *
|
||||
minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
|
||||
{
|
||||
@@ -0,0 +1,368 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sat, 10 Feb 2018 12:45:47 +0100
|
||||
Subject: [PATCH] mac80211: minstrel: reduce minstrel_mcs_groups size
|
||||
|
||||
By storing a shift value for all duration values of a group, we can
|
||||
reduce precision by a neglegible amount to make it fit into a u16 value.
|
||||
This improves cache footprint and reduces size:
|
||||
|
||||
Before:
|
||||
text data bss dec hex filename
|
||||
10024 116 0 10140 279c rc80211_minstrel_ht.o
|
||||
|
||||
After:
|
||||
text data bss dec hex filename
|
||||
9368 116 0 9484 250c rc80211_minstrel_ht.o
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/rc80211_minstrel_ht.c
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht.c
|
||||
@@ -52,22 +52,23 @@
|
||||
_streams - 1
|
||||
|
||||
/* MCS rate information for an MCS group */
|
||||
-#define MCS_GROUP(_streams, _sgi, _ht40) \
|
||||
+#define MCS_GROUP(_streams, _sgi, _ht40, _s) \
|
||||
[GROUP_IDX(_streams, _sgi, _ht40)] = { \
|
||||
.streams = _streams, \
|
||||
+ .shift = _s, \
|
||||
.flags = \
|
||||
IEEE80211_TX_RC_MCS | \
|
||||
(_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) | \
|
||||
(_ht40 ? IEEE80211_TX_RC_40_MHZ_WIDTH : 0), \
|
||||
.duration = { \
|
||||
- MCS_DURATION(_streams, _sgi, _ht40 ? 54 : 26), \
|
||||
- MCS_DURATION(_streams, _sgi, _ht40 ? 108 : 52), \
|
||||
- MCS_DURATION(_streams, _sgi, _ht40 ? 162 : 78), \
|
||||
- MCS_DURATION(_streams, _sgi, _ht40 ? 216 : 104), \
|
||||
- MCS_DURATION(_streams, _sgi, _ht40 ? 324 : 156), \
|
||||
- MCS_DURATION(_streams, _sgi, _ht40 ? 432 : 208), \
|
||||
- MCS_DURATION(_streams, _sgi, _ht40 ? 486 : 234), \
|
||||
- MCS_DURATION(_streams, _sgi, _ht40 ? 540 : 260) \
|
||||
+ MCS_DURATION(_streams, _sgi, _ht40 ? 54 : 26) >> _s, \
|
||||
+ MCS_DURATION(_streams, _sgi, _ht40 ? 108 : 52) >> _s, \
|
||||
+ MCS_DURATION(_streams, _sgi, _ht40 ? 162 : 78) >> _s, \
|
||||
+ MCS_DURATION(_streams, _sgi, _ht40 ? 216 : 104) >> _s, \
|
||||
+ MCS_DURATION(_streams, _sgi, _ht40 ? 324 : 156) >> _s, \
|
||||
+ MCS_DURATION(_streams, _sgi, _ht40 ? 432 : 208) >> _s, \
|
||||
+ MCS_DURATION(_streams, _sgi, _ht40 ? 486 : 234) >> _s, \
|
||||
+ MCS_DURATION(_streams, _sgi, _ht40 ? 540 : 260) >> _s \
|
||||
} \
|
||||
}
|
||||
|
||||
@@ -80,9 +81,10 @@
|
||||
#define BW2VBPS(_bw, r3, r2, r1) \
|
||||
(_bw == BW_80 ? r3 : _bw == BW_40 ? r2 : r1)
|
||||
|
||||
-#define VHT_GROUP(_streams, _sgi, _bw) \
|
||||
+#define VHT_GROUP(_streams, _sgi, _bw, _s) \
|
||||
[VHT_GROUP_IDX(_streams, _sgi, _bw)] = { \
|
||||
.streams = _streams, \
|
||||
+ .shift = _s, \
|
||||
.flags = \
|
||||
IEEE80211_TX_RC_VHT_MCS | \
|
||||
(_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) | \
|
||||
@@ -90,25 +92,25 @@
|
||||
_bw == BW_40 ? IEEE80211_TX_RC_40_MHZ_WIDTH : 0), \
|
||||
.duration = { \
|
||||
MCS_DURATION(_streams, _sgi, \
|
||||
- BW2VBPS(_bw, 117, 54, 26)), \
|
||||
+ BW2VBPS(_bw, 117, 54, 26)) >> _s, \
|
||||
MCS_DURATION(_streams, _sgi, \
|
||||
- BW2VBPS(_bw, 234, 108, 52)), \
|
||||
+ BW2VBPS(_bw, 234, 108, 52)) >> _s, \
|
||||
MCS_DURATION(_streams, _sgi, \
|
||||
- BW2VBPS(_bw, 351, 162, 78)), \
|
||||
+ BW2VBPS(_bw, 351, 162, 78)) >> _s, \
|
||||
MCS_DURATION(_streams, _sgi, \
|
||||
- BW2VBPS(_bw, 468, 216, 104)), \
|
||||
+ BW2VBPS(_bw, 468, 216, 104)) >> _s, \
|
||||
MCS_DURATION(_streams, _sgi, \
|
||||
- BW2VBPS(_bw, 702, 324, 156)), \
|
||||
+ BW2VBPS(_bw, 702, 324, 156)) >> _s, \
|
||||
MCS_DURATION(_streams, _sgi, \
|
||||
- BW2VBPS(_bw, 936, 432, 208)), \
|
||||
+ BW2VBPS(_bw, 936, 432, 208)) >> _s, \
|
||||
MCS_DURATION(_streams, _sgi, \
|
||||
- BW2VBPS(_bw, 1053, 486, 234)), \
|
||||
+ BW2VBPS(_bw, 1053, 486, 234)) >> _s, \
|
||||
MCS_DURATION(_streams, _sgi, \
|
||||
- BW2VBPS(_bw, 1170, 540, 260)), \
|
||||
+ BW2VBPS(_bw, 1170, 540, 260)) >> _s, \
|
||||
MCS_DURATION(_streams, _sgi, \
|
||||
- BW2VBPS(_bw, 1404, 648, 312)), \
|
||||
+ BW2VBPS(_bw, 1404, 648, 312)) >> _s, \
|
||||
MCS_DURATION(_streams, _sgi, \
|
||||
- BW2VBPS(_bw, 1560, 720, 346)) \
|
||||
+ BW2VBPS(_bw, 1560, 720, 346)) >> _s \
|
||||
} \
|
||||
}
|
||||
|
||||
@@ -121,19 +123,20 @@
|
||||
(CCK_DURATION((_bitrate > 10 ? 20 : 10), false, 60) + \
|
||||
CCK_DURATION(_bitrate, _short, AVG_PKT_SIZE))
|
||||
|
||||
-#define CCK_DURATION_LIST(_short) \
|
||||
- CCK_ACK_DURATION(10, _short), \
|
||||
- CCK_ACK_DURATION(20, _short), \
|
||||
- CCK_ACK_DURATION(55, _short), \
|
||||
- CCK_ACK_DURATION(110, _short)
|
||||
+#define CCK_DURATION_LIST(_short, _s) \
|
||||
+ CCK_ACK_DURATION(10, _short) >> _s, \
|
||||
+ CCK_ACK_DURATION(20, _short) >> _s, \
|
||||
+ CCK_ACK_DURATION(55, _short) >> _s, \
|
||||
+ CCK_ACK_DURATION(110, _short) >> _s
|
||||
|
||||
-#define CCK_GROUP \
|
||||
+#define CCK_GROUP(_s) \
|
||||
[MINSTREL_CCK_GROUP] = { \
|
||||
.streams = 1, \
|
||||
.flags = 0, \
|
||||
+ .shift = _s, \
|
||||
.duration = { \
|
||||
- CCK_DURATION_LIST(false), \
|
||||
- CCK_DURATION_LIST(true) \
|
||||
+ CCK_DURATION_LIST(false, _s), \
|
||||
+ CCK_DURATION_LIST(true, _s) \
|
||||
} \
|
||||
}
|
||||
|
||||
@@ -151,47 +154,47 @@ MODULE_PARM_DESC(minstrel_vht_only,
|
||||
* BW -> SGI -> #streams
|
||||
*/
|
||||
const struct mcs_group minstrel_mcs_groups[] = {
|
||||
- MCS_GROUP(1, 0, BW_20),
|
||||
- MCS_GROUP(2, 0, BW_20),
|
||||
- MCS_GROUP(3, 0, BW_20),
|
||||
-
|
||||
- MCS_GROUP(1, 1, BW_20),
|
||||
- MCS_GROUP(2, 1, BW_20),
|
||||
- MCS_GROUP(3, 1, BW_20),
|
||||
-
|
||||
- MCS_GROUP(1, 0, BW_40),
|
||||
- MCS_GROUP(2, 0, BW_40),
|
||||
- MCS_GROUP(3, 0, BW_40),
|
||||
-
|
||||
- MCS_GROUP(1, 1, BW_40),
|
||||
- MCS_GROUP(2, 1, BW_40),
|
||||
- MCS_GROUP(3, 1, BW_40),
|
||||
-
|
||||
- CCK_GROUP,
|
||||
-
|
||||
- VHT_GROUP(1, 0, BW_20),
|
||||
- VHT_GROUP(2, 0, BW_20),
|
||||
- VHT_GROUP(3, 0, BW_20),
|
||||
-
|
||||
- VHT_GROUP(1, 1, BW_20),
|
||||
- VHT_GROUP(2, 1, BW_20),
|
||||
- VHT_GROUP(3, 1, BW_20),
|
||||
-
|
||||
- VHT_GROUP(1, 0, BW_40),
|
||||
- VHT_GROUP(2, 0, BW_40),
|
||||
- VHT_GROUP(3, 0, BW_40),
|
||||
-
|
||||
- VHT_GROUP(1, 1, BW_40),
|
||||
- VHT_GROUP(2, 1, BW_40),
|
||||
- VHT_GROUP(3, 1, BW_40),
|
||||
-
|
||||
- VHT_GROUP(1, 0, BW_80),
|
||||
- VHT_GROUP(2, 0, BW_80),
|
||||
- VHT_GROUP(3, 0, BW_80),
|
||||
-
|
||||
- VHT_GROUP(1, 1, BW_80),
|
||||
- VHT_GROUP(2, 1, BW_80),
|
||||
- VHT_GROUP(3, 1, BW_80),
|
||||
+ MCS_GROUP(1, 0, BW_20, 5),
|
||||
+ MCS_GROUP(2, 0, BW_20, 4),
|
||||
+ MCS_GROUP(3, 0, BW_20, 4),
|
||||
+
|
||||
+ MCS_GROUP(1, 1, BW_20, 5),
|
||||
+ MCS_GROUP(2, 1, BW_20, 4),
|
||||
+ MCS_GROUP(3, 1, BW_20, 4),
|
||||
+
|
||||
+ MCS_GROUP(1, 0, BW_40, 4),
|
||||
+ MCS_GROUP(2, 0, BW_40, 4),
|
||||
+ MCS_GROUP(3, 0, BW_40, 4),
|
||||
+
|
||||
+ MCS_GROUP(1, 1, BW_40, 4),
|
||||
+ MCS_GROUP(2, 1, BW_40, 4),
|
||||
+ MCS_GROUP(3, 1, BW_40, 4),
|
||||
+
|
||||
+ CCK_GROUP(8),
|
||||
+
|
||||
+ VHT_GROUP(1, 0, BW_20, 5),
|
||||
+ VHT_GROUP(2, 0, BW_20, 4),
|
||||
+ VHT_GROUP(3, 0, BW_20, 4),
|
||||
+
|
||||
+ VHT_GROUP(1, 1, BW_20, 5),
|
||||
+ VHT_GROUP(2, 1, BW_20, 4),
|
||||
+ VHT_GROUP(3, 1, BW_20, 4),
|
||||
+
|
||||
+ VHT_GROUP(1, 0, BW_40, 4),
|
||||
+ VHT_GROUP(2, 0, BW_40, 4),
|
||||
+ VHT_GROUP(3, 0, BW_40, 4),
|
||||
+
|
||||
+ VHT_GROUP(1, 1, BW_40, 4),
|
||||
+ VHT_GROUP(2, 1, BW_40, 4),
|
||||
+ VHT_GROUP(3, 1, BW_40, 4),
|
||||
+
|
||||
+ VHT_GROUP(1, 0, BW_80, 4),
|
||||
+ VHT_GROUP(2, 0, BW_80, 4),
|
||||
+ VHT_GROUP(3, 0, BW_80, 4),
|
||||
+
|
||||
+ VHT_GROUP(1, 1, BW_80, 4),
|
||||
+ VHT_GROUP(2, 1, BW_80, 4),
|
||||
+ VHT_GROUP(3, 1, BW_80, 4),
|
||||
};
|
||||
|
||||
static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES] __read_mostly;
|
||||
@@ -308,7 +311,8 @@ minstrel_ht_get_tp_avg(struct minstrel_h
|
||||
if (group != MINSTREL_CCK_GROUP)
|
||||
nsecs = 1000 * mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len);
|
||||
|
||||
- nsecs += minstrel_mcs_groups[group].duration[rate];
|
||||
+ nsecs += minstrel_mcs_groups[group].duration[rate] <<
|
||||
+ minstrel_mcs_groups[group].shift;
|
||||
|
||||
/*
|
||||
* For the throughput calculation, limit the probability value to 90% to
|
||||
@@ -756,12 +760,19 @@ minstrel_ht_tx_status(void *priv, struct
|
||||
minstrel_ht_update_rates(mp, mi);
|
||||
}
|
||||
|
||||
+static inline int
|
||||
+minstrel_get_duration(int index)
|
||||
+{
|
||||
+ const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];
|
||||
+ unsigned int duration = group->duration[index % MCS_GROUP_RATES];
|
||||
+ return duration << group->shift;
|
||||
+}
|
||||
+
|
||||
static void
|
||||
minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
|
||||
int index)
|
||||
{
|
||||
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;
|
||||
unsigned int ctime = 0;
|
||||
@@ -780,8 +791,7 @@ minstrel_calc_retransmit(struct minstrel
|
||||
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;
|
||||
+ tx_time_data = minstrel_get_duration(index) * ampdu_len / 1000;
|
||||
|
||||
/* Contention time for first 2 tries */
|
||||
ctime = (t_slot * cw) >> 1;
|
||||
@@ -875,20 +885,24 @@ minstrel_ht_get_max_amsdu_len(struct min
|
||||
int group = mi->max_prob_rate / MCS_GROUP_RATES;
|
||||
const struct mcs_group *g = &minstrel_mcs_groups[group];
|
||||
int rate = mi->max_prob_rate % MCS_GROUP_RATES;
|
||||
+ unsigned int duration;
|
||||
|
||||
/* Disable A-MSDU if max_prob_rate is bad */
|
||||
if (mi->groups[group].rates[rate].prob_ewma < MINSTREL_FRAC(50, 100))
|
||||
return 1;
|
||||
|
||||
+ duration = g->duration[rate];
|
||||
+ duration <<= g->shift;
|
||||
+
|
||||
/* If the rate is slower than single-stream MCS1, make A-MSDU limit small */
|
||||
- if (g->duration[rate] > MCS_DURATION(1, 0, 52))
|
||||
+ if (duration > MCS_DURATION(1, 0, 52))
|
||||
return 500;
|
||||
|
||||
/*
|
||||
* If the rate is slower than single-stream MCS4, limit A-MSDU to usual
|
||||
* data packet size
|
||||
*/
|
||||
- if (g->duration[rate] > MCS_DURATION(1, 0, 104))
|
||||
+ if (duration > MCS_DURATION(1, 0, 104))
|
||||
return 1600;
|
||||
|
||||
/*
|
||||
@@ -896,7 +910,7 @@ minstrel_ht_get_max_amsdu_len(struct min
|
||||
* rate success probability is less than 75%, limit A-MSDU to twice the usual
|
||||
* data packet size
|
||||
*/
|
||||
- if (g->duration[rate] > MCS_DURATION(1, 0, 260) ||
|
||||
+ if (duration > MCS_DURATION(1, 0, 260) ||
|
||||
(minstrel_ht_get_prob_ewma(mi, mi->max_tp_rate[0]) <
|
||||
MINSTREL_FRAC(75, 100)))
|
||||
return 3200;
|
||||
@@ -943,13 +957,6 @@ minstrel_ht_update_rates(struct minstrel
|
||||
rate_control_set_rates(mp->hw, mi->sta, rates);
|
||||
}
|
||||
|
||||
-static inline int
|
||||
-minstrel_get_duration(int index)
|
||||
-{
|
||||
- const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];
|
||||
- return group->duration[index % MCS_GROUP_RATES];
|
||||
-}
|
||||
-
|
||||
static int
|
||||
minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
|
||||
{
|
||||
--- a/net/mac80211/rc80211_minstrel_ht.h
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht.h
|
||||
@@ -33,9 +33,10 @@
|
||||
#define MCS_GROUP_RATES 10
|
||||
|
||||
struct mcs_group {
|
||||
- u32 flags;
|
||||
- unsigned int streams;
|
||||
- unsigned int duration[MCS_GROUP_RATES];
|
||||
+ u16 flags;
|
||||
+ u8 streams;
|
||||
+ u8 shift;
|
||||
+ u16 duration[MCS_GROUP_RATES];
|
||||
};
|
||||
|
||||
extern const struct mcs_group minstrel_mcs_groups[];
|
||||
--- a/net/mac80211/rc80211_minstrel_ht_debugfs.c
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c
|
||||
@@ -58,6 +58,7 @@ minstrel_ht_stats_dump(struct minstrel_h
|
||||
static const int bitrates[4] = { 10, 20, 55, 110 };
|
||||
int idx = i * MCS_GROUP_RATES + j;
|
||||
unsigned int prob_ewmsd;
|
||||
+ unsigned int duration;
|
||||
|
||||
if (!(mi->supported[i] & BIT(j)))
|
||||
continue;
|
||||
@@ -95,7 +96,9 @@ minstrel_ht_stats_dump(struct minstrel_h
|
||||
p += sprintf(p, " %3u ", idx);
|
||||
|
||||
/* tx_time[rate(i)] in usec */
|
||||
- tx_time = DIV_ROUND_CLOSEST(mg->duration[j], 1000);
|
||||
+ duration = mg->duration[j];
|
||||
+ duration <<= mg->shift;
|
||||
+ tx_time = DIV_ROUND_CLOSEST(duration, 1000);
|
||||
p += sprintf(p, "%6u ", tx_time);
|
||||
|
||||
tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100));
|
||||
@@ -204,6 +207,7 @@ minstrel_ht_stats_csv_dump(struct minstr
|
||||
static const int bitrates[4] = { 10, 20, 55, 110 };
|
||||
int idx = i * MCS_GROUP_RATES + j;
|
||||
unsigned int prob_ewmsd;
|
||||
+ unsigned int duration;
|
||||
|
||||
if (!(mi->supported[i] & BIT(j)))
|
||||
continue;
|
||||
@@ -238,7 +242,10 @@ minstrel_ht_stats_csv_dump(struct minstr
|
||||
}
|
||||
|
||||
p += sprintf(p, "%u,", idx);
|
||||
- tx_time = DIV_ROUND_CLOSEST(mg->duration[j], 1000);
|
||||
+
|
||||
+ duration = mg->duration[j];
|
||||
+ duration <<= mg->shift;
|
||||
+ tx_time = DIV_ROUND_CLOSEST(duration, 1000);
|
||||
p += sprintf(p, "%u,", tx_time);
|
||||
|
||||
tp_max = minstrel_ht_get_tp_avg(mi, i, j, MINSTREL_FRAC(100, 100));
|
||||
@@ -0,0 +1,40 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sat, 3 Mar 2018 18:48:58 +0100
|
||||
Subject: [PATCH] mac80211: minstrel: do not sample rates 3 times slower than
|
||||
max_prob_rate
|
||||
|
||||
These rates are highly unlikely to be used quickly, even if the link
|
||||
deteriorates rapidly. This improves throughput in cases where CCK rates
|
||||
are not reliable enough to be skipped entirely during sampling.
|
||||
Sampling these rates regularly can cost a lot of airtime.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/rc80211_minstrel_ht.c
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht.c
|
||||
@@ -1004,10 +1004,13 @@ minstrel_get_sample_rate(struct minstrel
|
||||
return -1;
|
||||
|
||||
/*
|
||||
- * Do not sample if the probability is already higher than 95%
|
||||
- * to avoid wasting airtime.
|
||||
+ * Do not sample if the probability is already higher than 95%,
|
||||
+ * or if the rate is 3 times slower than the current max probability
|
||||
+ * rate, to avoid wasting airtime.
|
||||
*/
|
||||
- if (mrs->prob_ewma > MINSTREL_FRAC(95, 100))
|
||||
+ sample_dur = minstrel_get_duration(sample_idx);
|
||||
+ if (mrs->prob_ewma > MINSTREL_FRAC(95, 100) ||
|
||||
+ minstrel_get_duration(mi->max_prob_rate) * 3 < sample_dur)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
@@ -1017,7 +1020,6 @@ minstrel_get_sample_rate(struct minstrel
|
||||
|
||||
cur_max_tp_streams = minstrel_mcs_groups[tp_rate1 /
|
||||
MCS_GROUP_RATES].streams;
|
||||
- sample_dur = minstrel_get_duration(sample_idx);
|
||||
if (sample_dur >= minstrel_get_duration(tp_rate2) &&
|
||||
(cur_max_tp_streams - 1 <
|
||||
minstrel_mcs_groups[sample_group].streams ||
|
||||
@@ -0,0 +1,122 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Wed, 16 Jan 2019 22:32:12 +0100
|
||||
Subject: [PATCH] mac80211: minstrel_ht: add flag to indicate
|
||||
missing/inaccurate tx A-MPDU length
|
||||
|
||||
Some hardware (e.g. MediaTek MT7603) cannot report A-MPDU length in tx status
|
||||
information. Add support for a flag to indicate that, to allow minstrel_ht
|
||||
to use a fixed value in its internal calculation (which gives better results
|
||||
than just defaulting to 1).
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/include/net/mac80211.h
|
||||
+++ b/include/net/mac80211.h
|
||||
@@ -2131,6 +2131,9 @@ struct ieee80211_txq {
|
||||
* @IEEE80211_HW_DOESNT_SUPPORT_QOS_NDP: The driver (or firmware) doesn't
|
||||
* support QoS NDP for AP probing - that's most likely a driver bug.
|
||||
*
|
||||
+ * @IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN: Driver does not report accurate A-MPDU
|
||||
+ * length in tx status information
|
||||
+ *
|
||||
* @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
|
||||
*/
|
||||
enum ieee80211_hw_flags {
|
||||
@@ -2176,6 +2179,7 @@ enum ieee80211_hw_flags {
|
||||
IEEE80211_HW_SUPPORTS_TDLS_BUFFER_STA,
|
||||
IEEE80211_HW_DEAUTH_NEED_MGD_TX_PREP,
|
||||
IEEE80211_HW_DOESNT_SUPPORT_QOS_NDP,
|
||||
+ IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN,
|
||||
|
||||
/* keep last, obviously */
|
||||
NUM_IEEE80211_HW_FLAGS
|
||||
--- a/net/mac80211/debugfs.c
|
||||
+++ b/net/mac80211/debugfs.c
|
||||
@@ -214,6 +214,7 @@ static const char *hw_flag_names[] = {
|
||||
FLAG(SUPPORTS_TDLS_BUFFER_STA),
|
||||
FLAG(DEAUTH_NEED_MGD_TX_PREP),
|
||||
FLAG(DOESNT_SUPPORT_QOS_NDP),
|
||||
+ FLAG(TX_STATUS_NO_AMPDU_LEN),
|
||||
#undef FLAG
|
||||
};
|
||||
|
||||
--- a/net/mac80211/rc80211_minstrel_ht.c
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht.c
|
||||
@@ -294,6 +294,15 @@ minstrel_get_ratestats(struct minstrel_h
|
||||
return &mi->groups[index / MCS_GROUP_RATES].rates[index % MCS_GROUP_RATES];
|
||||
}
|
||||
|
||||
+static unsigned int
|
||||
+minstrel_ht_avg_ampdu_len(struct minstrel_ht_sta *mi)
|
||||
+{
|
||||
+ if (!mi->avg_ampdu_len)
|
||||
+ return AVG_AMPDU_SIZE;
|
||||
+
|
||||
+ return MINSTREL_TRUNC(mi->avg_ampdu_len);
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* Return current throughput based on the average A-MPDU length, taking into
|
||||
* account the expected number of retransmissions and their expected length
|
||||
@@ -309,7 +318,7 @@ minstrel_ht_get_tp_avg(struct minstrel_h
|
||||
return 0;
|
||||
|
||||
if (group != MINSTREL_CCK_GROUP)
|
||||
- nsecs = 1000 * mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len);
|
||||
+ nsecs = 1000 * mi->overhead / minstrel_ht_avg_ampdu_len(mi);
|
||||
|
||||
nsecs += minstrel_mcs_groups[group].duration[rate] <<
|
||||
minstrel_mcs_groups[group].shift;
|
||||
@@ -503,8 +512,12 @@ minstrel_ht_update_stats(struct minstrel
|
||||
u16 tmp_cck_tp_rate[MAX_THR_RATES], index;
|
||||
|
||||
if (mi->ampdu_packets > 0) {
|
||||
- mi->avg_ampdu_len = minstrel_ewma(mi->avg_ampdu_len,
|
||||
- MINSTREL_FRAC(mi->ampdu_len, mi->ampdu_packets), EWMA_LEVEL);
|
||||
+ if (!ieee80211_hw_check(mp->hw, TX_STATUS_NO_AMPDU_LEN))
|
||||
+ mi->avg_ampdu_len = minstrel_ewma(mi->avg_ampdu_len,
|
||||
+ MINSTREL_FRAC(mi->ampdu_len, mi->ampdu_packets),
|
||||
+ EWMA_LEVEL);
|
||||
+ else
|
||||
+ mi->avg_ampdu_len = 0;
|
||||
mi->ampdu_len = 0;
|
||||
mi->ampdu_packets = 0;
|
||||
}
|
||||
@@ -709,7 +722,9 @@ minstrel_ht_tx_status(void *priv, struct
|
||||
mi->ampdu_len += info->status.ampdu_len;
|
||||
|
||||
if (!mi->sample_wait && !mi->sample_tries && mi->sample_count > 0) {
|
||||
- mi->sample_wait = 16 + 2 * MINSTREL_TRUNC(mi->avg_ampdu_len);
|
||||
+ int avg_ampdu_len = minstrel_ht_avg_ampdu_len(mi);
|
||||
+
|
||||
+ mi->sample_wait = 16 + 2 * avg_ampdu_len;
|
||||
mi->sample_tries = 1;
|
||||
mi->sample_count--;
|
||||
}
|
||||
@@ -777,7 +792,7 @@ minstrel_calc_retransmit(struct minstrel
|
||||
unsigned int cw = mp->cw_min;
|
||||
unsigned int ctime = 0;
|
||||
unsigned int t_slot = 9; /* FIXME */
|
||||
- unsigned int ampdu_len = MINSTREL_TRUNC(mi->avg_ampdu_len);
|
||||
+ unsigned int ampdu_len = minstrel_ht_avg_ampdu_len(mi);
|
||||
unsigned int overhead = 0, overhead_rtscts = 0;
|
||||
|
||||
mrs = minstrel_get_ratestats(mi, index);
|
||||
--- a/net/mac80211/rc80211_minstrel_ht_debugfs.c
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c
|
||||
@@ -163,9 +163,10 @@ minstrel_ht_stats_open(struct inode *ino
|
||||
"lookaround %d\n",
|
||||
max(0, (int) mi->total_packets - (int) mi->sample_packets),
|
||||
mi->sample_packets);
|
||||
- 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);
|
||||
+ if (mi->avg_ampdu_len)
|
||||
+ 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;
|
||||
WARN_ON(ms->len + sizeof(*ms) > 32768);
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Wed, 5 Jun 2019 20:42:49 +0200
|
||||
Subject: [PATCH] mac80211: minstrel_ht: reduce unnecessary rate probing
|
||||
attempts
|
||||
|
||||
On hardware with static fallback tables (e.g. mt76x2), rate probing attempts
|
||||
can be very expensive.
|
||||
On such devices, avoid sampling rates slower than the per-group max throughput
|
||||
rate, based on the assumption that the fallback table will take care of probing
|
||||
lower rates within that group if the higher rates fail.
|
||||
To make this work, this also fixes a wrong initialization in the previously
|
||||
unused per-group sorted rate array.
|
||||
To further reduce unnecessary probing attempts, skip duplicate attempts on
|
||||
rates slower than the max throughput rate.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/rc80211_minstrel_ht.c
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht.c
|
||||
@@ -1028,6 +1028,21 @@ minstrel_get_sample_rate(struct minstrel
|
||||
minstrel_get_duration(mi->max_prob_rate) * 3 < sample_dur)
|
||||
return -1;
|
||||
|
||||
+
|
||||
+ /*
|
||||
+ * For devices with no configurable multi-rate retry, skip sampling
|
||||
+ * below the per-group max throughput rate, and only use one sampling
|
||||
+ * attempt per rate
|
||||
+ */
|
||||
+ if (mp->hw->max_rates == 1 &&
|
||||
+ (minstrel_get_duration(mg->max_group_tp_rate[0]) < sample_dur ||
|
||||
+ mrs->attempts))
|
||||
+ return -1;
|
||||
+
|
||||
+ /* Skip already sampled slow rates */
|
||||
+ if (sample_dur >= minstrel_get_duration(tp_rate1) && mrs->attempts)
|
||||
+ return -1;
|
||||
+
|
||||
/*
|
||||
* Make sure that lower rates get sampled only occasionally,
|
||||
* if the link is working perfectly.
|
||||
@@ -0,0 +1,46 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Fri, 14 Jun 2019 21:14:22 +0200
|
||||
Subject: [PATCH] mac80211: minstrel_ht: fix default max throughput rate
|
||||
indexes
|
||||
|
||||
Use the first supported rate instead of 0 (which can be invalid)
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/rc80211_minstrel_ht.c
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht.c
|
||||
@@ -453,7 +453,7 @@ minstrel_ht_assign_best_tp_rates(struct
|
||||
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) {
|
||||
+ if (tmp_cck_tp_rate && tmp_cck_tp > tmp_mcs_tp) {
|
||||
for(i = 0; i < MAX_THR_RATES; i++) {
|
||||
minstrel_ht_sort_best_tp_rates(mi, tmp_cck_tp_rate[i],
|
||||
tmp_mcs_tp_rate);
|
||||
@@ -525,11 +525,19 @@ minstrel_ht_update_stats(struct minstrel
|
||||
mi->sample_slow = 0;
|
||||
mi->sample_count = 0;
|
||||
|
||||
- /* Initialize global rate indexes */
|
||||
- for(j = 0; j < MAX_THR_RATES; j++){
|
||||
- tmp_mcs_tp_rate[j] = 0;
|
||||
- tmp_cck_tp_rate[j] = 0;
|
||||
- }
|
||||
+ memset(tmp_mcs_tp_rate, 0, sizeof(tmp_mcs_tp_rate));
|
||||
+ memset(tmp_cck_tp_rate, 0, sizeof(tmp_cck_tp_rate));
|
||||
+ if (mi->supported[MINSTREL_CCK_GROUP])
|
||||
+ for (j = 0; j < ARRAY_SIZE(tmp_cck_tp_rate); j++)
|
||||
+ tmp_cck_tp_rate[j] = MINSTREL_CCK_GROUP * MCS_GROUP_RATES;
|
||||
+
|
||||
+ if (mi->supported[MINSTREL_VHT_GROUP_0])
|
||||
+ index = MINSTREL_VHT_GROUP_0 * MCS_GROUP_RATES;
|
||||
+ else
|
||||
+ index = MINSTREL_HT_GROUP_0 * MCS_GROUP_RATES;
|
||||
+
|
||||
+ for (j = 0; j < ARRAY_SIZE(tmp_mcs_tp_rate); j++)
|
||||
+ tmp_mcs_tp_rate[j] = index;
|
||||
|
||||
/* Find best rate sets within all MCS groups*/
|
||||
for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) {
|
||||
@@ -0,0 +1,481 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Fri, 14 Jun 2019 21:15:47 +0200
|
||||
Subject: [PATCH] mac80211: minstrel_ht: improve rate probing for devices
|
||||
with static fallback
|
||||
|
||||
On some devices that only support static rate fallback tables sending rate
|
||||
control probing packets can be really expensive.
|
||||
Probing lower rates can already hurt throughput quite a bit. What hurts even
|
||||
more is the fact that on mt76x0/mt76x2, single probing packets can only be
|
||||
forced by directing packets at a different internal hardware queue, which
|
||||
causes some heavy reordering and extra latency.
|
||||
The reordering issue is mainly problematic while pushing lots of packets to
|
||||
a particular station. If there is little activity, the overhead of probing is
|
||||
neglegible.
|
||||
|
||||
The static fallback behavior is designed to pretty much only handle rate
|
||||
control algorithms that use only a very limited set of rates on which the
|
||||
algorithm switches up/down based on packet error rate.
|
||||
|
||||
In order to better support that kind of hardware, this patch implements a
|
||||
different approach to rate probing where it switches to a slightly higher rate,
|
||||
waits for tx status feedback, then updates the stats and switches back to
|
||||
the new max throughput rate. This only triggers above a packet rate of 100
|
||||
per stats interval (~50ms).
|
||||
For that kind of probing, the code has to reduce the set of probing rates
|
||||
a lot more compared to single packet probing, so it uses only one packet
|
||||
per MCS group which is either slightly faster, or as close as possible to
|
||||
the max throughput rate.
|
||||
This allows switching between similar rates with different numbers of
|
||||
streams. The algorithm assumes that the hardware will work its way lower
|
||||
within an MCS group in case of retransmissions, so that lower rates don't
|
||||
have to be probed by the high packets per second rate probing code.
|
||||
|
||||
To further reduce the search space, it also does not probe rates with lower
|
||||
channel bandwidth than the max throughput rate.
|
||||
|
||||
At the moment, these changes will only affect mt76x0/mt76x2.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/rc80211_minstrel.h
|
||||
+++ b/net/mac80211/rc80211_minstrel.h
|
||||
@@ -113,6 +113,7 @@ struct minstrel_sta_info {
|
||||
struct minstrel_priv {
|
||||
struct ieee80211_hw *hw;
|
||||
bool has_mrr;
|
||||
+ u32 sample_switch;
|
||||
unsigned int cw_min;
|
||||
unsigned int cw_max;
|
||||
unsigned int max_retry;
|
||||
--- a/net/mac80211/rc80211_minstrel_ht.c
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht.c
|
||||
@@ -21,6 +21,8 @@
|
||||
#define AVG_AMPDU_SIZE 16
|
||||
#define AVG_PKT_SIZE 1200
|
||||
|
||||
+#define SAMPLE_SWITCH_THR 100
|
||||
+
|
||||
/* Number of bits for an average sized packet */
|
||||
#define MCS_NBITS ((AVG_PKT_SIZE * AVG_AMPDU_SIZE) << 3)
|
||||
|
||||
@@ -56,6 +58,7 @@
|
||||
[GROUP_IDX(_streams, _sgi, _ht40)] = { \
|
||||
.streams = _streams, \
|
||||
.shift = _s, \
|
||||
+ .bw = _ht40, \
|
||||
.flags = \
|
||||
IEEE80211_TX_RC_MCS | \
|
||||
(_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) | \
|
||||
@@ -85,6 +88,7 @@
|
||||
[VHT_GROUP_IDX(_streams, _sgi, _bw)] = { \
|
||||
.streams = _streams, \
|
||||
.shift = _s, \
|
||||
+ .bw = _bw, \
|
||||
.flags = \
|
||||
IEEE80211_TX_RC_VHT_MCS | \
|
||||
(_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) | \
|
||||
@@ -493,6 +497,133 @@ minstrel_ht_prob_rate_reduce_streams(str
|
||||
}
|
||||
}
|
||||
|
||||
+static inline int
|
||||
+minstrel_get_duration(int index)
|
||||
+{
|
||||
+ const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];
|
||||
+ unsigned int duration = group->duration[index % MCS_GROUP_RATES];
|
||||
+ return duration << group->shift;
|
||||
+}
|
||||
+
|
||||
+static bool
|
||||
+minstrel_ht_probe_group(struct minstrel_ht_sta *mi, const struct mcs_group *tp_group,
|
||||
+ int tp_idx, const struct mcs_group *group)
|
||||
+{
|
||||
+ if (group->bw < tp_group->bw)
|
||||
+ return false;
|
||||
+
|
||||
+ if (group->streams == tp_group->streams)
|
||||
+ return true;
|
||||
+
|
||||
+ if (tp_idx < 4 && group->streams == tp_group->streams - 1)
|
||||
+ return true;
|
||||
+
|
||||
+ return group->streams == tp_group->streams + 1;
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+minstrel_ht_find_probe_rates(struct minstrel_ht_sta *mi, u16 *rates, int *n_rates,
|
||||
+ bool faster_rate)
|
||||
+{
|
||||
+ const struct mcs_group *group, *tp_group;
|
||||
+ int i, g, max_dur;
|
||||
+ int tp_idx;
|
||||
+
|
||||
+ tp_group = &minstrel_mcs_groups[mi->max_tp_rate[0] / MCS_GROUP_RATES];
|
||||
+ tp_idx = mi->max_tp_rate[0] % MCS_GROUP_RATES;
|
||||
+
|
||||
+ max_dur = minstrel_get_duration(mi->max_tp_rate[0]);
|
||||
+ if (faster_rate)
|
||||
+ max_dur -= max_dur / 16;
|
||||
+
|
||||
+ for (g = 0; g < MINSTREL_GROUPS_NB; g++) {
|
||||
+ u16 supported = mi->supported[g];
|
||||
+
|
||||
+ if (!supported)
|
||||
+ continue;
|
||||
+
|
||||
+ group = &minstrel_mcs_groups[g];
|
||||
+ if (!minstrel_ht_probe_group(mi, tp_group, tp_idx, group))
|
||||
+ continue;
|
||||
+
|
||||
+ for (i = 0; supported; supported >>= 1, i++) {
|
||||
+ int idx;
|
||||
+
|
||||
+ if (!(supported & 1))
|
||||
+ continue;
|
||||
+
|
||||
+ if ((group->duration[i] << group->shift) > max_dur)
|
||||
+ continue;
|
||||
+
|
||||
+ idx = g * MCS_GROUP_RATES + i;
|
||||
+ if (idx == mi->max_tp_rate[0])
|
||||
+ continue;
|
||||
+
|
||||
+ rates[(*n_rates)++] = idx;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+minstrel_ht_rate_sample_switch(struct minstrel_priv *mp,
|
||||
+ struct minstrel_ht_sta *mi)
|
||||
+{
|
||||
+ struct minstrel_rate_stats *mrs;
|
||||
+ u16 rates[MINSTREL_GROUPS_NB];
|
||||
+ int n_rates = 0;
|
||||
+ int probe_rate = 0;
|
||||
+ bool faster_rate;
|
||||
+ int i;
|
||||
+ u8 random;
|
||||
+
|
||||
+ /*
|
||||
+ * Use rate switching instead of probing packets for devices with
|
||||
+ * little control over retry fallback behavior
|
||||
+ */
|
||||
+ if (mp->hw->max_rates > 1)
|
||||
+ return;
|
||||
+
|
||||
+ /*
|
||||
+ * If the current EWMA prob is >75%, look for a rate that's 6.25%
|
||||
+ * faster than the max tp rate.
|
||||
+ * If that fails, look again for a rate that is at least as fast
|
||||
+ */
|
||||
+ mrs = minstrel_get_ratestats(mi, mi->max_tp_rate[0]);
|
||||
+ faster_rate = mrs->prob_ewma > MINSTREL_FRAC(75, 100);
|
||||
+ minstrel_ht_find_probe_rates(mi, rates, &n_rates, faster_rate);
|
||||
+ if (!n_rates && faster_rate)
|
||||
+ minstrel_ht_find_probe_rates(mi, rates, &n_rates, false);
|
||||
+
|
||||
+ /* If no suitable rate was found, try to pick the next one in the group */
|
||||
+ if (!n_rates) {
|
||||
+ int g_idx = mi->max_tp_rate[0] / MCS_GROUP_RATES;
|
||||
+ u16 supported = mi->supported[g_idx];
|
||||
+
|
||||
+ supported >>= mi->max_tp_rate[0] % MCS_GROUP_RATES;
|
||||
+ for (i = 0; supported; i++) {
|
||||
+ if (!(supported & 1))
|
||||
+ continue;
|
||||
+
|
||||
+ probe_rate = mi->max_tp_rate[0] + i;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ i = 0;
|
||||
+ if (n_rates > 1) {
|
||||
+ random = prandom_u32();
|
||||
+ i = random % n_rates;
|
||||
+ }
|
||||
+ probe_rate = rates[i];
|
||||
+
|
||||
+out:
|
||||
+ mi->sample_rate = probe_rate;
|
||||
+ mi->sample_mode = MINSTREL_SAMPLE_ACTIVE;
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* Update rate statistics and select new primary rates
|
||||
*
|
||||
@@ -503,7 +634,8 @@ minstrel_ht_prob_rate_reduce_streams(str
|
||||
* higher throughput rates, even if the probablity is a bit lower
|
||||
*/
|
||||
static void
|
||||
-minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
|
||||
+minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
|
||||
+ bool sample)
|
||||
{
|
||||
struct minstrel_mcs_group_data *mg;
|
||||
struct minstrel_rate_stats *mrs;
|
||||
@@ -511,6 +643,18 @@ minstrel_ht_update_stats(struct minstrel
|
||||
u16 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES];
|
||||
u16 tmp_cck_tp_rate[MAX_THR_RATES], index;
|
||||
|
||||
+ mi->sample_mode = MINSTREL_SAMPLE_IDLE;
|
||||
+
|
||||
+ if (sample) {
|
||||
+ mi->total_packets_cur = mi->total_packets -
|
||||
+ mi->total_packets_last;
|
||||
+ mi->total_packets_last = mi->total_packets;
|
||||
+ }
|
||||
+ if (!mp->sample_switch)
|
||||
+ sample = false;
|
||||
+ if (mi->total_packets_cur < SAMPLE_SWITCH_THR && mp->sample_switch != 1)
|
||||
+ sample = false;
|
||||
+
|
||||
if (mi->ampdu_packets > 0) {
|
||||
if (!ieee80211_hw_check(mp->hw, TX_STATUS_NO_AMPDU_LEN))
|
||||
mi->avg_ampdu_len = minstrel_ewma(mi->avg_ampdu_len,
|
||||
@@ -597,12 +741,16 @@ minstrel_ht_update_stats(struct minstrel
|
||||
/* try to sample all available rates during each interval */
|
||||
mi->sample_count *= 8;
|
||||
|
||||
+ if (sample)
|
||||
+ minstrel_ht_rate_sample_switch(mp, mi);
|
||||
+
|
||||
#ifdef CPTCFG_MAC80211_DEBUGFS
|
||||
/* use fixed index if set */
|
||||
if (mp->fixed_rate_idx != -1) {
|
||||
for (i = 0; i < 4; i++)
|
||||
mi->max_tp_rate[i] = mp->fixed_rate_idx;
|
||||
mi->max_prob_rate = mp->fixed_rate_idx;
|
||||
+ mi->sample_mode = MINSTREL_SAMPLE_IDLE;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -706,15 +854,17 @@ minstrel_ht_tx_status(void *priv, struct
|
||||
struct minstrel_ht_sta_priv *msp = priv_sta;
|
||||
struct minstrel_ht_sta *mi = &msp->ht;
|
||||
struct ieee80211_tx_rate *ar = info->status.rates;
|
||||
- struct minstrel_rate_stats *rate, *rate2;
|
||||
+ struct minstrel_rate_stats *rate, *rate2, *rate_sample = NULL;
|
||||
struct minstrel_priv *mp = priv;
|
||||
bool last, update = false;
|
||||
+ bool sample_status = false;
|
||||
int i;
|
||||
|
||||
if (!msp->is_ht)
|
||||
return mac80211_minstrel.tx_status_ext(priv, sband,
|
||||
&msp->legacy, st);
|
||||
|
||||
+
|
||||
/* This packet was aggregated but doesn't carry status info */
|
||||
if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
|
||||
!(info->flags & IEEE80211_TX_STAT_AMPDU))
|
||||
@@ -740,12 +890,17 @@ minstrel_ht_tx_status(void *priv, struct
|
||||
if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE)
|
||||
mi->sample_packets += info->status.ampdu_len;
|
||||
|
||||
+ if (mi->sample_mode != MINSTREL_SAMPLE_IDLE)
|
||||
+ rate_sample = minstrel_get_ratestats(mi, mi->sample_rate);
|
||||
+
|
||||
last = !minstrel_ht_txstat_valid(mp, &ar[0]);
|
||||
for (i = 0; !last; i++) {
|
||||
last = (i == IEEE80211_TX_MAX_RATES - 1) ||
|
||||
!minstrel_ht_txstat_valid(mp, &ar[i + 1]);
|
||||
|
||||
rate = minstrel_ht_get_stats(mp, mi, &ar[i]);
|
||||
+ if (rate == rate_sample)
|
||||
+ sample_status = true;
|
||||
|
||||
if (last)
|
||||
rate->success += info->status.ampdu_ack_len;
|
||||
@@ -753,44 +908,60 @@ minstrel_ht_tx_status(void *priv, struct
|
||||
rate->attempts += ar[i].count * info->status.ampdu_len;
|
||||
}
|
||||
|
||||
- /*
|
||||
- * check for sudden death of spatial multiplexing,
|
||||
- * downgrade to a lower number of streams if necessary.
|
||||
- */
|
||||
- rate = minstrel_get_ratestats(mi, mi->max_tp_rate[0]);
|
||||
- if (rate->attempts > 30 &&
|
||||
- MINSTREL_FRAC(rate->success, rate->attempts) <
|
||||
- MINSTREL_FRAC(20, 100)) {
|
||||
- minstrel_downgrade_rate(mi, &mi->max_tp_rate[0], true);
|
||||
+ switch (mi->sample_mode) {
|
||||
+ case MINSTREL_SAMPLE_IDLE:
|
||||
+ break;
|
||||
+
|
||||
+ case MINSTREL_SAMPLE_ACTIVE:
|
||||
+ if (!sample_status)
|
||||
+ break;
|
||||
+
|
||||
+ mi->sample_mode = MINSTREL_SAMPLE_PENDING;
|
||||
update = true;
|
||||
- }
|
||||
+ break;
|
||||
+
|
||||
+ case MINSTREL_SAMPLE_PENDING:
|
||||
+ if (sample_status)
|
||||
+ break;
|
||||
|
||||
- rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate[1]);
|
||||
- if (rate2->attempts > 30 &&
|
||||
- MINSTREL_FRAC(rate2->success, rate2->attempts) <
|
||||
- MINSTREL_FRAC(20, 100)) {
|
||||
- minstrel_downgrade_rate(mi, &mi->max_tp_rate[1], false);
|
||||
update = true;
|
||||
+ minstrel_ht_update_stats(mp, mi, false);
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ if (mp->hw->max_rates > 1) {
|
||||
+ /*
|
||||
+ * check for sudden death of spatial multiplexing,
|
||||
+ * downgrade to a lower number of streams if necessary.
|
||||
+ */
|
||||
+ rate = minstrel_get_ratestats(mi, mi->max_tp_rate[0]);
|
||||
+ if (rate->attempts > 30 &&
|
||||
+ MINSTREL_FRAC(rate->success, rate->attempts) <
|
||||
+ MINSTREL_FRAC(20, 100)) {
|
||||
+ minstrel_downgrade_rate(mi, &mi->max_tp_rate[0], true);
|
||||
+ update = true;
|
||||
+ }
|
||||
+
|
||||
+ rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate[1]);
|
||||
+ if (rate2->attempts > 30 &&
|
||||
+ MINSTREL_FRAC(rate2->success, rate2->attempts) <
|
||||
+ MINSTREL_FRAC(20, 100)) {
|
||||
+ minstrel_downgrade_rate(mi, &mi->max_tp_rate[1], false);
|
||||
+ update = true;
|
||||
+ }
|
||||
}
|
||||
|
||||
if (time_after(jiffies, mi->last_stats_update +
|
||||
(mp->update_interval / 2 * HZ) / 1000)) {
|
||||
update = true;
|
||||
- minstrel_ht_update_stats(mp, mi);
|
||||
+ minstrel_ht_update_stats(mp, mi, true);
|
||||
}
|
||||
|
||||
if (update)
|
||||
minstrel_ht_update_rates(mp, mi);
|
||||
}
|
||||
|
||||
-static inline int
|
||||
-minstrel_get_duration(int index)
|
||||
-{
|
||||
- const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];
|
||||
- unsigned int duration = group->duration[index % MCS_GROUP_RATES];
|
||||
- return duration << group->shift;
|
||||
-}
|
||||
-
|
||||
static void
|
||||
minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
|
||||
int index)
|
||||
@@ -955,14 +1126,18 @@ static void
|
||||
minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
|
||||
{
|
||||
struct ieee80211_sta_rates *rates;
|
||||
+ u16 first_rate = mi->max_tp_rate[0];
|
||||
int i = 0;
|
||||
|
||||
+ if (mi->sample_mode == MINSTREL_SAMPLE_ACTIVE)
|
||||
+ first_rate = mi->sample_rate;
|
||||
+
|
||||
rates = kzalloc(sizeof(*rates), GFP_ATOMIC);
|
||||
if (!rates)
|
||||
return;
|
||||
|
||||
/* Start with max_tp_rate[0] */
|
||||
- minstrel_ht_set_rate(mp, mi, rates, i++, mi->max_tp_rate[0]);
|
||||
+ minstrel_ht_set_rate(mp, mi, rates, i++, first_rate);
|
||||
|
||||
if (mp->hw->max_rates >= 3) {
|
||||
/* At least 3 tx rates supported, use max_tp_rate[1] next */
|
||||
@@ -989,6 +1164,11 @@ minstrel_get_sample_rate(struct minstrel
|
||||
int tp_rate1, tp_rate2;
|
||||
int sample_idx = 0;
|
||||
|
||||
+ if (mp->hw->max_rates == 1 && mp->sample_switch &&
|
||||
+ (mi->total_packets_cur >= SAMPLE_SWITCH_THR ||
|
||||
+ mp->sample_switch == 1))
|
||||
+ return -1;
|
||||
+
|
||||
if (mi->sample_wait > 0) {
|
||||
mi->sample_wait--;
|
||||
return -1;
|
||||
@@ -1315,7 +1495,7 @@ minstrel_ht_update_caps(void *priv, stru
|
||||
mi->supported[MINSTREL_CCK_GROUP] |= mi->cck_supported_short << 4;
|
||||
|
||||
/* create an initial rate table with the lowest supported rates */
|
||||
- minstrel_ht_update_stats(mp, mi);
|
||||
+ minstrel_ht_update_stats(mp, mi, true);
|
||||
minstrel_ht_update_rates(mp, mi);
|
||||
|
||||
return;
|
||||
@@ -1433,6 +1613,8 @@ minstrel_ht_alloc(struct ieee80211_hw *h
|
||||
if (!mp)
|
||||
return NULL;
|
||||
|
||||
+ mp->sample_switch = -1;
|
||||
+
|
||||
/* contention window settings
|
||||
* Just an approximation. Using the per-queue values would complicate
|
||||
* the calculations and is probably unnecessary */
|
||||
@@ -1464,6 +1646,8 @@ minstrel_ht_alloc(struct ieee80211_hw *h
|
||||
mp->fixed_rate_idx = (u32) -1;
|
||||
debugfs_create_u32("fixed_rate_idx", S_IRUGO | S_IWUGO, debugfsdir,
|
||||
&mp->fixed_rate_idx);
|
||||
+ debugfs_create_u32("sample_switch", S_IRUGO | S_IWUSR, debugfsdir,
|
||||
+ &mp->sample_switch);
|
||||
#endif
|
||||
|
||||
minstrel_ht_init_cck_rates(mp);
|
||||
--- a/net/mac80211/rc80211_minstrel_ht.h
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht.h
|
||||
@@ -36,6 +36,7 @@ struct mcs_group {
|
||||
u16 flags;
|
||||
u8 streams;
|
||||
u8 shift;
|
||||
+ u8 bw;
|
||||
u16 duration[MCS_GROUP_RATES];
|
||||
};
|
||||
|
||||
@@ -53,6 +54,12 @@ struct minstrel_mcs_group_data {
|
||||
struct minstrel_rate_stats rates[MCS_GROUP_RATES];
|
||||
};
|
||||
|
||||
+enum minstrel_sample_mode {
|
||||
+ MINSTREL_SAMPLE_IDLE,
|
||||
+ MINSTREL_SAMPLE_ACTIVE,
|
||||
+ MINSTREL_SAMPLE_PENDING,
|
||||
+};
|
||||
+
|
||||
struct minstrel_ht_sta {
|
||||
struct ieee80211_sta *sta;
|
||||
|
||||
@@ -74,6 +81,8 @@ struct minstrel_ht_sta {
|
||||
unsigned int overhead;
|
||||
unsigned int overhead_rtscts;
|
||||
|
||||
+ unsigned int total_packets_last;
|
||||
+ unsigned int total_packets_cur;
|
||||
unsigned int total_packets;
|
||||
unsigned int sample_packets;
|
||||
|
||||
@@ -85,6 +94,9 @@ struct minstrel_ht_sta {
|
||||
u8 sample_count;
|
||||
u8 sample_slow;
|
||||
|
||||
+ enum minstrel_sample_mode sample_mode;
|
||||
+ u16 sample_rate;
|
||||
+
|
||||
/* current MCS group to be sampled */
|
||||
u8 sample_group;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
From: Colin Ian King <colin.king@canonical.com>
|
||||
Date: Thu, 22 Aug 2019 13:20:34 +0100
|
||||
Subject: [PATCH] mac80211: minstrel_ht: fix infinite loop because supported is
|
||||
not being shifted
|
||||
|
||||
Currently the for-loop will spin forever if variable supported is
|
||||
non-zero because supported is never changed. Fix this by adding in
|
||||
the missing right shift of supported.
|
||||
|
||||
Addresses-Coverity: ("Infinite loop")
|
||||
Fixes: 48cb39522a9d ("mac80211: minstrel_ht: improve rate probing for devices with static fallback")
|
||||
Signed-off-by: Colin Ian King <colin.king@canonical.com>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/rc80211_minstrel_ht.c
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht.c
|
||||
@@ -601,7 +601,7 @@ minstrel_ht_rate_sample_switch(struct mi
|
||||
u16 supported = mi->supported[g_idx];
|
||||
|
||||
supported >>= mi->max_tp_rate[0] % MCS_GROUP_RATES;
|
||||
- for (i = 0; supported; i++) {
|
||||
+ for (i = 0; supported; supported >>= 1, i++) {
|
||||
if (!(supported & 1))
|
||||
continue;
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= <toke@toke.dk>
|
||||
Date: Tue, 18 Dec 2018 17:02:06 -0800
|
||||
Subject: [PATCH] mac80211: Add TXQ scheduling API
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
This adds an API to mac80211 to handle scheduling of TXQs. The interface
|
||||
between driver and mac80211 for TXQ handling is changed by adding two new
|
||||
functions: ieee80211_next_txq(), which will return the next TXQ to schedule
|
||||
in the current round-robin rotation, and ieee80211_return_txq(), which the
|
||||
driver uses to indicate that it has finished scheduling a TXQ (which will
|
||||
then be put back in the scheduling rotation if it isn't empty).
|
||||
|
||||
The driver must call ieee80211_txq_schedule_start() at the start of each
|
||||
scheduling session, and ieee80211_txq_schedule_end() at the end. The API
|
||||
then guarantees that the same TXQ is not returned twice in the same
|
||||
session (so a driver can loop on ieee80211_next_txq() without worrying
|
||||
about breaking the loop.
|
||||
|
||||
Usage of the new API is optional, so drivers can be ported one at a time.
|
||||
In this patch, the actual scheduling performed by mac80211 is simple
|
||||
round-robin, but a subsequent commit adds airtime fairness awareness to the
|
||||
scheduler.
|
||||
|
||||
Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk>
|
||||
[minor kernel-doc fix, propagate sparse locking checks out]
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/include/net/mac80211.h
|
||||
+++ b/include/net/mac80211.h
|
||||
@@ -107,9 +107,15 @@
|
||||
* The driver is expected to initialize its private per-queue data for stations
|
||||
* and interfaces in the .add_interface and .sta_add ops.
|
||||
*
|
||||
- * The driver can't access the queue directly. To dequeue a frame, it calls
|
||||
- * ieee80211_tx_dequeue(). Whenever mac80211 adds a new frame to a queue, it
|
||||
- * calls the .wake_tx_queue driver op.
|
||||
+ * The driver can't access the queue directly. To dequeue a frame from a
|
||||
+ * txq, it calls ieee80211_tx_dequeue(). Whenever mac80211 adds a new frame to a
|
||||
+ * queue, it calls the .wake_tx_queue driver op.
|
||||
+ *
|
||||
+ * Drivers can optionally delegate responsibility for scheduling queues to
|
||||
+ * mac80211, to take advantage of airtime fairness accounting. In this case, to
|
||||
+ * obtain the next queue to pull frames from, the driver calls
|
||||
+ * ieee80211_next_txq(). The driver is then expected to return the txq using
|
||||
+ * ieee80211_return_txq().
|
||||
*
|
||||
* For AP powersave TIM handling, the driver only needs to indicate if it has
|
||||
* buffered packets in the driver specific data structures by calling
|
||||
@@ -5979,7 +5985,8 @@ void ieee80211_unreserve_tid(struct ieee
|
||||
* ieee80211_tx_dequeue - dequeue a packet from a software tx queue
|
||||
*
|
||||
* @hw: pointer as obtained from ieee80211_alloc_hw()
|
||||
- * @txq: pointer obtained from station or virtual interface
|
||||
+ * @txq: pointer obtained from station or virtual interface, or from
|
||||
+ * ieee80211_next_txq()
|
||||
*
|
||||
* Returns the skb if successful, %NULL if no frame was available.
|
||||
*/
|
||||
@@ -5987,6 +5994,54 @@ struct sk_buff *ieee80211_tx_dequeue(str
|
||||
struct ieee80211_txq *txq);
|
||||
|
||||
/**
|
||||
+ * ieee80211_next_txq - get next tx queue to pull packets from
|
||||
+ *
|
||||
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
|
||||
+ * @ac: AC number to return packets from.
|
||||
+ *
|
||||
+ * Should only be called between calls to ieee80211_txq_schedule_start()
|
||||
+ * and ieee80211_txq_schedule_end().
|
||||
+ * Returns the next txq if successful, %NULL if no queue is eligible. If a txq
|
||||
+ * is returned, it should be returned with ieee80211_return_txq() after the
|
||||
+ * driver has finished scheduling it.
|
||||
+ */
|
||||
+struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac);
|
||||
+
|
||||
+/**
|
||||
+ * ieee80211_return_txq - return a TXQ previously acquired by ieee80211_next_txq()
|
||||
+ *
|
||||
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
|
||||
+ * @txq: pointer obtained from station or virtual interface
|
||||
+ *
|
||||
+ * Should only be called between calls to ieee80211_txq_schedule_start()
|
||||
+ * and ieee80211_txq_schedule_end().
|
||||
+ */
|
||||
+void ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq);
|
||||
+
|
||||
+/**
|
||||
+ * ieee80211_txq_schedule_start - acquire locks for safe scheduling of an AC
|
||||
+ *
|
||||
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
|
||||
+ * @ac: AC number to acquire locks for
|
||||
+ *
|
||||
+ * Acquire locks needed to schedule TXQs from the given AC. Should be called
|
||||
+ * before ieee80211_next_txq() or ieee80211_return_txq().
|
||||
+ */
|
||||
+void ieee80211_txq_schedule_start(struct ieee80211_hw *hw, u8 ac)
|
||||
+ __acquires(txq_lock);
|
||||
+
|
||||
+/**
|
||||
+ * ieee80211_txq_schedule_end - release locks for safe scheduling of an AC
|
||||
+ *
|
||||
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
|
||||
+ * @ac: AC number to acquire locks for
|
||||
+ *
|
||||
+ * Release locks previously acquired by ieee80211_txq_schedule_end().
|
||||
+ */
|
||||
+void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac)
|
||||
+ __releases(txq_lock);
|
||||
+
|
||||
+/**
|
||||
* ieee80211_txq_get_depth - get pending frame/byte count of given txq
|
||||
*
|
||||
* The values are not guaranteed to be coherent with regard to each other, i.e.
|
||||
--- a/net/mac80211/agg-tx.c
|
||||
+++ b/net/mac80211/agg-tx.c
|
||||
@@ -229,7 +229,7 @@ ieee80211_agg_start_txq(struct sta_info
|
||||
clear_bit(IEEE80211_TXQ_STOP, &txqi->flags);
|
||||
local_bh_disable();
|
||||
rcu_read_lock();
|
||||
- drv_wake_tx_queue(sta->sdata->local, txqi);
|
||||
+ schedule_and_wake_txq(sta->sdata->local, txqi);
|
||||
rcu_read_unlock();
|
||||
local_bh_enable();
|
||||
}
|
||||
--- a/net/mac80211/driver-ops.h
|
||||
+++ b/net/mac80211/driver-ops.h
|
||||
@@ -1176,6 +1176,15 @@ static inline void drv_wake_tx_queue(str
|
||||
local->ops->wake_tx_queue(&local->hw, &txq->txq);
|
||||
}
|
||||
|
||||
+static inline void schedule_and_wake_txq(struct ieee80211_local *local,
|
||||
+ struct txq_info *txqi)
|
||||
+{
|
||||
+ spin_lock_bh(&local->active_txq_lock[txqi->txq.ac]);
|
||||
+ ieee80211_return_txq(&local->hw, &txqi->txq);
|
||||
+ spin_unlock_bh(&local->active_txq_lock[txqi->txq.ac]);
|
||||
+ drv_wake_tx_queue(local, txqi);
|
||||
+}
|
||||
+
|
||||
static inline int drv_start_nan(struct ieee80211_local *local,
|
||||
struct ieee80211_sub_if_data *sdata,
|
||||
struct cfg80211_nan_conf *conf)
|
||||
--- a/net/mac80211/ieee80211_i.h
|
||||
+++ b/net/mac80211/ieee80211_i.h
|
||||
@@ -829,6 +829,8 @@ enum txq_info_flags {
|
||||
* a fq_flow which is already owned by a different tin
|
||||
* @def_cvars: codel vars for @def_flow
|
||||
* @frags: used to keep fragments created after dequeue
|
||||
+ * @schedule_order: used with ieee80211_local->active_txqs
|
||||
+ * @schedule_round: counter to prevent infinite loops on TXQ scheduling
|
||||
*/
|
||||
struct txq_info {
|
||||
struct fq_tin tin;
|
||||
@@ -836,6 +838,8 @@ struct txq_info {
|
||||
struct codel_vars def_cvars;
|
||||
struct codel_stats cstats;
|
||||
struct sk_buff_head frags;
|
||||
+ struct list_head schedule_order;
|
||||
+ u16 schedule_round;
|
||||
unsigned long flags;
|
||||
|
||||
/* keep last! */
|
||||
@@ -1127,6 +1131,11 @@ struct ieee80211_local {
|
||||
struct codel_vars *cvars;
|
||||
struct codel_params cparams;
|
||||
|
||||
+ /* protects active_txqs and txqi->schedule_order */
|
||||
+ spinlock_t active_txq_lock[IEEE80211_NUM_ACS];
|
||||
+ struct list_head active_txqs[IEEE80211_NUM_ACS];
|
||||
+ u16 schedule_round[IEEE80211_NUM_ACS];
|
||||
+
|
||||
const struct ieee80211_ops *ops;
|
||||
|
||||
/*
|
||||
--- a/net/mac80211/main.c
|
||||
+++ b/net/mac80211/main.c
|
||||
@@ -652,6 +652,11 @@ struct ieee80211_hw *ieee80211_alloc_hw_
|
||||
spin_lock_init(&local->rx_path_lock);
|
||||
spin_lock_init(&local->queue_stop_reason_lock);
|
||||
|
||||
+ for (i = 0; i < IEEE80211_NUM_ACS; i++) {
|
||||
+ INIT_LIST_HEAD(&local->active_txqs[i]);
|
||||
+ spin_lock_init(&local->active_txq_lock[i]);
|
||||
+ }
|
||||
+
|
||||
INIT_LIST_HEAD(&local->chanctx_list);
|
||||
mutex_init(&local->chanctx_mtx);
|
||||
|
||||
--- a/net/mac80211/sta_info.c
|
||||
+++ b/net/mac80211/sta_info.c
|
||||
@@ -1261,7 +1261,7 @@ void ieee80211_sta_ps_deliver_wakeup(str
|
||||
if (!txq_has_queue(sta->sta.txq[i]))
|
||||
continue;
|
||||
|
||||
- drv_wake_tx_queue(local, to_txq_info(sta->sta.txq[i]));
|
||||
+ schedule_and_wake_txq(local, to_txq_info(sta->sta.txq[i]));
|
||||
}
|
||||
}
|
||||
|
||||
--- a/net/mac80211/tx.c
|
||||
+++ b/net/mac80211/tx.c
|
||||
@@ -1441,6 +1441,7 @@ void ieee80211_txq_init(struct ieee80211
|
||||
codel_vars_init(&txqi->def_cvars);
|
||||
codel_stats_init(&txqi->cstats);
|
||||
__skb_queue_head_init(&txqi->frags);
|
||||
+ INIT_LIST_HEAD(&txqi->schedule_order);
|
||||
|
||||
txqi->txq.vif = &sdata->vif;
|
||||
|
||||
@@ -1464,6 +1465,9 @@ void ieee80211_txq_purge(struct ieee8021
|
||||
|
||||
fq_tin_reset(fq, tin, fq_skb_free_func);
|
||||
ieee80211_purge_tx_queue(&local->hw, &txqi->frags);
|
||||
+ spin_lock_bh(&local->active_txq_lock[txqi->txq.ac]);
|
||||
+ list_del_init(&txqi->schedule_order);
|
||||
+ spin_unlock_bh(&local->active_txq_lock[txqi->txq.ac]);
|
||||
}
|
||||
|
||||
void ieee80211_txq_set_params(struct ieee80211_local *local)
|
||||
@@ -1580,7 +1584,7 @@ static bool ieee80211_queue_skb(struct i
|
||||
ieee80211_txq_enqueue(local, txqi, skb);
|
||||
spin_unlock_bh(&fq->lock);
|
||||
|
||||
- drv_wake_tx_queue(local, txqi);
|
||||
+ schedule_and_wake_txq(local, txqi);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -3631,6 +3635,60 @@ out:
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_tx_dequeue);
|
||||
|
||||
+struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac)
|
||||
+{
|
||||
+ struct ieee80211_local *local = hw_to_local(hw);
|
||||
+ struct txq_info *txqi = NULL;
|
||||
+
|
||||
+ lockdep_assert_held(&local->active_txq_lock[ac]);
|
||||
+
|
||||
+ txqi = list_first_entry_or_null(&local->active_txqs[ac],
|
||||
+ struct txq_info,
|
||||
+ schedule_order);
|
||||
+
|
||||
+ if (!txqi || txqi->schedule_round == local->schedule_round[ac])
|
||||
+ return NULL;
|
||||
+
|
||||
+ list_del_init(&txqi->schedule_order);
|
||||
+ txqi->schedule_round = local->schedule_round[ac];
|
||||
+ return &txqi->txq;
|
||||
+}
|
||||
+EXPORT_SYMBOL(ieee80211_next_txq);
|
||||
+
|
||||
+void ieee80211_return_txq(struct ieee80211_hw *hw,
|
||||
+ struct ieee80211_txq *txq)
|
||||
+{
|
||||
+ struct ieee80211_local *local = hw_to_local(hw);
|
||||
+ struct txq_info *txqi = to_txq_info(txq);
|
||||
+
|
||||
+ lockdep_assert_held(&local->active_txq_lock[txq->ac]);
|
||||
+
|
||||
+ if (list_empty(&txqi->schedule_order) &&
|
||||
+ (!skb_queue_empty(&txqi->frags) || txqi->tin.backlog_packets))
|
||||
+ list_add_tail(&txqi->schedule_order,
|
||||
+ &local->active_txqs[txq->ac]);
|
||||
+}
|
||||
+EXPORT_SYMBOL(ieee80211_return_txq);
|
||||
+
|
||||
+void ieee80211_txq_schedule_start(struct ieee80211_hw *hw, u8 ac)
|
||||
+ __acquires(txq_lock)
|
||||
+{
|
||||
+ struct ieee80211_local *local = hw_to_local(hw);
|
||||
+
|
||||
+ spin_lock_bh(&local->active_txq_lock[ac]);
|
||||
+ local->schedule_round[ac]++;
|
||||
+}
|
||||
+EXPORT_SYMBOL(ieee80211_txq_schedule_start);
|
||||
+
|
||||
+void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac)
|
||||
+ __releases(txq_lock)
|
||||
+{
|
||||
+ struct ieee80211_local *local = hw_to_local(hw);
|
||||
+
|
||||
+ spin_unlock_bh(&local->active_txq_lock[ac]);
|
||||
+}
|
||||
+EXPORT_SYMBOL(ieee80211_txq_schedule_end);
|
||||
+
|
||||
void __ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
struct net_device *dev,
|
||||
u32 info_flags,
|
||||
@@ -0,0 +1,202 @@
|
||||
From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= <toke@toke.dk>
|
||||
Date: Tue, 18 Dec 2018 17:02:07 -0800
|
||||
Subject: [PATCH] cfg80211: Add airtime statistics and settings
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
This adds TX airtime statistics to the cfg80211 station dump (to go along
|
||||
with the RX info already present), and adds a new parameter to set the
|
||||
airtime weight of each station. The latter allows userspace to implement
|
||||
policies for different stations by varying their weights.
|
||||
|
||||
Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk>
|
||||
[rmanohar@codeaurora.org: fixed checkpatch warnings]
|
||||
Signed-off-by: Rajkumar Manoharan <rmanohar@codeaurora.org>
|
||||
[move airtime weight != 0 check into policy]
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/include/net/cfg80211.h
|
||||
+++ b/include/net/cfg80211.h
|
||||
@@ -988,6 +988,7 @@ enum station_parameters_apply_mask {
|
||||
* @support_p2p_ps: information if station supports P2P PS mechanism
|
||||
* @he_capa: HE capabilities of station
|
||||
* @he_capa_len: the length of the HE capabilities
|
||||
+ * @airtime_weight: airtime scheduler weight for this station
|
||||
*/
|
||||
struct station_parameters {
|
||||
const u8 *supported_rates;
|
||||
@@ -1017,6 +1018,7 @@ struct station_parameters {
|
||||
int support_p2p_ps;
|
||||
const struct ieee80211_he_cap_elem *he_capa;
|
||||
u8 he_capa_len;
|
||||
+ u16 airtime_weight;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1284,6 +1286,8 @@ struct cfg80211_tid_stats {
|
||||
* @rx_beacon_signal_avg: signal strength average (in dBm) for beacons received
|
||||
* from this peer
|
||||
* @rx_duration: aggregate PPDU duration(usecs) for all the frames from a peer
|
||||
+ * @tx_duration: aggregate PPDU duration(usecs) for all the frames to a peer
|
||||
+ * @airtime_weight: current airtime scheduling weight
|
||||
* @pertid: per-TID statistics, see &struct cfg80211_tid_stats, using the last
|
||||
* (IEEE80211_NUM_TIDS) index for MSDUs not encapsulated in QoS-MPDUs.
|
||||
* Note that this doesn't use the @filled bit, but is used if non-NULL.
|
||||
@@ -1330,12 +1334,15 @@ struct station_info {
|
||||
|
||||
u32 expected_throughput;
|
||||
|
||||
- u64 rx_beacon;
|
||||
+ u64 tx_duration;
|
||||
u64 rx_duration;
|
||||
+ u64 rx_beacon;
|
||||
u8 rx_beacon_signal_avg;
|
||||
struct cfg80211_tid_stats *pertid;
|
||||
s8 ack_signal;
|
||||
s8 avg_ack_signal;
|
||||
+
|
||||
+ u16 airtime_weight;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CPTCFG_CFG80211)
|
||||
@@ -2361,6 +2368,8 @@ enum wiphy_params_flags {
|
||||
WIPHY_PARAM_TXQ_QUANTUM = 1 << 8,
|
||||
};
|
||||
|
||||
+#define IEEE80211_DEFAULT_AIRTIME_WEIGHT 256
|
||||
+
|
||||
/**
|
||||
* struct cfg80211_pmksa - PMK Security Association
|
||||
*
|
||||
--- a/include/uapi/linux/nl80211.h
|
||||
+++ b/include/uapi/linux/nl80211.h
|
||||
@@ -2241,6 +2241,9 @@ enum nl80211_commands {
|
||||
* association request when used with NL80211_CMD_NEW_STATION). Can be set
|
||||
* only if %NL80211_STA_FLAG_WME is set.
|
||||
*
|
||||
+ * @NL80211_ATTR_AIRTIME_WEIGHT: Station's weight when scheduled by the airtime
|
||||
+ * scheduler.
|
||||
+ *
|
||||
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
|
||||
* @NL80211_ATTR_MAX: highest attribute number currently defined
|
||||
* @__NL80211_ATTR_AFTER_LAST: internal use
|
||||
@@ -2682,6 +2685,14 @@ enum nl80211_attrs {
|
||||
|
||||
NL80211_ATTR_HE_CAPABILITY,
|
||||
|
||||
+ /* not backported yet */
|
||||
+ NL80211_ATTR_FTM_RESPONDER,
|
||||
+ NL80211_ATTR_FTM_RESPONDER_STATS,
|
||||
+ NL80211_ATTR_TIMEOUT,
|
||||
+ NL80211_ATTR_PEER_MEASUREMENTS,
|
||||
+
|
||||
+ NL80211_ATTR_AIRTIME_WEIGHT,
|
||||
+
|
||||
/* add attributes here, update the policy in nl80211.c */
|
||||
|
||||
__NL80211_ATTR_AFTER_LAST,
|
||||
@@ -3052,6 +3063,9 @@ enum nl80211_sta_bss_param {
|
||||
* @NL80211_STA_INFO_ACK_SIGNAL: signal strength of the last ACK frame(u8, dBm)
|
||||
* @NL80211_STA_INFO_DATA_ACK_SIGNAL_AVG: avg signal strength of (data)
|
||||
* ACK frame (s8, dBm)
|
||||
+ * @NL80211_STA_INFO_TX_DURATION: aggregate PPDU duration for all frames
|
||||
+ * sent to the station (u64, usec)
|
||||
+ * @NL80211_STA_INFO_AIRTIME_WEIGHT: current airtime weight for station (u16)
|
||||
* @__NL80211_STA_INFO_AFTER_LAST: internal
|
||||
* @NL80211_STA_INFO_MAX: highest possible station info attribute
|
||||
*/
|
||||
@@ -3093,6 +3107,14 @@ enum nl80211_sta_info {
|
||||
NL80211_STA_INFO_ACK_SIGNAL,
|
||||
NL80211_STA_INFO_DATA_ACK_SIGNAL_AVG,
|
||||
|
||||
+ /* not backported yet */
|
||||
+ NL80211_STA_INFO_RX_MPDUS,
|
||||
+ NL80211_STA_INFO_FCS_ERROR_COUNT,
|
||||
+ NL80211_STA_INFO_CONNECTED_TO_GATE,
|
||||
+
|
||||
+ NL80211_STA_INFO_TX_DURATION,
|
||||
+ NL80211_STA_INFO_AIRTIME_WEIGHT,
|
||||
+
|
||||
/* keep last */
|
||||
__NL80211_STA_INFO_AFTER_LAST,
|
||||
NL80211_STA_INFO_MAX = __NL80211_STA_INFO_AFTER_LAST - 1
|
||||
@@ -5224,6 +5246,10 @@ enum nl80211_feature_flags {
|
||||
* except for supported rates from the probe request content if requested
|
||||
* by the %NL80211_SCAN_FLAG_MIN_PREQ_CONTENT flag.
|
||||
*
|
||||
+ * @NL80211_EXT_FEATURE_AIRTIME_FAIRNESS: Driver supports getting airtime
|
||||
+ * fairness for transmitted packets and has enabled airtime fairness
|
||||
+ * scheduling.
|
||||
+ *
|
||||
* @NUM_NL80211_EXT_FEATURES: number of extended features.
|
||||
* @MAX_NL80211_EXT_FEATURES: highest extended feature index.
|
||||
*/
|
||||
@@ -5260,6 +5286,12 @@ enum nl80211_ext_feature_index {
|
||||
NL80211_EXT_FEATURE_SCAN_RANDOM_SN,
|
||||
NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT,
|
||||
|
||||
+ /* --- not backported yet --- */
|
||||
+ NL80211_EXT_FEATURE_CAN_REPLACE_PTK0,
|
||||
+ NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER,
|
||||
+
|
||||
+ NL80211_EXT_FEATURE_AIRTIME_FAIRNESS,
|
||||
+
|
||||
/* add new features before the definition below */
|
||||
NUM_NL80211_EXT_FEATURES,
|
||||
MAX_NL80211_EXT_FEATURES = NUM_NL80211_EXT_FEATURES - 1
|
||||
--- a/net/wireless/nl80211.c
|
||||
+++ b/net/wireless/nl80211.c
|
||||
@@ -469,6 +469,7 @@ static const struct nla_policy nl80211_p
|
||||
[NL80211_ATTR_TXQ_QUANTUM] = { .type = NLA_U32 },
|
||||
[NL80211_ATTR_HE_CAPABILITY] = { .type = NLA_BINARY,
|
||||
.len = NL80211_HE_MAX_CAPABILITY_LEN },
|
||||
+ [NL80211_ATTR_AIRTIME_WEIGHT] = NLA_POLICY_MIN(NLA_U16, 1),
|
||||
};
|
||||
|
||||
/* policy for the key attributes */
|
||||
@@ -4715,6 +4716,11 @@ static int nl80211_send_station(struct s
|
||||
PUT_SINFO(PLID, plid, u16);
|
||||
PUT_SINFO(PLINK_STATE, plink_state, u8);
|
||||
PUT_SINFO_U64(RX_DURATION, rx_duration);
|
||||
+ PUT_SINFO_U64(TX_DURATION, tx_duration);
|
||||
+
|
||||
+ if (wiphy_ext_feature_isset(&rdev->wiphy,
|
||||
+ NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
|
||||
+ PUT_SINFO(AIRTIME_WEIGHT, airtime_weight, u16);
|
||||
|
||||
switch (rdev->wiphy.signal_type) {
|
||||
case CFG80211_SIGNAL_TYPE_MBM:
|
||||
@@ -5351,6 +5357,15 @@ static int nl80211_set_station(struct sk
|
||||
nla_get_u8(info->attrs[NL80211_ATTR_OPMODE_NOTIF]);
|
||||
}
|
||||
|
||||
+ if (info->attrs[NL80211_ATTR_AIRTIME_WEIGHT])
|
||||
+ params.airtime_weight =
|
||||
+ nla_get_u16(info->attrs[NL80211_ATTR_AIRTIME_WEIGHT]);
|
||||
+
|
||||
+ if (params.airtime_weight &&
|
||||
+ !wiphy_ext_feature_isset(&rdev->wiphy,
|
||||
+ NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
/* Include parameters for TDLS peer (will check later) */
|
||||
err = nl80211_set_station_tdls(info, ¶ms);
|
||||
if (err)
|
||||
@@ -5489,6 +5504,15 @@ static int nl80211_new_station(struct sk
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
+ if (info->attrs[NL80211_ATTR_AIRTIME_WEIGHT])
|
||||
+ params.airtime_weight =
|
||||
+ nla_get_u16(info->attrs[NL80211_ATTR_AIRTIME_WEIGHT]);
|
||||
+
|
||||
+ if (params.airtime_weight &&
|
||||
+ !wiphy_ext_feature_isset(&rdev->wiphy,
|
||||
+ NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
err = nl80211_parse_sta_channel_info(info, ¶ms);
|
||||
if (err)
|
||||
return err;
|
||||
@@ -0,0 +1,522 @@
|
||||
From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= <toke@toke.dk>
|
||||
Date: Tue, 18 Dec 2018 17:02:08 -0800
|
||||
Subject: [PATCH] mac80211: Add airtime accounting and scheduling to TXQs
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
This adds airtime accounting and scheduling to the mac80211 TXQ
|
||||
scheduler. A new callback, ieee80211_sta_register_airtime(), is added
|
||||
that drivers can call to report airtime usage for stations.
|
||||
|
||||
When airtime information is present, mac80211 will schedule TXQs
|
||||
(through ieee80211_next_txq()) in a way that enforces airtime fairness
|
||||
between active stations. This scheduling works the same way as the ath9k
|
||||
in-driver airtime fairness scheduling. If no airtime usage is reported
|
||||
by the driver, the scheduler will default to round-robin scheduling.
|
||||
|
||||
For drivers that don't control TXQ scheduling in software, a new API
|
||||
function, ieee80211_txq_may_transmit(), is added which the driver can use
|
||||
to check if the TXQ is eligible for transmission, or should be throttled to
|
||||
enforce fairness. Calls to this function must also be enclosed in
|
||||
ieee80211_txq_schedule_{start,end}() calls to ensure proper locking.
|
||||
|
||||
The API ieee80211_txq_may_transmit() also ensures that TXQ list will be
|
||||
aligned aginst driver's own round-robin scheduler list. i.e it rotates
|
||||
the TXQ list till it makes the requested node becomes the first entry
|
||||
in TXQ list. Thus both the TXQ list and driver's list are in sync.
|
||||
|
||||
Co-developed-by: Rajkumar Manoharan <rmanohar@codeaurora.org>
|
||||
Signed-off-by: Louie Lu <git@louie.lu>
|
||||
[added debugfs write op to reset airtime counter]
|
||||
Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk>
|
||||
Signed-off-by: Rajkumar Manoharan <rmanohar@codeaurora.org>
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/include/net/mac80211.h
|
||||
+++ b/include/net/mac80211.h
|
||||
@@ -2304,6 +2304,9 @@ enum ieee80211_hw_flags {
|
||||
* supported by HW.
|
||||
* @max_nan_de_entries: maximum number of NAN DE functions supported by the
|
||||
* device.
|
||||
+ *
|
||||
+ * @weight_multipler: Driver specific airtime weight multiplier used while
|
||||
+ * refilling deficit of each TXQ.
|
||||
*/
|
||||
struct ieee80211_hw {
|
||||
struct ieee80211_conf conf;
|
||||
@@ -2339,6 +2342,7 @@ struct ieee80211_hw {
|
||||
u8 n_cipher_schemes;
|
||||
const struct ieee80211_cipher_scheme *cipher_schemes;
|
||||
u8 max_nan_de_entries;
|
||||
+ u8 weight_multiplier;
|
||||
};
|
||||
|
||||
static inline bool _ieee80211_hw_check(struct ieee80211_hw *hw,
|
||||
@@ -5299,6 +5303,34 @@ void ieee80211_sta_eosp(struct ieee80211
|
||||
void ieee80211_send_eosp_nullfunc(struct ieee80211_sta *pubsta, int tid);
|
||||
|
||||
/**
|
||||
+ * ieee80211_sta_register_airtime - register airtime usage for a sta/tid
|
||||
+ *
|
||||
+ * Register airtime usage for a given sta on a given tid. The driver can call
|
||||
+ * this function to notify mac80211 that a station used a certain amount of
|
||||
+ * airtime. This information will be used by the TXQ scheduler to schedule
|
||||
+ * stations in a way that ensures airtime fairness.
|
||||
+ *
|
||||
+ * The reported airtime should as a minimum include all time that is spent
|
||||
+ * transmitting to the remote station, including overhead and padding, but not
|
||||
+ * including time spent waiting for a TXOP. If the time is not reported by the
|
||||
+ * hardware it can in some cases be calculated from the rate and known frame
|
||||
+ * composition. When possible, the time should include any failed transmission
|
||||
+ * attempts.
|
||||
+ *
|
||||
+ * The driver can either call this function synchronously for every packet or
|
||||
+ * aggregate, or asynchronously as airtime usage information becomes available.
|
||||
+ * TX and RX airtime can be reported together, or separately by setting one of
|
||||
+ * them to 0.
|
||||
+ *
|
||||
+ * @pubsta: the station
|
||||
+ * @tid: the TID to register airtime for
|
||||
+ * @tx_airtime: airtime used during TX (in usec)
|
||||
+ * @rx_airtime: airtime used during RX (in usec)
|
||||
+ */
|
||||
+void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid,
|
||||
+ u32 tx_airtime, u32 rx_airtime);
|
||||
+
|
||||
+/**
|
||||
* ieee80211_iter_keys - iterate keys programmed into the device
|
||||
* @hw: pointer obtained from ieee80211_alloc_hw()
|
||||
* @vif: virtual interface to iterate, may be %NULL for all
|
||||
@@ -6042,6 +6074,33 @@ void ieee80211_txq_schedule_end(struct i
|
||||
__releases(txq_lock);
|
||||
|
||||
/**
|
||||
+ * ieee80211_txq_may_transmit - check whether TXQ is allowed to transmit
|
||||
+ *
|
||||
+ * This function is used to check whether given txq is allowed to transmit by
|
||||
+ * the airtime scheduler, and can be used by drivers to access the airtime
|
||||
+ * fairness accounting without going using the scheduling order enfored by
|
||||
+ * next_txq().
|
||||
+ *
|
||||
+ * Returns %true if the airtime scheduler thinks the TXQ should be allowed to
|
||||
+ * transmit, and %false if it should be throttled. This function can also have
|
||||
+ * the side effect of rotating the TXQ in the scheduler rotation, which will
|
||||
+ * eventually bring the deficit to positive and allow the station to transmit
|
||||
+ * again.
|
||||
+ *
|
||||
+ * The API ieee80211_txq_may_transmit() also ensures that TXQ list will be
|
||||
+ * aligned aginst driver's own round-robin scheduler list. i.e it rotates
|
||||
+ * the TXQ list till it makes the requested node becomes the first entry
|
||||
+ * in TXQ list. Thus both the TXQ list and driver's list are in sync. If this
|
||||
+ * function returns %true, the driver is expected to schedule packets
|
||||
+ * for transmission, and then return the TXQ through ieee80211_return_txq().
|
||||
+ *
|
||||
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
|
||||
+ * @txq: pointer obtained from station or virtual interface
|
||||
+ */
|
||||
+bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
|
||||
+ struct ieee80211_txq *txq);
|
||||
+
|
||||
+/**
|
||||
* ieee80211_txq_get_depth - get pending frame/byte count of given txq
|
||||
*
|
||||
* The values are not guaranteed to be coherent with regard to each other, i.e.
|
||||
--- a/net/mac80211/cfg.c
|
||||
+++ b/net/mac80211/cfg.c
|
||||
@@ -1391,6 +1391,9 @@ static int sta_apply_parameters(struct i
|
||||
if (ieee80211_vif_is_mesh(&sdata->vif))
|
||||
sta_apply_mesh_params(local, sta, params);
|
||||
|
||||
+ if (params->airtime_weight)
|
||||
+ sta->airtime_weight = params->airtime_weight;
|
||||
+
|
||||
/* set the STA state after all sta info from usermode has been set */
|
||||
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) ||
|
||||
set & BIT(NL80211_STA_FLAG_ASSOCIATED)) {
|
||||
--- a/net/mac80211/debugfs.c
|
||||
+++ b/net/mac80211/debugfs.c
|
||||
@@ -380,6 +380,9 @@ void debugfs_hw_add(struct ieee80211_loc
|
||||
if (local->ops->wake_tx_queue)
|
||||
DEBUGFS_ADD_MODE(aqm, 0600);
|
||||
|
||||
+ debugfs_create_u16("airtime_flags", 0600,
|
||||
+ phyd, &local->airtime_flags);
|
||||
+
|
||||
statsd = debugfs_create_dir("statistics", phyd);
|
||||
|
||||
/* if the dir failed, don't put all the other things into the root! */
|
||||
--- a/net/mac80211/debugfs_sta.c
|
||||
+++ b/net/mac80211/debugfs_sta.c
|
||||
@@ -178,9 +178,9 @@ static ssize_t sta_aqm_read(struct file
|
||||
txqi->tin.tx_bytes,
|
||||
txqi->tin.tx_packets,
|
||||
txqi->flags,
|
||||
- txqi->flags & (1<<IEEE80211_TXQ_STOP) ? "STOP" : "RUN",
|
||||
- txqi->flags & (1<<IEEE80211_TXQ_AMPDU) ? " AMPDU" : "",
|
||||
- txqi->flags & (1<<IEEE80211_TXQ_NO_AMSDU) ? " NO-AMSDU" : "");
|
||||
+ test_bit(IEEE80211_TXQ_STOP, &txqi->flags) ? "STOP" : "RUN",
|
||||
+ test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags) ? " AMPDU" : "",
|
||||
+ test_bit(IEEE80211_TXQ_NO_AMSDU, &txqi->flags) ? " NO-AMSDU" : "");
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
@@ -192,6 +192,64 @@ static ssize_t sta_aqm_read(struct file
|
||||
}
|
||||
STA_OPS(aqm);
|
||||
|
||||
+static ssize_t sta_airtime_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->sdata->local;
|
||||
+ size_t bufsz = 200;
|
||||
+ char *buf = kzalloc(bufsz, GFP_KERNEL), *p = buf;
|
||||
+ u64 rx_airtime = 0, tx_airtime = 0;
|
||||
+ s64 deficit[IEEE80211_NUM_ACS];
|
||||
+ ssize_t rv;
|
||||
+ int ac;
|
||||
+
|
||||
+ if (!buf)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
||||
+ spin_lock_bh(&local->active_txq_lock[ac]);
|
||||
+ rx_airtime += sta->airtime[ac].rx_airtime;
|
||||
+ tx_airtime += sta->airtime[ac].tx_airtime;
|
||||
+ deficit[ac] = sta->airtime[ac].deficit;
|
||||
+ spin_unlock_bh(&local->active_txq_lock[ac]);
|
||||
+ }
|
||||
+
|
||||
+ p += scnprintf(p, bufsz + buf - p,
|
||||
+ "RX: %llu us\nTX: %llu us\nWeight: %u\n"
|
||||
+ "Deficit: VO: %lld us VI: %lld us BE: %lld us BK: %lld us\n",
|
||||
+ rx_airtime,
|
||||
+ tx_airtime,
|
||||
+ sta->airtime_weight,
|
||||
+ deficit[0],
|
||||
+ deficit[1],
|
||||
+ deficit[2],
|
||||
+ deficit[3]);
|
||||
+
|
||||
+ rv = simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
|
||||
+ kfree(buf);
|
||||
+ return rv;
|
||||
+}
|
||||
+
|
||||
+static ssize_t sta_airtime_write(struct file *file, const char __user *userbuf,
|
||||
+ size_t count, loff_t *ppos)
|
||||
+{
|
||||
+ struct sta_info *sta = file->private_data;
|
||||
+ struct ieee80211_local *local = sta->sdata->local;
|
||||
+ int ac;
|
||||
+
|
||||
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
||||
+ spin_lock_bh(&local->active_txq_lock[ac]);
|
||||
+ sta->airtime[ac].rx_airtime = 0;
|
||||
+ sta->airtime[ac].tx_airtime = 0;
|
||||
+ sta->airtime[ac].deficit = sta->airtime_weight;
|
||||
+ spin_unlock_bh(&local->active_txq_lock[ac]);
|
||||
+ }
|
||||
+
|
||||
+ return count;
|
||||
+}
|
||||
+STA_OPS_RW(airtime);
|
||||
+
|
||||
static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
@@ -546,6 +604,10 @@ void ieee80211_sta_debugfs_add(struct st
|
||||
if (local->ops->wake_tx_queue)
|
||||
DEBUGFS_ADD(aqm);
|
||||
|
||||
+ if (wiphy_ext_feature_isset(local->hw.wiphy,
|
||||
+ NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
|
||||
+ DEBUGFS_ADD(airtime);
|
||||
+
|
||||
if (sizeof(sta->driver_buffered_tids) == sizeof(u32))
|
||||
debugfs_create_x32("driver_buffered_tids", 0400,
|
||||
sta->debugfs_dir,
|
||||
--- a/net/mac80211/ieee80211_i.h
|
||||
+++ b/net/mac80211/ieee80211_i.h
|
||||
@@ -1136,6 +1136,8 @@ struct ieee80211_local {
|
||||
struct list_head active_txqs[IEEE80211_NUM_ACS];
|
||||
u16 schedule_round[IEEE80211_NUM_ACS];
|
||||
|
||||
+ u16 airtime_flags;
|
||||
+
|
||||
const struct ieee80211_ops *ops;
|
||||
|
||||
/*
|
||||
--- a/net/mac80211/main.c
|
||||
+++ b/net/mac80211/main.c
|
||||
@@ -656,6 +656,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_
|
||||
INIT_LIST_HEAD(&local->active_txqs[i]);
|
||||
spin_lock_init(&local->active_txq_lock[i]);
|
||||
}
|
||||
+ local->airtime_flags = AIRTIME_USE_TX | AIRTIME_USE_RX;
|
||||
|
||||
INIT_LIST_HEAD(&local->chanctx_list);
|
||||
mutex_init(&local->chanctx_mtx);
|
||||
@@ -1142,6 +1143,9 @@ int ieee80211_register_hw(struct ieee802
|
||||
if (!local->hw.max_nan_de_entries)
|
||||
local->hw.max_nan_de_entries = IEEE80211_MAX_NAN_INSTANCE_ID;
|
||||
|
||||
+ if (!local->hw.weight_multiplier)
|
||||
+ local->hw.weight_multiplier = 1;
|
||||
+
|
||||
result = ieee80211_wep_init(local);
|
||||
if (result < 0)
|
||||
wiphy_debug(local->hw.wiphy, "Failed to initialize wep: %d\n",
|
||||
--- a/net/mac80211/sta_info.c
|
||||
+++ b/net/mac80211/sta_info.c
|
||||
@@ -90,7 +90,6 @@ static void __cleanup_single_sta(struct
|
||||
struct tid_ampdu_tx *tid_tx;
|
||||
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
- struct fq *fq = &local->fq;
|
||||
struct ps_data *ps;
|
||||
|
||||
if (test_sta_flag(sta, WLAN_STA_PS_STA) ||
|
||||
@@ -115,9 +114,7 @@ static void __cleanup_single_sta(struct
|
||||
for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
|
||||
struct txq_info *txqi = to_txq_info(sta->sta.txq[i]);
|
||||
|
||||
- spin_lock_bh(&fq->lock);
|
||||
ieee80211_txq_purge(local, txqi);
|
||||
- spin_unlock_bh(&fq->lock);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,9 +396,12 @@ struct sta_info *sta_info_alloc(struct i
|
||||
if (sta_prepare_rate_control(local, sta, gfp))
|
||||
goto free_txq;
|
||||
|
||||
+ sta->airtime_weight = IEEE80211_DEFAULT_AIRTIME_WEIGHT;
|
||||
+
|
||||
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
|
||||
skb_queue_head_init(&sta->ps_tx_buf[i]);
|
||||
skb_queue_head_init(&sta->tx_filtered[i]);
|
||||
+ sta->airtime[i].deficit = sta->airtime_weight;
|
||||
}
|
||||
|
||||
for (i = 0; i < IEEE80211_NUM_TIDS; i++)
|
||||
@@ -1838,6 +1838,27 @@ void ieee80211_sta_set_buffered(struct i
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_sta_set_buffered);
|
||||
|
||||
+void ieee80211_sta_register_airtime(struct ieee80211_sta *pubsta, u8 tid,
|
||||
+ u32 tx_airtime, u32 rx_airtime)
|
||||
+{
|
||||
+ struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
|
||||
+ struct ieee80211_local *local = sta->sdata->local;
|
||||
+ u8 ac = ieee80211_ac_from_tid(tid);
|
||||
+ u32 airtime = 0;
|
||||
+
|
||||
+ if (sta->local->airtime_flags & AIRTIME_USE_TX)
|
||||
+ airtime += tx_airtime;
|
||||
+ if (sta->local->airtime_flags & AIRTIME_USE_RX)
|
||||
+ airtime += rx_airtime;
|
||||
+
|
||||
+ spin_lock_bh(&local->active_txq_lock[ac]);
|
||||
+ sta->airtime[ac].tx_airtime += tx_airtime;
|
||||
+ sta->airtime[ac].rx_airtime += rx_airtime;
|
||||
+ sta->airtime[ac].deficit -= airtime;
|
||||
+ spin_unlock_bh(&local->active_txq_lock[ac]);
|
||||
+}
|
||||
+EXPORT_SYMBOL(ieee80211_sta_register_airtime);
|
||||
+
|
||||
int sta_info_move_state(struct sta_info *sta,
|
||||
enum ieee80211_sta_state new_state)
|
||||
{
|
||||
@@ -2208,6 +2229,23 @@ void sta_set_sinfo(struct sta_info *sta,
|
||||
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
|
||||
}
|
||||
|
||||
+ if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_RX_DURATION))) {
|
||||
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
|
||||
+ sinfo->rx_duration += sta->airtime[ac].rx_airtime;
|
||||
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_DURATION);
|
||||
+ }
|
||||
+
|
||||
+ if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_TX_DURATION))) {
|
||||
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
|
||||
+ sinfo->tx_duration += sta->airtime[ac].tx_airtime;
|
||||
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_DURATION);
|
||||
+ }
|
||||
+
|
||||
+ if (!(sinfo->filled & BIT_ULL(NL80211_STA_INFO_AIRTIME_WEIGHT))) {
|
||||
+ sinfo->airtime_weight = sta->airtime_weight;
|
||||
+ sinfo->filled |= BIT_ULL(NL80211_STA_INFO_AIRTIME_WEIGHT);
|
||||
+ }
|
||||
+
|
||||
sinfo->rx_dropped_misc = sta->rx_stats.dropped;
|
||||
if (sta->pcpu_rx_stats) {
|
||||
for_each_possible_cpu(cpu) {
|
||||
--- a/net/mac80211/sta_info.h
|
||||
+++ b/net/mac80211/sta_info.h
|
||||
@@ -127,6 +127,16 @@ enum ieee80211_agg_stop_reason {
|
||||
AGG_STOP_DESTROY_STA,
|
||||
};
|
||||
|
||||
+/* Debugfs flags to enable/disable use of RX/TX airtime in scheduler */
|
||||
+#define AIRTIME_USE_TX BIT(0)
|
||||
+#define AIRTIME_USE_RX BIT(1)
|
||||
+
|
||||
+struct airtime_info {
|
||||
+ u64 rx_airtime;
|
||||
+ u64 tx_airtime;
|
||||
+ s64 deficit;
|
||||
+};
|
||||
+
|
||||
struct sta_info;
|
||||
|
||||
/**
|
||||
@@ -563,6 +573,9 @@ struct sta_info {
|
||||
} tx_stats;
|
||||
u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1];
|
||||
|
||||
+ struct airtime_info airtime[IEEE80211_NUM_ACS];
|
||||
+ u16 airtime_weight;
|
||||
+
|
||||
/*
|
||||
* Aggregation information, locked with lock.
|
||||
*/
|
||||
--- a/net/mac80211/status.c
|
||||
+++ b/net/mac80211/status.c
|
||||
@@ -827,6 +827,12 @@ static void __ieee80211_tx_status(struct
|
||||
ieee80211_sta_tx_notify(sta->sdata, (void *) skb->data,
|
||||
acked, info->status.tx_time);
|
||||
|
||||
+ if (info->status.tx_time &&
|
||||
+ wiphy_ext_feature_isset(local->hw.wiphy,
|
||||
+ NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
|
||||
+ ieee80211_sta_register_airtime(&sta->sta, tid,
|
||||
+ info->status.tx_time, 0);
|
||||
+
|
||||
if (ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
|
||||
if (acked) {
|
||||
if (sta->status_stats.lost_packets)
|
||||
--- a/net/mac80211/tx.c
|
||||
+++ b/net/mac80211/tx.c
|
||||
@@ -1463,8 +1463,11 @@ void ieee80211_txq_purge(struct ieee8021
|
||||
struct fq *fq = &local->fq;
|
||||
struct fq_tin *tin = &txqi->tin;
|
||||
|
||||
+ spin_lock_bh(&fq->lock);
|
||||
fq_tin_reset(fq, tin, fq_skb_free_func);
|
||||
ieee80211_purge_tx_queue(&local->hw, &txqi->frags);
|
||||
+ spin_unlock_bh(&fq->lock);
|
||||
+
|
||||
spin_lock_bh(&local->active_txq_lock[txqi->txq.ac]);
|
||||
list_del_init(&txqi->schedule_order);
|
||||
spin_unlock_bh(&local->active_txq_lock[txqi->txq.ac]);
|
||||
@@ -3642,11 +3645,28 @@ struct ieee80211_txq *ieee80211_next_txq
|
||||
|
||||
lockdep_assert_held(&local->active_txq_lock[ac]);
|
||||
|
||||
+ begin:
|
||||
txqi = list_first_entry_or_null(&local->active_txqs[ac],
|
||||
struct txq_info,
|
||||
schedule_order);
|
||||
+ if (!txqi)
|
||||
+ return NULL;
|
||||
+
|
||||
+ if (txqi->txq.sta) {
|
||||
+ struct sta_info *sta = container_of(txqi->txq.sta,
|
||||
+ struct sta_info, sta);
|
||||
+
|
||||
+ if (sta->airtime[txqi->txq.ac].deficit < 0) {
|
||||
+ sta->airtime[txqi->txq.ac].deficit +=
|
||||
+ sta->airtime_weight;
|
||||
+ list_move_tail(&txqi->schedule_order,
|
||||
+ &local->active_txqs[txqi->txq.ac]);
|
||||
+ goto begin;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
|
||||
- if (!txqi || txqi->schedule_round == local->schedule_round[ac])
|
||||
+ if (txqi->schedule_round == local->schedule_round[ac])
|
||||
return NULL;
|
||||
|
||||
list_del_init(&txqi->schedule_order);
|
||||
@@ -3664,12 +3684,74 @@ void ieee80211_return_txq(struct ieee802
|
||||
lockdep_assert_held(&local->active_txq_lock[txq->ac]);
|
||||
|
||||
if (list_empty(&txqi->schedule_order) &&
|
||||
- (!skb_queue_empty(&txqi->frags) || txqi->tin.backlog_packets))
|
||||
- list_add_tail(&txqi->schedule_order,
|
||||
- &local->active_txqs[txq->ac]);
|
||||
+ (!skb_queue_empty(&txqi->frags) || txqi->tin.backlog_packets)) {
|
||||
+ /* If airtime accounting is active, always enqueue STAs at the
|
||||
+ * head of the list to ensure that they only get moved to the
|
||||
+ * back by the airtime DRR scheduler once they have a negative
|
||||
+ * deficit. A station that already has a negative deficit will
|
||||
+ * get immediately moved to the back of the list on the next
|
||||
+ * call to ieee80211_next_txq().
|
||||
+ */
|
||||
+ if (txqi->txq.sta &&
|
||||
+ wiphy_ext_feature_isset(local->hw.wiphy,
|
||||
+ NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
|
||||
+ list_add(&txqi->schedule_order,
|
||||
+ &local->active_txqs[txq->ac]);
|
||||
+ else
|
||||
+ list_add_tail(&txqi->schedule_order,
|
||||
+ &local->active_txqs[txq->ac]);
|
||||
+ }
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_return_txq);
|
||||
|
||||
+bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
|
||||
+ struct ieee80211_txq *txq)
|
||||
+{
|
||||
+ struct ieee80211_local *local = hw_to_local(hw);
|
||||
+ struct txq_info *iter, *tmp, *txqi = to_txq_info(txq);
|
||||
+ struct sta_info *sta;
|
||||
+ u8 ac = txq->ac;
|
||||
+
|
||||
+ lockdep_assert_held(&local->active_txq_lock[ac]);
|
||||
+
|
||||
+ if (!txqi->txq.sta)
|
||||
+ goto out;
|
||||
+
|
||||
+ if (list_empty(&txqi->schedule_order))
|
||||
+ goto out;
|
||||
+
|
||||
+ list_for_each_entry_safe(iter, tmp, &local->active_txqs[ac],
|
||||
+ schedule_order) {
|
||||
+ if (iter == txqi)
|
||||
+ break;
|
||||
+
|
||||
+ if (!iter->txq.sta) {
|
||||
+ list_move_tail(&iter->schedule_order,
|
||||
+ &local->active_txqs[ac]);
|
||||
+ continue;
|
||||
+ }
|
||||
+ sta = container_of(iter->txq.sta, struct sta_info, sta);
|
||||
+ if (sta->airtime[ac].deficit < 0)
|
||||
+ sta->airtime[ac].deficit += sta->airtime_weight;
|
||||
+ list_move_tail(&iter->schedule_order, &local->active_txqs[ac]);
|
||||
+ }
|
||||
+
|
||||
+ sta = container_of(txqi->txq.sta, struct sta_info, sta);
|
||||
+ if (sta->airtime[ac].deficit >= 0)
|
||||
+ goto out;
|
||||
+
|
||||
+ sta->airtime[ac].deficit += sta->airtime_weight;
|
||||
+ list_move_tail(&txqi->schedule_order, &local->active_txqs[ac]);
|
||||
+
|
||||
+ return false;
|
||||
+out:
|
||||
+ if (!list_empty(&txqi->schedule_order))
|
||||
+ list_del_init(&txqi->schedule_order);
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+EXPORT_SYMBOL(ieee80211_txq_may_transmit);
|
||||
+
|
||||
void ieee80211_txq_schedule_start(struct ieee80211_hw *hw, u8 ac)
|
||||
__acquires(txq_lock)
|
||||
{
|
||||
@@ -0,0 +1,73 @@
|
||||
From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= <toke@redhat.com>
|
||||
Date: Tue, 22 Jan 2019 15:20:16 +0100
|
||||
Subject: [PATCH] mac80211: Expose ieee80211_schedule_txq() function
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Since we reworked ieee80211_return_txq() so it assumes that the caller
|
||||
takes care of logging, we need another function that can be called without
|
||||
holding any locks. Introduce ieee80211_schedule_txq() which serves this
|
||||
purpose.
|
||||
|
||||
Signed-off-by: Toke Høiland-Jørgensen <toke@redhat.com>
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/include/net/mac80211.h
|
||||
+++ b/include/net/mac80211.h
|
||||
@@ -6074,6 +6074,19 @@ void ieee80211_txq_schedule_end(struct i
|
||||
__releases(txq_lock);
|
||||
|
||||
/**
|
||||
+ * ieee80211_schedule_txq - schedule a TXQ for transmission
|
||||
+ *
|
||||
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
|
||||
+ * @txq: pointer obtained from station or virtual interface
|
||||
+ *
|
||||
+ * Schedules a TXQ for transmission if it is not already scheduled. Takes a
|
||||
+ * lock, which means it must *not* be called between
|
||||
+ * ieee80211_txq_schedule_start() and ieee80211_txq_schedule_end()
|
||||
+ */
|
||||
+void ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
|
||||
+ __acquires(txq_lock) __releases(txq_lock);
|
||||
+
|
||||
+/**
|
||||
* ieee80211_txq_may_transmit - check whether TXQ is allowed to transmit
|
||||
*
|
||||
* This function is used to check whether given txq is allowed to transmit by
|
||||
--- a/net/mac80211/driver-ops.h
|
||||
+++ b/net/mac80211/driver-ops.h
|
||||
@@ -1179,9 +1179,7 @@ static inline void drv_wake_tx_queue(str
|
||||
static inline void schedule_and_wake_txq(struct ieee80211_local *local,
|
||||
struct txq_info *txqi)
|
||||
{
|
||||
- spin_lock_bh(&local->active_txq_lock[txqi->txq.ac]);
|
||||
- ieee80211_return_txq(&local->hw, &txqi->txq);
|
||||
- spin_unlock_bh(&local->active_txq_lock[txqi->txq.ac]);
|
||||
+ ieee80211_schedule_txq(&local->hw, &txqi->txq);
|
||||
drv_wake_tx_queue(local, txqi);
|
||||
}
|
||||
|
||||
--- a/net/mac80211/tx.c
|
||||
+++ b/net/mac80211/tx.c
|
||||
@@ -3704,6 +3704,19 @@ void ieee80211_return_txq(struct ieee802
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_return_txq);
|
||||
|
||||
+void ieee80211_schedule_txq(struct ieee80211_hw *hw,
|
||||
+ struct ieee80211_txq *txq)
|
||||
+ __acquires(txq_lock) __releases(txq_lock)
|
||||
+{
|
||||
+ struct ieee80211_local *local = hw_to_local(hw);
|
||||
+ struct txq_info *txqi = to_txq_info(txq);
|
||||
+
|
||||
+ spin_lock_bh(&local->active_txq_lock[txq->ac]);
|
||||
+ ieee80211_return_txq(hw, txq);
|
||||
+ spin_unlock_bh(&local->active_txq_lock[txq->ac]);
|
||||
+}
|
||||
+EXPORT_SYMBOL(ieee80211_schedule_txq);
|
||||
+
|
||||
bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
|
||||
struct ieee80211_txq *txq)
|
||||
{
|
||||
@@ -0,0 +1,228 @@
|
||||
From: Janusz Dziedzic <janusz.dziedzic@tieto.com>
|
||||
Date: Fri, 19 Feb 2016 11:01:49 +0100
|
||||
Subject: [PATCH] mac80211: add hdrlen to ieee80211_tx_data
|
||||
|
||||
This is preparation for adding support for inserting padding between the
|
||||
802.11 header and LLC data
|
||||
|
||||
Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/ieee80211_i.h
|
||||
+++ b/net/mac80211/ieee80211_i.h
|
||||
@@ -179,6 +179,7 @@ struct ieee80211_tx_data {
|
||||
struct ieee80211_tx_rate rate;
|
||||
|
||||
unsigned int flags;
|
||||
+ unsigned int hdrlen;
|
||||
};
|
||||
|
||||
|
||||
--- a/net/mac80211/tx.c
|
||||
+++ b/net/mac80211/tx.c
|
||||
@@ -925,7 +925,7 @@ ieee80211_tx_h_fragment(struct ieee80211
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
struct ieee80211_hdr *hdr = (void *)skb->data;
|
||||
int frag_threshold = tx->local->hw.wiphy->frag_threshold;
|
||||
- int hdrlen;
|
||||
+ int hdrlen = tx->hdrlen;
|
||||
int fragnum;
|
||||
|
||||
/* no matter what happens, tx->skb moves to tx->skbs */
|
||||
@@ -946,8 +946,6 @@ ieee80211_tx_h_fragment(struct ieee80211
|
||||
if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU))
|
||||
return TX_DROP;
|
||||
|
||||
- hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
||||
-
|
||||
/* internal error, why isn't DONTFRAG set? */
|
||||
if (WARN_ON(skb->len + FCS_LEN <= frag_threshold))
|
||||
return TX_DROP;
|
||||
@@ -1178,6 +1176,8 @@ ieee80211_tx_prepare(struct ieee80211_su
|
||||
|
||||
hdr = (struct ieee80211_hdr *) skb->data;
|
||||
|
||||
+ tx->hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
||||
+
|
||||
if (likely(sta)) {
|
||||
if (!IS_ERR(sta))
|
||||
tx->sta = sta;
|
||||
@@ -3536,6 +3536,7 @@ begin:
|
||||
tx.local = local;
|
||||
tx.skb = skb;
|
||||
tx.sdata = vif_to_sdata(info->control.vif);
|
||||
+ tx.hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
||||
|
||||
if (txq->sta) {
|
||||
tx.sta = container_of(txq->sta, struct sta_info, sta);
|
||||
@@ -3580,7 +3581,7 @@ begin:
|
||||
|
||||
if (tx.key &&
|
||||
(tx.key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV))
|
||||
- pn_offs = ieee80211_hdrlen(hdr->frame_control);
|
||||
+ pn_offs = tx.hdrlen;
|
||||
|
||||
ieee80211_xmit_fast_finish(sta->sdata, sta, pn_offs,
|
||||
tx.key, skb);
|
||||
@@ -4039,6 +4040,7 @@ ieee80211_build_data_template(struct iee
|
||||
hdr = (void *)skb->data;
|
||||
tx.sta = sta_info_get(sdata, hdr->addr1);
|
||||
tx.skb = skb;
|
||||
+ tx.hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
||||
|
||||
if (ieee80211_tx_h_select_key(&tx) != TX_CONTINUE) {
|
||||
rcu_read_unlock();
|
||||
--- a/net/mac80211/util.c
|
||||
+++ b/net/mac80211/util.c
|
||||
@@ -1396,6 +1396,7 @@ void ieee80211_send_auth(struct ieee8021
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct sk_buff *skb;
|
||||
struct ieee80211_mgmt *mgmt;
|
||||
+ unsigned int hdrlen;
|
||||
int err;
|
||||
|
||||
/* 24 + 6 = header + auth_algo + auth_transaction + status_code */
|
||||
@@ -1419,8 +1420,10 @@ void ieee80211_send_auth(struct ieee8021
|
||||
skb_put_data(skb, extra, extra_len);
|
||||
|
||||
if (auth_alg == WLAN_AUTH_SHARED_KEY && transaction == 3) {
|
||||
+ hdrlen = ieee80211_hdrlen(mgmt->frame_control);
|
||||
mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
|
||||
- err = ieee80211_wep_encrypt(local, skb, key, key_len, key_idx);
|
||||
+ err = ieee80211_wep_encrypt(local, skb, hdrlen, key,
|
||||
+ key_len, key_idx);
|
||||
WARN_ON(err);
|
||||
}
|
||||
|
||||
--- a/net/mac80211/wep.c
|
||||
+++ b/net/mac80211/wep.c
|
||||
@@ -89,11 +89,11 @@ static void ieee80211_wep_get_iv(struct
|
||||
|
||||
static u8 *ieee80211_wep_add_iv(struct ieee80211_local *local,
|
||||
struct sk_buff *skb,
|
||||
+ unsigned int hdrlen,
|
||||
int keylen, int keyidx)
|
||||
{
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
- unsigned int hdrlen;
|
||||
u8 *newhdr;
|
||||
|
||||
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
|
||||
@@ -101,7 +101,6 @@ static u8 *ieee80211_wep_add_iv(struct i
|
||||
if (WARN_ON(skb_headroom(skb) < IEEE80211_WEP_IV_LEN))
|
||||
return NULL;
|
||||
|
||||
- hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
||||
newhdr = skb_push(skb, IEEE80211_WEP_IV_LEN);
|
||||
memmove(newhdr, newhdr + IEEE80211_WEP_IV_LEN, hdrlen);
|
||||
|
||||
@@ -160,6 +159,7 @@ int ieee80211_wep_encrypt_data(struct cr
|
||||
*/
|
||||
int ieee80211_wep_encrypt(struct ieee80211_local *local,
|
||||
struct sk_buff *skb,
|
||||
+ unsigned int hdrlen,
|
||||
const u8 *key, int keylen, int keyidx)
|
||||
{
|
||||
u8 *iv;
|
||||
@@ -169,7 +169,7 @@ int ieee80211_wep_encrypt(struct ieee802
|
||||
if (WARN_ON(skb_tailroom(skb) < IEEE80211_WEP_ICV_LEN))
|
||||
return -1;
|
||||
|
||||
- iv = ieee80211_wep_add_iv(local, skb, keylen, keyidx);
|
||||
+ iv = ieee80211_wep_add_iv(local, skb, hdrlen, keylen, keyidx);
|
||||
if (!iv)
|
||||
return -1;
|
||||
|
||||
@@ -307,13 +307,14 @@ static int wep_encrypt_skb(struct ieee80
|
||||
struct ieee80211_key_conf *hw_key = info->control.hw_key;
|
||||
|
||||
if (!hw_key) {
|
||||
- if (ieee80211_wep_encrypt(tx->local, skb, tx->key->conf.key,
|
||||
+ if (ieee80211_wep_encrypt(tx->local, skb, tx->hdrlen,
|
||||
+ tx->key->conf.key,
|
||||
tx->key->conf.keylen,
|
||||
tx->key->conf.keyidx))
|
||||
return -1;
|
||||
} else if ((hw_key->flags & IEEE80211_KEY_FLAG_GENERATE_IV) ||
|
||||
(hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) {
|
||||
- if (!ieee80211_wep_add_iv(tx->local, skb,
|
||||
+ if (!ieee80211_wep_add_iv(tx->local, skb, tx->hdrlen,
|
||||
tx->key->conf.keylen,
|
||||
tx->key->conf.keyidx))
|
||||
return -1;
|
||||
--- a/net/mac80211/wep.h
|
||||
+++ b/net/mac80211/wep.h
|
||||
@@ -22,6 +22,7 @@ int ieee80211_wep_encrypt_data(struct cr
|
||||
size_t klen, u8 *data, size_t data_len);
|
||||
int ieee80211_wep_encrypt(struct ieee80211_local *local,
|
||||
struct sk_buff *skb,
|
||||
+ unsigned int hdrlen,
|
||||
const u8 *key, int keylen, int keyidx);
|
||||
int ieee80211_wep_decrypt_data(struct crypto_cipher *tfm, u8 *rc4key,
|
||||
size_t klen, u8 *data, size_t data_len);
|
||||
--- a/net/mac80211/wpa.c
|
||||
+++ b/net/mac80211/wpa.c
|
||||
@@ -44,7 +44,7 @@ ieee80211_tx_h_michael_mic_add(struct ie
|
||||
skb->len < 24 || !ieee80211_is_data_present(hdr->frame_control))
|
||||
return TX_CONTINUE;
|
||||
|
||||
- hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
||||
+ hdrlen = tx->hdrlen;
|
||||
if (skb->len < hdrlen)
|
||||
return TX_DROP;
|
||||
|
||||
@@ -195,7 +195,6 @@ mic_fail_no_key:
|
||||
|
||||
static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
|
||||
{
|
||||
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
||||
struct ieee80211_key *key = tx->key;
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
unsigned int hdrlen;
|
||||
@@ -210,7 +209,7 @@ static int tkip_encrypt_skb(struct ieee8
|
||||
return 0;
|
||||
}
|
||||
|
||||
- hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
||||
+ hdrlen = tx->hdrlen;
|
||||
len = skb->len - hdrlen;
|
||||
|
||||
if (info->control.hw_key)
|
||||
@@ -428,7 +427,7 @@ static int ccmp_encrypt_skb(struct ieee8
|
||||
return 0;
|
||||
}
|
||||
|
||||
- hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
||||
+ hdrlen = tx->hdrlen;
|
||||
len = skb->len - hdrlen;
|
||||
|
||||
if (info->control.hw_key)
|
||||
@@ -660,7 +659,7 @@ static int gcmp_encrypt_skb(struct ieee8
|
||||
return 0;
|
||||
}
|
||||
|
||||
- hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
||||
+ hdrlen = tx->hdrlen;
|
||||
len = skb->len - hdrlen;
|
||||
|
||||
if (info->control.hw_key)
|
||||
@@ -800,7 +799,6 @@ static ieee80211_tx_result
|
||||
ieee80211_crypto_cs_encrypt(struct ieee80211_tx_data *tx,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
||||
struct ieee80211_key *key = tx->key;
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
int hdrlen;
|
||||
@@ -816,8 +814,7 @@ ieee80211_crypto_cs_encrypt(struct ieee8
|
||||
pskb_expand_head(skb, iv_len, 0, GFP_ATOMIC)))
|
||||
return TX_DROP;
|
||||
|
||||
- hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
||||
-
|
||||
+ hdrlen = tx->hdrlen;
|
||||
pos = skb_push(skb, iv_len);
|
||||
memmove(pos, pos + iv_len, hdrlen);
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
From: Janusz Dziedzic <janusz.dziedzic@tieto.com>
|
||||
Date: Sun, 10 Mar 2019 17:22:08 +0100
|
||||
Subject: [PATCH] mac80211: add TX_NEEDS_ALIGNED4_SKBS hw flag
|
||||
|
||||
The driver should set this flag if the hardware requires tx skb data
|
||||
(starting with the LLC header) to be aligned to 4 bytes.
|
||||
|
||||
Padding is added after ieee80211_hdr, before IV/LLC.
|
||||
|
||||
Before this patch, we have to do memmove(hdrlen) twice in the driver:
|
||||
Once before we pass this to HW and once again in tx completion
|
||||
(to fix up the skb for monitor mode).
|
||||
|
||||
With this patch we can skip this memmove() and thus reduce CPU cycles in
|
||||
the data path.
|
||||
|
||||
Signed-off-by: Janusz Dziedzic <janusz.dziedzic@tieto.com>
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/include/net/mac80211.h
|
||||
+++ b/include/net/mac80211.h
|
||||
@@ -2140,6 +2140,9 @@ struct ieee80211_txq {
|
||||
* @IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN: Driver does not report accurate A-MPDU
|
||||
* length in tx status information
|
||||
*
|
||||
+ * @IEEE80211_HW_TX_NEEDS_ALIGNED4_SKBS: Driver need aligned skbs to four-byte.
|
||||
+ * Padding will be added after ieee80211_hdr, before IV/LLC.
|
||||
+ *
|
||||
* @NUM_IEEE80211_HW_FLAGS: number of hardware flags, used for sizing arrays
|
||||
*/
|
||||
enum ieee80211_hw_flags {
|
||||
@@ -2186,6 +2189,7 @@ enum ieee80211_hw_flags {
|
||||
IEEE80211_HW_DEAUTH_NEED_MGD_TX_PREP,
|
||||
IEEE80211_HW_DOESNT_SUPPORT_QOS_NDP,
|
||||
IEEE80211_HW_TX_STATUS_NO_AMPDU_LEN,
|
||||
+ IEEE80211_HW_TX_NEEDS_ALIGNED4_SKBS,
|
||||
|
||||
/* keep last, obviously */
|
||||
NUM_IEEE80211_HW_FLAGS
|
||||
@@ -2472,6 +2476,40 @@ ieee80211_get_alt_retry_rate(const struc
|
||||
void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb);
|
||||
|
||||
/**
|
||||
+ * ieee80211_hdr_padsize - get size of padding between 802.11 header and LLC
|
||||
+ * @hw: the hardware
|
||||
+ * @hdrlen: 802.11 header length
|
||||
+ */
|
||||
+static inline unsigned int
|
||||
+ieee80211_hdr_padsize(struct ieee80211_hw *hw, unsigned int hdrlen)
|
||||
+{
|
||||
+ /*
|
||||
+ * While hdrlen is already aligned to two-byte boundaries,
|
||||
+ * simple check with & 2 will return correct padsize.
|
||||
+ */
|
||||
+ if (ieee80211_hw_check(hw, TX_NEEDS_ALIGNED4_SKBS))
|
||||
+ return hdrlen & 2;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * ieee80211_padded_hdrlen - get padded 802.11 header size
|
||||
+ * @hw: the hardware
|
||||
+ * @fc: frame control field in little-endian format
|
||||
+ */
|
||||
+static inline unsigned int
|
||||
+ieee80211_padded_hdrlen(struct ieee80211_hw *hw, __le16 fc)
|
||||
+{
|
||||
+ unsigned int hdrlen;
|
||||
+
|
||||
+ hdrlen = ieee80211_hdrlen(fc);
|
||||
+ hdrlen += ieee80211_hdr_padsize(hw, hdrlen);
|
||||
+
|
||||
+ return hdrlen;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+/**
|
||||
* DOC: Hardware crypto acceleration
|
||||
*
|
||||
* mac80211 is capable of taking advantage of many hardware
|
||||
--- a/net/mac80211/iface.c
|
||||
+++ b/net/mac80211/iface.c
|
||||
@@ -1871,6 +1871,10 @@ int ieee80211_if_add(struct ieee80211_lo
|
||||
+ 8 /* rfc1042/bridge tunnel */
|
||||
- ETH_HLEN /* ethernet hard_header_len */
|
||||
+ IEEE80211_ENCRYPT_HEADROOM;
|
||||
+
|
||||
+ if (ieee80211_hw_check(&local->hw, TX_NEEDS_ALIGNED4_SKBS))
|
||||
+ ndev->needed_headroom += 2; /* padding */
|
||||
+
|
||||
ndev->needed_tailroom = IEEE80211_ENCRYPT_TAILROOM;
|
||||
|
||||
ret = dev_alloc_name(ndev, ndev->name);
|
||||
--- a/net/mac80211/mesh_pathtbl.c
|
||||
+++ b/net/mac80211/mesh_pathtbl.c
|
||||
@@ -105,13 +105,15 @@ void mesh_path_assign_nexthop(struct mes
|
||||
static void prepare_for_gate(struct sk_buff *skb, char *dst_addr,
|
||||
struct mesh_path *gate_mpath)
|
||||
{
|
||||
+ struct ieee80211_sub_if_data *sdata = gate_mpath->sdata;
|
||||
+ struct ieee80211_hw *hw = &sdata->local->hw;
|
||||
struct ieee80211_hdr *hdr;
|
||||
struct ieee80211s_hdr *mshdr;
|
||||
int mesh_hdrlen, hdrlen;
|
||||
char *next_hop;
|
||||
|
||||
hdr = (struct ieee80211_hdr *) skb->data;
|
||||
- hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
||||
+ hdrlen = ieee80211_padded_hdrlen(hw, hdr->frame_control);
|
||||
mshdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);
|
||||
|
||||
if (!(mshdr->flags & MESH_FLAGS_AE)) {
|
||||
--- a/net/mac80211/rx.c
|
||||
+++ b/net/mac80211/rx.c
|
||||
@@ -2621,7 +2621,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80
|
||||
struct ieee80211_local *local = rx->local;
|
||||
struct ieee80211_sub_if_data *sdata = rx->sdata;
|
||||
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
|
||||
- u16 ac, q, hdrlen;
|
||||
+ u16 ac, q, hdrlen, padsize;
|
||||
int tailroom = 0;
|
||||
|
||||
hdr = (struct ieee80211_hdr *) skb->data;
|
||||
@@ -2714,7 +2714,9 @@ ieee80211_rx_h_mesh_fwding(struct ieee80
|
||||
if (sdata->crypto_tx_tailroom_needed_cnt)
|
||||
tailroom = IEEE80211_ENCRYPT_TAILROOM;
|
||||
|
||||
- fwd_skb = skb_copy_expand(skb, local->tx_headroom +
|
||||
+ padsize = ieee80211_hdr_padsize(&local->hw, hdrlen);
|
||||
+
|
||||
+ fwd_skb = skb_copy_expand(skb, local->tx_headroom + padsize +
|
||||
sdata->encrypt_headroom,
|
||||
tailroom, GFP_ATOMIC);
|
||||
if (!fwd_skb)
|
||||
@@ -2746,6 +2748,12 @@ ieee80211_rx_h_mesh_fwding(struct ieee80
|
||||
return RX_DROP_MONITOR;
|
||||
}
|
||||
|
||||
+ if (padsize) {
|
||||
+ skb_push(fwd_skb, padsize);
|
||||
+ memmove(fwd_skb->data, skb->data + padsize, hdrlen);
|
||||
+ memset(fwd_skb->data + hdrlen, 0, padsize);
|
||||
+ }
|
||||
+
|
||||
IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, fwded_frames);
|
||||
ieee80211_add_pending_skb(local, fwd_skb);
|
||||
out:
|
||||
--- a/net/mac80211/sta_info.h
|
||||
+++ b/net/mac80211/sta_info.h
|
||||
@@ -311,7 +311,7 @@ struct ieee80211_fast_tx {
|
||||
u8 hdr_len;
|
||||
u8 sa_offs, da_offs, pn_offs;
|
||||
u8 band;
|
||||
- u8 hdr[30 + 2 + IEEE80211_FAST_XMIT_MAX_IV +
|
||||
+ u8 hdr[30 + 2 + 2 + IEEE80211_FAST_XMIT_MAX_IV +
|
||||
sizeof(rfc1042_header)] __aligned(2);
|
||||
|
||||
struct rcu_head rcu_head;
|
||||
--- a/net/mac80211/status.c
|
||||
+++ b/net/mac80211/status.c
|
||||
@@ -514,6 +514,7 @@ static void ieee80211_report_used_skb(st
|
||||
{
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
struct ieee80211_hdr *hdr = (void *)skb->data;
|
||||
+ struct ieee80211_hw *hw = &local->hw;
|
||||
bool acked = info->flags & IEEE80211_TX_STAT_ACK;
|
||||
|
||||
if (dropped)
|
||||
@@ -530,7 +531,7 @@ static void ieee80211_report_used_skb(st
|
||||
skb->dev = NULL;
|
||||
} else {
|
||||
unsigned int hdr_size =
|
||||
- ieee80211_hdrlen(hdr->frame_control);
|
||||
+ ieee80211_padded_hdrlen(hw, hdr->frame_control);
|
||||
|
||||
/* Check to see if packet is a TDLS teardown packet */
|
||||
if (ieee80211_is_data(hdr->frame_control) &&
|
||||
@@ -654,9 +655,22 @@ void ieee80211_tx_monitor(struct ieee802
|
||||
struct sk_buff *skb2;
|
||||
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
+ struct ieee80211_hdr *hdr = (void *)skb->data;
|
||||
struct net_device *prev_dev = NULL;
|
||||
+ unsigned int hdrlen, padsize;
|
||||
int rtap_len;
|
||||
|
||||
+ /* Remove padding if was added */
|
||||
+ if (ieee80211_hw_check(&local->hw, TX_NEEDS_ALIGNED4_SKBS)) {
|
||||
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
||||
+ padsize = ieee80211_hdr_padsize(&local->hw, hdrlen);
|
||||
+
|
||||
+ if (padsize && skb->len > hdrlen + padsize) {
|
||||
+ memmove(skb->data + padsize, skb->data, hdrlen);
|
||||
+ skb_pull(skb, padsize);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
/* send frame to monitor interfaces now */
|
||||
rtap_len = ieee80211_tx_radiotap_len(info);
|
||||
if (WARN_ON_ONCE(skb_headroom(skb) < rtap_len)) {
|
||||
--- a/net/mac80211/tkip.c
|
||||
+++ b/net/mac80211/tkip.c
|
||||
@@ -201,10 +201,12 @@ void ieee80211_get_tkip_p2k(struct ieee8
|
||||
{
|
||||
struct ieee80211_key *key = (struct ieee80211_key *)
|
||||
container_of(keyconf, struct ieee80211_key, conf);
|
||||
+ struct ieee80211_hw *hw = &key->local->hw;
|
||||
const u8 *tk = &key->conf.key[NL80211_TKIP_DATA_OFFSET_ENCR_KEY];
|
||||
struct tkip_ctx *ctx = &key->u.tkip.tx;
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
|
||||
- const u8 *data = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control);
|
||||
+ const u8 *data = (u8 *)hdr + ieee80211_padded_hdrlen(hw,
|
||||
+ hdr->frame_control);
|
||||
u32 iv32 = get_unaligned_le32(&data[4]);
|
||||
u16 iv16 = data[2] | (data[0] << 8);
|
||||
|
||||
--- a/net/mac80211/tx.c
|
||||
+++ b/net/mac80211/tx.c
|
||||
@@ -1175,8 +1175,7 @@ ieee80211_tx_prepare(struct ieee80211_su
|
||||
info->flags &= ~IEEE80211_TX_INTFL_NEED_TXPROCESSING;
|
||||
|
||||
hdr = (struct ieee80211_hdr *) skb->data;
|
||||
-
|
||||
- tx->hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
||||
+ tx->hdrlen = ieee80211_padded_hdrlen(&local->hw, hdr->frame_control);
|
||||
|
||||
if (likely(sta)) {
|
||||
if (!IS_ERR(sta))
|
||||
@@ -2233,7 +2232,7 @@ netdev_tx_t ieee80211_monitor_start_xmit
|
||||
goto fail;
|
||||
|
||||
hdr = (struct ieee80211_hdr *)(skb->data + len_rthdr);
|
||||
- hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
||||
+ hdrlen = ieee80211_padded_hdrlen(&local->hw, hdr->frame_control);
|
||||
|
||||
if (skb->len < len_rthdr + hdrlen)
|
||||
goto fail;
|
||||
@@ -2452,7 +2451,7 @@ static struct sk_buff *ieee80211_build_h
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
struct ieee80211_sub_if_data *ap_sdata;
|
||||
enum nl80211_band band;
|
||||
- int ret;
|
||||
+ int padsize, ret;
|
||||
|
||||
if (IS_ERR(sta))
|
||||
sta = NULL;
|
||||
@@ -2751,7 +2750,9 @@ static struct sk_buff *ieee80211_build_h
|
||||
}
|
||||
|
||||
skb_pull(skb, skip_header_bytes);
|
||||
+ padsize = ieee80211_hdr_padsize(&local->hw, hdrlen);
|
||||
head_need = hdrlen + encaps_len + meshhdrlen - skb_headroom(skb);
|
||||
+ head_need += padsize;
|
||||
|
||||
/*
|
||||
* So we need to modify the skb header and hence need a copy of
|
||||
@@ -2784,6 +2785,9 @@ static struct sk_buff *ieee80211_build_h
|
||||
memcpy(skb_push(skb, meshhdrlen), &mesh_hdr, meshhdrlen);
|
||||
#endif
|
||||
|
||||
+ if (padsize)
|
||||
+ memset(skb_push(skb, padsize), 0, padsize);
|
||||
+
|
||||
if (ieee80211_is_data_qos(fc)) {
|
||||
__le16 *qos_control;
|
||||
|
||||
@@ -2960,6 +2964,8 @@ void ieee80211_check_fast_xmit(struct st
|
||||
fc |= cpu_to_le16(IEEE80211_STYPE_QOS_DATA);
|
||||
}
|
||||
|
||||
+ build.hdr_len += ieee80211_hdr_padsize(&local->hw, build.hdr_len);
|
||||
+
|
||||
/* We store the key here so there's no point in using rcu_dereference()
|
||||
* but that's fine because the code that changes the pointers will call
|
||||
* this function after doing so. For a single CPU that would be enough,
|
||||
@@ -3536,7 +3542,7 @@ begin:
|
||||
tx.local = local;
|
||||
tx.skb = skb;
|
||||
tx.sdata = vif_to_sdata(info->control.vif);
|
||||
- tx.hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
||||
+ tx.hdrlen = ieee80211_padded_hdrlen(hw, hdr->frame_control);
|
||||
|
||||
if (txq->sta) {
|
||||
tx.sta = container_of(txq->sta, struct sta_info, sta);
|
||||
@@ -4040,7 +4046,7 @@ ieee80211_build_data_template(struct iee
|
||||
hdr = (void *)skb->data;
|
||||
tx.sta = sta_info_get(sdata, hdr->addr1);
|
||||
tx.skb = skb;
|
||||
- tx.hdrlen = ieee80211_hdrlen(hdr->frame_control);
|
||||
+ tx.hdrlen = ieee80211_padded_hdrlen(&tx.local->hw, hdr->frame_control);
|
||||
|
||||
if (ieee80211_tx_h_select_key(&tx) != TX_CONTINUE) {
|
||||
rcu_read_unlock();
|
||||
--- a/net/mac80211/debugfs.c
|
||||
+++ b/net/mac80211/debugfs.c
|
||||
@@ -215,6 +215,7 @@ static const char *hw_flag_names[] = {
|
||||
FLAG(DEAUTH_NEED_MGD_TX_PREP),
|
||||
FLAG(DOESNT_SUPPORT_QOS_NDP),
|
||||
FLAG(TX_STATUS_NO_AMPDU_LEN),
|
||||
+ FLAG(TX_NEEDS_ALIGNED4_SKBS),
|
||||
#undef FLAG
|
||||
};
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Wed, 13 Mar 2019 19:09:22 +0100
|
||||
Subject: [PATCH] mac80211: rework locking for txq scheduling / airtime
|
||||
fairness
|
||||
|
||||
Holding the lock around the entire duration of tx scheduling can create
|
||||
some nasty lock contention, especially when processing airtime information
|
||||
from the tx status or the rx path.
|
||||
Improve locking by only holding the active_txq_lock for lookups / scheduling
|
||||
list modifications.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/include/net/mac80211.h
|
||||
+++ b/include/net/mac80211.h
|
||||
@@ -6069,8 +6069,6 @@ struct sk_buff *ieee80211_tx_dequeue(str
|
||||
* @hw: pointer as obtained from ieee80211_alloc_hw()
|
||||
* @ac: AC number to return packets from.
|
||||
*
|
||||
- * Should only be called between calls to ieee80211_txq_schedule_start()
|
||||
- * and ieee80211_txq_schedule_end().
|
||||
* Returns the next txq if successful, %NULL if no queue is eligible. If a txq
|
||||
* is returned, it should be returned with ieee80211_return_txq() after the
|
||||
* driver has finished scheduling it.
|
||||
@@ -6078,51 +6076,41 @@ struct sk_buff *ieee80211_tx_dequeue(str
|
||||
struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac);
|
||||
|
||||
/**
|
||||
- * ieee80211_return_txq - return a TXQ previously acquired by ieee80211_next_txq()
|
||||
- *
|
||||
- * @hw: pointer as obtained from ieee80211_alloc_hw()
|
||||
- * @txq: pointer obtained from station or virtual interface
|
||||
- *
|
||||
- * Should only be called between calls to ieee80211_txq_schedule_start()
|
||||
- * and ieee80211_txq_schedule_end().
|
||||
- */
|
||||
-void ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq);
|
||||
-
|
||||
-/**
|
||||
- * ieee80211_txq_schedule_start - acquire locks for safe scheduling of an AC
|
||||
+ * ieee80211_txq_schedule_start - start new scheduling round for TXQs
|
||||
*
|
||||
* @hw: pointer as obtained from ieee80211_alloc_hw()
|
||||
* @ac: AC number to acquire locks for
|
||||
*
|
||||
- * Acquire locks needed to schedule TXQs from the given AC. Should be called
|
||||
- * before ieee80211_next_txq() or ieee80211_return_txq().
|
||||
+ * Should be called before ieee80211_next_txq() or ieee80211_return_txq().
|
||||
*/
|
||||
-void ieee80211_txq_schedule_start(struct ieee80211_hw *hw, u8 ac)
|
||||
- __acquires(txq_lock);
|
||||
+void ieee80211_txq_schedule_start(struct ieee80211_hw *hw, u8 ac);
|
||||
+
|
||||
+/* (deprecated) */
|
||||
+static inline void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac)
|
||||
+{
|
||||
+}
|
||||
|
||||
/**
|
||||
- * ieee80211_txq_schedule_end - release locks for safe scheduling of an AC
|
||||
+ * ieee80211_schedule_txq - schedule a TXQ for transmission
|
||||
*
|
||||
* @hw: pointer as obtained from ieee80211_alloc_hw()
|
||||
- * @ac: AC number to acquire locks for
|
||||
+ * @txq: pointer obtained from station or virtual interface
|
||||
*
|
||||
- * Release locks previously acquired by ieee80211_txq_schedule_end().
|
||||
+ * Schedules a TXQ for transmission if it is not already scheduled.
|
||||
*/
|
||||
-void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac)
|
||||
- __releases(txq_lock);
|
||||
+void ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq);
|
||||
|
||||
/**
|
||||
- * ieee80211_schedule_txq - schedule a TXQ for transmission
|
||||
+ * ieee80211_return_txq - return a TXQ previously acquired by ieee80211_next_txq()
|
||||
*
|
||||
* @hw: pointer as obtained from ieee80211_alloc_hw()
|
||||
* @txq: pointer obtained from station or virtual interface
|
||||
- *
|
||||
- * Schedules a TXQ for transmission if it is not already scheduled. Takes a
|
||||
- * lock, which means it must *not* be called between
|
||||
- * ieee80211_txq_schedule_start() and ieee80211_txq_schedule_end()
|
||||
*/
|
||||
-void ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
|
||||
- __acquires(txq_lock) __releases(txq_lock);
|
||||
+static inline void
|
||||
+ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
|
||||
+{
|
||||
+ ieee80211_schedule_txq(hw, txq);
|
||||
+}
|
||||
|
||||
/**
|
||||
* ieee80211_txq_may_transmit - check whether TXQ is allowed to transmit
|
||||
--- a/net/mac80211/tx.c
|
||||
+++ b/net/mac80211/tx.c
|
||||
@@ -3648,16 +3648,17 @@ EXPORT_SYMBOL(ieee80211_tx_dequeue);
|
||||
struct ieee80211_txq *ieee80211_next_txq(struct ieee80211_hw *hw, u8 ac)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
+ struct ieee80211_txq *ret = NULL;
|
||||
struct txq_info *txqi = NULL;
|
||||
|
||||
- lockdep_assert_held(&local->active_txq_lock[ac]);
|
||||
+ spin_lock_bh(&local->active_txq_lock[ac]);
|
||||
|
||||
begin:
|
||||
txqi = list_first_entry_or_null(&local->active_txqs[ac],
|
||||
struct txq_info,
|
||||
schedule_order);
|
||||
if (!txqi)
|
||||
- return NULL;
|
||||
+ goto out;
|
||||
|
||||
if (txqi->txq.sta) {
|
||||
struct sta_info *sta = container_of(txqi->txq.sta,
|
||||
@@ -3674,21 +3675,25 @@ struct ieee80211_txq *ieee80211_next_txq
|
||||
|
||||
|
||||
if (txqi->schedule_round == local->schedule_round[ac])
|
||||
- return NULL;
|
||||
+ goto out;
|
||||
|
||||
list_del_init(&txqi->schedule_order);
|
||||
txqi->schedule_round = local->schedule_round[ac];
|
||||
- return &txqi->txq;
|
||||
+ ret = &txqi->txq;
|
||||
+
|
||||
+out:
|
||||
+ spin_unlock_bh(&local->active_txq_lock[ac]);
|
||||
+ return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_next_txq);
|
||||
|
||||
-void ieee80211_return_txq(struct ieee80211_hw *hw,
|
||||
- struct ieee80211_txq *txq)
|
||||
+void ieee80211_schedule_txq(struct ieee80211_hw *hw,
|
||||
+ struct ieee80211_txq *txq)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
struct txq_info *txqi = to_txq_info(txq);
|
||||
|
||||
- lockdep_assert_held(&local->active_txq_lock[txq->ac]);
|
||||
+ spin_lock_bh(&local->active_txq_lock[txq->ac]);
|
||||
|
||||
if (list_empty(&txqi->schedule_order) &&
|
||||
(!skb_queue_empty(&txqi->frags) || txqi->tin.backlog_packets)) {
|
||||
@@ -3708,18 +3713,7 @@ void ieee80211_return_txq(struct ieee802
|
||||
list_add_tail(&txqi->schedule_order,
|
||||
&local->active_txqs[txq->ac]);
|
||||
}
|
||||
-}
|
||||
-EXPORT_SYMBOL(ieee80211_return_txq);
|
||||
|
||||
-void ieee80211_schedule_txq(struct ieee80211_hw *hw,
|
||||
- struct ieee80211_txq *txq)
|
||||
- __acquires(txq_lock) __releases(txq_lock)
|
||||
-{
|
||||
- struct ieee80211_local *local = hw_to_local(hw);
|
||||
- struct txq_info *txqi = to_txq_info(txq);
|
||||
-
|
||||
- spin_lock_bh(&local->active_txq_lock[txq->ac]);
|
||||
- ieee80211_return_txq(hw, txq);
|
||||
spin_unlock_bh(&local->active_txq_lock[txq->ac]);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_schedule_txq);
|
||||
@@ -3732,7 +3726,7 @@ bool ieee80211_txq_may_transmit(struct i
|
||||
struct sta_info *sta;
|
||||
u8 ac = txq->ac;
|
||||
|
||||
- lockdep_assert_held(&local->active_txq_lock[ac]);
|
||||
+ spin_lock_bh(&local->active_txq_lock[ac]);
|
||||
|
||||
if (!txqi->txq.sta)
|
||||
goto out;
|
||||
@@ -3762,34 +3756,27 @@ bool ieee80211_txq_may_transmit(struct i
|
||||
|
||||
sta->airtime[ac].deficit += sta->airtime_weight;
|
||||
list_move_tail(&txqi->schedule_order, &local->active_txqs[ac]);
|
||||
+ spin_unlock_bh(&local->active_txq_lock[ac]);
|
||||
|
||||
return false;
|
||||
out:
|
||||
if (!list_empty(&txqi->schedule_order))
|
||||
list_del_init(&txqi->schedule_order);
|
||||
+ spin_unlock_bh(&local->active_txq_lock[ac]);
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_txq_may_transmit);
|
||||
|
||||
void ieee80211_txq_schedule_start(struct ieee80211_hw *hw, u8 ac)
|
||||
- __acquires(txq_lock)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
|
||||
spin_lock_bh(&local->active_txq_lock[ac]);
|
||||
local->schedule_round[ac]++;
|
||||
-}
|
||||
-EXPORT_SYMBOL(ieee80211_txq_schedule_start);
|
||||
-
|
||||
-void ieee80211_txq_schedule_end(struct ieee80211_hw *hw, u8 ac)
|
||||
- __releases(txq_lock)
|
||||
-{
|
||||
- struct ieee80211_local *local = hw_to_local(hw);
|
||||
-
|
||||
spin_unlock_bh(&local->active_txq_lock[ac]);
|
||||
}
|
||||
-EXPORT_SYMBOL(ieee80211_txq_schedule_end);
|
||||
+EXPORT_SYMBOL(ieee80211_txq_schedule_start);
|
||||
|
||||
void __ieee80211_subif_start_xmit(struct sk_buff *skb,
|
||||
struct net_device *dev,
|
||||
@@ -0,0 +1,96 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sat, 16 Mar 2019 17:43:58 +0100
|
||||
Subject: [PATCH] mac80211: mesh: drop redundant rcu_read_lock/unlock calls
|
||||
|
||||
The callers of these functions are all within RCU locked sections
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/mesh_hwmp.c
|
||||
+++ b/net/mac80211/mesh_hwmp.c
|
||||
@@ -1122,16 +1122,13 @@ int mesh_nexthop_resolve(struct ieee8021
|
||||
struct mesh_path *mpath;
|
||||
struct sk_buff *skb_to_free = NULL;
|
||||
u8 *target_addr = hdr->addr3;
|
||||
- int err = 0;
|
||||
|
||||
/* Nulls are only sent to peers for PS and should be pre-addressed */
|
||||
if (ieee80211_is_qos_nullfunc(hdr->frame_control))
|
||||
return 0;
|
||||
|
||||
- rcu_read_lock();
|
||||
- err = mesh_nexthop_lookup(sdata, skb);
|
||||
- if (!err)
|
||||
- goto endlookup;
|
||||
+ if (!mesh_nexthop_lookup(sdata, skb))
|
||||
+ return 0;
|
||||
|
||||
/* no nexthop found, start resolving */
|
||||
mpath = mesh_path_lookup(sdata, target_addr);
|
||||
@@ -1139,8 +1136,7 @@ int mesh_nexthop_resolve(struct ieee8021
|
||||
mpath = mesh_path_add(sdata, target_addr);
|
||||
if (IS_ERR(mpath)) {
|
||||
mesh_path_discard_frame(sdata, skb);
|
||||
- err = PTR_ERR(mpath);
|
||||
- goto endlookup;
|
||||
+ return PTR_ERR(mpath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1154,13 +1150,10 @@ int mesh_nexthop_resolve(struct ieee8021
|
||||
info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
|
||||
ieee80211_set_qos_hdr(sdata, skb);
|
||||
skb_queue_tail(&mpath->frame_queue, skb);
|
||||
- err = -ENOENT;
|
||||
if (skb_to_free)
|
||||
mesh_path_discard_frame(sdata, skb_to_free);
|
||||
|
||||
-endlookup:
|
||||
- rcu_read_unlock();
|
||||
- return err;
|
||||
+ return -ENOENT;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1180,13 +1173,10 @@ int mesh_nexthop_lookup(struct ieee80211
|
||||
struct sta_info *next_hop;
|
||||
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
|
||||
u8 *target_addr = hdr->addr3;
|
||||
- int err = -ENOENT;
|
||||
|
||||
- rcu_read_lock();
|
||||
mpath = mesh_path_lookup(sdata, target_addr);
|
||||
-
|
||||
if (!mpath || !(mpath->flags & MESH_PATH_ACTIVE))
|
||||
- goto endlookup;
|
||||
+ return -ENOENT;
|
||||
|
||||
if (time_after(jiffies,
|
||||
mpath->exp_time -
|
||||
@@ -1201,12 +1191,10 @@ int mesh_nexthop_lookup(struct ieee80211
|
||||
memcpy(hdr->addr1, next_hop->sta.addr, ETH_ALEN);
|
||||
memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN);
|
||||
ieee80211_mps_set_frame_flags(sdata, next_hop, hdr);
|
||||
- err = 0;
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
-endlookup:
|
||||
- rcu_read_unlock();
|
||||
- return err;
|
||||
+ return -ENOENT;
|
||||
}
|
||||
|
||||
void mesh_path_timer(struct timer_list *t)
|
||||
--- a/net/mac80211/mesh_pathtbl.c
|
||||
+++ b/net/mac80211/mesh_pathtbl.c
|
||||
@@ -219,7 +219,7 @@ static struct mesh_path *mpath_lookup(st
|
||||
{
|
||||
struct mesh_path *mpath;
|
||||
|
||||
- mpath = rhashtable_lookup_fast(&tbl->rhead, dst, mesh_rht_params);
|
||||
+ mpath = rhashtable_lookup(&tbl->rhead, dst, mesh_rht_params);
|
||||
|
||||
if (mpath && mpath_expired(mpath)) {
|
||||
spin_lock_bh(&mpath->state_lock);
|
||||
@@ -0,0 +1,140 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sat, 16 Mar 2019 17:57:38 +0100
|
||||
Subject: [PATCH] mac80211: calculate hash for fq without holding fq->lock
|
||||
in itxq enqueue
|
||||
|
||||
Reduces lock contention on enqueue/dequeue of iTXQ packets
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/include/net/fq_impl.h
|
||||
+++ b/include/net/fq_impl.h
|
||||
@@ -107,29 +107,31 @@ begin:
|
||||
return skb;
|
||||
}
|
||||
|
||||
-static struct fq_flow *fq_flow_classify(struct fq *fq,
|
||||
- struct fq_tin *tin,
|
||||
- struct sk_buff *skb,
|
||||
- fq_flow_get_default_t get_default_func)
|
||||
+static u32 fq_flow_idx(struct fq *fq, struct sk_buff *skb)
|
||||
{
|
||||
- struct fq_flow *flow;
|
||||
- u32 hash;
|
||||
- u32 idx;
|
||||
-
|
||||
- lockdep_assert_held(&fq->lock);
|
||||
-
|
||||
#if LINUX_VERSION_IS_GEQ(5,3,10) || \
|
||||
LINUX_VERSION_IN_RANGE(4,19,83, 4,20,0) || \
|
||||
LINUX_VERSION_IN_RANGE(4,14,153, 4,15,0) || \
|
||||
LINUX_VERSION_IN_RANGE(4,9,200, 4,10,0) || \
|
||||
LINUX_VERSION_IN_RANGE(4,4,200, 4,5,0)
|
||||
- hash = skb_get_hash_perturb(skb, &fq->perturbation);
|
||||
+ u32 hash = skb_get_hash_perturb(skb, &fq->perturbation);
|
||||
#else
|
||||
- hash = skb_get_hash_perturb(skb, fq->perturbation);
|
||||
+ u32 hash = skb_get_hash_perturb(skb, fq->perturbation);
|
||||
#endif
|
||||
- idx = reciprocal_scale(hash, fq->flows_cnt);
|
||||
- flow = &fq->flows[idx];
|
||||
|
||||
+ return reciprocal_scale(hash, fq->flows_cnt);
|
||||
+}
|
||||
+
|
||||
+static struct fq_flow *fq_flow_classify(struct fq *fq,
|
||||
+ struct fq_tin *tin, u32 idx,
|
||||
+ struct sk_buff *skb,
|
||||
+ fq_flow_get_default_t get_default_func)
|
||||
+{
|
||||
+ struct fq_flow *flow;
|
||||
+
|
||||
+ lockdep_assert_held(&fq->lock);
|
||||
+
|
||||
+ flow = &fq->flows[idx];
|
||||
if (flow->tin && flow->tin != tin) {
|
||||
flow = get_default_func(fq, tin, idx, skb);
|
||||
tin->collisions++;
|
||||
@@ -161,7 +163,7 @@ static void fq_recalc_backlog(struct fq
|
||||
}
|
||||
|
||||
static void fq_tin_enqueue(struct fq *fq,
|
||||
- struct fq_tin *tin,
|
||||
+ struct fq_tin *tin, u32 idx,
|
||||
struct sk_buff *skb,
|
||||
fq_skb_free_t free_func,
|
||||
fq_flow_get_default_t get_default_func)
|
||||
@@ -171,7 +173,7 @@ static void fq_tin_enqueue(struct fq *fq
|
||||
|
||||
lockdep_assert_held(&fq->lock);
|
||||
|
||||
- flow = fq_flow_classify(fq, tin, skb, get_default_func);
|
||||
+ flow = fq_flow_classify(fq, tin, idx, skb, get_default_func);
|
||||
|
||||
flow->tin = tin;
|
||||
flow->backlog += skb->len;
|
||||
--- a/net/mac80211/tx.c
|
||||
+++ b/net/mac80211/tx.c
|
||||
@@ -1390,11 +1390,15 @@ static void ieee80211_txq_enqueue(struct
|
||||
{
|
||||
struct fq *fq = &local->fq;
|
||||
struct fq_tin *tin = &txqi->tin;
|
||||
+ u32 flow_idx = fq_flow_idx(fq, skb);
|
||||
|
||||
ieee80211_set_skb_enqueue_time(skb);
|
||||
- fq_tin_enqueue(fq, tin, skb,
|
||||
+
|
||||
+ spin_lock_bh(&fq->lock);
|
||||
+ fq_tin_enqueue(fq, tin, flow_idx, skb,
|
||||
fq_skb_free_func,
|
||||
fq_flow_get_default_func);
|
||||
+ spin_unlock_bh(&fq->lock);
|
||||
}
|
||||
|
||||
static bool fq_vlan_filter_func(struct fq *fq, struct fq_tin *tin,
|
||||
@@ -1564,7 +1568,6 @@ static bool ieee80211_queue_skb(struct i
|
||||
struct sta_info *sta,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
- struct fq *fq = &local->fq;
|
||||
struct ieee80211_vif *vif;
|
||||
struct txq_info *txqi;
|
||||
|
||||
@@ -1582,9 +1585,7 @@ static bool ieee80211_queue_skb(struct i
|
||||
if (!txqi)
|
||||
return false;
|
||||
|
||||
- spin_lock_bh(&fq->lock);
|
||||
ieee80211_txq_enqueue(local, txqi, skb);
|
||||
- spin_unlock_bh(&fq->lock);
|
||||
|
||||
schedule_and_wake_txq(local, txqi);
|
||||
|
||||
@@ -3211,6 +3212,7 @@ static bool ieee80211_amsdu_aggregate(st
|
||||
u8 max_subframes = sta->sta.max_amsdu_subframes;
|
||||
int max_frags = local->hw.max_tx_fragments;
|
||||
int max_amsdu_len = sta->sta.max_amsdu_len;
|
||||
+ u32 flow_idx;
|
||||
int orig_truesize;
|
||||
__be16 len;
|
||||
void *data;
|
||||
@@ -3233,6 +3235,8 @@ static bool ieee80211_amsdu_aggregate(st
|
||||
max_amsdu_len = min_t(int, max_amsdu_len,
|
||||
sta->sta.max_rc_amsdu_len);
|
||||
|
||||
+ flow_idx = fq_flow_idx(fq, skb);
|
||||
+
|
||||
spin_lock_bh(&fq->lock);
|
||||
|
||||
/* TODO: Ideally aggregation should be done on dequeue to remain
|
||||
@@ -3240,7 +3244,8 @@ static bool ieee80211_amsdu_aggregate(st
|
||||
*/
|
||||
|
||||
tin = &txqi->tin;
|
||||
- flow = fq_flow_classify(fq, tin, skb, fq_flow_get_default_func);
|
||||
+ flow = fq_flow_classify(fq, tin, flow_idx, skb,
|
||||
+ fq_flow_get_default_func);
|
||||
head = skb_peek_tail(&flow->queue);
|
||||
if (!head)
|
||||
goto out;
|
||||
@@ -0,0 +1,55 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sat, 16 Mar 2019 18:00:12 +0100
|
||||
Subject: [PATCH] mac80211: run late dequeue late tx handlers without
|
||||
holding fq->lock
|
||||
|
||||
Reduces lock contention on enqueue/dequeue of iTXQ packets
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/tx.c
|
||||
+++ b/net/mac80211/tx.c
|
||||
@@ -3518,6 +3518,7 @@ struct sk_buff *ieee80211_tx_dequeue(str
|
||||
ieee80211_tx_result r;
|
||||
struct ieee80211_vif *vif = txq->vif;
|
||||
|
||||
+begin:
|
||||
spin_lock_bh(&fq->lock);
|
||||
|
||||
if (test_bit(IEEE80211_TXQ_STOP, &txqi->flags) ||
|
||||
@@ -3534,11 +3535,12 @@ struct sk_buff *ieee80211_tx_dequeue(str
|
||||
if (skb)
|
||||
goto out;
|
||||
|
||||
-begin:
|
||||
skb = fq_tin_dequeue(fq, tin, fq_tin_dequeue_func);
|
||||
if (!skb)
|
||||
goto out;
|
||||
|
||||
+ spin_unlock_bh(&fq->lock);
|
||||
+
|
||||
hdr = (struct ieee80211_hdr *)skb->data;
|
||||
info = IEEE80211_SKB_CB(skb);
|
||||
|
||||
@@ -3602,8 +3604,11 @@ begin:
|
||||
|
||||
skb = __skb_dequeue(&tx.skbs);
|
||||
|
||||
- if (!skb_queue_empty(&tx.skbs))
|
||||
+ if (!skb_queue_empty(&tx.skbs)) {
|
||||
+ spin_lock_bh(&fq->lock);
|
||||
skb_queue_splice_tail(&tx.skbs, &txqi->frags);
|
||||
+ spin_unlock_bh(&fq->lock);
|
||||
+ }
|
||||
}
|
||||
|
||||
if (skb && skb_has_frag_list(skb) &&
|
||||
@@ -3642,6 +3647,7 @@ begin:
|
||||
}
|
||||
|
||||
IEEE80211_SKB_CB(skb)->control.vif = vif;
|
||||
+ return skb;
|
||||
|
||||
out:
|
||||
spin_unlock_bh(&fq->lock);
|
||||
@@ -0,0 +1,22 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sat, 16 Mar 2019 18:01:53 +0100
|
||||
Subject: [PATCH] mac80211: set NETIF_F_LLTX when using intermediate tx
|
||||
queues
|
||||
|
||||
When using iTXQ, tx sequence number allocation and statistics are run at
|
||||
dequeue time. Because of that, it is safe to enable NETIF_F_LLTX, which
|
||||
allows tx handlers to run on multiple CPUs in parallel.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/iface.c
|
||||
+++ b/net/mac80211/iface.c
|
||||
@@ -1301,6 +1301,7 @@ static void ieee80211_if_setup(struct ne
|
||||
static void ieee80211_if_setup_no_queue(struct net_device *dev)
|
||||
{
|
||||
ieee80211_if_setup(dev);
|
||||
+ dev->features |= NETIF_F_LLTX;
|
||||
#if LINUX_VERSION_IS_GEQ(4,3,0)
|
||||
dev->priv_flags |= IFF_NO_QUEUE;
|
||||
#else
|
||||
@@ -0,0 +1,105 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sun, 17 Mar 2019 14:26:59 +0100
|
||||
Subject: [PATCH] mac80211: make ieee80211_schedule_txq schedule empty TXQs
|
||||
|
||||
Currently there is no way for the driver to signal to mac80211 that it should
|
||||
schedule a TXQ even if there are no packets on the mac80211 part of that queue.
|
||||
This is problematic if the driver has an internal retry queue to deal with
|
||||
software A-MPDU retry.
|
||||
|
||||
This patch changes the behavior of ieee80211_schedule_txq to always schedule
|
||||
the queue, as its only user (ath9k) seems to expect such behavior already:
|
||||
it calls this function on tx status and on powersave wakeup whenever its
|
||||
internal retry queue is not empty.
|
||||
|
||||
Also add an extra argument to ieee80211_return_txq to get the same behavior.
|
||||
|
||||
This fixes an issue on ath9k where tx queues with packets to retry (and no
|
||||
new packets in mac80211) would not get serviced.
|
||||
|
||||
Fixes: 89cea7493a346 ("ath9k: Switch to mac80211 TXQ scheduling and airtime APIs")
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/include/net/mac80211.h
|
||||
+++ b/include/net/mac80211.h
|
||||
@@ -6090,26 +6090,42 @@ static inline void ieee80211_txq_schedul
|
||||
{
|
||||
}
|
||||
|
||||
+void __ieee80211_schedule_txq(struct ieee80211_hw *hw,
|
||||
+ struct ieee80211_txq *txq, bool force);
|
||||
+
|
||||
/**
|
||||
* ieee80211_schedule_txq - schedule a TXQ for transmission
|
||||
*
|
||||
* @hw: pointer as obtained from ieee80211_alloc_hw()
|
||||
* @txq: pointer obtained from station or virtual interface
|
||||
*
|
||||
- * Schedules a TXQ for transmission if it is not already scheduled.
|
||||
+ * Schedules a TXQ for transmission if it is not already scheduled,
|
||||
+ * even if mac80211 does not have any packets buffered.
|
||||
+ *
|
||||
+ * The driver may call this function if it has buffered packets for
|
||||
+ * this TXQ internally.
|
||||
*/
|
||||
-void ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq);
|
||||
+static inline void
|
||||
+ieee80211_schedule_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
|
||||
+{
|
||||
+ __ieee80211_schedule_txq(hw, txq, true);
|
||||
+}
|
||||
|
||||
/**
|
||||
* ieee80211_return_txq - return a TXQ previously acquired by ieee80211_next_txq()
|
||||
*
|
||||
* @hw: pointer as obtained from ieee80211_alloc_hw()
|
||||
* @txq: pointer obtained from station or virtual interface
|
||||
+ * @force: schedule txq even if mac80211 does not have any buffered packets.
|
||||
+ *
|
||||
+ * The driver may set force=true if it has buffered packets for this TXQ
|
||||
+ * internally.
|
||||
*/
|
||||
static inline void
|
||||
-ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq)
|
||||
+ieee80211_return_txq(struct ieee80211_hw *hw, struct ieee80211_txq *txq,
|
||||
+ bool force)
|
||||
{
|
||||
- ieee80211_schedule_txq(hw, txq);
|
||||
+ __ieee80211_schedule_txq(hw, txq, force);
|
||||
}
|
||||
|
||||
/**
|
||||
--- a/net/mac80211/tx.c
|
||||
+++ b/net/mac80211/tx.c
|
||||
@@ -3698,8 +3698,9 @@ out:
|
||||
}
|
||||
EXPORT_SYMBOL(ieee80211_next_txq);
|
||||
|
||||
-void ieee80211_schedule_txq(struct ieee80211_hw *hw,
|
||||
- struct ieee80211_txq *txq)
|
||||
+void __ieee80211_schedule_txq(struct ieee80211_hw *hw,
|
||||
+ struct ieee80211_txq *txq,
|
||||
+ bool force)
|
||||
{
|
||||
struct ieee80211_local *local = hw_to_local(hw);
|
||||
struct txq_info *txqi = to_txq_info(txq);
|
||||
@@ -3707,7 +3708,8 @@ void ieee80211_schedule_txq(struct ieee8
|
||||
spin_lock_bh(&local->active_txq_lock[txq->ac]);
|
||||
|
||||
if (list_empty(&txqi->schedule_order) &&
|
||||
- (!skb_queue_empty(&txqi->frags) || txqi->tin.backlog_packets)) {
|
||||
+ (force || !skb_queue_empty(&txqi->frags) ||
|
||||
+ txqi->tin.backlog_packets)) {
|
||||
/* If airtime accounting is active, always enqueue STAs at the
|
||||
* head of the list to ensure that they only get moved to the
|
||||
* back by the airtime DRR scheduler once they have a negative
|
||||
@@ -3727,7 +3729,7 @@ void ieee80211_schedule_txq(struct ieee8
|
||||
|
||||
spin_unlock_bh(&local->active_txq_lock[txq->ac]);
|
||||
}
|
||||
-EXPORT_SYMBOL(ieee80211_schedule_txq);
|
||||
+EXPORT_SYMBOL(__ieee80211_schedule_txq);
|
||||
|
||||
bool ieee80211_txq_may_transmit(struct ieee80211_hw *hw,
|
||||
struct ieee80211_txq *txq)
|
||||
@@ -0,0 +1,31 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Tue, 19 Mar 2019 11:36:12 +0100
|
||||
Subject: [PATCH] mac80211: un-schedule TXQs on powersave start
|
||||
|
||||
Once a station enters powersave, its queues should not be returned by
|
||||
ieee80211_next_txq() anymore. They will be re-scheduled again after the
|
||||
station has woken up again
|
||||
|
||||
Fixes: 1866760096bf4 ("mac80211: Add TXQ scheduling API")
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/rx.c
|
||||
+++ b/net/mac80211/rx.c
|
||||
@@ -1507,7 +1507,15 @@ static void sta_ps_start(struct sta_info
|
||||
return;
|
||||
|
||||
for (tid = 0; tid < ARRAY_SIZE(sta->sta.txq); tid++) {
|
||||
- if (txq_has_queue(sta->sta.txq[tid]))
|
||||
+ struct ieee80211_txq *txq = sta->sta.txq[tid];
|
||||
+ struct txq_info *txqi = to_txq_info(txq);
|
||||
+
|
||||
+ spin_lock(&local->active_txq_lock[txq->ac]);
|
||||
+ if (!list_empty(&txqi->schedule_order))
|
||||
+ list_del_init(&txqi->schedule_order);
|
||||
+ spin_unlock(&local->active_txq_lock[txq->ac]);
|
||||
+
|
||||
+ if (txq_has_queue(txq))
|
||||
set_bit(tid, &sta->txq_buffered_tids);
|
||||
else
|
||||
clear_bit(tid, &sta->txq_buffered_tids);
|
||||
@@ -0,0 +1,183 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Fri, 22 Mar 2019 18:06:03 +0100
|
||||
Subject: [PATCH] mac80211: when using iTXQ, select the queue in
|
||||
ieee80211_subif_start_xmit
|
||||
|
||||
When using iTXQ, the network stack does not need the real queue number, since
|
||||
mac80211 is using its internal queues anyway. In that case we can defer
|
||||
selecting the queue and remove a redundant station lookup in the tx path to save
|
||||
some CPU cycles.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/tx.c
|
||||
+++ b/net/mac80211/tx.c
|
||||
@@ -3797,6 +3797,7 @@ void __ieee80211_subif_start_xmit(struct
|
||||
u32 ctrl_flags)
|
||||
{
|
||||
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||||
+ struct ieee80211_local *local = sdata->local;
|
||||
struct sta_info *sta;
|
||||
struct sk_buff *next;
|
||||
|
||||
@@ -3810,7 +3811,15 @@ void __ieee80211_subif_start_xmit(struct
|
||||
if (ieee80211_lookup_ra_sta(sdata, skb, &sta))
|
||||
goto out_free;
|
||||
|
||||
- if (!IS_ERR_OR_NULL(sta)) {
|
||||
+ if (IS_ERR(sta))
|
||||
+ sta = NULL;
|
||||
+
|
||||
+ if (local->ops->wake_tx_queue) {
|
||||
+ u16 queue = __ieee80211_select_queue(sdata, sta, skb);
|
||||
+ skb_set_queue_mapping(skb, queue);
|
||||
+ }
|
||||
+
|
||||
+ if (sta) {
|
||||
struct ieee80211_fast_tx *fast_tx;
|
||||
|
||||
/* We need a bit of data queued to build aggregates properly, so
|
||||
--- a/net/mac80211/wme.c
|
||||
+++ b/net/mac80211/wme.c
|
||||
@@ -141,6 +141,42 @@ u16 ieee80211_select_queue_80211(struct
|
||||
return ieee80211_downgrade_queue(sdata, NULL, skb);
|
||||
}
|
||||
|
||||
+u16 __ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
|
||||
+ struct sta_info *sta, struct sk_buff *skb)
|
||||
+{
|
||||
+ struct mac80211_qos_map *qos_map;
|
||||
+ bool qos;
|
||||
+
|
||||
+ /* all mesh/ocb stations are required to support WME */
|
||||
+ if (sdata->vif.type == NL80211_IFTYPE_MESH_POINT ||
|
||||
+ sdata->vif.type == NL80211_IFTYPE_OCB)
|
||||
+ qos = true;
|
||||
+ else if (sta)
|
||||
+ qos = sta->sta.wme;
|
||||
+ else
|
||||
+ qos = false;
|
||||
+
|
||||
+ if (!qos) {
|
||||
+ skb->priority = 0; /* required for correct WPA/11i MIC */
|
||||
+ return IEEE80211_AC_BE;
|
||||
+ }
|
||||
+
|
||||
+ if (skb->protocol == sdata->control_port_protocol) {
|
||||
+ skb->priority = 7;
|
||||
+ goto downgrade;
|
||||
+ }
|
||||
+
|
||||
+ /* use the data classifier to determine what 802.1d tag the
|
||||
+ * data frame has */
|
||||
+ qos_map = rcu_dereference(sdata->qos_map);
|
||||
+ skb->priority = cfg80211_classify8021d(skb, qos_map ?
|
||||
+ &qos_map->qos_map : NULL);
|
||||
+
|
||||
+ downgrade:
|
||||
+ return ieee80211_downgrade_queue(sdata, sta, skb);
|
||||
+}
|
||||
+
|
||||
+
|
||||
/* Indicate which queue to use. */
|
||||
u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb)
|
||||
@@ -148,10 +184,12 @@ u16 ieee80211_select_queue(struct ieee80
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct sta_info *sta = NULL;
|
||||
const u8 *ra = NULL;
|
||||
- bool qos = false;
|
||||
- struct mac80211_qos_map *qos_map;
|
||||
u16 ret;
|
||||
|
||||
+ /* when using iTXQ, we can do this later */
|
||||
+ if (local->ops->wake_tx_queue)
|
||||
+ return 0;
|
||||
+
|
||||
if (local->hw.queues < IEEE80211_NUM_ACS || skb->len < 6) {
|
||||
skb->priority = 0; /* required for correct WPA/11i MIC */
|
||||
return 0;
|
||||
@@ -161,10 +199,8 @@ u16 ieee80211_select_queue(struct ieee80
|
||||
switch (sdata->vif.type) {
|
||||
case NL80211_IFTYPE_AP_VLAN:
|
||||
sta = rcu_dereference(sdata->u.vlan.sta);
|
||||
- if (sta) {
|
||||
- qos = sta->sta.wme;
|
||||
+ if (sta)
|
||||
break;
|
||||
- }
|
||||
/* fall through */
|
||||
case NL80211_IFTYPE_AP:
|
||||
ra = skb->data;
|
||||
@@ -172,56 +208,26 @@ u16 ieee80211_select_queue(struct ieee80
|
||||
case NL80211_IFTYPE_WDS:
|
||||
ra = sdata->u.wds.remote_addr;
|
||||
break;
|
||||
-#ifdef CPTCFG_MAC80211_MESH
|
||||
- case NL80211_IFTYPE_MESH_POINT:
|
||||
- qos = true;
|
||||
- break;
|
||||
-#endif
|
||||
case NL80211_IFTYPE_STATION:
|
||||
/* might be a TDLS station */
|
||||
sta = sta_info_get(sdata, skb->data);
|
||||
if (sta)
|
||||
- qos = sta->sta.wme;
|
||||
+ break;
|
||||
|
||||
ra = sdata->u.mgd.bssid;
|
||||
break;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
ra = skb->data;
|
||||
break;
|
||||
- case NL80211_IFTYPE_OCB:
|
||||
- /* all stations are required to support WME */
|
||||
- qos = true;
|
||||
- break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
- if (!sta && ra && !is_multicast_ether_addr(ra)) {
|
||||
+ if (!sta && ra && !is_multicast_ether_addr(ra))
|
||||
sta = sta_info_get(sdata, ra);
|
||||
- if (sta)
|
||||
- qos = sta->sta.wme;
|
||||
- }
|
||||
|
||||
- if (!qos) {
|
||||
- skb->priority = 0; /* required for correct WPA/11i MIC */
|
||||
- ret = IEEE80211_AC_BE;
|
||||
- goto out;
|
||||
- }
|
||||
+ ret = __ieee80211_select_queue(sdata, sta, skb);
|
||||
|
||||
- if (skb->protocol == sdata->control_port_protocol) {
|
||||
- skb->priority = 7;
|
||||
- goto downgrade;
|
||||
- }
|
||||
-
|
||||
- /* use the data classifier to determine what 802.1d tag the
|
||||
- * data frame has */
|
||||
- qos_map = rcu_dereference(sdata->qos_map);
|
||||
- skb->priority = cfg80211_classify8021d(skb, qos_map ?
|
||||
- &qos_map->qos_map : NULL);
|
||||
-
|
||||
- downgrade:
|
||||
- ret = ieee80211_downgrade_queue(sdata, sta, skb);
|
||||
- out:
|
||||
rcu_read_unlock();
|
||||
return ret;
|
||||
}
|
||||
--- a/net/mac80211/wme.h
|
||||
+++ b/net/mac80211/wme.h
|
||||
@@ -16,6 +16,8 @@
|
||||
u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb,
|
||||
struct ieee80211_hdr *hdr);
|
||||
+u16 __ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
|
||||
+ struct sta_info *sta, struct sk_buff *skb);
|
||||
u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
|
||||
struct sk_buff *skb);
|
||||
void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata,
|
||||
@@ -0,0 +1,58 @@
|
||||
From: Lorenzo Bianconi <lorenzo@kernel.org>
|
||||
Date: Tue, 16 Jul 2019 00:09:19 +0200
|
||||
Subject: [PATCH] mac80211: add IEEE80211_KEY_FLAG_GENERATE_MMIE to
|
||||
ieee80211_key_flags
|
||||
|
||||
Add IEEE80211_KEY_FLAG_GENERATE_MMIE flag to ieee80211_key_flags in order
|
||||
to allow the driver to notify mac80211 to generate MMIE and that it
|
||||
requires sequence number generation only.
|
||||
This is a preliminary patch to add BIP_CMAC_128 hw support to mt7615
|
||||
driver
|
||||
|
||||
Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org>
|
||||
Link: https://lore.kernel.org/r/dfe275f9aa0f1cc6b33085f9efd5d8447f68ad13.1563228405.git.lorenzo@kernel.org
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
|
||||
--- a/include/net/mac80211.h
|
||||
+++ b/include/net/mac80211.h
|
||||
@@ -1616,6 +1616,9 @@ struct wireless_dev *ieee80211_vif_to_wd
|
||||
* @IEEE80211_KEY_FLAG_PUT_MIC_SPACE: This flag should be set by the driver for
|
||||
* a TKIP key if it only requires MIC space. Do not set together with
|
||||
* @IEEE80211_KEY_FLAG_GENERATE_MMIC on the same key.
|
||||
+ * @IEEE80211_KEY_FLAG_GENERATE_MMIE: This flag should be set by the driver
|
||||
+ * for a AES_CMAC key to indicate that it requires sequence number
|
||||
+ * generation only
|
||||
*/
|
||||
enum ieee80211_key_flags {
|
||||
IEEE80211_KEY_FLAG_GENERATE_IV_MGMT = BIT(0),
|
||||
@@ -1627,6 +1630,7 @@ enum ieee80211_key_flags {
|
||||
IEEE80211_KEY_FLAG_RX_MGMT = BIT(6),
|
||||
IEEE80211_KEY_FLAG_RESERVE_TAILROOM = BIT(7),
|
||||
IEEE80211_KEY_FLAG_PUT_MIC_SPACE = BIT(8),
|
||||
+ IEEE80211_KEY_FLAG_GENERATE_MMIE = BIT(10),
|
||||
};
|
||||
|
||||
/**
|
||||
--- a/net/mac80211/wpa.c
|
||||
+++ b/net/mac80211/wpa.c
|
||||
@@ -947,7 +947,8 @@ ieee80211_crypto_aes_cmac_encrypt(struct
|
||||
|
||||
info = IEEE80211_SKB_CB(skb);
|
||||
|
||||
- if (info->control.hw_key)
|
||||
+ if (info->control.hw_key &&
|
||||
+ !(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIE))
|
||||
return TX_CONTINUE;
|
||||
|
||||
if (WARN_ON(skb_tailroom(skb) < sizeof(*mmie)))
|
||||
@@ -963,6 +964,9 @@ ieee80211_crypto_aes_cmac_encrypt(struct
|
||||
|
||||
bip_ipn_set64(mmie->sequence_number, pn64);
|
||||
|
||||
+ if (info->control.hw_key)
|
||||
+ return TX_CONTINUE;
|
||||
+
|
||||
bip_aad(skb, aad);
|
||||
|
||||
/*
|
||||
@@ -0,0 +1,61 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sat, 28 Sep 2019 15:44:06 +0200
|
||||
Subject: [PATCH] mac80211: minstrel: remove divisions in tx status path
|
||||
|
||||
Use a slightly different threshold for downgrading spatial streams to
|
||||
make it easier to calculate without divisions.
|
||||
Slightly reduces CPU overhead.
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/rc80211_minstrel.c
|
||||
+++ b/net/mac80211/rc80211_minstrel.c
|
||||
@@ -289,8 +289,7 @@ minstrel_tx_status(void *priv, struct ie
|
||||
mi->r[ndx].stats.success += success;
|
||||
}
|
||||
|
||||
- if (time_after(jiffies, mi->last_stats_update +
|
||||
- (mp->update_interval * HZ) / 1000))
|
||||
+ if (time_after(jiffies, mi->last_stats_update + mp->update_interval))
|
||||
minstrel_update_stats(mp, mi);
|
||||
}
|
||||
|
||||
--- a/net/mac80211/rc80211_minstrel_ht.c
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht.c
|
||||
@@ -937,23 +937,21 @@ minstrel_ht_tx_status(void *priv, struct
|
||||
*/
|
||||
rate = minstrel_get_ratestats(mi, mi->max_tp_rate[0]);
|
||||
if (rate->attempts > 30 &&
|
||||
- MINSTREL_FRAC(rate->success, rate->attempts) <
|
||||
- MINSTREL_FRAC(20, 100)) {
|
||||
+ rate->success < rate->attempts / 4) {
|
||||
minstrel_downgrade_rate(mi, &mi->max_tp_rate[0], true);
|
||||
update = true;
|
||||
}
|
||||
|
||||
rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate[1]);
|
||||
if (rate2->attempts > 30 &&
|
||||
- MINSTREL_FRAC(rate2->success, rate2->attempts) <
|
||||
- MINSTREL_FRAC(20, 100)) {
|
||||
+ rate2->success < rate2->attempts / 4) {
|
||||
minstrel_downgrade_rate(mi, &mi->max_tp_rate[1], false);
|
||||
update = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (time_after(jiffies, mi->last_stats_update +
|
||||
- (mp->update_interval / 2 * HZ) / 1000)) {
|
||||
+ mp->update_interval / 2)) {
|
||||
update = true;
|
||||
minstrel_ht_update_stats(mp, mi, true);
|
||||
}
|
||||
@@ -1640,7 +1638,7 @@ minstrel_ht_alloc(struct ieee80211_hw *h
|
||||
mp->has_mrr = true;
|
||||
|
||||
mp->hw = hw;
|
||||
- mp->update_interval = 100;
|
||||
+ mp->update_interval = HZ / 10;
|
||||
|
||||
#ifdef CPTCFG_MAC80211_DEBUGFS
|
||||
mp->fixed_rate_idx = (u32) -1;
|
||||
@@ -0,0 +1,235 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Sat, 28 Sep 2019 15:46:06 +0200
|
||||
Subject: [PATCH] mac80211: minstrel_ht: replace rate stats ewma with a
|
||||
better moving average
|
||||
|
||||
Rate success probability usually fluctuates a lot under normal conditions.
|
||||
With a simple EWMA, noise and fluctuation can be reduced by increasing the
|
||||
window length, but that comes at the cost of introducing lag on sudden
|
||||
changes.
|
||||
|
||||
This change replaces the EWMA implementation with a moving average that's
|
||||
designed to significantly reduce lag while keeping a bigger window size
|
||||
by being better at filtering out noise.
|
||||
|
||||
It is only slightly more expensive than the simple EWMA and still avoids
|
||||
divisions in its calculation.
|
||||
|
||||
The algorithm is adapted from an implementation intended for a completely
|
||||
different field (stock market trading), where the tradeoff of lag vs
|
||||
noise filtering is equally important.
|
||||
|
||||
The algorithm works in the same way as the "smoothing filter" from
|
||||
http://www.stockspotter.com/files/PredictiveIndicators.pdf adapted for
|
||||
fixed-point math with some constants, using only addition, bit shifts
|
||||
and multiplication
|
||||
|
||||
To better make use of the filtering and bigger window size, the update
|
||||
interval is cut in half.
|
||||
|
||||
For testing, the algorithm can be reverted to the older one via debugfs
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/rc80211_minstrel.c
|
||||
+++ b/net/mac80211/rc80211_minstrel.c
|
||||
@@ -157,14 +157,18 @@ minstrel_update_rates(struct minstrel_pr
|
||||
* Recalculate statistics and counters of a given rate
|
||||
*/
|
||||
void
|
||||
-minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs)
|
||||
+minstrel_calc_rate_stats(struct minstrel_priv *mp,
|
||||
+ struct minstrel_rate_stats *mrs)
|
||||
{
|
||||
unsigned int cur_prob;
|
||||
|
||||
if (unlikely(mrs->attempts > 0)) {
|
||||
mrs->sample_skipped = 0;
|
||||
cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts);
|
||||
- if (unlikely(!mrs->att_hist)) {
|
||||
+ if (mp->new_avg) {
|
||||
+ mrs->prob_ewma = minstrel_filter_avg_add(&mrs->avg,
|
||||
+ cur_prob);
|
||||
+ } else if (unlikely(!mrs->att_hist)) {
|
||||
mrs->prob_ewma = cur_prob;
|
||||
} else {
|
||||
/* update exponential weighted moving variance */
|
||||
@@ -206,7 +210,7 @@ minstrel_update_stats(struct minstrel_pr
|
||||
struct minstrel_rate_stats *tmp_mrs = &mi->r[tmp_prob_rate].stats;
|
||||
|
||||
/* Update statistics of success probability per rate */
|
||||
- minstrel_calc_rate_stats(mrs);
|
||||
+ minstrel_calc_rate_stats(mp, mrs);
|
||||
|
||||
/* Sample less often below the 10% chance of success.
|
||||
* Sample less often above the 95% chance of success. */
|
||||
@@ -289,7 +293,8 @@ minstrel_tx_status(void *priv, struct ie
|
||||
mi->r[ndx].stats.success += success;
|
||||
}
|
||||
|
||||
- if (time_after(jiffies, mi->last_stats_update + mp->update_interval))
|
||||
+ if (time_after(jiffies, mi->last_stats_update +
|
||||
+ mp->update_interval / (mp->new_avg ? 2 : 1)))
|
||||
minstrel_update_stats(mp, mi);
|
||||
}
|
||||
|
||||
--- a/net/mac80211/rc80211_minstrel.h
|
||||
+++ b/net/mac80211/rc80211_minstrel.h
|
||||
@@ -22,6 +22,21 @@
|
||||
#define MAX_THR_RATES 4
|
||||
|
||||
/*
|
||||
+ * Coefficients for moving average with noise filter (period=16),
|
||||
+ * scaled by 10 bits
|
||||
+ *
|
||||
+ * a1 = exp(-pi * sqrt(2) / period)
|
||||
+ * coeff2 = 2 * a1 * cos(sqrt(2) * 2 * pi / period)
|
||||
+ * coeff3 = -sqr(a1)
|
||||
+ * coeff1 = 1 - coeff2 - coeff3
|
||||
+ */
|
||||
+#define MINSTREL_AVG_COEFF1 (MINSTREL_FRAC(1, 1) - \
|
||||
+ MINSTREL_AVG_COEFF2 - \
|
||||
+ MINSTREL_AVG_COEFF3)
|
||||
+#define MINSTREL_AVG_COEFF2 0x00001499
|
||||
+#define MINSTREL_AVG_COEFF3 -0x0000092e
|
||||
+
|
||||
+/*
|
||||
* Perform EWMA (Exponentially Weighted Moving Average) calculation
|
||||
*/
|
||||
static inline int
|
||||
@@ -48,6 +63,41 @@ minstrel_ewmv(int old_ewmv, int cur_prob
|
||||
return weight * (old_ewmv + MINSTREL_TRUNC(diff * incr)) / EWMA_DIV;
|
||||
}
|
||||
|
||||
+struct minstrel_avg_ctx {
|
||||
+ s32 prev[2];
|
||||
+};
|
||||
+
|
||||
+static inline int minstrel_filter_avg_add(struct minstrel_avg_ctx *ctx, s32 in)
|
||||
+{
|
||||
+ s32 out_1 = ctx->prev[0];
|
||||
+ s32 out_2 = ctx->prev[1];
|
||||
+ s32 val;
|
||||
+
|
||||
+ if (!in)
|
||||
+ in += 1;
|
||||
+
|
||||
+ if (!out_1) {
|
||||
+ val = out_1 = in;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ val = MINSTREL_AVG_COEFF1 * in;
|
||||
+ val += MINSTREL_AVG_COEFF2 * out_1;
|
||||
+ val += MINSTREL_AVG_COEFF3 * out_2;
|
||||
+ val >>= MINSTREL_SCALE;
|
||||
+
|
||||
+ if (val > 1 << MINSTREL_SCALE)
|
||||
+ val = 1 << MINSTREL_SCALE;
|
||||
+ if (val < 0)
|
||||
+ val = 1;
|
||||
+
|
||||
+out:
|
||||
+ ctx->prev[1] = out_1;
|
||||
+ ctx->prev[0] = val;
|
||||
+
|
||||
+ return val;
|
||||
+}
|
||||
+
|
||||
struct minstrel_rate_stats {
|
||||
/* current / last sampling period attempts/success counters */
|
||||
u16 attempts, last_attempts;
|
||||
@@ -56,6 +106,8 @@ struct minstrel_rate_stats {
|
||||
/* total attempts/success counters */
|
||||
u32 att_hist, succ_hist;
|
||||
|
||||
+ struct minstrel_avg_ctx avg;
|
||||
+
|
||||
/* statistis of packet delivery probability
|
||||
* prob_ewma - exponential weighted moving average of prob
|
||||
* prob_ewmsd - exp. weighted moving standard deviation of prob */
|
||||
@@ -113,6 +165,7 @@ struct minstrel_sta_info {
|
||||
struct minstrel_priv {
|
||||
struct ieee80211_hw *hw;
|
||||
bool has_mrr;
|
||||
+ bool new_avg;
|
||||
u32 sample_switch;
|
||||
unsigned int cw_min;
|
||||
unsigned int cw_max;
|
||||
@@ -152,7 +205,8 @@ extern const struct rate_control_ops mac
|
||||
void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir);
|
||||
|
||||
/* Recalculate success probabilities and counters for a given rate using EWMA */
|
||||
-void minstrel_calc_rate_stats(struct minstrel_rate_stats *mrs);
|
||||
+void minstrel_calc_rate_stats(struct minstrel_priv *mp,
|
||||
+ struct minstrel_rate_stats *mrs);
|
||||
int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_ewma);
|
||||
|
||||
/* debugfs */
|
||||
--- a/net/mac80211/rc80211_minstrel_ht.c
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht.c
|
||||
@@ -704,7 +704,7 @@ minstrel_ht_update_stats(struct minstrel
|
||||
|
||||
mrs = &mg->rates[i];
|
||||
mrs->retry_updated = false;
|
||||
- minstrel_calc_rate_stats(mrs);
|
||||
+ minstrel_calc_rate_stats(mp, mrs);
|
||||
cur_prob = mrs->prob_ewma;
|
||||
|
||||
if (minstrel_ht_get_tp_avg(mi, group, i, cur_prob) == 0)
|
||||
@@ -740,6 +740,8 @@ minstrel_ht_update_stats(struct minstrel
|
||||
|
||||
/* try to sample all available rates during each interval */
|
||||
mi->sample_count *= 8;
|
||||
+ if (mp->new_avg)
|
||||
+ mi->sample_count /= 2;
|
||||
|
||||
if (sample)
|
||||
minstrel_ht_rate_sample_switch(mp, mi);
|
||||
@@ -856,6 +858,7 @@ minstrel_ht_tx_status(void *priv, struct
|
||||
struct ieee80211_tx_rate *ar = info->status.rates;
|
||||
struct minstrel_rate_stats *rate, *rate2, *rate_sample = NULL;
|
||||
struct minstrel_priv *mp = priv;
|
||||
+ u32 update_interval = mp->update_interval / 2;
|
||||
bool last, update = false;
|
||||
bool sample_status = false;
|
||||
int i;
|
||||
@@ -910,6 +913,10 @@ minstrel_ht_tx_status(void *priv, struct
|
||||
|
||||
switch (mi->sample_mode) {
|
||||
case MINSTREL_SAMPLE_IDLE:
|
||||
+ if (mp->new_avg &&
|
||||
+ (mp->hw->max_rates > 1 ||
|
||||
+ mi->total_packets_cur < SAMPLE_SWITCH_THR))
|
||||
+ update_interval /= 2;
|
||||
break;
|
||||
|
||||
case MINSTREL_SAMPLE_ACTIVE:
|
||||
@@ -950,8 +957,7 @@ minstrel_ht_tx_status(void *priv, struct
|
||||
}
|
||||
}
|
||||
|
||||
- if (time_after(jiffies, mi->last_stats_update +
|
||||
- mp->update_interval / 2)) {
|
||||
+ if (time_after(jiffies, mi->last_stats_update + update_interval)) {
|
||||
update = true;
|
||||
minstrel_ht_update_stats(mp, mi, true);
|
||||
}
|
||||
@@ -1639,6 +1645,7 @@ minstrel_ht_alloc(struct ieee80211_hw *h
|
||||
|
||||
mp->hw = hw;
|
||||
mp->update_interval = HZ / 10;
|
||||
+ mp->new_avg = true;
|
||||
|
||||
#ifdef CPTCFG_MAC80211_DEBUGFS
|
||||
mp->fixed_rate_idx = (u32) -1;
|
||||
@@ -1646,6 +1653,8 @@ minstrel_ht_alloc(struct ieee80211_hw *h
|
||||
&mp->fixed_rate_idx);
|
||||
debugfs_create_u32("sample_switch", S_IRUGO | S_IWUSR, debugfsdir,
|
||||
&mp->sample_switch);
|
||||
+ debugfs_create_bool("new_avg", S_IRUGO | S_IWUSR, debugfsdir,
|
||||
+ &mp->new_avg);
|
||||
#endif
|
||||
|
||||
minstrel_ht_init_cck_rates(mp);
|
||||
@@ -0,0 +1,437 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Tue, 8 Oct 2019 18:54:46 +0200
|
||||
Subject: [PATCH] mac80211: minstrel_ht: rename prob_ewma to prob_avg, use it
|
||||
for the new average
|
||||
|
||||
Reduces per-rate data structure size
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/net/mac80211/rc80211_minstrel.c
|
||||
+++ b/net/mac80211/rc80211_minstrel.c
|
||||
@@ -70,7 +70,7 @@ rix_to_ndx(struct minstrel_sta_info *mi,
|
||||
}
|
||||
|
||||
/* return current EMWA throughput */
|
||||
-int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_ewma)
|
||||
+int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_avg)
|
||||
{
|
||||
int usecs;
|
||||
|
||||
@@ -79,13 +79,13 @@ int minstrel_get_tp_avg(struct minstrel_
|
||||
usecs = 1000000;
|
||||
|
||||
/* reset thr. below 10% success */
|
||||
- if (mr->stats.prob_ewma < MINSTREL_FRAC(10, 100))
|
||||
+ if (mr->stats.prob_avg < MINSTREL_FRAC(10, 100))
|
||||
return 0;
|
||||
|
||||
- if (prob_ewma > MINSTREL_FRAC(90, 100))
|
||||
+ if (prob_avg > MINSTREL_FRAC(90, 100))
|
||||
return MINSTREL_TRUNC(100000 * (MINSTREL_FRAC(90, 100) / usecs));
|
||||
else
|
||||
- return MINSTREL_TRUNC(100000 * (prob_ewma / usecs));
|
||||
+ return MINSTREL_TRUNC(100000 * (prob_avg / usecs));
|
||||
}
|
||||
|
||||
/* find & sort topmost throughput rates */
|
||||
@@ -98,8 +98,8 @@ minstrel_sort_best_tp_rates(struct minst
|
||||
|
||||
for (j = MAX_THR_RATES; j > 0; --j) {
|
||||
tmp_mrs = &mi->r[tp_list[j - 1]].stats;
|
||||
- if (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))
|
||||
+ if (minstrel_get_tp_avg(&mi->r[i], cur_mrs->prob_avg) <=
|
||||
+ minstrel_get_tp_avg(&mi->r[tp_list[j - 1]], tmp_mrs->prob_avg))
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -166,21 +166,21 @@ minstrel_calc_rate_stats(struct minstrel
|
||||
mrs->sample_skipped = 0;
|
||||
cur_prob = MINSTREL_FRAC(mrs->success, mrs->attempts);
|
||||
if (mp->new_avg) {
|
||||
- mrs->prob_ewma = minstrel_filter_avg_add(&mrs->avg,
|
||||
- cur_prob);
|
||||
+ minstrel_filter_avg_add(&mrs->prob_avg,
|
||||
+ &mrs->prob_avg_1, cur_prob);
|
||||
} else if (unlikely(!mrs->att_hist)) {
|
||||
- mrs->prob_ewma = cur_prob;
|
||||
+ mrs->prob_avg = cur_prob;
|
||||
} else {
|
||||
/* update exponential weighted moving variance */
|
||||
mrs->prob_ewmv = minstrel_ewmv(mrs->prob_ewmv,
|
||||
cur_prob,
|
||||
- mrs->prob_ewma,
|
||||
+ mrs->prob_avg,
|
||||
EWMA_LEVEL);
|
||||
|
||||
/*update exponential weighted moving avarage */
|
||||
- mrs->prob_ewma = minstrel_ewma(mrs->prob_ewma,
|
||||
- cur_prob,
|
||||
- EWMA_LEVEL);
|
||||
+ mrs->prob_avg = minstrel_ewma(mrs->prob_avg,
|
||||
+ cur_prob,
|
||||
+ EWMA_LEVEL);
|
||||
}
|
||||
mrs->att_hist += mrs->attempts;
|
||||
mrs->succ_hist += mrs->success;
|
||||
@@ -214,8 +214,8 @@ minstrel_update_stats(struct minstrel_pr
|
||||
|
||||
/* 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) ||
|
||||
- mrs->prob_ewma < MINSTREL_FRAC(10, 100)) {
|
||||
+ if (mrs->prob_avg > MINSTREL_FRAC(95, 100) ||
|
||||
+ mrs->prob_avg < MINSTREL_FRAC(10, 100)) {
|
||||
mr->adjusted_retry_count = mrs->retry_count >> 1;
|
||||
if (mr->adjusted_retry_count > 2)
|
||||
mr->adjusted_retry_count = 2;
|
||||
@@ -235,14 +235,14 @@ minstrel_update_stats(struct minstrel_pr
|
||||
* 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->prob_ewma >= MINSTREL_FRAC(95, 100)) {
|
||||
- tmp_cur_tp = minstrel_get_tp_avg(mr, mrs->prob_ewma);
|
||||
+ if (mrs->prob_avg >= MINSTREL_FRAC(95, 100)) {
|
||||
+ tmp_cur_tp = minstrel_get_tp_avg(mr, mrs->prob_avg);
|
||||
tmp_prob_tp = minstrel_get_tp_avg(&mi->r[tmp_prob_rate],
|
||||
- tmp_mrs->prob_ewma);
|
||||
+ tmp_mrs->prob_avg);
|
||||
if (tmp_cur_tp >= tmp_prob_tp)
|
||||
tmp_prob_rate = i;
|
||||
} else {
|
||||
- if (mrs->prob_ewma >= tmp_mrs->prob_ewma)
|
||||
+ if (mrs->prob_avg >= tmp_mrs->prob_avg)
|
||||
tmp_prob_rate = i;
|
||||
}
|
||||
}
|
||||
@@ -418,7 +418,7 @@ minstrel_get_rate(void *priv, struct iee
|
||||
* 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.prob_ewma > MINSTREL_FRAC(95, 100)))
|
||||
+ (mi->r[ndx].stats.prob_avg > MINSTREL_FRAC(95, 100)))
|
||||
return;
|
||||
|
||||
mi->prev_sample = true;
|
||||
@@ -570,7 +570,7 @@ static u32 minstrel_get_expected_through
|
||||
* computing cur_tp
|
||||
*/
|
||||
tmp_mrs = &mi->r[idx].stats;
|
||||
- tmp_cur_tp = minstrel_get_tp_avg(&mi->r[idx], tmp_mrs->prob_ewma) * 10;
|
||||
+ tmp_cur_tp = minstrel_get_tp_avg(&mi->r[idx], tmp_mrs->prob_avg) * 10;
|
||||
tmp_cur_tp = tmp_cur_tp * 1200 * 8 / 1024;
|
||||
|
||||
return tmp_cur_tp;
|
||||
--- a/net/mac80211/rc80211_minstrel.h
|
||||
+++ b/net/mac80211/rc80211_minstrel.h
|
||||
@@ -63,14 +63,10 @@ minstrel_ewmv(int old_ewmv, int cur_prob
|
||||
return weight * (old_ewmv + MINSTREL_TRUNC(diff * incr)) / EWMA_DIV;
|
||||
}
|
||||
|
||||
-struct minstrel_avg_ctx {
|
||||
- s32 prev[2];
|
||||
-};
|
||||
-
|
||||
-static inline int minstrel_filter_avg_add(struct minstrel_avg_ctx *ctx, s32 in)
|
||||
+static inline int minstrel_filter_avg_add(u16 *prev_1, u16 *prev_2, s32 in)
|
||||
{
|
||||
- s32 out_1 = ctx->prev[0];
|
||||
- s32 out_2 = ctx->prev[1];
|
||||
+ s32 out_1 = *prev_1;
|
||||
+ s32 out_2 = *prev_2;
|
||||
s32 val;
|
||||
|
||||
if (!in)
|
||||
@@ -92,8 +88,8 @@ static inline int minstrel_filter_avg_ad
|
||||
val = 1;
|
||||
|
||||
out:
|
||||
- ctx->prev[1] = out_1;
|
||||
- ctx->prev[0] = val;
|
||||
+ *prev_2 = out_1;
|
||||
+ *prev_1 = val;
|
||||
|
||||
return val;
|
||||
}
|
||||
@@ -106,14 +102,15 @@ struct minstrel_rate_stats {
|
||||
/* total attempts/success counters */
|
||||
u32 att_hist, succ_hist;
|
||||
|
||||
- struct minstrel_avg_ctx avg;
|
||||
-
|
||||
/* statistis of packet delivery probability
|
||||
* prob_ewma - exponential weighted moving average of prob
|
||||
* prob_ewmsd - exp. weighted moving standard deviation of prob */
|
||||
- u16 prob_ewma;
|
||||
u16 prob_ewmv;
|
||||
|
||||
+ /* prob_avg - moving average of prob */
|
||||
+ u16 prob_avg;
|
||||
+ u16 prob_avg_1;
|
||||
+
|
||||
/* maximum retry counts */
|
||||
u8 retry_count;
|
||||
u8 retry_count_rtscts;
|
||||
@@ -207,7 +204,7 @@ void minstrel_add_sta_debugfs(void *priv
|
||||
/* Recalculate success probabilities and counters for a given rate using EWMA */
|
||||
void minstrel_calc_rate_stats(struct minstrel_priv *mp,
|
||||
struct minstrel_rate_stats *mrs);
|
||||
-int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_ewma);
|
||||
+int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_avg);
|
||||
|
||||
/* debugfs */
|
||||
int minstrel_stats_open(struct inode *inode, struct file *file);
|
||||
--- a/net/mac80211/rc80211_minstrel_debugfs.c
|
||||
+++ b/net/mac80211/rc80211_minstrel_debugfs.c
|
||||
@@ -91,8 +91,9 @@ minstrel_stats_open(struct inode *inode,
|
||||
p += sprintf(p, "%6u ", mr->perfect_tx_time);
|
||||
|
||||
tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100));
|
||||
- tp_avg = minstrel_get_tp_avg(mr, mrs->prob_ewma);
|
||||
- eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
|
||||
+ tp_avg = minstrel_get_tp_avg(mr, mrs->prob_avg);
|
||||
+ eprob = MINSTREL_TRUNC(mrs->prob_avg * 1000);
|
||||
+
|
||||
prob_ewmsd = minstrel_get_ewmsd10(mrs);
|
||||
|
||||
p += sprintf(p, "%4u.%1u %4u.%1u %3u.%1u %3u.%1u"
|
||||
@@ -151,8 +152,8 @@ minstrel_stats_csv_open(struct inode *in
|
||||
p += sprintf(p, "%u,",mr->perfect_tx_time);
|
||||
|
||||
tp_max = minstrel_get_tp_avg(mr, MINSTREL_FRAC(100,100));
|
||||
- tp_avg = minstrel_get_tp_avg(mr, mrs->prob_ewma);
|
||||
- eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
|
||||
+ tp_avg = minstrel_get_tp_avg(mr, mrs->prob_avg);
|
||||
+ eprob = MINSTREL_TRUNC(mrs->prob_avg * 1000);
|
||||
prob_ewmsd = minstrel_get_ewmsd10(mrs);
|
||||
|
||||
p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u,%u,%u,"
|
||||
--- a/net/mac80211/rc80211_minstrel_ht.c
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht.c
|
||||
@@ -313,12 +313,12 @@ minstrel_ht_avg_ampdu_len(struct minstre
|
||||
*/
|
||||
int
|
||||
minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate,
|
||||
- int prob_ewma)
|
||||
+ int prob_avg)
|
||||
{
|
||||
unsigned int nsecs = 0;
|
||||
|
||||
/* do not account throughput if sucess prob is below 10% */
|
||||
- if (prob_ewma < MINSTREL_FRAC(10, 100))
|
||||
+ if (prob_avg < MINSTREL_FRAC(10, 100))
|
||||
return 0;
|
||||
|
||||
if (group != MINSTREL_CCK_GROUP)
|
||||
@@ -332,11 +332,11 @@ minstrel_ht_get_tp_avg(struct minstrel_h
|
||||
* account for collision related packet error rate fluctuation
|
||||
* (prob is scaled - see MINSTREL_FRAC above)
|
||||
*/
|
||||
- if (prob_ewma > MINSTREL_FRAC(90, 100))
|
||||
+ if (prob_avg > MINSTREL_FRAC(90, 100))
|
||||
return MINSTREL_TRUNC(100000 * ((MINSTREL_FRAC(90, 100) * 1000)
|
||||
/ nsecs));
|
||||
else
|
||||
- return MINSTREL_TRUNC(100000 * ((prob_ewma * 1000) / nsecs));
|
||||
+ return MINSTREL_TRUNC(100000 * ((prob_avg * 1000) / nsecs));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -356,13 +356,13 @@ minstrel_ht_sort_best_tp_rates(struct mi
|
||||
|
||||
cur_group = index / MCS_GROUP_RATES;
|
||||
cur_idx = index % MCS_GROUP_RATES;
|
||||
- cur_prob = mi->groups[cur_group].rates[cur_idx].prob_ewma;
|
||||
+ cur_prob = mi->groups[cur_group].rates[cur_idx].prob_avg;
|
||||
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_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma;
|
||||
+ tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_avg;
|
||||
tmp_tp_avg = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx,
|
||||
tmp_prob);
|
||||
if (cur_tp_avg < tmp_tp_avg ||
|
||||
@@ -399,7 +399,7 @@ minstrel_ht_set_best_prob_rate(struct mi
|
||||
|
||||
tmp_group = mi->max_prob_rate / MCS_GROUP_RATES;
|
||||
tmp_idx = mi->max_prob_rate % MCS_GROUP_RATES;
|
||||
- tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma;
|
||||
+ tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_avg;
|
||||
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
|
||||
@@ -411,11 +411,11 @@ minstrel_ht_set_best_prob_rate(struct mi
|
||||
|
||||
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_prob = mi->groups[max_gpr_group].rates[max_gpr_idx].prob_avg;
|
||||
|
||||
- if (mrs->prob_ewma > MINSTREL_FRAC(75, 100)) {
|
||||
+ if (mrs->prob_avg > MINSTREL_FRAC(75, 100)) {
|
||||
cur_tp_avg = minstrel_ht_get_tp_avg(mi, cur_group, cur_idx,
|
||||
- mrs->prob_ewma);
|
||||
+ mrs->prob_avg);
|
||||
if (cur_tp_avg > tmp_tp_avg)
|
||||
mi->max_prob_rate = index;
|
||||
|
||||
@@ -425,9 +425,9 @@ minstrel_ht_set_best_prob_rate(struct mi
|
||||
if (cur_tp_avg > max_gpr_tp_avg)
|
||||
mg->max_group_prob_rate = index;
|
||||
} else {
|
||||
- if (mrs->prob_ewma > tmp_prob)
|
||||
+ if (mrs->prob_avg > tmp_prob)
|
||||
mi->max_prob_rate = index;
|
||||
- if (mrs->prob_ewma > max_gpr_prob)
|
||||
+ if (mrs->prob_avg > max_gpr_prob)
|
||||
mg->max_group_prob_rate = index;
|
||||
}
|
||||
}
|
||||
@@ -449,12 +449,12 @@ minstrel_ht_assign_best_tp_rates(struct
|
||||
|
||||
tmp_group = tmp_cck_tp_rate[0] / MCS_GROUP_RATES;
|
||||
tmp_idx = tmp_cck_tp_rate[0] % MCS_GROUP_RATES;
|
||||
- tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma;
|
||||
+ tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_avg;
|
||||
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_prob = mi->groups[tmp_group].rates[tmp_idx].prob_ewma;
|
||||
+ tmp_prob = mi->groups[tmp_group].rates[tmp_idx].prob_avg;
|
||||
tmp_mcs_tp = minstrel_ht_get_tp_avg(mi, tmp_group, tmp_idx, tmp_prob);
|
||||
|
||||
if (tmp_cck_tp_rate && tmp_cck_tp > tmp_mcs_tp) {
|
||||
@@ -485,7 +485,7 @@ minstrel_ht_prob_rate_reduce_streams(str
|
||||
continue;
|
||||
|
||||
tmp_idx = mg->max_group_prob_rate % MCS_GROUP_RATES;
|
||||
- tmp_prob = mi->groups[group].rates[tmp_idx].prob_ewma;
|
||||
+ tmp_prob = mi->groups[group].rates[tmp_idx].prob_avg;
|
||||
|
||||
if (tmp_tp < minstrel_ht_get_tp_avg(mi, group, tmp_idx, tmp_prob) &&
|
||||
(minstrel_mcs_groups[group].streams < tmp_max_streams)) {
|
||||
@@ -590,7 +590,7 @@ minstrel_ht_rate_sample_switch(struct mi
|
||||
* If that fails, look again for a rate that is at least as fast
|
||||
*/
|
||||
mrs = minstrel_get_ratestats(mi, mi->max_tp_rate[0]);
|
||||
- faster_rate = mrs->prob_ewma > MINSTREL_FRAC(75, 100);
|
||||
+ faster_rate = mrs->prob_avg > MINSTREL_FRAC(75, 100);
|
||||
minstrel_ht_find_probe_rates(mi, rates, &n_rates, faster_rate);
|
||||
if (!n_rates && faster_rate)
|
||||
minstrel_ht_find_probe_rates(mi, rates, &n_rates, false);
|
||||
@@ -705,7 +705,7 @@ minstrel_ht_update_stats(struct minstrel
|
||||
mrs = &mg->rates[i];
|
||||
mrs->retry_updated = false;
|
||||
minstrel_calc_rate_stats(mp, mrs);
|
||||
- cur_prob = mrs->prob_ewma;
|
||||
+ cur_prob = mrs->prob_avg;
|
||||
|
||||
if (minstrel_ht_get_tp_avg(mi, group, i, cur_prob) == 0)
|
||||
continue;
|
||||
@@ -979,7 +979,7 @@ minstrel_calc_retransmit(struct minstrel
|
||||
unsigned int overhead = 0, overhead_rtscts = 0;
|
||||
|
||||
mrs = minstrel_get_ratestats(mi, index);
|
||||
- if (mrs->prob_ewma < MINSTREL_FRAC(1, 10)) {
|
||||
+ if (mrs->prob_avg < MINSTREL_FRAC(1, 10)) {
|
||||
mrs->retry_count = 1;
|
||||
mrs->retry_count_rtscts = 1;
|
||||
return;
|
||||
@@ -1036,7 +1036,7 @@ minstrel_ht_set_rate(struct minstrel_pri
|
||||
if (!mrs->retry_updated)
|
||||
minstrel_calc_retransmit(mp, mi, index);
|
||||
|
||||
- if (mrs->prob_ewma < MINSTREL_FRAC(20, 100) || !mrs->retry_count) {
|
||||
+ if (mrs->prob_avg < MINSTREL_FRAC(20, 100) || !mrs->retry_count) {
|
||||
ratetbl->rate[offset].count = 2;
|
||||
ratetbl->rate[offset].count_rts = 2;
|
||||
ratetbl->rate[offset].count_cts = 2;
|
||||
@@ -1070,11 +1070,11 @@ minstrel_ht_set_rate(struct minstrel_pri
|
||||
}
|
||||
|
||||
static inline int
|
||||
-minstrel_ht_get_prob_ewma(struct minstrel_ht_sta *mi, int rate)
|
||||
+minstrel_ht_get_prob_avg(struct minstrel_ht_sta *mi, int rate)
|
||||
{
|
||||
int group = rate / MCS_GROUP_RATES;
|
||||
rate %= MCS_GROUP_RATES;
|
||||
- return mi->groups[group].rates[rate].prob_ewma;
|
||||
+ return mi->groups[group].rates[rate].prob_avg;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -1086,7 +1086,7 @@ minstrel_ht_get_max_amsdu_len(struct min
|
||||
unsigned int duration;
|
||||
|
||||
/* Disable A-MSDU if max_prob_rate is bad */
|
||||
- if (mi->groups[group].rates[rate].prob_ewma < MINSTREL_FRAC(50, 100))
|
||||
+ if (mi->groups[group].rates[rate].prob_avg < MINSTREL_FRAC(50, 100))
|
||||
return 1;
|
||||
|
||||
duration = g->duration[rate];
|
||||
@@ -1109,7 +1109,7 @@ minstrel_ht_get_max_amsdu_len(struct min
|
||||
* data packet size
|
||||
*/
|
||||
if (duration > MCS_DURATION(1, 0, 260) ||
|
||||
- (minstrel_ht_get_prob_ewma(mi, mi->max_tp_rate[0]) <
|
||||
+ (minstrel_ht_get_prob_avg(mi, mi->max_tp_rate[0]) <
|
||||
MINSTREL_FRAC(75, 100)))
|
||||
return 3200;
|
||||
|
||||
@@ -1216,7 +1216,7 @@ minstrel_get_sample_rate(struct minstrel
|
||||
* rate, to avoid wasting airtime.
|
||||
*/
|
||||
sample_dur = minstrel_get_duration(sample_idx);
|
||||
- if (mrs->prob_ewma > MINSTREL_FRAC(95, 100) ||
|
||||
+ if (mrs->prob_avg > MINSTREL_FRAC(95, 100) ||
|
||||
minstrel_get_duration(mi->max_prob_rate) * 3 < sample_dur)
|
||||
return -1;
|
||||
|
||||
@@ -1679,7 +1679,7 @@ static u32 minstrel_ht_get_expected_thro
|
||||
|
||||
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;
|
||||
+ prob = mi->groups[i].rates[j].prob_avg;
|
||||
|
||||
/* convert tp_avg from pkt per second in kbps */
|
||||
tp_avg = minstrel_ht_get_tp_avg(mi, i, j, prob) * 10;
|
||||
--- a/net/mac80211/rc80211_minstrel_ht.h
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht.h
|
||||
@@ -122,6 +122,6 @@ struct minstrel_ht_sta_priv {
|
||||
|
||||
void minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir);
|
||||
int minstrel_ht_get_tp_avg(struct minstrel_ht_sta *mi, int group, int rate,
|
||||
- int prob_ewma);
|
||||
+ int prob_avg);
|
||||
|
||||
#endif
|
||||
--- a/net/mac80211/rc80211_minstrel_ht_debugfs.c
|
||||
+++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c
|
||||
@@ -102,8 +102,8 @@ minstrel_ht_stats_dump(struct minstrel_h
|
||||
p += sprintf(p, "%6u ", tx_time);
|
||||
|
||||
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);
|
||||
- eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
|
||||
+ tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_avg);
|
||||
+ eprob = MINSTREL_TRUNC(mrs->prob_avg * 1000);
|
||||
prob_ewmsd = minstrel_get_ewmsd10(mrs);
|
||||
|
||||
p += sprintf(p, "%4u.%1u %4u.%1u %3u.%1u %3u.%1u"
|
||||
@@ -250,8 +250,8 @@ minstrel_ht_stats_csv_dump(struct minstr
|
||||
p += sprintf(p, "%u,", tx_time);
|
||||
|
||||
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);
|
||||
- eprob = MINSTREL_TRUNC(mrs->prob_ewma * 1000);
|
||||
+ tp_avg = minstrel_ht_get_tp_avg(mi, i, j, mrs->prob_avg);
|
||||
+ eprob = MINSTREL_TRUNC(mrs->prob_avg * 1000);
|
||||
prob_ewmsd = minstrel_get_ewmsd10(mrs);
|
||||
|
||||
p += sprintf(p, "%u.%u,%u.%u,%u.%u,%u.%u,%u,%u,"
|
||||
@@ -0,0 +1,128 @@
|
||||
From 4b08d1b6a994dbb593557bd2095ba4f0c3c47819 Mon Sep 17 00:00:00 2001
|
||||
From: Johannes Berg <johannes.berg@intel.com>
|
||||
Date: Fri, 30 Aug 2019 14:24:51 +0300
|
||||
Subject: [PATCH] mac80211: IBSS: send deauth when expiring inactive STAs
|
||||
|
||||
When we expire an inactive station, try to send it a deauth. This
|
||||
helps if it's actually still around, and just has issues with
|
||||
beacon distribution (or we do), and it will not also remove us.
|
||||
Then, if we have shared state, this may not be reset properly,
|
||||
causing problems; for example, we saw a case where aggregation
|
||||
sessions weren't removed properly (due to the TX start being
|
||||
offloaded to firmware and it relying on deauth for stop), causing
|
||||
a lot of traffic to get lost due to the SN reset after remove/add
|
||||
of the peer.
|
||||
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
|
||||
Link: https://lore.kernel.org/r/20190830112451.21655-9-luca@coelho.fi
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
net/mac80211/ibss.c | 8 ++++++++
|
||||
net/mac80211/ieee80211_i.h | 3 ++-
|
||||
net/mac80211/mlme.c | 11 ++++++-----
|
||||
net/mac80211/util.c | 5 +++--
|
||||
4 files changed, 19 insertions(+), 8 deletions(-)
|
||||
|
||||
--- a/net/mac80211/ibss.c
|
||||
+++ b/net/mac80211/ibss.c
|
||||
@@ -1253,6 +1253,7 @@ void ieee80211_ibss_rx_no_sta(struct iee
|
||||
|
||||
static void ieee80211_ibss_sta_expire(struct ieee80211_sub_if_data *sdata)
|
||||
{
|
||||
+ struct ieee80211_if_ibss *ifibss = &sdata->u.ibss;
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
struct sta_info *sta, *tmp;
|
||||
unsigned long exp_time = IEEE80211_IBSS_INACTIVITY_LIMIT;
|
||||
@@ -1269,10 +1270,17 @@ static void ieee80211_ibss_sta_expire(st
|
||||
if (time_is_before_jiffies(last_active + exp_time) ||
|
||||
(time_is_before_jiffies(last_active + exp_rsn) &&
|
||||
sta->sta_state != IEEE80211_STA_AUTHORIZED)) {
|
||||
+ u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN];
|
||||
+
|
||||
sta_dbg(sta->sdata, "expiring inactive %sSTA %pM\n",
|
||||
sta->sta_state != IEEE80211_STA_AUTHORIZED ?
|
||||
"not authorized " : "", sta->sta.addr);
|
||||
|
||||
+ ieee80211_send_deauth_disassoc(sdata, sta->sta.addr,
|
||||
+ ifibss->bssid,
|
||||
+ IEEE80211_STYPE_DEAUTH,
|
||||
+ WLAN_REASON_DEAUTH_LEAVING,
|
||||
+ true, frame_buf);
|
||||
WARN_ON(__sta_info_destroy(sta));
|
||||
}
|
||||
}
|
||||
--- a/net/mac80211/ieee80211_i.h
|
||||
+++ b/net/mac80211/ieee80211_i.h
|
||||
@@ -2068,7 +2068,8 @@ void ieee80211_send_auth(struct ieee8021
|
||||
const u8 *da, const u8 *key, u8 key_len, u8 key_idx,
|
||||
u32 tx_flags);
|
||||
void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
|
||||
- const u8 *bssid, u16 stype, u16 reason,
|
||||
+ const u8 *da, const u8 *bssid,
|
||||
+ u16 stype, u16 reason,
|
||||
bool send_frame, u8 *frame_buf);
|
||||
|
||||
enum {
|
||||
--- a/net/mac80211/mlme.c
|
||||
+++ b/net/mac80211/mlme.c
|
||||
@@ -2203,8 +2203,9 @@ static void ieee80211_set_disassoc(struc
|
||||
!ifmgd->have_beacon)
|
||||
drv_mgd_prepare_tx(sdata->local, sdata, 0);
|
||||
|
||||
- ieee80211_send_deauth_disassoc(sdata, ifmgd->bssid, stype,
|
||||
- reason, tx, frame_buf);
|
||||
+ ieee80211_send_deauth_disassoc(sdata, ifmgd->bssid,
|
||||
+ ifmgd->bssid, stype, reason,
|
||||
+ tx, frame_buf);
|
||||
}
|
||||
|
||||
/* flush out frame - make sure the deauth was actually sent */
|
||||
@@ -4369,7 +4370,7 @@ void ieee80211_mgd_quiesce(struct ieee80
|
||||
* cfg80211 won't know and won't actually abort those attempts,
|
||||
* thus we need to do that ourselves.
|
||||
*/
|
||||
- ieee80211_send_deauth_disassoc(sdata, bssid,
|
||||
+ ieee80211_send_deauth_disassoc(sdata, bssid, bssid,
|
||||
IEEE80211_STYPE_DEAUTH,
|
||||
WLAN_REASON_DEAUTH_LEAVING,
|
||||
false, frame_buf);
|
||||
@@ -5349,7 +5350,7 @@ int ieee80211_mgd_deauth(struct ieee8021
|
||||
ieee80211_get_reason_code_string(req->reason_code));
|
||||
|
||||
drv_mgd_prepare_tx(sdata->local, sdata, 0);
|
||||
- ieee80211_send_deauth_disassoc(sdata, req->bssid,
|
||||
+ ieee80211_send_deauth_disassoc(sdata, req->bssid, req->bssid,
|
||||
IEEE80211_STYPE_DEAUTH,
|
||||
req->reason_code, tx,
|
||||
frame_buf);
|
||||
@@ -5369,7 +5370,7 @@ int ieee80211_mgd_deauth(struct ieee8021
|
||||
ieee80211_get_reason_code_string(req->reason_code));
|
||||
|
||||
drv_mgd_prepare_tx(sdata->local, sdata, 0);
|
||||
- ieee80211_send_deauth_disassoc(sdata, req->bssid,
|
||||
+ ieee80211_send_deauth_disassoc(sdata, req->bssid, req->bssid,
|
||||
IEEE80211_STYPE_DEAUTH,
|
||||
req->reason_code, tx,
|
||||
frame_buf);
|
||||
--- a/net/mac80211/util.c
|
||||
+++ b/net/mac80211/util.c
|
||||
@@ -1433,7 +1433,8 @@ void ieee80211_send_auth(struct ieee8021
|
||||
}
|
||||
|
||||
void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
|
||||
- const u8 *bssid, u16 stype, u16 reason,
|
||||
+ const u8 *da, const u8 *bssid,
|
||||
+ u16 stype, u16 reason,
|
||||
bool send_frame, u8 *frame_buf)
|
||||
{
|
||||
struct ieee80211_local *local = sdata->local;
|
||||
@@ -1444,7 +1445,7 @@ void ieee80211_send_deauth_disassoc(stru
|
||||
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
|
||||
mgmt->duration = 0; /* initialize only */
|
||||
mgmt->seq_ctrl = 0; /* initialize only */
|
||||
- memcpy(mgmt->da, bssid, ETH_ALEN);
|
||||
+ memcpy(mgmt->da, da, ETH_ALEN);
|
||||
memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
|
||||
memcpy(mgmt->bssid, bssid, ETH_ALEN);
|
||||
/* u.deauth.reason_code == u.disassoc.reason_code */
|
||||
@@ -0,0 +1,38 @@
|
||||
From b478e06a16a8baa00c5ecc87c1d636981f2206d5 Mon Sep 17 00:00:00 2001
|
||||
From: Johannes Berg <johannes.berg@intel.com>
|
||||
Date: Tue, 29 Oct 2019 10:25:25 +0100
|
||||
Subject: [PATCH] mac80211: sta: randomize BA session dialog token allocator
|
||||
|
||||
We currently always start the dialog token generator at zero,
|
||||
so the first dialog token we use is always 1. This would be
|
||||
OK if we had a perfect guarantee that we always do a proper
|
||||
deauth/re-auth handshake, but in IBSS mode this doesn't always
|
||||
happen properly.
|
||||
|
||||
To make problems with block ack (aggregation) sessions getting
|
||||
stuck less likely, randomize the dialog token so if we start a
|
||||
new session but the peer still has old state for us, it can
|
||||
better detect this.
|
||||
|
||||
This is really just a workaround to make things a bit more
|
||||
robust than they are now - a better fix would be to do a full
|
||||
authentication handshake in IBSS mode upon having discovered a
|
||||
new station, and on the receiver resetting the state (removing
|
||||
and re-adding the station) on receiving the authentication
|
||||
packet.
|
||||
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
net/mac80211/sta_info.c | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
--- a/net/mac80211/sta_info.c
|
||||
+++ b/net/mac80211/sta_info.c
|
||||
@@ -340,6 +340,7 @@ struct sta_info *sta_info_alloc(struct i
|
||||
INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames);
|
||||
INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
|
||||
mutex_init(&sta->ampdu_mlme.mtx);
|
||||
+ sta->ampdu_mlme.dialog_token_allocator = prandom_u32_max(U8_MAX);
|
||||
#ifdef CPTCFG_MAC80211_MESH
|
||||
if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
||||
sta->mesh = kzalloc(sizeof(*sta->mesh), gfp);
|
||||
@@ -0,0 +1,77 @@
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Wed, 28 Aug 2019 12:13:55 +0200
|
||||
Subject: [PATCH] cfg80211: add local BSS receive time to survey information
|
||||
|
||||
This is useful for checking how much airtime is being used up by other
|
||||
transmissions on the channel, e.g. by calculating (time_rx - time_bss_rx)
|
||||
or (time_busy - time_bss_rx - time_tx)
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
|
||||
--- a/include/net/cfg80211.h
|
||||
+++ b/include/net/cfg80211.h
|
||||
@@ -668,6 +668,7 @@ ieee80211_chandef_max_power(struct cfg80
|
||||
* @SURVEY_INFO_TIME_RX: receive time was filled in
|
||||
* @SURVEY_INFO_TIME_TX: transmit time was filled in
|
||||
* @SURVEY_INFO_TIME_SCAN: scan time was filled in
|
||||
+ * @SURVEY_INFO_TIME_BSS_RX: local BSS receive time was filled in
|
||||
*
|
||||
* Used by the driver to indicate which info in &struct survey_info
|
||||
* it has filled in during the get_survey().
|
||||
@@ -681,6 +682,7 @@ enum survey_info_flags {
|
||||
SURVEY_INFO_TIME_RX = BIT(5),
|
||||
SURVEY_INFO_TIME_TX = BIT(6),
|
||||
SURVEY_INFO_TIME_SCAN = BIT(7),
|
||||
+ SURVEY_INFO_TIME_BSS_RX = BIT(8),
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -697,6 +699,7 @@ enum survey_info_flags {
|
||||
* @time_rx: amount of time the radio spent receiving data
|
||||
* @time_tx: amount of time the radio spent transmitting data
|
||||
* @time_scan: amount of time the radio spent for scanning
|
||||
+ * @time_bss_rx: amount of time the radio spent receiving data on a local BSS
|
||||
*
|
||||
* Used by dump_survey() to report back per-channel survey information.
|
||||
*
|
||||
@@ -711,6 +714,7 @@ struct survey_info {
|
||||
u64 time_rx;
|
||||
u64 time_tx;
|
||||
u64 time_scan;
|
||||
+ u64 time_bss_rx;
|
||||
u32 filled;
|
||||
s8 noise;
|
||||
};
|
||||
--- a/include/uapi/linux/nl80211.h
|
||||
+++ b/include/uapi/linux/nl80211.h
|
||||
@@ -3693,6 +3693,8 @@ enum nl80211_user_reg_hint_type {
|
||||
* @NL80211_SURVEY_INFO_TIME_SCAN: time the radio spent for scan
|
||||
* (on this channel or globally)
|
||||
* @NL80211_SURVEY_INFO_PAD: attribute used for padding for 64-bit alignment
|
||||
+ * @NL80211_SURVEY_INFO_TIME_BSS_RX: amount of time the radio spent
|
||||
+ * receiving local BSS data
|
||||
* @NL80211_SURVEY_INFO_MAX: highest survey info attribute number
|
||||
* currently defined
|
||||
* @__NL80211_SURVEY_INFO_AFTER_LAST: internal use
|
||||
@@ -3709,6 +3711,7 @@ enum nl80211_survey_info {
|
||||
NL80211_SURVEY_INFO_TIME_TX,
|
||||
NL80211_SURVEY_INFO_TIME_SCAN,
|
||||
NL80211_SURVEY_INFO_PAD,
|
||||
+ NL80211_SURVEY_INFO_TIME_BSS_RX,
|
||||
|
||||
/* keep last */
|
||||
__NL80211_SURVEY_INFO_AFTER_LAST,
|
||||
--- a/net/wireless/nl80211.c
|
||||
+++ b/net/wireless/nl80211.c
|
||||
@@ -8379,6 +8379,10 @@ static int nl80211_send_survey(struct sk
|
||||
nla_put_u64_64bit(msg, NL80211_SURVEY_INFO_TIME_SCAN,
|
||||
survey->time_scan, NL80211_SURVEY_INFO_PAD))
|
||||
goto nla_put_failure;
|
||||
+ if ((survey->filled & SURVEY_INFO_TIME_BSS_RX) &&
|
||||
+ nla_put_u64_64bit(msg, NL80211_SURVEY_INFO_TIME_BSS_RX,
|
||||
+ survey->time_bss_rx, NL80211_SURVEY_INFO_PAD))
|
||||
+ goto nla_put_failure;
|
||||
|
||||
nla_nest_end(msg, infoattr);
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
--- a/include/net/cfg80211.h
|
||||
+++ b/include/net/cfg80211.h
|
||||
@@ -2972,6 +2972,7 @@ struct cfg80211_external_auth_params {
|
||||
* (as advertised by the nl80211 feature flag.)
|
||||
* @get_tx_power: store the current TX power into the dbm variable;
|
||||
* return 0 if successful
|
||||
+ * @set_antenna_gain: set antenna gain to reduce maximum tx power if necessary
|
||||
*
|
||||
* @set_wds_peer: set the WDS peer for a WDS interface
|
||||
*
|
||||
@@ -3275,6 +3276,7 @@ struct cfg80211_ops {
|
||||
enum nl80211_tx_power_setting type, int mbm);
|
||||
int (*get_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev,
|
||||
int *dbm);
|
||||
+ int (*set_antenna_gain)(struct wiphy *wiphy, int dbi);
|
||||
|
||||
int (*set_wds_peer)(struct wiphy *wiphy, struct net_device *dev,
|
||||
const u8 *addr);
|
||||
--- a/include/net/mac80211.h
|
||||
+++ b/include/net/mac80211.h
|
||||
@@ -1395,6 +1395,7 @@ enum ieee80211_smps_mode {
|
||||
*
|
||||
* @power_level: requested transmit power (in dBm), backward compatibility
|
||||
* value only that is set to the minimum of all interfaces
|
||||
+ * @max_antenna_gain: maximum antenna gain adjusted by user config (in dBi)
|
||||
*
|
||||
* @chandef: the channel definition to tune to
|
||||
* @radar_enabled: whether radar detection is enabled
|
||||
@@ -1415,6 +1416,7 @@ enum ieee80211_smps_mode {
|
||||
struct ieee80211_conf {
|
||||
u32 flags;
|
||||
int power_level, dynamic_ps_timeout;
|
||||
+ int max_antenna_gain;
|
||||
|
||||
u16 listen_interval;
|
||||
u8 ps_dtim_period;
|
||||
--- a/include/uapi/linux/nl80211.h
|
||||
+++ b/include/uapi/linux/nl80211.h
|
||||
@@ -2244,6 +2244,9 @@ enum nl80211_commands {
|
||||
* @NL80211_ATTR_AIRTIME_WEIGHT: Station's weight when scheduled by the airtime
|
||||
* scheduler.
|
||||
*
|
||||
+ * @NL80211_ATTR_WIPHY_ANTENNA_GAIN: Configured antenna gain. Used to reduce
|
||||
+ * transmit power to stay within regulatory limits. u32, dBi.
|
||||
+ *
|
||||
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
|
||||
* @NL80211_ATTR_MAX: highest attribute number currently defined
|
||||
* @__NL80211_ATTR_AFTER_LAST: internal use
|
||||
@@ -2693,6 +2696,8 @@ enum nl80211_attrs {
|
||||
|
||||
NL80211_ATTR_AIRTIME_WEIGHT,
|
||||
|
||||
+ NL80211_ATTR_WIPHY_ANTENNA_GAIN,
|
||||
+
|
||||
/* add attributes here, update the policy in nl80211.c */
|
||||
|
||||
__NL80211_ATTR_AFTER_LAST,
|
||||
--- a/net/mac80211/cfg.c
|
||||
+++ b/net/mac80211/cfg.c
|
||||
@@ -2458,6 +2458,19 @@ static int ieee80211_get_tx_power(struct
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int ieee80211_set_antenna_gain(struct wiphy *wiphy, int dbi)
|
||||
+{
|
||||
+ struct ieee80211_local *local = wiphy_priv(wiphy);
|
||||
+
|
||||
+ if (dbi < 0)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ local->user_antenna_gain = dbi;
|
||||
+ ieee80211_hw_config(local, 0);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int ieee80211_set_wds_peer(struct wiphy *wiphy, struct net_device *dev,
|
||||
const u8 *addr)
|
||||
{
|
||||
@@ -3847,6 +3860,7 @@ const struct cfg80211_ops mac80211_confi
|
||||
.set_wiphy_params = ieee80211_set_wiphy_params,
|
||||
.set_tx_power = ieee80211_set_tx_power,
|
||||
.get_tx_power = ieee80211_get_tx_power,
|
||||
+ .set_antenna_gain = ieee80211_set_antenna_gain,
|
||||
.set_wds_peer = ieee80211_set_wds_peer,
|
||||
.rfkill_poll = ieee80211_rfkill_poll,
|
||||
CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)
|
||||
--- a/net/mac80211/ieee80211_i.h
|
||||
+++ b/net/mac80211/ieee80211_i.h
|
||||
@@ -1365,6 +1365,7 @@ struct ieee80211_local {
|
||||
int dynamic_ps_forced_timeout;
|
||||
|
||||
int user_power_level; /* in dBm, for all interfaces */
|
||||
+ int user_antenna_gain; /* in dBi */
|
||||
|
||||
enum ieee80211_smps_mode smps_mode;
|
||||
|
||||
--- a/net/mac80211/main.c
|
||||
+++ b/net/mac80211/main.c
|
||||
@@ -94,7 +94,7 @@ static u32 ieee80211_hw_conf_chan(struct
|
||||
struct ieee80211_sub_if_data *sdata;
|
||||
struct cfg80211_chan_def chandef = {};
|
||||
u32 changed = 0;
|
||||
- int power;
|
||||
+ int power, max_power;
|
||||
u32 offchannel_flag;
|
||||
|
||||
offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
|
||||
@@ -151,6 +151,12 @@ static u32 ieee80211_hw_conf_chan(struct
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
+ max_power = chandef.chan->max_reg_power;
|
||||
+ if (local->user_antenna_gain > 0) {
|
||||
+ max_power -= local->user_antenna_gain;
|
||||
+ power = min(power, max_power);
|
||||
+ }
|
||||
+
|
||||
if (local->hw.conf.power_level != power) {
|
||||
changed |= IEEE80211_CONF_CHANGE_POWER;
|
||||
local->hw.conf.power_level = power;
|
||||
@@ -626,6 +632,7 @@ struct ieee80211_hw *ieee80211_alloc_hw_
|
||||
IEEE80211_RADIOTAP_MCS_HAVE_BW;
|
||||
local->hw.radiotap_vht_details = IEEE80211_RADIOTAP_VHT_KNOWN_GI |
|
||||
IEEE80211_RADIOTAP_VHT_KNOWN_BANDWIDTH;
|
||||
+ local->user_antenna_gain = 0;
|
||||
local->hw.uapsd_queues = IEEE80211_DEFAULT_UAPSD_QUEUES;
|
||||
local->hw.uapsd_max_sp_len = IEEE80211_DEFAULT_MAX_SP_LEN;
|
||||
local->user_power_level = IEEE80211_UNSET_POWER_LEVEL;
|
||||
--- a/net/wireless/nl80211.c
|
||||
+++ b/net/wireless/nl80211.c
|
||||
@@ -470,6 +470,7 @@ static const struct nla_policy nl80211_p
|
||||
[NL80211_ATTR_HE_CAPABILITY] = { .type = NLA_BINARY,
|
||||
.len = NL80211_HE_MAX_CAPABILITY_LEN },
|
||||
[NL80211_ATTR_AIRTIME_WEIGHT] = NLA_POLICY_MIN(NLA_U16, 1),
|
||||
+ [NL80211_ATTR_WIPHY_ANTENNA_GAIN] = { .type = NLA_U32 },
|
||||
};
|
||||
|
||||
/* policy for the key attributes */
|
||||
@@ -2632,6 +2633,20 @@ static int nl80211_set_wiphy(struct sk_b
|
||||
if (result)
|
||||
return result;
|
||||
}
|
||||
+
|
||||
+ if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_GAIN]) {
|
||||
+ int idx, dbi = 0;
|
||||
+
|
||||
+ if (!rdev->ops->set_antenna_gain)
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
+ idx = NL80211_ATTR_WIPHY_ANTENNA_GAIN;
|
||||
+ dbi = nla_get_u32(info->attrs[idx]);
|
||||
+
|
||||
+ result = rdev->ops->set_antenna_gain(&rdev->wiphy, dbi);
|
||||
+ if (result)
|
||||
+ return result;
|
||||
+ }
|
||||
|
||||
if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] &&
|
||||
info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) {
|
||||
Reference in New Issue
Block a user