ath10k: fix memory allocation issues on platforms where DMA coherent memory is constr...
authorFelix Fietkau <nbd@openwrt.org>
Tue, 24 Nov 2015 19:09:45 +0000 (19:09 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Tue, 24 Nov 2015 19:09:45 +0000 (19:09 +0000)
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
SVN-Revision: 47625

package/kernel/mac80211/patches/316-ath10k-do-not-use-coherent-memory-for-allocated-devi.patch [new file with mode: 0644]
package/kernel/mac80211/patches/317-ath10k-stop-abusing-GFP_DMA.patch [new file with mode: 0644]

diff --git a/package/kernel/mac80211/patches/316-ath10k-do-not-use-coherent-memory-for-allocated-devi.patch b/package/kernel/mac80211/patches/316-ath10k-do-not-use-coherent-memory-for-allocated-devi.patch
new file mode 100644 (file)
index 0000000..66ebe76
--- /dev/null
@@ -0,0 +1,103 @@
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Sun, 22 Nov 2015 14:03:40 +0100
+Subject: [PATCH] ath10k: do not use coherent memory for allocated device
+ memory chunks
+
+Coherent memory is more expensive to allocate (and constrained on some
+architectures where it has to be pre-allocated). It is also completely
+unnecessary, since the host has no reason to even access these allocated
+memory spaces
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+---
+
+--- a/drivers/net/wireless/ath/ath10k/wmi.c
++++ b/drivers/net/wireless/ath/ath10k/wmi.c
+@@ -4258,34 +4258,58 @@ void ath10k_wmi_event_vdev_resume_req(st
+       ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_RESUME_REQ_EVENTID\n");
+ }
+-static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id,
+-                                   u32 num_units, u32 unit_len)
++static int ath10k_wmi_alloc_chunk(struct ath10k *ar, u32 req_id,
++                                u32 num_units, u32 unit_len)
+ {
+       dma_addr_t paddr;
+       u32 pool_size;
+       int idx = ar->wmi.num_mem_chunks;
++      void *vaddr = NULL;
+-      pool_size = num_units * round_up(unit_len, 4);
++      if (ar->wmi.num_mem_chunks == ARRAY_SIZE(ar->wmi.mem_chunks))
++              return -ENOMEM;
+-      if (!pool_size)
+-              return -EINVAL;
++      while (!vaddr && num_units) {
++              pool_size = num_units * round_up(unit_len, 4);
++              if (!pool_size)
++                      return -EINVAL;
+-      ar->wmi.mem_chunks[idx].vaddr = dma_alloc_coherent(ar->dev,
+-                                                         pool_size,
+-                                                         &paddr,
+-                                                         GFP_KERNEL);
+-      if (!ar->wmi.mem_chunks[idx].vaddr) {
+-              ath10k_warn(ar, "failed to allocate memory chunk\n");
+-              return -ENOMEM;
++              vaddr = kzalloc(pool_size, GFP_KERNEL | __GFP_NOWARN);
++              if (!vaddr)
++                      num_units /= 2;
+       }
+-      memset(ar->wmi.mem_chunks[idx].vaddr, 0, pool_size);
++      if (!num_units)
++              return -ENOMEM;
++      paddr = dma_map_single(ar->dev, vaddr, pool_size, DMA_TO_DEVICE);
++      if (dma_mapping_error(ar->dev, paddr)) {
++              kfree(vaddr);
++              return -ENOMEM;
++      }
++
++      ar->wmi.mem_chunks[idx].vaddr = vaddr;
+       ar->wmi.mem_chunks[idx].paddr = paddr;
+       ar->wmi.mem_chunks[idx].len = pool_size;
+       ar->wmi.mem_chunks[idx].req_id = req_id;
+       ar->wmi.num_mem_chunks++;
++      return num_units;
++}
++
++static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id,
++                                   u32 num_units, u32 unit_len)
++{
++      int ret;
++
++      while (num_units) {
++              ret = ath10k_wmi_alloc_chunk(ar, req_id, num_units, unit_len);
++              if (ret < 0)
++                      return ret;
++
++              num_units -= ret;
++      }
++
+       return 0;
+ }
+@@ -7616,10 +7640,11 @@ void ath10k_wmi_free_host_mem(struct ath
+       /* free the host memory chunks requested by firmware */
+       for (i = 0; i < ar->wmi.num_mem_chunks; i++) {
+-              dma_free_coherent(ar->dev,
+-                                ar->wmi.mem_chunks[i].len,
+-                                ar->wmi.mem_chunks[i].vaddr,
+-                                ar->wmi.mem_chunks[i].paddr);
++              dma_unmap_single(ar->dev,
++                               ar->wmi.mem_chunks[i].paddr,
++                               ar->wmi.mem_chunks[i].len,
++                               DMA_TO_DEVICE);
++              kfree(ar->wmi.mem_chunks[i].vaddr);
+       }
+       ar->wmi.num_mem_chunks = 0;
diff --git a/package/kernel/mac80211/patches/317-ath10k-stop-abusing-GFP_DMA.patch b/package/kernel/mac80211/patches/317-ath10k-stop-abusing-GFP_DMA.patch
new file mode 100644 (file)
index 0000000..a229a13
--- /dev/null
@@ -0,0 +1,54 @@
+From: Felix Fietkau <nbd@openwrt.org>
+Date: Tue, 24 Nov 2015 11:33:54 +0100
+Subject: [PATCH] ath10k: stop abusing GFP_DMA
+
+Allocations from the DMA zone were originally added for legacy ISA
+stuff, or PCI devices that have specific limitations in their DMA
+addressing capabilities. It has no place in ath10k, which can do
+full 32-bit DMA.
+
+Fixes memory allocation errors on some platforms.
+
+Signed-off-by: Felix Fietkau <nbd@openwrt.org>
+---
+
+--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
++++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
+@@ -536,7 +536,7 @@ int ath10k_htt_rx_alloc(struct ath10k_ht
+       size = htt->rx_ring.size * sizeof(htt->rx_ring.paddrs_ring);
+-      vaddr = dma_alloc_coherent(htt->ar->dev, size, &paddr, GFP_DMA);
++      vaddr = dma_alloc_coherent(htt->ar->dev, size, &paddr, GFP_KERNEL);
+       if (!vaddr)
+               goto err_dma_ring;
+@@ -545,7 +545,7 @@ int ath10k_htt_rx_alloc(struct ath10k_ht
+       vaddr = dma_alloc_coherent(htt->ar->dev,
+                                  sizeof(*htt->rx_ring.alloc_idx.vaddr),
+-                                 &paddr, GFP_DMA);
++                                 &paddr, GFP_KERNEL);
+       if (!vaddr)
+               goto err_dma_idx;
+--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
++++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
+@@ -111,7 +111,7 @@ int ath10k_htt_tx_alloc(struct ath10k_ht
+       size = htt->max_num_pending_tx * sizeof(struct ath10k_htt_txbuf);
+       htt->txbuf.vaddr = dma_alloc_coherent(ar->dev, size,
+                                                 &htt->txbuf.paddr,
+-                                                GFP_DMA);
++                                                GFP_KERNEL);
+       if (!htt->txbuf.vaddr) {
+               ath10k_err(ar, "failed to alloc tx buffer\n");
+               ret = -ENOMEM;
+@@ -124,7 +124,7 @@ int ath10k_htt_tx_alloc(struct ath10k_ht
+       size = htt->max_num_pending_tx * sizeof(struct htt_msdu_ext_desc);
+       htt->frag_desc.vaddr = dma_alloc_coherent(ar->dev, size,
+                                                 &htt->frag_desc.paddr,
+-                                                GFP_DMA);
++                                                GFP_KERNEL);
+       if (!htt->frag_desc.vaddr) {
+               ath10k_warn(ar, "failed to alloc fragment desc memory\n");
+               ret = -ENOMEM;