Remove "ath10k: fix flushing and tx stalls" patch set.
Remove the following patches that were not accepted upstream:
a5de255803d7 [RFT 1/4] ath10k: fix wmi-htc tx credit starvation
a3f668d218f3 [RFT 2/4] ath10k: rework peer accounting
a8316bc20134 [RFT 3/4] ath10k: wait for mgmt tx when flushing too
0ba4a78e7eca [RFT 4/4] ath10k: improve tx flushing
Change-Id: Ib6580579114734188127a15d847efccb915a5d3b
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 8164721..f34d113 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -41,7 +41,7 @@
#define ATH10K_SCAN_ID 0
#define WMI_READY_TIMEOUT (5 * HZ)
-#define ATH10K_FLUSH_TIMEOUT_HZ (1*HZ)
+#define ATH10K_FLUSH_TIMEOUT_HZ (5*HZ)
#define ATH10K_NUM_CHANS 38
/* Antenna noise floor */
@@ -202,14 +202,9 @@
#define ATH10K_MAX_NUM_PEER_IDS (1 << 11) /* htt rx_desc limit */
struct ath10k_peer {
- /* protected by conf_mutex + data_lock */
struct list_head list;
-
- /* protected by conf_mutex */
int vdev_id;
u8 addr[ETH_ALEN];
-
- /* protected by data_lock */
DECLARE_BITMAP(peer_ids, ATH10K_MAX_NUM_PEER_IDS);
struct ieee80211_key_conf *keys[WMI_MAX_KEY_INDEX + 1];
};
diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index a001051..f9a4008 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -322,61 +322,28 @@
static int ath10k_peer_create(struct ath10k *ar, u32 vdev_id, const u8 *addr)
{
- struct ath10k_peer *peer;
int ret;
lockdep_assert_held(&ar->conf_mutex);
- peer = ath10k_peer_find(ar, vdev_id, addr);
- if (peer) {
- ath10k_warn(ar, "peer %pM on vdev %i already exists\n",
- addr, vdev_id);
- return -EINVAL;
- }
-
- peer = kzalloc(sizeof(*peer), GFP_KERNEL);
- if (!peer) {
- ath10k_warn(ar, "failed to allocate peer %pM on vdev %i: not enough memory\n",
- addr, vdev_id);
- return -ENOMEM;
- }
-
- peer->vdev_id = vdev_id;
- memcpy(peer->addr, addr, ETH_ALEN);
-
- spin_lock_bh(&ar->data_lock);
- list_add(&peer->list, &ar->peers);
- spin_unlock_bh(&ar->data_lock);
-
ret = ath10k_wmi_peer_create(ar, vdev_id, addr);
if (ret) {
ath10k_warn(ar, "failed to create wmi peer %pM on vdev %i: %i\n",
addr, vdev_id, ret);
- goto err_free;
+ return ret;
}
ret = ath10k_wait_for_peer_created(ar, vdev_id, addr);
if (ret) {
ath10k_warn(ar, "failed to wait for created wmi peer %pM on vdev %i: %i\n",
addr, vdev_id, ret);
- goto err_free;
+ return ret;
}
spin_lock_bh(&ar->data_lock);
ar->num_peers++;
spin_unlock_bh(&ar->data_lock);
return 0;
-
-err_free:
- spin_lock_bh(&ar->data_lock);
- list_del(&peer->list);
- /* very unlikely, but check anyway */
- if (!bitmap_empty(peer->peer_ids, ATH10K_MAX_NUM_PEER_IDS))
- ath10k_warn(ar, "removing peer %pM on vdev %i still being mapped in firmware\n",
- addr, vdev_id);
- spin_unlock_bh(&ar->data_lock);
- kfree(peer);
- return ret;
}
static int ath10k_mac_set_kickout(struct ath10k_vif *arvif)
@@ -453,46 +420,22 @@
static int ath10k_peer_delete(struct ath10k *ar, u32 vdev_id, const u8 *addr)
{
- struct ath10k_peer *peer;
int ret;
lockdep_assert_held(&ar->conf_mutex);
- peer = ath10k_peer_find(ar, vdev_id, addr);
- if (!peer) {
- ath10k_warn(ar, "failed to lookup peer %pM on vdev %i\n",
- addr, vdev_id);
- return -ENOENT;
- }
-
ret = ath10k_wmi_peer_delete(ar, vdev_id, addr);
- if (ret) {
- ath10k_warn(ar, "failed to request wmi peer %pM on vdev %i removal: %d\n",
- addr, vdev_id, ret);
- goto out;
- }
+ if (ret)
+ return ret;
ret = ath10k_wait_for_peer_deleted(ar, vdev_id, addr);
- if (ret) {
- ath10k_warn(ar, "failed to wait for wmi peer %pM on vdev %i removal: %d\n",
- addr, vdev_id, ret);
- goto out;
- }
+ if (ret)
+ return ret;
spin_lock_bh(&ar->data_lock);
ar->num_peers--;
spin_unlock_bh(&ar->data_lock);
-out:
- spin_lock_bh(&ar->data_lock);
- list_del(&peer->list);
- if (!bitmap_empty(peer->peer_ids, ATH10K_MAX_NUM_PEER_IDS))
- ath10k_warn(ar, "removing peer %pM on vdev %i still being mapped in firmware\n",
- addr, vdev_id);
- spin_unlock_bh(&ar->data_lock);
-
- kfree(peer);
-
return 0;
}
@@ -2185,67 +2128,9 @@
}
}
-static void ath10k_mgmt_tx_flush(struct ath10k *ar, struct sk_buff *skb)
-{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- struct ieee80211_sta *sta;
- struct ath10k_vif *arvif;
- u8 *da = ieee80211_get_DA(hdr);
- u8 vdev_id = ATH10K_SKB_CB(skb)->vdev_id;
- u32 bcn_intval = 0;
- unsigned int msecs;
- int ret;
-
- lockdep_assert_held(&ar->conf_mutex);
-
- if (!is_unicast_ether_addr(da))
- return;
-
- rcu_read_lock();
- sta = ieee80211_find_sta_by_ifaddr(ar->hw, da, NULL);
- rcu_read_unlock();
-
- /*
- * FW Tx queues can be paused only for associated peers. Since this is
- * a workaround just assume worst case if station simply entry exists.
- */
- if (!sta)
- return;
-
- list_for_each_entry(arvif, &ar->arvifs, list) {
- if (arvif->vdev_id == vdev_id) {
- bcn_intval = arvif->beacon_interval;
- break;
- }
- }
-
- if (!bcn_intval)
- return;
-
- /*
- * Wait 2 beacon intervals before flushing so stations that are
- * asleep but are actually still in range have a chance to see
- * updated PVB, wake up and fetch the frame. There's no other way of
- * synchronizing other than sleeping because there's no tx completion
- * indication event for WMI management tx.
- */
- msecs = 2 * arvif->beacon_interval * 1024 / 1000;
- ath10k_dbg(ar, ATH10K_DBG_MAC,
- "mac flushing peer %pM on vdev %i mgmt tid for unicast mgmt (%d msecs)\n",
- da, vdev_id, msecs);
- msleep(msecs);
-
- ret = ath10k_wmi_peer_flush(ar, vdev_id, da,
- WMI_PEER_TID_MGMT_MASK);
- if (ret)
- ath10k_warn(ar, "failed to flush peer %pM mgmt tid: %d\n",
- da, ret);
-}
-
void ath10k_mgmt_over_wmi_tx_work(struct work_struct *work)
{
struct ath10k *ar = container_of(work, struct ath10k, wmi_mgmt_tx_work);
- struct ieee80211_tx_info *info;
struct sk_buff *skb;
int ret;
@@ -2254,50 +2139,12 @@
if (!skb)
break;
- mutex_lock(&ar->conf_mutex);
-
ret = ath10k_wmi_mgmt_tx(ar, skb);
if (ret) {
ath10k_warn(ar, "failed to transmit management frame via WMI: %d\n",
ret);
- mutex_unlock(&ar->conf_mutex);
ieee80211_free_txskb(ar->hw, skb);
- continue;
}
-
- /*
- * Each WMI management Tx consumes 1 HTC Tx credit and doesn't
- * replenish it until the frame is actually transmitted out of
- * firmware's Tx queues.
- *
- * If associated client was asleep and has gone out of range
- * then unicast frames won't be released for FW/HW queues for a
- * while (10 seconds per observation). This means that if more
- * management frames are queued then HTC Tx credits are drained
- * to 0 and no other commands can be submitted including
- * beacons and peer removal.
- *
- * This could in turn result in clients disconnecting due to
- * their beacon loss and may trigger spurious sta kickouts
- * because wmi peer removal command may never reach firmware
- * during disassociation.
- *
- * This could happen, e.g. when disconnecting client that has
- * gone away while asleep.
- *
- * As a workaround flush unicast management frames that can
- * possibly be buffered.
- *
- * Note: This is a deficiency in design of WMI_MGMT_TX command.
- */
- ath10k_mgmt_tx_flush(ar, skb);
-
- mutex_unlock(&ar->conf_mutex);
-
- /* there's no way to get ACK so just assume it's acked */
- info = IEEE80211_SKB_CB(skb);
- info->flags |= IEEE80211_TX_STAT_ACK;
- ieee80211_tx_status(ar->hw, skb);
}
}
@@ -3974,109 +3821,39 @@
return ret;
}
-static int ath10k_flush_all_peer_tids(struct ath10k *ar)
-{
- struct ath10k_peer *peer;
- int ret;
-
- lockdep_assert_held(&ar->conf_mutex);
-
- list_for_each_entry(peer, &ar->peers, list) {
- ret = ath10k_wmi_peer_flush(ar, peer->vdev_id, peer->addr,
- WMI_PEER_TID_ALL_MASK);
- if (ret) {
- ath10k_warn(ar, "failed to request peer %pM on vdev %i to flush %08x: %d\n",
- peer->addr, peer->vdev_id, WMI_PEER_TID_ALL_MASK, ret);
- return ret;
- }
- }
-
- return 0;
-}
-
-static int ath10k_flush_wait(struct ath10k *ar)
-{
- bool skip;
- int ret = wait_event_timeout(ar->htt.empty_tx_wq, ({
- bool htt_empty, wmi_empty;
- unsigned long flags;
-
- spin_lock_bh(&ar->htt.tx_msdu_pool_lock);
- htt_empty = (ar->htt.num_pending_tx == 0);
- spin_unlock_bh(&ar->htt.tx_msdu_pool_lock);
-
- spin_lock_irqsave(&ar->wmi_mgmt_tx_queue.lock, flags);
- wmi_empty = skb_queue_len(&ar->wmi_mgmt_tx_queue) == 0;
- spin_unlock_irqrestore(&ar->wmi_mgmt_tx_queue.lock,
- flags);
-
- skip = (ar->state == ATH10K_STATE_WEDGED);
-
- ((htt_empty && wmi_empty) || skip);
- }), ATH10K_FLUSH_TIMEOUT_HZ);
-
- if (ret == 0)
- ret = -ETIMEDOUT;
- else if (ret > 0)
- ret = 0;
-
- if (skip) {
- ath10k_warn(ar, "ignoring flushing result because hardware is wedged\n");
- ret = -EBUSY;
- }
-
- return ret;
-}
-
-static void ath10k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 queues, bool drop)
+static void ath10k_flush(struct ieee80211_hw *hw, u32 queues, bool drop)
{
struct ath10k *ar = hw->priv;
+ bool skip;
int ret;
+ /* mac80211 doesn't care if we really xmit queued frames or not
+ * we'll collect those frames either way if we stop/delete vdevs */
+ if (drop)
+ return;
+
mutex_lock(&ar->conf_mutex);
- if (drop) {
- ret = ath10k_flush_all_peer_tids(ar);
- if (ret) {
- ath10k_warn(ar, "failed to flush all peer tids: %d\n", ret);
- goto out;
- }
+ if (ar->state == ATH10K_STATE_WEDGED)
+ goto skip;
- goto out;
- }
+ ret = wait_event_timeout(ar->htt.empty_tx_wq, ({
+ bool empty;
- if (ar->state == ATH10K_STATE_WEDGED) {
- ath10k_warn(ar, "skipping flushing because hardware is wedged\n");
- ret = -EBUSY;
- goto out;
- }
+ spin_lock_bh(&ar->htt.tx_msdu_pool_lock);
+ empty = (ar->htt.num_pending_tx == 0);
+ spin_unlock_bh(&ar->htt.tx_msdu_pool_lock);
- ret = ath10k_flush_wait(ar);
- if (ret) {
- ath10k_dbg(ar, ATH10K_DBG_MAC,
- "failed to wait for tx to flush: %d, forcing\n",
- ret);
+ skip = (ar->state == ATH10K_STATE_WEDGED);
- ret = ath10k_flush_all_peer_tids(ar);
- if (ret) {
- ath10k_warn(ar, "failed to flush all peer tids: %d\n", ret);
- goto out;
- }
+ (empty || skip);
+ }), ATH10K_FLUSH_TIMEOUT_HZ);
- ret = ath10k_flush_wait(ar);
- if (ret) {
- ath10k_warn(ar, "failed to flush tx: %d\n", ret);
- goto out;
- }
- }
-
-out:
+ if (ret <= 0 || skip)
+ ath10k_warn(ar, "failed to flush transmit queue (skip %i ar-state %i): %i\n",
+ skip, ar->state, ret);
+skip:
mutex_unlock(&ar->conf_mutex);
-
- /* empty mgmt tx queue doesn't mean mgmt tx is flushed because the last
- * frame still may be processed by a worker */
- if (ret == 0)
- cancel_work_sync(&ar->wmi_mgmt_tx_work);
}
/* TODO: Implement this function properly
diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c
index 7c16b8b..7653738 100644
--- a/drivers/net/wireless/ath/ath10k/txrx.c
+++ b/drivers/net/wireless/ath/ath10k/txrx.c
@@ -100,13 +100,13 @@
/* we do not own the msdu anymore */
}
-/* hold conf_mutex for simple iteration, or conf_mutex+data_lock for
- * modifications */
struct ath10k_peer *ath10k_peer_find(struct ath10k *ar, int vdev_id,
const u8 *addr)
{
struct ath10k_peer *peer;
+ lockdep_assert_held(&ar->data_lock);
+
list_for_each_entry(peer, &ar->peers, list) {
if (peer->vdev_id != vdev_id)
continue;
@@ -138,14 +138,10 @@
int ret;
ret = wait_event_timeout(ar->peer_mapping_wq, ({
- struct ath10k_peer *peer;
- bool mapped = false;
+ bool mapped;
spin_lock_bh(&ar->data_lock);
- peer = ath10k_peer_find(ar, vdev_id, addr);
- if (peer)
- mapped = !bitmap_empty(peer->peer_ids,
- ATH10K_MAX_NUM_PEER_IDS);
+ mapped = !!ath10k_peer_find(ar, vdev_id, addr);
spin_unlock_bh(&ar->data_lock);
mapped == expect_mapped;
@@ -176,16 +172,20 @@
spin_lock_bh(&ar->data_lock);
peer = ath10k_peer_find(ar, ev->vdev_id, ev->addr);
if (!peer) {
- ath10k_warn(ar, "failed to map peer %pM on vdev %i: no such entry\n",
- ev->addr, ev->vdev_id);
- goto exit;
+ peer = kzalloc(sizeof(*peer), GFP_ATOMIC);
+ if (!peer)
+ goto exit;
+
+ peer->vdev_id = ev->vdev_id;
+ memcpy(peer->addr, ev->addr, ETH_ALEN);
+ list_add(&peer->list, &ar->peers);
+ wake_up(&ar->peer_mapping_wq);
}
ath10k_dbg(ar, ATH10K_DBG_HTT, "htt peer map vdev %d peer %pM id %d\n",
ev->vdev_id, ev->addr, ev->peer_id);
set_bit(ev->peer_id, peer->peer_ids);
- wake_up(&ar->peer_mapping_wq);
exit:
spin_unlock_bh(&ar->data_lock);
}
@@ -209,8 +209,11 @@
clear_bit(ev->peer_id, peer->peer_ids);
- if (bitmap_empty(peer->peer_ids, ATH10K_MAX_NUM_PEER_IDS))
+ if (bitmap_empty(peer->peer_ids, ATH10K_MAX_NUM_PEER_IDS)) {
+ list_del(&peer->list);
+ kfree(peer);
wake_up(&ar->peer_mapping_wq);
+ }
exit:
spin_unlock_bh(&ar->data_lock);
diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c
index fd39c22..ea7fdd3 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.c
+++ b/drivers/net/wireless/ath/ath10k/wmi.c
@@ -762,6 +762,7 @@
struct wmi_mgmt_tx_cmd *cmd;
struct ieee80211_hdr *hdr;
struct sk_buff *wmi_skb;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
int len;
u32 buf_len = skb->len;
u16 fc;
@@ -807,6 +808,10 @@
if (ret)
return ret;
+ /* TODO: report tx status to mac80211 - temporary just ACK */
+ info->flags |= IEEE80211_TX_STAT_ACK;
+ ieee80211_tx_status_irqsafe(ar->hw, skb);
+
return ret;
}
diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h
index 048ba28..86f5ebc 100644
--- a/drivers/net/wireless/ath/ath10k/wmi.h
+++ b/drivers/net/wireless/ath/ath10k/wmi.h
@@ -4293,10 +4293,6 @@
struct wmi_mac_addr peer_macaddr;
} __packed;
-#define WMI_PEER_TID_MGMT 17
-#define WMI_PEER_TID_MGMT_MASK BIT(WMI_PEER_TID_MGMT)
-#define WMI_PEER_TID_ALL_MASK 0xFFFFFFFF
-
struct wmi_peer_flush_tids_cmd {
__le32 vdev_id;
struct wmi_mac_addr peer_macaddr;