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>
This commit is contained in:
Toke Høiland-Jørgensen 2018-05-08 13:03:50 +02:00 committed by Johannes Berg
parent cc60dbbfed
commit 52539ca89f
7 changed files with 343 additions and 48 deletions

View file

@ -1079,6 +1079,37 @@ struct sta_bss_parameters {
u16 beacon_interval; u16 beacon_interval;
}; };
/**
* struct cfg80211_txq_stats - TXQ statistics for this TID
* @filled: bitmap of flags using the bits of &enum nl80211_txq_stats to
* indicate the relevant values in this struct are filled
* @backlog_bytes: total number of bytes currently backlogged
* @backlog_packets: total number of packets currently backlogged
* @flows: number of new flows seen
* @drops: total number of packets dropped
* @ecn_marks: total number of packets marked with ECN CE
* @overlimit: number of drops due to queue space overflow
* @overmemory: number of drops due to memory limit overflow
* @collisions: number of hash collisions
* @tx_bytes: total number of bytes dequeued
* @tx_packets: total number of packets dequeued
* @max_flows: maximum number of flows supported
*/
struct cfg80211_txq_stats {
u32 filled;
u32 backlog_bytes;
u32 backlog_packets;
u32 flows;
u32 drops;
u32 ecn_marks;
u32 overlimit;
u32 overmemory;
u32 collisions;
u32 tx_bytes;
u32 tx_packets;
u32 max_flows;
};
/** /**
* struct cfg80211_tid_stats - per-TID statistics * struct cfg80211_tid_stats - per-TID statistics
* @filled: bitmap of flags using the bits of &enum nl80211_tid_stats to * @filled: bitmap of flags using the bits of &enum nl80211_tid_stats to
@ -1088,6 +1119,7 @@ struct sta_bss_parameters {
* @tx_msdu_retries: number of retries (not counting the first) for * @tx_msdu_retries: number of retries (not counting the first) for
* transmitted MSDUs * transmitted MSDUs
* @tx_msdu_failed: number of failed transmitted MSDUs * @tx_msdu_failed: number of failed transmitted MSDUs
* @txq_stats: TXQ statistics
*/ */
struct cfg80211_tid_stats { struct cfg80211_tid_stats {
u32 filled; u32 filled;
@ -1095,6 +1127,7 @@ struct cfg80211_tid_stats {
u64 tx_msdu; u64 tx_msdu;
u64 tx_msdu_retries; u64 tx_msdu_retries;
u64 tx_msdu_failed; u64 tx_msdu_failed;
struct cfg80211_txq_stats txq_stats;
}; };
#define IEEE80211_MAX_CHAINS 4 #define IEEE80211_MAX_CHAINS 4
@ -2204,6 +2237,9 @@ enum cfg80211_connect_params_changed {
* @WIPHY_PARAM_RTS_THRESHOLD: wiphy->rts_threshold has changed * @WIPHY_PARAM_RTS_THRESHOLD: wiphy->rts_threshold has changed
* @WIPHY_PARAM_COVERAGE_CLASS: coverage class changed * @WIPHY_PARAM_COVERAGE_CLASS: coverage class changed
* @WIPHY_PARAM_DYN_ACK: dynack has been enabled * @WIPHY_PARAM_DYN_ACK: dynack has been enabled
* @WIPHY_PARAM_TXQ_LIMIT: TXQ packet limit has been changed
* @WIPHY_PARAM_TXQ_MEMORY_LIMIT: TXQ memory limit has been changed
* @WIPHY_PARAM_TXQ_QUANTUM: TXQ scheduler quantum
*/ */
enum wiphy_params_flags { enum wiphy_params_flags {
WIPHY_PARAM_RETRY_SHORT = 1 << 0, WIPHY_PARAM_RETRY_SHORT = 1 << 0,
@ -2212,6 +2248,9 @@ enum wiphy_params_flags {
WIPHY_PARAM_RTS_THRESHOLD = 1 << 3, WIPHY_PARAM_RTS_THRESHOLD = 1 << 3,
WIPHY_PARAM_COVERAGE_CLASS = 1 << 4, WIPHY_PARAM_COVERAGE_CLASS = 1 << 4,
WIPHY_PARAM_DYN_ACK = 1 << 5, WIPHY_PARAM_DYN_ACK = 1 << 5,
WIPHY_PARAM_TXQ_LIMIT = 1 << 6,
WIPHY_PARAM_TXQ_MEMORY_LIMIT = 1 << 7,
WIPHY_PARAM_TXQ_QUANTUM = 1 << 8,
}; };
/** /**
@ -2964,6 +3003,9 @@ struct cfg80211_external_auth_params {
* *
* @set_multicast_to_unicast: configure multicast to unicast conversion for BSS * @set_multicast_to_unicast: configure multicast to unicast conversion for BSS
* *
* @get_txq_stats: Get TXQ stats for interface or phy. If wdev is %NULL, this
* function should return phy stats, and interface stats otherwise.
*
* @set_pmk: configure the PMK to be used for offloaded 802.1X 4-Way handshake. * @set_pmk: configure the PMK to be used for offloaded 802.1X 4-Way handshake.
* If not deleted through @del_pmk the PMK remains valid until disconnect * If not deleted through @del_pmk the PMK remains valid until disconnect
* upon which the driver should clear it. * upon which the driver should clear it.
@ -3265,6 +3307,10 @@ struct cfg80211_ops {
struct net_device *dev, struct net_device *dev,
const bool enabled); const bool enabled);
int (*get_txq_stats)(struct wiphy *wiphy,
struct wireless_dev *wdev,
struct cfg80211_txq_stats *txqstats);
int (*set_pmk)(struct wiphy *wiphy, struct net_device *dev, int (*set_pmk)(struct wiphy *wiphy, struct net_device *dev,
const struct cfg80211_pmk_conf *conf); const struct cfg80211_pmk_conf *conf);
int (*del_pmk)(struct wiphy *wiphy, struct net_device *dev, int (*del_pmk)(struct wiphy *wiphy, struct net_device *dev,
@ -3943,6 +3989,10 @@ struct wiphy {
u8 nan_supported_bands; u8 nan_supported_bands;
u32 txq_limit;
u32 txq_memory_limit;
u32 txq_quantum;
char priv[0] __aligned(NETDEV_ALIGN); char priv[0] __aligned(NETDEV_ALIGN);
}; };

View file

@ -2226,6 +2226,16 @@ enum nl80211_commands {
* @NL80211_ATTR_NSS: Station's New/updated RX_NSS value notified using this * @NL80211_ATTR_NSS: Station's New/updated RX_NSS value notified using this
* u8 attribute. This is used with %NL80211_CMD_STA_OPMODE_CHANGED. * u8 attribute. This is used with %NL80211_CMD_STA_OPMODE_CHANGED.
* *
* @NL80211_ATTR_TXQ_STATS: TXQ statistics (nested attribute, see &enum
* nl80211_txq_stats)
* @NL80211_ATTR_TXQ_LIMIT: Total packet limit for the TXQ queues for this phy.
* The smaller of this and the memory limit is enforced.
* @NL80211_ATTR_TXQ_MEMORY_LIMIT: Total memory memory limit (in bytes) for the
* TXQ queues for this phy. The smaller of this and the packet limit is
* enforced.
* @NL80211_ATTR_TXQ_QUANTUM: TXQ scheduler quantum (bytes). Number of bytes
* a flow is assigned on each round of the DRR scheduler.
*
* @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined * @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use * @__NL80211_ATTR_AFTER_LAST: internal use
@ -2660,6 +2670,11 @@ enum nl80211_attrs {
NL80211_ATTR_CONTROL_PORT_OVER_NL80211, NL80211_ATTR_CONTROL_PORT_OVER_NL80211,
NL80211_ATTR_TXQ_STATS,
NL80211_ATTR_TXQ_LIMIT,
NL80211_ATTR_TXQ_MEMORY_LIMIT,
NL80211_ATTR_TXQ_QUANTUM,
/* add attributes here, update the policy in nl80211.c */ /* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST, __NL80211_ATTR_AFTER_LAST,
@ -3040,6 +3055,7 @@ enum nl80211_sta_info {
* @NL80211_TID_STATS_TX_MSDU_FAILED: number of failed transmitted * @NL80211_TID_STATS_TX_MSDU_FAILED: number of failed transmitted
* MSDUs (u64) * MSDUs (u64)
* @NL80211_TID_STATS_PAD: attribute used for padding for 64-bit alignment * @NL80211_TID_STATS_PAD: attribute used for padding for 64-bit alignment
* @NL80211_TID_STATS_TXQ_STATS: TXQ stats (nested attribute)
* @NUM_NL80211_TID_STATS: number of attributes here * @NUM_NL80211_TID_STATS: number of attributes here
* @NL80211_TID_STATS_MAX: highest numbered attribute here * @NL80211_TID_STATS_MAX: highest numbered attribute here
*/ */
@ -3050,12 +3066,51 @@ enum nl80211_tid_stats {
NL80211_TID_STATS_TX_MSDU_RETRIES, NL80211_TID_STATS_TX_MSDU_RETRIES,
NL80211_TID_STATS_TX_MSDU_FAILED, NL80211_TID_STATS_TX_MSDU_FAILED,
NL80211_TID_STATS_PAD, NL80211_TID_STATS_PAD,
NL80211_TID_STATS_TXQ_STATS,
/* keep last */ /* keep last */
NUM_NL80211_TID_STATS, NUM_NL80211_TID_STATS,
NL80211_TID_STATS_MAX = NUM_NL80211_TID_STATS - 1 NL80211_TID_STATS_MAX = NUM_NL80211_TID_STATS - 1
}; };
/**
* enum nl80211_txq_stats - per TXQ statistics attributes
* @__NL80211_TXQ_STATS_INVALID: attribute number 0 is reserved
* @NUM_NL80211_TXQ_STATS: number of attributes here
* @NL80211_TXQ_STATS_BACKLOG_BYTES: number of bytes currently backlogged
* @NL80211_TXQ_STATS_BACKLOG_PACKETS: number of packets currently
* backlogged
* @NL80211_TXQ_STATS_FLOWS: total number of new flows seen
* @NL80211_TXQ_STATS_DROPS: total number of packet drops
* @NL80211_TXQ_STATS_ECN_MARKS: total number of packet ECN marks
* @NL80211_TXQ_STATS_OVERLIMIT: number of drops due to queue space overflow
* @NL80211_TXQ_STATS_OVERMEMORY: number of drops due to memory limit overflow
* (only for per-phy stats)
* @NL80211_TXQ_STATS_COLLISIONS: number of hash collisions
* @NL80211_TXQ_STATS_TX_BYTES: total number of bytes dequeued from TXQ
* @NL80211_TXQ_STATS_TX_PACKETS: total number of packets dequeued from TXQ
* @NL80211_TXQ_STATS_MAX_FLOWS: number of flow buckets for PHY
* @NL80211_TXQ_STATS_MAX: highest numbered attribute here
*/
enum nl80211_txq_stats {
__NL80211_TXQ_STATS_INVALID,
NL80211_TXQ_STATS_BACKLOG_BYTES,
NL80211_TXQ_STATS_BACKLOG_PACKETS,
NL80211_TXQ_STATS_FLOWS,
NL80211_TXQ_STATS_DROPS,
NL80211_TXQ_STATS_ECN_MARKS,
NL80211_TXQ_STATS_OVERLIMIT,
NL80211_TXQ_STATS_OVERMEMORY,
NL80211_TXQ_STATS_COLLISIONS,
NL80211_TXQ_STATS_TX_BYTES,
NL80211_TXQ_STATS_TX_PACKETS,
NL80211_TXQ_STATS_MAX_FLOWS,
/* keep last */
NUM_NL80211_TXQ_STATS,
NL80211_TXQ_STATS_MAX = NUM_NL80211_TXQ_STATS - 1
};
/** /**
* enum nl80211_mpath_flags - nl80211 mesh path flags * enum nl80211_mpath_flags - nl80211 mesh path flags
* *
@ -5072,6 +5127,8 @@ enum nl80211_feature_flags {
* @NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT: This Driver support data ack * @NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT: This Driver support data ack
* rssi if firmware support, this flag is to intimate about ack rssi * rssi if firmware support, this flag is to intimate about ack rssi
* support to nl80211. * support to nl80211.
* @NL80211_EXT_FEATURE_TXQS: Driver supports FQ-CoDel-enabled intermediate
* TXQs.
* *
* @NUM_NL80211_EXT_FEATURES: number of extended features. * @NUM_NL80211_EXT_FEATURES: number of extended features.
* @MAX_NL80211_EXT_FEATURES: highest extended feature index. * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
@ -5105,6 +5162,7 @@ enum nl80211_ext_feature_index {
NL80211_EXT_FEATURE_DFS_OFFLOAD, NL80211_EXT_FEATURE_DFS_OFFLOAD,
NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211, NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211,
NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT, NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT,
NL80211_EXT_FEATURE_TXQS,
/* add new features before the definition below */ /* add new features before the definition below */
NUM_NL80211_EXT_FEATURES, NUM_NL80211_EXT_FEATURES,

View file

@ -71,11 +71,15 @@ static void ieee80211_get_stats(struct net_device *dev,
struct ieee80211_channel *channel; struct ieee80211_channel *channel;
struct sta_info *sta; struct sta_info *sta;
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct station_info sinfo; struct station_info *sinfo;
struct survey_info survey; struct survey_info survey;
int i, q; int i, q;
#define STA_STATS_SURVEY_LEN 7 #define STA_STATS_SURVEY_LEN 7
sinfo = kmalloc(sizeof(*sinfo), GFP_KERNEL);
if (!sinfo)
return;
memset(data, 0, sizeof(u64) * STA_STATS_LEN); memset(data, 0, sizeof(u64) * STA_STATS_LEN);
#define ADD_STA_STATS(sta) \ #define ADD_STA_STATS(sta) \
@ -86,8 +90,8 @@ static void ieee80211_get_stats(struct net_device *dev,
data[i++] += sta->rx_stats.fragments; \ data[i++] += sta->rx_stats.fragments; \
data[i++] += sta->rx_stats.dropped; \ data[i++] += sta->rx_stats.dropped; \
\ \
data[i++] += sinfo.tx_packets; \ data[i++] += sinfo->tx_packets; \
data[i++] += sinfo.tx_bytes; \ data[i++] += sinfo->tx_bytes; \
data[i++] += sta->status_stats.filtered; \ data[i++] += sta->status_stats.filtered; \
data[i++] += sta->status_stats.retry_failed; \ data[i++] += sta->status_stats.retry_failed; \
data[i++] += sta->status_stats.retry_count; \ data[i++] += sta->status_stats.retry_count; \
@ -107,8 +111,8 @@ static void ieee80211_get_stats(struct net_device *dev,
if (!(sta && !WARN_ON(sta->sdata->dev != dev))) if (!(sta && !WARN_ON(sta->sdata->dev != dev)))
goto do_survey; goto do_survey;
memset(&sinfo, 0, sizeof(sinfo)); memset(sinfo, 0, sizeof(*sinfo));
sta_set_sinfo(sta, &sinfo); sta_set_sinfo(sta, sinfo);
i = 0; i = 0;
ADD_STA_STATS(sta); ADD_STA_STATS(sta);
@ -116,17 +120,17 @@ static void ieee80211_get_stats(struct net_device *dev,
data[i++] = sta->sta_state; data[i++] = sta->sta_state;
if (sinfo.filled & BIT(NL80211_STA_INFO_TX_BITRATE)) if (sinfo->filled & BIT(NL80211_STA_INFO_TX_BITRATE))
data[i] = 100000 * data[i] = 100000 *
cfg80211_calculate_bitrate(&sinfo.txrate); cfg80211_calculate_bitrate(&sinfo->txrate);
i++; i++;
if (sinfo.filled & BIT(NL80211_STA_INFO_RX_BITRATE)) if (sinfo->filled & BIT(NL80211_STA_INFO_RX_BITRATE))
data[i] = 100000 * data[i] = 100000 *
cfg80211_calculate_bitrate(&sinfo.rxrate); cfg80211_calculate_bitrate(&sinfo->rxrate);
i++; i++;
if (sinfo.filled & BIT(NL80211_STA_INFO_SIGNAL_AVG)) if (sinfo->filled & BIT(NL80211_STA_INFO_SIGNAL_AVG))
data[i] = (u8)sinfo.signal_avg; data[i] = (u8)sinfo->signal_avg;
i++; i++;
} else { } else {
list_for_each_entry(sta, &local->sta_list, list) { list_for_each_entry(sta, &local->sta_list, list) {
@ -134,14 +138,16 @@ static void ieee80211_get_stats(struct net_device *dev,
if (sta->sdata->dev != dev) if (sta->sdata->dev != dev)
continue; continue;
memset(&sinfo, 0, sizeof(sinfo)); memset(sinfo, 0, sizeof(*sinfo));
sta_set_sinfo(sta, &sinfo); sta_set_sinfo(sta, sinfo);
i = 0; i = 0;
ADD_STA_STATS(sta); ADD_STA_STATS(sta);
} }
} }
do_survey: do_survey:
kfree(sinfo);
i = STA_STATS_LEN - STA_STATS_SURVEY_LEN; i = STA_STATS_LEN - STA_STATS_SURVEY_LEN;
/* Get survey stats for current channel */ /* Get survey stats for current channel */
survey.filled = 0; survey.filled = 0;

View file

@ -424,6 +424,10 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_PMK] = { .type = NLA_BINARY, .len = PMK_MAX_LEN }, [NL80211_ATTR_PMK] = { .type = NLA_BINARY, .len = PMK_MAX_LEN },
[NL80211_ATTR_SCHED_SCAN_MULTI] = { .type = NLA_FLAG }, [NL80211_ATTR_SCHED_SCAN_MULTI] = { .type = NLA_FLAG },
[NL80211_ATTR_EXTERNAL_AUTH_SUPPORT] = { .type = NLA_FLAG }, [NL80211_ATTR_EXTERNAL_AUTH_SUPPORT] = { .type = NLA_FLAG },
[NL80211_ATTR_TXQ_LIMIT] = { .type = NLA_U32 },
[NL80211_ATTR_TXQ_MEMORY_LIMIT] = { .type = NLA_U32 },
[NL80211_ATTR_TXQ_QUANTUM] = { .type = NLA_U32 },
}; };
/* policy for the key attributes */ /* policy for the key attributes */
@ -774,6 +778,39 @@ static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy,
return -ENOBUFS; return -ENOBUFS;
} }
static bool nl80211_put_txq_stats(struct sk_buff *msg,
struct cfg80211_txq_stats *txqstats,
int attrtype)
{
struct nlattr *txqattr;
#define PUT_TXQVAL_U32(attr, memb) do { \
if (txqstats->filled & BIT(NL80211_TXQ_STATS_ ## attr) && \
nla_put_u32(msg, NL80211_TXQ_STATS_ ## attr, txqstats->memb)) \
return false; \
} while (0)
txqattr = nla_nest_start(msg, attrtype);
if (!txqattr)
return false;
PUT_TXQVAL_U32(BACKLOG_BYTES, backlog_bytes);
PUT_TXQVAL_U32(BACKLOG_PACKETS, backlog_packets);
PUT_TXQVAL_U32(FLOWS, flows);
PUT_TXQVAL_U32(DROPS, drops);
PUT_TXQVAL_U32(ECN_MARKS, ecn_marks);
PUT_TXQVAL_U32(OVERLIMIT, overlimit);
PUT_TXQVAL_U32(OVERMEMORY, overmemory);
PUT_TXQVAL_U32(COLLISIONS, collisions);
PUT_TXQVAL_U32(TX_BYTES, tx_bytes);
PUT_TXQVAL_U32(TX_PACKETS, tx_packets);
PUT_TXQVAL_U32(MAX_FLOWS, max_flows);
nla_nest_end(msg, txqattr);
#undef PUT_TXQVAL_U32
return true;
}
/* netlink command implementations */ /* netlink command implementations */
struct key_parse { struct key_parse {
@ -1973,6 +2010,28 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
rdev->wiphy.nan_supported_bands)) rdev->wiphy.nan_supported_bands))
goto nla_put_failure; goto nla_put_failure;
if (wiphy_ext_feature_isset(&rdev->wiphy,
NL80211_EXT_FEATURE_TXQS)) {
struct cfg80211_txq_stats txqstats = {};
int res;
res = rdev_get_txq_stats(rdev, NULL, &txqstats);
if (!res &&
!nl80211_put_txq_stats(msg, &txqstats,
NL80211_ATTR_TXQ_STATS))
goto nla_put_failure;
if (nla_put_u32(msg, NL80211_ATTR_TXQ_LIMIT,
rdev->wiphy.txq_limit))
goto nla_put_failure;
if (nla_put_u32(msg, NL80211_ATTR_TXQ_MEMORY_LIMIT,
rdev->wiphy.txq_memory_limit))
goto nla_put_failure;
if (nla_put_u32(msg, NL80211_ATTR_TXQ_QUANTUM,
rdev->wiphy.txq_quantum))
goto nla_put_failure;
}
/* done */ /* done */
state->split_start = 0; state->split_start = 0;
break; break;
@ -2350,6 +2409,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
u8 retry_short = 0, retry_long = 0; u8 retry_short = 0, retry_long = 0;
u32 frag_threshold = 0, rts_threshold = 0; u32 frag_threshold = 0, rts_threshold = 0;
u8 coverage_class = 0; u8 coverage_class = 0;
u32 txq_limit = 0, txq_memory_limit = 0, txq_quantum = 0;
ASSERT_RTNL(); ASSERT_RTNL();
@ -2556,10 +2616,38 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
changed |= WIPHY_PARAM_DYN_ACK; changed |= WIPHY_PARAM_DYN_ACK;
} }
if (info->attrs[NL80211_ATTR_TXQ_LIMIT]) {
if (!wiphy_ext_feature_isset(&rdev->wiphy,
NL80211_EXT_FEATURE_TXQS))
return -EOPNOTSUPP;
txq_limit = nla_get_u32(
info->attrs[NL80211_ATTR_TXQ_LIMIT]);
changed |= WIPHY_PARAM_TXQ_LIMIT;
}
if (info->attrs[NL80211_ATTR_TXQ_MEMORY_LIMIT]) {
if (!wiphy_ext_feature_isset(&rdev->wiphy,
NL80211_EXT_FEATURE_TXQS))
return -EOPNOTSUPP;
txq_memory_limit = nla_get_u32(
info->attrs[NL80211_ATTR_TXQ_MEMORY_LIMIT]);
changed |= WIPHY_PARAM_TXQ_MEMORY_LIMIT;
}
if (info->attrs[NL80211_ATTR_TXQ_QUANTUM]) {
if (!wiphy_ext_feature_isset(&rdev->wiphy,
NL80211_EXT_FEATURE_TXQS))
return -EOPNOTSUPP;
txq_quantum = nla_get_u32(
info->attrs[NL80211_ATTR_TXQ_QUANTUM]);
changed |= WIPHY_PARAM_TXQ_QUANTUM;
}
if (changed) { if (changed) {
u8 old_retry_short, old_retry_long; u8 old_retry_short, old_retry_long;
u32 old_frag_threshold, old_rts_threshold; u32 old_frag_threshold, old_rts_threshold;
u8 old_coverage_class; u8 old_coverage_class;
u32 old_txq_limit, old_txq_memory_limit, old_txq_quantum;
if (!rdev->ops->set_wiphy_params) if (!rdev->ops->set_wiphy_params)
return -EOPNOTSUPP; return -EOPNOTSUPP;
@ -2569,6 +2657,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
old_frag_threshold = rdev->wiphy.frag_threshold; old_frag_threshold = rdev->wiphy.frag_threshold;
old_rts_threshold = rdev->wiphy.rts_threshold; old_rts_threshold = rdev->wiphy.rts_threshold;
old_coverage_class = rdev->wiphy.coverage_class; old_coverage_class = rdev->wiphy.coverage_class;
old_txq_limit = rdev->wiphy.txq_limit;
old_txq_memory_limit = rdev->wiphy.txq_memory_limit;
old_txq_quantum = rdev->wiphy.txq_quantum;
if (changed & WIPHY_PARAM_RETRY_SHORT) if (changed & WIPHY_PARAM_RETRY_SHORT)
rdev->wiphy.retry_short = retry_short; rdev->wiphy.retry_short = retry_short;
@ -2580,6 +2671,12 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
rdev->wiphy.rts_threshold = rts_threshold; rdev->wiphy.rts_threshold = rts_threshold;
if (changed & WIPHY_PARAM_COVERAGE_CLASS) if (changed & WIPHY_PARAM_COVERAGE_CLASS)
rdev->wiphy.coverage_class = coverage_class; rdev->wiphy.coverage_class = coverage_class;
if (changed & WIPHY_PARAM_TXQ_LIMIT)
rdev->wiphy.txq_limit = txq_limit;
if (changed & WIPHY_PARAM_TXQ_MEMORY_LIMIT)
rdev->wiphy.txq_memory_limit = txq_memory_limit;
if (changed & WIPHY_PARAM_TXQ_QUANTUM)
rdev->wiphy.txq_quantum = txq_quantum;
result = rdev_set_wiphy_params(rdev, changed); result = rdev_set_wiphy_params(rdev, changed);
if (result) { if (result) {
@ -2588,6 +2685,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
rdev->wiphy.frag_threshold = old_frag_threshold; rdev->wiphy.frag_threshold = old_frag_threshold;
rdev->wiphy.rts_threshold = old_rts_threshold; rdev->wiphy.rts_threshold = old_rts_threshold;
rdev->wiphy.coverage_class = old_coverage_class; rdev->wiphy.coverage_class = old_coverage_class;
rdev->wiphy.txq_limit = old_txq_limit;
rdev->wiphy.txq_memory_limit = old_txq_memory_limit;
rdev->wiphy.txq_quantum = old_txq_quantum;
return result; return result;
} }
} }
@ -2709,6 +2809,16 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag
} }
wdev_unlock(wdev); wdev_unlock(wdev);
if (rdev->ops->get_txq_stats) {
struct cfg80211_txq_stats txqstats = {};
int ret = rdev_get_txq_stats(rdev, wdev, &txqstats);
if (ret == 0 &&
!nl80211_put_txq_stats(msg, &txqstats,
NL80211_ATTR_TXQ_STATS))
goto nla_put_failure;
}
genlmsg_end(msg, hdr); genlmsg_end(msg, hdr);
return 0; return 0;
@ -4582,6 +4692,12 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
PUT_TIDVAL_U64(TX_MSDU_FAILED, tx_msdu_failed); PUT_TIDVAL_U64(TX_MSDU_FAILED, tx_msdu_failed);
#undef PUT_TIDVAL_U64 #undef PUT_TIDVAL_U64
if ((tidstats->filled &
BIT(NL80211_TID_STATS_TXQ_STATS)) &&
!nl80211_put_txq_stats(msg, &tidstats->txq_stats,
NL80211_TID_STATS_TXQ_STATS))
goto nla_put_failure;
nla_nest_end(msg, tidattr); nla_nest_end(msg, tidattr);
} }
@ -4606,13 +4722,17 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
static int nl80211_dump_station(struct sk_buff *skb, static int nl80211_dump_station(struct sk_buff *skb,
struct netlink_callback *cb) struct netlink_callback *cb)
{ {
struct station_info sinfo; struct station_info *sinfo;
struct cfg80211_registered_device *rdev; struct cfg80211_registered_device *rdev;
struct wireless_dev *wdev; struct wireless_dev *wdev;
u8 mac_addr[ETH_ALEN]; u8 mac_addr[ETH_ALEN];
int sta_idx = cb->args[2]; int sta_idx = cb->args[2];
int err; int err;
sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
if (!sinfo)
return -ENOMEM;
rtnl_lock(); rtnl_lock();
err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev); err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
if (err) if (err)
@ -4629,9 +4749,9 @@ static int nl80211_dump_station(struct sk_buff *skb,
} }
while (1) { while (1) {
memset(&sinfo, 0, sizeof(sinfo)); memset(sinfo, 0, sizeof(*sinfo));
err = rdev_dump_station(rdev, wdev->netdev, sta_idx, err = rdev_dump_station(rdev, wdev->netdev, sta_idx,
mac_addr, &sinfo); mac_addr, sinfo);
if (err == -ENOENT) if (err == -ENOENT)
break; break;
if (err) if (err)
@ -4641,7 +4761,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
NETLINK_CB(cb->skb).portid, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI, cb->nlh->nlmsg_seq, NLM_F_MULTI,
rdev, wdev->netdev, mac_addr, rdev, wdev->netdev, mac_addr,
&sinfo) < 0) sinfo) < 0)
goto out; goto out;
sta_idx++; sta_idx++;
@ -4652,6 +4772,7 @@ static int nl80211_dump_station(struct sk_buff *skb,
err = skb->len; err = skb->len;
out_err: out_err:
rtnl_unlock(); rtnl_unlock();
kfree(sinfo);
return err; return err;
} }
@ -4660,37 +4781,49 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
{ {
struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1]; struct net_device *dev = info->user_ptr[1];
struct station_info sinfo; struct station_info *sinfo;
struct sk_buff *msg; struct sk_buff *msg;
u8 *mac_addr = NULL; u8 *mac_addr = NULL;
int err; int err;
memset(&sinfo, 0, sizeof(sinfo)); sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
if (!sinfo)
return -ENOMEM;
if (!info->attrs[NL80211_ATTR_MAC]) if (!info->attrs[NL80211_ATTR_MAC]) {
return -EINVAL; err = -EINVAL;
goto out;
}
mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
if (!rdev->ops->get_station) if (!rdev->ops->get_station) {
return -EOPNOTSUPP; err = -EOPNOTSUPP;
goto out;
}
err = rdev_get_station(rdev, dev, mac_addr, &sinfo); err = rdev_get_station(rdev, dev, mac_addr, sinfo);
if (err) if (err)
return err; goto out;
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg) if (!msg) {
return -ENOMEM; err = -ENOMEM;
goto out;
}
if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION, if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION,
info->snd_portid, info->snd_seq, 0, info->snd_portid, info->snd_seq, 0,
rdev, dev, mac_addr, &sinfo) < 0) { rdev, dev, mac_addr, sinfo) < 0) {
nlmsg_free(msg); nlmsg_free(msg);
return -ENOBUFS; err = -ENOBUFS;
goto out;
} }
return genlmsg_reply(msg, info); err = genlmsg_reply(msg, info);
out:
kfree(sinfo);
return err;
} }
int cfg80211_check_station_change(struct wiphy *wiphy, int cfg80211_check_station_change(struct wiphy *wiphy,
@ -9954,18 +10087,26 @@ static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev,
*/ */
if (!wdev->cqm_config->last_rssi_event_value && wdev->current_bss && if (!wdev->cqm_config->last_rssi_event_value && wdev->current_bss &&
rdev->ops->get_station) { rdev->ops->get_station) {
struct station_info sinfo = {}; struct station_info *sinfo;
u8 *mac_addr; u8 *mac_addr;
sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
if (!sinfo)
return -ENOMEM;
mac_addr = wdev->current_bss->pub.bssid; mac_addr = wdev->current_bss->pub.bssid;
err = rdev_get_station(rdev, dev, mac_addr, &sinfo); err = rdev_get_station(rdev, dev, mac_addr, sinfo);
if (err) if (err) {
kfree(sinfo);
return err; return err;
}
if (sinfo.filled & BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG)) if (sinfo->filled & BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG))
wdev->cqm_config->last_rssi_event_value = wdev->cqm_config->last_rssi_event_value =
(s8) sinfo.rx_beacon_signal_avg; (s8)sinfo->rx_beacon_signal_avg;
kfree(sinfo);
} }
last = wdev->cqm_config->last_rssi_event_value; 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,
struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; struct wiphy *wiphy = dev->ieee80211_ptr->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
struct sk_buff *msg; struct sk_buff *msg;
struct station_info empty_sinfo = {}; struct station_info *empty_sinfo = NULL;
if (!sinfo) if (!sinfo) {
sinfo = &empty_sinfo; empty_sinfo = kzalloc(sizeof(*empty_sinfo), GFP_KERNEL);
if (!empty_sinfo)
return;
sinfo = empty_sinfo;
}
trace_cfg80211_del_sta(dev, mac_addr); trace_cfg80211_del_sta(dev, mac_addr);
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
if (!msg) if (!msg)
return; goto out;
if (nl80211_send_station(msg, NL80211_CMD_DEL_STATION, 0, 0, 0, if (nl80211_send_station(msg, NL80211_CMD_DEL_STATION, 0, 0, 0,
rdev, dev, mac_addr, sinfo) < 0) { rdev, dev, mac_addr, sinfo) < 0) {
nlmsg_free(msg); nlmsg_free(msg);
return; goto out;
} }
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0, genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
NL80211_MCGRP_MLME, gfp); NL80211_MCGRP_MLME, gfp);
out:
kfree(empty_sinfo);
} }
EXPORT_SYMBOL(cfg80211_del_sta_sinfo); EXPORT_SYMBOL(cfg80211_del_sta_sinfo);

View file

@ -586,6 +586,18 @@ rdev_set_multicast_to_unicast(struct cfg80211_registered_device *rdev,
return ret; return ret;
} }
static inline int
rdev_get_txq_stats(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
struct cfg80211_txq_stats *txqstats)
{
int ret;
trace_rdev_get_txq_stats(&rdev->wiphy, wdev);
ret = rdev->ops->get_txq_stats(&rdev->wiphy, wdev, txqstats);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
static inline void rdev_rfkill_poll(struct cfg80211_registered_device *rdev) static inline void rdev_rfkill_poll(struct cfg80211_registered_device *rdev)
{ {
trace_rdev_rfkill_poll(&rdev->wiphy); trace_rdev_rfkill_poll(&rdev->wiphy);

View file

@ -3243,6 +3243,20 @@ TRACE_EVENT(rdev_set_multicast_to_unicast,
WIPHY_PR_ARG, NETDEV_PR_ARG, WIPHY_PR_ARG, NETDEV_PR_ARG,
BOOL_TO_STR(__entry->enabled)) BOOL_TO_STR(__entry->enabled))
); );
TRACE_EVENT(rdev_get_txq_stats,
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev),
TP_ARGS(wiphy, wdev),
TP_STRUCT__entry(
WIPHY_ENTRY
WDEV_ENTRY
),
TP_fast_assign(
WIPHY_ASSIGN;
WDEV_ASSIGN;
),
TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG)
);
#endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */ #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
#undef TRACE_INCLUDE_PATH #undef TRACE_INCLUDE_PATH

View file

@ -1254,7 +1254,7 @@ static int cfg80211_wext_giwrate(struct net_device *dev,
{ {
struct wireless_dev *wdev = dev->ieee80211_ptr; struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
struct station_info sinfo = {}; struct station_info *sinfo;
u8 addr[ETH_ALEN]; u8 addr[ETH_ALEN];
int err; int err;
@ -1274,16 +1274,23 @@ static int cfg80211_wext_giwrate(struct net_device *dev,
if (err) if (err)
return err; return err;
err = rdev_get_station(rdev, dev, addr, &sinfo); sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
if (!sinfo)
return -ENOMEM;
err = rdev_get_station(rdev, dev, addr, sinfo);
if (err) if (err)
return err; goto out;
if (!(sinfo.filled & BIT(NL80211_STA_INFO_TX_BITRATE))) if (!(sinfo->filled & BIT(NL80211_STA_INFO_TX_BITRATE))) {
return -EOPNOTSUPP; err = -EOPNOTSUPP;
goto out;
}
rate->value = 100000 * cfg80211_calculate_bitrate(&sinfo.txrate); rate->value = 100000 * cfg80211_calculate_bitrate(&sinfo->txrate);
out:
return 0; kfree(sinfo);
return err;
} }
/* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */ /* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */