2016-01-21 13:45:37

by Michal Kazior

[permalink] [raw]
Subject: [PATCH 00/13] ath10k: implement push-pull tx model

This adds support for the new logic where host
tells firmware how many frames are queued for each
station/tid and then firmware asks host to submit
frames for given station/tid.

The patch count is a bit high but I tried
splitting the patches as much as possible to keep
them short and easy to review. Hopefully it's not
going to be a huge headache.

Note: This depends on my other patches:

http://lists.infradead.org/pipermail/ath10k/2016-January/006675.html
http://lists.infradead.org/pipermail/ath10k/2016-January/006729.html
http://thread.gmane.org/gmane.linux.kernel.wireless.general/147504

I'm posting this prior to the above getting merged
to get early feedback.

For convienence I'm providing a branch on github
which contains all dependencies and the patchset
itself:

https://github.com/kazikcz/linux/tree/ath10k-pull-push


Michal Kazior (13):
ath10k: refactor tx code
ath10k: unify txpath decision
ath10k: refactor tx pending management
ath10k: maintain peer_id for each sta and vif
ath10k: add fast peer_map lookup
ath10k: add new htt message generation/parsing logic
ath10k: implement wake_tx_queue
ath10k: implement updating shared htt txq state
ath10k: add txq placeholder
ath10k: implement flushing of pending frames in txqs
ath10k: store txq in skb_cb
ath10k: keep track of queue depth per txq
ath10k: implement push-pull tx

drivers/net/wireless/ath/ath10k/core.h | 13 +
drivers/net/wireless/ath/ath10k/htt.h | 21 +-
drivers/net/wireless/ath/ath10k/htt_rx.c | 302 +++++++++++++++-
drivers/net/wireless/ath/ath10k/htt_tx.c | 257 ++++++++++----
drivers/net/wireless/ath/ath10k/mac.c | 581 +++++++++++++++++++++++++++----
drivers/net/wireless/ath/ath10k/mac.h | 6 +
drivers/net/wireless/ath/ath10k/txrx.c | 11 +-
7 files changed, 1060 insertions(+), 131 deletions(-)

--
2.1.4



2016-01-26 10:28:21

by Michal Kazior

[permalink] [raw]
Subject: Re: [PATCH 13/13] ath10k: implement push-pull tx

On 22 January 2016 at 08:47, Michal Kazior <[email protected]> wrote:
> On 21 January 2016 at 18:40, Peter Oh <[email protected]> wrote:
>> On 01/21/2016 05:46 AM, Michal Kazior wrote:
[...]
>>> - /* TODO: apply configuration */
>>> + rcu_read_unlock();
>>> +
>>> + spin_lock_bh(&ar->htt.tx_lock);
>>> + ath10k_mac_tx_lock(ar, ATH10K_TX_PAUSE_Q_FLUSH_PENDING);
>>> + spin_unlock_bh(&ar->htt.tx_lock);
>>> +
>>
>> Isn't it proved it break Mesh from working?
>
> Yes, good point. I'm still working on this - I should've mentioned
> that in the cover letter.
>
> Nonetheless I wanted to get the review process going for this patchset.
>
> I'll address the mesh problem in v2.

For the record - the problem turned out to be a bug in mac80211 mesh
fwding, see:

https://patchwork.kernel.org/patch/8108711/


Michał

2016-01-21 13:45:43

by Michal Kazior

[permalink] [raw]
Subject: [PATCH 04/13] ath10k: maintain peer_id for each sta and vif

The 10.4.3 firmware with congestion control
guarantees that each peer has only a single
peer_id mapping.

The 1:1 mapping isn't the case for older firmwares
(e.g. 10.4.1, 10.2, 10.1) but it should not
matter. This 1:1 mapping is going to be only used
by future code which inherently (flow-wise) is for
10.4.3.

Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/core.h | 2 ++
drivers/net/wireless/ath/ath10k/mac.c | 38 ++++++++++++++++++++++++++++++++++
2 files changed, 40 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 822e3195533a..4b19c71bf6a5 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -309,6 +309,7 @@ struct ath10k_sta {
u32 bw;
u32 nss;
u32 smps;
+ u16 peer_id;

struct work_struct update_wk;

@@ -330,6 +331,7 @@ struct ath10k_vif {
struct list_head list;

u32 vdev_id;
+ u16 peer_id;
enum wmi_vdev_type vdev_type;
enum wmi_vdev_subtype vdev_subtype;
u32 beacon_interval;
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 212c840e1599..ec4fae8dcb92 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -4411,6 +4411,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
{
struct ath10k *ar = hw->priv;
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+ struct ath10k_peer *peer;
enum wmi_sta_powersave_param param;
int ret = 0;
u32 value;
@@ -4605,6 +4606,24 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
arvif->vdev_id, ret);
goto err_vdev_delete;
}
+
+ spin_lock_bh(&ar->data_lock);
+
+ peer = ath10k_peer_find(ar, arvif->vdev_id, vif->addr);
+ if (!peer) {
+ ath10k_warn(ar, "failed to lookup peer %pM on vdev %i\n",
+ vif->addr, arvif->vdev_id);
+ spin_unlock_bh(&ar->data_lock);
+ ret = -ENOENT;
+ goto err_peer_delete;
+ }
+
+ arvif->peer_id = find_first_bit(peer->peer_ids,
+ ATH10K_MAX_NUM_PEER_IDS);
+
+ spin_unlock_bh(&ar->data_lock);
+ } else {
+ arvif->peer_id = HTT_INVALID_PEERID;
}

if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
@@ -5486,6 +5505,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
struct ath10k *ar = hw->priv;
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
+ struct ath10k_peer *peer;
int ret = 0;

if (old_state == IEEE80211_STA_NOTEXIST &&
@@ -5536,6 +5556,24 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
goto exit;
}

+ spin_lock_bh(&ar->data_lock);
+
+ peer = ath10k_peer_find(ar, arvif->vdev_id, sta->addr);
+ if (!peer) {
+ ath10k_warn(ar, "failed to lookup peer %pM on vdev %i\n",
+ vif->addr, arvif->vdev_id);
+ spin_unlock_bh(&ar->data_lock);
+ ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
+ ath10k_mac_dec_num_stations(arvif, sta);
+ ret = -ENOENT;
+ goto exit;
+ }
+
+ arsta->peer_id = find_first_bit(peer->peer_ids,
+ ATH10K_MAX_NUM_PEER_IDS);
+
+ spin_unlock_bh(&ar->data_lock);
+
if (!sta->tdls)
goto exit;

--
2.1.4


2016-01-21 13:45:39

by Michal Kazior

[permalink] [raw]
Subject: [PATCH 01/13] ath10k: refactor tx code

This prepares the code for future reuse with
ieee80211_txq and wake_tx_queue() in mind.

Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/mac.c | 151 +++++++++++++++++++++-------------
1 file changed, 96 insertions(+), 55 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 368de5e5a04f..a684718f6389 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -3275,6 +3275,26 @@ static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar,
}
}

+static void ath10k_mac_tx_h_fill_cb(struct ath10k *ar,
+ struct ieee80211_vif *vif,
+ struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb);
+
+ cb->flags = 0;
+ if (!ath10k_tx_h_use_hwcrypto(vif, skb))
+ cb->flags |= ATH10K_SKB_F_NO_HWCRYPT;
+
+ if (ieee80211_is_mgmt(hdr->frame_control))
+ cb->flags |= ATH10K_SKB_F_MGMT;
+
+ if (ieee80211_is_data_qos(hdr->frame_control))
+ cb->flags |= ATH10K_SKB_F_QOS;
+
+ cb->vif = vif;
+}
+
bool ath10k_mac_tx_frm_has_freq(struct ath10k *ar)
{
/* FIXME: Not really sure since when the behaviour changed. At some
@@ -3310,8 +3330,9 @@ unlock:
return ret;
}

-static void ath10k_mac_tx(struct ath10k *ar, enum ath10k_hw_txrx_mode txmode,
- struct sk_buff *skb)
+static int ath10k_mac_tx_submit(struct ath10k *ar,
+ enum ath10k_hw_txrx_mode txmode,
+ struct sk_buff *skb)
{
struct ath10k_htt *htt = &ar->htt;
int ret = 0;
@@ -3338,6 +3359,63 @@ static void ath10k_mac_tx(struct ath10k *ar, enum ath10k_hw_txrx_mode txmode,
ret);
ieee80211_free_txskb(ar->hw, skb);
}
+
+ return ret;
+}
+
+/* This function consumes the sk_buff regardless of return value as far as
+ * caller is concerned so no freeing is necessary afterwards.
+ */
+static int ath10k_mac_tx(struct ath10k *ar,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ enum ath10k_hw_txrx_mode txmode,
+ struct sk_buff *skb)
+{
+ struct ieee80211_hw *hw = ar->hw;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ int ret;
+
+ /* We should disable CCK RATE due to P2P */
+ if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n");
+
+ switch (txmode) {
+ case ATH10K_HW_TXRX_MGMT:
+ case ATH10K_HW_TXRX_NATIVE_WIFI:
+ ath10k_tx_h_nwifi(hw, skb);
+ ath10k_tx_h_add_p2p_noa_ie(ar, vif, skb);
+ ath10k_tx_h_seq_no(vif, skb);
+ break;
+ case ATH10K_HW_TXRX_ETHERNET:
+ ath10k_tx_h_8023(skb);
+ break;
+ case ATH10K_HW_TXRX_RAW:
+ if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) {
+ WARN_ON_ONCE(1);
+ ieee80211_free_txskb(hw, skb);
+ return -ENOTSUPP;
+ }
+ }
+
+ if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
+ if (!ath10k_mac_tx_frm_has_freq(ar)) {
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "queued offchannel skb %p\n",
+ skb);
+
+ skb_queue_tail(&ar->offchan_tx_queue, skb);
+ ieee80211_queue_work(hw, &ar->offchan_tx_work);
+ return 0;
+ }
+ }
+
+ ret = ath10k_mac_tx_submit(ar, txmode, skb);
+ if (ret) {
+ ath10k_warn(ar, "failed to submit frame: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
}

void ath10k_offchan_tx_purge(struct ath10k *ar)
@@ -3358,12 +3436,12 @@ void ath10k_offchan_tx_work(struct work_struct *work)
struct ath10k *ar = container_of(work, struct ath10k, offchan_tx_work);
struct ath10k_peer *peer;
struct ath10k_vif *arvif;
+ enum ath10k_hw_txrx_mode txmode;
struct ieee80211_hdr *hdr;
struct ieee80211_vif *vif;
struct ieee80211_sta *sta;
struct sk_buff *skb;
const u8 *peer_addr;
- enum ath10k_hw_txrx_mode txmode;
int vdev_id;
int ret;
unsigned long time_left;
@@ -3428,7 +3506,12 @@ void ath10k_offchan_tx_work(struct work_struct *work)

txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);

- ath10k_mac_tx(ar, txmode, skb);
+ ret = ath10k_mac_tx(ar, vif, sta, txmode, skb);
+ if (ret) {
+ ath10k_warn(ar, "failed to transmit offchannel frame: %d\n",
+ ret);
+ /* not serious */
+ }

time_left =
wait_for_completion_timeout(&ar->offchan_tx_completed, 3 * HZ);
@@ -3642,66 +3725,24 @@ static int ath10k_start_scan(struct ath10k *ar,
/* mac80211 callbacks */
/**********************/

-static void ath10k_tx(struct ieee80211_hw *hw,
- struct ieee80211_tx_control *control,
- struct sk_buff *skb)
+static void ath10k_mac_op_tx(struct ieee80211_hw *hw,
+ struct ieee80211_tx_control *control,
+ struct sk_buff *skb)
{
struct ath10k *ar = hw->priv;
- struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_vif *vif = info->control.vif;
struct ieee80211_sta *sta = control->sta;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
enum ath10k_hw_txrx_mode txmode;
+ int ret;

- /* We should disable CCK RATE due to P2P */
- if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
- ath10k_dbg(ar, ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n");
+ ath10k_mac_tx_h_fill_cb(ar, vif, skb);

txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);

- skb_cb->flags = 0;
- if (!ath10k_tx_h_use_hwcrypto(vif, skb))
- skb_cb->flags |= ATH10K_SKB_F_NO_HWCRYPT;
-
- if (ieee80211_is_mgmt(hdr->frame_control))
- skb_cb->flags |= ATH10K_SKB_F_MGMT;
-
- if (ieee80211_is_data_qos(hdr->frame_control))
- skb_cb->flags |= ATH10K_SKB_F_QOS;
-
- skb_cb->vif = vif;
-
- switch (txmode) {
- case ATH10K_HW_TXRX_MGMT:
- case ATH10K_HW_TXRX_NATIVE_WIFI:
- ath10k_tx_h_nwifi(hw, skb);
- ath10k_tx_h_add_p2p_noa_ie(ar, vif, skb);
- ath10k_tx_h_seq_no(vif, skb);
- break;
- case ATH10K_HW_TXRX_ETHERNET:
- ath10k_tx_h_8023(skb);
- break;
- case ATH10K_HW_TXRX_RAW:
- if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) {
- WARN_ON_ONCE(1);
- ieee80211_free_txskb(hw, skb);
- return;
- }
- }
-
- if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
- if (!ath10k_mac_tx_frm_has_freq(ar)) {
- ath10k_dbg(ar, ATH10K_DBG_MAC, "queued offchannel skb %p\n",
- skb);
-
- skb_queue_tail(&ar->offchan_tx_queue, skb);
- ieee80211_queue_work(hw, &ar->offchan_tx_work);
- return;
- }
- }
-
- ath10k_mac_tx(ar, txmode, skb);
+ ret = ath10k_mac_tx(ar, vif, sta, txmode, skb);
+ if (ret)
+ ath10k_warn(ar, "failed to transmit frame: %d\n", ret);
}

/* Must not be called with conf_mutex held as workers can use that also. */
@@ -6792,7 +6833,7 @@ ath10k_mac_op_switch_vif_chanctx(struct ieee80211_hw *hw,
}

static const struct ieee80211_ops ath10k_ops = {
- .tx = ath10k_tx,
+ .tx = ath10k_mac_op_tx,
.start = ath10k_start,
.stop = ath10k_stop,
.config = ath10k_config,
--
2.1.4


2016-01-28 19:02:52

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH 13/13] ath10k: implement push-pull tx

Peter Oh <[email protected]> writes:

>> + spin_lock_bh(&ar->htt.tx_lock);
>> + ath10k_mac_tx_lock(ar, ATH10K_TX_PAUSE_Q_FLUSH_PENDING);
>> + spin_unlock_bh(&ar->htt.tx_lock);
>> +
>
> Isn't it proved it break Mesh from working?

Please think of others and edit your quotes, it was annoying to find
this oneliner from a 500+ line message.

--
Kalle Valo

2016-01-21 13:45:51

by Michal Kazior

[permalink] [raw]
Subject: [PATCH 10/13] ath10k: implement flushing of pending frames in txqs

Firmware supporting pull-push tx model may request
a switch between modes. When switching from
pull-push to push-only it will be necessary to
flush all pending frames. The code will do that
once other bits that actually trigger it are added.

Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/core.h | 1 +
drivers/net/wireless/ath/ath10k/htt_rx.c | 3 ++
drivers/net/wireless/ath/ath10k/mac.c | 82 ++++++++++++++++++++++++++++++++
drivers/net/wireless/ath/ath10k/mac.h | 1 +
4 files changed, 87 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index d09d6fdf1149..38ed4bbd220b 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -607,6 +607,7 @@ static inline const char *ath10k_scan_state_str(enum ath10k_scan_state state)

enum ath10k_tx_pause_reason {
ATH10K_TX_PAUSE_Q_FULL,
+ ATH10K_TX_PAUSE_Q_FLUSH_PENDING,
ATH10K_TX_PAUSE_MAX,
};

diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index be7dc88b3316..6e3d95c95568 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -2244,6 +2244,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
}

ath10k_txrx_tx_unref(htt, &tx_done);
+ ath10k_mac_tx_push_pending(ar);
break;
}
case HTT_T2H_MSG_TYPE_TX_COMPL_IND:
@@ -2370,6 +2371,8 @@ static void ath10k_htt_txrx_compl_task(unsigned long ptr)
dev_kfree_skb_any(skb);
}

+ ath10k_mac_tx_push_pending(ar);
+
spin_lock_bh(&htt->rx_ring.lock);
while ((skb = __skb_dequeue(&htt->rx_compl_q))) {
resp = (struct htt_resp *)skb->data;
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 205ccf3760a3..42020c22eaed 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -3710,6 +3710,88 @@ static void ath10k_mac_tx_wake(struct ieee80211_hw *hw,
ath10k_htt_tx_txq_update(hw, txq);
}

+struct ath10k_mac_tx_push_arg {
+ struct ieee80211_hw *hw;
+ bool more;
+};
+
+static void ath10k_mac_tx_push_pending_txq(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq,
+ struct ath10k_mac_tx_push_arg *arg)
+{
+ bool can_push;
+ bool has_more;
+ int ret;
+
+ if (!txq)
+ return;
+
+ while (txq->qdepth > 0) {
+ if (!ath10k_mac_tx_can_push(hw, txq))
+ break;
+
+ ret = ath10k_mac_tx_push_txq(hw, txq);
+ if (ret)
+ break;
+ }
+
+ can_push = ath10k_mac_tx_can_push(hw, txq);
+ has_more = txq->qdepth > 0;
+
+ if (can_push && has_more)
+ arg->more = true;
+}
+
+static void ath10k_mac_tx_push_pending_vif_iter(void *data,
+ u8 *mac,
+ struct ieee80211_vif *vif)
+{
+ struct ath10k_mac_tx_push_arg *arg = data;
+ struct ieee80211_hw *hw = arg->hw;
+
+ ath10k_mac_tx_push_pending_txq(hw, vif->txq, arg);
+}
+
+static void ath10k_mac_tx_push_pending_sta_iter(void *data,
+ struct ieee80211_sta *sta)
+{
+ struct ath10k_mac_tx_push_arg *arg = data;
+ struct ieee80211_hw *hw = arg->hw;
+ int tid;
+
+ for (tid = 0; tid < ARRAY_SIZE(sta->txq); tid++)
+ ath10k_mac_tx_push_pending_txq(hw, sta->txq[tid], arg);
+}
+
+void ath10k_mac_tx_push_pending(struct ath10k *ar)
+{
+ struct ieee80211_hw *hw = ar->hw;
+ struct ath10k_mac_tx_push_arg arg = {};
+
+ if (likely(!(ar->tx_paused & BIT(ATH10K_TX_PAUSE_Q_FLUSH_PENDING))))
+ return;
+
+ arg.hw = hw;
+ arg.more = false;
+
+ ieee80211_iterate_active_interfaces_atomic(hw,
+ IEEE80211_IFACE_ITER_NORMAL,
+ ath10k_mac_tx_push_pending_vif_iter,
+ &arg);
+ if (arg.more)
+ return;
+
+ ieee80211_iterate_stations_atomic(hw,
+ ath10k_mac_tx_push_pending_sta_iter,
+ &arg);
+ if (arg.more)
+ return;
+
+ spin_lock_bh(&ar->htt.tx_lock);
+ ath10k_mac_tx_unlock(ar, ATH10K_TX_PAUSE_Q_FLUSH_PENDING);
+ spin_unlock_bh(&ar->htt.tx_lock);
+}
+
/************/
/* Scanning */
/************/
diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h
index 53091588090d..453f606a250e 100644
--- a/drivers/net/wireless/ath/ath10k/mac.h
+++ b/drivers/net/wireless/ath/ath10k/mac.h
@@ -75,6 +75,7 @@ void ath10k_mac_tx_unlock(struct ath10k *ar, int reason);
void ath10k_mac_vif_tx_lock(struct ath10k_vif *arvif, int reason);
void ath10k_mac_vif_tx_unlock(struct ath10k_vif *arvif, int reason);
bool ath10k_mac_tx_frm_has_freq(struct ath10k *ar);
+void ath10k_mac_tx_push_pending(struct ath10k *ar);

static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
{
--
2.1.4


2016-01-21 13:45:40

by Michal Kazior

[permalink] [raw]
Subject: [PATCH 02/13] ath10k: unify txpath decision

Some future changes will need to determine final
tx method early on. Prepare the code.

Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/mac.c | 58 +++++++++++++++++++++++++++--------
1 file changed, 45 insertions(+), 13 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index a684718f6389..9abb52924432 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -2997,6 +2997,13 @@ static void ath10k_reg_notifier(struct wiphy *wiphy,
/* TX handlers */
/***************/

+enum ath10k_mac_tx_path {
+ ATH10K_MAC_TX_HTT,
+ ATH10K_MAC_TX_HTT_MGMT,
+ ATH10K_MAC_TX_WMI_MGMT,
+ ATH10K_MAC_TX_UNKNOWN,
+};
+
void ath10k_mac_tx_lock(struct ath10k *ar, int reason)
{
lockdep_assert_held(&ar->htt.tx_lock);
@@ -3330,27 +3337,52 @@ unlock:
return ret;
}

+static enum ath10k_mac_tx_path
+ath10k_mac_tx_h_get_txpath(struct ath10k *ar,
+ struct sk_buff *skb,
+ enum ath10k_hw_txrx_mode txmode)
+{
+ switch (txmode) {
+ case ATH10K_HW_TXRX_RAW:
+ case ATH10K_HW_TXRX_NATIVE_WIFI:
+ case ATH10K_HW_TXRX_ETHERNET:
+ return ATH10K_MAC_TX_HTT;
+ case ATH10K_HW_TXRX_MGMT:
+ if (test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
+ ar->fw_features))
+ return ATH10K_MAC_TX_WMI_MGMT;
+ else if (ar->htt.target_version_major >= 3)
+ return ATH10K_MAC_TX_HTT;
+ else
+ return ATH10K_MAC_TX_HTT_MGMT;
+ }
+
+ return ATH10K_MAC_TX_UNKNOWN;
+}
+
static int ath10k_mac_tx_submit(struct ath10k *ar,
enum ath10k_hw_txrx_mode txmode,
struct sk_buff *skb)
{
struct ath10k_htt *htt = &ar->htt;
- int ret = 0;
+ enum ath10k_mac_tx_path txpath;
+ int ret;

- switch (txmode) {
- case ATH10K_HW_TXRX_RAW:
- case ATH10K_HW_TXRX_NATIVE_WIFI:
- case ATH10K_HW_TXRX_ETHERNET:
+ txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);
+
+ switch (txpath) {
+ case ATH10K_MAC_TX_HTT:
ret = ath10k_htt_tx(htt, txmode, skb);
break;
- case ATH10K_HW_TXRX_MGMT:
- if (test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
- ar->fw_features))
- ret = ath10k_mac_tx_wmi_mgmt(ar, skb);
- else if (ar->htt.target_version_major >= 3)
- ret = ath10k_htt_tx(htt, txmode, skb);
- else
- ret = ath10k_htt_mgmt_tx(htt, skb);
+ case ATH10K_MAC_TX_HTT_MGMT:
+ ret = ath10k_htt_mgmt_tx(htt, skb);
+ break;
+ case ATH10K_MAC_TX_WMI_MGMT:
+ ret = ath10k_mac_tx_wmi_mgmt(ar, skb);
+ break;
+ case ATH10K_MAC_TX_UNKNOWN:
+ WARN_ON_ONCE(1);
+ ret = -EINVAL;
break;
}

--
2.1.4


2016-01-21 13:45:49

by Michal Kazior

[permalink] [raw]
Subject: [PATCH 09/13] ath10k: add txq placeholder

Driver will make use of this in the future.

Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/core.h | 4 ++++
drivers/net/wireless/ath/ath10k/mac.c | 1 +
2 files changed, 5 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index bc0e546c79d4..d09d6fdf1149 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -304,6 +304,10 @@ struct ath10k_peer {
struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1];
};

+struct ath10k_txq {
+ /* TBD */
+};
+
struct ath10k_sta {
struct ath10k_vif *arvif;

diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index d9566ba70af3..205ccf3760a3 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -7548,6 +7548,7 @@ int ath10k_mac_register(struct ath10k *ar)

ar->hw->vif_data_size = sizeof(struct ath10k_vif);
ar->hw->sta_data_size = sizeof(struct ath10k_sta);
+ ar->hw->txq_data_size = sizeof(struct ath10k_txq);

ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL;

--
2.1.4


2016-01-26 19:05:47

by Peter Oh

[permalink] [raw]
Subject: Re: [PATCH 13/13] ath10k: implement push-pull tx


On 01/26/2016 02:28 AM, Michal Kazior wrote:
> On 22 January 2016 at 08:47, Michal Kazior <[email protected]> wrote:
>> On 21 January 2016 at 18:40, Peter Oh <[email protected]> wrote:
>>> On 01/21/2016 05:46 AM, Michal Kazior wrote:
> [...]
>>>> - /* TODO: apply configuration */
>>>> + rcu_read_unlock();
>>>> +
>>>> + spin_lock_bh(&ar->htt.tx_lock);
>>>> + ath10k_mac_tx_lock(ar, ATH10K_TX_PAUSE_Q_FLUSH_PENDING);
>>>> + spin_unlock_bh(&ar->htt.tx_lock);
>>>> +
>>> Isn't it proved it break Mesh from working?
>> Yes, good point. I'm still working on this - I should've mentioned
>> that in the cover letter.
>>
>> Nonetheless I wanted to get the review process going for this patchset.
>>
>> I'll address the mesh problem in v2.
> For the record - the problem turned out to be a bug in mac80211 mesh
> fwding, see:
>
> https://patchwork.kernel.org/patch/8108711/
I've verified that the patch fixed the mesh issue.
Thank you Michal.
>
>
> Michał


2016-01-21 13:45:52

by Michal Kazior

[permalink] [raw]
Subject: [PATCH 11/13] ath10k: store txq in skb_cb

This will be necessary for later.

Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/core.h | 1 +
drivers/net/wireless/ath/ath10k/mac.c | 32 ++++++++++++++++++++++++++++++--
2 files changed, 31 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 38ed4bbd220b..5be56a8343bd 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -95,6 +95,7 @@ struct ath10k_skb_cb {
u8 eid;
u16 msdu_id;
struct ieee80211_vif *vif;
+ struct ieee80211_txq *txq;
} __packed;

struct ath10k_skb_rxcb {
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 42020c22eaed..639da03572e0 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -3311,6 +3311,7 @@ static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar,

static void ath10k_mac_tx_h_fill_cb(struct ath10k *ar,
struct ieee80211_vif *vif,
+ struct ieee80211_txq *txq,
struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (void *)skb->data;
@@ -3327,6 +3328,7 @@ static void ath10k_mac_tx_h_fill_cb(struct ath10k *ar,
cb->flags |= ATH10K_SKB_F_QOS;

cb->vif = vif;
+ cb->txq = txq;
}

bool ath10k_mac_tx_frm_has_freq(struct ath10k *ar)
@@ -3660,7 +3662,7 @@ static int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
return -ENOENT;
}

- ath10k_mac_tx_h_fill_cb(ar, vif, skb);
+ ath10k_mac_tx_h_fill_cb(ar, vif, txq, skb);

txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);
@@ -3792,6 +3794,27 @@ void ath10k_mac_tx_push_pending(struct ath10k *ar)
spin_unlock_bh(&ar->htt.tx_lock);
}

+static void ath10k_mac_txq_unref(struct ath10k *ar,
+ struct ieee80211_txq *txq)
+{
+ struct ath10k_skb_cb *cb;
+ struct sk_buff *msdu;
+ int msdu_id;
+
+ if (!txq)
+ return;
+
+ spin_lock_bh(&ar->htt.tx_lock);
+
+ idr_for_each_entry(&ar->htt.pending_tx, msdu, msdu_id) {
+ cb = ATH10K_SKB_CB(msdu);
+ if (cb->txq == txq)
+ cb->txq = NULL;
+ }
+
+ spin_unlock_bh(&ar->htt.tx_lock);
+}
+
/************/
/* Scanning */
/************/
@@ -3963,6 +3986,7 @@ static void ath10k_mac_op_tx(struct ieee80211_hw *hw,
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_vif *vif = info->control.vif;
struct ieee80211_sta *sta = control->sta;
+ struct ieee80211_txq *txq = NULL;
struct ieee80211_hdr *hdr = (void *)skb->data;
enum ath10k_hw_txrx_mode txmode;
enum ath10k_mac_tx_path txpath;
@@ -3971,7 +3995,7 @@ static void ath10k_mac_op_tx(struct ieee80211_hw *hw,
bool is_presp;
int ret;

- ath10k_mac_tx_h_fill_cb(ar, vif, skb);
+ ath10k_mac_tx_h_fill_cb(ar, vif, txq, skb);

txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);
@@ -5010,6 +5034,7 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
spin_unlock_bh(&ar->data_lock);

ath10k_peer_cleanup(ar, arvif->vdev_id);
+ ath10k_mac_txq_unref(ar, vif->txq);

if (vif->type == NL80211_IFTYPE_MONITOR) {
ar->monitor_arvif = NULL;
@@ -5872,6 +5897,9 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
}
spin_unlock_bh(&ar->data_lock);

+ for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
+ ath10k_mac_txq_unref(ar, sta->txq[i]);
+
if (!sta->tdls)
goto exit;

--
2.1.4


2016-01-22 00:43:54

by Ben Greear

[permalink] [raw]
Subject: Re: [PATCH 00/13] ath10k: implement push-pull tx model

On 01/21/2016 05:46 AM, Michal Kazior wrote:
> This adds support for the new logic where host
> tells firmware how many frames are queued for each
> station/tid and then firmware asks host to submit
> frames for given station/tid.

Just FYI, with your tree (including this patch-set),
the non-upstream 10.4.1-ish firmware that was giving me fits finally works,
at least enough to grab an IP address.

Thanks!
Ben


--
Ben Greear <[email protected]>
Candela Technologies Inc http://www.candelatech.com


2016-01-21 13:45:44

by Michal Kazior

[permalink] [raw]
Subject: [PATCH 05/13] ath10k: add fast peer_map lookup

The pull-push functionality of 10.4 will be based
on peer_id and tid. These will need to be mapped,
eventually, to ieee80211_txq to be used with
ieee80211_tx_dequeue().

Iterating over existing stations every time
peer_id needs to be mapped to a station would be
inefficient wrt CPU time.

The new firmware, which will be the only user of
the code flow-wise, will guarantee to use low
peer_ids first so despite peer_map's apparent huge
size d-cache thrashing should not be a problem.

Older firmware hot paths will effectively not use
peer_map.

Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/core.h | 4 ++
drivers/net/wireless/ath/ath10k/mac.c | 71 +++++++++++++++++++++++++++++++---
drivers/net/wireless/ath/ath10k/txrx.c | 2 +
3 files changed, 71 insertions(+), 6 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 4b19c71bf6a5..bc0e546c79d4 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -293,6 +293,9 @@ struct ath10k_dfs_stats {

struct ath10k_peer {
struct list_head list;
+ struct ieee80211_vif *vif;
+ struct ieee80211_sta *sta;
+
int vdev_id;
u8 addr[ETH_ALEN];
DECLARE_BITMAP(peer_ids, ATH10K_MAX_NUM_PEER_IDS);
@@ -780,6 +783,7 @@ struct ath10k {

struct list_head arvifs;
struct list_head peers;
+ struct ath10k_peer *peer_map[ATH10K_MAX_NUM_PEER_IDS];
wait_queue_head_t peer_mapping_wq;

/* protected by conf_mutex */
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index ec4fae8dcb92..00789afa5dec 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -618,10 +618,15 @@ ath10k_mac_get_any_chandef_iter(struct ieee80211_hw *hw,
*def = &conf->def;
}

-static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr,
+static int ath10k_peer_create(struct ath10k *ar,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ u32 vdev_id,
+ const u8 *addr,
enum wmi_peer_type peer_type)
{
struct ath10k_vif *arvif;
+ struct ath10k_peer *peer;
int num_peers = 0;
int ret;

@@ -650,6 +655,22 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr,
return ret;
}

+ spin_lock_bh(&ar->data_lock);
+
+ peer = ath10k_peer_find(ar, vdev_id, addr);
+ if (!peer) {
+ ath10k_warn(ar, "failed to find peer %pM on vdev %i after creation\n",
+ addr, vdev_id);
+ ath10k_wmi_peer_delete(ar, vdev_id, addr);
+ spin_unlock_bh(&ar->data_lock);
+ return -ENOENT;
+ }
+
+ peer->vif = vif;
+ peer->sta = sta;
+
+ spin_unlock_bh(&ar->data_lock);
+
ar->num_peers++;

return 0;
@@ -731,6 +752,7 @@ static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
{
struct ath10k_peer *peer, *tmp;
+ int peer_id;

lockdep_assert_held(&ar->conf_mutex);

@@ -742,6 +764,11 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
ath10k_warn(ar, "removing stale peer %pM from vdev_id %d\n",
peer->addr, vdev_id);

+ for_each_set_bit(peer_id, peer->peer_ids,
+ ATH10K_MAX_NUM_PEER_IDS) {
+ ar->peer_map[peer_id] = NULL;
+ }
+
list_del(&peer->list);
kfree(peer);
ar->num_peers--;
@@ -3510,7 +3537,8 @@ void ath10k_offchan_tx_work(struct work_struct *work)
peer_addr, vdev_id);

if (!peer) {
- ret = ath10k_peer_create(ar, vdev_id, peer_addr,
+ ret = ath10k_peer_create(ar, NULL, NULL, vdev_id,
+ peer_addr,
WMI_PEER_TYPE_DEFAULT);
if (ret)
ath10k_warn(ar, "failed to create peer %pM on vdev %d: %d\n",
@@ -4599,8 +4627,8 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,

if (arvif->vdev_type == WMI_VDEV_TYPE_AP ||
arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
- ret = ath10k_peer_create(ar, arvif->vdev_id, vif->addr,
- WMI_PEER_TYPE_DEFAULT);
+ ret = ath10k_peer_create(ar, vif, NULL, arvif->vdev_id,
+ vif->addr, WMI_PEER_TYPE_DEFAULT);
if (ret) {
ath10k_warn(ar, "failed to create vdev %i peer for AP/IBSS: %d\n",
arvif->vdev_id, ret);
@@ -4734,7 +4762,9 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
{
struct ath10k *ar = hw->priv;
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+ struct ath10k_peer *peer;
int ret;
+ int i;

cancel_work_sync(&arvif->ap_csa_work);
cancel_delayed_work_sync(&arvif->connection_loss_work);
@@ -4788,6 +4818,20 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
spin_unlock_bh(&ar->data_lock);
}

+ spin_lock_bh(&ar->data_lock);
+ for (i = 0; i < ARRAY_SIZE(ar->peer_map); i++) {
+ peer = ar->peer_map[i];
+ if (!peer)
+ continue;
+
+ if (peer->vif == vif) {
+ ath10k_warn(ar, "found vif peer %pM entry on vdev %i after it was supposedly removed\n",
+ vif->addr, arvif->vdev_id);
+ peer->vif = NULL;
+ }
+ }
+ spin_unlock_bh(&ar->data_lock);
+
ath10k_peer_cleanup(ar, arvif->vdev_id);

if (vif->type == NL80211_IFTYPE_MONITOR) {
@@ -5507,6 +5551,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
struct ath10k_peer *peer;
int ret = 0;
+ int i;

if (old_state == IEEE80211_STA_NOTEXIST &&
new_state == IEEE80211_STA_NONE) {
@@ -5547,8 +5592,8 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
if (sta->tdls)
peer_type = WMI_PEER_TYPE_TDLS;

- ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr,
- peer_type);
+ ret = ath10k_peer_create(ar, vif, sta, arvif->vdev_id,
+ sta->addr, peer_type);
if (ret) {
ath10k_warn(ar, "failed to add peer %pM for vdev %d when adding a new sta: %i\n",
sta->addr, arvif->vdev_id, ret);
@@ -5636,6 +5681,20 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,

ath10k_mac_dec_num_stations(arvif, sta);

+ spin_lock_bh(&ar->data_lock);
+ for (i = 0; i < ARRAY_SIZE(ar->peer_map); i++) {
+ peer = ar->peer_map[i];
+ if (!peer)
+ continue;
+
+ if (peer->sta == sta) {
+ ath10k_warn(ar, "found sta peer %pM entry on vdev %i after it was supposedly removed\n",
+ sta->addr, arvif->vdev_id);
+ peer->sta = NULL;
+ }
+ }
+ spin_unlock_bh(&ar->data_lock);
+
if (!sta->tdls)
goto exit;

diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c
index 118586ece20e..202e5192235b 100644
--- a/drivers/net/wireless/ath/ath10k/txrx.c
+++ b/drivers/net/wireless/ath/ath10k/txrx.c
@@ -203,6 +203,7 @@ void ath10k_peer_map_event(struct ath10k_htt *htt,
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt peer map vdev %d peer %pM id %d\n",
ev->vdev_id, ev->addr, ev->peer_id);

+ ar->peer_map[ev->peer_id] = peer;
set_bit(ev->peer_id, peer->peer_ids);
exit:
spin_unlock_bh(&ar->data_lock);
@@ -225,6 +226,7 @@ void ath10k_peer_unmap_event(struct ath10k_htt *htt,
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt peer unmap vdev %d peer %pM id %d\n",
peer->vdev_id, peer->addr, ev->peer_id);

+ ar->peer_map[ev->peer_id] = NULL;
clear_bit(ev->peer_id, peer->peer_ids);

if (bitmap_empty(peer->peer_ids, ATH10K_MAX_NUM_PEER_IDS)) {
--
2.1.4


2016-01-22 07:47:09

by Michal Kazior

[permalink] [raw]
Subject: Re: [PATCH 13/13] ath10k: implement push-pull tx

On 21 January 2016 at 18:40, Peter Oh <[email protected]> wrote:
>
> On 01/21/2016 05:46 AM, Michal Kazior wrote:
>>
>> The current/old tx path design was that host, at
>> its own leisure, pushed tx frames to the device.
>> For HTT there was ~1000-1400 msdu queue depth.
>>
>> After reaching that limit the driver would request
>> mac80211 to stop queues. There was little control
>> over what packets got in there as far as
>> DA/RA was considered so it was rather easy to
>> starve per-station traffic flows.
>>
>> With MU-MIMO this became a significant problem
>> because the queue depth was insufficient to buffer
>> frames from multiple clients (which could have
>> different signal quality and capabilities) in an
>> efficient fashion.
>>
>> Hence the new tx path in 10.4 was introduced: a
>> pull-push mode.
>>
>> Firmware and host can share tx queue state via
>> DMA. The state is logically a 2 dimensional array
>> addressed via peer_id+tid pair. Each entry is a
>> counter (either number of bytes or packets. Host
>> keeps it updated and firmware uses it for
>> scheduling Tx pull requests to host.
>>
>> This allows MU-MIMO to become a lot more effective
>> with 10+ clients.
>>
>> Signed-off-by: Michal Kazior <[email protected]>
>> ---
>> drivers/net/wireless/ath/ath10k/core.h | 1 +
>> drivers/net/wireless/ath/ath10k/htt.h | 6 ++
>> drivers/net/wireless/ath/ath10k/htt_rx.c | 117
>> ++++++++++++++++++++++++++++---
>> drivers/net/wireless/ath/ath10k/htt_tx.c | 39 ++++++++---
>> drivers/net/wireless/ath/ath10k/mac.c | 48 +++++++++++--
>> drivers/net/wireless/ath/ath10k/mac.h | 5 ++
>> 6 files changed, 192 insertions(+), 24 deletions(-)
>>
>> diff --git a/drivers/net/wireless/ath/ath10k/core.h
>> b/drivers/net/wireless/ath/ath10k/core.h
>> index f51887c6be74..ab8cbccc0f4b 100644
>> --- a/drivers/net/wireless/ath/ath10k/core.h
>> +++ b/drivers/net/wireless/ath/ath10k/core.h
>> @@ -307,6 +307,7 @@ struct ath10k_peer {
>> struct ath10k_txq {
>> unsigned long num_fw_queued;
>> + unsigned long num_push_allowed;
>> };
>> struct ath10k_sta {
>> diff --git a/drivers/net/wireless/ath/ath10k/htt.h
>> b/drivers/net/wireless/ath/ath10k/htt.h
>> index b1e40f44e76b..02cf55d306e8 100644
>> --- a/drivers/net/wireless/ath/ath10k/htt.h
>> +++ b/drivers/net/wireless/ath/ath10k/htt.h
>> @@ -1652,6 +1652,7 @@ struct ath10k_htt {
>> struct sk_buff_head tx_compl_q;
>> struct sk_buff_head rx_compl_q;
>> struct sk_buff_head rx_in_ord_compl_q;
>> + struct sk_buff_head tx_fetch_ind_q;
>> /* rx_status template */
>> struct ieee80211_rx_status rx_status;
>> @@ -1670,8 +1671,10 @@ struct ath10k_htt {
>> bool enabled;
>> struct htt_q_state *vaddr;
>> dma_addr_t paddr;
>> + u16 num_push_allowed;
>> u16 num_peers;
>> u16 num_tids;
>> + enum htt_tx_mode_switch_mode mode;
>> enum htt_q_depth_type type;
>> } tx_q_state;
>> };
>> @@ -1761,6 +1764,9 @@ int ath10k_htt_tx_fetch_resp(struct ath10k *ar,
>> void ath10k_htt_tx_txq_update(struct ieee80211_hw *hw,
>> struct ieee80211_txq *txq);
>> +void ath10k_htt_tx_txq_recalc(struct ieee80211_hw *hw,
>> + struct ieee80211_txq *txq);
>> +void ath10k_htt_tx_txq_sync(struct ath10k *ar);
>> void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt,
>> bool is_mgmt);
>> int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt,
>> diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c
>> b/drivers/net/wireless/ath/ath10k/htt_rx.c
>> index 6e3d95c95568..827c8369b589 100644
>> --- a/drivers/net/wireless/ath/ath10k/htt_rx.c
>> +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
>> @@ -229,6 +229,7 @@ void ath10k_htt_rx_free(struct ath10k_htt *htt)
>> skb_queue_purge(&htt->tx_compl_q);
>> skb_queue_purge(&htt->rx_compl_q);
>> skb_queue_purge(&htt->rx_in_ord_compl_q);
>> + skb_queue_purge(&htt->tx_fetch_ind_q);
>> ath10k_htt_rx_ring_free(htt);
>> @@ -569,6 +570,7 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt)
>> skb_queue_head_init(&htt->tx_compl_q);
>> skb_queue_head_init(&htt->rx_compl_q);
>> skb_queue_head_init(&htt->rx_in_ord_compl_q);
>> + skb_queue_head_init(&htt->tx_fetch_ind_q);
>> tasklet_init(&htt->txrx_compl_task, ath10k_htt_txrx_compl_task,
>> (unsigned long)htt);
>> @@ -2004,16 +2006,21 @@ static void
>> ath10k_htt_rx_tx_fetch_resp_id_confirm(struct ath10k *ar,
>> static void ath10k_htt_rx_tx_fetch_ind(struct ath10k *ar, struct
>> sk_buff
>> *skb)
>> {
>> + struct ieee80211_hw *hw = ar->hw;
>> + struct ieee80211_txq *txq;
>> struct htt_resp *resp = (struct htt_resp *)skb->data;
>> struct htt_tx_fetch_record *record;
>> size_t len;
>> size_t max_num_bytes;
>> size_t max_num_msdus;
>> + size_t num_bytes;
>> + size_t num_msdus;
>> const __le32 *resp_ids;
>> u16 num_records;
>> u16 num_resp_ids;
>> u16 peer_id;
>> u8 tid;
>> + int ret;
>> int i;
>> ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch ind\n");
>> @@ -2039,7 +2046,17 @@ static void ath10k_htt_rx_tx_fetch_ind(struct
>> ath10k *ar, struct sk_buff *skb)
>> num_records, num_resp_ids,
>> le16_to_cpu(resp->tx_fetch_ind.fetch_seq_num));
>> - /* TODO: runtime sanity checks */
>> + if (!ar->htt.tx_q_state.enabled) {
>> + ath10k_warn(ar, "received unexpected tx_fetch_ind event:
>> not enabled\n");
>> + return;
>> + }
>> +
>> + if (ar->htt.tx_q_state.mode == HTT_TX_MODE_SWITCH_PUSH) {
>> + ath10k_warn(ar, "received unexpected tx_fetch_ind event:
>> in push mode\n");
>> + return;
>> + }
>> +
>> + rcu_read_lock();
>> for (i = 0; i < num_records; i++) {
>> record = &resp->tx_fetch_ind.records[i];
>> @@ -2060,13 +2077,56 @@ static void ath10k_htt_rx_tx_fetch_ind(struct
>> ath10k *ar, struct sk_buff *skb)
>> continue;
>> }
>> - /* TODO: dequeue and submit tx to device */
>> + spin_lock_bh(&ar->data_lock);
>> + txq = ath10k_mac_txq_lookup(ar, peer_id, tid);
>> + spin_unlock_bh(&ar->data_lock);
>> +
>> + /* It is okay to release the lock and use txq because RCU
>> read
>> + * lock is held.
>> + */
>> +
>> + if (unlikely(!txq)) {
>> + ath10k_warn(ar, "failed to lookup txq for peer_id
>> %hu tid %hhu\n",
>> + peer_id, tid);
>> + continue;
>> + }
>> +
>> + num_msdus = 0;
>> + num_bytes = 0;
>> +
>> + while (num_msdus < max_num_msdus &&
>> + num_bytes < max_num_bytes) {
>> + ret = ath10k_mac_tx_push_txq(hw, txq);
>> + if (ret < 0)
>> + break;
>> +
>> + num_msdus++;
>> + num_bytes += ret;
>> + }
>> +
>> + record->num_msdus = cpu_to_le16(num_msdus);
>> + record->num_bytes = cpu_to_le32(num_bytes);
>> +
>> + ath10k_htt_tx_txq_recalc(hw, txq);
>> }
>> + rcu_read_unlock();
>> +
>> resp_ids =
>> ath10k_htt_get_tx_fetch_ind_resp_ids(&resp->tx_fetch_ind);
>> ath10k_htt_rx_tx_fetch_resp_id_confirm(ar, resp_ids,
>> num_resp_ids);
>> - /* TODO: generate and send fetch response to device */
>> + ret = ath10k_htt_tx_fetch_resp(ar,
>> + resp->tx_fetch_ind.token,
>> + resp->tx_fetch_ind.fetch_seq_num,
>> + resp->tx_fetch_ind.records,
>> + num_records);
>> + if (unlikely(ret)) {
>> + ath10k_warn(ar, "failed to submit tx fetch resp for token
>> 0x%08x: %d\n",
>> + le32_to_cpu(resp->tx_fetch_ind.token), ret);
>> + /* FIXME: request fw restart */
>> + }
>> +
>> + ath10k_htt_tx_txq_sync(ar);
>> }
>> static void ath10k_htt_rx_tx_fetch_confirm(struct ath10k *ar,
>> @@ -2102,6 +2162,8 @@ static void ath10k_htt_rx_tx_mode_switch_ind(struct
>> ath10k *ar,
>> {
>> const struct htt_resp *resp = (void *)skb->data;
>> const struct htt_tx_mode_switch_record *record;
>> + struct ieee80211_txq *txq;
>> + struct ath10k_txq *artxq;
>> size_t len;
>> size_t num_records;
>> enum htt_tx_mode_switch_mode mode;
>> @@ -2153,7 +2215,11 @@ static void ath10k_htt_rx_tx_mode_switch_ind(struct
>> ath10k *ar,
>> if (!enable)
>> return;
>> - /* TODO: apply configuration */
>> + ar->htt.tx_q_state.enabled = enable;
>> + ar->htt.tx_q_state.mode = mode;
>> + ar->htt.tx_q_state.num_push_allowed = threshold;
>> +
>> + rcu_read_lock();
>> for (i = 0; i < num_records; i++) {
>> record = &resp->tx_mode_switch_ind.records[i];
>> @@ -2168,10 +2234,33 @@ static void
>> ath10k_htt_rx_tx_mode_switch_ind(struct ath10k *ar,
>> continue;
>> }
>> - /* TODO: apply configuration */
>> + spin_lock_bh(&ar->data_lock);
>> + txq = ath10k_mac_txq_lookup(ar, peer_id, tid);
>> + spin_unlock_bh(&ar->data_lock);
>> +
>> + /* It is okay to release the lock and use txq because RCU
>> read
>> + * lock is held.
>> + */
>> +
>> + if (unlikely(!txq)) {
>> + ath10k_warn(ar, "failed to lookup txq for peer_id
>> %hu tid %hhu\n",
>> + peer_id, tid);
>> + continue;
>> + }
>> +
>> + spin_lock_bh(&ar->htt.tx_lock);
>> + artxq = (void *)txq->drv_priv;
>> + artxq->num_push_allowed =
>> le16_to_cpu(record->num_max_msdus);
>> + spin_unlock_bh(&ar->htt.tx_lock);
>> }
>> - /* TODO: apply configuration */
>> + rcu_read_unlock();
>> +
>> + spin_lock_bh(&ar->htt.tx_lock);
>> + ath10k_mac_tx_lock(ar, ATH10K_TX_PAUSE_Q_FLUSH_PENDING);
>> + spin_unlock_bh(&ar->htt.tx_lock);
>> +
>
> Isn't it proved it break Mesh from working?

Yes, good point. I'm still working on this - I should've mentioned
that in the cover letter.

Nonetheless I wanted to get the review process going for this patchset.

I'll address the mesh problem in v2.


Michał

2016-01-21 13:45:55

by Michal Kazior

[permalink] [raw]
Subject: [PATCH 13/13] ath10k: implement push-pull tx

The current/old tx path design was that host, at
its own leisure, pushed tx frames to the device.
For HTT there was ~1000-1400 msdu queue depth.

After reaching that limit the driver would request
mac80211 to stop queues. There was little control
over what packets got in there as far as
DA/RA was considered so it was rather easy to
starve per-station traffic flows.

With MU-MIMO this became a significant problem
because the queue depth was insufficient to buffer
frames from multiple clients (which could have
different signal quality and capabilities) in an
efficient fashion.

Hence the new tx path in 10.4 was introduced: a
pull-push mode.

Firmware and host can share tx queue state via
DMA. The state is logically a 2 dimensional array
addressed via peer_id+tid pair. Each entry is a
counter (either number of bytes or packets. Host
keeps it updated and firmware uses it for
scheduling Tx pull requests to host.

This allows MU-MIMO to become a lot more effective
with 10+ clients.

Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/core.h | 1 +
drivers/net/wireless/ath/ath10k/htt.h | 6 ++
drivers/net/wireless/ath/ath10k/htt_rx.c | 117 ++++++++++++++++++++++++++++---
drivers/net/wireless/ath/ath10k/htt_tx.c | 39 ++++++++---
drivers/net/wireless/ath/ath10k/mac.c | 48 +++++++++++--
drivers/net/wireless/ath/ath10k/mac.h | 5 ++
6 files changed, 192 insertions(+), 24 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index f51887c6be74..ab8cbccc0f4b 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -307,6 +307,7 @@ struct ath10k_peer {

struct ath10k_txq {
unsigned long num_fw_queued;
+ unsigned long num_push_allowed;
};

struct ath10k_sta {
diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
index b1e40f44e76b..02cf55d306e8 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -1652,6 +1652,7 @@ struct ath10k_htt {
struct sk_buff_head tx_compl_q;
struct sk_buff_head rx_compl_q;
struct sk_buff_head rx_in_ord_compl_q;
+ struct sk_buff_head tx_fetch_ind_q;

/* rx_status template */
struct ieee80211_rx_status rx_status;
@@ -1670,8 +1671,10 @@ struct ath10k_htt {
bool enabled;
struct htt_q_state *vaddr;
dma_addr_t paddr;
+ u16 num_push_allowed;
u16 num_peers;
u16 num_tids;
+ enum htt_tx_mode_switch_mode mode;
enum htt_q_depth_type type;
} tx_q_state;
};
@@ -1761,6 +1764,9 @@ int ath10k_htt_tx_fetch_resp(struct ath10k *ar,

void ath10k_htt_tx_txq_update(struct ieee80211_hw *hw,
struct ieee80211_txq *txq);
+void ath10k_htt_tx_txq_recalc(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq);
+void ath10k_htt_tx_txq_sync(struct ath10k *ar);
void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt,
bool is_mgmt);
int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt,
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index 6e3d95c95568..827c8369b589 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -229,6 +229,7 @@ void ath10k_htt_rx_free(struct ath10k_htt *htt)
skb_queue_purge(&htt->tx_compl_q);
skb_queue_purge(&htt->rx_compl_q);
skb_queue_purge(&htt->rx_in_ord_compl_q);
+ skb_queue_purge(&htt->tx_fetch_ind_q);

ath10k_htt_rx_ring_free(htt);

@@ -569,6 +570,7 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt)
skb_queue_head_init(&htt->tx_compl_q);
skb_queue_head_init(&htt->rx_compl_q);
skb_queue_head_init(&htt->rx_in_ord_compl_q);
+ skb_queue_head_init(&htt->tx_fetch_ind_q);

tasklet_init(&htt->txrx_compl_task, ath10k_htt_txrx_compl_task,
(unsigned long)htt);
@@ -2004,16 +2006,21 @@ static void ath10k_htt_rx_tx_fetch_resp_id_confirm(struct ath10k *ar,

static void ath10k_htt_rx_tx_fetch_ind(struct ath10k *ar, struct sk_buff *skb)
{
+ struct ieee80211_hw *hw = ar->hw;
+ struct ieee80211_txq *txq;
struct htt_resp *resp = (struct htt_resp *)skb->data;
struct htt_tx_fetch_record *record;
size_t len;
size_t max_num_bytes;
size_t max_num_msdus;
+ size_t num_bytes;
+ size_t num_msdus;
const __le32 *resp_ids;
u16 num_records;
u16 num_resp_ids;
u16 peer_id;
u8 tid;
+ int ret;
int i;

ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch ind\n");
@@ -2039,7 +2046,17 @@ static void ath10k_htt_rx_tx_fetch_ind(struct ath10k *ar, struct sk_buff *skb)
num_records, num_resp_ids,
le16_to_cpu(resp->tx_fetch_ind.fetch_seq_num));

- /* TODO: runtime sanity checks */
+ if (!ar->htt.tx_q_state.enabled) {
+ ath10k_warn(ar, "received unexpected tx_fetch_ind event: not enabled\n");
+ return;
+ }
+
+ if (ar->htt.tx_q_state.mode == HTT_TX_MODE_SWITCH_PUSH) {
+ ath10k_warn(ar, "received unexpected tx_fetch_ind event: in push mode\n");
+ return;
+ }
+
+ rcu_read_lock();

for (i = 0; i < num_records; i++) {
record = &resp->tx_fetch_ind.records[i];
@@ -2060,13 +2077,56 @@ static void ath10k_htt_rx_tx_fetch_ind(struct ath10k *ar, struct sk_buff *skb)
continue;
}

- /* TODO: dequeue and submit tx to device */
+ spin_lock_bh(&ar->data_lock);
+ txq = ath10k_mac_txq_lookup(ar, peer_id, tid);
+ spin_unlock_bh(&ar->data_lock);
+
+ /* It is okay to release the lock and use txq because RCU read
+ * lock is held.
+ */
+
+ if (unlikely(!txq)) {
+ ath10k_warn(ar, "failed to lookup txq for peer_id %hu tid %hhu\n",
+ peer_id, tid);
+ continue;
+ }
+
+ num_msdus = 0;
+ num_bytes = 0;
+
+ while (num_msdus < max_num_msdus &&
+ num_bytes < max_num_bytes) {
+ ret = ath10k_mac_tx_push_txq(hw, txq);
+ if (ret < 0)
+ break;
+
+ num_msdus++;
+ num_bytes += ret;
+ }
+
+ record->num_msdus = cpu_to_le16(num_msdus);
+ record->num_bytes = cpu_to_le32(num_bytes);
+
+ ath10k_htt_tx_txq_recalc(hw, txq);
}

+ rcu_read_unlock();
+
resp_ids = ath10k_htt_get_tx_fetch_ind_resp_ids(&resp->tx_fetch_ind);
ath10k_htt_rx_tx_fetch_resp_id_confirm(ar, resp_ids, num_resp_ids);

- /* TODO: generate and send fetch response to device */
+ ret = ath10k_htt_tx_fetch_resp(ar,
+ resp->tx_fetch_ind.token,
+ resp->tx_fetch_ind.fetch_seq_num,
+ resp->tx_fetch_ind.records,
+ num_records);
+ if (unlikely(ret)) {
+ ath10k_warn(ar, "failed to submit tx fetch resp for token 0x%08x: %d\n",
+ le32_to_cpu(resp->tx_fetch_ind.token), ret);
+ /* FIXME: request fw restart */
+ }
+
+ ath10k_htt_tx_txq_sync(ar);
}

static void ath10k_htt_rx_tx_fetch_confirm(struct ath10k *ar,
@@ -2102,6 +2162,8 @@ static void ath10k_htt_rx_tx_mode_switch_ind(struct ath10k *ar,
{
const struct htt_resp *resp = (void *)skb->data;
const struct htt_tx_mode_switch_record *record;
+ struct ieee80211_txq *txq;
+ struct ath10k_txq *artxq;
size_t len;
size_t num_records;
enum htt_tx_mode_switch_mode mode;
@@ -2153,7 +2215,11 @@ static void ath10k_htt_rx_tx_mode_switch_ind(struct ath10k *ar,
if (!enable)
return;

- /* TODO: apply configuration */
+ ar->htt.tx_q_state.enabled = enable;
+ ar->htt.tx_q_state.mode = mode;
+ ar->htt.tx_q_state.num_push_allowed = threshold;
+
+ rcu_read_lock();

for (i = 0; i < num_records; i++) {
record = &resp->tx_mode_switch_ind.records[i];
@@ -2168,10 +2234,33 @@ static void ath10k_htt_rx_tx_mode_switch_ind(struct ath10k *ar,
continue;
}

- /* TODO: apply configuration */
+ spin_lock_bh(&ar->data_lock);
+ txq = ath10k_mac_txq_lookup(ar, peer_id, tid);
+ spin_unlock_bh(&ar->data_lock);
+
+ /* It is okay to release the lock and use txq because RCU read
+ * lock is held.
+ */
+
+ if (unlikely(!txq)) {
+ ath10k_warn(ar, "failed to lookup txq for peer_id %hu tid %hhu\n",
+ peer_id, tid);
+ continue;
+ }
+
+ spin_lock_bh(&ar->htt.tx_lock);
+ artxq = (void *)txq->drv_priv;
+ artxq->num_push_allowed = le16_to_cpu(record->num_max_msdus);
+ spin_unlock_bh(&ar->htt.tx_lock);
}

- /* TODO: apply configuration */
+ rcu_read_unlock();
+
+ spin_lock_bh(&ar->htt.tx_lock);
+ ath10k_mac_tx_lock(ar, ATH10K_TX_PAUSE_Q_FLUSH_PENDING);
+ spin_unlock_bh(&ar->htt.tx_lock);
+
+ ath10k_mac_tx_push_pending(ar);
}

void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
@@ -2317,8 +2406,9 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
case HTT_T2H_MSG_TYPE_AGGR_CONF:
break;
case HTT_T2H_MSG_TYPE_TX_FETCH_IND:
- ath10k_htt_rx_tx_fetch_ind(ar, skb);
- break;
+ skb_queue_tail(&htt->tx_fetch_ind_q, skb);
+ tasklet_schedule(&htt->txrx_compl_task);
+ return;
case HTT_T2H_MSG_TYPE_TX_FETCH_CONFIRM:
ath10k_htt_rx_tx_fetch_confirm(ar, skb);
break;
@@ -2356,21 +2446,32 @@ static void ath10k_htt_txrx_compl_task(unsigned long ptr)
struct ath10k_htt *htt = (struct ath10k_htt *)ptr;
struct ath10k *ar = htt->ar;
struct sk_buff_head tx_q;
+ struct sk_buff_head tx_ind_q;
struct htt_resp *resp;
struct sk_buff *skb;
unsigned long flags;

__skb_queue_head_init(&tx_q);
+ __skb_queue_head_init(&tx_ind_q);

spin_lock_irqsave(&htt->tx_compl_q.lock, flags);
skb_queue_splice_init(&htt->tx_compl_q, &tx_q);
spin_unlock_irqrestore(&htt->tx_compl_q.lock, flags);

+ spin_lock_irqsave(&htt->tx_fetch_ind_q.lock, flags);
+ skb_queue_splice_init(&htt->tx_fetch_ind_q, &tx_ind_q);
+ spin_unlock_irqrestore(&htt->tx_fetch_ind_q.lock, flags);
+
while ((skb = __skb_dequeue(&tx_q))) {
ath10k_htt_rx_frm_tx_compl(htt->ar, skb);
dev_kfree_skb_any(skb);
}

+ while ((skb = __skb_dequeue(&tx_ind_q))) {
+ ath10k_htt_rx_tx_fetch_ind(ar, skb);
+ dev_kfree_skb_any(skb);
+ }
+
ath10k_mac_tx_push_pending(ar);

spin_lock_bh(&htt->rx_ring.lock);
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
index 5ef0a1b3773c..00877ff6861e 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -62,6 +62,9 @@ static void __ath10k_htt_tx_txq_recalc(struct ieee80211_hw *hw,
if (!ar->htt.tx_q_state.enabled)
return;

+ if (ar->htt.tx_q_state.mode != HTT_TX_MODE_SWITCH_PUSH_PULL)
+ return;
+
if (txq->sta)
peer_id = arsta->peer_id;
else
@@ -97,6 +100,9 @@ static void __ath10k_htt_tx_txq_sync(struct ath10k *ar)
if (!ar->htt.tx_q_state.enabled)
return;

+ if (ar->htt.tx_q_state.mode != HTT_TX_MODE_SWITCH_PUSH_PULL)
+ return;
+
seq = le32_to_cpu(ar->htt.tx_q_state.vaddr->seq);
seq++;
ar->htt.tx_q_state.vaddr->seq = cpu_to_le32(seq);
@@ -111,6 +117,23 @@ static void __ath10k_htt_tx_txq_sync(struct ath10k *ar)
DMA_TO_DEVICE);
}

+void ath10k_htt_tx_txq_recalc(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+{
+ struct ath10k *ar = hw->priv;
+
+ spin_lock_bh(&ar->htt.tx_lock);
+ __ath10k_htt_tx_txq_recalc(hw, txq);
+ spin_unlock_bh(&ar->htt.tx_lock);
+}
+
+void ath10k_htt_tx_txq_sync(struct ath10k *ar)
+{
+ spin_lock_bh(&ar->htt.tx_lock);
+ __ath10k_htt_tx_txq_sync(ar);
+ spin_unlock_bh(&ar->htt.tx_lock);
+}
+
void ath10k_htt_tx_txq_update(struct ieee80211_hw *hw,
struct ieee80211_txq *txq)
{
@@ -634,10 +657,14 @@ int ath10k_htt_tx_fetch_resp(struct ath10k *ar,
{
struct sk_buff *skb;
struct htt_cmd *cmd;
- u16 resp_id;
+ const u16 resp_id = 0;
int len = 0;
int ret;

+ /* Response IDs are echo-ed back only for host driver convienence
+ * purposes. They aren't used for anything in the driver yet so use 0.
+ */
+
len += sizeof(cmd->hdr);
len += sizeof(cmd->tx_fetch_resp);
len += sizeof(cmd->tx_fetch_resp.records[0]) * num_records;
@@ -646,11 +673,6 @@ int ath10k_htt_tx_fetch_resp(struct ath10k *ar,
if (!skb)
return -ENOMEM;

- resp_id = 0; /* TODO: allocate resp_id */
- ret = 0;
- if (ret)
- goto err_free_skb;
-
skb_put(skb, len);
cmd = (struct htt_cmd *)skb->data;
cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_TX_FETCH_RESP;
@@ -665,14 +687,11 @@ int ath10k_htt_tx_fetch_resp(struct ath10k *ar,
ret = ath10k_htc_send(&ar->htc, ar->htt.eid, skb);
if (ret) {
ath10k_warn(ar, "failed to submit htc command: %d\n", ret);
- goto err_free_resp_id;
+ goto err_free_skb;
}

return 0;

-err_free_resp_id:
- (void)resp_id; /* TODO: free resp_id */
-
err_free_skb:
dev_kfree_skb_any(skb);

diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 0dc16de1d9fb..ee94c83b5128 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -3629,11 +3629,25 @@ void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work)
static bool ath10k_mac_tx_can_push(struct ieee80211_hw *hw,
struct ieee80211_txq *txq)
{
- return 1; /* TBD */
+ struct ath10k *ar = hw->priv;
+ struct ath10k_txq *artxq = (void *)txq->drv_priv;
+
+ /* No need to get locks */
+
+ if (ar->htt.tx_q_state.mode == HTT_TX_MODE_SWITCH_PUSH)
+ return true;
+
+ if (ar->htt.num_pending_tx < ar->htt.tx_q_state.num_push_allowed)
+ return true;
+
+ if (artxq->num_fw_queued < artxq->num_push_allowed)
+ return true;
+
+ return false;
}

-static int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
- struct ieee80211_txq *txq)
+int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
{
const bool is_mgmt = false;
const bool is_presp = false;
@@ -3645,6 +3659,7 @@ static int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
enum ath10k_hw_txrx_mode txmode;
enum ath10k_mac_tx_path txpath;
struct sk_buff *skb;
+ size_t skb_len;
int ret;

spin_lock_bh(&ar->htt.tx_lock);
@@ -3665,6 +3680,7 @@ static int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,

ath10k_mac_tx_h_fill_cb(ar, vif, txq, skb);

+ skb_len = skb->len;
txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);

@@ -3683,7 +3699,7 @@ static int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
artxq->num_fw_queued++;
spin_unlock_bh(&ar->htt.tx_lock);

- return 0;
+ return skb_len;
}

static void ath10k_mac_tx_push(struct ieee80211_hw *hw,
@@ -3693,7 +3709,7 @@ static void ath10k_mac_tx_push(struct ieee80211_hw *hw,
int ret;

ret = ath10k_mac_tx_push_txq(hw, txq);
- if (unlikely(ret)) {
+ if (unlikely(ret < 0)) {
if (txq->sta)
ath10k_warn(ar, "failed to push tx to station %pM tid %hhu: %d\n",
txq->sta->addr, txq->tid, ret);
@@ -3738,7 +3754,7 @@ static void ath10k_mac_tx_push_pending_txq(struct ieee80211_hw *hw,
break;

ret = ath10k_mac_tx_push_txq(hw, txq);
- if (ret)
+ if (ret < 0)
break;
}

@@ -3820,6 +3836,26 @@ static void ath10k_mac_txq_unref(struct ath10k *ar,
spin_unlock_bh(&ar->htt.tx_lock);
}

+struct ieee80211_txq *ath10k_mac_txq_lookup(struct ath10k *ar,
+ u16 peer_id,
+ u8 tid)
+{
+ struct ath10k_peer *peer;
+
+ lockdep_assert_held(&ar->data_lock);
+
+ peer = ar->peer_map[peer_id];
+ if (!peer)
+ return NULL;
+
+ if (peer->sta)
+ return peer->sta->txq[tid];
+ else if (peer->vif)
+ return peer->vif->txq;
+ else
+ return NULL;
+}
+
/************/
/* Scanning */
/************/
diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h
index 453f606a250e..2c3327beb445 100644
--- a/drivers/net/wireless/ath/ath10k/mac.h
+++ b/drivers/net/wireless/ath/ath10k/mac.h
@@ -76,6 +76,11 @@ void ath10k_mac_vif_tx_lock(struct ath10k_vif *arvif, int reason);
void ath10k_mac_vif_tx_unlock(struct ath10k_vif *arvif, int reason);
bool ath10k_mac_tx_frm_has_freq(struct ath10k *ar);
void ath10k_mac_tx_push_pending(struct ath10k *ar);
+int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq);
+struct ieee80211_txq *ath10k_mac_txq_lookup(struct ath10k *ar,
+ u16 peer_id,
+ u8 tid);

static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
{
--
2.1.4


2016-01-21 13:45:45

by Michal Kazior

[permalink] [raw]
Subject: [PATCH 06/13] ath10k: add new htt message generation/parsing logic

This merely adds some parsing, generation and
sanity checks with placeholders for real
code/functionality to be added later.

Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/htt.h | 5 +
drivers/net/wireless/ath/ath10k/htt_rx.c | 198 ++++++++++++++++++++++++++++++-
drivers/net/wireless/ath/ath10k/htt_tx.c | 53 +++++++++
3 files changed, 255 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
index cb6d4fd687da..65dcd22f31df 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -1752,6 +1752,11 @@ int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt,
u8 max_subfrms_ampdu,
u8 max_subfrms_amsdu);
void ath10k_htt_hif_tx_complete(struct ath10k *ar, struct sk_buff *skb);
+int ath10k_htt_tx_fetch_resp(struct ath10k *ar,
+ __le32 token,
+ __le16 fetch_seq_num,
+ struct htt_tx_fetch_record *records,
+ size_t num_records);

void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt,
bool is_mgmt);
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index cc957a625605..be7dc88b3316 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -1982,6 +1982,198 @@ static void ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb)
tasklet_schedule(&htt->rx_replenish_task);
}

+static void ath10k_htt_rx_tx_fetch_resp_id_confirm(struct ath10k *ar,
+ const __le32 *resp_ids,
+ int num_resp_ids)
+{
+ int i;
+ u32 resp_id;
+
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch confirm num_resp_ids %d\n",
+ num_resp_ids);
+
+ for (i = 0; i < num_resp_ids; i++) {
+ resp_id = le32_to_cpu(resp_ids[i]);
+
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch confirm resp_id %u\n",
+ resp_id);
+
+ /* TODO: free resp_id */
+ }
+}
+
+static void ath10k_htt_rx_tx_fetch_ind(struct ath10k *ar, struct sk_buff *skb)
+{
+ struct htt_resp *resp = (struct htt_resp *)skb->data;
+ struct htt_tx_fetch_record *record;
+ size_t len;
+ size_t max_num_bytes;
+ size_t max_num_msdus;
+ const __le32 *resp_ids;
+ u16 num_records;
+ u16 num_resp_ids;
+ u16 peer_id;
+ u8 tid;
+ int i;
+
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch ind\n");
+
+ len = sizeof(resp->hdr) + sizeof(resp->tx_fetch_ind);
+ if (unlikely(skb->len < len)) {
+ ath10k_warn(ar, "received corrupted tx_fetch_ind event: buffer too short\n");
+ return;
+ }
+
+ num_records = le16_to_cpu(resp->tx_fetch_ind.num_records);
+ num_resp_ids = le16_to_cpu(resp->tx_fetch_ind.num_resp_ids);
+
+ len += sizeof(resp->tx_fetch_ind.records[0]) * num_records;
+ len += sizeof(resp->tx_fetch_ind.resp_ids[0]) * num_resp_ids;
+
+ if (unlikely(skb->len < len)) {
+ ath10k_warn(ar, "received corrupted tx_fetch_ind event: too many records/resp_ids\n");
+ return;
+ }
+
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch ind num records %hu num resps %hu seq %hu\n",
+ num_records, num_resp_ids,
+ le16_to_cpu(resp->tx_fetch_ind.fetch_seq_num));
+
+ /* TODO: runtime sanity checks */
+
+ for (i = 0; i < num_records; i++) {
+ record = &resp->tx_fetch_ind.records[i];
+ peer_id = MS(le16_to_cpu(record->info),
+ HTT_TX_FETCH_RECORD_INFO_PEER_ID);
+ tid = MS(le16_to_cpu(record->info),
+ HTT_TX_FETCH_RECORD_INFO_TID);
+ max_num_msdus = le16_to_cpu(record->num_msdus);
+ max_num_bytes = le32_to_cpu(record->num_bytes);
+
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch record %i peer_id %hu tid %hhu msdus %zu bytes %zu\n",
+ i, peer_id, tid, max_num_msdus, max_num_bytes);
+
+ if (unlikely(peer_id >= ar->htt.tx_q_state.num_peers) ||
+ unlikely(tid >= ar->htt.tx_q_state.num_tids)) {
+ ath10k_warn(ar, "received out of range peer_id %hu tid %hhu\n",
+ peer_id, tid);
+ continue;
+ }
+
+ /* TODO: dequeue and submit tx to device */
+ }
+
+ resp_ids = ath10k_htt_get_tx_fetch_ind_resp_ids(&resp->tx_fetch_ind);
+ ath10k_htt_rx_tx_fetch_resp_id_confirm(ar, resp_ids, num_resp_ids);
+
+ /* TODO: generate and send fetch response to device */
+}
+
+static void ath10k_htt_rx_tx_fetch_confirm(struct ath10k *ar,
+ struct sk_buff *skb)
+{
+ const struct htt_resp *resp = (void *)skb->data;
+ size_t len;
+ int num_resp_ids;
+
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch confirm\n");
+
+ len = sizeof(resp->hdr) + sizeof(resp->tx_fetch_confirm);
+ if (unlikely(skb->len < len)) {
+ ath10k_warn(ar, "received corrupted tx_fetch_confirm event: buffer too short\n");
+ return;
+ }
+
+ num_resp_ids = le16_to_cpu(resp->tx_fetch_confirm.num_resp_ids);
+ len += sizeof(resp->tx_fetch_confirm.resp_ids[0]) * num_resp_ids;
+
+ if (unlikely(skb->len < len)) {
+ ath10k_warn(ar, "received corrupted tx_fetch_confirm event: resp_ids buffer overflow\n");
+ return;
+ }
+
+ ath10k_htt_rx_tx_fetch_resp_id_confirm(ar,
+ resp->tx_fetch_confirm.resp_ids,
+ num_resp_ids);
+}
+
+static void ath10k_htt_rx_tx_mode_switch_ind(struct ath10k *ar,
+ struct sk_buff *skb)
+{
+ const struct htt_resp *resp = (void *)skb->data;
+ const struct htt_tx_mode_switch_record *record;
+ size_t len;
+ size_t num_records;
+ enum htt_tx_mode_switch_mode mode;
+ bool enable;
+ u16 info0;
+ u16 info1;
+ u16 threshold;
+ u16 peer_id;
+ u8 tid;
+ int i;
+
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx mode switch ind\n");
+
+ len = sizeof(resp->hdr) + sizeof(resp->tx_mode_switch_ind);
+ if (unlikely(skb->len < len)) {
+ ath10k_warn(ar, "received corrupted tx_mode_switch_ind event: buffer too short\n");
+ return;
+ }
+
+ info0 = le16_to_cpu(resp->tx_mode_switch_ind.info0);
+ info1 = le16_to_cpu(resp->tx_mode_switch_ind.info1);
+
+ enable = !!(info0 & HTT_TX_MODE_SWITCH_IND_INFO0_ENABLE);
+ num_records = MS(info0, HTT_TX_MODE_SWITCH_IND_INFO1_THRESHOLD);
+ mode = MS(info1, HTT_TX_MODE_SWITCH_IND_INFO1_MODE);
+ threshold = MS(info1, HTT_TX_MODE_SWITCH_IND_INFO1_THRESHOLD);
+
+ ath10k_dbg(ar, ATH10K_DBG_HTT,
+ "htt rx tx mode switch ind info0 0x%04hx info1 0x%04hx enable %d num records %zd mode %d threshold %hu\n",
+ info0, info1, enable, num_records, mode, threshold);
+
+ len += sizeof(resp->tx_mode_switch_ind.records[0]) * num_records;
+
+ if (unlikely(skb->len < len)) {
+ ath10k_warn(ar, "received corrupted tx_mode_switch_mode_ind event: too many records\n");
+ return;
+ }
+
+ switch (mode) {
+ case HTT_TX_MODE_SWITCH_PUSH:
+ case HTT_TX_MODE_SWITCH_PUSH_PULL:
+ break;
+ default:
+ ath10k_warn(ar, "received invalid tx_mode_switch_mode_ind mode %d, ignoring\n",
+ mode);
+ return;
+ }
+
+ if (!enable)
+ return;
+
+ /* TODO: apply configuration */
+
+ for (i = 0; i < num_records; i++) {
+ record = &resp->tx_mode_switch_ind.records[i];
+ info0 = le16_to_cpu(record->info0);
+ peer_id = MS(info0, HTT_TX_MODE_SWITCH_RECORD_INFO0_PEER_ID);
+ tid = MS(info0, HTT_TX_MODE_SWITCH_RECORD_INFO0_TID);
+
+ if (unlikely(peer_id >= ar->htt.tx_q_state.num_peers) ||
+ unlikely(tid >= ar->htt.tx_q_state.num_tids)) {
+ ath10k_warn(ar, "received out of range peer_id %hu tid %hhu\n",
+ peer_id, tid);
+ continue;
+ }
+
+ /* TODO: apply configuration */
+ }
+
+ /* TODO: apply configuration */
+}
+
void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
{
struct ath10k_htt *htt = &ar->htt;
@@ -2124,9 +2316,13 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
case HTT_T2H_MSG_TYPE_AGGR_CONF:
break;
case HTT_T2H_MSG_TYPE_TX_FETCH_IND:
+ ath10k_htt_rx_tx_fetch_ind(ar, skb);
+ break;
case HTT_T2H_MSG_TYPE_TX_FETCH_CONFIRM:
+ ath10k_htt_rx_tx_fetch_confirm(ar, skb);
+ break;
case HTT_T2H_MSG_TYPE_TX_MODE_SWITCH_IND:
- /* TODO: Implement pull-push logic */
+ ath10k_htt_rx_tx_mode_switch_ind(ar, skb);
break;
case HTT_T2H_MSG_TYPE_EN_STATS:
default:
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
index 860661d3812f..225f0561b3fd 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -526,6 +526,59 @@ int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt,
return 0;
}

+int ath10k_htt_tx_fetch_resp(struct ath10k *ar,
+ __le32 token,
+ __le16 fetch_seq_num,
+ struct htt_tx_fetch_record *records,
+ size_t num_records)
+{
+ struct sk_buff *skb;
+ struct htt_cmd *cmd;
+ u16 resp_id;
+ int len = 0;
+ int ret;
+
+ len += sizeof(cmd->hdr);
+ len += sizeof(cmd->tx_fetch_resp);
+ len += sizeof(cmd->tx_fetch_resp.records[0]) * num_records;
+
+ skb = ath10k_htc_alloc_skb(ar, len);
+ if (!skb)
+ return -ENOMEM;
+
+ resp_id = 0; /* TODO: allocate resp_id */
+ ret = 0;
+ if (ret)
+ goto err_free_skb;
+
+ skb_put(skb, len);
+ cmd = (struct htt_cmd *)skb->data;
+ cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_TX_FETCH_RESP;
+ cmd->tx_fetch_resp.resp_id = cpu_to_le16(resp_id);
+ cmd->tx_fetch_resp.fetch_seq_num = fetch_seq_num;
+ cmd->tx_fetch_resp.num_records = cpu_to_le16(num_records);
+ cmd->tx_fetch_resp.token = token;
+
+ memcpy(cmd->tx_fetch_resp.records, records,
+ sizeof(records[0]) * num_records);
+
+ ret = ath10k_htc_send(&ar->htc, ar->htt.eid, skb);
+ if (ret) {
+ ath10k_warn(ar, "failed to submit htc command: %d\n", ret);
+ goto err_free_resp_id;
+ }
+
+ return 0;
+
+err_free_resp_id:
+ (void)resp_id; /* TODO: free resp_id */
+
+err_free_skb:
+ dev_kfree_skb_any(skb);
+
+ return ret;
+}
+
static u8 ath10k_htt_tx_get_vdev_id(struct ath10k *ar, struct sk_buff *skb)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
--
2.1.4


2016-01-21 13:45:53

by Michal Kazior

[permalink] [raw]
Subject: [PATCH 12/13] ath10k: keep track of queue depth per txq

This will be necessary for later.

Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/core.h | 2 +-
drivers/net/wireless/ath/ath10k/mac.c | 5 +++++
drivers/net/wireless/ath/ath10k/txrx.c | 7 +++++++
3 files changed, 13 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 5be56a8343bd..f51887c6be74 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -306,7 +306,7 @@ struct ath10k_peer {
};

struct ath10k_txq {
- /* TBD */
+ unsigned long num_fw_queued;
};

struct ath10k_sta {
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 639da03572e0..0dc16de1d9fb 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -3639,6 +3639,7 @@ static int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
const bool is_presp = false;
struct ath10k *ar = hw->priv;
struct ath10k_htt *htt = &ar->htt;
+ struct ath10k_txq *artxq = (void *)txq->drv_priv;
struct ieee80211_vif *vif = txq->vif;
struct ieee80211_sta *sta = txq->sta;
enum ath10k_hw_txrx_mode txmode;
@@ -3678,6 +3679,10 @@ static int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
return ret;
}

+ spin_lock_bh(&ar->htt.tx_lock);
+ artxq->num_fw_queued++;
+ spin_unlock_bh(&ar->htt.tx_lock);
+
return 0;
}

diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c
index 202e5192235b..ea4d3000c8c3 100644
--- a/drivers/net/wireless/ath/ath10k/txrx.c
+++ b/drivers/net/wireless/ath/ath10k/txrx.c
@@ -55,7 +55,9 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
struct ath10k *ar = htt->ar;
struct device *dev = ar->dev;
struct ieee80211_tx_info *info;
+ struct ieee80211_txq *txq;
struct ath10k_skb_cb *skb_cb;
+ struct ath10k_txq *artxq;
struct sk_buff *msdu;
bool limit_mgmt_desc = false;

@@ -80,11 +82,16 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
}

skb_cb = ATH10K_SKB_CB(msdu);
+ txq = skb_cb->txq;
+ artxq = (void *)txq->drv_priv;

if (unlikely(skb_cb->flags & ATH10K_SKB_F_MGMT) &&
ar->hw_params.max_probe_resp_desc_thres)
limit_mgmt_desc = true;

+ if (txq)
+ artxq->num_fw_queued--;
+
ath10k_htt_tx_free_msdu_id(htt, tx_done->msdu_id);
ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc);
if (htt->num_pending_tx == 0)
--
2.1.4


2016-01-21 13:45:48

by Michal Kazior

[permalink] [raw]
Subject: [PATCH 08/13] ath10k: implement updating shared htt txq state

Firmware 10.4.3 onwards can support a pull-push Tx
model where it shares a Tx queue state with the
host.

The host updates the DMA region it pointed to
during HTT setup whenever number of software
queued from (on host) changes. Based on this
information firmware issues fetch requests to the
host telling the host how many frames from a list
of given stations/tids should be submitted to the
firmware.

The code won't be called because not all
appropriate HTT events are processed yet.

Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/htt.h | 3 +
drivers/net/wireless/ath/ath10k/htt_tx.c | 100 +++++++++++++++++++++++++++++++
drivers/net/wireless/ath/ath10k/mac.c | 9 ++-
3 files changed, 111 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
index 65dcd22f31df..b1e40f44e76b 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -1667,6 +1667,7 @@ struct ath10k_htt {
} txbuf;

struct {
+ bool enabled;
struct htt_q_state *vaddr;
dma_addr_t paddr;
u16 num_peers;
@@ -1758,6 +1759,8 @@ int ath10k_htt_tx_fetch_resp(struct ath10k *ar,
struct htt_tx_fetch_record *records,
size_t num_records);

+void ath10k_htt_tx_txq_update(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq);
void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt,
bool is_mgmt);
int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt,
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
index 225f0561b3fd..5ef0a1b3773c 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -22,6 +22,106 @@
#include "txrx.h"
#include "debug.h"

+static u8 ath10k_htt_tx_txq_calc_size(size_t count)
+{
+ int exp;
+ int factor;
+
+ exp = 0;
+ factor = count >> 7;
+
+ while (factor >= 64 && exp < 4) {
+ factor >>= 3;
+ exp++;
+ }
+
+ if (exp == 4)
+ return 0xff;
+
+ if (count > 0)
+ factor = max(1, factor);
+
+ return SM(exp, HTT_TX_Q_STATE_ENTRY_EXP) |
+ SM(factor, HTT_TX_Q_STATE_ENTRY_FACTOR);
+}
+
+static void __ath10k_htt_tx_txq_recalc(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+{
+ struct ath10k *ar = hw->priv;
+ struct ath10k_sta *arsta = (void *)txq->sta->drv_priv;
+ struct ath10k_vif *arvif = (void *)txq->vif->drv_priv;
+ int idx;
+ u32 bit;
+ u16 peer_id;
+ u8 tid;
+ u8 count;
+
+ lockdep_assert_held(&ar->htt.tx_lock);
+
+ if (!ar->htt.tx_q_state.enabled)
+ return;
+
+ if (txq->sta)
+ peer_id = arsta->peer_id;
+ else
+ peer_id = arvif->peer_id;
+
+ tid = txq->tid;
+ bit = BIT(peer_id % 32);
+ idx = peer_id / 32;
+ count = ath10k_htt_tx_txq_calc_size(txq->qsize);
+
+ if (unlikely(peer_id >= ar->htt.tx_q_state.num_peers) ||
+ unlikely(tid >= ar->htt.tx_q_state.num_tids)) {
+ ath10k_warn(ar, "refusing to update txq for peer_id %hu tid %hhu due to out of bounds\n",
+ peer_id, tid);
+ return;
+ }
+
+ ar->htt.tx_q_state.vaddr->count[tid][peer_id] = count;
+ ar->htt.tx_q_state.vaddr->map[tid][idx] &= ~bit;
+ ar->htt.tx_q_state.vaddr->map[tid][idx] |= count ? bit : 0;
+
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx txq state update peer_id %hu tid %hhu count %hhu\n",
+ peer_id, tid, count);
+}
+
+static void __ath10k_htt_tx_txq_sync(struct ath10k *ar)
+{
+ u32 seq;
+ size_t size;
+
+ lockdep_assert_held(&ar->htt.tx_lock);
+
+ if (!ar->htt.tx_q_state.enabled)
+ return;
+
+ seq = le32_to_cpu(ar->htt.tx_q_state.vaddr->seq);
+ seq++;
+ ar->htt.tx_q_state.vaddr->seq = cpu_to_le32(seq);
+
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx txq state update commit seq %u\n",
+ seq);
+
+ size = sizeof(*ar->htt.tx_q_state.vaddr);
+ dma_sync_single_for_device(ar->dev,
+ ar->htt.tx_q_state.paddr,
+ size,
+ DMA_TO_DEVICE);
+}
+
+void ath10k_htt_tx_txq_update(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+{
+ struct ath10k *ar = hw->priv;
+
+ spin_lock_bh(&ar->htt.tx_lock);
+ __ath10k_htt_tx_txq_recalc(hw, txq);
+ __ath10k_htt_tx_txq_sync(ar);
+ spin_unlock_bh(&ar->htt.tx_lock);
+}
+
void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt,
bool is_mgmt)
{
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 111baa104f8e..d9566ba70af3 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -3695,12 +3695,19 @@ static void ath10k_mac_tx_push(struct ieee80211_hw *hw,
txq->vif->addr, ret);
return;
}
+
+ ath10k_htt_tx_txq_update(hw, txq);
}

static void ath10k_mac_tx_wake(struct ieee80211_hw *hw,
struct ieee80211_txq *txq)
{
- WARN_ON(1); /* TBD */
+ /* TODO: Possible optimization is to defer the updates until a certain
+ * time/quantity threshold is reached to avoid hammering DMA and
+ * flushing d-caches for each submitted msdu.
+ */
+
+ ath10k_htt_tx_txq_update(hw, txq);
}

/************/
--
2.1.4


2016-01-21 13:45:47

by Michal Kazior

[permalink] [raw]
Subject: [PATCH 07/13] ath10k: implement wake_tx_queue

This actually prepares a few knobs for
new firmware supporting pull-push Tx model where
firmware asks host to submit frames for given
stations and tids.

Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/mac.c | 89 +++++++++++++++++++++++++++++++++++
1 file changed, 89 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 00789afa5dec..111baa104f8e 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -3624,6 +3624,85 @@ void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work)
}
}

+static bool ath10k_mac_tx_can_push(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+{
+ return 1; /* TBD */
+}
+
+static int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+{
+ const bool is_mgmt = false;
+ const bool is_presp = false;
+ struct ath10k *ar = hw->priv;
+ struct ath10k_htt *htt = &ar->htt;
+ struct ieee80211_vif *vif = txq->vif;
+ struct ieee80211_sta *sta = txq->sta;
+ enum ath10k_hw_txrx_mode txmode;
+ enum ath10k_mac_tx_path txpath;
+ struct sk_buff *skb;
+ int ret;
+
+ spin_lock_bh(&ar->htt.tx_lock);
+ ret = ath10k_htt_tx_inc_pending(htt, is_mgmt, is_presp);
+ spin_unlock_bh(&ar->htt.tx_lock);
+
+ if (ret)
+ return ret;
+
+ skb = ieee80211_tx_dequeue(hw, txq);
+ if (!skb) {
+ spin_lock_bh(&ar->htt.tx_lock);
+ ath10k_htt_tx_dec_pending(htt, is_mgmt);
+ spin_unlock_bh(&ar->htt.tx_lock);
+
+ return -ENOENT;
+ }
+
+ ath10k_mac_tx_h_fill_cb(ar, vif, skb);
+
+ txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
+ txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);
+
+ ret = ath10k_mac_tx(ar, vif, sta, txmode, txpath, skb);
+ if (unlikely(ret)) {
+ ath10k_warn(ar, "failed to push frame: %d\n", ret);
+
+ spin_lock_bh(&ar->htt.tx_lock);
+ ath10k_htt_tx_dec_pending(htt, is_mgmt);
+ spin_unlock_bh(&ar->htt.tx_lock);
+
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ath10k_mac_tx_push(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+{
+ struct ath10k *ar = hw->priv;
+ int ret;
+
+ ret = ath10k_mac_tx_push_txq(hw, txq);
+ if (unlikely(ret)) {
+ if (txq->sta)
+ ath10k_warn(ar, "failed to push tx to station %pM tid %hhu: %d\n",
+ txq->sta->addr, txq->tid, ret);
+ else
+ ath10k_warn(ar, "failed to push tx to interface %pM: %d\n",
+ txq->vif->addr, ret);
+ return;
+ }
+}
+
+static void ath10k_mac_tx_wake(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+{
+ WARN_ON(1); /* TBD */
+}
+
/************/
/* Scanning */
/************/
@@ -3840,6 +3919,15 @@ static void ath10k_mac_op_tx(struct ieee80211_hw *hw,
}
}

+static void ath10k_mac_op_wake_tx_queue(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+{
+ if (ath10k_mac_tx_can_push(hw, txq))
+ ath10k_mac_tx_push(hw, txq);
+ else
+ ath10k_mac_tx_wake(hw, txq);
+}
+
/* Must not be called with conf_mutex held as workers can use that also. */
void ath10k_drain_tx(struct ath10k *ar)
{
@@ -6998,6 +7086,7 @@ ath10k_mac_op_switch_vif_chanctx(struct ieee80211_hw *hw,

static const struct ieee80211_ops ath10k_ops = {
.tx = ath10k_mac_op_tx,
+ .wake_tx_queue = ath10k_mac_op_wake_tx_queue,
.start = ath10k_start,
.stop = ath10k_stop,
.config = ath10k_config,
--
2.1.4


2016-01-21 17:43:09

by Peter Oh

[permalink] [raw]
Subject: Re: [PATCH 13/13] ath10k: implement push-pull tx


On 01/21/2016 05:46 AM, Michal Kazior wrote:
> The current/old tx path design was that host, at
> its own leisure, pushed tx frames to the device.
> For HTT there was ~1000-1400 msdu queue depth.
>
> After reaching that limit the driver would request
> mac80211 to stop queues. There was little control
> over what packets got in there as far as
> DA/RA was considered so it was rather easy to
> starve per-station traffic flows.
>
> With MU-MIMO this became a significant problem
> because the queue depth was insufficient to buffer
> frames from multiple clients (which could have
> different signal quality and capabilities) in an
> efficient fashion.
>
> Hence the new tx path in 10.4 was introduced: a
> pull-push mode.
>
> Firmware and host can share tx queue state via
> DMA. The state is logically a 2 dimensional array
> addressed via peer_id+tid pair. Each entry is a
> counter (either number of bytes or packets. Host
> keeps it updated and firmware uses it for
> scheduling Tx pull requests to host.
>
> This allows MU-MIMO to become a lot more effective
> with 10+ clients.
>
> Signed-off-by: Michal Kazior <[email protected]>
> ---
> drivers/net/wireless/ath/ath10k/core.h | 1 +
> drivers/net/wireless/ath/ath10k/htt.h | 6 ++
> drivers/net/wireless/ath/ath10k/htt_rx.c | 117
> ++++++++++++++++++++++++++++---
> drivers/net/wireless/ath/ath10k/htt_tx.c | 39 ++++++++---
> drivers/net/wireless/ath/ath10k/mac.c | 48 +++++++++++--
> drivers/net/wireless/ath/ath10k/mac.h | 5 ++
> 6 files changed, 192 insertions(+), 24 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath10k/core.h
> b/drivers/net/wireless/ath/ath10k/core.h
> index f51887c6be74..ab8cbccc0f4b 100644
> --- a/drivers/net/wireless/ath/ath10k/core.h
> +++ b/drivers/net/wireless/ath/ath10k/core.h
> @@ -307,6 +307,7 @@ struct ath10k_peer {
>
> struct ath10k_txq {
> unsigned long num_fw_queued;
> + unsigned long num_push_allowed;
> };
>
> struct ath10k_sta {
> diff --git a/drivers/net/wireless/ath/ath10k/htt.h
> b/drivers/net/wireless/ath/ath10k/htt.h
> index b1e40f44e76b..02cf55d306e8 100644
> --- a/drivers/net/wireless/ath/ath10k/htt.h
> +++ b/drivers/net/wireless/ath/ath10k/htt.h
> @@ -1652,6 +1652,7 @@ struct ath10k_htt {
> struct sk_buff_head tx_compl_q;
> struct sk_buff_head rx_compl_q;
> struct sk_buff_head rx_in_ord_compl_q;
> + struct sk_buff_head tx_fetch_ind_q;
>
> /* rx_status template */
> struct ieee80211_rx_status rx_status;
> @@ -1670,8 +1671,10 @@ struct ath10k_htt {
> bool enabled;
> struct htt_q_state *vaddr;
> dma_addr_t paddr;
> + u16 num_push_allowed;
> u16 num_peers;
> u16 num_tids;
> + enum htt_tx_mode_switch_mode mode;
> enum htt_q_depth_type type;
> } tx_q_state;
> };
> @@ -1761,6 +1764,9 @@ int ath10k_htt_tx_fetch_resp(struct ath10k *ar,
>
> void ath10k_htt_tx_txq_update(struct ieee80211_hw *hw,
> struct ieee80211_txq *txq);
> +void ath10k_htt_tx_txq_recalc(struct ieee80211_hw *hw,
> + struct ieee80211_txq *txq);
> +void ath10k_htt_tx_txq_sync(struct ath10k *ar);
> void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt,
> bool is_mgmt);
> int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt,
> diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c
> b/drivers/net/wireless/ath/ath10k/htt_rx.c
> index 6e3d95c95568..827c8369b589 100644
> --- a/drivers/net/wireless/ath/ath10k/htt_rx.c
> +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
> @@ -229,6 +229,7 @@ void ath10k_htt_rx_free(struct ath10k_htt *htt)
> skb_queue_purge(&htt->tx_compl_q);
> skb_queue_purge(&htt->rx_compl_q);
> skb_queue_purge(&htt->rx_in_ord_compl_q);
> + skb_queue_purge(&htt->tx_fetch_ind_q);
>
> ath10k_htt_rx_ring_free(htt);
>
> @@ -569,6 +570,7 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt)
> skb_queue_head_init(&htt->tx_compl_q);
> skb_queue_head_init(&htt->rx_compl_q);
> skb_queue_head_init(&htt->rx_in_ord_compl_q);
> + skb_queue_head_init(&htt->tx_fetch_ind_q);
>
> tasklet_init(&htt->txrx_compl_task, ath10k_htt_txrx_compl_task,
> (unsigned long)htt);
> @@ -2004,16 +2006,21 @@ static void
> ath10k_htt_rx_tx_fetch_resp_id_confirm(struct ath10k *ar,
>
> static void ath10k_htt_rx_tx_fetch_ind(struct ath10k *ar, struct sk_buff
> *skb)
> {
> + struct ieee80211_hw *hw = ar->hw;
> + struct ieee80211_txq *txq;
> struct htt_resp *resp = (struct htt_resp *)skb->data;
> struct htt_tx_fetch_record *record;
> size_t len;
> size_t max_num_bytes;
> size_t max_num_msdus;
> + size_t num_bytes;
> + size_t num_msdus;
> const __le32 *resp_ids;
> u16 num_records;
> u16 num_resp_ids;
> u16 peer_id;
> u8 tid;
> + int ret;
> int i;
>
> ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch ind\n");
> @@ -2039,7 +2046,17 @@ static void ath10k_htt_rx_tx_fetch_ind(struct
> ath10k *ar, struct sk_buff *skb)
> num_records, num_resp_ids,
> le16_to_cpu(resp->tx_fetch_ind.fetch_seq_num));
>
> - /* TODO: runtime sanity checks */
> + if (!ar->htt.tx_q_state.enabled) {
> + ath10k_warn(ar, "received unexpected tx_fetch_ind event:
> not enabled\n");
> + return;
> + }
> +
> + if (ar->htt.tx_q_state.mode == HTT_TX_MODE_SWITCH_PUSH) {
> + ath10k_warn(ar, "received unexpected tx_fetch_ind event:
> in push mode\n");
> + return;
> + }
> +
> + rcu_read_lock();
>
> for (i = 0; i < num_records; i++) {
> record = &resp->tx_fetch_ind.records[i];
> @@ -2060,13 +2077,56 @@ static void ath10k_htt_rx_tx_fetch_ind(struct
> ath10k *ar, struct sk_buff *skb)
> continue;
> }
>
> - /* TODO: dequeue and submit tx to device */
> + spin_lock_bh(&ar->data_lock);
> + txq = ath10k_mac_txq_lookup(ar, peer_id, tid);
> + spin_unlock_bh(&ar->data_lock);
> +
> + /* It is okay to release the lock and use txq because RCU
> read
> + * lock is held.
> + */
> +
> + if (unlikely(!txq)) {
> + ath10k_warn(ar, "failed to lookup txq for peer_id
> %hu tid %hhu\n",
> + peer_id, tid);
> + continue;
> + }
> +
> + num_msdus = 0;
> + num_bytes = 0;
> +
> + while (num_msdus < max_num_msdus &&
> + num_bytes < max_num_bytes) {
> + ret = ath10k_mac_tx_push_txq(hw, txq);
> + if (ret < 0)
> + break;
> +
> + num_msdus++;
> + num_bytes += ret;
> + }
> +
> + record->num_msdus = cpu_to_le16(num_msdus);
> + record->num_bytes = cpu_to_le32(num_bytes);
> +
> + ath10k_htt_tx_txq_recalc(hw, txq);
> }
>
> + rcu_read_unlock();
> +
> resp_ids =
> ath10k_htt_get_tx_fetch_ind_resp_ids(&resp->tx_fetch_ind);
> ath10k_htt_rx_tx_fetch_resp_id_confirm(ar, resp_ids,
> num_resp_ids);
>
> - /* TODO: generate and send fetch response to device */
> + ret = ath10k_htt_tx_fetch_resp(ar,
> + resp->tx_fetch_ind.token,
> + resp->tx_fetch_ind.fetch_seq_num,
> + resp->tx_fetch_ind.records,
> + num_records);
> + if (unlikely(ret)) {
> + ath10k_warn(ar, "failed to submit tx fetch resp for token
> 0x%08x: %d\n",
> + le32_to_cpu(resp->tx_fetch_ind.token), ret);
> + /* FIXME: request fw restart */
> + }
> +
> + ath10k_htt_tx_txq_sync(ar);
> }
>
> static void ath10k_htt_rx_tx_fetch_confirm(struct ath10k *ar,
> @@ -2102,6 +2162,8 @@ static void ath10k_htt_rx_tx_mode_switch_ind(struct
> ath10k *ar,
> {
> const struct htt_resp *resp = (void *)skb->data;
> const struct htt_tx_mode_switch_record *record;
> + struct ieee80211_txq *txq;
> + struct ath10k_txq *artxq;
> size_t len;
> size_t num_records;
> enum htt_tx_mode_switch_mode mode;
> @@ -2153,7 +2215,11 @@ static void ath10k_htt_rx_tx_mode_switch_ind(struct
> ath10k *ar,
> if (!enable)
> return;
>
> - /* TODO: apply configuration */
> + ar->htt.tx_q_state.enabled = enable;
> + ar->htt.tx_q_state.mode = mode;
> + ar->htt.tx_q_state.num_push_allowed = threshold;
> +
> + rcu_read_lock();
>
> for (i = 0; i < num_records; i++) {
> record = &resp->tx_mode_switch_ind.records[i];
> @@ -2168,10 +2234,33 @@ static void
> ath10k_htt_rx_tx_mode_switch_ind(struct ath10k *ar,
> continue;
> }
>
> - /* TODO: apply configuration */
> + spin_lock_bh(&ar->data_lock);
> + txq = ath10k_mac_txq_lookup(ar, peer_id, tid);
> + spin_unlock_bh(&ar->data_lock);
> +
> + /* It is okay to release the lock and use txq because RCU
> read
> + * lock is held.
> + */
> +
> + if (unlikely(!txq)) {
> + ath10k_warn(ar, "failed to lookup txq for peer_id
> %hu tid %hhu\n",
> + peer_id, tid);
> + continue;
> + }
> +
> + spin_lock_bh(&ar->htt.tx_lock);
> + artxq = (void *)txq->drv_priv;
> + artxq->num_push_allowed =
> le16_to_cpu(record->num_max_msdus);
> + spin_unlock_bh(&ar->htt.tx_lock);
> }
>
> - /* TODO: apply configuration */
> + rcu_read_unlock();
> +
> + spin_lock_bh(&ar->htt.tx_lock);
> + ath10k_mac_tx_lock(ar, ATH10K_TX_PAUSE_Q_FLUSH_PENDING);
> + spin_unlock_bh(&ar->htt.tx_lock);
> +
Isn't it proved it break Mesh from working?
> + ath10k_mac_tx_push_pending(ar);
> }
>
> void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
> @@ -2317,8 +2406,9 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar,
> struct sk_buff *skb)
> case HTT_T2H_MSG_TYPE_AGGR_CONF:
> break;
> case HTT_T2H_MSG_TYPE_TX_FETCH_IND:
> - ath10k_htt_rx_tx_fetch_ind(ar, skb);
> - break;
> + skb_queue_tail(&htt->tx_fetch_ind_q, skb);
> + tasklet_schedule(&htt->txrx_compl_task);
> + return;
> case HTT_T2H_MSG_TYPE_TX_FETCH_CONFIRM:
> ath10k_htt_rx_tx_fetch_confirm(ar, skb);
> break;
> @@ -2356,21 +2446,32 @@ static void ath10k_htt_txrx_compl_task(unsigned
> long ptr)
> struct ath10k_htt *htt = (struct ath10k_htt *)ptr;
> struct ath10k *ar = htt->ar;
> struct sk_buff_head tx_q;
> + struct sk_buff_head tx_ind_q;
> struct htt_resp *resp;
> struct sk_buff *skb;
> unsigned long flags;
>
> __skb_queue_head_init(&tx_q);
> + __skb_queue_head_init(&tx_ind_q);
>
> spin_lock_irqsave(&htt->tx_compl_q.lock, flags);
> skb_queue_splice_init(&htt->tx_compl_q, &tx_q);
> spin_unlock_irqrestore(&htt->tx_compl_q.lock, flags);
>
> + spin_lock_irqsave(&htt->tx_fetch_ind_q.lock, flags);
> + skb_queue_splice_init(&htt->tx_fetch_ind_q, &tx_ind_q);
> + spin_unlock_irqrestore(&htt->tx_fetch_ind_q.lock, flags);
> +
> while ((skb = __skb_dequeue(&tx_q))) {
> ath10k_htt_rx_frm_tx_compl(htt->ar, skb);
> dev_kfree_skb_any(skb);
> }
>
> + while ((skb = __skb_dequeue(&tx_ind_q))) {
> + ath10k_htt_rx_tx_fetch_ind(ar, skb);
> + dev_kfree_skb_any(skb);
> + }
> +
> ath10k_mac_tx_push_pending(ar);
>
> spin_lock_bh(&htt->rx_ring.lock);
> diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c
> b/drivers/net/wireless/ath/ath10k/htt_tx.c
> index 5ef0a1b3773c..00877ff6861e 100644
> --- a/drivers/net/wireless/ath/ath10k/htt_tx.c
> +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
> @@ -62,6 +62,9 @@ static void __ath10k_htt_tx_txq_recalc(struct
> ieee80211_hw *hw,
> if (!ar->htt.tx_q_state.enabled)
> return;
>
> + if (ar->htt.tx_q_state.mode != HTT_TX_MODE_SWITCH_PUSH_PULL)
> + return;
> +
> if (txq->sta)
> peer_id = arsta->peer_id;
> else
> @@ -97,6 +100,9 @@ static void __ath10k_htt_tx_txq_sync(struct ath10k *ar)
> if (!ar->htt.tx_q_state.enabled)
> return;
>
> + if (ar->htt.tx_q_state.mode != HTT_TX_MODE_SWITCH_PUSH_PULL)
> + return;
> +
> seq = le32_to_cpu(ar->htt.tx_q_state.vaddr->seq);
> seq++;
> ar->htt.tx_q_state.vaddr->seq = cpu_to_le32(seq);
> @@ -111,6 +117,23 @@ static void __ath10k_htt_tx_txq_sync(struct ath10k
> *ar)
> DMA_TO_DEVICE);
> }
>
> +void ath10k_htt_tx_txq_recalc(struct ieee80211_hw *hw,
> + struct ieee80211_txq *txq)
> +{
> + struct ath10k *ar = hw->priv;
> +
> + spin_lock_bh(&ar->htt.tx_lock);
> + __ath10k_htt_tx_txq_recalc(hw, txq);
> + spin_unlock_bh(&ar->htt.tx_lock);
> +}
> +
> +void ath10k_htt_tx_txq_sync(struct ath10k *ar)
> +{
> + spin_lock_bh(&ar->htt.tx_lock);
> + __ath10k_htt_tx_txq_sync(ar);
> + spin_unlock_bh(&ar->htt.tx_lock);
> +}
> +
> void ath10k_htt_tx_txq_update(struct ieee80211_hw *hw,
> struct ieee80211_txq *txq)
> {
> @@ -634,10 +657,14 @@ int ath10k_htt_tx_fetch_resp(struct ath10k *ar,
> {
> struct sk_buff *skb;
> struct htt_cmd *cmd;
> - u16 resp_id;
> + const u16 resp_id = 0;
> int len = 0;
> int ret;
>
> + /* Response IDs are echo-ed back only for host driver convienence
> + * purposes. They aren't used for anything in the driver yet so
> use 0.
> + */
> +
> len += sizeof(cmd->hdr);
> len += sizeof(cmd->tx_fetch_resp);
> len += sizeof(cmd->tx_fetch_resp.records[0]) * num_records;
> @@ -646,11 +673,6 @@ int ath10k_htt_tx_fetch_resp(struct ath10k *ar,
> if (!skb)
> return -ENOMEM;
>
> - resp_id = 0; /* TODO: allocate resp_id */
> - ret = 0;
> - if (ret)
> - goto err_free_skb;
> -
> skb_put(skb, len);
> cmd = (struct htt_cmd *)skb->data;
> cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_TX_FETCH_RESP;
> @@ -665,14 +687,11 @@ int ath10k_htt_tx_fetch_resp(struct ath10k *ar,
> ret = ath10k_htc_send(&ar->htc, ar->htt.eid, skb);
> if (ret) {
> ath10k_warn(ar, "failed to submit htc command: %d\n",
> ret);
> - goto err_free_resp_id;
> + goto err_free_skb;
> }
>
> return 0;
>
> -err_free_resp_id:
> - (void)resp_id; /* TODO: free resp_id */
> -
> err_free_skb:
> dev_kfree_skb_any(skb);
>
> diff --git a/drivers/net/wireless/ath/ath10k/mac.c
> b/drivers/net/wireless/ath/ath10k/mac.c
> index 0dc16de1d9fb..ee94c83b5128 100644
> --- a/drivers/net/wireless/ath/ath10k/mac.c
> +++ b/drivers/net/wireless/ath/ath10k/mac.c
> @@ -3629,11 +3629,25 @@ void ath10k_mgmt_over_wmi_tx_work(struct
> work_struct *work)
> static bool ath10k_mac_tx_can_push(struct ieee80211_hw *hw,
> struct ieee80211_txq *txq)
> {
> - return 1; /* TBD */
> + struct ath10k *ar = hw->priv;
> + struct ath10k_txq *artxq = (void *)txq->drv_priv;
> +
> + /* No need to get locks */
> +
> + if (ar->htt.tx_q_state.mode == HTT_TX_MODE_SWITCH_PUSH)
> + return true;
> +
> + if (ar->htt.num_pending_tx < ar->htt.tx_q_state.num_push_allowed)
> + return true;
> +
> + if (artxq->num_fw_queued < artxq->num_push_allowed)
> + return true;
> +
> + return false;
> }
>
> -static int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
> - struct ieee80211_txq *txq)
> +int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
> + struct ieee80211_txq *txq)
> {
> const bool is_mgmt = false;
> const bool is_presp = false;
> @@ -3645,6 +3659,7 @@ static int ath10k_mac_tx_push_txq(struct
> ieee80211_hw *hw,
> enum ath10k_hw_txrx_mode txmode;
> enum ath10k_mac_tx_path txpath;
> struct sk_buff *skb;
> + size_t skb_len;
> int ret;
>
> spin_lock_bh(&ar->htt.tx_lock);
> @@ -3665,6 +3680,7 @@ static int ath10k_mac_tx_push_txq(struct
> ieee80211_hw *hw,
>
> ath10k_mac_tx_h_fill_cb(ar, vif, txq, skb);
>
> + skb_len = skb->len;
> txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
> txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);
>
> @@ -3683,7 +3699,7 @@ static int ath10k_mac_tx_push_txq(struct
> ieee80211_hw *hw,
> artxq->num_fw_queued++;
> spin_unlock_bh(&ar->htt.tx_lock);
>
> - return 0;
> + return skb_len;
> }
>
> static void ath10k_mac_tx_push(struct ieee80211_hw *hw,
> @@ -3693,7 +3709,7 @@ static void ath10k_mac_tx_push(struct ieee80211_hw
> *hw,
> int ret;
>
> ret = ath10k_mac_tx_push_txq(hw, txq);
> - if (unlikely(ret)) {
> + if (unlikely(ret < 0)) {
> if (txq->sta)
> ath10k_warn(ar, "failed to push tx to station %pM
> tid %hhu: %d\n",
> txq->sta->addr, txq->tid, ret);
> @@ -3738,7 +3754,7 @@ static void ath10k_mac_tx_push_pending_txq(struct
> ieee80211_hw *hw,
> break;
>
> ret = ath10k_mac_tx_push_txq(hw, txq);
> - if (ret)
> + if (ret < 0)
> break;
> }
>
> @@ -3820,6 +3836,26 @@ static void ath10k_mac_txq_unref(struct ath10k *ar,
> spin_unlock_bh(&ar->htt.tx_lock);
> }
>
> +struct ieee80211_txq *ath10k_mac_txq_lookup(struct ath10k *ar,
> + u16 peer_id,
> + u8 tid)
> +{
> + struct ath10k_peer *peer;
> +
> + lockdep_assert_held(&ar->data_lock);
> +
> + peer = ar->peer_map[peer_id];
> + if (!peer)
> + return NULL;
> +
> + if (peer->sta)
> + return peer->sta->txq[tid];
> + else if (peer->vif)
> + return peer->vif->txq;
> + else
> + return NULL;
> +}
> +
> /************/
> /* Scanning */
> /************/
> diff --git a/drivers/net/wireless/ath/ath10k/mac.h
> b/drivers/net/wireless/ath/ath10k/mac.h
> index 453f606a250e..2c3327beb445 100644
> --- a/drivers/net/wireless/ath/ath10k/mac.h
> +++ b/drivers/net/wireless/ath/ath10k/mac.h
> @@ -76,6 +76,11 @@ void ath10k_mac_vif_tx_lock(struct ath10k_vif *arvif,
> int reason);
> void ath10k_mac_vif_tx_unlock(struct ath10k_vif *arvif, int reason);
> bool ath10k_mac_tx_frm_has_freq(struct ath10k *ar);
> void ath10k_mac_tx_push_pending(struct ath10k *ar);
> +int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
> + struct ieee80211_txq *txq);
> +struct ieee80211_txq *ath10k_mac_txq_lookup(struct ath10k *ar,
> + u16 peer_id,
> + u8 tid);
>
> static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif
> *vif)
> {


2016-01-21 13:45:41

by Michal Kazior

[permalink] [raw]
Subject: [PATCH 03/13] ath10k: refactor tx pending management

Tx pending counter logic assumed that the sk_buff
is already known and hence was performed in HTT
functions themselves.

However, for the sake of future wake_tx_queue()
usage the driver must be able to tell whether it
can submit more frames to firmware before it
dequeues frame from ieee80211_txq (and thus long
before HTT Tx functions are called) because once a
frame is dequeued it cannot be requeud back to
mac80211.

This prepares the driver for future changes.

Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/htt.h | 7 ++-
drivers/net/wireless/ath/ath10k/htt_tx.c | 85 ++++++++------------------------
drivers/net/wireless/ath/ath10k/mac.c | 51 ++++++++++++++++---
drivers/net/wireless/ath/ath10k/txrx.c | 2 +-
4 files changed, 71 insertions(+), 74 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
index 13391ea4422d..cb6d4fd687da 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -1753,7 +1753,12 @@ int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt,
u8 max_subfrms_amsdu);
void ath10k_htt_hif_tx_complete(struct ath10k *ar, struct sk_buff *skb);

-void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt, bool limit_mgmt_desc);
+void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt,
+ bool is_mgmt);
+int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt,
+ bool is_mgmt,
+ bool is_presp);
+
int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt, struct sk_buff *skb);
void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id);
int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *);
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
index 95acb727c068..860661d3812f 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -22,9 +22,12 @@
#include "txrx.h"
#include "debug.h"

-void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt, bool limit_mgmt_desc)
+void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt,
+ bool is_mgmt)
{
- if (limit_mgmt_desc)
+ lockdep_assert_held(&htt->tx_lock);
+
+ if (is_mgmt)
htt->num_pending_mgmt_tx--;

htt->num_pending_tx--;
@@ -32,43 +35,31 @@ void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt, bool limit_mgmt_desc)
ath10k_mac_tx_unlock(htt->ar, ATH10K_TX_PAUSE_Q_FULL);
}

-static void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt,
- bool limit_mgmt_desc)
-{
- spin_lock_bh(&htt->tx_lock);
- __ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc);
- spin_unlock_bh(&htt->tx_lock);
-}
-
-static int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt,
- bool limit_mgmt_desc, bool is_probe_resp)
+int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt,
+ bool is_mgmt,
+ bool is_presp)
{
struct ath10k *ar = htt->ar;
- int ret = 0;

- spin_lock_bh(&htt->tx_lock);
+ lockdep_assert_held(&htt->tx_lock);

- if (htt->num_pending_tx >= htt->max_num_pending_tx) {
- ret = -EBUSY;
- goto exit;
- }
+ if (htt->num_pending_tx >= htt->max_num_pending_tx)
+ return -EBUSY;

- if (limit_mgmt_desc) {
- if (is_probe_resp && (htt->num_pending_mgmt_tx >
- ar->hw_params.max_probe_resp_desc_thres)) {
- ret = -EBUSY;
- goto exit;
- }
+ if (is_mgmt &&
+ is_presp &&
+ ar->hw_params.max_probe_resp_desc_thres &&
+ ar->hw_params.max_probe_resp_desc_thres < htt->num_pending_mgmt_tx)
+ return -EBUSY;
+
+ if (is_mgmt)
htt->num_pending_mgmt_tx++;
- }

htt->num_pending_tx++;
if (htt->num_pending_tx == htt->max_num_pending_tx)
ath10k_mac_tx_lock(htt->ar, ATH10K_TX_PAUSE_Q_FULL);

-exit:
- spin_unlock_bh(&htt->tx_lock);
- return ret;
+ return 0;
}

int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt, struct sk_buff *skb)
@@ -576,20 +567,6 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
int msdu_id = -1;
int res;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data;
- bool limit_mgmt_desc = false;
- bool is_probe_resp = false;
-
- if (ar->hw_params.max_probe_resp_desc_thres) {
- limit_mgmt_desc = true;
-
- if (ieee80211_is_probe_resp(hdr->frame_control))
- is_probe_resp = true;
- }
-
- res = ath10k_htt_tx_inc_pending(htt, limit_mgmt_desc, is_probe_resp);
-
- if (res)
- goto err;

len += sizeof(cmd->hdr);
len += sizeof(cmd->mgmt_tx);
@@ -598,7 +575,7 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
res = ath10k_htt_tx_alloc_msdu_id(htt, msdu);
spin_unlock_bh(&htt->tx_lock);
if (res < 0)
- goto err_tx_dec;
+ goto err;

msdu_id = res;

@@ -649,8 +626,6 @@ err_free_msdu_id:
spin_lock_bh(&htt->tx_lock);
ath10k_htt_tx_free_msdu_id(htt, msdu_id);
spin_unlock_bh(&htt->tx_lock);
-err_tx_dec:
- ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc);
err:
return res;
}
@@ -677,26 +652,12 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode,
u32 frags_paddr = 0;
u32 txbuf_paddr;
struct htt_msdu_ext_desc *ext_desc = NULL;
- bool limit_mgmt_desc = false;
- bool is_probe_resp = false;
-
- if (unlikely(ieee80211_is_mgmt(hdr->frame_control)) &&
- ar->hw_params.max_probe_resp_desc_thres) {
- limit_mgmt_desc = true;
-
- if (ieee80211_is_probe_resp(hdr->frame_control))
- is_probe_resp = true;
- }
-
- res = ath10k_htt_tx_inc_pending(htt, limit_mgmt_desc, is_probe_resp);
- if (res)
- goto err;

spin_lock_bh(&htt->tx_lock);
res = ath10k_htt_tx_alloc_msdu_id(htt, msdu);
spin_unlock_bh(&htt->tx_lock);
if (res < 0)
- goto err_tx_dec;
+ goto err;

msdu_id = res;

@@ -862,11 +823,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode,
err_unmap_msdu:
dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
err_free_msdu_id:
- spin_lock_bh(&htt->tx_lock);
ath10k_htt_tx_free_msdu_id(htt, msdu_id);
- spin_unlock_bh(&htt->tx_lock);
-err_tx_dec:
- ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc);
err:
return res;
}
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 9abb52924432..212c840e1599 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -3362,13 +3362,11 @@ ath10k_mac_tx_h_get_txpath(struct ath10k *ar,

static int ath10k_mac_tx_submit(struct ath10k *ar,
enum ath10k_hw_txrx_mode txmode,
+ enum ath10k_mac_tx_path txpath,
struct sk_buff *skb)
{
struct ath10k_htt *htt = &ar->htt;
- enum ath10k_mac_tx_path txpath;
- int ret;
-
- txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);
+ int ret = -EINVAL;

switch (txpath) {
case ATH10K_MAC_TX_HTT:
@@ -3402,6 +3400,7 @@ static int ath10k_mac_tx(struct ath10k *ar,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
enum ath10k_hw_txrx_mode txmode,
+ enum ath10k_mac_tx_path txpath,
struct sk_buff *skb)
{
struct ieee80211_hw *hw = ar->hw;
@@ -3441,7 +3440,7 @@ static int ath10k_mac_tx(struct ath10k *ar,
}
}

- ret = ath10k_mac_tx_submit(ar, txmode, skb);
+ ret = ath10k_mac_tx_submit(ar, txmode, txpath, skb);
if (ret) {
ath10k_warn(ar, "failed to submit frame: %d\n", ret);
return ret;
@@ -3469,6 +3468,7 @@ void ath10k_offchan_tx_work(struct work_struct *work)
struct ath10k_peer *peer;
struct ath10k_vif *arvif;
enum ath10k_hw_txrx_mode txmode;
+ enum ath10k_mac_tx_path txpath;
struct ieee80211_hdr *hdr;
struct ieee80211_vif *vif;
struct ieee80211_sta *sta;
@@ -3537,8 +3537,9 @@ void ath10k_offchan_tx_work(struct work_struct *work)
}

txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
+ txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);

- ret = ath10k_mac_tx(ar, vif, sta, txmode, skb);
+ ret = ath10k_mac_tx(ar, vif, sta, txmode, txpath, skb);
if (ret) {
ath10k_warn(ar, "failed to transmit offchannel frame: %d\n",
ret);
@@ -3762,19 +3763,53 @@ static void ath10k_mac_op_tx(struct ieee80211_hw *hw,
struct sk_buff *skb)
{
struct ath10k *ar = hw->priv;
+ struct ath10k_htt *htt = &ar->htt;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_vif *vif = info->control.vif;
struct ieee80211_sta *sta = control->sta;
+ struct ieee80211_hdr *hdr = (void *)skb->data;
enum ath10k_hw_txrx_mode txmode;
+ enum ath10k_mac_tx_path txpath;
+ bool is_htt;
+ bool is_mgmt;
+ bool is_presp;
int ret;

ath10k_mac_tx_h_fill_cb(ar, vif, skb);

txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
+ txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);
+ is_htt = (txpath == ATH10K_MAC_TX_HTT ||
+ txpath == ATH10K_MAC_TX_HTT_MGMT);

- ret = ath10k_mac_tx(ar, vif, sta, txmode, skb);
- if (ret)
+ if (is_htt) {
+ spin_lock_bh(&ar->htt.tx_lock);
+
+ is_mgmt = ieee80211_is_mgmt(hdr->frame_control);
+ is_presp = ieee80211_is_probe_resp(hdr->frame_control);
+
+ ret = ath10k_htt_tx_inc_pending(htt, is_mgmt, is_presp);
+ if (ret) {
+ ath10k_warn(ar, "failed to increase tx pending count: %d, dropping\n",
+ ret);
+ spin_unlock_bh(&ar->htt.tx_lock);
+ ieee80211_free_txskb(ar->hw, skb);
+ return;
+ }
+
+ spin_unlock_bh(&ar->htt.tx_lock);
+ }
+
+ ret = ath10k_mac_tx(ar, vif, sta, txmode, txpath, skb);
+ if (ret) {
ath10k_warn(ar, "failed to transmit frame: %d\n", ret);
+ if (is_htt) {
+ spin_lock_bh(&ar->htt.tx_lock);
+ ath10k_htt_tx_dec_pending(htt, is_mgmt);
+ spin_unlock_bh(&ar->htt.tx_lock);
+ }
+ return;
+ }
}

/* Must not be called with conf_mutex held as workers can use that also. */
diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c
index fbfb608e48ab..118586ece20e 100644
--- a/drivers/net/wireless/ath/ath10k/txrx.c
+++ b/drivers/net/wireless/ath/ath10k/txrx.c
@@ -86,7 +86,7 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
limit_mgmt_desc = true;

ath10k_htt_tx_free_msdu_id(htt, tx_done->msdu_id);
- __ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc);
+ ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc);
if (htt->num_pending_tx == 0)
wake_up(&htt->empty_tx_wq);
spin_unlock_bh(&htt->tx_lock);
--
2.1.4


2016-03-01 10:32:25

by Michal Kazior

[permalink] [raw]
Subject: [PATCH v2 11/11] ath10k: implement push-pull tx

The current/old tx path design was that host, at
its own leisure, pushed tx frames to the device.
For HTT there was ~1000-1400 msdu queue depth.

After reaching that limit the driver would request
mac80211 to stop queues. There was little control
over what packets got in there as far as
DA/RA was considered so it was rather easy to
starve per-station traffic flows.

With MU-MIMO this became a significant problem
because the queue depth was insufficient to buffer
frames from multiple clients (which could have
different signal quality and capabilities) in an
efficient fashion.

Hence the new tx path in 10.4 was introduced: a
pull-push mode.

Firmware and host can share tx queue state via
DMA. The state is logically a 2 dimensional array
addressed via peer_id+tid pair. Each entry is a
counter (either number of bytes or packets. Host
keeps it updated and firmware uses it for
scheduling Tx pull requests to host.

This allows MU-MIMO to become a lot more effective
with 10+ clients.

Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/core.h | 1 +
drivers/net/wireless/ath/ath10k/htt.h | 6 ++
drivers/net/wireless/ath/ath10k/htt_rx.c | 113 ++++++++++++++++++++++++++++---
drivers/net/wireless/ath/ath10k/htt_tx.c | 39 ++++++++---
drivers/net/wireless/ath/ath10k/mac.c | 44 ++++++++++--
drivers/net/wireless/ath/ath10k/mac.h | 5 ++
6 files changed, 186 insertions(+), 22 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 926ecb2244a5..3050e497fd22 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -312,6 +312,7 @@ struct ath10k_peer {
struct ath10k_txq {
struct list_head list;
unsigned long num_fw_queued;
+ unsigned long num_push_allowed;
};

struct ath10k_sta {
diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
index b1e40f44e76b..02cf55d306e8 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -1652,6 +1652,7 @@ struct ath10k_htt {
struct sk_buff_head tx_compl_q;
struct sk_buff_head rx_compl_q;
struct sk_buff_head rx_in_ord_compl_q;
+ struct sk_buff_head tx_fetch_ind_q;

/* rx_status template */
struct ieee80211_rx_status rx_status;
@@ -1670,8 +1671,10 @@ struct ath10k_htt {
bool enabled;
struct htt_q_state *vaddr;
dma_addr_t paddr;
+ u16 num_push_allowed;
u16 num_peers;
u16 num_tids;
+ enum htt_tx_mode_switch_mode mode;
enum htt_q_depth_type type;
} tx_q_state;
};
@@ -1761,6 +1764,9 @@ int ath10k_htt_tx_fetch_resp(struct ath10k *ar,

void ath10k_htt_tx_txq_update(struct ieee80211_hw *hw,
struct ieee80211_txq *txq);
+void ath10k_htt_tx_txq_recalc(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq);
+void ath10k_htt_tx_txq_sync(struct ath10k *ar);
void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt,
bool is_mgmt);
int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt,
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index 6e3d95c95568..8e9a887fcb94 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -229,6 +229,7 @@ void ath10k_htt_rx_free(struct ath10k_htt *htt)
skb_queue_purge(&htt->tx_compl_q);
skb_queue_purge(&htt->rx_compl_q);
skb_queue_purge(&htt->rx_in_ord_compl_q);
+ skb_queue_purge(&htt->tx_fetch_ind_q);

ath10k_htt_rx_ring_free(htt);

@@ -569,6 +570,7 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt)
skb_queue_head_init(&htt->tx_compl_q);
skb_queue_head_init(&htt->rx_compl_q);
skb_queue_head_init(&htt->rx_in_ord_compl_q);
+ skb_queue_head_init(&htt->tx_fetch_ind_q);

tasklet_init(&htt->txrx_compl_task, ath10k_htt_txrx_compl_task,
(unsigned long)htt);
@@ -2004,16 +2006,21 @@ static void ath10k_htt_rx_tx_fetch_resp_id_confirm(struct ath10k *ar,

static void ath10k_htt_rx_tx_fetch_ind(struct ath10k *ar, struct sk_buff *skb)
{
+ struct ieee80211_hw *hw = ar->hw;
+ struct ieee80211_txq *txq;
struct htt_resp *resp = (struct htt_resp *)skb->data;
struct htt_tx_fetch_record *record;
size_t len;
size_t max_num_bytes;
size_t max_num_msdus;
+ size_t num_bytes;
+ size_t num_msdus;
const __le32 *resp_ids;
u16 num_records;
u16 num_resp_ids;
u16 peer_id;
u8 tid;
+ int ret;
int i;

ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch ind\n");
@@ -2039,7 +2046,17 @@ static void ath10k_htt_rx_tx_fetch_ind(struct ath10k *ar, struct sk_buff *skb)
num_records, num_resp_ids,
le16_to_cpu(resp->tx_fetch_ind.fetch_seq_num));

- /* TODO: runtime sanity checks */
+ if (!ar->htt.tx_q_state.enabled) {
+ ath10k_warn(ar, "received unexpected tx_fetch_ind event: not enabled\n");
+ return;
+ }
+
+ if (ar->htt.tx_q_state.mode == HTT_TX_MODE_SWITCH_PUSH) {
+ ath10k_warn(ar, "received unexpected tx_fetch_ind event: in push mode\n");
+ return;
+ }
+
+ rcu_read_lock();

for (i = 0; i < num_records; i++) {
record = &resp->tx_fetch_ind.records[i];
@@ -2060,13 +2077,56 @@ static void ath10k_htt_rx_tx_fetch_ind(struct ath10k *ar, struct sk_buff *skb)
continue;
}

- /* TODO: dequeue and submit tx to device */
+ spin_lock_bh(&ar->data_lock);
+ txq = ath10k_mac_txq_lookup(ar, peer_id, tid);
+ spin_unlock_bh(&ar->data_lock);
+
+ /* It is okay to release the lock and use txq because RCU read
+ * lock is held.
+ */
+
+ if (unlikely(!txq)) {
+ ath10k_warn(ar, "failed to lookup txq for peer_id %hu tid %hhu\n",
+ peer_id, tid);
+ continue;
+ }
+
+ num_msdus = 0;
+ num_bytes = 0;
+
+ while (num_msdus < max_num_msdus &&
+ num_bytes < max_num_bytes) {
+ ret = ath10k_mac_tx_push_txq(hw, txq);
+ if (ret < 0)
+ break;
+
+ num_msdus++;
+ num_bytes += ret;
+ }
+
+ record->num_msdus = cpu_to_le16(num_msdus);
+ record->num_bytes = cpu_to_le32(num_bytes);
+
+ ath10k_htt_tx_txq_recalc(hw, txq);
}

+ rcu_read_unlock();
+
resp_ids = ath10k_htt_get_tx_fetch_ind_resp_ids(&resp->tx_fetch_ind);
ath10k_htt_rx_tx_fetch_resp_id_confirm(ar, resp_ids, num_resp_ids);

- /* TODO: generate and send fetch response to device */
+ ret = ath10k_htt_tx_fetch_resp(ar,
+ resp->tx_fetch_ind.token,
+ resp->tx_fetch_ind.fetch_seq_num,
+ resp->tx_fetch_ind.records,
+ num_records);
+ if (unlikely(ret)) {
+ ath10k_warn(ar, "failed to submit tx fetch resp for token 0x%08x: %d\n",
+ le32_to_cpu(resp->tx_fetch_ind.token), ret);
+ /* FIXME: request fw restart */
+ }
+
+ ath10k_htt_tx_txq_sync(ar);
}

static void ath10k_htt_rx_tx_fetch_confirm(struct ath10k *ar,
@@ -2102,6 +2162,8 @@ static void ath10k_htt_rx_tx_mode_switch_ind(struct ath10k *ar,
{
const struct htt_resp *resp = (void *)skb->data;
const struct htt_tx_mode_switch_record *record;
+ struct ieee80211_txq *txq;
+ struct ath10k_txq *artxq;
size_t len;
size_t num_records;
enum htt_tx_mode_switch_mode mode;
@@ -2153,7 +2215,11 @@ static void ath10k_htt_rx_tx_mode_switch_ind(struct ath10k *ar,
if (!enable)
return;

- /* TODO: apply configuration */
+ ar->htt.tx_q_state.enabled = enable;
+ ar->htt.tx_q_state.mode = mode;
+ ar->htt.tx_q_state.num_push_allowed = threshold;
+
+ rcu_read_lock();

for (i = 0; i < num_records; i++) {
record = &resp->tx_mode_switch_ind.records[i];
@@ -2168,10 +2234,29 @@ static void ath10k_htt_rx_tx_mode_switch_ind(struct ath10k *ar,
continue;
}

- /* TODO: apply configuration */
+ spin_lock_bh(&ar->data_lock);
+ txq = ath10k_mac_txq_lookup(ar, peer_id, tid);
+ spin_unlock_bh(&ar->data_lock);
+
+ /* It is okay to release the lock and use txq because RCU read
+ * lock is held.
+ */
+
+ if (unlikely(!txq)) {
+ ath10k_warn(ar, "failed to lookup txq for peer_id %hu tid %hhu\n",
+ peer_id, tid);
+ continue;
+ }
+
+ spin_lock_bh(&ar->htt.tx_lock);
+ artxq = (void *)txq->drv_priv;
+ artxq->num_push_allowed = le16_to_cpu(record->num_max_msdus);
+ spin_unlock_bh(&ar->htt.tx_lock);
}

- /* TODO: apply configuration */
+ rcu_read_unlock();
+
+ ath10k_mac_tx_push_pending(ar);
}

void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
@@ -2317,8 +2402,9 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
case HTT_T2H_MSG_TYPE_AGGR_CONF:
break;
case HTT_T2H_MSG_TYPE_TX_FETCH_IND:
- ath10k_htt_rx_tx_fetch_ind(ar, skb);
- break;
+ skb_queue_tail(&htt->tx_fetch_ind_q, skb);
+ tasklet_schedule(&htt->txrx_compl_task);
+ return;
case HTT_T2H_MSG_TYPE_TX_FETCH_CONFIRM:
ath10k_htt_rx_tx_fetch_confirm(ar, skb);
break;
@@ -2356,21 +2442,32 @@ static void ath10k_htt_txrx_compl_task(unsigned long ptr)
struct ath10k_htt *htt = (struct ath10k_htt *)ptr;
struct ath10k *ar = htt->ar;
struct sk_buff_head tx_q;
+ struct sk_buff_head tx_ind_q;
struct htt_resp *resp;
struct sk_buff *skb;
unsigned long flags;

__skb_queue_head_init(&tx_q);
+ __skb_queue_head_init(&tx_ind_q);

spin_lock_irqsave(&htt->tx_compl_q.lock, flags);
skb_queue_splice_init(&htt->tx_compl_q, &tx_q);
spin_unlock_irqrestore(&htt->tx_compl_q.lock, flags);

+ spin_lock_irqsave(&htt->tx_fetch_ind_q.lock, flags);
+ skb_queue_splice_init(&htt->tx_fetch_ind_q, &tx_ind_q);
+ spin_unlock_irqrestore(&htt->tx_fetch_ind_q.lock, flags);
+
while ((skb = __skb_dequeue(&tx_q))) {
ath10k_htt_rx_frm_tx_compl(htt->ar, skb);
dev_kfree_skb_any(skb);
}

+ while ((skb = __skb_dequeue(&tx_ind_q))) {
+ ath10k_htt_rx_tx_fetch_ind(ar, skb);
+ dev_kfree_skb_any(skb);
+ }
+
ath10k_mac_tx_push_pending(ar);

spin_lock_bh(&htt->rx_ring.lock);
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
index 6643be8692b5..a30c34eae0a7 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -64,6 +64,9 @@ static void __ath10k_htt_tx_txq_recalc(struct ieee80211_hw *hw,
if (!ar->htt.tx_q_state.enabled)
return;

+ if (ar->htt.tx_q_state.mode != HTT_TX_MODE_SWITCH_PUSH_PULL)
+ return;
+
if (txq->sta)
peer_id = arsta->peer_id;
else
@@ -101,6 +104,9 @@ static void __ath10k_htt_tx_txq_sync(struct ath10k *ar)
if (!ar->htt.tx_q_state.enabled)
return;

+ if (ar->htt.tx_q_state.mode != HTT_TX_MODE_SWITCH_PUSH_PULL)
+ return;
+
seq = le32_to_cpu(ar->htt.tx_q_state.vaddr->seq);
seq++;
ar->htt.tx_q_state.vaddr->seq = cpu_to_le32(seq);
@@ -115,6 +121,23 @@ static void __ath10k_htt_tx_txq_sync(struct ath10k *ar)
DMA_TO_DEVICE);
}

+void ath10k_htt_tx_txq_recalc(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+{
+ struct ath10k *ar = hw->priv;
+
+ spin_lock_bh(&ar->htt.tx_lock);
+ __ath10k_htt_tx_txq_recalc(hw, txq);
+ spin_unlock_bh(&ar->htt.tx_lock);
+}
+
+void ath10k_htt_tx_txq_sync(struct ath10k *ar)
+{
+ spin_lock_bh(&ar->htt.tx_lock);
+ __ath10k_htt_tx_txq_sync(ar);
+ spin_unlock_bh(&ar->htt.tx_lock);
+}
+
void ath10k_htt_tx_txq_update(struct ieee80211_hw *hw,
struct ieee80211_txq *txq)
{
@@ -638,10 +661,14 @@ int ath10k_htt_tx_fetch_resp(struct ath10k *ar,
{
struct sk_buff *skb;
struct htt_cmd *cmd;
- u16 resp_id;
+ const u16 resp_id = 0;
int len = 0;
int ret;

+ /* Response IDs are echo-ed back only for host driver convienence
+ * purposes. They aren't used for anything in the driver yet so use 0.
+ */
+
len += sizeof(cmd->hdr);
len += sizeof(cmd->tx_fetch_resp);
len += sizeof(cmd->tx_fetch_resp.records[0]) * num_records;
@@ -650,11 +677,6 @@ int ath10k_htt_tx_fetch_resp(struct ath10k *ar,
if (!skb)
return -ENOMEM;

- resp_id = 0; /* TODO: allocate resp_id */
- ret = 0;
- if (ret)
- goto err_free_skb;
-
skb_put(skb, len);
cmd = (struct htt_cmd *)skb->data;
cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_TX_FETCH_RESP;
@@ -669,14 +691,11 @@ int ath10k_htt_tx_fetch_resp(struct ath10k *ar,
ret = ath10k_htc_send(&ar->htc, ar->htt.eid, skb);
if (ret) {
ath10k_warn(ar, "failed to submit htc command: %d\n", ret);
- goto err_free_resp_id;
+ goto err_free_skb;
}

return 0;

-err_free_resp_id:
- (void)resp_id; /* TODO: free resp_id */
-
err_free_skb:
dev_kfree_skb_any(skb);

diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 5bf614f1f75a..4a27f27a8e8b 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -3656,14 +3656,48 @@ static void ath10k_mac_txq_unref(struct ath10k *ar, struct ieee80211_txq *txq)
spin_unlock_bh(&ar->htt.tx_lock);
}

+struct ieee80211_txq *ath10k_mac_txq_lookup(struct ath10k *ar,
+ u16 peer_id,
+ u8 tid)
+{
+ struct ath10k_peer *peer;
+
+ lockdep_assert_held(&ar->data_lock);
+
+ peer = ar->peer_map[peer_id];
+ if (!peer)
+ return NULL;
+
+ if (peer->sta)
+ return peer->sta->txq[tid];
+ else if (peer->vif)
+ return peer->vif->txq;
+ else
+ return NULL;
+}
+
static bool ath10k_mac_tx_can_push(struct ieee80211_hw *hw,
struct ieee80211_txq *txq)
{
- return 1; /* TBD */
+ struct ath10k *ar = hw->priv;
+ struct ath10k_txq *artxq = (void *)txq->drv_priv;
+
+ /* No need to get locks */
+
+ if (ar->htt.tx_q_state.mode == HTT_TX_MODE_SWITCH_PUSH)
+ return true;
+
+ if (ar->htt.num_pending_tx < ar->htt.tx_q_state.num_push_allowed)
+ return true;
+
+ if (artxq->num_fw_queued < artxq->num_push_allowed)
+ return true;
+
+ return false;
}

-static int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
- struct ieee80211_txq *txq)
+int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
{
const bool is_mgmt = false;
const bool is_presp = false;
@@ -3675,6 +3709,7 @@ static int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
enum ath10k_hw_txrx_mode txmode;
enum ath10k_mac_tx_path txpath;
struct sk_buff *skb;
+ size_t skb_len;
int ret;

spin_lock_bh(&ar->htt.tx_lock);
@@ -3695,6 +3730,7 @@ static int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,

ath10k_mac_tx_h_fill_cb(ar, vif, txq, skb);

+ skb_len = skb->len;
txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);

@@ -3713,7 +3749,7 @@ static int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
artxq->num_fw_queued++;
spin_unlock_bh(&ar->htt.tx_lock);

- return 0;
+ return skb_len;
}

void ath10k_mac_tx_push_pending(struct ath10k *ar)
diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h
index 453f606a250e..2c3327beb445 100644
--- a/drivers/net/wireless/ath/ath10k/mac.h
+++ b/drivers/net/wireless/ath/ath10k/mac.h
@@ -76,6 +76,11 @@ void ath10k_mac_vif_tx_lock(struct ath10k_vif *arvif, int reason);
void ath10k_mac_vif_tx_unlock(struct ath10k_vif *arvif, int reason);
bool ath10k_mac_tx_frm_has_freq(struct ath10k *ar);
void ath10k_mac_tx_push_pending(struct ath10k *ar);
+int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq);
+struct ieee80211_txq *ath10k_mac_txq_lookup(struct ath10k *ar,
+ u16 peer_id,
+ u8 tid);

static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
{
--
2.1.4


2016-03-01 10:32:16

by Michal Kazior

[permalink] [raw]
Subject: [PATCH v2 05/11] ath10k: add fast peer_map lookup

The pull-push functionality of 10.4 will be based
on peer_id and tid. These will need to be mapped,
eventually, to ieee80211_txq to be used with
ieee80211_tx_dequeue().

Iterating over existing stations every time
peer_id needs to be mapped to a station would be
inefficient wrt CPU time.

The new firmware, which will be the only user of
the code flow-wise, will guarantee to use low
peer_ids first so despite peer_map's apparent huge
size d-cache thrashing should not be a problem.

Older firmware hot paths will effectively not use
peer_map.

Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/core.h | 4 ++
drivers/net/wireless/ath/ath10k/mac.c | 71 +++++++++++++++++++++++++++++++---
drivers/net/wireless/ath/ath10k/txrx.c | 2 +
3 files changed, 71 insertions(+), 6 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 523585e85f35..5f447444fe97 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -297,6 +297,9 @@ struct ath10k_dfs_stats {

struct ath10k_peer {
struct list_head list;
+ struct ieee80211_vif *vif;
+ struct ieee80211_sta *sta;
+
int vdev_id;
u8 addr[ETH_ALEN];
DECLARE_BITMAP(peer_ids, ATH10K_MAX_NUM_PEER_IDS);
@@ -791,6 +794,7 @@ struct ath10k {

struct list_head arvifs;
struct list_head peers;
+ struct ath10k_peer *peer_map[ATH10K_MAX_NUM_PEER_IDS];
wait_queue_head_t peer_mapping_wq;

/* protected by conf_mutex */
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 72b8f177445d..4b69a373382b 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -618,10 +618,15 @@ ath10k_mac_get_any_chandef_iter(struct ieee80211_hw *hw,
*def = &conf->def;
}

-static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr,
+static int ath10k_peer_create(struct ath10k *ar,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ u32 vdev_id,
+ const u8 *addr,
enum wmi_peer_type peer_type)
{
struct ath10k_vif *arvif;
+ struct ath10k_peer *peer;
int num_peers = 0;
int ret;

@@ -650,6 +655,22 @@ static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr,
return ret;
}

+ spin_lock_bh(&ar->data_lock);
+
+ peer = ath10k_peer_find(ar, vdev_id, addr);
+ if (!peer) {
+ ath10k_warn(ar, "failed to find peer %pM on vdev %i after creation\n",
+ addr, vdev_id);
+ ath10k_wmi_peer_delete(ar, vdev_id, addr);
+ spin_unlock_bh(&ar->data_lock);
+ return -ENOENT;
+ }
+
+ peer->vif = vif;
+ peer->sta = sta;
+
+ spin_unlock_bh(&ar->data_lock);
+
ar->num_peers++;

return 0;
@@ -731,6 +752,7 @@ static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
{
struct ath10k_peer *peer, *tmp;
+ int peer_id;

lockdep_assert_held(&ar->conf_mutex);

@@ -742,6 +764,11 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
ath10k_warn(ar, "removing stale peer %pM from vdev_id %d\n",
peer->addr, vdev_id);

+ for_each_set_bit(peer_id, peer->peer_ids,
+ ATH10K_MAX_NUM_PEER_IDS) {
+ ar->peer_map[peer_id] = NULL;
+ }
+
list_del(&peer->list);
kfree(peer);
ar->num_peers--;
@@ -3506,7 +3533,8 @@ void ath10k_offchan_tx_work(struct work_struct *work)
peer_addr, vdev_id);

if (!peer) {
- ret = ath10k_peer_create(ar, vdev_id, peer_addr,
+ ret = ath10k_peer_create(ar, NULL, NULL, vdev_id,
+ peer_addr,
WMI_PEER_TYPE_DEFAULT);
if (ret)
ath10k_warn(ar, "failed to create peer %pM on vdev %d: %d\n",
@@ -4614,8 +4642,8 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,

if (arvif->vdev_type == WMI_VDEV_TYPE_AP ||
arvif->vdev_type == WMI_VDEV_TYPE_IBSS) {
- ret = ath10k_peer_create(ar, arvif->vdev_id, vif->addr,
- WMI_PEER_TYPE_DEFAULT);
+ ret = ath10k_peer_create(ar, vif, NULL, arvif->vdev_id,
+ vif->addr, WMI_PEER_TYPE_DEFAULT);
if (ret) {
ath10k_warn(ar, "failed to create vdev %i peer for AP/IBSS: %d\n",
arvif->vdev_id, ret);
@@ -4749,7 +4777,9 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
{
struct ath10k *ar = hw->priv;
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+ struct ath10k_peer *peer;
int ret;
+ int i;

cancel_work_sync(&arvif->ap_csa_work);
cancel_delayed_work_sync(&arvif->connection_loss_work);
@@ -4803,6 +4833,20 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
spin_unlock_bh(&ar->data_lock);
}

+ spin_lock_bh(&ar->data_lock);
+ for (i = 0; i < ARRAY_SIZE(ar->peer_map); i++) {
+ peer = ar->peer_map[i];
+ if (!peer)
+ continue;
+
+ if (peer->vif == vif) {
+ ath10k_warn(ar, "found vif peer %pM entry on vdev %i after it was supposedly removed\n",
+ vif->addr, arvif->vdev_id);
+ peer->vif = NULL;
+ }
+ }
+ spin_unlock_bh(&ar->data_lock);
+
ath10k_peer_cleanup(ar, arvif->vdev_id);

if (vif->type == NL80211_IFTYPE_MONITOR) {
@@ -5522,6 +5566,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
struct ath10k_peer *peer;
int ret = 0;
+ int i;

if (old_state == IEEE80211_STA_NOTEXIST &&
new_state == IEEE80211_STA_NONE) {
@@ -5562,8 +5607,8 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
if (sta->tdls)
peer_type = WMI_PEER_TYPE_TDLS;

- ret = ath10k_peer_create(ar, arvif->vdev_id, sta->addr,
- peer_type);
+ ret = ath10k_peer_create(ar, vif, sta, arvif->vdev_id,
+ sta->addr, peer_type);
if (ret) {
ath10k_warn(ar, "failed to add peer %pM for vdev %d when adding a new sta: %i\n",
sta->addr, arvif->vdev_id, ret);
@@ -5651,6 +5696,20 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,

ath10k_mac_dec_num_stations(arvif, sta);

+ spin_lock_bh(&ar->data_lock);
+ for (i = 0; i < ARRAY_SIZE(ar->peer_map); i++) {
+ peer = ar->peer_map[i];
+ if (!peer)
+ continue;
+
+ if (peer->sta == sta) {
+ ath10k_warn(ar, "found sta peer %pM entry on vdev %i after it was supposedly removed\n",
+ sta->addr, arvif->vdev_id);
+ peer->sta = NULL;
+ }
+ }
+ spin_unlock_bh(&ar->data_lock);
+
if (!sta->tdls)
goto exit;

diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c
index 118586ece20e..202e5192235b 100644
--- a/drivers/net/wireless/ath/ath10k/txrx.c
+++ b/drivers/net/wireless/ath/ath10k/txrx.c
@@ -203,6 +203,7 @@ void ath10k_peer_map_event(struct ath10k_htt *htt,
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt peer map vdev %d peer %pM id %d\n",
ev->vdev_id, ev->addr, ev->peer_id);

+ ar->peer_map[ev->peer_id] = peer;
set_bit(ev->peer_id, peer->peer_ids);
exit:
spin_unlock_bh(&ar->data_lock);
@@ -225,6 +226,7 @@ void ath10k_peer_unmap_event(struct ath10k_htt *htt,
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt peer unmap vdev %d peer %pM id %d\n",
peer->vdev_id, peer->addr, ev->peer_id);

+ ar->peer_map[ev->peer_id] = NULL;
clear_bit(ev->peer_id, peer->peer_ids);

if (bitmap_empty(peer->peer_ids, ATH10K_MAX_NUM_PEER_IDS)) {
--
2.1.4


2016-03-06 14:27:44

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH v2 07/11] ath10k: implement wake_tx_queue

Michal Kazior <[email protected]> writes:

> This implements very basic support for software
> queueing. It also contains some knobs that will be
> patched later.
>
> Signed-off-by: Michal Kazior <[email protected]>

Oddly this patch introduces a new warning:

drivers/net/wireless/ath/ath10k/mac.c: In function ath10k_mac_op_tx:
drivers/net/wireless/ath/ath10k/mac.c:3949:29: warning: is_mgmt may be used uninitialized in this function [-Wuninitialized]

But I think that's just a false warning, most likely because my gcc is
ancient. Do you agree?

gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3

--
Kalle Valo

2016-03-01 10:32:12

by Michal Kazior

[permalink] [raw]
Subject: [PATCH v2 03/11] ath10k: refactor tx pending management

Tx pending counter logic assumed that the sk_buff
is already known and hence was performed in HTT
functions themselves.

However, for the sake of future wake_tx_queue()
usage the driver must be able to tell whether it
can submit more frames to firmware before it
dequeues frame from ieee80211_txq (and thus long
before HTT Tx functions are called) because once a
frame is dequeued it cannot be requeud back to
mac80211.

This prepares the driver for future changes.

Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/htt.h | 7 ++-
drivers/net/wireless/ath/ath10k/htt_tx.c | 85 ++++++++------------------------
drivers/net/wireless/ath/ath10k/mac.c | 51 ++++++++++++++++---
drivers/net/wireless/ath/ath10k/txrx.c | 2 +-
4 files changed, 71 insertions(+), 74 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
index 13391ea4422d..cb6d4fd687da 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -1753,7 +1753,12 @@ int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt,
u8 max_subfrms_amsdu);
void ath10k_htt_hif_tx_complete(struct ath10k *ar, struct sk_buff *skb);

-void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt, bool limit_mgmt_desc);
+void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt,
+ bool is_mgmt);
+int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt,
+ bool is_mgmt,
+ bool is_presp);
+
int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt, struct sk_buff *skb);
void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id);
int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *);
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
index 95acb727c068..860661d3812f 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -22,9 +22,12 @@
#include "txrx.h"
#include "debug.h"

-void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt, bool limit_mgmt_desc)
+void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt,
+ bool is_mgmt)
{
- if (limit_mgmt_desc)
+ lockdep_assert_held(&htt->tx_lock);
+
+ if (is_mgmt)
htt->num_pending_mgmt_tx--;

htt->num_pending_tx--;
@@ -32,43 +35,31 @@ void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt, bool limit_mgmt_desc)
ath10k_mac_tx_unlock(htt->ar, ATH10K_TX_PAUSE_Q_FULL);
}

-static void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt,
- bool limit_mgmt_desc)
-{
- spin_lock_bh(&htt->tx_lock);
- __ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc);
- spin_unlock_bh(&htt->tx_lock);
-}
-
-static int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt,
- bool limit_mgmt_desc, bool is_probe_resp)
+int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt,
+ bool is_mgmt,
+ bool is_presp)
{
struct ath10k *ar = htt->ar;
- int ret = 0;

- spin_lock_bh(&htt->tx_lock);
+ lockdep_assert_held(&htt->tx_lock);

- if (htt->num_pending_tx >= htt->max_num_pending_tx) {
- ret = -EBUSY;
- goto exit;
- }
+ if (htt->num_pending_tx >= htt->max_num_pending_tx)
+ return -EBUSY;

- if (limit_mgmt_desc) {
- if (is_probe_resp && (htt->num_pending_mgmt_tx >
- ar->hw_params.max_probe_resp_desc_thres)) {
- ret = -EBUSY;
- goto exit;
- }
+ if (is_mgmt &&
+ is_presp &&
+ ar->hw_params.max_probe_resp_desc_thres &&
+ ar->hw_params.max_probe_resp_desc_thres < htt->num_pending_mgmt_tx)
+ return -EBUSY;
+
+ if (is_mgmt)
htt->num_pending_mgmt_tx++;
- }

htt->num_pending_tx++;
if (htt->num_pending_tx == htt->max_num_pending_tx)
ath10k_mac_tx_lock(htt->ar, ATH10K_TX_PAUSE_Q_FULL);

-exit:
- spin_unlock_bh(&htt->tx_lock);
- return ret;
+ return 0;
}

int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt, struct sk_buff *skb)
@@ -576,20 +567,6 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
int msdu_id = -1;
int res;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)msdu->data;
- bool limit_mgmt_desc = false;
- bool is_probe_resp = false;
-
- if (ar->hw_params.max_probe_resp_desc_thres) {
- limit_mgmt_desc = true;
-
- if (ieee80211_is_probe_resp(hdr->frame_control))
- is_probe_resp = true;
- }
-
- res = ath10k_htt_tx_inc_pending(htt, limit_mgmt_desc, is_probe_resp);
-
- if (res)
- goto err;

len += sizeof(cmd->hdr);
len += sizeof(cmd->mgmt_tx);
@@ -598,7 +575,7 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu)
res = ath10k_htt_tx_alloc_msdu_id(htt, msdu);
spin_unlock_bh(&htt->tx_lock);
if (res < 0)
- goto err_tx_dec;
+ goto err;

msdu_id = res;

@@ -649,8 +626,6 @@ err_free_msdu_id:
spin_lock_bh(&htt->tx_lock);
ath10k_htt_tx_free_msdu_id(htt, msdu_id);
spin_unlock_bh(&htt->tx_lock);
-err_tx_dec:
- ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc);
err:
return res;
}
@@ -677,26 +652,12 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode,
u32 frags_paddr = 0;
u32 txbuf_paddr;
struct htt_msdu_ext_desc *ext_desc = NULL;
- bool limit_mgmt_desc = false;
- bool is_probe_resp = false;
-
- if (unlikely(ieee80211_is_mgmt(hdr->frame_control)) &&
- ar->hw_params.max_probe_resp_desc_thres) {
- limit_mgmt_desc = true;
-
- if (ieee80211_is_probe_resp(hdr->frame_control))
- is_probe_resp = true;
- }
-
- res = ath10k_htt_tx_inc_pending(htt, limit_mgmt_desc, is_probe_resp);
- if (res)
- goto err;

spin_lock_bh(&htt->tx_lock);
res = ath10k_htt_tx_alloc_msdu_id(htt, msdu);
spin_unlock_bh(&htt->tx_lock);
if (res < 0)
- goto err_tx_dec;
+ goto err;

msdu_id = res;

@@ -862,11 +823,7 @@ int ath10k_htt_tx(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txmode,
err_unmap_msdu:
dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE);
err_free_msdu_id:
- spin_lock_bh(&htt->tx_lock);
ath10k_htt_tx_free_msdu_id(htt, msdu_id);
- spin_unlock_bh(&htt->tx_lock);
-err_tx_dec:
- ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc);
err:
return res;
}
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 67a50a61afb4..140ad250ea69 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -3358,13 +3358,11 @@ ath10k_mac_tx_h_get_txpath(struct ath10k *ar,

static int ath10k_mac_tx_submit(struct ath10k *ar,
enum ath10k_hw_txrx_mode txmode,
+ enum ath10k_mac_tx_path txpath,
struct sk_buff *skb)
{
struct ath10k_htt *htt = &ar->htt;
- enum ath10k_mac_tx_path txpath;
- int ret;
-
- txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);
+ int ret = -EINVAL;

switch (txpath) {
case ATH10K_MAC_TX_HTT:
@@ -3398,6 +3396,7 @@ static int ath10k_mac_tx(struct ath10k *ar,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
enum ath10k_hw_txrx_mode txmode,
+ enum ath10k_mac_tx_path txpath,
struct sk_buff *skb)
{
struct ieee80211_hw *hw = ar->hw;
@@ -3437,7 +3436,7 @@ static int ath10k_mac_tx(struct ath10k *ar,
}
}

- ret = ath10k_mac_tx_submit(ar, txmode, skb);
+ ret = ath10k_mac_tx_submit(ar, txmode, txpath, skb);
if (ret) {
ath10k_warn(ar, "failed to submit frame: %d\n", ret);
return ret;
@@ -3465,6 +3464,7 @@ void ath10k_offchan_tx_work(struct work_struct *work)
struct ath10k_peer *peer;
struct ath10k_vif *arvif;
enum ath10k_hw_txrx_mode txmode;
+ enum ath10k_mac_tx_path txpath;
struct ieee80211_hdr *hdr;
struct ieee80211_vif *vif;
struct ieee80211_sta *sta;
@@ -3533,8 +3533,9 @@ void ath10k_offchan_tx_work(struct work_struct *work)
}

txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
+ txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);

- ret = ath10k_mac_tx(ar, vif, sta, txmode, skb);
+ ret = ath10k_mac_tx(ar, vif, sta, txmode, txpath, skb);
if (ret) {
ath10k_warn(ar, "failed to transmit offchannel frame: %d\n",
ret);
@@ -3758,19 +3759,53 @@ static void ath10k_mac_op_tx(struct ieee80211_hw *hw,
struct sk_buff *skb)
{
struct ath10k *ar = hw->priv;
+ struct ath10k_htt *htt = &ar->htt;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_vif *vif = info->control.vif;
struct ieee80211_sta *sta = control->sta;
+ struct ieee80211_hdr *hdr = (void *)skb->data;
enum ath10k_hw_txrx_mode txmode;
+ enum ath10k_mac_tx_path txpath;
+ bool is_htt;
+ bool is_mgmt;
+ bool is_presp;
int ret;

ath10k_mac_tx_h_fill_cb(ar, vif, skb);

txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
+ txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);
+ is_htt = (txpath == ATH10K_MAC_TX_HTT ||
+ txpath == ATH10K_MAC_TX_HTT_MGMT);

- ret = ath10k_mac_tx(ar, vif, sta, txmode, skb);
- if (ret)
+ if (is_htt) {
+ spin_lock_bh(&ar->htt.tx_lock);
+
+ is_mgmt = ieee80211_is_mgmt(hdr->frame_control);
+ is_presp = ieee80211_is_probe_resp(hdr->frame_control);
+
+ ret = ath10k_htt_tx_inc_pending(htt, is_mgmt, is_presp);
+ if (ret) {
+ ath10k_warn(ar, "failed to increase tx pending count: %d, dropping\n",
+ ret);
+ spin_unlock_bh(&ar->htt.tx_lock);
+ ieee80211_free_txskb(ar->hw, skb);
+ return;
+ }
+
+ spin_unlock_bh(&ar->htt.tx_lock);
+ }
+
+ ret = ath10k_mac_tx(ar, vif, sta, txmode, txpath, skb);
+ if (ret) {
ath10k_warn(ar, "failed to transmit frame: %d\n", ret);
+ if (is_htt) {
+ spin_lock_bh(&ar->htt.tx_lock);
+ ath10k_htt_tx_dec_pending(htt, is_mgmt);
+ spin_unlock_bh(&ar->htt.tx_lock);
+ }
+ return;
+ }
}

/* Must not be called with conf_mutex held as workers can use that also. */
diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c
index fbfb608e48ab..118586ece20e 100644
--- a/drivers/net/wireless/ath/ath10k/txrx.c
+++ b/drivers/net/wireless/ath/ath10k/txrx.c
@@ -86,7 +86,7 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
limit_mgmt_desc = true;

ath10k_htt_tx_free_msdu_id(htt, tx_done->msdu_id);
- __ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc);
+ ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc);
if (htt->num_pending_tx == 0)
wake_up(&htt->empty_tx_wq);
spin_unlock_bh(&htt->tx_lock);
--
2.1.4


2016-03-04 09:17:58

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH 10/13] ath10k: implement flushing of pending frames in txqs

Michal Kazior <[email protected]> writes:

> Firmware supporting pull-push tx model may request
> a switch between modes. When switching from
> pull-push to push-only it will be necessary to
> flush all pending frames. The code will do that
> once other bits that actually trigger it are added.
>
> Signed-off-by: Michal Kazior <[email protected]>

This fails to apply:

fatal: sha1 information is lacking or useless (drivers/net/wireless/ath/ath10k/htt_rx.c).
Repository lacks necessary blobs to fall back on 3-way merge.
Cannot fall back to three-way merge.
Patch failed at 0010 ath10k: implement flushing of pending frames in txqs

> @@ -2370,6 +2371,8 @@ static void ath10k_htt_txrx_compl_task(unsigned long ptr)
> dev_kfree_skb_any(skb);
> }
>
> + ath10k_mac_tx_push_pending(ar);
> +
> spin_lock_bh(&htt->rx_ring.lock);
> while ((skb = __skb_dequeue(&htt->rx_compl_q))) {
> resp = (struct htt_resp *)skb->data;

This hunk seems to be the problem. I could try to fix it myself but the
function has changed so I would prefer that you respin.

--
Kalle Valo

2016-03-06 14:18:15

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH v2 00/11] ath10k: implement push-pull tx model

Michal Kazior <[email protected]> writes:

> This adds support for the new logic where host
> tells firmware how many frames are queued for each
> station/tid and then firmware asks host to submit
> frames for given station/tid.
>
> The patch count is a bit high but I tried
> splitting the patches as much as possible to keep
> them short and easy to review. Hopefully it's not
> going to be a huge headache.
>
> v2:
> - squashes some patches
> - reworked pending frame scheduling
>
>
> Michal Kazior (11):
> ath10k: refactor tx code
> ath10k: unify txpath decision
> ath10k: refactor tx pending management
> ath10k: maintain peer_id for each sta and vif
> ath10k: add fast peer_map lookup
> ath10k: add new htt message generation/parsing logic
> ath10k: implement wake_tx_queue
> ath10k: implement updating shared htt txq state
> ath10k: store txq in skb_cb
> ath10k: keep track of queue depth per txq
> ath10k: implement push-pull tx

I saw two conflicts, please double check my resolutions in the pending
branch:

Applying: ath10k: implement wake_tx_queue
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
Auto-merging drivers/net/wireless/ath/ath10k/htt_rx.c
CONFLICT (content): Merge conflict in drivers/net/wireless/ath/ath10k/htt_rx.c
Auto-merging drivers/net/wireless/ath/ath10k/core.c
Failed to merge in the changes.
Patch failed at 0007 ath10k: implement wake_tx_queue

[...]

Applying: ath10k: implement push-pull tx
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
Auto-merging drivers/net/wireless/ath/ath10k/htt_rx.c
CONFLICT (content): Merge conflict in drivers/net/wireless/ath/ath10k/htt_rx.c
Failed to merge in the changes.
Patch failed at 0011 ath10k: implement push-pull tx

--
Kalle Valo

2016-03-04 15:37:33

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH 10/13] ath10k: implement flushing of pending frames in txqs

Michal Kazior <[email protected]> writes:

> On 4 March 2016 at 10:17, Valo, Kalle <[email protected]> wrote:
>> Michal Kazior <[email protected]> writes:
>>
>>> Firmware supporting pull-push tx model may request
>>> a switch between modes. When switching from
>>> pull-push to push-only it will be necessary to
>>> flush all pending frames. The code will do that
>>> once other bits that actually trigger it are added.
>>>
>>> Signed-off-by: Michal Kazior <[email protected]>
>>
>> This fails to apply:
>>
>> fatal: sha1 information is lacking or useless (drivers/net/wireless/ath/ath10k/htt_rx.c).
>> Repository lacks necessary blobs to fall back on 3-way merge.
>> Cannot fall back to three-way merge.
>> Patch failed at 0010 ath10k: implement flushing of pending frames in txqs
>>
>>> @@ -2370,6 +2371,8 @@ static void ath10k_htt_txrx_compl_task(unsigned long ptr)
>>> dev_kfree_skb_any(skb);
>>> }
>>>
>>> + ath10k_mac_tx_push_pending(ar);
>>> +
>>> spin_lock_bh(&htt->rx_ring.lock);
>>> while ((skb = __skb_dequeue(&htt->rx_compl_q))) {
>>> resp = (struct htt_resp *)skb->data;
>>
>> This hunk seems to be the problem. I could try to fix it myself but the
>> function has changed so I would prefer that you respin.
>
> This patch is in v1. It's base included some extra patches that
> weren't in ath.git at the time.
>
> The v2 patchset has this patch squashed into wake_tx_queue patch and
> the entire v2 patchset is based solely on ath.git without any extra
> patches in between. Did patchwork confuse these patchsets?

No, it was me who got confused :) I was accidentally applying v1 instead
of v2.

But v2 also has conflicts with ath-next. I need to first update my trees
to get latest cfg80211/mac80211 and then I'll try again.

--
Kalle Valo

2016-03-01 10:32:17

by Michal Kazior

[permalink] [raw]
Subject: [PATCH v2 06/11] ath10k: add new htt message generation/parsing logic

This merely adds some parsing, generation and
sanity checks with placeholders for real
code/functionality to be added later.

Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/htt.h | 5 +
drivers/net/wireless/ath/ath10k/htt_rx.c | 198 ++++++++++++++++++++++++++++++-
drivers/net/wireless/ath/ath10k/htt_tx.c | 53 +++++++++
3 files changed, 255 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
index cb6d4fd687da..65dcd22f31df 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -1752,6 +1752,11 @@ int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt,
u8 max_subfrms_ampdu,
u8 max_subfrms_amsdu);
void ath10k_htt_hif_tx_complete(struct ath10k *ar, struct sk_buff *skb);
+int ath10k_htt_tx_fetch_resp(struct ath10k *ar,
+ __le32 token,
+ __le16 fetch_seq_num,
+ struct htt_tx_fetch_record *records,
+ size_t num_records);

void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt,
bool is_mgmt);
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index cc957a625605..be7dc88b3316 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -1982,6 +1982,198 @@ static void ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb)
tasklet_schedule(&htt->rx_replenish_task);
}

