ath10k: implement tx pause wmi event
authorMichal Kazior <michal.kazior@tieto.com>
Tue, 31 Mar 2015 10:26:24 +0000 (10:26 +0000)
committerKalle Valo <kvalo@qca.qualcomm.com>
Wed, 1 Apr 2015 17:31:08 +0000 (20:31 +0300)
qca6174 wmi-tlv firmware defines a new wmi event
for host tx pausing (i.e. stop/wake tx queues).

Map these events to ath10k/mac80211 tx queue
control.

This is important for multi-channel throughput
performance.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
drivers/net/wireless/ath/ath10k/mac.c
drivers/net/wireless/ath/ath10k/mac.h
drivers/net/wireless/ath/ath10k/wmi-tlv.c
drivers/net/wireless/ath/ath10k/wmi-tlv.h

index 669662a2b416abf8ebc4932d571658455b069bd4..2da66ca635fd9e88222e78ccc27b0281ff84f219 100644 (file)
@@ -28,6 +28,7 @@
 #include "txrx.h"
 #include "testmode.h"
 #include "wmi.h"
+#include "wmi-tlv.h"
 #include "wmi-ops.h"
 #include "wow.h"
 
@@ -2919,6 +2920,83 @@ void ath10k_mac_vif_tx_unlock(struct ath10k_vif *arvif, int reason)
        ieee80211_wake_queue(ar->hw, arvif->vdev_id);
 }
 
+static void ath10k_mac_vif_handle_tx_pause(struct ath10k_vif *arvif,
+                                          enum wmi_tlv_tx_pause_id pause_id,
+                                          enum wmi_tlv_tx_pause_action action)
+{
+       struct ath10k *ar = arvif->ar;
+
+       lockdep_assert_held(&ar->htt.tx_lock);
+
+       switch (pause_id) {
+       case WMI_TLV_TX_PAUSE_ID_MCC:
+       case WMI_TLV_TX_PAUSE_ID_P2P_CLI_NOA:
+       case WMI_TLV_TX_PAUSE_ID_P2P_GO_PS:
+       case WMI_TLV_TX_PAUSE_ID_AP_PS:
+       case WMI_TLV_TX_PAUSE_ID_IBSS_PS:
+               switch (action) {
+               case WMI_TLV_TX_PAUSE_ACTION_STOP:
+                       ath10k_mac_vif_tx_lock(arvif, pause_id);
+                       break;
+               case WMI_TLV_TX_PAUSE_ACTION_WAKE:
+                       ath10k_mac_vif_tx_unlock(arvif, pause_id);
+                       break;
+               default:
+                       ath10k_warn(ar, "received unknown tx pause action %d on vdev %i, ignoring\n",
+                                   action, arvif->vdev_id);
+                       break;
+               }
+               break;
+       case WMI_TLV_TX_PAUSE_ID_AP_PEER_PS:
+       case WMI_TLV_TX_PAUSE_ID_AP_PEER_UAPSD:
+       case WMI_TLV_TX_PAUSE_ID_STA_ADD_BA:
+       case WMI_TLV_TX_PAUSE_ID_HOST:
+       default:
+               /* FIXME: Some pause_ids aren't vdev specific. Instead they
+                * target peer_id and tid. Implementing these could improve
+                * traffic scheduling fairness across multiple connected
+                * stations in AP/IBSS modes.
+                */
+               ath10k_dbg(ar, ATH10K_DBG_MAC,
+                          "mac ignoring unsupported tx pause vdev %i id %d\n",
+                          arvif->vdev_id, pause_id);
+               break;
+       }
+}
+
+struct ath10k_mac_tx_pause {
+       u32 vdev_id;
+       enum wmi_tlv_tx_pause_id pause_id;
+       enum wmi_tlv_tx_pause_action action;
+};
+
+static void ath10k_mac_handle_tx_pause_iter(void *data, u8 *mac,
+                                           struct ieee80211_vif *vif)
+{
+       struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+       struct ath10k_mac_tx_pause *arg = data;
+
+       ath10k_mac_vif_handle_tx_pause(arvif, arg->pause_id, arg->action);
+}
+
+void ath10k_mac_handle_tx_pause(struct ath10k *ar, u32 vdev_id,
+                               enum wmi_tlv_tx_pause_id pause_id,
+                               enum wmi_tlv_tx_pause_action action)
+{
+       struct ath10k_mac_tx_pause arg = {
+               .vdev_id = vdev_id,
+               .pause_id = pause_id,
+               .action = action,
+       };
+
+       spin_lock_bh(&ar->htt.tx_lock);
+       ieee80211_iterate_active_interfaces_atomic(ar->hw,
+                                                  IEEE80211_IFACE_ITER_RESUME_ALL,
+                                                  ath10k_mac_handle_tx_pause_iter,
+                                                  &arg);
+       spin_unlock_bh(&ar->htt.tx_lock);
+}
+
 static u8 ath10k_tx_h_get_tid(struct ieee80211_hdr *hdr)
 {
        if (ieee80211_is_mgmt(hdr->frame_control))
@@ -4174,6 +4252,14 @@ err:
        return ret;
 }
 
+static void ath10k_mac_vif_tx_unlock_all(struct ath10k_vif *arvif)
+{
+       int i;
+
+       for (i = 0; i < BITS_PER_LONG; i++)
+               ath10k_mac_vif_tx_unlock(arvif, i);
+}
+
 static void ath10k_remove_interface(struct ieee80211_hw *hw,
                                    struct ieee80211_vif *vif)
 {
@@ -4240,6 +4326,10 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
                        ath10k_warn(ar, "failed to recalc monitor: %d\n", ret);
        }
 
+       spin_lock_bh(&ar->htt.tx_lock);
+       ath10k_mac_vif_tx_unlock_all(arvif);
+       spin_unlock_bh(&ar->htt.tx_lock);
+
        mutex_unlock(&ar->conf_mutex);
 }
 
index 25819efb8b5d7a567da64d6ffb0d30ba236f4250..b291f063705c3bb816f811aad450e9549aca5dfb 100644 (file)
@@ -23,6 +23,9 @@
 
 #define WEP_KEYID_SHIFT 6
 
+enum wmi_tlv_tx_pause_id;
+enum wmi_tlv_tx_pause_action;
+
 struct ath10k_generic_iter {
        struct ath10k *ar;
        int ret;
@@ -55,8 +58,12 @@ bool ath10k_mac_is_peer_wep_key_set(struct ath10k *ar, const u8 *addr,
                                    u8 keyidx);
 int ath10k_mac_vif_chan(struct ieee80211_vif *vif,
                        struct cfg80211_chan_def *def);
+
 void ath10k_mac_handle_beacon(struct ath10k *ar, struct sk_buff *skb);
 void ath10k_mac_handle_beacon_miss(struct ath10k *ar, u32 vdev_id);
+void ath10k_mac_handle_tx_pause(struct ath10k *ar, u32 vdev_id,
+                               enum wmi_tlv_tx_pause_id pause_id,
+                               enum wmi_tlv_tx_pause_action action);
 
 u8 ath10k_mac_hw_rate_to_idx(const struct ieee80211_supported_band *sband,
                             u8 hw_rate);
index 28cab0777ef1171d82a64ea48177e6614c461776..03e34c351576f5376db08ca028364caf82124619 100644 (file)
@@ -16,6 +16,7 @@
  */
 #include "core.h"
 #include "debug.h"
+#include "mac.h"
 #include "hw.h"
 #include "mac.h"
 #include "wmi.h"
@@ -70,6 +71,8 @@ static const struct wmi_tlv_policy wmi_tlv_policies[] = {
                = { .min_len = sizeof(struct wmi_tlv_roam_ev) },
        [WMI_TLV_TAG_STRUCT_WOW_EVENT_INFO]
                = { .min_len = sizeof(struct wmi_tlv_wow_event_info) },
+       [WMI_TLV_TAG_STRUCT_TX_PAUSE_EVENT]
+               = { .min_len = sizeof(struct wmi_tlv_tx_pause_ev) },
 };
 
 static int
@@ -339,6 +342,48 @@ static int ath10k_wmi_tlv_event_p2p_noa(struct ath10k *ar,
                   vdev_id, noa->num_descriptors);
 
        ath10k_p2p_noa_update_by_vdev_id(ar, vdev_id, noa);
+       kfree(tb);
+       return 0;
+}
+
+static int ath10k_wmi_tlv_event_tx_pause(struct ath10k *ar,
+                                        struct sk_buff *skb)
+{
+       const void **tb;
+       const struct wmi_tlv_tx_pause_ev *ev;
+       int ret, vdev_id;
+       u32 pause_id, action, vdev_map, peer_id, tid_map;
+
+       tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC);
+       if (IS_ERR(tb)) {
+               ret = PTR_ERR(tb);
+               ath10k_warn(ar, "failed to parse tlv: %d\n", ret);
+               return ret;
+       }
+
+       ev = tb[WMI_TLV_TAG_STRUCT_TX_PAUSE_EVENT];
+       if (!ev) {
+               kfree(tb);
+               return -EPROTO;
+       }
+
+       pause_id = __le32_to_cpu(ev->pause_id);
+       action = __le32_to_cpu(ev->action);
+       vdev_map = __le32_to_cpu(ev->vdev_map);
+       peer_id = __le32_to_cpu(ev->peer_id);
+       tid_map = __le32_to_cpu(ev->tid_map);
+
+       ath10k_dbg(ar, ATH10K_DBG_WMI,
+                  "wmi tlv tx pause pause_id %u action %u vdev_map 0x%08x peer_id %u tid_map 0x%08x\n",
+                  pause_id, action, vdev_map, peer_id, tid_map);
+
+       for (vdev_id = 0; vdev_map; vdev_id++) {
+               if (!(vdev_map & BIT(vdev_id)))
+                       continue;
+
+               vdev_map &= ~BIT(vdev_id);
+               ath10k_mac_handle_tx_pause(ar, vdev_id, pause_id, action);
+       }
 
        kfree(tb);
        return 0;
