]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
qtnfmac: fix releasing Tx/Rx data buffers
authorSergey Matyukevich <sergey.matyukevich.os@quantenna.com>
Sat, 10 Feb 2018 14:04:17 +0000 (17:04 +0300)
committerKalle Valo <kvalo@codeaurora.org>
Tue, 27 Feb 2018 16:18:42 +0000 (18:18 +0200)
Add missing PCI unmap for Tx buffers and release all buffers explicitly.
Managed release using devm_add_action is not suitable for qtnfmac Tx/Rx
data buffers. The reason is in ordering and dependencies: buffers
should be released after transmission is stopped but before PCI
device resources and DMA allocations are released.

Signed-off-by: Sergey Matyukevich <sergey.matyukevich.os@quantenna.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/quantenna/qtnfmac/pearl/pcie.c

index 6f619096432046744ba86ee2a26aa88848ce9e41..be5813aa1486dbd008a77476260c9197b7d0f808 100644 (file)
@@ -478,10 +478,11 @@ static int alloc_rx_buffers(struct qtnf_pcie_bus_priv *priv)
 }
 
 /* all rx/tx activity should have ceased before calling this function */
-static void free_xfer_buffers(void *data)
+static void qtnf_free_xfer_buffers(struct qtnf_pcie_bus_priv *priv)
 {
-       struct qtnf_pcie_bus_priv *priv = (struct qtnf_pcie_bus_priv *)data;
+       struct qtnf_tx_bd *txbd;
        struct qtnf_rx_bd *rxbd;
+       struct sk_buff *skb;
        dma_addr_t paddr;
        int i;
 
@@ -489,19 +490,26 @@ static void free_xfer_buffers(void *data)
        for (i = 0; i < priv->rx_bd_num; i++) {
                if (priv->rx_skb && priv->rx_skb[i]) {
                        rxbd = &priv->rx_bd_vbase[i];
+                       skb = priv->rx_skb[i];
                        paddr = QTN_HOST_ADDR(le32_to_cpu(rxbd->addr_h),
                                              le32_to_cpu(rxbd->addr));
                        pci_unmap_single(priv->pdev, paddr, SKB_BUF_SIZE,
                                         PCI_DMA_FROMDEVICE);
-
-                       dev_kfree_skb_any(priv->rx_skb[i]);
+                       dev_kfree_skb_any(skb);
+                       priv->rx_skb[i] = NULL;
                }
        }
 
        /* free tx buffers */
        for (i = 0; i < priv->tx_bd_num; i++) {
                if (priv->tx_skb && priv->tx_skb[i]) {
-                       dev_kfree_skb_any(priv->tx_skb[i]);
+                       txbd = &priv->tx_bd_vbase[i];
+                       skb = priv->tx_skb[i];
+                       paddr = QTN_HOST_ADDR(le32_to_cpu(txbd->addr_h),
+                                             le32_to_cpu(txbd->addr));
+                       pci_unmap_single(priv->pdev, paddr, skb->len,
+                                        PCI_DMA_TODEVICE);
+                       dev_kfree_skb_any(skb);
                        priv->tx_skb[i] = NULL;
                }
        }
@@ -1321,12 +1329,6 @@ static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                goto err_base;
        }
 
-       ret = devm_add_action(&pdev->dev, free_xfer_buffers, (void *)pcie_priv);
-       if (ret) {
-               pr_err("custom release callback init failed\n");
-               goto err_base;
-       }
-
        ret = qtnf_pcie_init_xfer(pcie_priv);
        if (ret) {
                pr_err("PCIE xfer init failed\n");
@@ -1343,7 +1345,7 @@ static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                               "qtnf_pcie_irq", (void *)bus);
        if (ret) {
                pr_err("failed to request pcie irq %d\n", pdev->irq);
-               goto err_base;
+               goto err_xfer;
        }
 
        tasklet_init(&pcie_priv->reclaim_tq, qtnf_reclaim_tasklet_fn,
@@ -1387,6 +1389,9 @@ static int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 err_bringup_fw:
        netif_napi_del(&bus->mux_napi);
 
+err_xfer:
+       qtnf_free_xfer_buffers(pcie_priv);
+
 err_base:
        flush_workqueue(pcie_priv->workqueue);
        destroy_workqueue(pcie_priv->workqueue);
@@ -1416,6 +1421,7 @@ static void qtnf_pcie_remove(struct pci_dev *pdev)
        destroy_workqueue(priv->workqueue);
        tasklet_kill(&priv->reclaim_tq);
 
+       qtnf_free_xfer_buffers(priv);
        qtnf_debugfs_remove(bus);
 
        qtnf_pcie_free_shm_ipc(priv);