Skip to content

Commit edc660f

Browse files
wojtas-marcindavem330
authored andcommitted
net: mvpp2: replace TX coalescing interrupts with hrtimer
The PP2 controller is capable of per-CPU TX processing, which means there are per-CPU banked register sets and queues. Current version of the driver supports TX packet coalescing - once on given CPU sent packets amount reaches a threshold value, an IRQ occurs. However, there is a single interrupt line responsible for CPU0/1 TX and RX events (the latter is not per-CPU, the hardware does not support RSS). When the top-half executes the interrupt cause is not known. This is why in NAPI poll function, along with RX processing, IRQ cause register on both CPU's is accessed in order to determine on which of them the TX coalescing threshold might have been reached. Thus the egress processing and releasing the buffers is able to take place on the corresponding CPU. Hitherto approach lead to an illegal usage of on_each_cpu function in softirq context. The problem is solved by resigning from TX coalescing interrupts and separating egress finalization from NAPI processing. For that purpose a method of using hrtimer is introduced. In main transmit function (mvpp2_tx) buffers are released once a software coalescing threshold is reached. In case not all the data is processed a timer is set on this CPU - in its interrupt context a tasklet is scheduled in which all queues are processed. At once only one timer per-CPU can be running, which is controlled by a dedicated flag. This commit removes TX processing from NAPI polling function, disables hardware coalescing and enables hrtimer with tasklet, using new per-CPU port structure (mvpp2_port_pcpu). Signed-off-by: Marcin Wojtas <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 71ce391 commit edc660f

File tree

1 file changed

+130
-47
lines changed
  • drivers/net/ethernet/marvell

1 file changed

+130
-47
lines changed

drivers/net/ethernet/marvell/mvpp2.c

Lines changed: 130 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
#include <linux/of_address.h>
2828
#include <linux/phy.h>
2929
#include <linux/clk.h>
30+
#include <linux/hrtimer.h>
31+
#include <linux/ktime.h>
3032
#include <uapi/linux/ppp_defs.h>
3133
#include <net/ip.h>
3234
#include <net/ipv6.h>
@@ -299,6 +301,7 @@
299301

300302
/* Coalescing */
301303
#define MVPP2_TXDONE_COAL_PKTS_THRESH 15
304+
#define MVPP2_TXDONE_HRTIMER_PERIOD_NS 1000000UL
302305
#define MVPP2_RX_COAL_PKTS 32
303306
#define MVPP2_RX_COAL_USEC 100
304307

@@ -660,6 +663,14 @@ struct mvpp2_pcpu_stats {
660663
u64 tx_bytes;
661664
};
662665

