cfg80211: release station info tidstats where needed
[linux-block.git] / net / wireless / nl80211.c
index ff28f8feeb09270296bab5b086e928cc3a76bea0..e4a52a2b5e653ea99a511e817ee1c16b894ce9ff 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright 2015-2017 Intel Deutschland GmbH
+ * Copyright (C) 2018 Intel Corporation
  */
 
 #include <linux/if.h>
@@ -423,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_SCHED_SCAN_MULTI] = { .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 */
@@ -645,7 +650,43 @@ static inline void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
        return genlmsg_put(skb, portid, seq, &nl80211_fam, flags, cmd);
 }
 
-static int nl80211_msg_put_channel(struct sk_buff *msg,
+static int nl80211_msg_put_wmm_rules(struct sk_buff *msg,
+                                    const struct ieee80211_reg_rule *rule)
+{
+       int j;
+       struct nlattr *nl_wmm_rules =
+               nla_nest_start(msg, NL80211_FREQUENCY_ATTR_WMM);
+
+       if (!nl_wmm_rules)
+               goto nla_put_failure;
+
+       for (j = 0; j < IEEE80211_NUM_ACS; j++) {
+               struct nlattr *nl_wmm_rule = nla_nest_start(msg, j);
+
+               if (!nl_wmm_rule)
+                       goto nla_put_failure;
+
+               if (nla_put_u16(msg, NL80211_WMMR_CW_MIN,
+                               rule->wmm_rule->client[j].cw_min) ||
+                   nla_put_u16(msg, NL80211_WMMR_CW_MAX,
+                               rule->wmm_rule->client[j].cw_max) ||
+                   nla_put_u8(msg, NL80211_WMMR_AIFSN,
+                              rule->wmm_rule->client[j].aifsn) ||
+                   nla_put_u8(msg, NL80211_WMMR_TXOP,
+                              rule->wmm_rule->client[j].cot))
+                       goto nla_put_failure;
+
+               nla_nest_end(msg, nl_wmm_rule);
+       }
+       nla_nest_end(msg, nl_wmm_rules);
+
+       return 0;
+
+nla_put_failure:
+       return -ENOBUFS;
+}
+
+static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy,
                                   struct ieee80211_channel *chan,
                                   bool large)
 {
@@ -721,12 +762,55 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,
                        DBM_TO_MBM(chan->max_power)))
                goto nla_put_failure;
 
+       if (large) {
+               const struct ieee80211_reg_rule *rule =
+                       freq_reg_info(wiphy, chan->center_freq);
+
+               if (!IS_ERR(rule) && rule->wmm_rule) {
+                       if (nl80211_msg_put_wmm_rules(msg, rule))
+                               goto nla_put_failure;
+               }
+       }
+
        return 0;
 
  nla_put_failure:
        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 */
 
 struct key_parse {
@@ -1631,7 +1715,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
                                        chan = &sband->channels[i];
 
                                        if (nl80211_msg_put_channel(
-                                                       msg, chan,
+                                                       msg, &rdev->wiphy, chan,
                                                        state->split))
                                                goto nla_put_failure;
 
@@ -1926,6 +2010,28 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
                                rdev->wiphy.nan_supported_bands))
                        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 */
                state->split_start = 0;
                break;
@@ -2303,6 +2409,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
        u8 retry_short = 0, retry_long = 0;
        u32 frag_threshold = 0, rts_threshold = 0;
        u8 coverage_class = 0;
+       u32 txq_limit = 0, txq_memory_limit = 0, txq_quantum = 0;
 
        ASSERT_RTNL();
 
@@ -2509,10 +2616,38 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                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) {
                u8 old_retry_short, old_retry_long;
                u32 old_frag_threshold, old_rts_threshold;
                u8 old_coverage_class;
+               u32 old_txq_limit, old_txq_memory_limit, old_txq_quantum;
 
                if (!rdev->ops->set_wiphy_params)
                        return -EOPNOTSUPP;
@@ -2522,6 +2657,9 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                old_frag_threshold = rdev->wiphy.frag_threshold;
                old_rts_threshold = rdev->wiphy.rts_threshold;
                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)
                        rdev->wiphy.retry_short = retry_short;
@@ -2533,6 +2671,12 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
                        rdev->wiphy.rts_threshold = rts_threshold;
                if (changed & WIPHY_PARAM_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);
                if (result) {
@@ -2541,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.rts_threshold = old_rts_threshold;
                        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;
                }
        }
@@ -2662,6 +2809,16 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag
        }
        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);
        return 0;
 
@@ -4494,11 +4651,14 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
        PUT_SINFO_U64(BEACON_RX, rx_beacon);
        PUT_SINFO(BEACON_SIGNAL_AVG, rx_beacon_signal_avg, u8);
        PUT_SINFO(ACK_SIGNAL, ack_signal, u8);
+       if (wiphy_ext_feature_isset(&rdev->wiphy,
+                                   NL80211_EXT_FEATURE_DATA_ACK_SIGNAL_SUPPORT))
+               PUT_SINFO(DATA_ACK_SIGNAL_AVG, avg_ack_signal, s8);
 
 #undef PUT_SINFO
 #undef PUT_SINFO_U64
 
-       if (sinfo->filled & BIT(NL80211_STA_INFO_TID_STATS)) {
+       if (sinfo->pertid) {
                struct nlattr *tidsattr;
                int tid;
 
@@ -4532,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);
 
 #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);
                }
 
@@ -4545,10 +4711,12 @@ static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid,
                    sinfo->assoc_req_ies))
                goto nla_put_failure;
 
+       cfg80211_sinfo_release_content(sinfo);
        genlmsg_end(msg, hdr);
        return 0;
 
  nla_put_failure:
+       cfg80211_sinfo_release_content(sinfo);
        genlmsg_cancel(msg, hdr);
        return -EMSGSIZE;
 }
@@ -4630,8 +4798,10 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
                return err;
 
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
-       if (!msg)
+       if (!msg) {
+               cfg80211_sinfo_release_content(sinfo);
                return -ENOMEM;
+       }
 
        if (nl80211_send_station(msg, NL80211_CMD_NEW_STATION,
                                 info->snd_portid, info->snd_seq, 0,
@@ -14320,7 +14490,8 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
        nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
        if (!nl_freq)
                goto nla_put_failure;
-       if (nl80211_msg_put_channel(msg, channel_before, false))
+
+       if (nl80211_msg_put_channel(msg, wiphy, channel_before, false))
                goto nla_put_failure;
        nla_nest_end(msg, nl_freq);
 
@@ -14328,7 +14499,8 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
        nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
        if (!nl_freq)
                goto nla_put_failure;
-       if (nl80211_msg_put_channel(msg, channel_after, false))
+
+       if (nl80211_msg_put_channel(msg, wiphy, channel_after, false))
                goto nla_put_failure;
        nla_nest_end(msg, nl_freq);
 
@@ -14455,8 +14627,10 @@ void cfg80211_del_sta_sinfo(struct net_device *dev, const u8 *mac_addr,
        trace_cfg80211_del_sta(dev, mac_addr);
 
        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
-       if (!msg)
+       if (!msg) {
+               cfg80211_sinfo_release_content(sinfo);
                return;
+       }
 
        if (nl80211_send_station(msg, NL80211_CMD_DEL_STATION, 0, 0, 0,
                                 rdev, dev, mac_addr, sinfo) < 0) {