@@ -468,6 +513,9 @@ static void ath10k_wmi_tlv_op_rx(struct ath10k *ar, struct sk_buff *skb)
        case WMI_TLV_P2P_NOA_EVENTID:
                ath10k_wmi_tlv_event_p2p_noa(ar, skb);
                break;
+       case WMI_TLV_TX_PAUSE_EVENTID:
+               ath10k_wmi_tlv_event_tx_pause(ar, skb);
+               break;
        default:
                ath10k_warn(ar, "Unknown eventid: %d\n", id);
                break;
index 8d41492f3aff2971f1ae9a744abdad15d918169b..ad655c44afdb604572c36f70ca7a611f53f6c56b 100644 (file)
@@ -1580,6 +1580,48 @@ struct wmi_tlv_adaptive_qcs {
        __le32 enable;
 } __packed;
 
+/**
+ * wmi_tlv_tx_pause_id - firmware tx queue pause reason types
+ *
+ * @WMI_TLV_TX_PAUSE_ID_MCC: used for by multi-channel firmware scheduler.
+ *             Only vdev_map is valid.
+ * @WMI_TLV_TX_PAUSE_ID_AP_PEER_PS: peer in AP mode is asleep.
+ *             Only peer_id is valid.
+ * @WMI_TLV_TX_PAUSE_ID_AP_PEER_UAPSD: Only peer_id and tid_map are valid.
+ * @WMI_TLV_TX_PAUSE_ID_P2P_CLI_NOA: Only vdev_map is valid.
+ * @WMI_TLV_TX_PAUSE_ID_P2P_GO_PS: Only vdev_map is valid.
+ * @WMI_TLV_TX_PAUSE_ID_STA_ADD_BA: Only peer_id and tid_map are valid.
+ * @WMI_TLV_TX_PAUSE_ID_AP_PS: When all peers are asleep in AP mode. Only
+ *             vdev_map is valid.
+ * @WMI_TLV_TX_PAUSE_ID_IBSS_PS: When all peers are asleep in IBSS mode. Only
+ *             vdev_map is valid.
+ * @WMI_TLV_TX_PAUSE_ID_HOST: Host itself requested tx pause.
+ */
+enum wmi_tlv_tx_pause_id {
+       WMI_TLV_TX_PAUSE_ID_MCC = 1,
+       WMI_TLV_TX_PAUSE_ID_AP_PEER_PS = 2,
+       WMI_TLV_TX_PAUSE_ID_AP_PEER_UAPSD = 3,
+       WMI_TLV_TX_PAUSE_ID_P2P_CLI_NOA = 4,
+       WMI_TLV_TX_PAUSE_ID_P2P_GO_PS = 5,
+       WMI_TLV_TX_PAUSE_ID_STA_ADD_BA = 6,
+       WMI_TLV_TX_PAUSE_ID_AP_PS = 7,
+       WMI_TLV_TX_PAUSE_ID_IBSS_PS = 8,
+       WMI_TLV_TX_PAUSE_ID_HOST = 21,
+};
+
+enum wmi_tlv_tx_pause_action {
+       WMI_TLV_TX_PAUSE_ACTION_STOP,
+       WMI_TLV_TX_PAUSE_ACTION_WAKE,
+};
+
+struct wmi_tlv_tx_pause_ev {
+       __le32 pause_id;
+       __le32 action;
+       __le32 vdev_map;
+       __le32 peer_id;
+       __le32 tid_map;
+} __packed;
+
 void ath10k_wmi_tlv_attach(struct ath10k *ar);
 
 #endif