mac80211: export the expected throughput
authorAntonio Quartulli <antonio@open-mesh.com>
Mon, 19 May 2014 19:53:20 +0000 (21:53 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Wed, 21 May 2014 07:15:16 +0000 (09:15 +0200)
Add get_expected_throughput() API to mac80211 so that each
driver can implement its own version based on the RC
algorithm they are using (might be using an HW RC algo).
The API returns a value expressed in Kbps.

Also, add the new get_expected_throughput() member
to the rate_control_ops structure in order to be
able to query the RC algorithm (this patch provides an
implementation of this API for both minstrel and
minstrel_ht).

The related member in the station_info object is now
filled accordingly when dumping a station.

Cc: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Antonio Quartulli <antonio@open-mesh.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
include/net/mac80211.h
net/mac80211/cfg.c
net/mac80211/driver-ops.h
net/mac80211/rc80211_minstrel.c
net/mac80211/rc80211_minstrel_ht.c
net/mac80211/trace.h

index a34f26a4ed1803e9f2afa9a499ec3f7430fac02e..2c78997bc48d33b9d63d9611f5003bcf2a256083 100644 (file)
@@ -2769,6 +2769,10 @@ enum ieee80211_roc_type {
  *     information in bss_conf is set up and the beacon can be retrieved. A
  *     channel context is bound before this is called.
  * @leave_ibss: Leave the IBSS again.
+ *
+ * @get_expected_throughput: extract the expected throughput towards the
+ *     specified station. The returned value is expressed in Kbps. It returns 0
+ *     if the RC algorithm does not have proper data to provide.
  */
 struct ieee80211_ops {
        void (*tx)(struct ieee80211_hw *hw,
@@ -2962,6 +2966,7 @@ struct ieee80211_ops {
 
        int (*join_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
        void (*leave_ibss)(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
+       u32 (*get_expected_throughput)(struct ieee80211_sta *sta);
 };
 
 /**
@@ -4535,6 +4540,8 @@ struct rate_control_ops {
        void (*add_sta_debugfs)(void *priv, void *priv_sta,
                                struct dentry *dir);
        void (*remove_sta_debugfs)(void *priv, void *priv_sta);
+
+       u32 (*get_expected_throughput)(void *priv_sta);
 };
 
 static inline int rate_supported(struct ieee80211_sta *sta,
index ac45304590d817a214e7d942973580ccb53d33fd..d7513a503be11b180031342dcf316450fd6c69d3 100644 (file)
@@ -472,8 +472,10 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
 {
        struct ieee80211_sub_if_data *sdata = sta->sdata;
        struct ieee80211_local *local = sdata->local;
+       struct rate_control_ref *ref = local->rate_ctrl;
        struct timespec uptime;
        u64 packets = 0;
+       u32 thr = 0;
        int i, ac;
 
        sinfo->generation = sdata->local->sta_generation;
@@ -587,6 +589,17 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
                sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
        if (test_sta_flag(sta, WLAN_STA_TDLS_PEER))
                sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
+
+       /* check if the driver has a SW RC implementation */
+       if (ref && ref->ops->get_expected_throughput)
+               thr = ref->ops->get_expected_throughput(sta->rate_ctrl_priv);
+       else
+               thr = drv_get_expected_throughput(local, &sta->sta);
+
+       if (thr != 0) {
+               sinfo->filled |= STATION_INFO_EXPECTED_THROUGHPUT;
+               sinfo->expected_throughput = thr;
+       }
 }
 
 static const char ieee80211_gstrings_sta_stats[][ETH_GSTRING_LEN] = {
index df1d50291344f6884e6a6e4fdba0a50e83805722..696ef78b1fb754fa37305a2c77b870d05cdbcbec 100644 (file)
@@ -1156,4 +1156,17 @@ static inline void drv_leave_ibss(struct ieee80211_local *local,
        trace_drv_return_void(local);
 }
 
+static inline u32 drv_get_expected_throughput(struct ieee80211_local *local,
+                                             struct ieee80211_sta *sta)
+{
+       u32 ret = 0;
+
+       trace_drv_get_expected_throughput(sta);
+       if (local->ops->get_expected_throughput)
+               ret = local->ops->get_expected_throughput(sta);
+       trace_drv_return_u32(local, ret);
+
+       return ret;
+}
+
 #endif /* __MAC80211_DRIVER_OPS */
index 26fd94fa0aedb86e43382781f791b1f36de44954..1c1469c36dca05cbdf55e2aba55dc296962aba55 100644 (file)
@@ -657,6 +657,17 @@ minstrel_free(void *priv)
        kfree(priv);
 }
 
+static u32 minstrel_get_expected_throughput(void *priv_sta)
+{
+       struct minstrel_sta_info *mi = priv_sta;
+       int idx = mi->max_tp_rate[0];
+
+       /* convert pkt per sec in kbps (1200 is the average pkt size used for
+        * computing cur_tp
+        */
+       return MINSTREL_TRUNC(mi->r[idx].cur_tp) * 1200 * 8 / 1024;
+}
+
 const struct rate_control_ops mac80211_minstrel = {
        .name = "minstrel",
        .tx_status = minstrel_tx_status,
@@ -670,6 +681,7 @@ const struct rate_control_ops mac80211_minstrel = {
        .add_sta_debugfs = minstrel_add_sta_debugfs,
        .remove_sta_debugfs = minstrel_remove_sta_debugfs,
 #endif
+       .get_expected_throughput = minstrel_get_expected_throughput,
 };
 
 int __init
index 4e916ad51a20d9ccddc3271d8e8ba04db0a7b82c..85c1e74b7714c2160ffd6976bbec4322bb7e428d 100644 (file)
@@ -1032,6 +1032,22 @@ minstrel_ht_free(void *priv)
        mac80211_minstrel.free(priv);
 }
 
+static u32 minstrel_ht_get_expected_throughput(void *priv_sta)
+{
+       struct minstrel_ht_sta_priv *msp = priv_sta;
+       struct minstrel_ht_sta *mi = &msp->ht;
+       int i, j;
+
+       if (!msp->is_ht)
+               return mac80211_minstrel.get_expected_throughput(priv_sta);
+
+       i = mi->max_tp_rate / MCS_GROUP_RATES;
+       j = mi->max_tp_rate % MCS_GROUP_RATES;
+
+       /* convert cur_tp from pkt per second in kbps */
+       return mi->groups[i].rates[j].cur_tp * AVG_PKT_SIZE * 8 / 1024;
+}
+
 static const struct rate_control_ops mac80211_minstrel_ht = {
        .name = "minstrel_ht",
        .tx_status = minstrel_ht_tx_status,
@@ -1046,6 +1062,7 @@ static const struct rate_control_ops mac80211_minstrel_ht = {
        .add_sta_debugfs = minstrel_ht_add_sta_debugfs,
        .remove_sta_debugfs = minstrel_ht_remove_sta_debugfs,
 #endif
+       .get_expected_throughput = minstrel_ht_get_expected_throughput,
 };
 
 
index a0b0aea76525c341711c129519a1c3f89a704bb9..942f64b8ce0ef270a89e58ee2d9c845959516ab1 100644 (file)
@@ -184,6 +184,20 @@ TRACE_EVENT(drv_return_bool,
                  "true" : "false")
 );
 
+TRACE_EVENT(drv_return_u32,
+       TP_PROTO(struct ieee80211_local *local, u32 ret),
+       TP_ARGS(local, ret),
+       TP_STRUCT__entry(
+               LOCAL_ENTRY
+               __field(u32, ret)
+       ),
+       TP_fast_assign(
+               LOCAL_ASSIGN;
+               __entry->ret = ret;
+       ),
+       TP_printk(LOCAL_PR_FMT " - %u", LOCAL_PR_ARG, __entry->ret)
+);
+
 TRACE_EVENT(drv_return_u64,
        TP_PROTO(struct ieee80211_local *local, u64 ret),
        TP_ARGS(local, ret),
@@ -1499,6 +1513,24 @@ DEFINE_EVENT(local_sdata_evt, drv_leave_ibss,
        TP_ARGS(local, sdata)
 );
 
+TRACE_EVENT(drv_get_expected_throughput,
+       TP_PROTO(struct ieee80211_sta *sta),
+
+       TP_ARGS(sta),
+
+       TP_STRUCT__entry(
+               STA_ENTRY
+       ),
+
+       TP_fast_assign(
+               STA_ASSIGN;
+       ),
+
+       TP_printk(
+               STA_PR_FMT, STA_PR_ARG
+       )
+);
+
 /*
  * Tracing for API calls that drivers call.
  */