ath10k: abort incomplete scatter-gather pci tx properly
authorMichal Kazior <michal.kazior@tieto.com>
Mon, 26 May 2014 10:02:59 +0000 (12:02 +0200)
committerKalle Valo <kvalo@qca.qualcomm.com>
Tue, 27 May 2014 09:32:10 +0000 (12:32 +0300)
This prevents leaving incomplete scatter-gather
transfer on CE rings which can lead firmware to
crash.

Reported-By: Avery Pennarun <apenwarr@gmail.com>
Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
drivers/net/wireless/ath/ath10k/ce.c
drivers/net/wireless/ath/ath10k/ce.h
drivers/net/wireless/ath/ath10k/pci.c

index 1e4cad8..d185dc0 100644 (file)
@@ -329,6 +329,33 @@ exit:
        return ret;
 }
 
+void __ath10k_ce_send_revert(struct ath10k_ce_pipe *pipe)
+{
+       struct ath10k *ar = pipe->ar;
+       struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
+       struct ath10k_ce_ring *src_ring = pipe->src_ring;
+       u32 ctrl_addr = pipe->ctrl_addr;
+
+       lockdep_assert_held(&ar_pci->ce_lock);
+
+       /*
+        * This function must be called only if there is an incomplete
+        * scatter-gather transfer (before index register is updated)
+        * that needs to be cleaned up.
+        */
+       if (WARN_ON_ONCE(src_ring->write_index == src_ring->sw_index))
+               return;
+
+       if (WARN_ON_ONCE(src_ring->write_index ==
+                        ath10k_ce_src_ring_write_index_get(ar, ctrl_addr)))
+               return;
+
+       src_ring->write_index--;
+       src_ring->write_index &= src_ring->nentries_mask;
+
+       src_ring->per_transfer_context[src_ring->write_index] = NULL;
+}
+
 int ath10k_ce_send(struct ath10k_ce_pipe *ce_state,
                   void *per_transfer_context,
                   u32 buffer,
index fd0bc35..7a5a36f 100644 (file)
@@ -160,6 +160,8 @@ int ath10k_ce_send_nolock(struct ath10k_ce_pipe *ce_state,
                          unsigned int transfer_id,
                          unsigned int flags);
 
+void __ath10k_ce_send_revert(struct ath10k_ce_pipe *pipe);
+
 void ath10k_ce_send_cb_register(struct ath10k_ce_pipe *ce_state,
                                void (*send_cb)(struct ath10k_ce_pipe *),
                                int disable_interrupts);
index b1eb915..d0004d5 100644 (file)
@@ -765,7 +765,7 @@ static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
        unsigned int nentries_mask;
        unsigned int sw_index;
        unsigned int write_index;
-       int err, i;
+       int err, i = 0;
 
        spin_lock_bh(&ar_pci->ce_lock);
 
@@ -776,7 +776,7 @@ static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
        if (unlikely(CE_RING_DELTA(nentries_mask,
                                   write_index, sw_index - 1) < n_items)) {
                err = -ENOBUFS;
-               goto unlock;
+               goto err;
        }
 
        for (i = 0; i < n_items - 1; i++) {
@@ -793,7 +793,7 @@ static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
                                            items[i].transfer_id,
                                            CE_SEND_FLAG_GATHER);
                if (err)
-                       goto unlock;
+                       goto err;
        }
 
        /* `i` is equal to `n_items -1` after for() */
@@ -811,10 +811,15 @@ static int ath10k_pci_hif_tx_sg(struct ath10k *ar, u8 pipe_id,
                                    items[i].transfer_id,
                                    0);
        if (err)
-               goto unlock;
+               goto err;
+
+       spin_unlock_bh(&ar_pci->ce_lock);
+       return 0;
+
+err:
+       for (; i > 0; i--)
+               __ath10k_ce_send_revert(ce_pipe);
 
-       err = 0;
-unlock:
        spin_unlock_bh(&ar_pci->ce_lock);
        return err;
 }