Commit 2b9a7e1b authored by Johannes Berg's avatar Johannes Berg

mac80211: allow drivers to provide most station statistics

In many cases, drivers can filter things like beacons that will
skew statistics reported by mac80211. To get correct statistics
in these cases, call drivers to obtain statistics and let them
override all values, filling values from mac80211 if the driver
didn't provide them. Not all of them make sense for the driver
to fill, so some are still always done by mac80211.

Note that this doesn't currently allow a driver to say "I know
this value is wrong, don't report it at all", or to sum it up
with a mac80211 value (as could be useful for "dropped misc"),
that can be added if it turns out to be needed.

This also gets rid of the get_rssi() method as is can now be
implemented using sta_statistics().
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 6f7a8d26
...@@ -5376,14 +5376,15 @@ static void wlcore_op_sta_rc_update(struct ieee80211_hw *hw, ...@@ -5376,14 +5376,15 @@ static void wlcore_op_sta_rc_update(struct ieee80211_hw *hw,
wlcore_hw_sta_rc_update(wl, wlvif, sta, changed); wlcore_hw_sta_rc_update(wl, wlvif, sta, changed);
} }
static int wlcore_op_get_rssi(struct ieee80211_hw *hw, static void wlcore_op_sta_statistics(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, struct ieee80211_sta *sta,
s8 *rssi_dbm) struct station_info *sinfo)
{ {
struct wl1271 *wl = hw->priv; struct wl1271 *wl = hw->priv;
struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif);
int ret = 0; s8 rssi_dbm;
int ret;
wl1271_debug(DEBUG_MAC80211, "mac80211 get_rssi"); wl1271_debug(DEBUG_MAC80211, "mac80211 get_rssi");
...@@ -5396,17 +5397,18 @@ static int wlcore_op_get_rssi(struct ieee80211_hw *hw, ...@@ -5396,17 +5397,18 @@ static int wlcore_op_get_rssi(struct ieee80211_hw *hw,
if (ret < 0) if (ret < 0)
goto out_sleep; goto out_sleep;
ret = wlcore_acx_average_rssi(wl, wlvif, rssi_dbm); ret = wlcore_acx_average_rssi(wl, wlvif, &rssi_dbm);
if (ret < 0) if (ret < 0)
goto out_sleep; goto out_sleep;
sinfo->filled |= STATION_INFO_SIGNAL;
sinfo->signal = rssi_dbm;
out_sleep: out_sleep:
wl1271_ps_elp_sleep(wl); wl1271_ps_elp_sleep(wl);
out: out:
mutex_unlock(&wl->mutex); mutex_unlock(&wl->mutex);
return ret;
} }
static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw) static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw)
...@@ -5606,7 +5608,7 @@ static const struct ieee80211_ops wl1271_ops = { ...@@ -5606,7 +5608,7 @@ static const struct ieee80211_ops wl1271_ops = {
.assign_vif_chanctx = wlcore_op_assign_vif_chanctx, .assign_vif_chanctx = wlcore_op_assign_vif_chanctx,
.unassign_vif_chanctx = wlcore_op_unassign_vif_chanctx, .unassign_vif_chanctx = wlcore_op_unassign_vif_chanctx,
.sta_rc_update = wlcore_op_sta_rc_update, .sta_rc_update = wlcore_op_sta_rc_update,
.get_rssi = wlcore_op_get_rssi, .sta_statistics = wlcore_op_sta_statistics,
CFG80211_TESTMODE_CMD(wl1271_tm_cmd) CFG80211_TESTMODE_CMD(wl1271_tm_cmd)
}; };
......
...@@ -2708,6 +2708,14 @@ enum ieee80211_reconfig_type { ...@@ -2708,6 +2708,14 @@ enum ieee80211_reconfig_type {
* is only used if the configured rate control algorithm actually uses * is only used if the configured rate control algorithm actually uses
* the new rate table API, and is therefore optional. Must be atomic. * the new rate table API, and is therefore optional. Must be atomic.
* *
* @sta_statistics: Get statistics for this station. For example with beacon
* filtering, the statistics kept by mac80211 might not be accurate, so
* let the driver pre-fill the statistics. The driver can fill most of
* the values (indicating which by setting the filled bitmap), but not
* all of them make sense - see the source for which ones are possible.
* Statistics that the driver doesn't fill will be filled by mac80211.
* The callback can sleep.
*
* @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max), * @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
* bursting) for a hardware TX queue. * bursting) for a hardware TX queue.
* Returns a negative error code on failure. * Returns a negative error code on failure.
...@@ -2868,9 +2876,6 @@ enum ieee80211_reconfig_type { ...@@ -2868,9 +2876,6 @@ enum ieee80211_reconfig_type {
* @get_et_strings: Ethtool API to get a set of strings to describe stats * @get_et_strings: Ethtool API to get a set of strings to describe stats
* and perhaps other supported types of ethtool data-sets. * and perhaps other supported types of ethtool data-sets.
* *
* @get_rssi: Get current signal strength in dBm, the function is optional
* and can sleep.
*
* @mgd_prepare_tx: Prepare for transmitting a management frame for association * @mgd_prepare_tx: Prepare for transmitting a management frame for association
* before associated. In multi-channel scenarios, a virtual interface is * before associated. In multi-channel scenarios, a virtual interface is
* bound to a channel before it is associated, but as it isn't associated * bound to a channel before it is associated, but as it isn't associated
...@@ -3071,6 +3076,10 @@ struct ieee80211_ops { ...@@ -3071,6 +3076,10 @@ struct ieee80211_ops {
void (*sta_rate_tbl_update)(struct ieee80211_hw *hw, void (*sta_rate_tbl_update)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
struct ieee80211_sta *sta); struct ieee80211_sta *sta);
void (*sta_statistics)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct station_info *sinfo);
int (*conf_tx)(struct ieee80211_hw *hw, int (*conf_tx)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, u16 ac, struct ieee80211_vif *vif, u16 ac,
const struct ieee80211_tx_queue_params *params); const struct ieee80211_tx_queue_params *params);
...@@ -3138,8 +3147,6 @@ struct ieee80211_ops { ...@@ -3138,8 +3147,6 @@ struct ieee80211_ops {
void (*get_et_strings)(struct ieee80211_hw *hw, void (*get_et_strings)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
u32 sset, u8 *data); u32 sset, u8 *data);
int (*get_rssi)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, s8 *rssi_dbm);
void (*mgd_prepare_tx)(struct ieee80211_hw *hw, void (*mgd_prepare_tx)(struct ieee80211_hw *hw,
struct ieee80211_vif *vif); struct ieee80211_vif *vif);
......
...@@ -639,6 +639,21 @@ static inline void drv_sta_rate_tbl_update(struct ieee80211_local *local, ...@@ -639,6 +639,21 @@ static inline void drv_sta_rate_tbl_update(struct ieee80211_local *local,
trace_drv_return_void(local); trace_drv_return_void(local);
} }
static inline void drv_sta_statistics(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta *sta,
struct station_info *sinfo)
{
sdata = get_bss_sdata(sdata);
if (!check_sdata_in_driver(sdata))
return;
trace_drv_sta_statistics(local, sdata, sta);
if (local->ops->sta_statistics)
local->ops->sta_statistics(&local->hw, &sdata->vif, sta, sinfo);
trace_drv_return_void(local);
}
static inline int drv_conf_tx(struct ieee80211_local *local, static inline int drv_conf_tx(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata, u16 ac, struct ieee80211_sub_if_data *sdata, u16 ac,
const struct ieee80211_tx_queue_params *params) const struct ieee80211_tx_queue_params *params)
...@@ -966,21 +981,6 @@ drv_allow_buffered_frames(struct ieee80211_local *local, ...@@ -966,21 +981,6 @@ drv_allow_buffered_frames(struct ieee80211_local *local,
trace_drv_return_void(local); trace_drv_return_void(local);
} }
static inline int drv_get_rssi(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta *sta,
s8 *rssi_dbm)
{
int ret;
might_sleep();
ret = local->ops->get_rssi(&local->hw, &sdata->vif, sta, rssi_dbm);
trace_drv_get_rssi(local, sta, *rssi_dbm, ret);
return ret;
}
static inline void drv_mgd_prepare_tx(struct ieee80211_local *local, static inline void drv_mgd_prepare_tx(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata) struct ieee80211_sub_if_data *sdata)
{ {
......
...@@ -1746,7 +1746,6 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) ...@@ -1746,7 +1746,6 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct rate_control_ref *ref = NULL; struct rate_control_ref *ref = NULL;
struct timespec uptime; struct timespec uptime;
u64 packets = 0;
u32 thr = 0; u32 thr = 0;
int i, ac; int i, ac;
...@@ -1755,47 +1754,74 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) ...@@ -1755,47 +1754,74 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
sinfo->generation = sdata->local->sta_generation; sinfo->generation = sdata->local->sta_generation;
sinfo->filled = STATION_INFO_INACTIVE_TIME | drv_sta_statistics(local, sdata, &sta->sta, sinfo);
STATION_INFO_RX_BYTES64 |
STATION_INFO_TX_BYTES64 | sinfo->filled |= STATION_INFO_INACTIVE_TIME |
STATION_INFO_RX_PACKETS | STATION_INFO_STA_FLAGS |
STATION_INFO_TX_PACKETS | STATION_INFO_BSS_PARAM |
STATION_INFO_TX_RETRIES | STATION_INFO_CONNECTED_TIME |
STATION_INFO_TX_FAILED | STATION_INFO_RX_DROP_MISC |
STATION_INFO_TX_BITRATE | STATION_INFO_BEACON_LOSS_COUNT;
STATION_INFO_RX_BITRATE |
STATION_INFO_RX_DROP_MISC |
STATION_INFO_BSS_PARAM |
STATION_INFO_CONNECTED_TIME |
STATION_INFO_STA_FLAGS |
STATION_INFO_BEACON_LOSS_COUNT;
ktime_get_ts(&uptime); ktime_get_ts(&uptime);
sinfo->connected_time = uptime.tv_sec - sta->last_connected; sinfo->connected_time = uptime.tv_sec - sta->last_connected;
sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
sinfo->tx_bytes = 0;
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { if (!(sinfo->filled & (STATION_INFO_TX_BYTES64 |
sinfo->tx_bytes += sta->tx_bytes[ac]; STATION_INFO_TX_BYTES))) {
packets += sta->tx_packets[ac]; sinfo->tx_bytes = 0;
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
sinfo->tx_bytes += sta->tx_bytes[ac];
sinfo->filled |= STATION_INFO_TX_BYTES64;
}
if (!(sinfo->filled & STATION_INFO_TX_PACKETS)) {
sinfo->tx_packets = 0;
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
sinfo->tx_packets += sta->tx_packets[ac];
sinfo->filled |= STATION_INFO_TX_PACKETS;
}
if (!(sinfo->filled & (STATION_INFO_RX_BYTES64 |
STATION_INFO_RX_BYTES))) {
sinfo->rx_bytes = sta->rx_bytes;
sinfo->filled |= STATION_INFO_RX_BYTES64;
}
if (!(sinfo->filled & STATION_INFO_RX_PACKETS)) {
sinfo->rx_packets = sta->rx_packets;
sinfo->filled |= STATION_INFO_RX_PACKETS;
}
if (!(sinfo->filled & STATION_INFO_TX_RETRIES)) {
sinfo->tx_retries = sta->tx_retry_count;
sinfo->filled |= STATION_INFO_TX_RETRIES;
}
if (!(sinfo->filled & STATION_INFO_TX_FAILED)) {
sinfo->tx_failed = sta->tx_retry_failed;
sinfo->filled |= STATION_INFO_TX_FAILED;
} }
sinfo->tx_packets = packets;
sinfo->rx_bytes = sta->rx_bytes;
sinfo->rx_packets = sta->rx_packets;
sinfo->tx_retries = sta->tx_retry_count;
sinfo->tx_failed = sta->tx_retry_failed;
sinfo->rx_dropped_misc = sta->rx_dropped; sinfo->rx_dropped_misc = sta->rx_dropped;
sinfo->beacon_loss_count = sta->beacon_loss_count; sinfo->beacon_loss_count = sta->beacon_loss_count;
if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) || if ((sta->local->hw.flags & IEEE80211_HW_SIGNAL_DBM) ||
(sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) { (sta->local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC)) {
sinfo->filled |= STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG; if (!(sinfo->filled & STATION_INFO_SIGNAL)) {
if (!local->ops->get_rssi ||
drv_get_rssi(local, sdata, &sta->sta, &sinfo->signal))
sinfo->signal = (s8)sta->last_signal; sinfo->signal = (s8)sta->last_signal;
sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal); sinfo->filled |= STATION_INFO_SIGNAL;
}
if (!(sinfo->filled & STATION_INFO_SIGNAL_AVG)) {
sinfo->signal_avg = (s8) -ewma_read(&sta->avg_signal);
sinfo->filled |= STATION_INFO_SIGNAL_AVG;
}
} }
if (sta->chains) {
if (sta->chains &&
!(sinfo->filled & (STATION_INFO_CHAIN_SIGNAL |
STATION_INFO_CHAIN_SIGNAL_AVG))) {
sinfo->filled |= STATION_INFO_CHAIN_SIGNAL | sinfo->filled |= STATION_INFO_CHAIN_SIGNAL |
STATION_INFO_CHAIN_SIGNAL_AVG; STATION_INFO_CHAIN_SIGNAL_AVG;
...@@ -1807,8 +1833,15 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) ...@@ -1807,8 +1833,15 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
} }
} }
sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate); if (!(sinfo->filled & STATION_INFO_TX_BITRATE)) {
sta_set_rate_info_rx(sta, &sinfo->rxrate); sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate);
sinfo->filled |= STATION_INFO_TX_BITRATE;
}
if (!(sinfo->filled & STATION_INFO_RX_BITRATE)) {
sta_set_rate_info_rx(sta, &sinfo->rxrate);
sinfo->filled |= STATION_INFO_RX_BITRATE;
}
if (ieee80211_vif_is_mesh(&sdata->vif)) { if (ieee80211_vif_is_mesh(&sdata->vif)) {
#ifdef CONFIG_MAC80211_MESH #ifdef CONFIG_MAC80211_MESH
......
...@@ -825,6 +825,13 @@ DECLARE_EVENT_CLASS(sta_event, ...@@ -825,6 +825,13 @@ DECLARE_EVENT_CLASS(sta_event,
) )
); );
DEFINE_EVENT(sta_event, drv_sta_statistics,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
struct ieee80211_sta *sta),
TP_ARGS(local, sdata, sta)
);
DEFINE_EVENT(sta_event, drv_sta_add, DEFINE_EVENT(sta_event, drv_sta_add,
TP_PROTO(struct ieee80211_local *local, TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata, struct ieee80211_sub_if_data *sdata,
...@@ -1329,32 +1336,6 @@ DEFINE_EVENT(release_evt, drv_allow_buffered_frames, ...@@ -1329,32 +1336,6 @@ DEFINE_EVENT(release_evt, drv_allow_buffered_frames,
TP_ARGS(local, sta, tids, num_frames, reason, more_data) TP_ARGS(local, sta, tids, num_frames, reason, more_data)
); );
TRACE_EVENT(drv_get_rssi,
TP_PROTO(struct ieee80211_local *local, struct ieee80211_sta *sta,
s8 rssi, int ret),
TP_ARGS(local, sta, rssi, ret),
TP_STRUCT__entry(
LOCAL_ENTRY
STA_ENTRY
__field(s8, rssi)
__field(int, ret)
),
TP_fast_assign(
LOCAL_ASSIGN;
STA_ASSIGN;
__entry->rssi = rssi;
__entry->ret = ret;
),
TP_printk(
LOCAL_PR_FMT STA_PR_FMT " rssi:%d ret:%d",
LOCAL_PR_ARG, STA_PR_ARG, __entry->rssi, __entry->ret
)
);
DEFINE_EVENT(local_sdata_evt, drv_mgd_prepare_tx, DEFINE_EVENT(local_sdata_evt, drv_mgd_prepare_tx,
TP_PROTO(struct ieee80211_local *local, TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata), struct ieee80211_sub_if_data *sdata),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment