return 0;
 }
 
-static void maybe_tx_wakeup(struct vnet *vp)
+static void maybe_tx_wakeup(unsigned long param)
 {
+       struct vnet *vp = (struct vnet *)param;
        struct net_device *dev = vp->dev;
 
        netif_tx_lock(dev);
                        break;
        }
        spin_unlock(&vio->lock);
+       /* Kick off a tasklet to wake the queue.  We cannot call
+        * maybe_tx_wakeup directly here because we could deadlock on
+        * netif_tx_lock() with dev_watchdog()
+        */
        if (unlikely(tx_wakeup && err != -ECONNRESET))
-               maybe_tx_wakeup(port->vp);
+               tasklet_schedule(&port->vp->vnet_tx_wakeup);
+
        local_irq_restore(flags);
 }
 
        vp = netdev_priv(dev);
 
        spin_lock_init(&vp->lock);
+       tasklet_init(&vp->vnet_tx_wakeup, maybe_tx_wakeup, (unsigned long)vp);
        vp->dev = dev;
 
        INIT_LIST_HEAD(&vp->port_list);
                vp = list_first_entry(&vnet_list, struct vnet, list);
                list_del(&vp->list);
                dev = vp->dev;
+               tasklet_kill(&vp->vnet_tx_wakeup);
                /* vio_unregister_driver() should have cleaned up port_list */
                BUG_ON(!list_empty(&vp->port_list));
                unregister_netdev(dev);
 
 #ifndef _SUNVNET_H
 #define _SUNVNET_H
 
+#include <linux/interrupt.h>
+
 #define DESC_NCOOKIES(entry_size)      \
        ((entry_size) - sizeof(struct vio_net_desc))
 
 
        struct list_head        list;
        u64                     local_mac;
+
+       struct tasklet_struct   vnet_tx_wakeup;
 };
 
 #endif /* _SUNVNET_H */