mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	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:
		
							parent
							
								
									cc60dbbfed
								
							
						
					
					
						commit
						52539ca89f
					
				
					 7 changed files with 343 additions and 48 deletions
				
			
		| 
						 | 
					@ -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);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue