Commit 24201a64 authored by Andre Przywara's avatar Andre Przywara Committed by David S. Miller

net: axienet: Convert DMA error handler to a work queue

The DMA error handler routine is currently a tasklet, scheduled to run
after the DMA error IRQ was handled.
However it needs to take the MDIO mutex, which is not allowed to do in a
tasklet. A kernel (with debug options) complains consequently:
[  614.050361] net eth0: DMA Tx error 0x174019
[  614.064002] net eth0: Current BD is at: 0x8f84aa0ce
[  614.080195] BUG: sleeping function called from invalid context at kernel/locking/mutex.c:935
[  614.109484] in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 40, name: kworker/u4:4
[  614.135428] 3 locks held by kworker/u4:4/40:
[  614.149075]  #0: ffff000879863328 ((wq_completion)rpciod){....}, at: process_one_work+0x1f0/0x6a8
[  614.177528]  #1: ffff80001251bdf8 ((work_completion)(&task->u.tk_work)){....}, at: process_one_work+0x1f0/0x6a8
[  614.209033]  #2: ffff0008784e0110 (sk_lock-AF_INET-RPC){....}, at: tcp_sendmsg+0x24/0x58
[  614.235429] CPU: 0 PID: 40 Comm: kworker/u4:4 Not tainted 5.6.0-rc3-00926-g4a165a9d5921 #26
[  614.260854] Hardware name: ARM Test FPGA (DT)
[  614.274734] Workqueue: rpciod rpc_async_schedule
[  614.289022] Call trace:
[  614.296871]  dump_backtrace+0x0/0x1a0
[  614.308311]  show_stack+0x14/0x20
[  614.318751]  dump_stack+0xbc/0x100
[  614.329403]  ___might_sleep+0xf0/0x140
[  614.341018]  __might_sleep+0x4c/0x80
[  614.352201]  __mutex_lock+0x5c/0x8a8
[  614.363348]  mutex_lock_nested+0x1c/0x28
[  614.375654]  axienet_dma_err_handler+0x38/0x388
[  614.389999]  tasklet_action_common.isra.15+0x160/0x1a8
[  614.405894]  tasklet_action+0x24/0x30
[  614.417297]  efi_header_end+0xe0/0x494
[  614.429020]  irq_exit+0xd0/0xd8
[  614.439047]  __handle_domain_irq+0x60/0xb0
[  614.451877]  gic_handle_irq+0xdc/0x2d0
[  614.463486]  el1_irq+0xcc/0x180
[  614.473451]  __tcp_transmit_skb+0x41c/0xb58
[  614.486513]  tcp_write_xmit+0x224/0x10a0
[  614.498792]  __tcp_push_pending_frames+0x38/0xc8
[  614.513126]  tcp_rcv_established+0x41c/0x820
[  614.526301]  tcp_v4_do_rcv+0x8c/0x218
[  614.537784]  __release_sock+0x5c/0x108
[  614.549466]  release_sock+0x34/0xa0
[  614.560318]  tcp_sendmsg+0x40/0x58
[  614.571053]  inet_sendmsg+0x40/0x68
[  614.582061]  sock_sendmsg+0x18/0x30
[  614.593074]  xs_sendpages+0x218/0x328
[  614.604506]  xs_tcp_send_request+0xa0/0x1b8
[  614.617461]  xprt_transmit+0xc8/0x4f0
[  614.628943]  call_transmit+0x8c/0xa0
[  614.640028]  __rpc_execute+0xbc/0x6f8
[  614.651380]  rpc_async_schedule+0x28/0x48
[  614.663846]  process_one_work+0x298/0x6a8
[  614.676299]  worker_thread+0x40/0x490
[  614.687687]  kthread+0x134/0x138
[  614.697804]  ret_from_fork+0x10/0x18
[  614.717319] xilinx_axienet 7fe00000.ethernet eth0: Link is Down
[  615.748343] xilinx_axienet 7fe00000.ethernet eth0: Link is Up - 1Gbps/Full - flow control off

