virtio: fix race in enable_cb
[sfrench/cifs-2.6.git] / drivers / net / virtio_net.c
index 19fd4cb0ddf8583cbd9235b6085a7ad1100dfa19..b58472cf76f862c16fa861b02bcb494008c00f90 100644 (file)
@@ -203,8 +203,11 @@ again:
        if (received < budget) {
                netif_rx_complete(vi->dev, napi);
                if (unlikely(!vi->rvq->vq_ops->enable_cb(vi->rvq))
-                   && netif_rx_reschedule(vi->dev, napi))
+                   && napi_schedule_prep(napi)) {
+                       vi->rvq->vq_ops->disable_cb(vi->rvq);
+                       __netif_rx_schedule(vi->dev, napi);
                        goto again;
+               }
        }
 
        return received;
@@ -278,10 +281,11 @@ again:
                pr_debug("%s: virtio not prepared to send\n", dev->name);
                netif_stop_queue(dev);
 
-               /* Activate callback for using skbs: if this fails it
+               /* Activate callback for using skbs: if this returns false it
                 * means some were used in the meantime. */
                if (unlikely(!vi->svq->vq_ops->enable_cb(vi->svq))) {
-                       printk("Unlikely: restart svq failed\n");
+                       printk("Unlikely: restart svq race\n");
+                       vi->svq->vq_ops->disable_cb(vi->svq);
                        netif_start_queue(dev);
                        goto again;
                }
@@ -294,6 +298,15 @@ again:
        return 0;
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void virtnet_netpoll(struct net_device *dev)
+{
+       struct virtnet_info *vi = netdev_priv(dev);
+
+       napi_schedule(&vi->napi);
+}
+#endif
+
 static int virtnet_open(struct net_device *dev)
 {
        struct virtnet_info *vi = netdev_priv(dev);
@@ -336,6 +349,9 @@ static int virtnet_probe(struct virtio_device *vdev)
        dev->stop = virtnet_close;
        dev->hard_start_xmit = start_xmit;
        dev->features = NETIF_F_HIGHDMA;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       dev->poll_controller = virtnet_netpoll;
+#endif
        SET_NETDEV_DEV(dev, &vdev->dev);
 
        /* Do we support "hardware" checksums? */