ath10k: fix diag_read to collect data for larger memory
authorAshok Raj Nagarajan <arnagara@qti.qualcomm.com>
Sat, 28 May 2016 08:25:40 +0000 (11:25 +0300)
committerKalle Valo <kvalo@qca.qualcomm.com>
Thu, 2 Jun 2016 14:50:29 +0000 (17:50 +0300)
diag_read uses dma_alloc_coherent to allocate memory requested by the
caller. If this memory requested is larger, more than DIAG_TRANSFER_LIMIT
(2K), then it is likely that we may not get the requested memory and we
would fail.

To solve this, request dma_alloc_coherent for only DIAG_TRANSFER_LIMIT, and
reuse this buffer multiple times as needed to copy the data requested in
smaller chunks of size not more than DIAG_TRANSFER_LIMIT. Previously we
were reading into the caller's only after getting the complete requested
data.

Fixes: 68c03249f388 ('ath10k: convert pci_alloc_consistent() to dma_alloc_coherent()')
Signed-off-by: Ashok Raj Nagarajan <arnagara@qti.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
drivers/net/wireless/ath/ath10k/pci.c

index 8e8e1eb4dd0a41bb7719b0e205a3b5f30244c670..6109293e1213e95c7cbb1e854162df1a9fe3f7a6 100644 (file)
@@ -869,7 +869,7 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data,
        struct ath10k_pci *ar_pci = ath10k_pci_priv(ar);
        int ret = 0;
        u32 *buf;
-       unsigned int completed_nbytes, orig_nbytes, remaining_bytes;
+       unsigned int completed_nbytes, alloc_nbytes, remaining_bytes;
        struct ath10k_ce_pipe *ce_diag;
        /* Host buffer address in CE space */
        u32 ce_data;
@@ -887,9 +887,10 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data,
         *   1) 4-byte alignment
         *   2) Buffer in DMA-able space
         */
-       orig_nbytes = nbytes;
+       alloc_nbytes = min_t(unsigned int, nbytes, DIAG_TRANSFER_LIMIT);
+
        data_buf = (unsigned char *)dma_alloc_coherent(ar->dev,
-                                                      orig_nbytes,
+                                                      alloc_nbytes,
                                                       &ce_data_base,
                                                       GFP_ATOMIC);
 
@@ -897,9 +898,9 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data,
                ret = -ENOMEM;
                goto done;
        }
-       memset(data_buf, 0, orig_nbytes);
+       memset(data_buf, 0, alloc_nbytes);
 
-       remaining_bytes = orig_nbytes;
+       remaining_bytes = nbytes;
        ce_data = ce_data_base;
        while (remaining_bytes) {
                nbytes = min_t(unsigned int, remaining_bytes,
@@ -959,19 +960,22 @@ static int ath10k_pci_diag_read_mem(struct ath10k *ar, u32 address, void *data,
                }
 
                remaining_bytes -= nbytes;
+
+               if (ret) {
+                       ath10k_warn(ar, "failed to read diag value at 0x%x: %d\n",
+                                   address, ret);
+                       break;
+               }
+               memcpy(data, data_buf, nbytes);
+
                address += nbytes;
-               ce_data += nbytes;
+               data += nbytes;
        }
 
 done:
-       if (ret == 0)
-               memcpy(data, data_buf, orig_nbytes);
-       else
-               ath10k_warn(ar, "failed to read diag value at 0x%x: %d\n",
-                           address, ret);
 
        if (data_buf)
-               dma_free_coherent(ar->dev, orig_nbytes, data_buf,
+               dma_free_coherent(ar->dev, alloc_nbytes, data_buf,
                                  ce_data_base);
 
        spin_unlock_bh(&ar_pci->ce_lock);