ath10k: fix Tx DMA alloc failure during continuous wifi down/up
authorMohammed Shafi Shajakhan <mohammed@qti.qualcomm.com>
Wed, 30 Nov 2016 09:50:14 +0000 (15:20 +0530)
committerKalle Valo <kvalo@qca.qualcomm.com>
Thu, 1 Dec 2016 11:13:55 +0000 (13:13 +0200)
With maximum number of vap's configured in a two radio supported
systems of ~256 Mb RAM, doing a continuous wifi down/up and
intermittent traffic streaming from the connected stations results
in failure to allocate contiguous memory for tx buffers. This results
in the disappearance of all VAP's and a manual reboot is needed as
this is not a crash (or) OOM(for OOM killer to be invoked). To address
this allocate contiguous memory for tx buffers one time and re-use them
until the modules are unloaded but this results in a slight increase in
memory footprint of ath10k when the wifi is down, but the modules are
still loaded. Also as of now we use a separate bool 'tx_mem_allocated'
to keep track of the one time memory allocation, as we cannot come up
with something like 'ath10k_tx_{register,unregister}' before
'ath10k_probe_fw' is called as 'ath10k_htt_tx_alloc_cont_frag_desc'
memory allocation is dependent on the hw_param 'continuous_frag_desc'

a) memory footprint of ath10k without the change

lsmod | grep ath10k
ath10k_core           414498  1 ath10k_pci
ath10k_pci             38236  0

b) memory footprint of ath10k with the change

ath10k_core           414980  1 ath10k_pci
ath10k_pci             38236  0

Memory Failure Call trace:

hostapd: page allocation failure: order:6, mode:0xd0
 [<c021f150>] (__dma_alloc_buffer.isra.23) from
[<c021f23c>] (__alloc_remap_buffer.isra.26+0x14/0xb8)
[<c021f23c>] (__alloc_remap_buffer.isra.26) from
[<c021f664>] (__dma_alloc+0x224/0x2b8)
[<c021f664>] (__dma_alloc) from [<c021f810>]
(arm_dma_alloc+0x84/0x90)
[<c021f810>] (arm_dma_alloc) from [<bf954764>]
(ath10k_htt_tx_alloc+0xe0/0x2e4 [ath10k_core])
[<bf954764>] (ath10k_htt_tx_alloc [ath10k_core]) from
[<bf94e6ac>] (ath10k_core_start+0x538/0xcf8 [ath10k_core])
[<bf94e6ac>] (ath10k_core_start [ath10k_core]) from
[<bf947eec>] (ath10k_start+0xbc/0x56c [ath10k_core])
[<bf947eec>] (ath10k_start [ath10k_core]) from
[<bf8a7a04>] (drv_start+0x40/0x5c [mac80211])
[<bf8a7a04>] (drv_start [mac80211]) from [<bf8b7cf8>]
(ieee80211_do_open+0x170/0x82c [mac80211])
[<bf8b7cf8>] (ieee80211_do_open [mac80211]) from
[<c056afc8>] (__dev_open+0xa0/0xf4)
[21053.491752] Normal: 641*4kB (UEMR) 505*8kB (UEMR) 330*16kB (UEMR)
126*32kB (UEMR) 762*64kB (UEMR) 237*128kB (UEMR) 1*256kB (M) 0*512kB
0*1024kB 0*2048kB 0*4096kB = 95276kB

Signed-off-by: Mohammed Shafi Shajakhan <mohammed@qti.qualcomm.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
drivers/net/wireless/ath/ath10k/core.c
drivers/net/wireless/ath/ath10k/htt.h
drivers/net/wireless/ath/ath10k/htt_tx.c

index 71650edb3fb3687ec0f76248b9d12340a9d64dca..749e381edd380e945bcd7d26c4cab35eb51b3272 100644 (file)
@@ -1858,7 +1858,7 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode,
                goto err_wmi_detach;
        }
 
-       status = ath10k_htt_tx_alloc(&ar->htt);
+       status = ath10k_htt_tx_start(&ar->htt);
        if (status) {
                ath10k_err(ar, "failed to alloc htt tx: %d\n", status);
                goto err_wmi_detach;
@@ -2053,7 +2053,7 @@ void ath10k_core_stop(struct ath10k *ar)
                ath10k_wait_for_suspend(ar, WMI_PDEV_SUSPEND_AND_DISABLE_INTR);
 
        ath10k_hif_stop(ar);
-       ath10k_htt_tx_free(&ar->htt);
+       ath10k_htt_tx_stop(&ar->htt);
        ath10k_htt_rx_free(&ar->htt);
        ath10k_wmi_detach(ar);
 }
@@ -2386,6 +2386,7 @@ void ath10k_core_destroy(struct ath10k *ar)
        destroy_workqueue(ar->workqueue_aux);
 
        ath10k_debug_destroy(ar);