666+
/* Per-CPU port control */
667+
struct mvpp2_port_pcpu {
668+
struct hrtimer tx_done_timer;
669+
bool timer_scheduled;
670+
/* Tasklet for egress finalization */
671+
struct tasklet_struct tx_done_tasklet;
672+
};
673+
663674
struct mvpp2_port {
664675
u8 id;
665676

@@ -679,6 +690,9 @@ struct mvpp2_port {
679690
u32 pending_cause_rx;
680691
struct napi_struct napi;
681692

693+
/* Per-CPU port control */
694+
struct mvpp2_port_pcpu __percpu *pcpu;
695+
682696
/* Flags */
683697
unsigned long flags;
684698

@@ -3798,7 +3812,6 @@ static void mvpp2_interrupts_unmask(void *arg)
37983812

37993813
mvpp2_write(port->priv, MVPP2_ISR_RX_TX_MASK_REG(port->id),
38003814
(MVPP2_CAUSE_MISC_SUM_MASK |
3801-
MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK |
38023815
MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK));
38033816
}
38043817

@@ -4374,23 +4387,6 @@ static void mvpp2_rx_time_coal_set(struct mvpp2_port *port,
43744387
rxq->time_coal = usec;
43754388
}
43764389

4377-
/* Set threshold for TX_DONE pkts coalescing */
4378-
static void mvpp2_tx_done_pkts_coal_set(void *arg)
4379-
{
4380-
struct mvpp2_port *port = arg;
4381-
int queue;
4382-
u32 val;
4383-
4384-
for (queue = 0; queue < txq_number; queue++) {
4385-
struct mvpp2_tx_queue *txq = port->txqs[queue];
4386-
4387-
val = (txq->done_pkts_coal << MVPP2_TRANSMITTED_THRESH_OFFSET) &
4388-
MVPP2_TRANSMITTED_THRESH_MASK;
4389-
mvpp2_write(port->priv, MVPP2_TXQ_NUM_REG, txq->id);
4390-
mvpp2_write(port->priv, MVPP2_TXQ_THRESH_REG, val);
4391-
}
4392-
}
4393-
43944390
/* Free Tx queue skbuffs */
43954391
static void mvpp2_txq_bufs_free(struct mvpp2_port *port,
43964392
struct mvpp2_tx_queue *txq,
@@ -4425,7 +4421,7 @@ static inline struct mvpp2_rx_queue *mvpp2_get_rx_queue(struct mvpp2_port *port,
44254421
static inline struct mvpp2_tx_queue *mvpp2_get_tx_queue(struct mvpp2_port *port,
44264422
u32 cause)
44274423
{
4428-
int queue = fls(cause >> 16) - 1;
4424+
int queue = fls(cause) - 1;
44294425

44304426
return port->txqs[queue];
44314427
}
@@ -4452,6 +4448,29 @@ static void mvpp2_txq_done(struct mvpp2_port *port, struct mvpp2_tx_queue *txq,
44524448
netif_tx_wake_queue(nq);
44534449
}
44544450

4451+
static unsigned int mvpp2_tx_done(struct mvpp2_port *port, u32 cause)
4452+
{
4453+
struct mvpp2_tx_queue *txq;
4454+
struct mvpp2_txq_pcpu *txq_pcpu;
4455+
unsigned int tx_todo = 0;
4456+
4457+
while (cause) {
4458+
txq = mvpp2_get_tx_queue(port, cause);
4459+
if (!txq)
4460+
break;
4461+
4462+
txq_pcpu = this_cpu_ptr(txq->pcpu);
4463+
4464+
if (txq_pcpu->count) {
4465+
mvpp2_txq_done(port, txq, txq_pcpu);
4466+
tx_todo += txq_pcpu->count;
4467+
}
4468+
4469+
cause &= ~(1 << txq->log_id);
4470+
}
4471+
return tx_todo;
4472+
}
4473+
44554474
/* Rx/Tx queue initialization/cleanup methods */
44564475

44574476
/* Allocate and initialize descriptors for aggr TXQ */
@@ -4812,7 +4831,6 @@ static int mvpp2_setup_txqs(struct mvpp2_port *port)
48124831
goto err_cleanup;
48134832
}
48144833

4815-
on_each_cpu(mvpp2_tx_done_pkts_coal_set, port, 1);
48164834
on_each_cpu(mvpp2_txq_sent_counter_clear, port, 1);
48174835
return 0;
48184836

@@ -4894,6 +4912,49 @@ static void mvpp2_link_event(struct net_device *dev)
48944912
}
48954913
}
48964914

4915+
static void mvpp2_timer_set(struct mvpp2_port_pcpu *port_pcpu)
4916+
{
4917+
ktime_t interval;
4918+
4919+
if (!port_pcpu->timer_scheduled) {
4920+
port_pcpu->timer_scheduled = true;
4921+
interval = ktime_set(0, MVPP2_TXDONE_HRTIMER_PERIOD_NS);
4922+
hrtimer_start(&port_pcpu->tx_done_timer, interval,
4923+
HRTIMER_MODE_REL_PINNED);
4924+
}
4925+
}
4926+
4927+
static void mvpp2_tx_proc_cb(unsigned long data)
4928+
{
4929+
struct net_device *dev = (struct net_device *)data;
4930+
struct mvpp2_port *port = netdev_priv(dev);
4931+
struct mvpp2_port_pcpu *port_pcpu = this_cpu_ptr(port->pcpu);
4932+
unsigned int tx_todo, cause;
4933+
4934+
if (!netif_running(dev))
4935+
return;
4936+
port_pcpu->timer_scheduled = false;
4937+
4938+
/* Process all the Tx queues */
4939+
cause = (1 << txq_number) - 1;
4940+
tx_todo = mvpp2_tx_done(port, cause);
4941+
4942+
/* Set the timer in case not all the packets were processed */
4943+
if (tx_todo)
4944+
mvpp2_timer_set(port_pcpu);
4945+
}
4946+
4947+
static enum hrtimer_restart mvpp2_hr_timer_cb(struct hrtimer *timer)
4948+
{
4949+
struct mvpp2_port_pcpu *port_pcpu = container_of(timer,
4950+
struct mvpp2_port_pcpu,
4951+
tx_done_timer);
4952+
4953+
tasklet_schedule(&port_pcpu->tx_done_tasklet);
4954+
4955+
return HRTIMER_NORESTART;
4956+
}
4957+
48974958
/* Main RX/TX processing routines */
48984959

