Merge branch 'kvm-updates/2.6.37' of git://git.kernel.org/pub/scm/virt/kvm/kvm
[pandora-kernel.git] / drivers / s390 / kvm / kvm_virtio.c
index 4e298bc..5a46b8c 100644 (file)
@@ -32,6 +32,7 @@
  * The pointer to our (page) of device descriptions.
  */
 static void *kvm_devices;
+struct work_struct hotplug_work;
 
 struct kvm_device {
        struct virtio_device vdev;
@@ -327,6 +328,47 @@ static void scan_devices(void)
        }
 }
 
+/*
+ * match for a kvm device with a specific desc pointer
+ */
+static int match_desc(struct device *dev, void *data)
+{
+       if ((ulong)to_kvmdev(dev_to_virtio(dev))->desc == (ulong)data)
+               return 1;
+
+       return 0;
+}
+
+/*
+ * hotplug_device tries to find changes in the device page.
+ */
+static void hotplug_devices(struct work_struct *dummy)
+{
+       unsigned int i;
+       struct kvm_device_desc *d;
+       struct device *dev;
+
+       for (i = 0; i < PAGE_SIZE; i += desc_size(d)) {
+               d = kvm_devices + i;
+
+               /* end of list */
+               if (d->type == 0)
+                       break;
+
+               /* device already exists */
+               dev = device_find_child(kvm_root, d, match_desc);
+               if (dev) {
+                       /* XXX check for hotplug remove */
+                       put_device(dev);
+                       continue;
+               }
+
+               /* new device */
+               printk(KERN_INFO "Adding new virtio device %p\n", d);
+               add_kvm_device(d, i);
+       }
+}
+
 /*
  * we emulate the request_irq behaviour on top of s390 extints
  */
@@ -334,7 +376,7 @@ static void kvm_extint_handler(u16 code)
 {
        struct virtqueue *vq;
        u16 subcode;
-       int config_changed;
+       u32 param;
 
        subcode = S390_lowcore.cpu_addr;
        if ((subcode & 0xff00) != VIRTIO_SUBCODE_64)
@@ -343,18 +385,28 @@ static void kvm_extint_handler(u16 code)
        /* The LSB might be overloaded, we have to mask it */
        vq = (struct virtqueue *)(S390_lowcore.ext_params2 & ~1UL);
 
-       /* We use the LSB of extparam, to decide, if this interrupt is a config
-        * change or a "standard" interrupt */
-       config_changed = S390_lowcore.ext_params & 1;
+       /* We use ext_params to decide what this interrupt means */
+       param = S390_lowcore.ext_params & VIRTIO_PARAM_MASK;
 
-       if (config_changed) {
+       switch (param) {
+       case VIRTIO_PARAM_CONFIG_CHANGED:
+       {
                struct virtio_driver *drv;
                drv = container_of(vq->vdev->dev.driver,
                                   struct virtio_driver, driver);
                if (drv->config_changed)
                        drv->config_changed(vq->vdev);
-       } else
+
+               break;
+       }
+       case VIRTIO_PARAM_DEV_ADD:
+               schedule_work(&hotplug_work);
+               break;
+       case VIRTIO_PARAM_VRING_INTERRUPT:
+       default:
                vring_interrupt(0, vq);
+               break;
+       }
 }
 
 /*
@@ -383,6 +435,8 @@ static int __init kvm_devices_init(void)
 
        kvm_devices = (void *) real_memory_size;
 
+       INIT_WORK(&hotplug_work, hotplug_devices);
+
        ctl_set_bit(0, 9);
        register_external_interrupt(0x2603, kvm_extint_handler);