Commit d5a05e69 authored by Vincent Whitchurch's avatar Vincent Whitchurch Committed by Jakub Kicinski

net: stmmac: Use hrtimer for TX coalescing

This driver uses a normal timer for TX coalescing, which means that the
with the default tx-usecs of 1000 microseconds the cleanups actually
happen 10 ms or more later with HZ=100.  This leads to very low
througput with TCP when bridged to a slow link such as a 4G modem.  Fix
this by using an hrtimer instead.

On my ARM platform with HZ=100 and the default TX coalescing settings
(tx-frames 25 tx-usecs 1000), with "tc qdisc add dev eth0 root netem
delay 60ms 40ms rate 50Mbit" run on the server, netperf's TCP_STREAM
improves from ~5.5 Mbps to ~100 Mbps.
Signed-off-by: default avatarVincent Whitchurch <vincent.whitchurch@axis.com>
Link: https://lore.kernel.org/r/20201120150208.6838-1-vincent.whitchurch@axis.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 5112cf59
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#define DRV_MODULE_VERSION "Jan_2016" #define DRV_MODULE_VERSION "Jan_2016"
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/hrtimer.h>
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
#include <linux/stmmac.h> #include <linux/stmmac.h>
#include <linux/phylink.h> #include <linux/phylink.h>
...@@ -46,7 +47,7 @@ struct stmmac_tx_info { ...@@ -46,7 +47,7 @@ struct stmmac_tx_info {
struct stmmac_tx_queue { struct stmmac_tx_queue {
u32 tx_count_frames; u32 tx_count_frames;
int tbs; int tbs;
struct timer_list txtimer; struct hrtimer txtimer;
u32 queue_index; u32 queue_index;
struct stmmac_priv *priv_data; struct stmmac_priv *priv_data;
struct dma_extended_desc *dma_etx ____cacheline_aligned_in_smp; struct dma_extended_desc *dma_etx ____cacheline_aligned_in_smp;
......
...@@ -111,7 +111,7 @@ static void stmmac_init_fs(struct net_device *dev); ...@@ -111,7 +111,7 @@ static void stmmac_init_fs(struct net_device *dev);
static void stmmac_exit_fs(struct net_device *dev); static void stmmac_exit_fs(struct net_device *dev);
#endif #endif
#define STMMAC_COAL_TIMER(x) (jiffies + usecs_to_jiffies(x)) #define STMMAC_COAL_TIMER(x) (ns_to_ktime((x) * NSEC_PER_USEC))
/** /**
* stmmac_verify_args - verify the driver parameters. * stmmac_verify_args - verify the driver parameters.
...@@ -2076,7 +2076,8 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue) ...@@ -2076,7 +2076,8 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue)
/* We still have pending packets, let's call for a new scheduling */ /* We still have pending packets, let's call for a new scheduling */
if (tx_q->dirty_tx != tx_q->cur_tx) if (tx_q->dirty_tx != tx_q->cur_tx)
mod_timer(&tx_q->txtimer, STMMAC_COAL_TIMER(priv->tx_coal_timer)); hrtimer_start(&tx_q->txtimer, STMMAC_COAL_TIMER(priv->tx_coal_timer),
HRTIMER_MODE_REL);
__netif_tx_unlock_bh(netdev_get_tx_queue(priv->dev, queue)); __netif_tx_unlock_bh(netdev_get_tx_queue(priv->dev, queue));
...@@ -2360,7 +2361,8 @@ static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue) ...@@ -2360,7 +2361,8 @@ static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue)
{ {
struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue]; struct stmmac_tx_queue *tx_q = &priv->tx_queue[queue];
mod_timer(&tx_q->txtimer, STMMAC_COAL_TIMER(priv->tx_coal_timer)); hrtimer_start(&tx_q->txtimer, STMMAC_COAL_TIMER(priv->tx_coal_timer),
HRTIMER_MODE_REL);
} }
/** /**
...@@ -2369,9 +2371,9 @@ static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue) ...@@ -2369,9 +2371,9 @@ static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue)
* Description: * Description:
* This is the timer handler to directly invoke the stmmac_tx_clean. * This is the timer handler to directly invoke the stmmac_tx_clean.
*/ */
static void stmmac_tx_timer(struct timer_list *t) static enum hrtimer_restart stmmac_tx_timer(struct hrtimer *t)
{ {
struct stmmac_tx_queue *tx_q = from_timer(tx_q, t, txtimer); struct stmmac_tx_queue *tx_q = container_of(t, struct stmmac_tx_queue, txtimer);
struct stmmac_priv *priv = tx_q->priv_data; struct stmmac_priv *priv = tx_q->priv_data;
struct stmmac_channel *ch; struct stmmac_channel *ch;
...@@ -2385,6 +2387,8 @@ static void stmmac_tx_timer(struct timer_list *t) ...@@ -2385,6 +2387,8 @@ static void stmmac_tx_timer(struct timer_list *t)
spin_unlock_irqrestore(&ch->lock, flags); spin_unlock_irqrestore(&ch->lock, flags);
__napi_schedule(&ch->tx_napi); __napi_schedule(&ch->tx_napi);
} }
return HRTIMER_NORESTART;
} }
/** /**
...@@ -2407,7 +2411,8 @@ static void stmmac_init_coalesce(struct stmmac_priv *priv) ...@@ -2407,7 +2411,8 @@ static void stmmac_init_coalesce(struct stmmac_priv *priv)
for (chan = 0; chan < tx_channel_count; chan++) { for (chan = 0; chan < tx_channel_count; chan++) {
struct stmmac_tx_queue *tx_q = &priv->tx_queue[chan]; struct stmmac_tx_queue *tx_q = &priv->tx_queue[chan];
timer_setup(&tx_q->txtimer, stmmac_tx_timer, 0); hrtimer_init(&tx_q->txtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
tx_q->txtimer.function = stmmac_tx_timer;
} }
} }
...@@ -2899,7 +2904,7 @@ static int stmmac_open(struct net_device *dev) ...@@ -2899,7 +2904,7 @@ static int stmmac_open(struct net_device *dev)
phylink_stop(priv->phylink); phylink_stop(priv->phylink);
for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
del_timer_sync(&priv->tx_queue[chan].txtimer); hrtimer_cancel(&priv->tx_queue[chan].txtimer);
stmmac_hw_teardown(dev); stmmac_hw_teardown(dev);
init_error: init_error:
...@@ -2932,7 +2937,7 @@ static int stmmac_release(struct net_device *dev) ...@@ -2932,7 +2937,7 @@ static int stmmac_release(struct net_device *dev)
stmmac_disable_all_queues(priv); stmmac_disable_all_queues(priv);
for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
del_timer_sync(&priv->tx_queue[chan].txtimer); hrtimer_cancel(&priv->tx_queue[chan].txtimer);
/* Free the IRQ lines */ /* Free the IRQ lines */
free_irq(dev->irq, dev); free_irq(dev->irq, dev);
...@@ -5165,7 +5170,7 @@ int stmmac_suspend(struct device *dev) ...@@ -5165,7 +5170,7 @@ int stmmac_suspend(struct device *dev)
stmmac_disable_all_queues(priv); stmmac_disable_all_queues(priv);
for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++)
del_timer_sync(&priv->tx_queue[chan].txtimer); hrtimer_cancel(&priv->tx_queue[chan].txtimer);
/* Stop TX/RX DMA */ /* Stop TX/RX DMA */
stmmac_stop_all_dma(priv); stmmac_stop_all_dma(priv);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment