Fix a number of bugs which have been present since the first commit.
The bugs fixed in patch 1,2 and 4 have all been observed in real systems, and
was relatively easy to reproduce given an appropriate stress setup.
Esben Haabendal (4):
net: ll_temac: Fix race condition causing TX hang
net: ll_temac: Add more error handling of dma_map_single() calls
net: ll_temac: Fix RX buffer descriptor handling on GFP_ATOMIC
pressure
net: ll_temac: Handle DMA halt condition caused by buffer underrun
drivers/net/ethernet/xilinx/ll_temac.h | 4 +
drivers/net/ethernet/xilinx/ll_temac_main.c | 204 ++++++++++++++++----
2 files changed, 170 insertions(+), 38 deletions(-)
--
2.25.0
It is possible that the interrupt handler fires and frees up space in
the TX ring in between checking for sufficient TX ring space and
stopping the TX queue in temac_start_xmit. If this happens, the
queue wake from the interrupt handler will occur before the queue is
stopped, causing a lost wakeup and the adapter's transmit hanging.
To avoid this, after stopping the queue, check again whether there is
sufficient space in the TX ring. If so, wake up the queue again.
This is a port of the similar fix in axienet driver,
commit 7de44285c1f6 ("net: axienet: Fix race condition causing TX hang").
Fixes: 23ecc4bde21f ("net: ll_temac: fix checksum offload logic")
Signed-off-by: Esben Haabendal <redacted>
---
drivers/net/ethernet/xilinx/ll_temac_main.c | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
@@ -830,9 +833,19 @@ temac_start_xmit(struct sk_buff *skb, struct net_device *ndev)cur_p=&lp->tx_bd_v[lp->tx_bd_tail];if(temac_check_tx_bd_space(lp,num_frag+1)){-if(!netif_queue_stopped(ndev))-netif_stop_queue(ndev);-returnNETDEV_TX_BUSY;+if(netif_queue_stopped(ndev))+returnNETDEV_TX_BUSY;++netif_stop_queue(ndev);++/* Matches barrier in temac_start_xmit_done */+smp_mb();++/* Space might have just been freed - check again */+if(temac_check_tx_bd_space(lp,num_frag))+returnNETDEV_TX_BUSY;++netif_wake_queue(ndev);}cur_p->app0=0;
This adds error handling to the remaining dma_map_single() calls, so that
behavior is well defined if/when we run out of DMA memory.
Fixes: 92744989533c ("net: add Xilinx ll_temac device driver")
Signed-off-by: Esben Haabendal <redacted>
---
drivers/net/ethernet/xilinx/ll_temac_main.c | 26 +++++++++++++++++++--
1 file changed, 24 insertions(+), 2 deletions(-)
@@ -389,12 +389,13 @@ static int temac_dma_bd_init(struct net_device *ndev)lp->tx_bd_next=0;lp->tx_bd_tail=0;lp->rx_bd_ci=0;+lp->rx_bd_tail=RX_BD_NUM-1;/* Enable RX DMA transfers */wmb();lp->dma_out(lp,RX_CURDESC_PTR,lp->rx_bd_p);lp->dma_out(lp,RX_TAILDESC_PTR,-lp->rx_bd_p+(sizeof(*lp->rx_bd_v)*(RX_BD_NUM-1)));+lp->rx_bd_p+(sizeof(*lp->rx_bd_v)*lp->rx_bd_tail));/* Prepare for TX DMA transfer */lp->dma_out(lp,TX_CURDESC_PTR,lp->tx_bd_p);
@@ -923,27 +924,41 @@ temac_start_xmit(struct sk_buff *skb, struct net_device *ndev)staticvoidll_temac_recv(structnet_device*ndev){structtemac_local*lp=netdev_priv(ndev);-structsk_buff*skb,*new_skb;-unsignedintbdstat;-structcdmac_bd*cur_p;-dma_addr_ttail_p,skb_dma_addr;-intlength;unsignedlongflags;+intrx_bd;+boolupdate_tail=false;spin_lock_irqsave(&lp->rx_lock,flags);-tail_p=lp->rx_bd_p+sizeof(*lp->rx_bd_v)*lp->rx_bd_ci;-cur_p=&lp->rx_bd_v[lp->rx_bd_ci];--bdstat=be32_to_cpu(cur_p->app0);-while((bdstat&STS_CTRL_APP0_CMPLT)){+/* Process all received buffers, passing them on network+*stack.Afterthis,thebufferdescriptorswillbeinan+*un-allocatedstage,wherenoskbisallocatedforit,and+*theyarethereforenotavailableforTEMAC/DMA.+*/+do{+structcdmac_bd*bd=&lp->rx_bd_v[lp->rx_bd_ci];+structsk_buff*skb=lp->rx_skb[lp->rx_bd_ci];+unsignedintbdstat=be32_to_cpu(bd->app0);+intlength;++/* While this should not normally happen, we can end+*herewhenGFP_ATOMICallocationsfail,andwe+*thereforehaveun-allocatedbuffers.+*/+if(!skb)+break;-skb=lp->rx_skb[lp->rx_bd_ci];-length=be32_to_cpu(cur_p->app4)&0x3FFF;+/* Loop over all completed buffer descriptors */+if(!(bdstat&STS_CTRL_APP0_CMPLT))+break;-dma_unmap_single(ndev->dev.parent,be32_to_cpu(cur_p->phys),+dma_unmap_single(ndev->dev.parent,be32_to_cpu(bd->phys),XTE_MAX_JUMBO_FRAME_SIZE,DMA_FROM_DEVICE);+/* The buffer is not valid for DMA anymore */+bd->phys=0;+bd->len=0;+length=be32_to_cpu(bd->app4)&0x3FFF;skb_put(skb,length);skb->protocol=eth_type_trans(skb,ndev);skb_checksum_none_assert(skb);
@@ -958,39 +973,74 @@ static void ll_temac_recv(struct net_device *ndev)*(back)forproperIPchecksumbyteorder*(be16).*/-skb->csum=htons(be32_to_cpu(cur_p->app3)&0xFFFF);+skb->csum=htons(be32_to_cpu(bd->app3)&0xFFFF);skb->ip_summed=CHECKSUM_COMPLETE;}if(!skb_defer_rx_timestamp(skb))netif_rx(skb);+/* The skb buffer is now owned by network stack above */+lp->rx_skb[lp->rx_bd_ci]=NULL;ndev->stats.rx_packets++;ndev->stats.rx_bytes+=length;-new_skb=netdev_alloc_skb_ip_align(ndev,-XTE_MAX_JUMBO_FRAME_SIZE);-if(!new_skb){-spin_unlock_irqrestore(&lp->rx_lock,flags);-return;+rx_bd=lp->rx_bd_ci;+if(++lp->rx_bd_ci>=RX_BD_NUM)+lp->rx_bd_ci=0;+}while(rx_bd!=lp->rx_bd_tail);++/* Allocate new buffers for those buffer descriptors that were+*passedtonetworkstack.NotethatGFP_ATOMICallocations+*canfail(e.g.whenalargerburstofGFP_ATOMIC+*allocationsoccurs),sowhilewetrytoallocateall+*buffersinthesameinterruptwheretheywereprocessed,we+*continuewithwhatwecouldgetincaseofallocation+*failure.Allocationofremainingbufferswillberetried+*infollowingcalls.+*/+while(1){+structsk_buff*skb;+structcdmac_bd*bd;+dma_addr_tskb_dma_addr;++rx_bd=lp->rx_bd_tail+1;+if(rx_bd>=RX_BD_NUM)+rx_bd=0;+bd=&lp->rx_bd_v[rx_bd];++if(bd->phys)+break;/* All skb's allocated */++skb=netdev_alloc_skb_ip_align(ndev,XTE_MAX_JUMBO_FRAME_SIZE);+if(!skb){+dev_warn(&ndev->dev,"skb alloc failed\n");+break;}-cur_p->app0=cpu_to_be32(STS_CTRL_APP0_IRQONEND);-skb_dma_addr=dma_map_single(ndev->dev.parent,new_skb->data,+skb_dma_addr=dma_map_single(ndev->dev.parent,skb->data,XTE_MAX_JUMBO_FRAME_SIZE,DMA_FROM_DEVICE);-cur_p->phys=cpu_to_be32(skb_dma_addr);-cur_p->len=cpu_to_be32(XTE_MAX_JUMBO_FRAME_SIZE);-lp->rx_skb[lp->rx_bd_ci]=new_skb;+if(WARN_ON_ONCE(dma_mapping_error(ndev->dev.parent,+skb_dma_addr))){+dev_kfree_skb_any(skb);+break;+}-lp->rx_bd_ci++;-if(lp->rx_bd_ci>=RX_BD_NUM)-lp->rx_bd_ci=0;+bd->phys=cpu_to_be32(skb_dma_addr);+bd->len=cpu_to_be32(XTE_MAX_JUMBO_FRAME_SIZE);+bd->app0=cpu_to_be32(STS_CTRL_APP0_IRQONEND);+lp->rx_skb[rx_bd]=skb;++lp->rx_bd_tail=rx_bd;+update_tail=true;+}-cur_p=&lp->rx_bd_v[lp->rx_bd_ci];-bdstat=be32_to_cpu(cur_p->app0);+/* Move tail pointer when buffers have been allocated */+if(update_tail){+lp->dma_out(lp,RX_TAILDESC_PTR,+lp->rx_bd_p+sizeof(*lp->rx_bd_v)*lp->rx_bd_tail);}-lp->dma_out(lp,RX_TAILDESC_PTR,tail_p);spin_unlock_irqrestore(&lp->rx_lock,flags);}
The SDMA engine used by TEMAC halts operation when it has finished
processing of the last buffer descriptor in the buffer ring.
Unfortunately, no interrupt event is generated when this happens,
so we need to setup another mechanism to make sure DMA operation is
restarted when enough buffers have been added to the ring.
Fixes: 92744989533c ("net: add Xilinx ll_temac device driver")
Signed-off-by: Esben Haabendal <redacted>
---
drivers/net/ethernet/xilinx/ll_temac.h | 3 ++
drivers/net/ethernet/xilinx/ll_temac_main.c | 47 ++++++++++++++++++++-
2 files changed, 48 insertions(+), 2 deletions(-)
@@ -990,6 +1002,18 @@ static void ll_temac_recv(struct net_device *ndev)lp->rx_bd_ci=0;}while(rx_bd!=lp->rx_bd_tail);+/* DMA operations will halt when the last buffer descriptor is+*processed(ie.theonepointedtobyRX_TAILDESC_PTR).+*Whenthathappens,nomoreinterrupteventswillbe+*generated.NoIRQ_COALorIRQ_DLY,andnotevenan+*IRQ_ERR.Toavoidstalling,wescheduleadelayedwork+*whenthereisapotentialriskofthathappening.Thework+*willcallthisfunction,andthusre-scheduleitselfuntil+*enoughbuffersareavailableagain.+*/+if(ll_temac_recv_buffers_available(lp)<lp->coalesce_count_rx)+schedule_delayed_work(&lp->restart_work,HZ/1000);+/* Allocate new buffers for those buffer descriptors that were*passedtonetworkstack.NotethatGFP_ATOMICallocations*canfail(e.g.whenalargerburstofGFP_ATOMIC
@@ -1045,6 +1069,18 @@ static void ll_temac_recv(struct net_device *ndev)spin_unlock_irqrestore(&lp->rx_lock,flags);}+/* Function scheduled to ensure a restart in case of DMA halt+*conditioncausedbyrunningoutofbufferdescriptors.+*/+staticvoidll_temac_restart_work_func(structwork_struct*work)+{+structtemac_local*lp=container_of(work,structtemac_local,+restart_work.work);+structnet_device*ndev=lp->ndev;++ll_temac_recv(ndev);+}+staticirqreturn_tll_temac_tx_irq(intirq,void*_ndev){structnet_device*ndev=_ndev;
@@ -1137,6 +1173,8 @@ static int temac_stop(struct net_device *ndev)dev_dbg(&ndev->dev,"temac_close()\n");+cancel_delayed_work_sync(&lp->restart_work);+free_irq(lp->tx_irq,ndev);free_irq(lp->rx_irq,ndev);
@@ -1258,6 +1296,7 @@ static int temac_probe(struct platform_device *pdev)lp->dev=&pdev->dev;lp->options=XTE_OPTION_DEFAULTS;spin_lock_init(&lp->rx_lock);+INIT_DELAYED_WORK(&lp->restart_work,ll_temac_restart_work_func);/* Setup mutex for synchronization of indirect register access */if(pdata){
@@ -1364,6 +1403,7 @@ static int temac_probe(struct platform_device *pdev)*/lp->tx_chnl_ctrl=0x10220000;lp->rx_chnl_ctrl=0xff070000;+lp->coalesce_count_rx=0x07;/* Finished with the DMA node; drop the reference */of_node_put(dma_np);
@@ -1395,11 +1435,14 @@ static int temac_probe(struct platform_device *pdev)(pdata->tx_irq_count<<16);elselp->tx_chnl_ctrl=0x10220000;-if(pdata->rx_irq_timeout||pdata->rx_irq_count)+if(pdata->rx_irq_timeout||pdata->rx_irq_count){lp->rx_chnl_ctrl=(pdata->rx_irq_timeout<<24)|(pdata->rx_irq_count<<16);-else+lp->coalesce_count_rx=pdata->rx_irq_count;+}else{lp->rx_chnl_ctrl=0xff070000;+lp->coalesce_count_rx=0x07;+}}/* Error handle returned DMA RX and TX interrupts */
Fix a number of bugs which have been present since the first commit.
The bugs fixed in patch 1,2 and 4 have all been observed in real systems, and
was relatively easy to reproduce given an appropriate stress setup.
Changes since v1:
- Changed error handling of of dma_map_single() in temac_start_xmit() to drop
packet instead of returning NETDEV_TX_BUSY.
Esben Haabendal (4):
net: ll_temac: Fix race condition causing TX hang
net: ll_temac: Add more error handling of dma_map_single() calls
net: ll_temac: Fix RX buffer descriptor handling on GFP_ATOMIC
pressure
net: ll_temac: Handle DMA halt condition caused by buffer underrun
drivers/net/ethernet/xilinx/ll_temac.h | 4 +
drivers/net/ethernet/xilinx/ll_temac_main.c | 209 ++++++++++++++++----
2 files changed, 175 insertions(+), 38 deletions(-)
--
2.25.0
It is possible that the interrupt handler fires and frees up space in
the TX ring in between checking for sufficient TX ring space and
stopping the TX queue in temac_start_xmit. If this happens, the
queue wake from the interrupt handler will occur before the queue is
stopped, causing a lost wakeup and the adapter's transmit hanging.
To avoid this, after stopping the queue, check again whether there is
sufficient space in the TX ring. If so, wake up the queue again.
This is a port of the similar fix in axienet driver,
commit 7de44285c1f6 ("net: axienet: Fix race condition causing TX hang").
Fixes: 23ecc4bde21f ("net: ll_temac: fix checksum offload logic")
Signed-off-by: Esben Haabendal <redacted>
---
drivers/net/ethernet/xilinx/ll_temac_main.c | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
@@ -830,9 +833,19 @@ temac_start_xmit(struct sk_buff *skb, struct net_device *ndev)cur_p=&lp->tx_bd_v[lp->tx_bd_tail];if(temac_check_tx_bd_space(lp,num_frag+1)){-if(!netif_queue_stopped(ndev))-netif_stop_queue(ndev);-returnNETDEV_TX_BUSY;+if(netif_queue_stopped(ndev))+returnNETDEV_TX_BUSY;++netif_stop_queue(ndev);++/* Matches barrier in temac_start_xmit_done */+smp_mb();++/* Space might have just been freed - check again */+if(temac_check_tx_bd_space(lp,num_frag))+returnNETDEV_TX_BUSY;++netif_wake_queue(ndev);}cur_p->app0=0;
This adds error handling to the remaining dma_map_single() calls, so that
behavior is well defined if/when we run out of DMA memory.
Fixes: 92744989533c ("net: add Xilinx ll_temac device driver")
Signed-off-by: Esben Haabendal <redacted>
---
drivers/net/ethernet/xilinx/ll_temac_main.c | 26 +++++++++++++++++++--
1 file changed, 24 insertions(+), 2 deletions(-)
@@ -389,12 +389,13 @@ static int temac_dma_bd_init(struct net_device *ndev)lp->tx_bd_next=0;lp->tx_bd_tail=0;lp->rx_bd_ci=0;+lp->rx_bd_tail=RX_BD_NUM-1;/* Enable RX DMA transfers */wmb();lp->dma_out(lp,RX_CURDESC_PTR,lp->rx_bd_p);lp->dma_out(lp,RX_TAILDESC_PTR,-lp->rx_bd_p+(sizeof(*lp->rx_bd_v)*(RX_BD_NUM-1)));+lp->rx_bd_p+(sizeof(*lp->rx_bd_v)*lp->rx_bd_tail));/* Prepare for TX DMA transfer */lp->dma_out(lp,TX_CURDESC_PTR,lp->tx_bd_p);
@@ -923,27 +924,41 @@ temac_start_xmit(struct sk_buff *skb, struct net_device *ndev)staticvoidll_temac_recv(structnet_device*ndev){structtemac_local*lp=netdev_priv(ndev);-structsk_buff*skb,*new_skb;-unsignedintbdstat;-structcdmac_bd*cur_p;-dma_addr_ttail_p,skb_dma_addr;-intlength;unsignedlongflags;+intrx_bd;+boolupdate_tail=false;spin_lock_irqsave(&lp->rx_lock,flags);-tail_p=lp->rx_bd_p+sizeof(*lp->rx_bd_v)*lp->rx_bd_ci;-cur_p=&lp->rx_bd_v[lp->rx_bd_ci];--bdstat=be32_to_cpu(cur_p->app0);-while((bdstat&STS_CTRL_APP0_CMPLT)){+/* Process all received buffers, passing them on network+*stack.Afterthis,thebufferdescriptorswillbeinan+*un-allocatedstage,wherenoskbisallocatedforit,and+*theyarethereforenotavailableforTEMAC/DMA.+*/+do{+structcdmac_bd*bd=&lp->rx_bd_v[lp->rx_bd_ci];+structsk_buff*skb=lp->rx_skb[lp->rx_bd_ci];+unsignedintbdstat=be32_to_cpu(bd->app0);+intlength;++/* While this should not normally happen, we can end+*herewhenGFP_ATOMICallocationsfail,andwe+*thereforehaveun-allocatedbuffers.+*/+if(!skb)+break;-skb=lp->rx_skb[lp->rx_bd_ci];-length=be32_to_cpu(cur_p->app4)&0x3FFF;+/* Loop over all completed buffer descriptors */+if(!(bdstat&STS_CTRL_APP0_CMPLT))+break;-dma_unmap_single(ndev->dev.parent,be32_to_cpu(cur_p->phys),+dma_unmap_single(ndev->dev.parent,be32_to_cpu(bd->phys),XTE_MAX_JUMBO_FRAME_SIZE,DMA_FROM_DEVICE);+/* The buffer is not valid for DMA anymore */+bd->phys=0;+bd->len=0;+length=be32_to_cpu(bd->app4)&0x3FFF;skb_put(skb,length);skb->protocol=eth_type_trans(skb,ndev);skb_checksum_none_assert(skb);
@@ -958,39 +973,74 @@ static void ll_temac_recv(struct net_device *ndev)*(back)forproperIPchecksumbyteorder*(be16).*/-skb->csum=htons(be32_to_cpu(cur_p->app3)&0xFFFF);+skb->csum=htons(be32_to_cpu(bd->app3)&0xFFFF);skb->ip_summed=CHECKSUM_COMPLETE;}if(!skb_defer_rx_timestamp(skb))netif_rx(skb);+/* The skb buffer is now owned by network stack above */+lp->rx_skb[lp->rx_bd_ci]=NULL;ndev->stats.rx_packets++;ndev->stats.rx_bytes+=length;-new_skb=netdev_alloc_skb_ip_align(ndev,-XTE_MAX_JUMBO_FRAME_SIZE);-if(!new_skb){-spin_unlock_irqrestore(&lp->rx_lock,flags);-return;+rx_bd=lp->rx_bd_ci;+if(++lp->rx_bd_ci>=RX_BD_NUM)+lp->rx_bd_ci=0;+}while(rx_bd!=lp->rx_bd_tail);++/* Allocate new buffers for those buffer descriptors that were+*passedtonetworkstack.NotethatGFP_ATOMICallocations+*canfail(e.g.whenalargerburstofGFP_ATOMIC+*allocationsoccurs),sowhilewetrytoallocateall+*buffersinthesameinterruptwheretheywereprocessed,we+*continuewithwhatwecouldgetincaseofallocation+*failure.Allocationofremainingbufferswillberetried+*infollowingcalls.+*/+while(1){+structsk_buff*skb;+structcdmac_bd*bd;+dma_addr_tskb_dma_addr;++rx_bd=lp->rx_bd_tail+1;+if(rx_bd>=RX_BD_NUM)+rx_bd=0;+bd=&lp->rx_bd_v[rx_bd];++if(bd->phys)+break;/* All skb's allocated */++skb=netdev_alloc_skb_ip_align(ndev,XTE_MAX_JUMBO_FRAME_SIZE);+if(!skb){+dev_warn(&ndev->dev,"skb alloc failed\n");+break;}-cur_p->app0=cpu_to_be32(STS_CTRL_APP0_IRQONEND);-skb_dma_addr=dma_map_single(ndev->dev.parent,new_skb->data,+skb_dma_addr=dma_map_single(ndev->dev.parent,skb->data,XTE_MAX_JUMBO_FRAME_SIZE,DMA_FROM_DEVICE);-cur_p->phys=cpu_to_be32(skb_dma_addr);-cur_p->len=cpu_to_be32(XTE_MAX_JUMBO_FRAME_SIZE);-lp->rx_skb[lp->rx_bd_ci]=new_skb;+if(WARN_ON_ONCE(dma_mapping_error(ndev->dev.parent,+skb_dma_addr))){+dev_kfree_skb_any(skb);+break;+}-lp->rx_bd_ci++;-if(lp->rx_bd_ci>=RX_BD_NUM)-lp->rx_bd_ci=0;+bd->phys=cpu_to_be32(skb_dma_addr);+bd->len=cpu_to_be32(XTE_MAX_JUMBO_FRAME_SIZE);+bd->app0=cpu_to_be32(STS_CTRL_APP0_IRQONEND);+lp->rx_skb[rx_bd]=skb;++lp->rx_bd_tail=rx_bd;+update_tail=true;+}-cur_p=&lp->rx_bd_v[lp->rx_bd_ci];-bdstat=be32_to_cpu(cur_p->app0);+/* Move tail pointer when buffers have been allocated */+if(update_tail){+lp->dma_out(lp,RX_TAILDESC_PTR,+lp->rx_bd_p+sizeof(*lp->rx_bd_v)*lp->rx_bd_tail);}-lp->dma_out(lp,RX_TAILDESC_PTR,tail_p);spin_unlock_irqrestore(&lp->rx_lock,flags);}
The SDMA engine used by TEMAC halts operation when it has finished
processing of the last buffer descriptor in the buffer ring.
Unfortunately, no interrupt event is generated when this happens,
so we need to setup another mechanism to make sure DMA operation is
restarted when enough buffers have been added to the ring.
Fixes: 92744989533c ("net: add Xilinx ll_temac device driver")
Signed-off-by: Esben Haabendal <redacted>
---
drivers/net/ethernet/xilinx/ll_temac.h | 3 ++
drivers/net/ethernet/xilinx/ll_temac_main.c | 58 +++++++++++++++++++--
2 files changed, 56 insertions(+), 5 deletions(-)
@@ -990,6 +1007,18 @@ static void ll_temac_recv(struct net_device *ndev)lp->rx_bd_ci=0;}while(rx_bd!=lp->rx_bd_tail);+/* DMA operations will halt when the last buffer descriptor is+*processed(ie.theonepointedtobyRX_TAILDESC_PTR).+*Whenthathappens,nomoreinterrupteventswillbe+*generated.NoIRQ_COALorIRQ_DLY,andnotevenan+*IRQ_ERR.Toavoidstalling,wescheduleadelayedwork+*whenthereisapotentialriskofthathappening.Thework+*willcallthisfunction,andthusre-scheduleitselfuntil+*enoughbuffersareavailableagain.+*/+if(ll_temac_recv_buffers_available(lp)<lp->coalesce_count_rx)+schedule_delayed_work(&lp->restart_work,HZ/1000);+/* Allocate new buffers for those buffer descriptors that were*passedtonetworkstack.NotethatGFP_ATOMICallocations*canfail(e.g.whenalargerburstofGFP_ATOMIC
@@ -1045,6 +1074,18 @@ static void ll_temac_recv(struct net_device *ndev)spin_unlock_irqrestore(&lp->rx_lock,flags);}+/* Function scheduled to ensure a restart in case of DMA halt+*conditioncausedbyrunningoutofbufferdescriptors.+*/+staticvoidll_temac_restart_work_func(structwork_struct*work)+{+structtemac_local*lp=container_of(work,structtemac_local,+restart_work.work);+structnet_device*ndev=lp->ndev;++ll_temac_recv(ndev);+}+staticirqreturn_tll_temac_tx_irq(intirq,void*_ndev){structnet_device*ndev=_ndev;
@@ -1137,6 +1178,8 @@ static int temac_stop(struct net_device *ndev)dev_dbg(&ndev->dev,"temac_close()\n");+cancel_delayed_work_sync(&lp->restart_work);+free_irq(lp->tx_irq,ndev);free_irq(lp->rx_irq,ndev);
@@ -1258,6 +1301,7 @@ static int temac_probe(struct platform_device *pdev)lp->dev=&pdev->dev;lp->options=XTE_OPTION_DEFAULTS;spin_lock_init(&lp->rx_lock);+INIT_DELAYED_WORK(&lp->restart_work,ll_temac_restart_work_func);/* Setup mutex for synchronization of indirect register access */if(pdata){
@@ -1364,6 +1408,7 @@ static int temac_probe(struct platform_device *pdev)*/lp->tx_chnl_ctrl=0x10220000;lp->rx_chnl_ctrl=0xff070000;+lp->coalesce_count_rx=0x07;/* Finished with the DMA node; drop the reference */of_node_put(dma_np);
@@ -1395,11 +1440,14 @@ static int temac_probe(struct platform_device *pdev)(pdata->tx_irq_count<<16);elselp->tx_chnl_ctrl=0x10220000;-if(pdata->rx_irq_timeout||pdata->rx_irq_count)+if(pdata->rx_irq_timeout||pdata->rx_irq_count){lp->rx_chnl_ctrl=(pdata->rx_irq_timeout<<24)|(pdata->rx_irq_count<<16);-else+lp->coalesce_count_rx=pdata->rx_irq_count;+}else{lp->rx_chnl_ctrl=0xff070000;+lp->coalesce_count_rx=0x07;+}}/* Error handle returned DMA RX and TX interrupts */
Fix a number of bugs which have been present since the first commit.
The bugs fixed in patch 1,2 and 4 have all been observed in real systems, and
was relatively easy to reproduce given an appropriate stress setup.
Changes since v1:
- Changed error handling of of dma_map_single() in temac_start_xmit() to drop
packet instead of returning NETDEV_TX_BUSY.