Use per-peer noack tid bitmap, if it is configured,
when setting up the qos header. If no per-peer configuration
is set, use the existing nedev wide noack policy configuration.
Also modifies callback set_noack_tid_bitmap() with the provision
to send per-peer NoAck policy configuration to the drivers supporting
the NoAck offload functionality (IEEE80211_HW_SUPPORTS_NOACK_POLICY).

Signed-off-by: Vasanthakumar Thiagarajan <vthia...@codeaurora.org>
---
 include/net/mac80211.h    |  7 +++++--
 net/mac80211/cfg.c        | 42 +++++++++++++++++++++++++++++++++++++-----
 net/mac80211/driver-ops.h |  5 +++--
 net/mac80211/iface.c      |  2 +-
 net/mac80211/sta_info.h   |  3 +++
 net/mac80211/trace.h      |  9 ++++++---
 net/mac80211/tx.c         |  2 +-
 net/mac80211/wme.c        | 34 +++++++++++++++++++++++++++++++++-
 8 files changed, 89 insertions(+), 15 deletions(-)

diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 5a49c90..a5ed552 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -3499,7 +3499,9 @@ enum ieee80211_reconfig_type {
  *     ieee80211_nan_func_terminated() with
  *     NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST reason code upon removal.
  *
- * @set_noack_tid_bitmap: Set NoAck policy TID bitmap for a virtual interface.
+ * @set_noack_tid_bitmap: Set NoAck policy TID bitmap. Apply the TID NoAck
+ *     configuration for a particular station when @sta is non-NULL. When @sta
+ *     is NULL, apply TID NoAck configuration at virtual interface level.
  *     Drivers advertising NoAck policy handing support
  *     (%IEEE80211_HW_SUPPORTS_NOACK_POLICY) must implement this callback.
  */
@@ -3785,7 +3787,8 @@ struct ieee80211_ops {
                            u8 instance_id);
 
        int (*set_noack_tid_bitmap)(struct ieee80211_hw *hw,
-                                   struct ieee80211_vif *vif, u8 noack_map);
+                                   struct ieee80211_vif *vif,
+                                   struct ieee80211_sta *sta, u8 noack_map);
 };
 
 /**
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 621ef38..1efc9cf 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -345,18 +345,50 @@ static int ieee80211_set_noack_map(struct wiphy *wiphy,
                                  u16 noack_map)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+       struct sta_info *sta;
+       int ret;
 
-       sdata->noack_map = noack_map;
+       if (!peer) {
+               sdata->noack_map = noack_map;
 
-       if (!ieee80211_hw_check(&sdata->local->hw, SUPPORTS_NOACK_POLICY)) {
-               ieee80211_check_fast_xmit_iface(sdata);
-               return 0;
+               if (!ieee80211_hw_check(&sdata->local->hw, 
SUPPORTS_NOACK_POLICY)) {
+                       ieee80211_check_fast_xmit_iface(sdata);
+                       return 0;
+               }
+
+               if (!ieee80211_sdata_running(sdata))
+                       return 0;
+
+               return drv_set_noack_tid_bitmap(sdata->local, sdata, NULL,
+                                               noack_map);
        }
 
+       /* NoAck policy is for a connected client on the dev */
+
        if (!ieee80211_sdata_running(sdata))
+               return -ENETDOWN;
+
+       mutex_lock(&sdata->local->sta_mtx);
+
+       sta = sta_info_get_bss(sdata, peer);
+       if (!sta) {
+               mutex_unlock(&sdata->local->sta_mtx);
+               return -ENOENT;
+       }
+
+       sta->noack_map = noack_map;
+
+       if (!ieee80211_hw_check(&sdata->local->hw, SUPPORTS_NOACK_POLICY)) {
+               ieee80211_check_fast_xmit(sta);
+               mutex_unlock(&sdata->local->sta_mtx);
                return 0;
+       }
+
+       ret = drv_set_noack_tid_bitmap(sdata->local, sdata, sta, noack_map);
 
-       return drv_set_noack_tid_bitmap(sdata->local, sdata, noack_map);
+       mutex_unlock(&sdata->local->sta_mtx);
+
+       return ret;
 }
 
 static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index a0a2d3c..8a2154a 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -1252,6 +1252,7 @@ static inline void drv_del_nan_func(struct 
ieee80211_local *local,
 
 static inline int drv_set_noack_tid_bitmap(struct ieee80211_local *local,
                                           struct ieee80211_sub_if_data *sdata,
+                                          struct sta_info *sta,
                                           u16 noack_map)
 {
        int ret;
@@ -1263,9 +1264,9 @@ static inline int drv_set_noack_tid_bitmap(struct 
ieee80211_local *local,
        if (!local->ops->set_noack_tid_bitmap)
                return -EOPNOTSUPP;
 
-       trace_drv_set_noack_tid_bitmap(local, sdata, noack_map);
+       trace_drv_set_noack_tid_bitmap(local, sdata, &sta->sta, noack_map);
        ret = local->ops->set_noack_tid_bitmap(&local->hw, &sdata->vif,
-                                              noack_map);
+                                              &sta->sta, noack_map);
        trace_drv_return_int(local, ret);
 
        return ret;
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 4e120b9..1172bed 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -633,7 +633,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool 
coming_up)
                                goto err_del_interface;
 
                        if (sdata->noack_map)
-                               drv_set_noack_tid_bitmap(local, sdata,
+                               drv_set_noack_tid_bitmap(local, sdata, NULL,
                                                         sdata->noack_map);
                }
 
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index f64eb86..01bee75 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -581,6 +581,9 @@ struct sta_info {
 
        struct cfg80211_chan_def tdls_chandef;
 
+       /* TID bitmap for station's NoAck policy */
+       u16 noack_map;
+
        /* keep last! */
        struct ieee80211_sta sta;
 };
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 3b55569..84089f7 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -1866,25 +1866,28 @@ struct trace_switch_entry {
 TRACE_EVENT(drv_set_noack_tid_bitmap,
        TP_PROTO(struct ieee80211_local *local,
                 struct ieee80211_sub_if_data *sdata,
+                struct ieee80211_sta *sta,
                 u16 noack_map),
 
-       TP_ARGS(local, sdata, noack_map),
+       TP_ARGS(local, sdata, sta, noack_map),
        TP_STRUCT__entry(
                LOCAL_ENTRY
                VIF_ENTRY
+               STA_ENTRY
                __field(u16, noack_map)
        ),
 
        TP_fast_assign(
                LOCAL_ASSIGN;
                VIF_ASSIGN;
+               STA_ASSIGN;
                __entry->noack_map = noack_map;
        ),
 
        TP_printk(
-               LOCAL_PR_FMT  VIF_PR_FMT
+               LOCAL_PR_FMT  VIF_PR_FMT STA_PR_FMT
                ", noack_map: %u",
-               LOCAL_PR_ARG, VIF_PR_ARG, __entry->noack_map
+               LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->noack_map
        )
 );
 
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 27fcef3..5abe73b 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2817,7 +2817,7 @@ void ieee80211_check_fast_xmit(struct sta_info *sta)
            test_sta_flag(sta, WLAN_STA_CLEAR_PS_FILT))
                goto out;
 
-       if (sdata->noack_map &&
+       if ((sdata->noack_map || sta->noack_map) &&
            !ieee80211_hw_check(&local->hw, SUPPORTS_NOACK_POLICY))
                goto out;
 
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index 6512a47..645c50b 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -227,6 +227,38 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data 
*sdata,
 }
 
 /**
+ * ieee80211_get_noack_map - Get TID bitmap of NoAck policy. NoAck policy
+ * could be device wide or per-station.
+ *
+ * @sdata: local subif
+ * @mac: MAC address of the receiver
+ */
+u16 ieee80211_get_noack_map(struct ieee80211_sub_if_data *sdata, const u8 *mac)
+{
+       struct sta_info *sta;
+       u16 noack_map = 0;
+
+       /* Retrieve per-station noack_map config for the receiver, if any */
+
+       rcu_read_lock();
+
+       sta = sta_info_get(sdata, mac);
+       if (!sta) {
+               rcu_read_unlock();
+               return noack_map;
+       }
+
+       noack_map = sta->noack_map;
+
+       rcu_read_unlock();
+
+       if (!noack_map)
+               noack_map = sdata->noack_map;
+
+       return noack_map;
+}
+
+/**
  * ieee80211_set_qos_hdr - Fill in the QoS header if there is one.
  *
  * @sdata: local subif
@@ -257,7 +289,7 @@ void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data 
*sdata,
 
        if (is_multicast_ether_addr(hdr->addr1) ||
            (!ieee80211_hw_check(&sdata->local->hw, SUPPORTS_NOACK_POLICY) &&
-           sdata->noack_map & BIT(tid))) {
+           ieee80211_get_noack_map(sdata, hdr->addr1) & BIT(tid))) {
                flags |= IEEE80211_QOS_CTL_ACK_POLICY_NOACK;
                info->flags |= IEEE80211_TX_CTL_NO_ACK;
        }
-- 
1.9.1

Reply via email to