48994960
/* Display more error info */
@@ -5262,6 +5323,17 @@ static int mvpp2_tx(struct sk_buff *skb, struct net_device *dev)
52625323
dev_kfree_skb_any(skb);
52635324
}
52645325

5326+
/* Finalize TX processing */
5327+
if (txq_pcpu->count >= txq->done_pkts_coal)
5328+
mvpp2_txq_done(port, txq, txq_pcpu);
5329+
5330+
/* Set the timer in case not all frags were processed */
5331+
if (txq_pcpu->count <= frags && txq_pcpu->count > 0) {
5332+
struct mvpp2_port_pcpu *port_pcpu = this_cpu_ptr(port->pcpu);
5333+
5334+
mvpp2_timer_set(port_pcpu);
5335+
}
5336+
52655337
return NETDEV_TX_OK;
52665338
}
52675339

@@ -5275,10 +5347,11 @@ static inline void mvpp2_cause_error(struct net_device *dev, int cause)
52755347
netdev_err(dev, "tx fifo underrun error\n");
52765348
}
52775349

5278-
static void mvpp2_txq_done_percpu(void *arg)
5350+
static int mvpp2_poll(struct napi_struct *napi, int budget)
52795351
{
5280-
struct mvpp2_port *port = arg;
5281-
u32 cause_rx_tx, cause_tx, cause_misc;
5352+
u32 cause_rx_tx, cause_rx, cause_misc;
5353+
int rx_done = 0;
5354+
struct mvpp2_port *port = netdev_priv(napi->dev);
52825355

52835356
/* Rx/Tx cause register
52845357
*
@@ -5292,7 +5365,7 @@ static void mvpp2_txq_done_percpu(void *arg)
52925365
*/
52935366
cause_rx_tx = mvpp2_read(port->priv,
52945367
MVPP2_ISR_RX_TX_CAUSE_REG(port->id));
5295-
cause_tx = cause_rx_tx & MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK;
5368+
cause_rx_tx &= ~MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK;
52965369
cause_misc = cause_rx_tx & MVPP2_CAUSE_MISC_SUM_MASK;
52975370

52985371
if (cause_misc) {
@@ -5304,26 +5377,6 @@ static void mvpp2_txq_done_percpu(void *arg)
53045377
cause_rx_tx & ~MVPP2_CAUSE_MISC_SUM_MASK);
53055378
}
53065379

