Skip to content

Commit d3dfe8d

Browse files
Gerhard Englederdavem330
authored andcommitted
tsnep: Throttle interrupts
Without interrupt throttling, iperf server mode generates a CPU load of 100% (A53 1.2GHz). Also the throughput suffers with less than 900Mbit/s on a 1Gbit/s link. The reason is a high interrupt load with interrupts every ~20us. Reduce interrupt load by throttling of interrupts. Interrupt delay default is 64us. For iperf server mode the CPU load is significantly reduced to ~20% and the throughput reaches the maximum of 941MBit/s. Interrupts are generated every ~140us. RX and TX coalesce can be configured with ethtool. RX coalesce has priority over TX coalesce if the same interrupt is used. Signed-off-by: Gerhard Engleder <[email protected]> Reviewed-by: Andrew Lunn <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 4f661cc commit d3dfe8d

File tree

4 files changed

+187
-0
lines changed

4 files changed

+187
-0
lines changed

drivers/net/ethernet/engleder/tsnep.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ struct tsnep_queue {
132132

133133
int irq;
134134
u32 irq_mask;
135+
void __iomem *irq_delay_addr;
136+
u8 irq_delay;
135137
};
136138

137139
struct tsnep_adapter {
@@ -223,5 +225,7 @@ static inline void tsnep_ethtool_self_test(struct net_device *dev,
223225
#endif /* CONFIG_TSNEP_SELFTESTS */
224226

225227
void tsnep_get_system_time(struct tsnep_adapter *adapter, u64 *time);
228+
int tsnep_set_irq_coalesce(struct tsnep_queue *queue, u32 usecs);
229+
u32 tsnep_get_irq_coalesce(struct tsnep_queue *queue);
226230

227231
#endif /* _TSNEP_H */

drivers/net/ethernet/engleder/tsnep_ethtool.c

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,137 @@ static int tsnep_ethtool_get_ts_info(struct net_device *netdev,
324324
return 0;
325325
}
326326

327+
static struct tsnep_queue *tsnep_get_queue_with_tx(struct tsnep_adapter *adapter,
328+
int index)
329+
{
330+
int i;
331+
332+
for (i = 0; i < adapter->num_queues; i++) {
333+
if (adapter->queue[i].tx) {
334+
if (index == 0)
335+
return &adapter->queue[i];
336+
337+
index--;
338+
}
339+
}
340+
341+
return NULL;
342+
}
343+
344+
static struct tsnep_queue *tsnep_get_queue_with_rx(struct tsnep_adapter *adapter,
345+
int index)
346+
{
347+
int i;
348+
349+
for (i = 0; i < adapter->num_queues; i++) {
350+
if (adapter->queue[i].rx) {
351+
if (index == 0)
352+
return &adapter->queue[i];
353+
354+
index--;
355+
}
356+
}
357+
358+
return NULL;
359+
}
360+
361+
static int tsnep_ethtool_get_coalesce(struct net_device *netdev,
362+
struct ethtool_coalesce *ec,
363+
struct kernel_ethtool_coalesce *kernel_coal,
364+
struct netlink_ext_ack *extack)
365+
{
366+
struct tsnep_adapter *adapter = netdev_priv(netdev);
367+
struct tsnep_queue *queue;
368+
369+
queue = tsnep_get_queue_with_rx(adapter, 0);
370+
if (queue)
371+
ec->rx_coalesce_usecs = tsnep_get_irq_coalesce(queue);
372+
373+
queue = tsnep_get_queue_with_tx(adapter, 0);
374+
if (queue)
375+
ec->tx_coalesce_usecs = tsnep_get_irq_coalesce(queue);
376+
377+
return 0;
378+
}
379+
380+
static int tsnep_ethtool_set_coalesce(struct net_device *netdev,
381+
struct ethtool_coalesce *ec,
382+
struct kernel_ethtool_coalesce *kernel_coal,
383+
struct netlink_ext_ack *extack)
384+
{
385+
struct tsnep_adapter *adapter = netdev_priv(netdev);
386+
int i;
387+
int retval;
388+
389+
for (i = 0; i < adapter->num_queues; i++) {
390+
/* RX coalesce has priority for queues with TX and RX */
391+
if (adapter->queue[i].rx)
392+
retval = tsnep_set_irq_coalesce(&adapter->queue[i],
393+
ec->rx_coalesce_usecs);
394+
else
395+
retval = tsnep_set_irq_coalesce(&adapter->queue[i],
396+
ec->tx_coalesce_usecs);
397+
if (retval != 0)
398+
return retval;
399+
}
400+
401+
return 0;
402+
}
403+
404+
static int tsnep_ethtool_get_per_queue_coalesce(struct net_device *netdev,
405+
u32 queue,
406+
struct ethtool_coalesce *ec)
407+
{
408+
struct tsnep_adapter *adapter = netdev_priv(netdev);
409+
struct tsnep_queue *queue_with_rx;
410+
struct tsnep_queue *queue_with_tx;
411+
412+
if (queue >= max(adapter->num_tx_queues, adapter->num_rx_queues))
413+
return -EINVAL;
414+
415+
queue_with_rx = tsnep_get_queue_with_rx(adapter, queue);
416+
if (queue_with_rx)
417+
ec->rx_coalesce_usecs = tsnep_get_irq_coalesce(queue_with_rx);
418+
419+
queue_with_tx = tsnep_get_queue_with_tx(adapter, queue);
420+
if (queue_with_tx)
421+
ec->tx_coalesce_usecs = tsnep_get_irq_coalesce(queue_with_tx);
422+
423+
return 0;
424+
}
425+
426+
static int tsnep_ethtool_set_per_queue_coalesce(struct net_device *netdev,
427+
u32 queue,
428+
struct ethtool_coalesce *ec)
429+
{
430+
struct tsnep_adapter *adapter = netdev_priv(netdev);
431+
struct tsnep_queue *queue_with_rx;
432+
struct tsnep_queue *queue_with_tx;
433+
int retval;
434+
435+
if (queue >= max(adapter->num_tx_queues, adapter->num_rx_queues))
436+
return -EINVAL;
437+
438+
queue_with_rx = tsnep_get_queue_with_rx(adapter, queue);
439+
if (queue_with_rx) {
440+
retval = tsnep_set_irq_coalesce(queue_with_rx, ec->rx_coalesce_usecs);
441+
if (retval != 0)
442+
return retval;
443+
}
444+
445+
/* RX coalesce has priority for queues with TX and RX */
446+
queue_with_tx = tsnep_get_queue_with_tx(adapter, queue);
447+
if (queue_with_tx && !queue_with_tx->rx) {
448+
retval = tsnep_set_irq_coalesce(queue_with_tx, ec->tx_coalesce_usecs);
449+
if (retval != 0)
450+
return retval;
451+
}
452+
453+
return 0;
454+
}
455+
327456
const struct ethtool_ops tsnep_ethtool_ops = {
457+
.supported_coalesce_params = ETHTOOL_COALESCE_USECS,
328458
.get_drvinfo = tsnep_ethtool_get_drvinfo,
329459
.get_regs_len = tsnep_ethtool_get_regs_len,
330460
.get_regs = tsnep_ethtool_get_regs,
@@ -340,6 +470,10 @@ const struct ethtool_ops tsnep_ethtool_ops = {
340470
.set_rxnfc = tsnep_ethtool_set_rxnfc,
341471
.get_channels = tsnep_ethtool_get_channels,
342472
.get_ts_info = tsnep_ethtool_get_ts_info,
473+
.get_coalesce = tsnep_ethtool_get_coalesce,
474+
.set_coalesce = tsnep_ethtool_set_coalesce,
475+
.get_per_queue_coalesce = tsnep_ethtool_get_per_queue_coalesce,
476+
.set_per_queue_coalesce = tsnep_ethtool_set_per_queue_coalesce,
343477
.get_link_ksettings = phy_ethtool_get_link_ksettings,
344478
.set_link_ksettings = phy_ethtool_set_link_ksettings,
345479
};

drivers/net/ethernet/engleder/tsnep_hw.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@
4848
#define ECM_COUNTER_LOW 0x0028
4949
#define ECM_COUNTER_HIGH 0x002C
5050

51+
/* interrupt delay */
52+
#define ECM_INT_DELAY 0x0030
53+
#define ECM_INT_DELAY_MASK 0xF0
54+
#define ECM_INT_DELAY_SHIFT 4
55+
#define ECM_INT_DELAY_BASE_US 16
56+
#define ECM_INT_DELAY_OFFSET 1
57+
5158
/* control and status */
5259
#define ECM_STATUS 0x0080
5360
#define ECM_LINK_MODE_OFF 0x01000000

drivers/net/ethernet/engleder/tsnep_main.c

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@
3939
#endif
4040
#define DMA_ADDR_LOW(dma_addr) ((u32)((dma_addr) & 0xFFFFFFFF))
4141

42+
#define TSNEP_COALESCE_USECS_DEFAULT 64
43+
#define TSNEP_COALESCE_USECS_MAX ((ECM_INT_DELAY_MASK >> ECM_INT_DELAY_SHIFT) * \
44+
ECM_INT_DELAY_BASE_US + ECM_INT_DELAY_BASE_US - 1)
45+
4246
static void tsnep_enable_irq(struct tsnep_adapter *adapter, u32 mask)
4347
{
4448
iowrite32(mask, adapter->addr + ECM_INT_ENABLE);
@@ -83,6 +87,33 @@ static irqreturn_t tsnep_irq_txrx(int irq, void *arg)
8387
return IRQ_HANDLED;
8488
}
8589

90+
int tsnep_set_irq_coalesce(struct tsnep_queue *queue, u32 usecs)
91+
{
92+
if (usecs > TSNEP_COALESCE_USECS_MAX)
93+
return -ERANGE;
94+
95+
usecs /= ECM_INT_DELAY_BASE_US;
96+
usecs <<= ECM_INT_DELAY_SHIFT;
97+
usecs &= ECM_INT_DELAY_MASK;
98+
99+
queue->irq_delay &= ~ECM_INT_DELAY_MASK;
100+
queue->irq_delay |= usecs;
101+
iowrite8(queue->irq_delay, queue->irq_delay_addr);
102+
103+
return 0;
104+
}
105+
106+
u32 tsnep_get_irq_coalesce(struct tsnep_queue *queue)
107+
{
108+
u32 usecs;
109+
110+
usecs = (queue->irq_delay & ECM_INT_DELAY_MASK);
111+
usecs >>= ECM_INT_DELAY_SHIFT;
112+
usecs *= ECM_INT_DELAY_BASE_US;
113+
114+
return usecs;
115+
}
116+
86117
static int tsnep_mdiobus_read(struct mii_bus *bus, int addr, int regnum)
87118
{
88119
struct tsnep_adapter *adapter = bus->priv;
@@ -1371,6 +1402,11 @@ static int tsnep_queue_init(struct tsnep_adapter *adapter, int queue_count)
13711402
adapter->queue[0].tx = &adapter->tx[0];
13721403
adapter->queue[0].rx = &adapter->rx[0];
13731404
adapter->queue[0].irq_mask = irq_mask;
1405+
adapter->queue[0].irq_delay_addr = adapter->addr + ECM_INT_DELAY;
1406+
retval = tsnep_set_irq_coalesce(&adapter->queue[0],
1407+
TSNEP_COALESCE_USECS_DEFAULT);
1408+
if (retval < 0)
1409+
return retval;
13741410

13751411
adapter->netdev->irq = adapter->queue[0].irq;
13761412

@@ -1391,6 +1427,12 @@ static int tsnep_queue_init(struct tsnep_adapter *adapter, int queue_count)
13911427
adapter->queue[i].rx = &adapter->rx[i];
13921428
adapter->queue[i].irq_mask =
13931429
irq_mask << (ECM_INT_TXRX_SHIFT * i);
1430+
adapter->queue[i].irq_delay_addr =
1431+
adapter->addr + ECM_INT_DELAY + ECM_INT_DELAY_OFFSET * i;
1432+
retval = tsnep_set_irq_coalesce(&adapter->queue[i],
1433+
TSNEP_COALESCE_USECS_DEFAULT);
1434+
if (retval < 0)
1435+
return retval;
13941436
}
13951437

13961438
return 0;

0 commit comments

Comments
 (0)