ath9k_htc: Drain packets on station removal
authorSujith Manoharan <Sujith.Manoharan@atheros.com>
Wed, 13 Apr 2011 05:56:11 +0000 (11:26 +0530)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 13 Apr 2011 19:24:16 +0000 (15:24 -0400)
When a station entry is removed, there could still be
pending packets destined for that station in the HIF layer.
Sending these to the target is not necessary, so drain them
in the driver itself.

Signed-off-by: Sujith Manoharan <Sujith.Manoharan@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath9k/hif_usb.c
drivers/net/wireless/ath/ath9k/htc.h
drivers/net/wireless/ath/ath9k/htc_drv_main.c
drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
drivers/net/wireless/ath/ath9k/htc_hst.c
drivers/net/wireless/ath/ath9k/htc_hst.h

index b3f23c2be738a24d304f4776c47390723bfa0ce6..7fae79d166655c71650aba6ca53bb740bb03b329 100644 (file)
@@ -363,6 +363,40 @@ static int hif_usb_send(void *hif_handle, u8 pipe_id, struct sk_buff *skb)
        return ret;
 }
 
+static inline bool check_index(struct sk_buff *skb, u8 idx)
+{
+       struct ath9k_htc_tx_ctl *tx_ctl;
+
+       tx_ctl = HTC_SKB_CB(skb);
+
+       if ((tx_ctl->type == ATH9K_HTC_AMPDU) &&
+           (tx_ctl->sta_idx == idx))
+               return true;
+
+       return false;
+}
+
+static void hif_usb_sta_drain(void *hif_handle, u8 idx)
+{
+       struct hif_device_usb *hif_dev = (struct hif_device_usb *)hif_handle;
+       struct sk_buff *skb, *tmp;
+       unsigned long flags;
+
+       spin_lock_irqsave(&hif_dev->tx.tx_lock, flags);
+
+       skb_queue_walk_safe(&hif_dev->tx.tx_skb_queue, skb, tmp) {
+               if (check_index(skb, idx)) {
+                       __skb_unlink(skb, &hif_dev->tx.tx_skb_queue);
+                       ath9k_htc_txcompletion_cb(hif_dev->htc_handle,
+                                                 skb, false);
+                       hif_dev->tx.tx_skb_cnt--;
+                       TX_STAT_INC(skb_failed);
+               }
+       }
+
+       spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags);
+}
+
 static struct ath9k_htc_hif hif_usb = {
        .transport = ATH9K_HIF_USB,
        .name = "ath9k_hif_usb",
@@ -372,6 +406,7 @@ static struct ath9k_htc_hif hif_usb = {
 
        .start = hif_usb_start,
        .stop = hif_usb_stop,
+       .sta_drain = hif_usb_sta_drain,
        .send = hif_usb_send,
 };
 
index 0d2e2b10358d5abe0bba875181ac8cf8f8858f6e..41823fd6d9ad36df2f1b1ba26b87efe78d7b56d3 100644 (file)
@@ -280,6 +280,7 @@ struct ath9k_htc_tx_ctl {
        u8 type; /* ATH9K_HTC_* */
        u8 epid;
        u8 txok;
+       u8 sta_idx;
 };
 
 static inline struct ath9k_htc_tx_ctl *HTC_SKB_CB(struct sk_buff *skb)
index fb9ff1188a0a51379715454449edaa4bd65c66fd..ae85cc4373f061af10f4f57955836547009e9e35 100644 (file)
@@ -1303,10 +1303,13 @@ static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
                                struct ieee80211_sta *sta)
 {
        struct ath9k_htc_priv *priv = hw->priv;
+       struct ath9k_htc_sta *ista;
        int ret;
 
        mutex_lock(&priv->mutex);
        ath9k_htc_ps_wakeup(priv);
+       ista = (struct ath9k_htc_sta *) sta->drv_priv;
+       htc_sta_drain(priv->htc, ista->index);
        ret = ath9k_htc_remove_station(priv, vif, sta);
        ath9k_htc_ps_restore(priv);
        mutex_unlock(&priv->mutex);
index 9e0c34b0a794a8e36f5eb3867b2460e8af4f3f48..0790070a7f63d753fec977e9e234d33679da5f90 100644 (file)
@@ -248,6 +248,14 @@ int ath9k_htc_tx_start(struct ath9k_htc_priv *priv,
                tx_hdr.vif_idx = vif_idx;
                tx_hdr.cookie = slot;
 
+               /*
+                * This is a bit redundant but it helps to get
+                * the per-packet index quickly when draining the
+                * TX queue in the HIF layer. Otherwise we would
+                * have to parse the packet contents ...
+                */
+               tx_ctl->sta_idx = sta_idx;
+
                if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
                        tx_ctl->type = ATH9K_HTC_AMPDU;
                        tx_hdr.data_type = ATH9K_HTC_AMPDU;
index 7ced8ab1ae4caeaf90a3d46d9a032578357e93a6..5c76352b1319adef0820a48d2878ed76b0bc592e 100644 (file)
@@ -310,6 +310,11 @@ void htc_start(struct htc_target *target)
        target->hif->start(target->hif_dev);
 }
 
+void htc_sta_drain(struct htc_target *target, u8 idx)
+{
+       target->hif->sta_drain(target->hif_dev, idx);
+}
+
 void ath9k_htc_txcompletion_cb(struct htc_target *htc_handle,
                               struct sk_buff *skb, bool txok)
 {
index 191e3c0837a6ccad9aa76f015f82c679790654ae..cb9174ade53ee83445fa9656a3d094e68ddf2c70 100644 (file)
@@ -35,6 +35,7 @@ struct ath9k_htc_hif {
 
        void (*start) (void *hif_handle);
        void (*stop) (void *hif_handle);
+       void (*sta_drain) (void *hif_handle, u8 idx);
        int (*send) (void *hif_handle, u8 pipe, struct sk_buff *buf);
 };
 
@@ -209,6 +210,7 @@ int htc_send_epid(struct htc_target *target, struct sk_buff *skb,
                  enum htc_endpoint_id epid);
 void htc_stop(struct htc_target *target);
 void htc_start(struct htc_target *target);
+void htc_sta_drain(struct htc_target *target, u8 idx);
 
 void ath9k_htc_rx_msg(struct htc_target *htc_handle,
                      struct sk_buff *skb, u32 len, u8 pipe_id);