ath9k: fix an aggregation start related race condition
[deliverable/linux.git] / drivers / net / wireless / ath / ath9k / xmit.c
index bd52ac1117954a47056ba9ad1e46ca6cad003f77..d629bfbdfab47bbe0182f0dc9133241a0e7b1cd0 100644 (file)
@@ -120,26 +120,14 @@ static void ath_tx_queue_tid(struct ath_txq *txq, struct ath_atx_tid *tid)
        list_add_tail(&ac->list, &txq->axq_acq);
 }
 
-static void ath_tx_pause_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
-{
-       struct ath_txq *txq = &sc->tx.txq[tid->ac->qnum];
-
-       spin_lock_bh(&txq->axq_lock);
-       tid->paused++;
-       spin_unlock_bh(&txq->axq_lock);
-}
-
 static void ath_tx_resume_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
 {
        struct ath_txq *txq = &sc->tx.txq[tid->ac->qnum];
 
-       BUG_ON(tid->paused <= 0);
-       spin_lock_bh(&txq->axq_lock);
-
-       tid->paused--;
+       WARN_ON(!tid->paused);
 
-       if (tid->paused > 0)
-               goto unlock;
+       spin_lock_bh(&txq->axq_lock);
+       tid->paused = false;
 
        if (list_empty(&tid->buf_q))
                goto unlock;
@@ -157,15 +145,10 @@ static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *tid)
        struct list_head bf_head;
        INIT_LIST_HEAD(&bf_head);
 
-       BUG_ON(tid->paused <= 0);
-       spin_lock_bh(&txq->axq_lock);
-
-       tid->paused--;
+       WARN_ON(!tid->paused);
 
-       if (tid->paused > 0) {
-               spin_unlock_bh(&txq->axq_lock);
-               return;
-       }
+       spin_lock_bh(&txq->axq_lock);
+       tid->paused = false;
 
        while (!list_empty(&tid->buf_q)) {
                bf = list_first_entry(&tid->buf_q, struct ath_buf, list);
@@ -185,9 +168,9 @@ static void ath_tx_update_baw(struct ath_softc *sc, struct ath_atx_tid *tid,
        index  = ATH_BA_INDEX(tid->seq_start, seqno);
        cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
 
-       tid->tx_buf[cindex] = NULL;
+       __clear_bit(cindex, tid->tx_buf);
 
-       while (tid->baw_head != tid->baw_tail && !tid->tx_buf[tid->baw_head]) {
+       while (tid->baw_head != tid->baw_tail && !test_bit(tid->baw_head, tid->tx_buf)) {
                INCR(tid->seq_start, IEEE80211_SEQ_MAX);
                INCR(tid->baw_head, ATH_TID_MAX_BUFS);
        }
@@ -203,9 +186,7 @@ static void ath_tx_addto_baw(struct ath_softc *sc, struct ath_atx_tid *tid,
 
        index  = ATH_BA_INDEX(tid->seq_start, bf->bf_seqno);
        cindex = (tid->baw_head + index) & (ATH_TID_MAX_BUFS - 1);
-
-       BUG_ON(tid->tx_buf[cindex] != NULL);
-       tid->tx_buf[cindex] = bf;
+       __set_bit(cindex, tid->tx_buf);
 
        if (index >= ((tid->baw_tail - tid->baw_head) &
                (ATH_TID_MAX_BUFS - 1))) {
@@ -329,7 +310,6 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
        int isaggr, txfail, txpending, sendbar = 0, needreset = 0, nbad = 0;
        bool rc_update = true;
        struct ieee80211_tx_rate rates[4];
-       unsigned long flags;
 
        skb = bf->bf_mpdu;
        hdr = (struct ieee80211_hdr *)skb->data;
@@ -346,9 +326,21 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
        if (!sta) {
                rcu_read_unlock();
 
-               spin_lock_irqsave(&sc->tx.txbuflock, flags);
-               list_splice_tail_init(bf_q, &sc->tx.txbuf);
-               spin_unlock_irqrestore(&sc->tx.txbuflock, flags);
+               INIT_LIST_HEAD(&bf_head);
+               while (bf) {
+                       bf_next = bf->bf_next;
+
+                       bf->bf_state.bf_type |= BUF_XRETRY;
+                       if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) ||
+                           !bf->bf_stale || bf_next != NULL)
+                               list_move_tail(&bf->list, &bf_head);
+
+                       ath_tx_rc_status(bf, ts, 0, 0, false);
+                       ath_tx_complete_buf(sc, bf, txq, &bf_head, ts,
+                               0, 0);
+
+                       bf = bf_next;
+               }
                return;
        }
 
@@ -507,6 +499,14 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
                bf = bf_next;
        }
 
+       /* prepend un-acked frames to the beginning of the pending frame queue */
+       if (!list_empty(&bf_pending)) {
+               spin_lock_bh(&txq->axq_lock);
+               list_splice(&bf_pending, &tid->buf_q);
+               ath_tx_queue_tid(txq, tid);
+               spin_unlock_bh(&txq->axq_lock);
+       }
+
        if (tid->state & AGGR_CLEANUP) {
                if (tid->baw_head == tid->baw_tail) {
                        tid->state &= ~AGGR_ADDBA_COMPLETE;
@@ -519,14 +519,6 @@ static void ath_tx_complete_aggr(struct ath_softc *sc, struct ath_txq *txq,
                return;
        }
 
-       /* prepend un-acked frames to the beginning of the pending frame queue */
-       if (!list_empty(&bf_pending)) {
-               spin_lock_bh(&txq->axq_lock);
-               list_splice(&bf_pending, &tid->buf_q);
-               ath_tx_queue_tid(txq, tid);
-               spin_unlock_bh(&txq->axq_lock);
-       }
-
        rcu_read_unlock();
 
        if (needreset)
@@ -791,17 +783,23 @@ static void ath_tx_sched_aggr(struct ath_softc *sc, struct ath_txq *txq,
                 status != ATH_AGGR_BAW_CLOSED);
 }
 
-void ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
-                      u16 tid, u16 *ssn)
+int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
+                     u16 tid, u16 *ssn)
 {
        struct ath_atx_tid *txtid;
        struct ath_node *an;
 
        an = (struct ath_node *)sta->drv_priv;
        txtid = ATH_AN_2_TID(an, tid);
+
+       if (txtid->state & (AGGR_CLEANUP | AGGR_ADDBA_COMPLETE))
+               return -EAGAIN;
+
        txtid->state |= AGGR_ADDBA_PROGRESS;
-       ath_tx_pause_tid(sc, txtid);
+       txtid->paused = true;
        *ssn = txtid->seq_start;
+
+       return 0;
 }
 
 void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
@@ -824,10 +822,9 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u16 tid)
                return;
        }
 