+static void ath10k_htt_rx_tx_fetch_resp_id_confirm(struct ath10k *ar,
+ const __le32 *resp_ids,
+ int num_resp_ids)
+{
+ int i;
+ u32 resp_id;
+
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch confirm num_resp_ids %d\n",
+ num_resp_ids);
+
+ for (i = 0; i < num_resp_ids; i++) {
+ resp_id = le32_to_cpu(resp_ids[i]);
+
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch confirm resp_id %u\n",
+ resp_id);
+
+ /* TODO: free resp_id */
+ }
+}
+
+static void ath10k_htt_rx_tx_fetch_ind(struct ath10k *ar, struct sk_buff *skb)
+{
+ struct htt_resp *resp = (struct htt_resp *)skb->data;
+ struct htt_tx_fetch_record *record;
+ size_t len;
+ size_t max_num_bytes;
+ size_t max_num_msdus;
+ const __le32 *resp_ids;
+ u16 num_records;
+ u16 num_resp_ids;
+ u16 peer_id;
+ u8 tid;
+ int i;
+
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch ind\n");
+
+ len = sizeof(resp->hdr) + sizeof(resp->tx_fetch_ind);
+ if (unlikely(skb->len < len)) {
+ ath10k_warn(ar, "received corrupted tx_fetch_ind event: buffer too short\n");
+ return;
+ }
+
+ num_records = le16_to_cpu(resp->tx_fetch_ind.num_records);
+ num_resp_ids = le16_to_cpu(resp->tx_fetch_ind.num_resp_ids);
+
+ len += sizeof(resp->tx_fetch_ind.records[0]) * num_records;
+ len += sizeof(resp->tx_fetch_ind.resp_ids[0]) * num_resp_ids;
+
+ if (unlikely(skb->len < len)) {
+ ath10k_warn(ar, "received corrupted tx_fetch_ind event: too many records/resp_ids\n");
+ return;
+ }
+
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch ind num records %hu num resps %hu seq %hu\n",
+ num_records, num_resp_ids,
+ le16_to_cpu(resp->tx_fetch_ind.fetch_seq_num));
+
+ /* TODO: runtime sanity checks */
+
+ for (i = 0; i < num_records; i++) {
+ record = &resp->tx_fetch_ind.records[i];
+ peer_id = MS(le16_to_cpu(record->info),
+ HTT_TX_FETCH_RECORD_INFO_PEER_ID);
+ tid = MS(le16_to_cpu(record->info),
+ HTT_TX_FETCH_RECORD_INFO_TID);
+ max_num_msdus = le16_to_cpu(record->num_msdus);
+ max_num_bytes = le32_to_cpu(record->num_bytes);
+
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch record %i peer_id %hu tid %hhu msdus %zu bytes %zu\n",
+ i, peer_id, tid, max_num_msdus, max_num_bytes);
+
+ if (unlikely(peer_id >= ar->htt.tx_q_state.num_peers) ||
+ unlikely(tid >= ar->htt.tx_q_state.num_tids)) {
+ ath10k_warn(ar, "received out of range peer_id %hu tid %hhu\n",
+ peer_id, tid);
+ continue;
+ }
+
+ /* TODO: dequeue and submit tx to device */
+ }
+
+ resp_ids = ath10k_htt_get_tx_fetch_ind_resp_ids(&resp->tx_fetch_ind);
+ ath10k_htt_rx_tx_fetch_resp_id_confirm(ar, resp_ids, num_resp_ids);
+
+ /* TODO: generate and send fetch response to device */
+}
+
+static void ath10k_htt_rx_tx_fetch_confirm(struct ath10k *ar,
+ struct sk_buff *skb)
+{
+ const struct htt_resp *resp = (void *)skb->data;
+ size_t len;
+ int num_resp_ids;
+
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx fetch confirm\n");
+
+ len = sizeof(resp->hdr) + sizeof(resp->tx_fetch_confirm);
+ if (unlikely(skb->len < len)) {
+ ath10k_warn(ar, "received corrupted tx_fetch_confirm event: buffer too short\n");
+ return;
+ }
+
+ num_resp_ids = le16_to_cpu(resp->tx_fetch_confirm.num_resp_ids);
+ len += sizeof(resp->tx_fetch_confirm.resp_ids[0]) * num_resp_ids;
+
+ if (unlikely(skb->len < len)) {
+ ath10k_warn(ar, "received corrupted tx_fetch_confirm event: resp_ids buffer overflow\n");
+ return;
+ }
+
+ ath10k_htt_rx_tx_fetch_resp_id_confirm(ar,
+ resp->tx_fetch_confirm.resp_ids,
+ num_resp_ids);
+}
+
+static void ath10k_htt_rx_tx_mode_switch_ind(struct ath10k *ar,
+ struct sk_buff *skb)
+{
+ const struct htt_resp *resp = (void *)skb->data;
+ const struct htt_tx_mode_switch_record *record;
+ size_t len;
+ size_t num_records;
+ enum htt_tx_mode_switch_mode mode;
+ bool enable;
+ u16 info0;
+ u16 info1;
+ u16 threshold;
+ u16 peer_id;
+ u8 tid;
+ int i;
+
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt rx tx mode switch ind\n");
+
+ len = sizeof(resp->hdr) + sizeof(resp->tx_mode_switch_ind);
+ if (unlikely(skb->len < len)) {
+ ath10k_warn(ar, "received corrupted tx_mode_switch_ind event: buffer too short\n");
+ return;
+ }
+
+ info0 = le16_to_cpu(resp->tx_mode_switch_ind.info0);
+ info1 = le16_to_cpu(resp->tx_mode_switch_ind.info1);
+
+ enable = !!(info0 & HTT_TX_MODE_SWITCH_IND_INFO0_ENABLE);
+ num_records = MS(info0, HTT_TX_MODE_SWITCH_IND_INFO1_THRESHOLD);
+ mode = MS(info1, HTT_TX_MODE_SWITCH_IND_INFO1_MODE);
+ threshold = MS(info1, HTT_TX_MODE_SWITCH_IND_INFO1_THRESHOLD);
+
+ ath10k_dbg(ar, ATH10K_DBG_HTT,
+ "htt rx tx mode switch ind info0 0x%04hx info1 0x%04hx enable %d num records %zd mode %d threshold %hu\n",
+ info0, info1, enable, num_records, mode, threshold);
+
+ len += sizeof(resp->tx_mode_switch_ind.records[0]) * num_records;
+
+ if (unlikely(skb->len < len)) {
+ ath10k_warn(ar, "received corrupted tx_mode_switch_mode_ind event: too many records\n");
+ return;
+ }
+
+ switch (mode) {
+ case HTT_TX_MODE_SWITCH_PUSH:
+ case HTT_TX_MODE_SWITCH_PUSH_PULL:
+ break;
+ default:
+ ath10k_warn(ar, "received invalid tx_mode_switch_mode_ind mode %d, ignoring\n",
+ mode);
+ return;
+ }
+
+ if (!enable)
+ return;
+
+ /* TODO: apply configuration */
+
+ for (i = 0; i < num_records; i++) {
+ record = &resp->tx_mode_switch_ind.records[i];
+ info0 = le16_to_cpu(record->info0);
+ peer_id = MS(info0, HTT_TX_MODE_SWITCH_RECORD_INFO0_PEER_ID);
+ tid = MS(info0, HTT_TX_MODE_SWITCH_RECORD_INFO0_TID);
+
+ if (unlikely(peer_id >= ar->htt.tx_q_state.num_peers) ||
+ unlikely(tid >= ar->htt.tx_q_state.num_tids)) {
+ ath10k_warn(ar, "received out of range peer_id %hu tid %hhu\n",
+ peer_id, tid);
+ continue;
+ }
+
+ /* TODO: apply configuration */
+ }
+
+ /* TODO: apply configuration */
+}
+
void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
{
struct ath10k_htt *htt = &ar->htt;
@@ -2124,9 +2316,13 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
case HTT_T2H_MSG_TYPE_AGGR_CONF:
break;
case HTT_T2H_MSG_TYPE_TX_FETCH_IND:
+ ath10k_htt_rx_tx_fetch_ind(ar, skb);
+ break;
case HTT_T2H_MSG_TYPE_TX_FETCH_CONFIRM:
+ ath10k_htt_rx_tx_fetch_confirm(ar, skb);
+ break;
case HTT_T2H_MSG_TYPE_TX_MODE_SWITCH_IND:
- /* TODO: Implement pull-push logic */
+ ath10k_htt_rx_tx_mode_switch_ind(ar, skb);
break;
case HTT_T2H_MSG_TYPE_EN_STATS:
default:
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
index 860661d3812f..225f0561b3fd 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -526,6 +526,59 @@ int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt,
return 0;
}

+int ath10k_htt_tx_fetch_resp(struct ath10k *ar,
+ __le32 token,
+ __le16 fetch_seq_num,
+ struct htt_tx_fetch_record *records,
+ size_t num_records)
+{
+ struct sk_buff *skb;
+ struct htt_cmd *cmd;
+ u16 resp_id;
+ int len = 0;
+ int ret;
+
+ len += sizeof(cmd->hdr);
+ len += sizeof(cmd->tx_fetch_resp);
+ len += sizeof(cmd->tx_fetch_resp.records[0]) * num_records;
+
+ skb = ath10k_htc_alloc_skb(ar, len);
+ if (!skb)
+ return -ENOMEM;
+
+ resp_id = 0; /* TODO: allocate resp_id */
+ ret = 0;
+ if (ret)
+ goto err_free_skb;
+
+ skb_put(skb, len);
+ cmd = (struct htt_cmd *)skb->data;
+ cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_TX_FETCH_RESP;
+ cmd->tx_fetch_resp.resp_id = cpu_to_le16(resp_id);
+ cmd->tx_fetch_resp.fetch_seq_num = fetch_seq_num;
+ cmd->tx_fetch_resp.num_records = cpu_to_le16(num_records);
+ cmd->tx_fetch_resp.token = token;
+
+ memcpy(cmd->tx_fetch_resp.records, records,
+ sizeof(records[0]) * num_records);
+
+ ret = ath10k_htc_send(&ar->htc, ar->htt.eid, skb);
+ if (ret) {
+ ath10k_warn(ar, "failed to submit htc command: %d\n", ret);
+ goto err_free_resp_id;
+ }
+
+ return 0;
+
+err_free_resp_id:
+ (void)resp_id; /* TODO: free resp_id */
+
+err_free_skb:
+ dev_kfree_skb_any(skb);
+
+ return ret;
+}
+
static u8 ath10k_htt_tx_get_vdev_id(struct ath10k *ar, struct sk_buff *skb)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
--
2.1.4


