aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/sun
diff options
context:
space:
mode:
authorSowmini Varadhan <sowmini.varadhan@oracle.com>2014-08-13 10:29:41 -0400
committerDavid S. Miller <davem@davemloft.net>2014-08-13 23:04:46 -0400
commit1d311ad2f9fe5d75cf228372174170d0f435e3ef (patch)
treee1ceb11d8f5ea8602b970baac8475735dd88eda9 /drivers/net/ethernet/sun
parentadddc32d6fde82156c779997f928865100542e55 (diff)
sunvnet: Schedule maybe_tx_wakeup() as a tasklet from ldc_rx path
At the tail of vnet_event(), if we hit the maybe_tx_wakeup() condition, we try to take the netif_tx_lock() in the recv-interrupt-context and can deadlock with dev_watchdog(). vnet_event() should schedule maybe_tx_wakeup() as a tasklet to avoid this deadlock Signed-off-by: Sowmini Varadhan <sowmini.varadhan@oracle.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/sun')
-rw-r--r--drivers/net/ethernet/sun/sunvnet.c12
-rw-r--r--drivers/net/ethernet/sun/sunvnet.h4
2 files changed, 14 insertions, 2 deletions
diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c
index a5871791e452..23c89ab5a6ad 100644
--- a/drivers/net/ethernet/sun/sunvnet.c
+++ b/drivers/net/ethernet/sun/sunvnet.c
@@ -488,8 +488,9 @@ static int handle_mcast(struct vnet_port *port, void *msgbuf)
488 return 0; 488 return 0;
489} 489}
490 490
491static void maybe_tx_wakeup(struct vnet *vp) 491static void maybe_tx_wakeup(unsigned long param)
492{ 492{
493 struct vnet *vp = (struct vnet *)param;
493 struct net_device *dev = vp->dev; 494 struct net_device *dev = vp->dev;
494 495
495 netif_tx_lock(dev); 496 netif_tx_lock(dev);
@@ -586,8 +587,13 @@ static void vnet_event(void *arg, int event)
586 break; 587 break;
587 } 588 }
588 spin_unlock(&vio->lock); 589 spin_unlock(&vio->lock);
590 /* Kick off a tasklet to wake the queue. We cannot call
591 * maybe_tx_wakeup directly here because we could deadlock on
592 * netif_tx_lock() with dev_watchdog()
593 */
589 if (unlikely(tx_wakeup && err != -ECONNRESET)) 594 if (unlikely(tx_wakeup && err != -ECONNRESET))
590 maybe_tx_wakeup(port->vp); 595 tasklet_schedule(&port->vp->vnet_tx_wakeup);
596
591 local_irq_restore(flags); 597 local_irq_restore(flags);
592} 598}
593 599
@@ -1070,6 +1076,7 @@ static struct vnet *vnet_new(const u64 *local_mac)
1070 vp = netdev_priv(dev); 1076 vp = netdev_priv(dev);
1071 1077
1072 spin_lock_init(&vp->lock); 1078 spin_lock_init(&vp->lock);
1079 tasklet_init(&vp->vnet_tx_wakeup, maybe_tx_wakeup, (unsigned long)vp);
1073 vp->dev = dev; 1080 vp->dev = dev;
1074 1081
1075 INIT_LIST_HEAD(&vp->port_list); 1082 INIT_LIST_HEAD(&vp->port_list);
@@ -1129,6 +1136,7 @@ static void vnet_cleanup(void)
1129 vp = list_first_entry(&vnet_list, struct vnet, list); 1136 vp = list_first_entry(&vnet_list, struct vnet, list);
1130 list_del(&vp->list); 1137 list_del(&vp->list);
1131 dev = vp->dev; 1138 dev = vp->dev;
1139 tasklet_kill(&vp->vnet_tx_wakeup);
1132 /* vio_unregister_driver() should have cleaned up port_list */ 1140 /* vio_unregister_driver() should have cleaned up port_list */
1133 BUG_ON(!list_empty(&vp->port_list)); 1141 BUG_ON(!list_empty(&vp->port_list));
1134 unregister_netdev(dev); 1142 unregister_netdev(dev);
diff --git a/drivers/net/ethernet/sun/sunvnet.h b/drivers/net/ethernet/sun/sunvnet.h
index d347a5bf24b0..de5c2c64996f 100644
--- a/drivers/net/ethernet/sun/sunvnet.h
+++ b/drivers/net/ethernet/sun/sunvnet.h
@@ -1,6 +1,8 @@
1#ifndef _SUNVNET_H 1#ifndef _SUNVNET_H
2#define _SUNVNET_H 2#define _SUNVNET_H
3 3
4#include <linux/interrupt.h>
5
4#define DESC_NCOOKIES(entry_size) \ 6#define DESC_NCOOKIES(entry_size) \
5 ((entry_size) - sizeof(struct vio_net_desc)) 7 ((entry_size) - sizeof(struct vio_net_desc))
6 8
@@ -78,6 +80,8 @@ struct vnet {
78 80
79 struct list_head list; 81 struct list_head list;
80 u64 local_mac; 82 u64 local_mac;
83
84 struct tasklet_struct vnet_tx_wakeup;
81}; 85};
82 86
83#endif /* _SUNVNET_H */ 87#endif /* _SUNVNET_H */