From 97dede3ff979a998d1330805fdaa992a0796f03d Mon Sep 17 00:00:00 2001 From: Ajay Gupta Date: Mon, 4 Nov 2019 15:05:51 -0800 Subject: [PATCH] nvethernet: enable tx interrupt coalescing Tx frame count and software timer based interrupt coalescing is enabled. Tx frame count based interrupt coalescing can be enabled only when tx software timer based interrupt coalescing is also enabled. Bug 200529168 Change-Id: I8ac701c86238e8d34d7dbe9924df1162083c023e Signed-off-by: Ajay Gupta Reviewed-on: https://git-master.nvidia.com/r/2234610 Reviewed-by: mobile promotions Tested-by: mobile promotions --- .../ethernet/nvidia/nvethernet/ether_linux.c | 99 ++++++++++++++++++- .../ethernet/nvidia/nvethernet/ether_linux.h | 6 ++ .../net/ethernet/nvidia/nvethernet/ethtool.c | 49 ++++++++- 3 files changed, 149 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c index b66f1c14..b269840b 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c @@ -364,7 +364,8 @@ static irqreturn_t ether_tx_chan_isr(int irq, void *data) if (likely(napi_schedule_prep(&tx_napi->napi))) { __napi_schedule_irqoff(&tx_napi->napi); - } else { + /* NAPI may get scheduled when tx_usecs is enabled */ + } else if (osi_dma->use_tx_usecs == OSI_DISABLE) { pr_err("Tx DMA-%d IRQ when NAPI already scheduled!\n", chan); WARN_ON(true); } @@ -1363,7 +1364,11 @@ static inline void ether_reset_stats(struct ether_priv_data *pdata) static int ether_close(struct net_device *ndev) { struct ether_priv_data *pdata = netdev_priv(ndev); - int ret = 0; + int ret = 0, i; + + /* Cancel hrtimer */ + for (i = 0; i < pdata->osi_dma->num_dma_chans; i++) + hrtimer_cancel(&pdata->tx_napi[i]->tx_usecs_timer); /* Unregister broadcasting MAC timestamp to clients */ tegra_unregister_hwtime_source(); @@ -1766,6 +1771,15 @@ static int ether_start_xmit(struct sk_buff *skb, struct net_device *ndev) netdev_dbg(ndev, "Tx ring[%d] insufficient desc.\n", chan); } + if (osi_dma->use_tx_usecs == OSI_ENABLE && + atomic_read(&pdata->tx_napi[chan]->tx_usecs_timer_armed) == + OSI_DISABLE) { + atomic_set(&pdata->tx_napi[chan]->tx_usecs_timer_armed, + OSI_ENABLE); + hrtimer_start(&pdata->tx_napi[chan]->tx_usecs_timer, + osi_dma->tx_usecs * NSEC_PER_USEC, + HRTIMER_MODE_REL); + } return NETDEV_TX_OK; } @@ -2355,6 +2369,17 @@ static int ether_napi_poll_tx(struct napi_struct *napi, int budget) int processed; processed = osi_process_tx_completions(osi_dma, chan); + + /* re-arm the timer if tx ring is not empty */ + if (!osi_txring_empty(osi_dma, chan) && + osi_dma->use_tx_usecs == OSI_ENABLE && + atomic_read(&tx_napi->tx_usecs_timer_armed) == OSI_DISABLE) { + atomic_set(&tx_napi->tx_usecs_timer_armed, OSI_ENABLE); + hrtimer_start(&tx_napi->tx_usecs_timer, + osi_dma->tx_usecs * NSEC_PER_USEC, + HRTIMER_MODE_REL); + } + if (processed == 0) { napi_complete(napi); spin_lock_irqsave(&pdata->rlock, flags); @@ -2366,6 +2391,26 @@ static int ether_napi_poll_tx(struct napi_struct *napi, int budget) return budget; } +static enum hrtimer_restart ether_tx_usecs_hrtimer(struct hrtimer *data) +{ + struct ether_tx_napi *tx_napi = container_of(data, struct ether_tx_napi, + tx_usecs_timer); + struct ether_priv_data *pdata = tx_napi->pdata; + struct osi_core_priv_data *osi_core = pdata->osi_core; + unsigned long val; + + val = osi_core->xstats.tx_usecs_swtimer_n[tx_napi->chan]; + osi_core->xstats.tx_usecs_swtimer_n[tx_napi->chan] = + osi_update_stats_counter(val, 1U); + + atomic_set(&pdata->tx_napi[tx_napi->chan]->tx_usecs_timer_armed, + OSI_DISABLE); + if (likely(napi_schedule_prep(&tx_napi->napi))) + __napi_schedule_irqoff(&tx_napi->napi); + + return HRTIMER_NORESTART; +} + /** * @brief Allocate NAPI resources. * @@ -3236,6 +3281,44 @@ static int ether_parse_dt(struct ether_priv_data *pdata) } } + /* tx_usecs value to be set */ + ret = of_property_read_u32(np, "nvidia,tx_usecs", &osi_dma->tx_usecs); + if (ret < 0) { + osi_dma->use_tx_usecs = OSI_DISABLE; + } else { + if (osi_dma->tx_usecs > OSI_MAX_TX_COALESCE_USEC || + osi_dma->tx_usecs < OSI_MIN_TX_COALESCE_USEC) { + dev_err(dev, + "invalid tx_riwt, must be inrange %d to %d\n", + OSI_MIN_TX_COALESCE_USEC, + OSI_MAX_TX_COALESCE_USEC); + return -EINVAL; + } + osi_dma->use_tx_usecs = OSI_ENABLE; + } + /* tx_frames value to be set */ + ret = of_property_read_u32(np, "nvidia,tx_frames", + &osi_dma->tx_frames); + if (ret < 0) { + osi_dma->use_tx_frames = OSI_DISABLE; + } else { + if (osi_dma->tx_frames > ETHER_TX_MAX_FRAME || + osi_dma->tx_frames < OSI_MIN_TX_COALESCE_FRAMES) { + dev_err(dev, + "invalid tx-frames, must be inrange %d to %ld", + OSI_MIN_TX_COALESCE_FRAMES, ETHER_TX_MAX_FRAME); + return -EINVAL; + } + osi_dma->use_tx_frames = OSI_ENABLE; + } + + if (osi_dma->use_tx_usecs == OSI_DISABLE && + osi_dma->use_tx_frames == OSI_ENABLE) { + dev_err(dev, "invalid settings : tx_frames must be enabled" + " along with tx_usecs in DT\n"); + return -EINVAL; + } + /* RIWT value to be set */ ret = of_property_read_u32(np, "nvidia,rx_riwt", &osi_dma->rx_riwt); if (ret < 0) { @@ -3499,7 +3582,7 @@ static int ether_probe(struct platform_device *pdev) struct osi_core_priv_data *osi_core; struct osi_dma_priv_data *osi_dma; struct net_device *ndev; - int ret = 0; + int ret = 0, i; ether_get_num_dma_chan_mtl_q(pdev, &num_dma_chans, &mac, &num_mtl_queues); @@ -3609,6 +3692,16 @@ static int ether_probe(struct platform_device *pdev) goto err_napi; } + /* Setup the tx_usecs timer */ + for (i = 0; i < osi_dma->num_dma_chans; i++) { + atomic_set(&pdata->tx_napi[i]->tx_usecs_timer_armed, + OSI_DISABLE); + hrtimer_init(&pdata->tx_napi[i]->tx_usecs_timer, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); + pdata->tx_napi[i]->tx_usecs_timer.function = + ether_tx_usecs_hrtimer; + } + /* Register sysfs entry */ ret = ether_sysfs_register(pdata->dev); if (ret < 0) { diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h index 998f4b19..97f281d6 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.h @@ -39,6 +39,7 @@ #include #include #include +#include #include #include @@ -137,6 +138,7 @@ */ #define ETHER_TX_DESC_THRESHOLD (MAX_SKB_FRAGS + ETHER_TX_MAX_SPLIT + 2) +#define ETHER_TX_MAX_FRAME (TX_DESC_CNT / ETHER_TX_DESC_THRESHOLD) /** *@brief Returns count of available transmit descriptors * @@ -178,6 +180,10 @@ struct ether_tx_napi { struct ether_priv_data *pdata; /** NAPI instance associated with transmit channel */ struct napi_struct napi; + /** SW timer associated with transmit channel */ + struct hrtimer tx_usecs_timer; + /** SW timer flag associated with transmit channel */ + atomic_t tx_usecs_timer_armed; }; /** diff --git a/drivers/net/ethernet/nvidia/nvethernet/ethtool.c b/drivers/net/ethernet/nvidia/nvethernet/ethtool.c index 09667fbd..ebbf2682 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ethtool.c +++ b/drivers/net/ethernet/nvidia/nvethernet/ethtool.c @@ -116,6 +116,10 @@ static const struct eqos_stats eqos_gstrings_stats[] = { EQOS_EXTRA_STAT(tx_normal_irq_n[1]), EQOS_EXTRA_STAT(tx_normal_irq_n[2]), EQOS_EXTRA_STAT(tx_normal_irq_n[3]), + EQOS_EXTRA_STAT(tx_usecs_swtimer_n[0]), + EQOS_EXTRA_STAT(tx_usecs_swtimer_n[1]), + EQOS_EXTRA_STAT(tx_usecs_swtimer_n[2]), + EQOS_EXTRA_STAT(tx_usecs_swtimer_n[3]), EQOS_EXTRA_STAT(rx_normal_irq_n[0]), EQOS_EXTRA_STAT(rx_normal_irq_n[1]), EQOS_EXTRA_STAT(rx_normal_irq_n[2]), @@ -579,11 +583,42 @@ static int ether_set_coalesce(struct net_device *dev, (ec->rx_max_coalesced_frames_high) || (ec->tx_max_coalesced_frames_irq) || (ec->stats_block_coalesce_usecs) || - (ec->tx_max_coalesced_frames_high) || (ec->rate_sample_interval) || - (ec->tx_coalesce_usecs) || (ec->tx_max_coalesced_frames)) { + (ec->tx_max_coalesced_frames_high) || (ec->rate_sample_interval)) { return -EOPNOTSUPP; } + if (ec->tx_max_coalesced_frames == OSI_DISABLE) { + osi_dma->use_tx_frames = OSI_DISABLE; + } else if ((ec->tx_max_coalesced_frames > ETHER_TX_MAX_FRAME) || + (ec->tx_max_coalesced_frames < OSI_MIN_TX_COALESCE_FRAMES)) { + netdev_err(dev, + "invalid tx-frames, must be in the range of" + " %d to %ld frames\n", OSI_MIN_TX_COALESCE_FRAMES, + ETHER_TX_MAX_FRAME); + return -EINVAL; + } else { + osi_dma->use_tx_frames = OSI_ENABLE; + } + + if (ec->tx_coalesce_usecs == OSI_DISABLE) { + osi_dma->use_tx_usecs = OSI_DISABLE; + } else if ((ec->tx_coalesce_usecs > OSI_MAX_TX_COALESCE_USEC) || + (ec->tx_coalesce_usecs < OSI_MIN_TX_COALESCE_USEC)) { + netdev_err(dev, + "invalid tx_usecs, must be in a range of" + " %d to %d usec\n", OSI_MIN_TX_COALESCE_USEC, + OSI_MAX_TX_COALESCE_USEC); + return -EINVAL; + } else { + osi_dma->use_tx_usecs = OSI_ENABLE; + } + + netdev_err(dev, "TX COALESCING USECS is %s\n", osi_dma->use_tx_usecs ? + "ENABLED" : "DISABLED"); + + netdev_err(dev, "TX COALESCING FRAMES is %s\n", osi_dma->use_tx_frames ? + "ENABLED" : "DISABLED"); + if (ec->rx_max_coalesced_frames == OSI_DISABLE) { osi_dma->use_rx_frames = OSI_DISABLE; } else if ((ec->rx_max_coalesced_frames > RX_DESC_CNT) || @@ -610,6 +645,12 @@ static int ether_set_coalesce(struct net_device *dev, osi_dma->use_riwt = OSI_ENABLE; } + if (osi_dma->use_tx_usecs == OSI_DISABLE && + osi_dma->use_tx_frames == OSI_ENABLE) { + netdev_err(dev, "invalid settings : tx-frames must be enabled" + " along with tx-usecs\n"); + return -EINVAL; + } if (osi_dma->use_riwt == OSI_DISABLE && osi_dma->use_rx_frames == OSI_ENABLE) { netdev_err(dev, "invalid settings : rx-frames must be enabled" @@ -624,6 +665,8 @@ static int ether_set_coalesce(struct net_device *dev, osi_dma->rx_riwt = ec->rx_coalesce_usecs; osi_dma->rx_frames = ec->rx_max_coalesced_frames; + osi_dma->tx_usecs = ec->tx_coalesce_usecs; + osi_dma->tx_frames = ec->tx_max_coalesced_frames; return 0; } @@ -651,6 +694,8 @@ static int ether_get_coalesce(struct net_device *dev, memset(ec, 0, sizeof(struct ethtool_coalesce)); ec->rx_coalesce_usecs = osi_dma->rx_riwt; ec->rx_max_coalesced_frames = osi_dma->rx_frames; + ec->tx_coalesce_usecs = osi_dma->tx_usecs; + ec->tx_max_coalesced_frames = osi_dma->tx_frames; return 0; }