2016-03-07 09:36:54

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH v2 07/11] ath10k: implement wake_tx_queue

Michal Kazior <[email protected]> writes:

> On 6 March 2016 at 15:27, Valo, Kalle <[email protected]> wrote:
>> Michal Kazior <[email protected]> writes:
>>
>>> This implements very basic support for software
>>> queueing. It also contains some knobs that will be
>>> patched later.
>>>
>>> Signed-off-by: Michal Kazior <[email protected]>
>>
>> Oddly this patch introduces a new warning:
>>
>> drivers/net/wireless/ath/ath10k/mac.c: In function ath10k_mac_op_tx:
>> drivers/net/wireless/ath/ath10k/mac.c:3949:29: warning: is_mgmt may be used uninitialized in this function [-Wuninitialized]
>>
>> But I think that's just a false warning, most likely because my gcc is
>> ancient. Do you agree?
>
> Yes, I agree. It must be the fact is_mgmt is set/used across two
> is_htt condition branches.
>
>
>> gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
>
> gcc (Debian 4.9.2-10) 4.9.2 -- no warning for me.

Actually kbuild also found this but let's just ignore it.

--
Kalle Valo

2016-03-01 10:32:10

by Michal Kazior

[permalink] [raw]
Subject: [PATCH v2 02/11] ath10k: unify txpath decision

