xen-netback: add control ring boilerplate
[cascardo/linux.git] / drivers / net / xen-netback / interface.c
index f5231a2..78a10d2 100644 (file)
@@ -128,6 +128,15 @@ irqreturn_t xenvif_interrupt(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+irqreturn_t xenvif_ctrl_interrupt(int irq, void *dev_id)
+{
+       struct xenvif *vif = dev_id;
+
+       wake_up(&vif->ctrl_wq);
+
+       return IRQ_HANDLED;
+}
+
 int xenvif_queue_stopped(struct xenvif_queue *queue)
 {
        struct net_device *dev = queue->vif->dev;
@@ -527,9 +536,66 @@ void xenvif_carrier_on(struct xenvif *vif)
        rtnl_unlock();
 }
 
-int xenvif_connect(struct xenvif_queue *queue, unsigned long tx_ring_ref,
-                  unsigned long rx_ring_ref, unsigned int tx_evtchn,
-                  unsigned int rx_evtchn)
+int xenvif_connect_ctrl(struct xenvif *vif, grant_ref_t ring_ref,
+                       unsigned int evtchn)
+{
+       struct net_device *dev = vif->dev;
+       void *addr;
+       struct xen_netif_ctrl_sring *shared;
+       struct task_struct *task;
+       int err = -ENOMEM;
+
+       err = xenbus_map_ring_valloc(xenvif_to_xenbus_device(vif),
+                                    &ring_ref, 1, &addr);
+       if (err)
+               goto err;
+
+       shared = (struct xen_netif_ctrl_sring *)addr;
+       BACK_RING_INIT(&vif->ctrl, shared, XEN_PAGE_SIZE);
+
+       init_waitqueue_head(&vif->ctrl_wq);
+
+       err = bind_interdomain_evtchn_to_irqhandler(vif->domid, evtchn,
+                                                   xenvif_ctrl_interrupt,
+                                                   0, dev->name, vif);
+       if (err < 0)
+               goto err_unmap;
+
+       vif->ctrl_irq = err;
+
+       task = kthread_create(xenvif_ctrl_kthread, (void *)vif,
+                             "%s-control", dev->name);
+       if (IS_ERR(task)) {
+               pr_warn("Could not allocate kthread for %s\n", dev->name);
+               err = PTR_ERR(task);
+               goto err_deinit;
+       }
+
+       get_task_struct(task);
+       vif->ctrl_task = task;
+
+       wake_up_process(vif->ctrl_task);
+
+       return 0;
+
+err_deinit:
+       unbind_from_irqhandler(vif->ctrl_irq, vif);
+       vif->ctrl_irq = 0;
+
+err_unmap:
+       xenbus_unmap_ring_vfree(xenvif_to_xenbus_device(vif),
+                               vif->ctrl.sring);
+       vif->ctrl.sring = NULL;
+
+err:
+       return err;
+}
+
+int xenvif_connect_data(struct xenvif_queue *queue,
+                       unsigned long tx_ring_ref,
+                       unsigned long rx_ring_ref,
+                       unsigned int tx_evtchn,
+                       unsigned int rx_evtchn)
 {
        struct task_struct *task;
        int err = -ENOMEM;
@@ -538,7 +604,8 @@ int xenvif_connect(struct xenvif_queue *queue, unsigned long tx_ring_ref,
        BUG_ON(queue->task);
        BUG_ON(queue->dealloc_task);
 
-       err = xenvif_map_frontend_rings(queue, tx_ring_ref, rx_ring_ref);
+       err = xenvif_map_frontend_data_rings(queue, tx_ring_ref,
+                                            rx_ring_ref);
        if (err < 0)
                goto err;
 
@@ -614,7 +681,7 @@ err_tx_unbind:
        unbind_from_irqhandler(queue->tx_irq, queue);
        queue->tx_irq = 0;
 err_unmap:
-       xenvif_unmap_frontend_rings(queue);
+       xenvif_unmap_frontend_data_rings(queue);
        netif_napi_del(&queue->napi);
 err:
        module_put(THIS_MODULE);
@@ -634,7 +701,7 @@ void xenvif_carrier_off(struct xenvif *vif)
        rtnl_unlock();
 }
 
-void xenvif_disconnect(struct xenvif *vif)
+void xenvif_disconnect_data(struct xenvif *vif)
 {
        struct xenvif_queue *queue = NULL;
        unsigned int num_queues = vif->num_queues;
@@ -668,12 +735,32 @@ void xenvif_disconnect(struct xenvif *vif)
                        queue->tx_irq = 0;
                }
 
-               xenvif_unmap_frontend_rings(queue);
+               xenvif_unmap_frontend_data_rings(queue);
        }
 
        xenvif_mcast_addr_list_free(vif);
 }
 
+void xenvif_disconnect_ctrl(struct xenvif *vif)
+{
+       if (vif->ctrl_task) {
+               kthread_stop(vif->ctrl_task);
+               put_task_struct(vif->ctrl_task);
+               vif->ctrl_task = NULL;
+       }
+
+       if (vif->ctrl_irq) {
+               unbind_from_irqhandler(vif->ctrl_irq, vif);
+               vif->ctrl_irq = 0;
+       }
+
+       if (vif->ctrl.sring) {
+               xenbus_unmap_ring_vfree(xenvif_to_xenbus_device(vif),
+                                       vif->ctrl.sring);
+               vif->ctrl.sring = NULL;
+       }
+}
+
 /* Reverse the relevant parts of xenvif_init_queue().
  * Used for queue teardown from xenvif_free(), and on the
  * error handling paths in xenbus.c:connect().