ath9k: Fix bug in paprd
authorVasanthakumar Thiagarajan <vasanth@atheros.com>
Thu, 24 Jun 2010 09:42:44 +0000 (02:42 -0700)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 24 Jun 2010 19:42:39 +0000 (15:42 -0400)
It is possbile that the transmission of paprd test frame
might not get completed in 100ms if tx is stuck. Freeing
this skb upon timeout in ath_paprd_calibrate() will result
in accessing already freed memory when the associated pending
buffer is drained in txq. This patch fixes this issue.

Signed-off-by: Vasanthakumar Thiagarajan <vasanth@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/ath9k/ath9k.h
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/ath9k/xmit.c

index 3a14630e808e824f9658064e233b5cee4360e425..ce74af6ef08ccaa2aa9df67bb05b0fbdfd926f20 100644 (file)
@@ -226,6 +226,7 @@ struct ath_buf_state {
        int bfs_retries;
        u8 bf_type;
        u8 bfs_paprd;
+       unsigned long bfs_paprd_timestamp;
        u32 bfs_keyix;
        enum ath9k_key_type bfs_keytype;
 };
@@ -425,6 +426,8 @@ int ath_beaconq_config(struct ath_softc *sc);
 #define ATH_LONG_CALINTERVAL      30000   /* 30 seconds */
 #define ATH_RESTART_CALINTERVAL   1200000 /* 20 minutes */
 
+#define ATH_PAPRD_TIMEOUT      100 /* msecs */
+
 void ath_paprd_calibrate(struct work_struct *work);
 void ath_ani_calibrate(unsigned long data);
 
index 5af259644bf6f0cf0daafe6db690fe97e5892912..c070e01c8de3b253a66b5ef1d81df7ef2d5b1ea3 100644 (file)
@@ -310,13 +310,13 @@ void ath_paprd_calibrate(struct work_struct *work)
                        break;
 
                time_left = wait_for_completion_timeout(&sc->paprd_complete,
-                                                       100);
+                               msecs_to_jiffies(ATH_PAPRD_TIMEOUT));
                if (!time_left) {
                        ath_print(ath9k_hw_common(ah), ATH_DBG_CALIBRATE,
                                  "Timeout waiting for paprd training on "
                                  "TX chain %d\n",
                                  chain);
-                       break;
+                       goto fail_paprd;
                }
 
                if (!ar9003_paprd_is_done(ah))
@@ -334,6 +334,7 @@ void ath_paprd_calibrate(struct work_struct *work)
                ath_paprd_activate(sc);
        }
 
+fail_paprd:
        ath9k_ps_restore(sc);
 }
 
index 20221b8c04fd886ea3556a609bda5494800ba2df..edbeffb14a1ce6d38965bae92ec2808585908311 100644 (file)
@@ -1644,6 +1644,8 @@ static int ath_tx_setup_buffer(struct ieee80211_hw *hw, struct ath_buf *bf,
        }
 
        bf->bf_state.bfs_paprd = txctl->paprd;
+       if (txctl->paprd)
+               bf->bf_state.bfs_paprd_timestamp = jiffies;
        bf->bf_flags = setup_tx_flags(skb, use_ldpc);
 
        bf->bf_keytype = get_hw_crypto_keytype(skb);
@@ -1944,8 +1946,14 @@ static void ath_tx_complete_buf(struct ath_softc *sc, struct ath_buf *bf,
        dma_unmap_single(sc->dev, bf->bf_dmacontext, skb->len, DMA_TO_DEVICE);
 
        if (bf->bf_state.bfs_paprd) {
-               sc->paprd_txok = txok;
-               complete(&sc->paprd_complete);
+               if (time_after(jiffies,
+                              bf->bf_state.bfs_paprd_timestamp +
+                              msecs_to_jiffies(ATH_PAPRD_TIMEOUT))) {
+                       dev_kfree_skb_any(skb);
+               } else {
+                       sc->paprd_txok = txok;
+                       complete(&sc->paprd_complete);
+               }
        } else {
                ath_tx_complete(sc, skb, bf->aphy, tx_flags);
                ath_debug_stat_tx(sc, txq, bf, ts);