Some future changes will need to determine final
tx method early on. Prepare the code.

Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/mac.c | 58 +++++++++++++++++++++++++++--------
1 file changed, 45 insertions(+), 13 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index b3a790addb0a..67a50a61afb4 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -2994,6 +2994,13 @@ static void ath10k_reg_notifier(struct wiphy *wiphy,
/* TX handlers */
/***************/

+enum ath10k_mac_tx_path {
+ ATH10K_MAC_TX_HTT,
+ ATH10K_MAC_TX_HTT_MGMT,
+ ATH10K_MAC_TX_WMI_MGMT,
+ ATH10K_MAC_TX_UNKNOWN,
+};
+
void ath10k_mac_tx_lock(struct ath10k *ar, int reason)
{
lockdep_assert_held(&ar->htt.tx_lock);
@@ -3326,27 +3333,52 @@ unlock:
return ret;
}

+static enum ath10k_mac_tx_path
+ath10k_mac_tx_h_get_txpath(struct ath10k *ar,
+ struct sk_buff *skb,
+ enum ath10k_hw_txrx_mode txmode)
+{
+ switch (txmode) {
+ case ATH10K_HW_TXRX_RAW:
+ case ATH10K_HW_TXRX_NATIVE_WIFI:
+ case ATH10K_HW_TXRX_ETHERNET:
+ return ATH10K_MAC_TX_HTT;
+ case ATH10K_HW_TXRX_MGMT:
+ if (test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
+ ar->fw_features))
+ return ATH10K_MAC_TX_WMI_MGMT;
+ else if (ar->htt.target_version_major >= 3)
+ return ATH10K_MAC_TX_HTT;
+ else
+ return ATH10K_MAC_TX_HTT_MGMT;
+ }
+
+ return ATH10K_MAC_TX_UNKNOWN;
+}
+
static int ath10k_mac_tx_submit(struct ath10k *ar,
enum ath10k_hw_txrx_mode txmode,
struct sk_buff *skb)
{
struct ath10k_htt *htt = &ar->htt;
- int ret = 0;
+ enum ath10k_mac_tx_path txpath;
+ int ret;

- switch (txmode) {
- case ATH10K_HW_TXRX_RAW:
- case ATH10K_HW_TXRX_NATIVE_WIFI:
- case ATH10K_HW_TXRX_ETHERNET:
+ txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);
+
+ switch (txpath) {
+ case ATH10K_MAC_TX_HTT:
ret = ath10k_htt_tx(htt, txmode, skb);
break;
- case ATH10K_HW_TXRX_MGMT:
- if (test_bit(ATH10K_FW_FEATURE_HAS_WMI_MGMT_TX,
- ar->fw_features))
- ret = ath10k_mac_tx_wmi_mgmt(ar, skb);
- else if (ar->htt.target_version_major >= 3)
- ret = ath10k_htt_tx(htt, txmode, skb);
- else
- ret = ath10k_htt_mgmt_tx(htt, skb);
+ case ATH10K_MAC_TX_HTT_MGMT:
+ ret = ath10k_htt_mgmt_tx(htt, skb);
+ break;
+ case ATH10K_MAC_TX_WMI_MGMT:
+ ret = ath10k_mac_tx_wmi_mgmt(ar, skb);
+ break;
+ case ATH10K_MAC_TX_UNKNOWN:
+ WARN_ON_ONCE(1);
+ ret = -EINVAL;
break;
}

--
2.1.4


2016-03-07 06:13:17

by Michal Kazior

[permalink] [raw]
Subject: Re: [PATCH v2 00/11] ath10k: implement push-pull tx model

On 6 March 2016 at 15:18, Valo, Kalle <[email protected]> wrote:
> Michal Kazior <[email protected]> writes:
>
>> This adds support for the new logic where host
>> tells firmware how many frames are queued for each
>> station/tid and then firmware asks host to submit
>> frames for given station/tid.
>>
>> The patch count is a bit high but I tried
>> splitting the patches as much as possible to keep
>> them short and easy to review. Hopefully it's not
>> going to be a huge headache.
>>
>> v2:
>> - squashes some patches
>> - reworked pending frame scheduling
>>
>>
>> Michal Kazior (11):
>> ath10k: refactor tx code
>> ath10k: unify txpath decision
>> ath10k: refactor tx pending management
>> ath10k: maintain peer_id for each sta and vif
>> ath10k: add fast peer_map lookup
>> ath10k: add new htt message generation/parsing logic
>> ath10k: implement wake_tx_queue
>> ath10k: implement updating shared htt txq state
>> ath10k: store txq in skb_cb
>> ath10k: keep track of queue depth per txq
>> ath10k: implement push-pull tx
>
> I saw two conflicts, please double check my resolutions in the pending
> branch:
>
> Applying: ath10k: implement wake_tx_queue
> CONFLICT (content): Merge conflict in drivers/net/wireless/ath/ath10k/htt_rx.c
[...]
> Applying: ath10k: implement push-pull tx
> CONFLICT (content): Merge conflict in drivers/net/wireless/ath/ath10k/htt_rx.c

Looks good, thanks!


Michał

2016-03-01 10:32:09

by Michal Kazior

[permalink] [raw]
Subject: [PATCH v2 01/11] ath10k: refactor tx code

This prepares the code for future reuse with
ieee80211_txq and wake_tx_queue() in mind.

Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/mac.c | 151 +++++++++++++++++++++-------------
1 file changed, 96 insertions(+), 55 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 78999c9de23b..b3a790addb0a 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -3271,6 +3271,26 @@ static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar,
}
}

