mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 18:21:24 +02:00
net: mvneta: Handle per-cpu interrupts
Now that our interrupt controller is allowing us to use per-CPU interrupts, actually use it in the mvneta driver. This involves obviously reworking the driver to have a CPU-local NAPI structure, and report for incoming packet using that structure. Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
2502d0ef27
commit
12bb03b436
|
|
@ -285,7 +285,21 @@ struct mvneta_pcpu_stats {
|
||||||
u64 tx_bytes;
|
u64 tx_bytes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct mvneta_pcpu_port {
|
||||||
|
/* Pointer to the shared port */
|
||||||
|
struct mvneta_port *pp;
|
||||||
|
|
||||||
|
/* Pointer to the CPU-local NAPI struct */
|
||||||
|
struct napi_struct napi;
|
||||||
|
|
||||||
|
/* Cause of the previous interrupt */
|
||||||
|
u32 cause_rx_tx;
|
||||||
|
};
|
||||||
|
|
||||||
struct mvneta_port {
|
struct mvneta_port {
|
||||||
|
struct mvneta_pcpu_port __percpu *ports;
|
||||||
|
struct mvneta_pcpu_stats __percpu *stats;
|
||||||
|
|
||||||
int pkt_size;
|
int pkt_size;
|
||||||
unsigned int frag_size;
|
unsigned int frag_size;
|
||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
|
|
@ -293,15 +307,11 @@ struct mvneta_port {
|
||||||
struct mvneta_tx_queue *txqs;
|
struct mvneta_tx_queue *txqs;
|
||||||
struct net_device *dev;
|
struct net_device *dev;
|
||||||
|
|
||||||
u32 cause_rx_tx;
|
|
||||||
struct napi_struct napi;
|
|
||||||
|
|
||||||
/* Core clock */
|
/* Core clock */
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
u8 mcast_count[256];
|
u8 mcast_count[256];
|
||||||
u16 tx_ring_size;
|
u16 tx_ring_size;
|
||||||
u16 rx_ring_size;
|
u16 rx_ring_size;
|
||||||
struct mvneta_pcpu_stats *stats;
|
|
||||||
|
|
||||||
struct mii_bus *mii_bus;
|
struct mii_bus *mii_bus;
|
||||||
struct phy_device *phy_dev;
|
struct phy_device *phy_dev;
|
||||||
|
|
@ -1461,6 +1471,7 @@ static void mvneta_rxq_drop_pkts(struct mvneta_port *pp,
|
||||||
static int mvneta_rx(struct mvneta_port *pp, int rx_todo,
|
static int mvneta_rx(struct mvneta_port *pp, int rx_todo,
|
||||||
struct mvneta_rx_queue *rxq)
|
struct mvneta_rx_queue *rxq)
|
||||||
{
|
{
|
||||||
|
struct mvneta_pcpu_port *port = this_cpu_ptr(pp->ports);
|
||||||
struct net_device *dev = pp->dev;
|
struct net_device *dev = pp->dev;
|
||||||
int rx_done;
|
int rx_done;
|
||||||
u32 rcvd_pkts = 0;
|
u32 rcvd_pkts = 0;
|
||||||
|
|
@ -1515,7 +1526,7 @@ static int mvneta_rx(struct mvneta_port *pp, int rx_todo,
|
||||||
|
|
||||||
skb->protocol = eth_type_trans(skb, dev);
|
skb->protocol = eth_type_trans(skb, dev);
|
||||||
mvneta_rx_csum(pp, rx_status, skb);
|
mvneta_rx_csum(pp, rx_status, skb);
|
||||||
napi_gro_receive(&pp->napi, skb);
|
napi_gro_receive(&port->napi, skb);
|
||||||
|
|
||||||
rcvd_pkts++;
|
rcvd_pkts++;
|
||||||
rcvd_bytes += rx_bytes;
|
rcvd_bytes += rx_bytes;
|
||||||
|
|
@ -1550,7 +1561,7 @@ static int mvneta_rx(struct mvneta_port *pp, int rx_todo,
|
||||||
|
|
||||||
mvneta_rx_csum(pp, rx_status, skb);
|
mvneta_rx_csum(pp, rx_status, skb);
|
||||||
|
|
||||||
napi_gro_receive(&pp->napi, skb);
|
napi_gro_receive(&port->napi, skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rcvd_pkts) {
|
if (rcvd_pkts) {
|
||||||
|
|
@ -2061,12 +2072,11 @@ static void mvneta_set_rx_mode(struct net_device *dev)
|
||||||
/* Interrupt handling - the callback for request_irq() */
|
/* Interrupt handling - the callback for request_irq() */
|
||||||
static irqreturn_t mvneta_isr(int irq, void *dev_id)
|
static irqreturn_t mvneta_isr(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct mvneta_port *pp = (struct mvneta_port *)dev_id;
|
struct mvneta_pcpu_port *port = (struct mvneta_pcpu_port *)dev_id;
|
||||||
|
|
||||||
/* Mask all interrupts */
|
disable_percpu_irq(port->pp->dev->irq);
|
||||||
mvreg_write(pp, MVNETA_INTR_NEW_MASK, 0);
|
|
||||||
|
|
||||||
napi_schedule(&pp->napi);
|
napi_schedule(&port->napi);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
@ -2104,11 +2114,11 @@ static int mvneta_poll(struct napi_struct *napi, int budget)
|
||||||
{
|
{
|
||||||
int rx_done = 0;
|
int rx_done = 0;
|
||||||
u32 cause_rx_tx;
|
u32 cause_rx_tx;
|
||||||
unsigned long flags;
|
|
||||||
struct mvneta_port *pp = netdev_priv(napi->dev);
|
struct mvneta_port *pp = netdev_priv(napi->dev);
|
||||||
|
struct mvneta_pcpu_port *port = this_cpu_ptr(pp->ports);
|
||||||
|
|
||||||
if (!netif_running(pp->dev)) {
|
if (!netif_running(pp->dev)) {
|
||||||
napi_complete(napi);
|
napi_complete(&port->napi);
|
||||||
return rx_done;
|
return rx_done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2135,7 +2145,7 @@ static int mvneta_poll(struct napi_struct *napi, int budget)
|
||||||
/* For the case where the last mvneta_poll did not process all
|
/* For the case where the last mvneta_poll did not process all
|
||||||
* RX packets
|
* RX packets
|
||||||
*/
|
*/
|
||||||
cause_rx_tx |= pp->cause_rx_tx;
|
cause_rx_tx |= port->cause_rx_tx;
|
||||||
if (rxq_number > 1) {
|
if (rxq_number > 1) {
|
||||||
while ((cause_rx_tx & MVNETA_RX_INTR_MASK_ALL) && (budget > 0)) {
|
while ((cause_rx_tx & MVNETA_RX_INTR_MASK_ALL) && (budget > 0)) {
|
||||||
int count;
|
int count;
|
||||||
|
|
@ -2166,16 +2176,11 @@ static int mvneta_poll(struct napi_struct *napi, int budget)
|
||||||
|
|
||||||
if (budget > 0) {
|
if (budget > 0) {
|
||||||
cause_rx_tx = 0;
|
cause_rx_tx = 0;
|
||||||
napi_complete(napi);
|
napi_complete(&port->napi);
|
||||||
local_irq_save(flags);
|
enable_percpu_irq(pp->dev->irq, 0);
|
||||||
mvreg_write(pp, MVNETA_INTR_NEW_MASK,
|
|
||||||
MVNETA_RX_INTR_MASK(rxq_number) |
|
|
||||||
MVNETA_TX_INTR_MASK(txq_number) |
|
|
||||||
MVNETA_MISCINTR_INTR_MASK);
|
|
||||||
local_irq_restore(flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pp->cause_rx_tx = cause_rx_tx;
|
port->cause_rx_tx = cause_rx_tx;
|
||||||
return rx_done;
|
return rx_done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2424,6 +2429,8 @@ static int mvneta_setup_txqs(struct mvneta_port *pp)
|
||||||
|
|
||||||
static void mvneta_start_dev(struct mvneta_port *pp)
|
static void mvneta_start_dev(struct mvneta_port *pp)
|
||||||
{
|
{
|
||||||
|
unsigned int cpu;
|
||||||
|
|
||||||
mvneta_max_rx_size_set(pp, pp->pkt_size);
|
mvneta_max_rx_size_set(pp, pp->pkt_size);
|
||||||
mvneta_txq_max_tx_size_set(pp, pp->pkt_size);
|
mvneta_txq_max_tx_size_set(pp, pp->pkt_size);
|
||||||
|
|
||||||
|
|
@ -2431,7 +2438,11 @@ static void mvneta_start_dev(struct mvneta_port *pp)
|
||||||
mvneta_port_enable(pp);
|
mvneta_port_enable(pp);
|
||||||
|
|
||||||
/* Enable polling on the port */
|
/* Enable polling on the port */
|
||||||
napi_enable(&pp->napi);
|
for_each_present_cpu(cpu) {
|
||||||
|
struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu);
|
||||||
|
|
||||||
|
napi_enable(&port->napi);
|
||||||
|
}
|
||||||
|
|
||||||
/* Unmask interrupts */
|
/* Unmask interrupts */
|
||||||
mvreg_write(pp, MVNETA_INTR_NEW_MASK,
|
mvreg_write(pp, MVNETA_INTR_NEW_MASK,
|
||||||
|
|
@ -2449,9 +2460,15 @@ static void mvneta_start_dev(struct mvneta_port *pp)
|
||||||
|
|
||||||
static void mvneta_stop_dev(struct mvneta_port *pp)
|
static void mvneta_stop_dev(struct mvneta_port *pp)
|
||||||
{
|
{
|
||||||
|
unsigned int cpu;
|
||||||
|
|
||||||
phy_stop(pp->phy_dev);
|
phy_stop(pp->phy_dev);
|
||||||
|
|
||||||
napi_disable(&pp->napi);
|
for_each_present_cpu(cpu) {
|
||||||
|
struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu);
|
||||||
|
|
||||||
|
napi_disable(&port->napi);
|
||||||
|
}
|
||||||
|
|
||||||
netif_carrier_off(pp->dev);
|
netif_carrier_off(pp->dev);
|
||||||
|
|
||||||
|
|
@ -2709,8 +2726,8 @@ static int mvneta_open(struct net_device *dev)
|
||||||
goto err_cleanup_rxqs;
|
goto err_cleanup_rxqs;
|
||||||
|
|
||||||
/* Connect to port interrupt line */
|
/* Connect to port interrupt line */
|
||||||
ret = request_irq(pp->dev->irq, mvneta_isr, 0,
|
ret = request_percpu_irq(pp->dev->irq, mvneta_isr,
|
||||||
MVNETA_DRIVER_NAME, pp);
|
MVNETA_DRIVER_NAME, pp->ports);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
netdev_err(pp->dev, "cannot request irq %d\n", pp->dev->irq);
|
netdev_err(pp->dev, "cannot request irq %d\n", pp->dev->irq);
|
||||||
goto err_cleanup_txqs;
|
goto err_cleanup_txqs;
|
||||||
|
|
@ -2730,7 +2747,7 @@ static int mvneta_open(struct net_device *dev)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_free_irq:
|
err_free_irq:
|
||||||
free_irq(pp->dev->irq, pp);
|
free_percpu_irq(pp->dev->irq, pp->ports);
|
||||||
err_cleanup_txqs:
|
err_cleanup_txqs:
|
||||||
mvneta_cleanup_txqs(pp);
|
mvneta_cleanup_txqs(pp);
|
||||||
err_cleanup_rxqs:
|
err_cleanup_rxqs:
|
||||||
|
|
@ -2745,7 +2762,7 @@ static int mvneta_stop(struct net_device *dev)
|
||||||
|
|
||||||
mvneta_stop_dev(pp);
|
mvneta_stop_dev(pp);
|
||||||
mvneta_mdio_remove(pp);
|
mvneta_mdio_remove(pp);
|
||||||
free_irq(dev->irq, pp);
|
free_percpu_irq(dev->irq, pp->ports);
|
||||||
mvneta_cleanup_rxqs(pp);
|
mvneta_cleanup_rxqs(pp);
|
||||||
mvneta_cleanup_txqs(pp);
|
mvneta_cleanup_txqs(pp);
|
||||||
|
|
||||||
|
|
@ -3032,6 +3049,7 @@ static int mvneta_probe(struct platform_device *pdev)
|
||||||
const char *managed;
|
const char *managed;
|
||||||
int phy_mode;
|
int phy_mode;
|
||||||
int err;
|
int err;
|
||||||
|
int cpu;
|
||||||
|
|
||||||
/* Our multiqueue support is not complete, so for now, only
|
/* Our multiqueue support is not complete, so for now, only
|
||||||
* allow the usage of the first RX queue
|
* allow the usage of the first RX queue
|
||||||
|
|
@ -3107,11 +3125,18 @@ static int mvneta_probe(struct platform_device *pdev)
|
||||||
goto err_clk;
|
goto err_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Alloc per-cpu port structure */
|
||||||
|
pp->ports = alloc_percpu(struct mvneta_pcpu_port);
|
||||||
|
if (!pp->ports) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto err_clk;
|
||||||
|
}
|
||||||
|
|
||||||
/* Alloc per-cpu stats */
|
/* Alloc per-cpu stats */
|
||||||
pp->stats = netdev_alloc_pcpu_stats(struct mvneta_pcpu_stats);
|
pp->stats = netdev_alloc_pcpu_stats(struct mvneta_pcpu_stats);
|
||||||
if (!pp->stats) {
|
if (!pp->stats) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
goto err_clk;
|
goto err_free_ports;
|
||||||
}
|
}
|
||||||
|
|
||||||
dt_mac_addr = of_get_mac_address(dn);
|
dt_mac_addr = of_get_mac_address(dn);
|
||||||
|
|
@ -3152,7 +3177,12 @@ static int mvneta_probe(struct platform_device *pdev)
|
||||||
if (dram_target_info)
|
if (dram_target_info)
|
||||||
mvneta_conf_mbus_windows(pp, dram_target_info);
|
mvneta_conf_mbus_windows(pp, dram_target_info);
|
||||||
|
|
||||||
netif_napi_add(dev, &pp->napi, mvneta_poll, NAPI_POLL_WEIGHT);
|
for_each_present_cpu(cpu) {
|
||||||
|
struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu);
|
||||||
|
|
||||||
|
netif_napi_add(dev, &port->napi, mvneta_poll, NAPI_POLL_WEIGHT);
|
||||||
|
port->pp = pp;
|
||||||
|
}
|
||||||
|
|
||||||
dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
|
dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO;
|
||||||
dev->hw_features |= dev->features;
|
dev->hw_features |= dev->features;
|
||||||
|
|
@ -3183,6 +3213,8 @@ static int mvneta_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
err_free_stats:
|
err_free_stats:
|
||||||
free_percpu(pp->stats);
|
free_percpu(pp->stats);
|
||||||
|
err_free_ports:
|
||||||
|
free_percpu(pp->ports);
|
||||||
err_clk:
|
err_clk:
|
||||||
clk_disable_unprepare(pp->clk);
|
clk_disable_unprepare(pp->clk);
|
||||||
err_put_phy_node:
|
err_put_phy_node:
|
||||||
|
|
@ -3202,6 +3234,7 @@ static int mvneta_remove(struct platform_device *pdev)
|
||||||
|
|
||||||
unregister_netdev(dev);
|
unregister_netdev(dev);
|
||||||
clk_disable_unprepare(pp->clk);
|
clk_disable_unprepare(pp->clk);
|
||||||
|
free_percpu(pp->ports);
|
||||||
free_percpu(pp->stats);
|
free_percpu(pp->stats);
|
||||||
irq_dispose_mapping(dev->irq);
|
irq_dispose_mapping(dev->irq);
|
||||||
of_node_put(pp->phy_node);
|
of_node_put(pp->phy_node);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user