Commit e08e4c85d4735a3296266cccc4594c23c14cd333
Committed by
Kishon Vijay Abraham I
1 parent
2001f67e47
Exists in
ti-linux-3.12.y
and in
3 other branches
drivers: net: cpsw: convert tx completion to NAPI
CPSW driver TX and RX interrupt handling is not optimal. The driver handles both irq's together and relies on RX NAPI to proces the TX packet. Lets seperate the interrupt handling and convert the TX completion to NAPI as well. With these changes the network perfromance shoots up by almost 40 % for UDP. For TCP we also we get pretty good boost. Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com> Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
Showing 1 changed file with 87 additions and 20 deletions Side-by-side Diff
drivers/net/ethernet/ti/cpsw.c
... | ... | @@ -368,6 +368,9 @@ |
368 | 368 | struct platform_device *pdev; |
369 | 369 | struct net_device *ndev; |
370 | 370 | struct napi_struct napi; |
371 | + struct napi_struct napi_tx; | |
372 | + bool irq_enabled; | |
373 | + bool irq_tx_enabled; | |
371 | 374 | struct device *dev; |
372 | 375 | struct cpsw_platform_data data; |
373 | 376 | struct cpsw_ss_regs __iomem *regs; |
... | ... | @@ -390,7 +393,6 @@ |
390 | 393 | /* snapshot of IRQ numbers */ |
391 | 394 | u32 irqs_table[4]; |
392 | 395 | u32 num_irqs; |
393 | - bool irq_enabled; | |
394 | 396 | struct cpts *cpts; |
395 | 397 | u32 emac_port; |
396 | 398 | }; |
... | ... | @@ -484,6 +486,7 @@ |
484 | 486 | #define CPSW_STATS_LEN ARRAY_SIZE(cpsw_gstrings_stats) |
485 | 487 | |
486 | 488 | #define napi_to_priv(napi) container_of(napi, struct cpsw_priv, napi) |
489 | +#define napi_tx_to_priv(napi) container_of(napi, struct cpsw_priv, napi_tx) | |
487 | 490 | #define for_each_slave(priv, func, arg...) \ |
488 | 491 | do { \ |
489 | 492 | struct cpsw_slave *slave; \ |
... | ... | @@ -713,9 +716,11 @@ |
713 | 716 | { |
714 | 717 | struct cpsw_priv *priv = dev_id; |
715 | 718 | |
716 | - cpsw_intr_disable(priv); | |
717 | - if (priv->irq_enabled == true) { | |
718 | - cpsw_disable_irq(priv); | |
719 | + __raw_writel(0, &priv->wr_regs->rx_en); | |
720 | + if (priv->irq_enabled) { | |
721 | + disable_irq_nosync(priv->irqs_table[0]); | |
722 | + disable_irq_nosync(priv->irqs_table[1]); | |
723 | + disable_irq_nosync(priv->irqs_table[3]); | |
719 | 724 | priv->irq_enabled = false; |
720 | 725 | } |
721 | 726 | |
722 | 727 | |
723 | 728 | |
724 | 729 | |
725 | 730 | |
726 | 731 | |
727 | 732 | |
728 | 733 | |
... | ... | @@ -735,36 +740,84 @@ |
735 | 740 | return IRQ_NONE; |
736 | 741 | } |
737 | 742 | |
743 | +static irqreturn_t cpsw_tx_interrupt(int irq, void *dev_id) | |
744 | +{ | |
745 | + struct cpsw_priv *priv = dev_id; | |
746 | + | |
747 | + __raw_writel(0, &priv->wr_regs->tx_en); | |
748 | + if (priv->irq_tx_enabled) { | |
749 | + disable_irq_nosync(priv->irqs_table[2]); | |
750 | + priv->irq_tx_enabled = false; | |
751 | + } | |
752 | + | |
753 | + if (netif_running(priv->ndev)) { | |
754 | + napi_schedule(&priv->napi_tx); | |
755 | + return IRQ_HANDLED; | |
756 | + } | |
757 | + | |
758 | + priv = cpsw_get_slave_priv(priv, 1); | |
759 | + if (!priv) | |
760 | + return IRQ_NONE; | |
761 | + | |
762 | + if (netif_running(priv->ndev)) { | |
763 | + napi_schedule(&priv->napi_tx); | |
764 | + return IRQ_HANDLED; | |
765 | + } | |
766 | + return IRQ_NONE; | |
767 | +} | |
768 | + | |
738 | 769 | static int cpsw_poll(struct napi_struct *napi, int budget) |
739 | 770 | { |
740 | 771 | struct cpsw_priv *priv = napi_to_priv(napi); |
741 | - int num_tx, num_rx; | |
772 | + int num_rx; | |
742 | 773 | |
743 | - num_tx = cpdma_chan_process(priv->txch, 128); | |
744 | - if (num_tx) | |
745 | - cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_TX); | |
746 | - | |
747 | 774 | num_rx = cpdma_chan_process(priv->rxch, budget); |
748 | 775 | if (num_rx < budget) { |
749 | 776 | struct cpsw_priv *prim_cpsw; |
750 | 777 | |
751 | 778 | napi_complete(napi); |
752 | - cpsw_intr_enable(priv); | |
779 | + __raw_writel(0xFF, &priv->wr_regs->rx_en); | |
753 | 780 | cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_RX); |
754 | 781 | prim_cpsw = cpsw_get_slave_priv(priv, 0); |
755 | - if (prim_cpsw->irq_enabled == false) { | |
782 | + if (!prim_cpsw->irq_enabled) { | |
756 | 783 | prim_cpsw->irq_enabled = true; |
757 | - cpsw_enable_irq(priv); | |
784 | + enable_irq(priv->irqs_table[0]); | |
785 | + enable_irq(priv->irqs_table[1]); | |
786 | + enable_irq(priv->irqs_table[3]); | |
758 | 787 | } |
759 | 788 | } |
760 | 789 | |
761 | - if (num_rx || num_tx) | |
762 | - cpsw_dbg(priv, intr, "poll %d rx, %d tx pkts\n", | |
763 | - num_rx, num_tx); | |
790 | + if (num_rx) | |
791 | + cpsw_dbg(priv, intr, "poll %d rx pkts\n", num_rx); | |
764 | 792 | |
765 | 793 | return num_rx; |
766 | 794 | } |
767 | 795 | |
796 | +static int cpsw_tx_poll(struct napi_struct *napi, int budget) | |
797 | +{ | |
798 | + struct cpsw_priv *priv = napi_tx_to_priv(napi); | |
799 | + int num_tx; | |
800 | + | |
801 | + num_tx = cpdma_chan_process(priv->txch, budget); | |
802 | + if (num_tx < budget) { | |
803 | + struct cpsw_priv *prim_cpsw; | |
804 | + | |
805 | + napi_complete(napi); | |
806 | + __raw_writel(0xFF, &priv->wr_regs->tx_en); | |
807 | + cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_TX); | |
808 | + prim_cpsw = cpsw_get_slave_priv(priv, 0); | |
809 | + if (!prim_cpsw->irq_tx_enabled) { | |
810 | + prim_cpsw->irq_tx_enabled = true; | |
811 | + enable_irq(priv->irqs_table[2]); | |
812 | + } | |
813 | + } | |
814 | + | |
815 | + if (num_tx) | |
816 | + cpsw_dbg(priv, intr, "poll %d tx pkts\n", num_tx); | |
817 | + | |
818 | + return num_tx; | |
819 | +} | |
820 | + | |
768 | 821 | static inline void soft_reset(const char *module, void __iomem *reg) |
769 | 822 | { |
770 | 823 | unsigned long timeout = jiffies + HZ; |
771 | 824 | |
772 | 825 | |
... | ... | @@ -1245,15 +1298,17 @@ |
1245 | 1298 | } |
1246 | 1299 | |
1247 | 1300 | napi_enable(&priv->napi); |
1301 | + napi_enable(&priv->napi_tx); | |
1248 | 1302 | cpdma_ctlr_start(priv->dma); |
1249 | 1303 | cpsw_intr_enable(priv); |
1250 | 1304 | cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_RX); |
1251 | 1305 | cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_TX); |
1252 | 1306 | |
1253 | 1307 | prim_cpsw = cpsw_get_slave_priv(priv, 0); |
1254 | - if (prim_cpsw->irq_enabled == false) { | |
1308 | + if (!prim_cpsw->irq_enabled && !prim_cpsw->irq_tx_enabled) { | |
1255 | 1309 | if ((priv == prim_cpsw) || !netif_running(prim_cpsw->ndev)) { |
1256 | 1310 | prim_cpsw->irq_enabled = true; |
1311 | + prim_cpsw->irq_tx_enabled = true; | |
1257 | 1312 | cpsw_enable_irq(prim_cpsw); |
1258 | 1313 | } |
1259 | 1314 | } |
... | ... | @@ -1277,6 +1332,7 @@ |
1277 | 1332 | cpsw_info(priv, ifdown, "shutting down cpsw device\n"); |
1278 | 1333 | netif_stop_queue(priv->ndev); |
1279 | 1334 | napi_disable(&priv->napi); |
1335 | + napi_disable(&priv->napi_tx); | |
1280 | 1336 | netif_carrier_off(priv->ndev); |
1281 | 1337 | |
1282 | 1338 | if (cpsw_common_res_usage_state(priv) <= 1) { |
... | ... | @@ -1948,6 +2004,8 @@ |
1948 | 2004 | ndev->netdev_ops = &cpsw_netdev_ops; |
1949 | 2005 | SET_ETHTOOL_OPS(ndev, &cpsw_ethtool_ops); |
1950 | 2006 | netif_napi_add(ndev, &priv_sl2->napi, cpsw_poll, CPSW_POLL_WEIGHT); |
2007 | + netif_napi_add(ndev, &priv_sl2->napi_tx, cpsw_tx_poll, | |
2008 | + CPSW_POLL_WEIGHT); | |
1951 | 2009 | |
1952 | 2010 | /* register the network device */ |
1953 | 2011 | SET_NETDEV_DEV(ndev, &pdev->dev); |
... | ... | @@ -1971,7 +2029,7 @@ |
1971 | 2029 | void __iomem *ss_regs; |
1972 | 2030 | struct resource *res, *ss_res; |
1973 | 2031 | u32 slave_offset, sliver_offset, slave_size; |
1974 | - int ret = 0, i, k = 0; | |
2032 | + int ret = 0, i, j = 0, k = 0; | |
1975 | 2033 | |
1976 | 2034 | ndev = alloc_etherdev(sizeof(struct cpsw_priv)); |
1977 | 2035 | if (!ndev) { |
... | ... | @@ -1989,6 +2047,7 @@ |
1989 | 2047 | priv->rx_packet_max = max(rx_packet_max, 128); |
1990 | 2048 | priv->cpts = devm_kzalloc(&pdev->dev, sizeof(struct cpts), GFP_KERNEL); |
1991 | 2049 | priv->irq_enabled = true; |
2050 | + priv->irq_tx_enabled = true; | |
1992 | 2051 | if (!priv->cpts) { |
1993 | 2052 | pr_err("error allocating cpts\n"); |
1994 | 2053 | goto clean_ndev_ret; |
... | ... | @@ -2164,6 +2223,7 @@ |
2164 | 2223 | ndev->netdev_ops = &cpsw_netdev_ops; |
2165 | 2224 | SET_ETHTOOL_OPS(ndev, &cpsw_ethtool_ops); |
2166 | 2225 | netif_napi_add(ndev, &priv->napi, cpsw_poll, CPSW_POLL_WEIGHT); |
2226 | + netif_napi_add(ndev, &priv->napi_tx, cpsw_tx_poll, CPSW_POLL_WEIGHT); | |
2167 | 2227 | |
2168 | 2228 | /* register the network device */ |
2169 | 2229 | SET_NETDEV_DEV(ndev, &pdev->dev); |
... | ... | @@ -2175,9 +2235,16 @@ |
2175 | 2235 | } |
2176 | 2236 | |
2177 | 2237 | while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, k))) { |
2178 | - for (i = res->start; i <= res->end; i++) { | |
2179 | - if (devm_request_irq(&pdev->dev, i, cpsw_interrupt, 0, | |
2180 | - dev_name(priv->dev), priv)) { | |
2238 | + for (i = res->start; i <= res->end; i++, j++) { | |
2239 | + if (j == 2) | |
2240 | + ret = devm_request_irq(&pdev->dev, i, | |
2241 | + cpsw_tx_interrupt, 0, | |
2242 | + "eth-tx", priv); | |
2243 | + else | |
2244 | + ret = devm_request_irq(&pdev->dev, i, | |
2245 | + cpsw_interrupt, 0, | |
2246 | + dev_name(priv->dev), priv); | |
2247 | + if (ret) { | |
2181 | 2248 | dev_err(priv->dev, "error attaching irq\n"); |
2182 | 2249 | goto clean_ale_ret; |
2183 | 2250 | } |