+static void ath10k_mac_tx_h_fill_cb(struct ath10k *ar,
+ struct ieee80211_vif *vif,
+ struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (void *)skb->data;
+ struct ath10k_skb_cb *cb = ATH10K_SKB_CB(skb);
+
+ cb->flags = 0;
+ if (!ath10k_tx_h_use_hwcrypto(vif, skb))
+ cb->flags |= ATH10K_SKB_F_NO_HWCRYPT;
+
+ if (ieee80211_is_mgmt(hdr->frame_control))
+ cb->flags |= ATH10K_SKB_F_MGMT;
+
+ if (ieee80211_is_data_qos(hdr->frame_control))
+ cb->flags |= ATH10K_SKB_F_QOS;
+
+ cb->vif = vif;
+}
+
bool ath10k_mac_tx_frm_has_freq(struct ath10k *ar)
{
/* FIXME: Not really sure since when the behaviour changed. At some
@@ -3306,8 +3326,9 @@ unlock:
return ret;
}

-static void ath10k_mac_tx(struct ath10k *ar, enum ath10k_hw_txrx_mode txmode,
- struct sk_buff *skb)
+static int ath10k_mac_tx_submit(struct ath10k *ar,
+ enum ath10k_hw_txrx_mode txmode,
+ struct sk_buff *skb)
{
struct ath10k_htt *htt = &ar->htt;
int ret = 0;
@@ -3334,6 +3355,63 @@ static void ath10k_mac_tx(struct ath10k *ar, enum ath10k_hw_txrx_mode txmode,
ret);
ieee80211_free_txskb(ar->hw, skb);
}
+
+ return ret;
+}
+
+/* This function consumes the sk_buff regardless of return value as far as
+ * caller is concerned so no freeing is necessary afterwards.
+ */
+static int ath10k_mac_tx(struct ath10k *ar,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ enum ath10k_hw_txrx_mode txmode,
+ struct sk_buff *skb)
+{
+ struct ieee80211_hw *hw = ar->hw;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ int ret;
+
+ /* We should disable CCK RATE due to P2P */
+ if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n");
+
+ switch (txmode) {
+ case ATH10K_HW_TXRX_MGMT:
+ case ATH10K_HW_TXRX_NATIVE_WIFI:
+ ath10k_tx_h_nwifi(hw, skb);
+ ath10k_tx_h_add_p2p_noa_ie(ar, vif, skb);
+ ath10k_tx_h_seq_no(vif, skb);
+ break;
+ case ATH10K_HW_TXRX_ETHERNET:
+ ath10k_tx_h_8023(skb);
+ break;
+ case ATH10K_HW_TXRX_RAW:
+ if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) {
+ WARN_ON_ONCE(1);
+ ieee80211_free_txskb(hw, skb);
+ return -ENOTSUPP;
+ }
+ }
+
+ if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
+ if (!ath10k_mac_tx_frm_has_freq(ar)) {
+ ath10k_dbg(ar, ATH10K_DBG_MAC, "queued offchannel skb %p\n",
+ skb);
+
+ skb_queue_tail(&ar->offchan_tx_queue, skb);
+ ieee80211_queue_work(hw, &ar->offchan_tx_work);
+ return 0;
+ }
+ }
+
+ ret = ath10k_mac_tx_submit(ar, txmode, skb);
+ if (ret) {
+ ath10k_warn(ar, "failed to submit frame: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
}

void ath10k_offchan_tx_purge(struct ath10k *ar)
@@ -3354,12 +3432,12 @@ void ath10k_offchan_tx_work(struct work_struct *work)
struct ath10k *ar = container_of(work, struct ath10k, offchan_tx_work);
struct ath10k_peer *peer;
struct ath10k_vif *arvif;
+ enum ath10k_hw_txrx_mode txmode;
struct ieee80211_hdr *hdr;
struct ieee80211_vif *vif;
struct ieee80211_sta *sta;
struct sk_buff *skb;
const u8 *peer_addr;
- enum ath10k_hw_txrx_mode txmode;
int vdev_id;
int ret;
unsigned long time_left;
@@ -3424,7 +3502,12 @@ void ath10k_offchan_tx_work(struct work_struct *work)

txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);

- ath10k_mac_tx(ar, txmode, skb);
+ ret = ath10k_mac_tx(ar, vif, sta, txmode, skb);
+ if (ret) {
+ ath10k_warn(ar, "failed to transmit offchannel frame: %d\n",
+ ret);
+ /* not serious */
+ }

time_left =
wait_for_completion_timeout(&ar->offchan_tx_completed, 3 * HZ);
@@ -3638,66 +3721,24 @@ static int ath10k_start_scan(struct ath10k *ar,
/* mac80211 callbacks */
/**********************/

-static void ath10k_tx(struct ieee80211_hw *hw,
- struct ieee80211_tx_control *control,
- struct sk_buff *skb)
+static void ath10k_mac_op_tx(struct ieee80211_hw *hw,
+ struct ieee80211_tx_control *control,
+ struct sk_buff *skb)
{
struct ath10k *ar = hw->priv;
- struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_vif *vif = info->control.vif;
struct ieee80211_sta *sta = control->sta;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
enum ath10k_hw_txrx_mode txmode;
+ int ret;

- /* We should disable CCK RATE due to P2P */
- if (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)
- ath10k_dbg(ar, ATH10K_DBG_MAC, "IEEE80211_TX_CTL_NO_CCK_RATE\n");
+ ath10k_mac_tx_h_fill_cb(ar, vif, skb);

txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);

- skb_cb->flags = 0;
- if (!ath10k_tx_h_use_hwcrypto(vif, skb))
- skb_cb->flags |= ATH10K_SKB_F_NO_HWCRYPT;
-
- if (ieee80211_is_mgmt(hdr->frame_control))
- skb_cb->flags |= ATH10K_SKB_F_MGMT;
-
- if (ieee80211_is_data_qos(hdr->frame_control))
- skb_cb->flags |= ATH10K_SKB_F_QOS;
-
- skb_cb->vif = vif;
-
- switch (txmode) {
- case ATH10K_HW_TXRX_MGMT:
- case ATH10K_HW_TXRX_NATIVE_WIFI:
- ath10k_tx_h_nwifi(hw, skb);
- ath10k_tx_h_add_p2p_noa_ie(ar, vif, skb);
- ath10k_tx_h_seq_no(vif, skb);
- break;
- case ATH10K_HW_TXRX_ETHERNET:
- ath10k_tx_h_8023(skb);
- break;
- case ATH10K_HW_TXRX_RAW:
- if (!test_bit(ATH10K_FLAG_RAW_MODE, &ar->dev_flags)) {
- WARN_ON_ONCE(1);
- ieee80211_free_txskb(hw, skb);
- return;
- }
- }
-
- if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) {
- if (!ath10k_mac_tx_frm_has_freq(ar)) {
- ath10k_dbg(ar, ATH10K_DBG_MAC, "queued offchannel skb %p\n",
- skb);
-
- skb_queue_tail(&ar->offchan_tx_queue, skb);
- ieee80211_queue_work(hw, &ar->offchan_tx_work);
- return;
- }
- }
-
- ath10k_mac_tx(ar, txmode, skb);
+ ret = ath10k_mac_tx(ar, vif, sta, txmode, skb);
+ if (ret)
+ ath10k_warn(ar, "failed to transmit frame: %d\n", ret);
}

/* Must not be called with conf_mutex held as workers can use that also. */
@@ -6807,7 +6848,7 @@ ath10k_mac_op_switch_vif_chanctx(struct ieee80211_hw *hw,
}

static const struct ieee80211_ops ath10k_ops = {
- .tx = ath10k_tx,
+ .tx = ath10k_mac_op_tx,
.start = ath10k_start,
.stop = ath10k_stop,
.config = ath10k_config,
--
2.1.4


2016-03-07 06:04:17

by Michal Kazior

[permalink] [raw]
Subject: Re: [PATCH v2 07/11] ath10k: implement wake_tx_queue

On 6 March 2016 at 15:27, Valo, Kalle <[email protected]> wrote:
> Michal Kazior <[email protected]> writes:
>
>> This implements very basic support for software
>> queueing. It also contains some knobs that will be
>> patched later.
>>
>> Signed-off-by: Michal Kazior <[email protected]>
>
> Oddly this patch introduces a new warning:
>
> drivers/net/wireless/ath/ath10k/mac.c: In function ath10k_mac_op_tx:
> drivers/net/wireless/ath/ath10k/mac.c:3949:29: warning: is_mgmt may be used uninitialized in this function [-Wuninitialized]
>
> But I think that's just a false warning, most likely because my gcc is
> ancient. Do you agree?

Yes, I agree. It must be the fact is_mgmt is set/used across two
is_htt condition branches.


> gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3

gcc (Debian 4.9.2-10) 4.9.2 -- no warning for me.


Michał

2016-03-01 10:32:14

by Michal Kazior

[permalink] [raw]
Subject: [PATCH v2 04/11] ath10k: maintain peer_id for each sta and vif

The 10.4.3 firmware with congestion control
guarantees that each peer has only a single
peer_id mapping.

The 1:1 mapping isn't the case for older firmwares
(e.g. 10.4.1, 10.2, 10.1) but it should not
matter. This 1:1 mapping is going to be only used
by future code which inherently (flow-wise) is for
10.4.3.

Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/core.h | 2 ++
drivers/net/wireless/ath/ath10k/mac.c | 38 ++++++++++++++++++++++++++++++++++
2 files changed, 40 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index a62b62a62266..523585e85f35 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -313,6 +313,7 @@ struct ath10k_sta {
u32 bw;
u32 nss;
u32 smps;
+ u16 peer_id;

struct work_struct update_wk;

@@ -335,6 +336,7 @@ struct ath10k_vif {
struct list_head list;

u32 vdev_id;
+ u16 peer_id;
enum wmi_vdev_type vdev_type;
enum wmi_vdev_subtype vdev_subtype;
u32 beacon_interval;
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 140ad250ea69..72b8f177445d 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -4421,6 +4421,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
{
struct ath10k *ar = hw->priv;
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
+ struct ath10k_peer *peer;
enum wmi_sta_powersave_param param;
int ret = 0;
u32 value;
@@ -4620,6 +4621,24 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
arvif->vdev_id, ret);
goto err_vdev_delete;
}
+
+ spin_lock_bh(&ar->data_lock);
+
+ peer = ath10k_peer_find(ar, arvif->vdev_id, vif->addr);
+ if (!peer) {
+ ath10k_warn(ar, "failed to lookup peer %pM on vdev %i\n",
+ vif->addr, arvif->vdev_id);
+ spin_unlock_bh(&ar->data_lock);
+ ret = -ENOENT;
+ goto err_peer_delete;
+ }
+
+ arvif->peer_id = find_first_bit(peer->peer_ids,
+ ATH10K_MAX_NUM_PEER_IDS);
+
+ spin_unlock_bh(&ar->data_lock);
+ } else {
+ arvif->peer_id = HTT_INVALID_PEERID;
}

if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
@@ -5501,6 +5520,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
struct ath10k *ar = hw->priv;
struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif);
struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv;
+ struct ath10k_peer *peer;
int ret = 0;