5307-
/* Release TX descriptors */
5308-
if (cause_tx) {
5309-
struct mvpp2_tx_queue *txq = mvpp2_get_tx_queue(port, cause_tx);
5310-
struct mvpp2_txq_pcpu *txq_pcpu = this_cpu_ptr(txq->pcpu);
5311-
5312-
if (txq_pcpu->count)
5313-
mvpp2_txq_done(port, txq, txq_pcpu);
5314-
}
5315-
}
5316-
5317-
static int mvpp2_poll(struct napi_struct *napi, int budget)
5318-
{
5319-
u32 cause_rx_tx, cause_rx;
5320-
int rx_done = 0;
5321-
struct mvpp2_port *port = netdev_priv(napi->dev);
5322-
5323-
on_each_cpu(mvpp2_txq_done_percpu, port, 1);
5324-
5325-
cause_rx_tx = mvpp2_read(port->priv,
5326-
MVPP2_ISR_RX_TX_CAUSE_REG(port->id));
53275380
cause_rx = cause_rx_tx & MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK;
53285381

53295382
/* Process RX packets */
@@ -5568,6 +5621,8 @@ static int mvpp2_open(struct net_device *dev)
55685621
static int mvpp2_stop(struct net_device *dev)
55695622
{
55705623
struct mvpp2_port *port = netdev_priv(dev);
5624+
struct mvpp2_port_pcpu *port_pcpu;
5625+
int cpu;
55715626

55725627
mvpp2_stop_dev(port);
55735628
mvpp2_phy_disconnect(port);
@@ -5576,6 +5631,13 @@ static int mvpp2_stop(struct net_device *dev)
55765631
on_each_cpu(mvpp2_interrupts_mask, port, 1);
55775632

55785633
free_irq(port->irq, port);
5634+
for_each_present_cpu(cpu) {
5635+
port_pcpu = per_cpu_ptr(port->pcpu, cpu);
5636+
5637+
hrtimer_cancel(&port_pcpu->tx_done_timer);
5638+
port_pcpu->timer_scheduled = false;
5639+
tasklet_kill(&port_pcpu->tx_done_tasklet);
5640+
}
55795641
mvpp2_cleanup_rxqs(port);
55805642
mvpp2_cleanup_txqs(port);
55815643

@@ -5791,7 +5853,6 @@ static int mvpp2_ethtool_set_coalesce(struct net_device *dev,
57915853
txq->done_pkts_coal = c->tx_max_coalesced_frames;
57925854
}
57935855

5794-
on_each_cpu(mvpp2_tx_done_pkts_coal_set, port, 1);
57955856
return 0;
57965857
}
57975858

@@ -6042,6 +6103,7 @@ static int mvpp2_port_probe(struct platform_device *pdev,
60426103
{
60436104
struct device_node *phy_node;
60446105
struct mvpp2_port *port;
6106+
struct mvpp2_port_pcpu *port_pcpu;
60456107
struct net_device *dev;
60466108
struct resource *res;
60476109
const char *dt_mac_addr;
@@ -6051,7 +6113,7 @@ static int mvpp2_port_probe(struct platform_device *pdev,
60516113
int features;
60526114
int phy_mode;
60536115
int priv_common_regs_num = 2;
6054-
int err, i;
6116+
int err, i, cpu;
60556117

60566118
dev = alloc_etherdev_mqs(sizeof(struct mvpp2_port), txq_number,
60576119
rxq_number);
@@ -6142,6 +6204,24 @@ static int mvpp2_port_probe(struct platform_device *pdev,
61426204
}
61436205
mvpp2_port_power_up(port);
61446206

6207+
port->pcpu = alloc_percpu(struct mvpp2_port_pcpu);
6208+
if (!port->pcpu) {
6209+
err = -ENOMEM;
6210+
goto err_free_txq_pcpu;
6211+
}
6212+
6213+
for_each_present_cpu(cpu) {
6214+
port_pcpu = per_cpu_ptr(port->pcpu, cpu);
6215+
6216+
hrtimer_init(&port_pcpu->tx_done_timer, CLOCK_MONOTONIC,
6217+
HRTIMER_MODE_REL_PINNED);
6218+
port_pcpu->tx_done_timer.function = mvpp2_hr_timer_cb;
6219+
port_pcpu->timer_scheduled = false;
6220+
6221+
tasklet_init(&port_pcpu->tx_done_tasklet, mvpp2_tx_proc_cb,
6222+
(unsigned long)dev);
6223+
}
6224+
61456225
netif_napi_add(dev, &port->napi, mvpp2_poll, NAPI_POLL_WEIGHT);
61466226
features = NETIF_F_SG | NETIF_F_IP_CSUM;
61476227
dev->features = features | NETIF_F_RXCSUM;
@@ -6151,7 +6231,7 @@ static int mvpp2_port_probe(struct platform_device *pdev,
61516231
err = register_netdev(dev);
61526232
if (err < 0) {
61536233
dev_err(&pdev->dev, "failed to register netdev\n");
6154-
goto err_free_txq_pcpu;
6234+
goto err_free_port_pcpu;
61556235
}
61566236
netdev_info(dev, "Using %s mac address %pM\n", mac_from, dev->dev_addr);
61576237

@@ -6160,6 +6240,8 @@ static int mvpp2_port_probe(struct platform_device *pdev,
61606240
priv->port_list[id] = port;
61616241
return 0;
61626242

6243+
err_free_port_pcpu:
6244+
free_percpu(port->pcpu);
61636245
err_free_txq_pcpu:
61646246
for (i = 0; i < txq_number; i++)
61656247
free_percpu(port->txqs[i]->pcpu);
@@ -6178,6 +6260,7 @@ static void mvpp2_port_remove(struct mvpp2_port *port)
61786260
int i;
61796261

61806262
unregister_netdev(port->dev);
6263+
free_percpu(port->pcpu);
61816264
free_percpu(port->stats);
61826265
for (i = 0; i < txq_number; i++)
61836266
free_percpu(port->txqs[i]->pcpu);

0 commit comments

Comments
 (0)