summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorToke Høiland-Jørgensen <toke@toke.dk>2018-05-08 07:03:50 -0400
committerJohannes Berg <johannes.berg@intel.com>2018-05-08 07:19:24 -0400
commit52539ca89f365d3db530535fbffa88a3cca4d2ec (patch)
tree5bfc75ba1383976149d0c99a3f87bb9ef69af4e8
parentcc60dbbfed8ff0bd4c530ee48e9e915333a35470 (diff)
cfg80211: Expose TXQ stats and parameters to userspace
This adds support for exporting the mac80211 TXQ stats via nl80211 by way of a nested TXQ stats attribute, as well as for configuring the quantum and limits that were previously only changeable through debugfs. This commit adds just the nl80211 API, a subsequent commit adds support to mac80211 itself. Signed-off-by: Toke Høiland-Jørgensen <toke@toke.dk> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r--include/net/cfg80211.h50
-rw-r--r--include/uapi/linux/nl80211.h58
-rw-r--r--net/mac80211/ethtool.c32
-rw-r--r--net/wireless/nl80211.c202
-rw-r--r--net/wireless/rdev-ops.h12
-rw-r--r--net/wireless/trace.h14
-rw-r--r--net/wireless/wext-compat.c23
7 files changed, 343 insertions, 48 deletions
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 5e888ec62811..8db6071b6063 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1080,6 +1080,37 @@ struct sta_bss_parameters {
1080}; 1080};
1081 1081
1082/** 1082/**
1083 * struct cfg80211_txq_stats - TXQ statistics for this TID
1084 * @filled: bitmap of flags using the bits of &enum nl80211_txq_stats to
1085 * indicate the relevant values in this struct are filled
1086 * @backlog_bytes: total number of bytes currently backlogged
1087 * @backlog_packets: total number of packets currently backlogged
1088 * @flows: number of new flows seen
1089 * @drops: total number of packets dropped
1090 * @ecn_marks: total number of packets marked with ECN CE
1091 * @overlimit: number of drops due to queue space overflow
1092 * @overmemory: number of drops due to memory limit overflow
1093 * @collisions: number of hash collisions
1094 * @tx_bytes: total number of bytes dequeued
1095 * @tx_packets: total number of packets dequeued
1096 * @max_flows: maximum number of flows supported
1097 */
1098struct cfg80211_txq_stats {
1099 u32 filled;
1100 u32 backlog_bytes;
1101 u32 backlog_packets;
1102 u32 flows;
1103 u32 drops;
1104 u32 ecn_marks;
1105 u32 overlimit;
1106 u32 overmemory;
1107 u32 collisions;
1108 u32 tx_bytes;
1109 u32 tx_packets;
1110 u32 max_flows;
1111};
1112
1113/**
1083 * struct cfg80211_tid_stats - per-TID statistics 1114 * struct cfg80211_tid_stats - per-TID statistics
1084 * @filled: bitmap of flags using the bits of &enum nl80211_tid_stats to 1115 * @filled: bitmap of flags using the bits of &enum nl80211_tid_stats to
1085 * indicate the relevant values in this struct are filled 1116 * indicate the relevant values in this struct are filled
@@ -1088,6 +1119,7 @@ struct sta_bss_parameters {
1088 * @tx_msdu_retries: number of retries (not counting the first) for 1119 * @tx_msdu_retries: number of retries (not counting the first) for
1089 * transmitted MSDUs 1120 * transmitted MSDUs
1090 * @tx_msdu_failed: number of failed transmitted MSDUs 1121 * @tx_msdu_failed: number of failed transmitted MSDUs
1122 * @txq_stats: TXQ statistics
1091 */ 1123 */
1092struct cfg80211_tid_stats { 1124struct cfg80211_tid_stats {
1093 u32 filled; 1125 u32 filled;
@@ -1095,6 +1127,7 @@ struct cfg80211_tid_stats {
1095 u64 tx_msdu; 1127 u64 tx_msdu;
1096 u64 tx_msdu_retries; 1128 u64 tx_msdu_retries;
1097 u64 tx_msdu_failed; 1129 u64 tx_msdu_failed;
1130 struct cfg80211_txq_stats txq_stats;
1098}; 1131};
1099 1132
1100#define IEEE80211_MAX_CHAINS 4 1133#define IEEE80211_MAX_CHAINS 4
@@ -2204,6 +2237,9 @@ enum cfg80211_connect_params_changed {
2204 * @WIPHY_PARAM_RTS_THRESHOLD: wiphy->rts_threshold has changed 2237 * @WIPHY_PARAM_RTS_THRESHOLD: wiphy->rts_threshold has changed
2205 * @WIPHY_PARAM_COVERAGE_CLASS: coverage class changed 2238 * @WIPHY_PARAM_COVERAGE_CLASS: coverage class changed
2206 * @WIPHY_PARAM_DYN_ACK: dynack has been enabled 2239 * @WIPHY_PARAM_DYN_ACK: dynack has been enabled
2240 * @WIPHY_PARAM_TXQ_LIMIT: TXQ packet limit has been changed
2241 * @WIPHY_PARAM_TXQ_MEMORY_LIMIT: TXQ memory limit has been changed
2242 * @WIPHY_PARAM_TXQ_QUANTUM: TXQ scheduler quantum
2207 */ 2243 */
2208enum wiphy_params_flags { 2244enum wiphy_params_flags {
2209 WIPHY_PARAM_RETRY_SHORT = 1 << 0, 2245 WIPHY_PARAM_RETRY_SHORT = 1 << 0,
@@ -2212,6 +2248,9 @@ enum wiphy_params_flags {
2212 WIPHY_PARAM_RTS_THRESHOLD = 1 << 3, 2248 WIPHY_PARAM_RTS_THRESHOLD = 1 << 3,
2213 WIPHY_PARAM_COVERAGE_CLASS = 1 << 4, 2249 WIPHY_PARAM_COVERAGE_CLASS = 1 << 4,
2214 WIPHY_PARAM_DYN_ACK = 1 << 5, 2250 WIPHY_PARAM_DYN_ACK = 1 << 5,
2251 WIPHY_PARAM_TXQ_LIMIT = 1 << 6,
2252 WIPHY_PARAM_TXQ_MEMORY_LIMIT = 1 << 7,
2253 WIPHY_PARAM_TXQ_QUANTUM = 1 << 8,
2215}; 2254};
2216 2255
2217/** 2256/**
@@ -2964,6 +3003,9 @@ struct cfg80211_external_auth_params {
2964 * 3003 *
2965 * @set_multicast_to_unicast: configure multicast to unicast conversion for BSS 3004 * @set_multicast_to_unicast: configure multicast to unicast conversion for BSS
2966 * 3005 *
3006 * @get_txq_stats: Get TXQ stats for interface or phy. If wdev is %NULL, this
3007 * function should return phy stats, and interface stats otherwise.
3008 *
2967 * @set_pmk: configure the PMK to be used for offloaded 802.1X 4-Way handshake. 3009 * @set_pmk: configure the PMK to be used for offloaded 802.1X 4-Way handshake.
2968 * If not deleted through @del_pmk the PMK remains valid until disconnect 3010 * If not deleted through @del_pmk the PMK remains valid until disconnect
2969 * upon which the driver should clear it. 3011 * upon which the driver should clear it.
@@ -3265,6 +3307,10 @@ struct cfg80211_ops {
3265 struct net_device *dev, 3307 struct net_device *dev,
3266 const bool enabled); 3308 const bool enabled);
3267 3309
3310 int (*get_txq_stats)(struct wiphy *wiphy,
3311 struct wireless_dev *wdev,
3312 struct cfg80211_txq_stats *txqstats);
3313
3268 int (*set_pmk)(struct wiphy *wiphy, struct net_device *dev, 3314 int (*set_pmk)(struct wiphy *wiphy, struct net_device *dev,
3269 const struct cfg80211_pmk_conf *conf); 3315 const struct cfg80211_pmk_conf *conf);
3270 int (*del_pmk)(struct wiphy *wiphy, struct net_device *dev, 3316 int (*del_pmk)(struct wiphy *wiphy, struct net_device *dev,
@@ -3943,6 +3989,10 @@ struct wiphy {
3943 3989
3944 u8 nan_supported_bands; 3990 u8 nan_supported_bands;
3945 3991
3992 u32 txq_limit;
3993 u32 txq_memory_limit;
3994 u32 txq_quantum;
3995
3946 char priv[0] __aligned(NETDEV_ALIGN); 3996 char priv[0] __aligned(NETDEV_ALIGN);
3947}; 3997};
3948 3998
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 8e55b63ed3ff..5e67e3444aba 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -2226,6 +2226,16 @@ enum nl80211_commands {
2226 * @NL80211_ATTR_NSS: Station's New/updated RX_NSS value notified using this 2226 * @NL80211_ATTR_NSS: Station's New/updated RX_NSS value notified using this
2227 * u8 attribute. This is used with %NL80211_CMD_STA_OPMODE_CHANGED. 2227 * u8 attribute. This is used with %NL80211_CMD_STA_OPMODE_CHANGED.
2228 * 2228 *
2229 * @NL80211_ATTR_TXQ_STATS: TXQ statistics (nested attribute, see &enum
2230 * nl80211_txq_stats)
2231 * @NL80211_ATTR_TXQ_LIMIT: Total packet limit for the TXQ queues for this phy.
2232 * The smaller of this and the memory limit is enforced.
2233 * @NL80211_ATTR_TXQ_MEMORY_LIMIT: Total memory memory limit (in bytes) for the
2234 * TXQ queues for this phy. The smaller of this and the packet limit is
2235 * enforced.
2236 * @NL80211_ATTR_TXQ_QUANTUM: TXQ scheduler quantum (bytes). Number of bytes
2237 * a flow is assigned on each round of the DRR scheduler.
2238 *
2229 * @NUM_NL80211_ATTR: total number of nl80211_attrs available 2239 * @NUM_NL80211_ATTR: total number of nl80211_attrs available
2230 * @NL80211_ATTR_MAX: highest attribute number currently defined 2240 * @NL80211_ATTR_MAX: highest attribute number currently defined
2231 * @__NL80211_ATTR_AFTER_LAST: internal use 2241 * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2660,6 +2670,11 @@ enum nl80211_attrs {
2660 2670
2661 NL80211_ATTR_CONTROL_PORT_OVER_NL80211, 2671 NL80211_ATTR_CONTROL_PORT_OVER_NL80211,
2662 2672
2673 NL80211_ATTR_TXQ_STATS,
2674 NL80211_ATTR_TXQ_LIMIT,
2675 NL80211_ATTR_TXQ_MEMORY_LIMIT,
2676 NL80211_ATTR_TXQ_QUANTUM,
2677
2663 /* add attributes here, update the policy in nl80211.c */ 2678 /* add attributes here, update the policy in nl80211.c */
2664 2679
2665 __NL80211_ATTR_AFTER_LAST, 2680 __NL80211_ATTR_AFTER_LAST,
@@ -3040,6 +3055,7 @@ enum nl80211_sta_info {
3040 * @NL80211_TID_STATS_TX_MSDU_FAILED: number of failed transmitted 3055 * @NL80211_TID_STATS_TX_MSDU_FAILED: number of failed transmitted
3041 * MSDUs (u64) 3056 * MSDUs (u64)
3042 * @NL80211_TID_STATS_PAD: attribute used for padding for 64-bit alignment 3057 * @NL80211_TID_STATS_PAD: attribute used for padding for 64-bit alignment
3058 * @NL80211_TID_STATS_TXQ_STATS: TXQ stats (nested attribute)
3043 * @NUM_NL80211_TID_STATS: number of attributes here 3059 * @NUM_NL80211_TID_STATS: number of attributes here
3044 * @NL80211_TID_STATS_MAX: highest numbered attribute here 3060 * @NL80211_TID_STATS_MAX: highest numbered attribute here
3045 */ 3061 */
@@ -3050,6 +3066,7 @@ enum nl80211_tid_stats {
3050 NL80211_TID_STATS_TX_MSDU_RETRIES, 3066 NL80211_TID_STATS_TX_MSDU_RETRIES,
3051 NL80211_TID_STATS_TX_MSDU_FAILED, 3067 NL80211_TID_STATS_TX_MSDU_FAILED,
3052 NL80211_TID_STATS_PAD, 3068 NL80211_TID_STATS_PAD,
3069 NL80211_TID_STATS_TXQ_STATS,
3053 3070
3054 /* keep last */ 3071 /* keep last */
3055 NUM_NL80211_TID_STATS, 3072 NUM_NL80211_TID_STATS,
@@ -3057,6 +3074,44 @@ enum nl80211_tid_stats {
3057}; 3074};
3058 3075
3059/** 3076/**
3077 * enum nl80211_txq_stats - per TXQ statistics attributes
3078 * @__NL80211_TXQ_STATS_INVALID: attribute number 0 is reserved
3079 * @NUM_NL80211_TXQ_STATS: number of attributes here
3080 * @NL80211_TXQ_STATS_BACKLOG_BYTES: number of bytes currently backlogged
3081 * @NL80211_TXQ_STATS_BACKLOG_PACKETS: number of packets currently
3082 * backlogged
3083 * @NL80211_TXQ_STATS_FLOWS: total number of new flows seen
3084 * @NL80211_TXQ_STATS_DROPS: total number of packet drops
3085 * @NL80211_TXQ_STATS_ECN_MARKS: total number of packet ECN marks
3086 * @NL80211_TXQ_STATS_OVERLIMIT: number of drops due to queue space overflow
3087 * @NL80211_TXQ_STATS_OVERMEMORY: number of drops due to memory limit overflow
3088 * (only for per-phy stats)
3089 * @NL80211_TXQ_STATS_COLLISIONS: number of hash collisions
3090 * @NL80211_TXQ_STATS_TX_BYTES: total number of bytes dequeued from TXQ
3091 * @NL80211_TXQ_STATS_TX_PACKETS: total number of packets dequeued from TXQ
3092 * @NL80211_TXQ_STATS_MAX_FLOWS: number of flow buckets for PHY
3093 * @NL80211_TXQ_STATS_MAX: highest numbered attribute here
3094 */
3095enum nl80211_txq_stats {
3096 __NL80211_TXQ_STATS_INVALID,
3097 NL80211_TXQ_STATS_BACKLOG_BYTES,
3098 NL80211_TXQ_STATS_BACKLOG_PACKETS,
3099 NL80211_TXQ_STATS_FLOWS,
3100 NL80211_TXQ_STATS_DROPS,
3101 NL80211_TXQ_STATS_ECN_MARKS,
3102 NL80211_TXQ_STATS_OVERLIMIT,
3103 NL80211_TXQ_STATS_OVERMEMORY,
3104 NL80211_TXQ_STATS_COLLISIONS,
3105 NL80211_TXQ_STATS_TX_BYTES,
3106 NL80211_TXQ_STATS_TX_PACKETS,
3107 NL80211_TXQ_STATS_MAX_FLOWS,
3108
3109 /* keep last */
3110 NUM_NL80211_TXQ_STATS,
3111 NL80211_TXQ_STATS_MAX = NUM_NL80211_TXQ_STATS - 1
3112};
3113
3114/**
3060 * enum nl80211_mpath_flags - nl80211 mesh path flags 3115 * enum nl80211_mpath_flags - nl80211 mesh path flags
3061 * 3116 *
3062 * @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active 3117 * @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active
@@ -5072,6 +5127,8 @@ enum nl80211_feature_flags {
5072 * @NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT: This Driver support data ack 5127 * @NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT: This Driver support data ack
5073 * rssi if firmware support, this flag is to intimate about ack rssi 5128 * rssi if firmware support, this flag is to intimate about ack rssi
5074 * support to nl80211. 5129 * support to nl80211.
5130 * @NL80211_EXT_FEATURE_TXQS: Driver supports FQ-CoDel-enabled intermediate
5131 * TXQs.
5075 * 5132 *
5076 * @NUM_NL80211_EXT_FEATURES: number of extended features. 5133 * @NUM_NL80211_EXT_FEATURES: number of extended features.
5077 * @MAX_NL80211_EXT_FEATURES: highest extended feature index. 5134 * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
@@ -5105,6 +5162,7 @@ enum nl80211_ext_feature_index {
5105 NL80211_EXT_FEATURE_DFS_OFFLOAD, 5162 NL80211_EXT_FEATURE_DFS_OFFLOAD,
5106 NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211, 5163 NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211,
5107 NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT, 5164 NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT,
5165 NL80211_EXT_FEATURE_TXQS,
5108 5166
5109 /* add new features before the definition below */ 5167 /* add new features before the definition below */
5110 NUM_NL80211_EXT_FEATURES, 5168 NUM_NL80211_EXT_FEATURES,
diff --git a/net/mac80211/ethtool.c b/net/mac80211/ethtool.c
index 08408520c3f8..1afeff94af8b 100644
--- a/net/mac80211/ethtool.c
+++ b/net/mac80211/ethtool.c
@@ -71,11 +71,15 @@ static void ieee80211_get_stats(struct net_device *dev,
71 struct ieee80211_channel *channel; 71 struct ieee80211_channel *channel;
72 struct sta_info *sta; 72 struct sta_info *sta;
73 struct ieee80211_local *local = sdata->local; 73 struct ieee80211_local *local = sdata->local;
74 struct station_info sinfo; 74 struct station_info *sinfo;
75 struct survey_info survey; 75 struct survey_info survey;
76 int i, q; 76 int i, q;
77#define STA_STATS_SURVEY_LEN 7 77#define STA_STATS_SURVEY_LEN 7
78 78
79 sinfo = kmalloc(sizeof(*sinfo), GFP_KERNEL);
80 if (!sinfo)
81 return;
82
79 memset(data, 0, sizeof(u64) * STA_STATS_LEN); 83 memset(data, 0, sizeof(u64) * STA_STATS_LEN);
80 84
81#define ADD_STA_STATS(sta) \ 85#define ADD_STA_STATS(sta) \
@@ -86,8 +90,8 @@ static void ieee80211_get_stats(struct net_device *dev,
86 data[i++] += sta->rx_stats.fragments; \ 90 data[i++] += sta->rx_stats.fragments; \
87 data[i++] += sta->rx_stats.dropped; \ 91 data[i++] += sta->rx_stats.dropped; \
88 \ 92 \
89 data[i++] += sinfo.tx_packets; \ 93 data[i++] += sinfo->tx_packets; \
90 data[i++] += sinfo.tx_bytes; \ 94 data[i++] += sinfo->tx_bytes; \
91 data[i++] += sta->status_stats.filtered; \ 95 data[i++] += sta->status_stats.filtered; \
92 data[i++] += sta->status_stats.retry_failed; \ 96 data[i++] += sta->status_stats.retry_failed; \
93 data[i++] += sta->status_stats.retry_count; \ 97 data[i++] += sta->status_stats.retry_count; \
@@ -107,8 +111,8 @@ static void ieee80211_get_stats(struct net_device *dev,
107 if (!(sta && !WARN_ON(sta->sdata->dev != dev))) 111 if (!(sta && !WARN_ON(sta->sdata->dev != dev)))
108 goto do_survey; 112 goto do_survey;
109 113
110 memset(&sinfo, 0, sizeof(sinfo)); 114 memset(sinfo, 0, sizeof(*sinfo));
111 sta_set_sinfo(sta, &sinfo); 115 sta_set_sinfo(sta, sinfo);
112 116
113 i = 0; 117 i = 0;
114 ADD_STA_STATS(sta); 118 ADD_STA_STATS(sta);
@@ -116,17 +120,17 @@ static void ieee80211_get_stats(struct net_device *dev,
116 data[i++] = sta->sta_state; 120 data[i++] = sta->sta_state;
117 121
118 122
119 if (sinfo.filled & BIT(NL80211_STA_INFO_TX_BITRATE)) 123 if (sinfo->filled & BIT(NL80211_STA_INFO_TX_BITRATE))
120 data[i] = 100000 * 124 data[i] = 100000 *
121 cfg80211_calculate_bitrate(&sinfo.txrate); 125 cfg80211_calculate_bitrate(&sinfo->txrate);
122 i++; 126 i++;
123 if (sinfo.filled & BIT(NL80211_STA_INFO_RX_BITRATE)) 127 if (sinfo->filled & BIT(NL80211_STA_INFO_RX_BITRATE))
124 data[i] = 100000 * 128 data[i] = 100000 *
125 cfg80211_calculate_bitrate(&sinfo.rxrate); 129 cfg80211_calculate_bitrate(&sinfo->rxrate);
126 i++; 130 i++;
127 131
128 if (sinfo.filled & BIT(NL80211_STA_INFO_SIGNAL_AVG)) 132 if (sinfo->filled & BIT(NL80211_STA_INFO_SIGNAL_AVG))
129 data[i] = (u8)sinfo.signal_avg; 133 data[i] = (u8)sinfo->signal_avg;
130 i++; 134 i++;
131 } else { 135 } else {
132 list_for_each_entry(sta, &local->sta_list, list) { 136 list_for_each_entry(sta, &local->sta_list, list) {
@@ -134,14 +138,16 @@ static void ieee80211_get_stats(struct net_device *dev,
134 if (sta->sdata->dev != dev) 138 if (sta->sdata->dev != dev)
135 continue; 139 continue;
136 140
137 memset(&sinfo, 0, sizeof(sinfo)); 141 memset(sinfo, 0, sizeof(*sinfo));
138 sta_set_sinfo(sta, &sinfo); 142 sta_set_sinfo(sta, sinfo);
139 i = 0; 143 i = 0;
140 ADD_STA_STATS(sta); 144 ADD_STA_STATS(sta);
141 } 145 }
142 } 146 }
143 147
144do_survey: 148do_survey:
149 kfree(sinfo);
150
145 i = STA_STATS_LEN - STA_STATS_SURVEY_LEN; 151 i = STA_STATS_LEN - STA_STATS_SURVEY_LEN;
146 /* Get survey stats for current channel */ 152 /* Get survey stats for current channel */
147 survey.filled = 0; 153 survey.filled = 0;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 6b942a68d1c8..f7715b85fd2b 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -424,6 +424,10 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
424 [NL80211_ATTR_PMK] = { .type = NLA_BINARY, .len = PMK_MAX_LEN }, 424 [NL80211_ATTR_PMK] = { .type = NLA_BINARY, .len = PMK_MAX_LEN },
425 [NL80211_ATTR_SCHED_SCAN_MULTI] = { .type = NLA_FLAG }, 425 [NL80211_ATTR_SCHED_SCAN_MULTI] = { .type = NLA_FLAG },
426 [NL80211_ATTR_EXTERNAL_AUTH_SUPPORT] = { .type = NLA_FLAG }, 426 [NL80211_ATTR_EXTERNAL_AUTH_SUPPORT] = { .type = NLA_FLAG },
427
428 [NL80211_ATTR_TXQ_LIMIT] = { .type = NLA_U32 },
429 [NL80211_ATTR_TXQ_MEMORY_LIMIT] = { .type = NLA_U32 },
430 [NL80211_ATTR_TXQ_QUANTUM] = { .type = NLA_U32 },
427}; 431};
428 432
429/* policy for the key attributes */ 433/* policy for the key attributes */
@@ -774,6 +778,39 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy,
774 return -ENOBUFS; 778 return -ENOBUFS;
775} 779}
776 780
781static bool nl80211_put_txq_stats(struct sk_buff *msg,
782 struct cfg80211_txq_stats *txqstats,
783 int attrtype)
784{
785 struct nlattr *txqattr;
786
787#define PUT_TXQVAL_U32(attr, memb) do { \
788 if (txqstats->filled & BIT(NL80211_TXQ_STATS_ ## attr) && \
789 nla_put_u32(msg, NL80211_TXQ_STATS_ ## attr, txqstats->memb)) \
790 return false; \
791 } while (0)
792
793 txqattr = nla_nest_start(msg, attrtype);
794 if (!txqattr)
795 return false;
796
797 PUT_TXQVAL_U32(BACKLOG_BYTES, backlog_bytes);
798 PUT_TXQVAL_U32(BACKLOG_PACKETS, backlog_packets);
799 PUT_TXQVAL_U32(FLOWS, flows);
800 PUT_TXQVAL_U32(DROPS, drops);
801 PUT_TXQVAL_U32(ECN_MARKS, ecn_marks);
802 PUT_TXQVAL_U32(OVERLIMIT, overlimit);
803 PUT_TXQVAL_U32(OVERMEMORY, overmemory);
804 PUT_TXQVAL_U32(COLLISIONS, collisions);
805 PUT_TXQVAL_U32(TX_BYTES, tx_bytes);
806 PUT_TXQVAL_U32(TX_PACKETS, tx_packets);
807 PUT_TXQVAL_U32(MAX_FLOWS, max_flows);
808 nla_nest_end(msg, txqattr);
809
810#undef PUT_TXQVAL_U32
811 return true;
812}
813
777/* netlink command implementations */ 814/* netlink command implementations */
778 815
779struct key_parse { 816struct key_parse {
@@ -1973,6 +2010,28 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
1973 rdev->wiphy.nan_supported_bands)) 2010 rdev->wiphy.nan_supported_bands))
1974 goto nla_put_failure; 2011 goto nla_put_failure;
1975 2012
2013 if (wiphy_ext_feature_isset(&rdev->wiphy,
2014 NL80211_EXT_FEATURE_TXQS)) {
2015 struct cfg80211_txq_stats txqstats = {};
2016 int res;
2017
2018 res = rdev_get_txq_stats(rdev, NULL, &txqstats);
2019 if (!res &&
2020 !nl80211_put_txq_stats(msg, &txqstats,
2021 NL80211_ATTR_TXQ_STATS))
2022 goto nla_put_failure;
2023
2024 if (nla_put_u32(msg, NL80211_ATTR_TXQ_LIMIT,
2025 rdev->wiphy.txq_limit))
2026 goto nla_put_failure;
2027 if (nla_put_u32(msg, NL80211_ATTR_TXQ_MEMORY_LIMIT,
2028 rdev->wiphy.txq_memory_limit))
2029 goto nla_put_failure;
2030 if (nla_put_u32(msg, NL80211_ATTR_TXQ_QUANTUM,
2031 rdev->wiphy.txq_quantum))
2032 goto nla_put_failure;
2033 }
2034
1976 /* done */ 2035 /* done */
1977 state->split_start = 0; 2036 state->split_start = 0;
1978 break; 2037 break;
@@ -2350,6 +2409,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
2350 u8 retry_short = 0, retry_long = 0; 2409 u8 retry_short = 0, retry_long = 0;
2351 u32 frag_threshold = 0, rts_threshold = 0; 2410 u32 frag_threshold = 0, rts_threshold = 0;
2352 u8 coverage_class = 0; 2411 u8 coverage_class = 0;
2412 u32 txq_limit = 0, txq_memory_limit = 0, txq_quantum = 0;
2353 2413
2354 ASSERT_RTNL(); 2414 ASSERT_RTNL();
2355 2415
@@ -2556,10 +2616,38 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
2556 changed |= WIPHY_PARAM_DYN_ACK; 2616 changed |= WIPHY_PARAM_DYN_ACK;
2557 } 2617 }
2558 2618
2619 if (info->attrs[NL80211_ATTR_TXQ_LIMIT]) {
2620 if (!wiphy_ext_feature_isset(&rdev->wiphy,
2621 NL80211_EXT_FEATURE_TXQS))
2622 return -EOPNOTSUPP;
2623 txq_limit = nla_get_u32(
2624 info->attrs[NL80211_ATTR_TXQ_LIMIT]);
2625 changed |= WIPHY_PARAM_TXQ_LIMIT;
2626 }
2627
2628 if (info->attrs[NL80211_ATTR_TXQ_MEMORY_LIMIT]) {
2629 if (!wiphy_ext_feature_isset(&rdev->wiphy,
2630 NL80211_EXT_FEATURE_TXQS))
2631 return -EOPNOTSUPP;
2632 txq_memory_limit = nla_get_u32(
2633 info->attrs[NL80211_ATTR_TXQ_MEMORY_LIMIT]);
2634 changed |= WIPHY_PARAM_TXQ_MEMORY_LIMIT;
2635 }
2636
2637 if (info->attrs[NL80211_ATTR_TXQ_QUANTUM]) {
2638 if (!wiphy_ext_feature_isset(&rdev->wiphy,
2639 NL80211_EXT_FEATURE_TXQS))
2640 return -EOPNOTSUPP;
2641 txq_quantum = nla_get_u32(
2642 info->attrs[NL80211_ATTR_TXQ_QUANTUM]);
2643 changed |= WIPHY_PARAM_TXQ_QUANTUM;
2644 }
2645
2559 if (changed) { 2646 if (changed) {
2560 u8 old_retry_short, old_retry_long; 2647 u8 old_retry_short, old_retry_long;
2561 u32 old_frag_threshold, old_rts_threshold; 2648 u32 old_frag_threshold, old_rts_threshold;
2562 u8 old_coverage_class; 2649 u8 old_coverage_class;
2650 u32 old_txq_limit, old_txq_memory_limit, old_txq_quantum;
2563 2651
2564 if (!rdev->ops->set_wiphy_params) 2652 if (!rdev->ops->set_wiphy_params)
2565 return -EOPNOTSUPP; 2653 return -EOPNOTSUPP;
@@ -2569,6 +2657,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
2569 old_frag_threshold = rdev->wiphy.frag_threshold; 2657 old_frag_threshold = rdev->wiphy.frag_threshold;
2570 old_rts_threshold = rdev->wiphy.rts_threshold; 2658 old_rts_threshold = rdev->wiphy.rts_threshold;
2571 old_coverage_class = rdev->wiphy.coverage_class; 2659 old_coverage_class = rdev->wiphy.coverage_class;
2660 old_txq_limit = rdev->wiphy.txq_limit;
2661 old_txq_memory_limit = rdev->wiphy.txq_memory_limit;
2662 old_txq_quantum = rdev->wiphy.txq_quantum;
2572 2663
2573 if (changed & WIPHY_PARAM_RETRY_SHORT) 2664 if (changed & WIPHY_PARAM_RETRY_SHORT)
2574 rdev->wiphy.retry_short = retry_short; 2665 rdev->wiphy.retry_short = retry_short;
@@ -2580,6 +2671,12 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
2580 rdev->wiphy.rts_threshold = rts_threshold; 2671 rdev->wiphy.rts_threshold = rts_threshold;
2581 if (changed & WIPHY_PARAM_COVERAGE_CLASS) 2672 if (changed & WIPHY_PARAM_COVERAGE_CLASS)
2582 rdev->wiphy.coverage_class = coverage_class; 2673 rdev->wiphy.coverage_class = coverage_class;
2674 if (changed & WIPHY_PARAM_TXQ_LIMIT)
2675 rdev->wiphy.txq_limit = txq_limit;
2676 if (changed & WIPHY_PARAM_TXQ_MEMORY_LIMIT)
2677 rdev->wiphy.txq_memory_limit = txq_memory_limit;
2678 if (changed & WIPHY_PARAM_TXQ_QUANTUM)
2679 rdev->wiphy.txq_quantum = txq_quantum;
2583 2680
2584 result = rdev_set_wiphy_params(rdev, changed); 2681 result = rdev_set_wiphy_params(rdev, changed);
2585 if (result) { 2682 if (result) {
@@ -2588,6 +2685,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
2588 rdev->wiphy.frag_threshold = old_frag_threshold; 2685 rdev->wiphy.frag_threshold = old_frag_threshold;
2589 rdev->wiphy.rts_threshold = old_rts_threshold; 2686 rdev->wiphy.rts_threshold = old_rts_threshold;
2590 rdev->wiphy.coverage_class = old_coverage_class; 2687 rdev->wiphy.coverage_class = old_coverage_class;
2688 rdev->wiphy.txq_limit = old_txq_limit;
2689 rdev->wiphy.txq_memory_limit = old_txq_memory_limit;
2690 rdev->wiphy.txq_quantum = old_txq_quantum;
2591 return result; 2691 return result;
2592 } 2692 }
2593 } 2693 }
@@ -2709,6 +2809,16 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag
2709 } 2809 }
2710 wdev_unlock(wdev); 2810 wdev_unlock(wdev);
2711 2811
2812 if (rdev->ops->get_txq_stats) {
2813 struct cfg80211_txq_stats txqstats = {};
2814 int ret = rdev_get_txq_stats(rdev, wdev, &txqstats);
2815
2816 if (ret == 0 &&
2817 !nl80211_put_txq_stats(msg, &txqstats,
2818 NL80211_ATTR_TXQ_STATS))
2819 goto nla_put_failure;
2820 }
2821
2712 genlmsg_end(msg, hdr); 2822 genlmsg_end(msg, hdr);
2713 return 0; 2823 return 0;
2714 2824
@@ -4582,6 +4692,12 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
4582 PUT_TIDVAL_U64(TX_MSDU_FAILED, tx_msdu_failed); 4692 PUT_TIDVAL_U64(TX_MSDU_FAILED, tx_msdu_failed);
4583 4693
4584#undef PUT_TIDVAL_U64 4694#undef PUT_TIDVAL_U64
4695 if ((tidstats->filled &
4696 BIT(NL80211_TID_STATS_TXQ_STATS)) &&
4697 !nl80211_put_txq_stats(msg, &tidstats->txq_stats,
4698 NL80211_TID_STATS_TXQ_STATS))
4699 goto nla_put_failure;
4700
4585 nla_nest_end(msg, tidattr); 4701 nla_nest_end(msg, tidattr);
4586 } 4702 }
4587 4703
@@ -4606,13 +4722,17 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
4606static int nl80211_dump_station(struct sk_buff *skb, 4722static int nl80211_dump_station(struct sk_buff *skb,
4607 struct netlink_callback *cb) 4723 struct netlink_callback *cb)
4608{ 4724{
4609 struct station_info sinfo; 4725 struct station_info *sinfo;
4610 struct cfg80211_registered_device *rdev; 4726 struct cfg80211_registered_device *rdev;
4611 struct wireless_dev *wdev; 4727 struct wireless_dev *wdev;
4612 u8 mac_addr[ETH_ALEN]; 4728 u8 mac_addr[ETH_ALEN];
4613 int sta_idx = cb->args[2]; 4729 int sta_idx = cb->args[2];
4614 int err; 4730 int err;
4615 4731
4732 sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
4733 if (!sinfo)
4734 return -ENOMEM;
4735
4616 rtnl_lock(); 4736 rtnl_lock();
4617 err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev); 4737 err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
4618 if (err) 4738 if (err)
@@ -4629,9 +4749,9 @@ static int nl80211_dump_station(struct sk_buff *skb,
4629 } 4749 }
4630 4750
4631 while (1) { 4751 while (1) {
4632 memset(&sinfo, 0, sizeof(sinfo)); 4752 memset(sinfo, 0, sizeof(*sinfo));
4633 err = rdev_dump_station(rdev, wdev->netdev, sta_idx, 4753 err = rdev_dump_station(rdev, wdev->netdev, sta_idx,
4634 mac_addr, &sinfo); 4754 mac_addr, sinfo);
4635 if (err == -ENOENT) 4755 if (err == -ENOENT)
4636 break; 4756 break;
4637 if (err) 4757 if (err)
@@ -4641,7 +4761,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
4641 NETLINK_CB(cb->skb).portid, 4761 NETLINK_CB(cb->skb).portid,
4642 cb->nlh->nlmsg_seq, NLM_F_MULTI, 4762 cb->nlh->nlmsg_seq, NLM_F_MULTI,
4643 rdev, wdev->netdev, mac_addr, 4763 rdev, wdev->netdev, mac_addr,
4644 &sinfo) < 0) 4764 sinfo) < 0)
4645 goto out; 4765 goto out;
4646 4766
4647 sta_idx++; 4767 sta_idx++;
@@ -4652,6 +4772,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
4652 err = skb->len; 4772 err = skb->len;
4653 out_err: 4773 out_err:
4654 rtnl_unlock(); 4774 rtnl_unlock();
4775 kfree(sinfo);
4655 4776
4656 return err; 4777 return err;
4657} 4778}
@@ -4660,37 +4781,49 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
4660{ 4781{
4661 struct cfg80211_registered_device *rdev = info->user_ptr[0]; 4782 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4662 struct net_device *dev = info->user_ptr[1]; 4783 struct net_device *dev = info->user_ptr[1];
4663 struct station_info sinfo; 4784 struct station_info *sinfo;
4664 struct sk_buff *msg; 4785 struct sk_buff *msg;
4665 u8 *mac_addr = NULL; 4786 u8 *mac_addr = NULL;
4666 int err; 4787 int err;
4667 4788
4668 memset(&sinfo, 0, sizeof(sinfo)); 4789 sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
4790 if (!sinfo)
4791 return -ENOMEM;
4669 4792
4670 if (!info->attrs[NL80211_ATTR_MAC]) 4793 if (!info->attrs[NL80211_ATTR_MAC]) {
4671 return -EINVAL; 4794 err = -EINVAL;
4795 goto out;
4796 }
4672 4797
4673 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); 4798 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
4674 4799
4675 if (!rdev->ops->get_station) 4800 if (!rdev->ops->get_station) {
4676 return -EOPNOTSUPP; 4801 err = -EOPNOTSUPP;
4802 goto out;
4803 }
4677 4804
4678 err = rdev_get_station(rdev, dev, mac_addr, &sinfo); 4805 err = rdev_get_station(rdev, dev, mac_addr, sinfo);
4679 if (err) 4806 if (err)
4680 return err; 4807 goto out;
4681 4808
4682 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 4809 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
4683 if (!msg) 4810 if (!msg) {
4684 return -ENOMEM; 4811 err = -ENOMEM;
4812 goto out;
4813 }
4685 4814
4686 if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION, 4815 if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION,
4687 info->snd_portid, info->snd_seq, 0, 4816 info->snd_portid, info->snd_seq, 0,
4688 rdev, dev, mac_addr, &sinfo) < 0) { 4817 rdev, dev, mac_addr, sinfo) < 0) {
4689 nlmsg_free(msg); 4818 nlmsg_free(msg);
4690 return -ENOBUFS; 4819 err = -ENOBUFS;
4820 goto out;
4691 } 4821 }
4692 4822
4693 return genlmsg_reply(msg, info); 4823 err = genlmsg_reply(msg, info);
4824out:
4825 kfree(sinfo);
4826 return err;
4694} 4827}
4695 4828
4696int cfg80211_check_station_change(struct wiphy *wiphy, 4829int cfg80211_check_station_change(struct wiphy *wiphy,
@@ -9954,18 +10087,26 @@ static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev,
9954 */ 10087 */
9955 if (!wdev->cqm_config->last_rssi_event_value && wdev->current_bss && 10088 if (!wdev->cqm_config->last_rssi_event_value && wdev->current_bss &&
9956 rdev->ops->get_station) { 10089 rdev->ops->get_station) {
9957 struct station_info sinfo = {}; 10090 struct station_info *sinfo;
9958 u8 *mac_addr; 10091 u8 *mac_addr;
9959 10092
10093 sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
10094 if (!sinfo)
10095 return -ENOMEM;
10096
9960 mac_addr = wdev->current_bss->pub.bssid; 10097 mac_addr = wdev->current_bss->pub.bssid;
9961 10098
9962 err = rdev_get_station(rdev, dev, mac_addr, &sinfo); 10099 err = rdev_get_station(rdev, dev, mac_addr, sinfo);
9963 if (err) 10100 if (err) {
10101 kfree(sinfo);
9964 return err; 10102 return err;
10103 }
9965 10104
9966 if (sinfo.filled & BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG)) 10105 if (sinfo->filled & BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG))
9967 wdev->cqm_config->last_rssi_event_value = 10106 wdev->cqm_config->last_rssi_event_value =
9968 (s8) sinfo.rx_beacon_signal_avg; 10107 (s8)sinfo->rx_beacon_signal_avg;
10108
10109 kfree(sinfo);
9969 } 10110 }
9970 10111
9971 last = wdev->cqm_config->last_rssi_event_value; 10112 last = wdev->cqm_config->last_rssi_event_value;
@@ -14499,25 +14640,32 @@ void cfg80211_del_sta_sinfo(struct net_device *dev, const u8 *mac_addr,
14499 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; 14640 struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
14500 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); 14641 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
14501 struct sk_buff *msg; 14642 struct sk_buff *msg;
14502 struct station_info empty_sinfo = {}; 14643 struct station_info *empty_sinfo = NULL;
14503 14644
14504 if (!sinfo) 14645 if (!sinfo) {
14505 sinfo = &empty_sinfo; 14646 empty_sinfo = kzalloc(sizeof(*empty_sinfo), GFP_KERNEL);
14647 if (!empty_sinfo)
14648 return;
14649 sinfo = empty_sinfo;
14650 }
14506 14651
14507 trace_cfg80211_del_sta(dev, mac_addr); 14652 trace_cfg80211_del_sta(dev, mac_addr);
14508 14653
14509 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); 14654 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
14510 if (!msg) 14655 if (!msg)
14511 return; 14656 goto out;
14512 14657
14513 if (nl80211_send_station(msg, NL80211_CMD_DEL_STATION, 0, 0, 0, 14658 if (nl80211_send_station(msg, NL80211_CMD_DEL_STATION, 0, 0, 0,
14514 rdev, dev, mac_addr, sinfo) < 0) { 14659 rdev, dev, mac_addr, sinfo) < 0) {
14515 nlmsg_free(msg); 14660 nlmsg_free(msg);
14516 return; 14661 goto out;
14517 } 14662 }
14518 14663
14519 genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, 14664 genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
14520 NL80211_MCGRP_MLME, gfp); 14665 NL80211_MCGRP_MLME, gfp);
14666
14667out:
14668 kfree(empty_sinfo);
14521} 14669}
14522EXPORT_SYMBOL(cfg80211_del_sta_sinfo); 14670EXPORT_SYMBOL(cfg80211_del_sta_sinfo);
14523 14671
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 87479a53411b..364f5d67f05b 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -586,6 +586,18 @@ rdev_set_multicast_to_unicast(struct cfg80211_registered_device *rdev,
586 return ret; 586 return ret;
587} 587}
588 588
589static inline int
590rdev_get_txq_stats(struct cfg80211_registered_device *rdev,
591 struct wireless_dev *wdev,
592 struct cfg80211_txq_stats *txqstats)
593{
594 int ret;
595 trace_rdev_get_txq_stats(&rdev->wiphy, wdev);
596 ret = rdev->ops->get_txq_stats(&rdev->wiphy, wdev, txqstats);
597 trace_rdev_return_int(&rdev->wiphy, ret);
598 return ret;
599}
600
589static inline void rdev_rfkill_poll(struct cfg80211_registered_device *rdev) 601static inline void rdev_rfkill_poll(struct cfg80211_registered_device *rdev)
590{ 602{
591 trace_rdev_rfkill_poll(&rdev->wiphy); 603 trace_rdev_rfkill_poll(&rdev->wiphy);
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index 55fb279a5196..2b417a2fe63f 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -3243,6 +3243,20 @@ TRACE_EVENT(rdev_set_multicast_to_unicast,
3243 WIPHY_PR_ARG, NETDEV_PR_ARG, 3243 WIPHY_PR_ARG, NETDEV_PR_ARG,
3244 BOOL_TO_STR(__entry->enabled)) 3244 BOOL_TO_STR(__entry->enabled))
3245); 3245);
3246
3247TRACE_EVENT(rdev_get_txq_stats,
3248 TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
3249 TP_ARGS(wiphy, wdev),
3250 TP_STRUCT__entry(
3251 WIPHY_ENTRY
3252 WDEV_ENTRY
3253 ),
3254 TP_fast_assign(
3255 WIPHY_ASSIGN;
3256 WDEV_ASSIGN;
3257 ),
3258 TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG)
3259);
3246#endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ 3260#endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
3247 3261
3248#undef TRACE_INCLUDE_PATH 3262#undef TRACE_INCLUDE_PATH
diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c
index 05186a47878f..9e002df0f8d8 100644
--- a/net/wireless/wext-compat.c
+++ b/net/wireless/wext-compat.c
@@ -1254,7 +1254,7 @@ static int cfg80211_wext_giwrate(struct net_device *dev,
1254{ 1254{
1255 struct wireless_dev *wdev = dev->ieee80211_ptr; 1255 struct wireless_dev *wdev = dev->ieee80211_ptr;
1256 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 1256 struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
1257 struct station_info sinfo = {}; 1257 struct station_info *sinfo;
1258 u8 addr[ETH_ALEN]; 1258 u8 addr[ETH_ALEN];
1259 int err; 1259 int err;
1260 1260
@@ -1274,16 +1274,23 @@ static int cfg80211_wext_giwrate(struct net_device *dev,
1274 if (err) 1274 if (err)
1275 return err; 1275 return err;
1276 1276
1277 err = rdev_get_station(rdev, dev, addr, &sinfo); 1277 sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
1278 if (err) 1278 if (!sinfo)
1279 return err; 1279 return -ENOMEM;
1280 1280
1281 if (!(sinfo.filled & BIT(NL80211_STA_INFO_TX_BITRATE))) 1281 err = rdev_get_station(rdev, dev, addr, sinfo);
1282 return -EOPNOTSUPP; 1282 if (err)
1283 goto out;
1283 1284
1284 rate->value = 100000 * cfg80211_calculate_bitrate(&sinfo.txrate); 1285 if (!(sinfo->filled & BIT(NL80211_STA_INFO_TX_BITRATE))) {
1286 err = -EOPNOTSUPP;
1287 goto out;
1288 }
1285 1289
1286 return 0; 1290 rate->value = 100000 * cfg80211_calculate_bitrate(&sinfo->txrate);
1291out:
1292 kfree(sinfo);
1293 return err;
1287} 1294}
1288 1295
1289/* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */ 1296/* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */