sunvnet - add missing rmb() for sunvnet driver
[firefly-linux-kernel-4.4.55.git] / drivers / net / ethernet / sun / sunvnet.c
index a5871791e45243e389a9a84529c72b47ad0fec50..f67539650c380010eced17ea5ab880ec3dc0e328 100644 (file)
@@ -350,14 +350,17 @@ static int vnet_walk_rx_one(struct vnet_port *port,
        if (IS_ERR(desc))
                return PTR_ERR(desc);
 
+       if (desc->hdr.state != VIO_DESC_READY)
+               return 1;
+
+       rmb();
+
        viodbg(DATA, "vio_walk_rx_one desc[%02x:%02x:%08x:%08x:%llx:%llx]\n",
               desc->hdr.state, desc->hdr.ack,
               desc->size, desc->ncookies,
               desc->cookies[0].cookie_addr,
               desc->cookies[0].cookie_size);
 
-       if (desc->hdr.state != VIO_DESC_READY)
-               return 1;
        err = vnet_rx_one(port, desc->size, desc->cookies, desc->ncookies);
        if (err == -ECONNRESET)
                return err;
@@ -488,8 +491,9 @@ static int handle_mcast(struct vnet_port *port, void *msgbuf)
        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);
@@ -586,8 +590,13 @@ static void vnet_event(void *arg, int event)
                        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);
 }
 
@@ -1070,6 +1079,7 @@ static struct vnet *vnet_new(const u64 *local_mac)
        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);
@@ -1129,6 +1139,7 @@ static void vnet_cleanup(void)
                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);