Since tasklets are not really popular anymore anyway, lets convert this
over to a work queue, which can sleep and thus can take the MDIO mutex.
Signed-off-by: default avatarAndre Przywara <andre.przywara@arm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e8b6c54f
...@@ -435,7 +435,7 @@ struct axienet_local { ...@@ -435,7 +435,7 @@ struct axienet_local {
void __iomem *regs; void __iomem *regs;
void __iomem *dma_regs; void __iomem *dma_regs;
struct tasklet_struct dma_err_tasklet; struct work_struct dma_err_task;
int tx_irq; int tx_irq;
int rx_irq; int rx_irq;
......
...@@ -806,7 +806,7 @@ static irqreturn_t axienet_tx_irq(int irq, void *_ndev) ...@@ -806,7 +806,7 @@ static irqreturn_t axienet_tx_irq(int irq, void *_ndev)
/* Write to the Rx channel control register */ /* Write to the Rx channel control register */
axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr); axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr);
tasklet_schedule(&lp->dma_err_tasklet); schedule_work(&lp->dma_err_task);
axienet_dma_out32(lp, XAXIDMA_TX_SR_OFFSET, status); axienet_dma_out32(lp, XAXIDMA_TX_SR_OFFSET, status);
} }
out: out:
...@@ -855,7 +855,7 @@ static irqreturn_t axienet_rx_irq(int irq, void *_ndev) ...@@ -855,7 +855,7 @@ static irqreturn_t axienet_rx_irq(int irq, void *_ndev)
/* write to the Rx channel control register */ /* write to the Rx channel control register */
axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr); axienet_dma_out32(lp, XAXIDMA_RX_CR_OFFSET, cr);
tasklet_schedule(&lp->dma_err_tasklet); schedule_work(&lp->dma_err_task);
axienet_dma_out32(lp, XAXIDMA_RX_SR_OFFSET, status); axienet_dma_out32(lp, XAXIDMA_RX_SR_OFFSET, status);
} }
out: out:
...@@ -891,7 +891,7 @@ static irqreturn_t axienet_eth_irq(int irq, void *_ndev) ...@@ -891,7 +891,7 @@ static irqreturn_t axienet_eth_irq(int irq, void *_ndev)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static void axienet_dma_err_handler(unsigned long data); static void axienet_dma_err_handler(struct work_struct *work);
/** /**
* axienet_open - Driver open routine. * axienet_open - Driver open routine.
...@@ -935,9 +935,8 @@ static int axienet_open(struct net_device *ndev) ...@@ -935,9 +935,8 @@ static int axienet_open(struct net_device *ndev)
phylink_start(lp->phylink); phylink_start(lp->phylink);
/* Enable tasklets for Axi DMA error handling */ /* Enable worker thread for Axi DMA error handling */
tasklet_init(&lp->dma_err_tasklet, axienet_dma_err_handler, INIT_WORK(&lp->dma_err_task, axienet_dma_err_handler);
(unsigned long) lp);
/* Enable interrupts for Axi DMA Tx */ /* Enable interrupts for Axi DMA Tx */
ret = request_irq(lp->tx_irq, axienet_tx_irq, IRQF_SHARED, ret = request_irq(lp->tx_irq, axienet_tx_irq, IRQF_SHARED,
...@@ -966,7 +965,7 @@ static int axienet_open(struct net_device *ndev) ...@@ -966,7 +965,7 @@ static int axienet_open(struct net_device *ndev)
err_tx_irq: err_tx_irq:
phylink_stop(lp->phylink); phylink_stop(lp->phylink);
phylink_disconnect_phy(lp->phylink); phylink_disconnect_phy(lp->phylink);
tasklet_kill(&lp->dma_err_tasklet); cancel_work_sync(&lp->dma_err_task);
dev_err(lp->dev, "request_irq() failed\n"); dev_err(lp->dev, "request_irq() failed\n");
return ret; return ret;
} }
...@@ -1025,7 +1024,7 @@ static int axienet_stop(struct net_device *ndev) ...@@ -1025,7 +1024,7 @@ static int axienet_stop(struct net_device *ndev)
axienet_mdio_enable(lp); axienet_mdio_enable(lp);
mutex_unlock(&lp->mii_bus->mdio_lock); mutex_unlock(&lp->mii_bus->mdio_lock);
tasklet_kill(&lp->dma_err_tasklet); cancel_work_sync(&lp->dma_err_task);
if (lp->eth_irq > 0) if (lp->eth_irq > 0)
free_irq(lp->eth_irq, ndev); free_irq(lp->eth_irq, ndev);
...@@ -1484,17 +1483,18 @@ static const struct phylink_mac_ops axienet_phylink_ops = { ...@@ -1484,17 +1483,18 @@ static const struct phylink_mac_ops axienet_phylink_ops = {
}; };
/** /**
* axienet_dma_err_handler - Tasklet handler for Axi DMA Error * axienet_dma_err_handler - Work queue task for Axi DMA Error
* @data: Data passed * @work: pointer to work_struct
* *
* Resets the Axi DMA and Axi Ethernet devices, and reconfigures the * Resets the Axi DMA and Axi Ethernet devices, and reconfigures the
* Tx/Rx BDs. * Tx/Rx BDs.
*/ */
static void axienet_dma_err_handler(unsigned long data) static void axienet_dma_err_handler(struct work_struct *work)
{ {
u32 axienet_status; u32 axienet_status;
u32 cr, i; u32 cr, i;
struct axienet_local *lp = (struct axienet_local *) data; struct axienet_local *lp = container_of(work, struct axienet_local,
dma_err_task);
struct net_device *ndev = lp->ndev; struct net_device *ndev = lp->ndev;
struct axidma_bd *cur_p; struct axidma_bd *cur_p;
......
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