-       ath_tx_pause_tid(sc, txtid);
-
        /* drop all software retried frames and mark this TID */
        spin_lock_bh(&txq->axq_lock);
+       txtid->paused = true;
        while (!list_empty(&txtid->buf_q)) {
                bf = list_first_entry(&txtid->buf_q, struct ath_buf, list);
                if (!bf_isretried(bf)) {
@@ -1170,7 +1167,7 @@ void ath_drain_all_txq(struct ath_softc *sc, bool retry_tx)
                          "Failed to stop TX DMA. Resetting hardware!\n");
 
                spin_lock_bh(&sc->sc_resetlock);
-               r = ath9k_hw_reset(ah, sc->sc_ah->curchan, false);
+               r = ath9k_hw_reset(ah, sc->sc_ah->curchan, ah->caldata, false);
                if (r)
                        ath_print(common, ATH_DBG_FATAL,
                                  "Unable to reset hardware; reset status %d\n",
@@ -1414,22 +1411,6 @@ static enum ath9k_pkt_type get_hw_packet_type(struct sk_buff *skb)
        return htype;
 }
 
-static int get_hw_crypto_keytype(struct sk_buff *skb)
-{
-       struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
-
-       if (tx_info->control.hw_key) {
-               if (tx_info->control.hw_key->alg == ALG_WEP)
-                       return ATH9K_KEY_TYPE_WEP;
-               else if (tx_info->control.hw_key->alg == ALG_TKIP)
-                       return ATH9K_KEY_TYPE_TKIP;
-               else if (tx_info->control.hw_key->alg == ALG_CCMP)
-                       return ATH9K_KEY_TYPE_AES;
-       }
-
-       return ATH9K_KEY_TYPE_CLEAR;
-}
-
 static void assign_aggr_tid_seqno(struct sk_buff *skb,
                                  struct ath_buf *bf)
 {
@@ -1668,7 +1649,7 @@ static int ath_tx_setup_buffer(struct ieee80211_hw *hw, struct ath_buf *bf,
                bf->bf_state.bfs_paprd_timestamp = jiffies;
        bf->bf_flags = setup_tx_flags(skb, use_ldpc);
 
-       bf->bf_keytype = get_hw_crypto_keytype(skb);
+       bf->bf_keytype = ath9k_cmn_get_hw_crypto_keytype(skb);
        if (bf->bf_keytype != ATH9K_KEY_TYPE_CLEAR) {
                bf->bf_frmlen += tx_info->control.hw_key->icv_len;
                bf->bf_keyix = tx_info->control.hw_key->hw_key_idx;
@@ -2066,8 +2047,8 @@ static void ath_wake_mac80211_queue(struct ath_softc *sc, struct ath_txq *txq)
 
        spin_lock_bh(&txq->axq_lock);
        if (txq->stopped && sc->tx.pending_frames[qnum] < ATH_MAX_QDEPTH) {
-               ath_mac80211_start_queue(sc, qnum);
-               txq->stopped = 0;
+               if (ath_mac80211_start_queue(sc, qnum))
+                       txq->stopped = 0;
        }
        spin_unlock_bh(&txq->axq_lock);
 }
This page took 0.031788 seconds and 5 git commands to generate.