if (old_state == IEEE80211_STA_NOTEXIST &&
@@ -5551,6 +5571,24 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
goto exit;
}

+ spin_lock_bh(&ar->data_lock);
+
+ peer = ath10k_peer_find(ar, arvif->vdev_id, sta->addr);
+ if (!peer) {
+ ath10k_warn(ar, "failed to lookup peer %pM on vdev %i\n",
+ vif->addr, arvif->vdev_id);
+ spin_unlock_bh(&ar->data_lock);
+ ath10k_peer_delete(ar, arvif->vdev_id, sta->addr);
+ ath10k_mac_dec_num_stations(arvif, sta);
+ ret = -ENOENT;
+ goto exit;
+ }
+
+ arsta->peer_id = find_first_bit(peer->peer_ids,
+ ATH10K_MAX_NUM_PEER_IDS);
+
+ spin_unlock_bh(&ar->data_lock);
+
if (!sta->tdls)
goto exit;

--
2.1.4


2016-03-01 10:32:22

by Michal Kazior

[permalink] [raw]
Subject: [PATCH v2 09/11] ath10k: store txq in skb_cb

This will be necessary for later.

Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/core.h | 1 +
drivers/net/wireless/ath/ath10k/mac.c | 19 +++++++++++++++++--
2 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 7df35628ca35..89f789f3e5b4 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -98,6 +98,7 @@ struct ath10k_skb_cb {
u8 eid;
u16 msdu_id;
struct ieee80211_vif *vif;
+ struct ieee80211_txq *txq;
} __packed;

struct ath10k_skb_rxcb {
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 900c64b65b43..8d02d53fdc2c 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -3307,6 +3307,7 @@ static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar,

static void ath10k_mac_tx_h_fill_cb(struct ath10k *ar,
struct ieee80211_vif *vif,
+ struct ieee80211_txq *txq,
struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (void *)skb->data;
@@ -3323,6 +3324,7 @@ static void ath10k_mac_tx_h_fill_cb(struct ath10k *ar,
cb->flags |= ATH10K_SKB_F_QOS;

cb->vif = vif;
+ cb->txq = txq;
}

bool ath10k_mac_tx_frm_has_freq(struct ath10k *ar)
@@ -3633,6 +3635,9 @@ static void ath10k_mac_txq_init(struct ieee80211_txq *txq)
static void ath10k_mac_txq_unref(struct ath10k *ar, struct ieee80211_txq *txq)
{
struct ath10k_txq *artxq = (void *)txq->drv_priv;
+ struct ath10k_skb_cb *cb;
+ struct sk_buff *msdu;
+ int msdu_id;

if (!txq)
return;
@@ -3641,6 +3646,14 @@ static void ath10k_mac_txq_unref(struct ath10k *ar, struct ieee80211_txq *txq)
if (!list_empty(&artxq->list))
list_del_init(&artxq->list);
spin_unlock_bh(&ar->txqs_lock);
+
+ spin_lock_bh(&ar->htt.tx_lock);
+ idr_for_each_entry(&ar->htt.pending_tx, msdu, msdu_id) {
+ cb = ATH10K_SKB_CB(msdu);
+ if (cb->txq == txq)
+ cb->txq = NULL;
+ }
+ spin_unlock_bh(&ar->htt.tx_lock);
}

static bool ath10k_mac_tx_can_push(struct ieee80211_hw *hw,
@@ -3679,7 +3692,7 @@ static int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
return -ENOENT;
}

- ath10k_mac_tx_h_fill_cb(ar, vif, skb);
+ ath10k_mac_tx_h_fill_cb(ar, vif, txq, skb);

txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);
@@ -3909,6 +3922,7 @@ static void ath10k_mac_op_tx(struct ieee80211_hw *hw,
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_vif *vif = info->control.vif;
struct ieee80211_sta *sta = control->sta;
+ struct ieee80211_txq *txq = NULL;
struct ieee80211_hdr *hdr = (void *)skb->data;
enum ath10k_hw_txrx_mode txmode;
enum ath10k_mac_tx_path txpath;
@@ -3917,7 +3931,7 @@ static void ath10k_mac_op_tx(struct ieee80211_hw *hw,
bool is_presp;
int ret;

- ath10k_mac_tx_h_fill_cb(ar, vif, skb);
+ ath10k_mac_tx_h_fill_cb(ar, vif, txq, skb);

txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);
@@ -4985,6 +4999,7 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
spin_unlock_bh(&ar->data_lock);

ath10k_peer_cleanup(ar, arvif->vdev_id);
+ ath10k_mac_txq_unref(ar, vif->txq);

if (vif->type == NL80211_IFTYPE_MONITOR) {
ar->monitor_arvif = NULL;
--
2.1.4


2016-03-04 09:52:05

by Michal Kazior

[permalink] [raw]
Subject: Re: [PATCH 10/13] ath10k: implement flushing of pending frames in txqs

On 4 March 2016 at 10:17, Valo, Kalle <[email protected]> wrote:
> Michal Kazior <[email protected]> writes:
>
>> Firmware supporting pull-push tx model may request
>> a switch between modes. When switching from
>> pull-push to push-only it will be necessary to
>> flush all pending frames. The code will do that
>> once other bits that actually trigger it are added.
>>
>> Signed-off-by: Michal Kazior <[email protected]>
>
> This fails to apply:
>
> fatal: sha1 information is lacking or useless (drivers/net/wireless/ath/ath10k/htt_rx.c).
> Repository lacks necessary blobs to fall back on 3-way merge.
> Cannot fall back to three-way merge.
> Patch failed at 0010 ath10k: implement flushing of pending frames in txqs
>
>> @@ -2370,6 +2371,8 @@ static void ath10k_htt_txrx_compl_task(unsigned long ptr)
>> dev_kfree_skb_any(skb);
>> }
>>
>> + ath10k_mac_tx_push_pending(ar);
>> +
>> spin_lock_bh(&htt->rx_ring.lock);
>> while ((skb = __skb_dequeue(&htt->rx_compl_q))) {
>> resp = (struct htt_resp *)skb->data;
>
> This hunk seems to be the problem. I could try to fix it myself but the
> function has changed so I would prefer that you respin.

This patch is in v1. It's base included some extra patches that
weren't in ath.git at the time.

The v2 patchset has this patch squashed into wake_tx_queue patch and
the entire v2 patchset is based solely on ath.git without any extra
patches in between. Did patchwork confuse these patchsets?


Michał

2016-03-01 10:32:19

by Michal Kazior

[permalink] [raw]
Subject: [PATCH v2 07/11] ath10k: implement wake_tx_queue

This implements very basic support for software
queueing. It also contains some knobs that will be
patched later.

Signed-off-by: Michal Kazior <[email protected]>
---

Notes:
v2:
- squashed with `ath10k: add txq placeholder`
- squashed with `ath10k: implement flushing of pending frames in txqs`
- reworked pending logic

drivers/net/wireless/ath/ath10k/core.c | 2 +
drivers/net/wireless/ath/ath10k/core.h | 7 ++
drivers/net/wireless/ath/ath10k/htt_rx.c | 3 +
drivers/net/wireless/ath/ath10k/mac.c | 144 +++++++++++++++++++++++++++++++
drivers/net/wireless/ath/ath10k/mac.h | 1 +
5 files changed, 157 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 4d3176492ae7..6d02266ca615 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -2043,7 +2043,9 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,

mutex_init(&ar->conf_mutex);
spin_lock_init(&ar->data_lock);
+ spin_lock_init(&ar->txqs_lock);

+ INIT_LIST_HEAD(&ar->txqs);
INIT_LIST_HEAD(&ar->peers);
init_waitqueue_head(&ar->peer_mapping_wq);
init_waitqueue_head(&ar->htt.empty_tx_wq);
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 5f447444fe97..7df35628ca35 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -308,6 +308,10 @@ struct ath10k_peer {
struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1];
};

+struct ath10k_txq {
+ struct list_head list;
+};
+
struct ath10k_sta {
struct ath10k_vif *arvif;

@@ -791,7 +795,10 @@ struct ath10k {

/* protects shared structure data */
spinlock_t data_lock;
+ /* protects: ar->txqs, artxq->list */
+ spinlock_t txqs_lock;

+ struct list_head txqs;
struct list_head arvifs;
struct list_head peers;
struct ath10k_peer *peer_map[ATH10K_MAX_NUM_PEER_IDS];
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index be7dc88b3316..6e3d95c95568 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -2244,6 +2244,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
}

ath10k_txrx_tx_unref(htt, &tx_done);
+ ath10k_mac_tx_push_pending(ar);
break;
}
case HTT_T2H_MSG_TYPE_TX_COMPL_IND:
@@ -2370,6 +2371,8 @@ static void ath10k_htt_txrx_compl_task(unsigned long ptr)
dev_kfree_skb_any(skb);
}

+ ath10k_mac_tx_push_pending(ar);
+
spin_lock_bh(&htt->rx_ring.lock);
while ((skb = __skb_dequeue(&htt->rx_compl_q))) {
resp = (struct htt_resp *)skb->data;
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 4b69a373382b..74a42e3465e4 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -3620,6 +3620,123 @@ void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work)
}
}

+static void ath10k_mac_txq_init(struct ieee80211_txq *txq)
+{
+ struct ath10k_txq *artxq = (void *)txq->drv_priv;
+
+ if (!txq)
+ return;
+
+ INIT_LIST_HEAD(&artxq->list);
+}
+
+static void ath10k_mac_txq_unref(struct ath10k *ar, struct ieee80211_txq *txq)
+{
+ struct ath10k_txq *artxq = (void *)txq->drv_priv;
+
+ if (!txq)
+ return;
+
+ spin_lock_bh(&ar->txqs_lock);
+ if (!list_empty(&artxq->list))
+ list_del_init(&artxq->list);
+ spin_unlock_bh(&ar->txqs_lock);
+}
+
+static bool ath10k_mac_tx_can_push(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+{
+ return 1; /* TBD */
+}
+
+static int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+{
+ const bool is_mgmt = false;
+ const bool is_presp = false;
+ struct ath10k *ar = hw->priv;
+ struct ath10k_htt *htt = &ar->htt;
+ struct ieee80211_vif *vif = txq->vif;
+ struct ieee80211_sta *sta = txq->sta;
+ enum ath10k_hw_txrx_mode txmode;
+ enum ath10k_mac_tx_path txpath;
+ struct sk_buff *skb;
+ int ret;
+
+ spin_lock_bh(&ar->htt.tx_lock);
+ ret = ath10k_htt_tx_inc_pending(htt, is_mgmt, is_presp);
+ spin_unlock_bh(&ar->htt.tx_lock);
+
+ if (ret)
+ return ret;
+
+ skb = ieee80211_tx_dequeue(hw, txq);
+ if (!skb) {
+ spin_lock_bh(&ar->htt.tx_lock);
+ ath10k_htt_tx_dec_pending(htt, is_mgmt);
+ spin_unlock_bh(&ar->htt.tx_lock);
+
+ return -ENOENT;
+ }
+
+ ath10k_mac_tx_h_fill_cb(ar, vif, skb);
+
+ txmode = ath10k_mac_tx_h_get_txmode(ar, vif, sta, skb);
+ txpath = ath10k_mac_tx_h_get_txpath(ar, skb, txmode);
+
+ ret = ath10k_mac_tx(ar, vif, sta, txmode, txpath, skb);
+ if (unlikely(ret)) {
+ ath10k_warn(ar, "failed to push frame: %d\n", ret);
+
+ spin_lock_bh(&ar->htt.tx_lock);
+ ath10k_htt_tx_dec_pending(htt, is_mgmt);
+ spin_unlock_bh(&ar->htt.tx_lock);
+
+ return ret;
+ }
+
+ return 0;
+}
+
+void ath10k_mac_tx_push_pending(struct ath10k *ar)
+{
+ struct ieee80211_hw *hw = ar->hw;
+ struct ieee80211_txq *txq;
+ struct ath10k_txq *artxq;
+ struct ath10k_txq *last;
+ int ret;
+ int max;
+
+ spin_lock_bh(&ar->txqs_lock);
+ rcu_read_lock();
+
+ last = list_last_entry(&ar->txqs, struct ath10k_txq, list);
+ while (!list_empty(&ar->txqs)) {
+ artxq = list_first_entry(&ar->txqs, struct ath10k_txq, list);
+ txq = container_of((void *)artxq, struct ieee80211_txq,
+ drv_priv);
+
+ /* Prevent aggressive sta/tid taking over tx queue */
+ max = 16;
+ while (max--) {
+ ret = ath10k_mac_tx_push_txq(hw, txq);
+ if (ret < 0)
+ break;
+ }
+
+ list_del_init(&artxq->list);
+
+ if (artxq == last || (ret < 0 && ret != -ENOENT)) {
+ if (ret != -ENOENT)
+ list_add_tail(&artxq->list, &ar->txqs);
+ break;
+ }
+ }
+
+ rcu_read_unlock();
+ spin_unlock_bh(&ar->txqs_lock);
+}
+
/************/
/* Scanning */
/************/
@@ -3836,6 +3953,22 @@ static void ath10k_mac_op_tx(struct ieee80211_hw *hw,
}
}

+static void ath10k_mac_op_wake_tx_queue(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+{
+ struct ath10k *ar = hw->priv;
+ struct ath10k_txq *artxq = (void *)txq->drv_priv;
+
+ if (ath10k_mac_tx_can_push(hw, txq)) {
+ spin_lock_bh(&ar->txqs_lock);
+ if (list_empty(&artxq->list))
+ list_add_tail(&artxq->list, &ar->txqs);
+ spin_unlock_bh(&ar->txqs_lock);
+
+ tasklet_schedule(&ar->htt.txrx_compl_task);
+ }
+}
+
/* Must not be called with conf_mutex held as workers can use that also. */
void ath10k_drain_tx(struct ath10k *ar)
{
@@ -4462,6 +4595,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
mutex_lock(&ar->conf_mutex);

memset(arvif, 0, sizeof(*arvif));
+ ath10k_mac_txq_init(vif->txq);

arvif->ar = ar;
arvif->vif = vif;
@@ -4860,6 +4994,8 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,
ath10k_mac_vif_tx_unlock_all(arvif);
spin_unlock_bh(&ar->htt.tx_lock);

+ ath10k_mac_txq_unref(ar, vif->txq);
+
mutex_unlock(&ar->conf_mutex);
}

@@ -5573,6 +5709,9 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
memset(arsta, 0, sizeof(*arsta));
arsta->arvif = arvif;
INIT_WORK(&arsta->update_wk, ath10k_sta_rc_update_wk);
+
+ for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
+ ath10k_mac_txq_init(sta->txq[i]);
}

/* cancel must be done outside the mutex to avoid deadlock */
@@ -5710,6 +5849,9 @@ static int ath10k_sta_state(struct ieee80211_hw *hw,
}
spin_unlock_bh(&ar->data_lock);

+ for (i = 0; i < ARRAY_SIZE(sta->txq); i++)
+ ath10k_mac_txq_unref(ar, sta->txq[i]);
+
if (!sta->tdls)
goto exit;

@@ -7013,6 +7155,7 @@ ath10k_mac_op_switch_vif_chanctx(struct ieee80211_hw *hw,

static const struct ieee80211_ops ath10k_ops = {
.tx = ath10k_mac_op_tx,
+ .wake_tx_queue = ath10k_mac_op_wake_tx_queue,
.start = ath10k_start,
.stop = ath10k_stop,
.config = ath10k_config,
@@ -7467,6 +7610,7 @@ int ath10k_mac_register(struct ath10k *ar)

ar->hw->vif_data_size = sizeof(struct ath10k_vif);
ar->hw->sta_data_size = sizeof(struct ath10k_sta);
+ ar->hw->txq_data_size = sizeof(struct ath10k_txq);

ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL;

diff --git a/drivers/net/wireless/ath/ath10k/mac.h b/drivers/net/wireless/ath/ath10k/mac.h
index 53091588090d..453f606a250e 100644
--- a/drivers/net/wireless/ath/ath10k/mac.h
+++ b/drivers/net/wireless/ath/ath10k/mac.h
@@ -75,6 +75,7 @@ void ath10k_mac_tx_unlock(struct ath10k *ar, int reason);
void ath10k_mac_vif_tx_lock(struct ath10k_vif *arvif, int reason);
void ath10k_mac_vif_tx_unlock(struct ath10k_vif *arvif, int reason);
bool ath10k_mac_tx_frm_has_freq(struct ath10k *ar);
+void ath10k_mac_tx_push_pending(struct ath10k *ar);

static inline struct ath10k_vif *ath10k_vif_to_arvif(struct ieee80211_vif *vif)
{
--
2.1.4


2016-03-07 09:48:27

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH v2 00/11] ath10k: implement push-pull tx model

Michal Kazior <[email protected]> writes:

> This adds support for the new logic where host
> tells firmware how many frames are queued for each
> station/tid and then firmware asks host to submit
> frames for given station/tid.
>
> The patch count is a bit high but I tried
> splitting the patches as much as possible to keep
> them short and easy to review. Hopefully it's not
> going to be a huge headache.
>
> v2:
> - squashes some patches
> - reworked pending frame scheduling
>
>
> Michal Kazior (11):
> ath10k: refactor tx code
> ath10k: unify txpath decision
> ath10k: refactor tx pending management
> ath10k: maintain peer_id for each sta and vif
> ath10k: add fast peer_map lookup
> ath10k: add new htt message generation/parsing logic
> ath10k: implement wake_tx_queue
> ath10k: implement updating shared htt txq state
> ath10k: store txq in skb_cb
> ath10k: keep track of queue depth per txq
> ath10k: implement push-pull tx

All applied, thanks.

--
Kalle Valo

2016-03-01 10:32:23

by Michal Kazior

[permalink] [raw]
Subject: [PATCH v2 10/11] ath10k: keep track of queue depth per txq

This will be necessary for later.

Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/core.h | 1 +
drivers/net/wireless/ath/ath10k/mac.c | 5 +++++
drivers/net/wireless/ath/ath10k/txrx.c | 7 +++++++
3 files changed, 13 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 89f789f3e5b4..926ecb2244a5 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -311,6 +311,7 @@ struct ath10k_peer {

struct ath10k_txq {
struct list_head list;
+ unsigned long num_fw_queued;
};

struct ath10k_sta {
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 8d02d53fdc2c..5bf614f1f75a 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -3669,6 +3669,7 @@ static int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
const bool is_presp = false;
struct ath10k *ar = hw->priv;
struct ath10k_htt *htt = &ar->htt;
+ struct ath10k_txq *artxq = (void *)txq->drv_priv;
struct ieee80211_vif *vif = txq->vif;
struct ieee80211_sta *sta = txq->sta;
enum ath10k_hw_txrx_mode txmode;
@@ -3708,6 +3709,10 @@ static int ath10k_mac_tx_push_txq(struct ieee80211_hw *hw,
return ret;
}

+ spin_lock_bh(&ar->htt.tx_lock);
+ artxq->num_fw_queued++;
+ spin_unlock_bh(&ar->htt.tx_lock);
+
return 0;
}

diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c
index 202e5192235b..ea4d3000c8c3 100644
--- a/drivers/net/wireless/ath/ath10k/txrx.c
+++ b/drivers/net/wireless/ath/ath10k/txrx.c
@@ -55,7 +55,9 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
struct ath10k *ar = htt->ar;
struct device *dev = ar->dev;
struct ieee80211_tx_info *info;
+ struct ieee80211_txq *txq;
struct ath10k_skb_cb *skb_cb;
+ struct ath10k_txq *artxq;
struct sk_buff *msdu;
bool limit_mgmt_desc = false;

@@ -80,11 +82,16 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt,
}

skb_cb = ATH10K_SKB_CB(msdu);
+ txq = skb_cb->txq;
+ artxq = (void *)txq->drv_priv;

if (unlikely(skb_cb->flags & ATH10K_SKB_F_MGMT) &&
ar->hw_params.max_probe_resp_desc_thres)
limit_mgmt_desc = true;

+ if (txq)
+ artxq->num_fw_queued--;
+
ath10k_htt_tx_free_msdu_id(htt, tx_done->msdu_id);
ath10k_htt_tx_dec_pending(htt, limit_mgmt_desc);
if (htt->num_pending_tx == 0)
--
2.1.4


2016-03-01 10:32:08

by Michal Kazior

[permalink] [raw]
Subject: [PATCH v2 00/11] ath10k: implement push-pull tx model

This adds support for the new logic where host
tells firmware how many frames are queued for each
station/tid and then firmware asks host to submit
frames for given station/tid.

The patch count is a bit high but I tried
splitting the patches as much as possible to keep
them short and easy to review. Hopefully it's not
going to be a huge headache.

v2:
- squashes some patches
- reworked pending frame scheduling


Michal Kazior (11):
ath10k: refactor tx code
ath10k: unify txpath decision
ath10k: refactor tx pending management
ath10k: maintain peer_id for each sta and vif
ath10k: add fast peer_map lookup
ath10k: add new htt message generation/parsing logic
ath10k: implement wake_tx_queue
ath10k: implement updating shared htt txq state
ath10k: store txq in skb_cb
ath10k: keep track of queue depth per txq
ath10k: implement push-pull tx

drivers/net/wireless/ath/ath10k/core.c | 2 +
drivers/net/wireless/ath/ath10k/core.h | 16 +
drivers/net/wireless/ath/ath10k/htt.h | 21 +-
drivers/net/wireless/ath/ath10k/htt_rx.c | 298 ++++++++++++++++-
drivers/net/wireless/ath/ath10k/htt_tx.c | 261 +++++++++++----
drivers/net/wireless/ath/ath10k/mac.c | 534 +++++++++++++++++++++++++++----
drivers/net/wireless/ath/ath10k/mac.h | 6 +
drivers/net/wireless/ath/ath10k/txrx.c | 11 +-
8 files changed, 1019 insertions(+), 130 deletions(-)

--
2.1.4


2016-03-01 10:32:21

by Michal Kazior

[permalink] [raw]
Subject: [PATCH v2 08/11] ath10k: implement updating shared htt txq state

Firmware 10.4.3 onwards can support a pull-push Tx
model where it shares a Tx queue state with the
host.

The host updates the DMA region it pointed to
during HTT setup whenever number of software
queued from (on host) changes. Based on this
information firmware issues fetch requests to the
host telling the host how many frames from a list
of given stations/tids should be submitted to the
firmware.

The code won't be called because not all
appropriate HTT events are processed yet.

Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/htt.h | 3 +
drivers/net/wireless/ath/ath10k/htt_tx.c | 104 +++++++++++++++++++++++++++++++
drivers/net/wireless/ath/ath10k/mac.c | 3 +
3 files changed, 110 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
index 65dcd22f31df..b1e40f44e76b 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -1667,6 +1667,7 @@ struct ath10k_htt {
} txbuf;

struct {
+ bool enabled;
struct htt_q_state *vaddr;
dma_addr_t paddr;
u16 num_peers;
@@ -1758,6 +1759,8 @@ int ath10k_htt_tx_fetch_resp(struct ath10k *ar,
struct htt_tx_fetch_record *records,
size_t num_records);

+void ath10k_htt_tx_txq_update(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq);
void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt,
bool is_mgmt);
int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt,
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
index 225f0561b3fd..6643be8692b5 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -22,6 +22,110 @@
#include "txrx.h"
#include "debug.h"

+static u8 ath10k_htt_tx_txq_calc_size(size_t count)
+{
+ int exp;
+ int factor;
+
+ exp = 0;
+ factor = count >> 7;
+
+ while (factor >= 64 && exp < 4) {
+ factor >>= 3;
+ exp++;
+ }
+
+ if (exp == 4)
+ return 0xff;
+
+ if (count > 0)
+ factor = max(1, factor);
+
+ return SM(exp, HTT_TX_Q_STATE_ENTRY_EXP) |
+ SM(factor, HTT_TX_Q_STATE_ENTRY_FACTOR);
+}
+
+static void __ath10k_htt_tx_txq_recalc(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+{
+ struct ath10k *ar = hw->priv;
+ struct ath10k_sta *arsta = (void *)txq->sta->drv_priv;
+ struct ath10k_vif *arvif = (void *)txq->vif->drv_priv;
+ unsigned long frame_cnt;
+ unsigned long byte_cnt;
+ int idx;
+ u32 bit;
+ u16 peer_id;
+ u8 tid;
+ u8 count;
+
+ lockdep_assert_held(&ar->htt.tx_lock);
+
+ if (!ar->htt.tx_q_state.enabled)
+ return;
+
+ if (txq->sta)
+ peer_id = arsta->peer_id;
+ else
+ peer_id = arvif->peer_id;
+
+ tid = txq->tid;
+ bit = BIT(peer_id % 32);
+ idx = peer_id / 32;
+
+ ieee80211_txq_get_depth(txq, &frame_cnt, &byte_cnt);
+ count = ath10k_htt_tx_txq_calc_size(byte_cnt);
+
+ if (unlikely(peer_id >= ar->htt.tx_q_state.num_peers) ||
+ unlikely(tid >= ar->htt.tx_q_state.num_tids)) {
+ ath10k_warn(ar, "refusing to update txq for peer_id %hu tid %hhu due to out of bounds\n",
+ peer_id, tid);
+ return;
+ }
+
+ ar->htt.tx_q_state.vaddr->count[tid][peer_id] = count;
+ ar->htt.tx_q_state.vaddr->map[tid][idx] &= ~bit;
+ ar->htt.tx_q_state.vaddr->map[tid][idx] |= count ? bit : 0;
+
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx txq state update peer_id %hu tid %hhu count %hhu\n",
+ peer_id, tid, count);
+}
+
+static void __ath10k_htt_tx_txq_sync(struct ath10k *ar)
+{
+ u32 seq;
+ size_t size;
+
+ lockdep_assert_held(&ar->htt.tx_lock);
+
+ if (!ar->htt.tx_q_state.enabled)
+ return;
+
+ seq = le32_to_cpu(ar->htt.tx_q_state.vaddr->seq);
+ seq++;
+ ar->htt.tx_q_state.vaddr->seq = cpu_to_le32(seq);
+
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx txq state update commit seq %u\n",
+ seq);
+
+ size = sizeof(*ar->htt.tx_q_state.vaddr);
+ dma_sync_single_for_device(ar->dev,
+ ar->htt.tx_q_state.paddr,
+ size,
+ DMA_TO_DEVICE);
+}
+
+void ath10k_htt_tx_txq_update(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+{
+ struct ath10k *ar = hw->priv;
+
+ spin_lock_bh(&ar->htt.tx_lock);
+ __ath10k_htt_tx_txq_recalc(hw, txq);
+ __ath10k_htt_tx_txq_sync(ar);
+ spin_unlock_bh(&ar->htt.tx_lock);
+}
+
void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt,
bool is_mgmt)
{
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 74a42e3465e4..900c64b65b43 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -3725,6 +3725,7 @@ void ath10k_mac_tx_push_pending(struct ath10k *ar)
}

list_del_init(&artxq->list);
+ ath10k_htt_tx_txq_update(hw, txq);

if (artxq == last || (ret < 0 && ret != -ENOENT)) {
if (ret != -ENOENT)
@@ -3967,6 +3968,8 @@ static void ath10k_mac_op_wake_tx_queue(struct ieee80211_hw *hw,

tasklet_schedule(&ar->htt.txrx_compl_task);
}
+
+ ath10k_htt_tx_txq_update(hw, txq);
}

/* Must not be called with conf_mutex held as workers can use that also. */
--
2.1.4