+       ath10k_htt_tx_destroy(&ar->htt);
        ath10k_wmi_free_host_mem(ar);
        ath10k_mac_destroy(ar);
 }
index 164eb3a1056667213656f4002367bf4738617be1..44b25cf00553eb8380e0aa885c522fb370d5f079 100644 (file)
@@ -1717,6 +1717,8 @@ struct ath10k_htt {
                enum htt_tx_mode_switch_mode mode;
                enum htt_q_depth_type type;
        } tx_q_state;
+
+       bool tx_mem_allocated;
 };
 
 #define RX_HTT_HDR_STATUS_LEN 64
@@ -1779,7 +1781,9 @@ int ath10k_htt_connect(struct ath10k_htt *htt);
 int ath10k_htt_init(struct ath10k *ar);
 int ath10k_htt_setup(struct ath10k_htt *htt);
 
-int ath10k_htt_tx_alloc(struct ath10k_htt *htt);
+int ath10k_htt_tx_start(struct ath10k_htt *htt);
+void ath10k_htt_tx_stop(struct ath10k_htt *htt);
+void ath10k_htt_tx_destroy(struct ath10k_htt *htt);
 void ath10k_htt_tx_free(struct ath10k_htt *htt);
 
 int ath10k_htt_rx_alloc(struct ath10k_htt *htt);
index ccbc8c03abc1a159303edaebad01c93c749405e0..27e49db4287a526430588449c89ae5ee4c1048f5 100644 (file)
@@ -350,21 +350,15 @@ static int ath10k_htt_tx_alloc_txdone_fifo(struct ath10k_htt *htt)
        return ret;
 }
 
-int ath10k_htt_tx_alloc(struct ath10k_htt *htt)
+static int ath10k_htt_tx_alloc_buf(struct ath10k_htt *htt)
 {
        struct ath10k *ar = htt->ar;
        int ret;
 
-       ath10k_dbg(ar, ATH10K_DBG_BOOT, "htt tx max num pending tx %d\n",
-                  htt->max_num_pending_tx);
-
-       spin_lock_init(&htt->tx_lock);
-       idr_init(&htt->pending_tx);
-
        ret = ath10k_htt_tx_alloc_cont_txbuf(htt);
        if (ret) {
                ath10k_err(ar, "failed to alloc cont tx buffer: %d\n", ret);
-               goto free_idr_pending_tx;
+               return ret;
        }
 
        ret = ath10k_htt_tx_alloc_cont_frag_desc(htt);
@@ -396,6 +390,31 @@ free_frag_desc:
 free_txbuf:
        ath10k_htt_tx_free_cont_txbuf(htt);
 
+       return ret;
+}
+
+int ath10k_htt_tx_start(struct ath10k_htt *htt)
+{
+       struct ath10k *ar = htt->ar;
+       int ret;
+
+       ath10k_dbg(ar, ATH10K_DBG_BOOT, "htt tx max num pending tx %d\n",
+                  htt->max_num_pending_tx);
+
+       spin_lock_init(&htt->tx_lock);
+       idr_init(&htt->pending_tx);
+
+       if (htt->tx_mem_allocated)
+               return 0;
+
+       ret = ath10k_htt_tx_alloc_buf(htt);
+       if (ret)
+               goto free_idr_pending_tx;
+
+       htt->tx_mem_allocated = true;
+
+       return 0;
+
 free_idr_pending_tx:
        idr_destroy(&htt->pending_tx);
 
@@ -418,15 +437,28 @@ static int ath10k_htt_tx_clean_up_pending(int msdu_id, void *skb, void *ctx)
        return 0;
 }
 
-void ath10k_htt_tx_free(struct ath10k_htt *htt)
+void ath10k_htt_tx_destroy(struct ath10k_htt *htt)
 {
-       idr_for_each(&htt->pending_tx, ath10k_htt_tx_clean_up_pending, htt->ar);
-       idr_destroy(&htt->pending_tx);
+       if (!htt->tx_mem_allocated)
+               return;
 
        ath10k_htt_tx_free_cont_txbuf(htt);
        ath10k_htt_tx_free_txq(htt);
        ath10k_htt_tx_free_cont_frag_desc(htt);
        ath10k_htt_tx_free_txdone_fifo(htt);
+       htt->tx_mem_allocated = false;
+}
+
+void ath10k_htt_tx_stop(struct ath10k_htt *htt)
+{
+       idr_for_each(&htt->pending_tx, ath10k_htt_tx_clean_up_pending, htt->ar);
+       idr_destroy(&htt->pending_tx);
+}
+
+void ath10k_htt_tx_free(struct ath10k_htt *htt)
+{
+       ath10k_htt_tx_stop(htt);
+       ath10k_htt_tx_destroy(htt);
 }
 
 void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)