aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390')
-rw-r--r--drivers/s390/kvm/kvm_virtio.c66
1 files changed, 60 insertions, 6 deletions
diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c
index 4e298bc8949d..5a46b8c5d68a 100644
--- a/drivers/s390/kvm/kvm_virtio.c
+++ b/drivers/s390/kvm/kvm_virtio.c
@@ -32,6 +32,7 @@
32 * The pointer to our (page) of device descriptions. 32 * The pointer to our (page) of device descriptions.
33 */ 33 */
34static void *kvm_devices; 34static void *kvm_devices;
35struct work_struct hotplug_work;
35 36
36struct kvm_device { 37struct kvm_device {
37 struct virtio_device vdev; 38 struct virtio_device vdev;
@@ -328,13 +329,54 @@ static void scan_devices(void)
328} 329}
329 330
330/* 331/*
332 * match for a kvm device with a specific desc pointer
333 */
334static int match_desc(struct device *dev, void *data)
335{
336 if ((ulong)to_kvmdev(dev_to_virtio(dev))->desc == (ulong)data)
337 return 1;
338
339 return 0;
340}
341
342/*
343 * hotplug_device tries to find changes in the device page.
344 */
345static void hotplug_devices(struct work_struct *dummy)
346{
347 unsigned int i;
348 struct kvm_device_desc *d;
349 struct device *dev;
350
351 for (i = 0; i < PAGE_SIZE; i += desc_size(d)) {
352 d = kvm_devices + i;
353
354 /* end of list */
355 if (d->type == 0)
356 break;
357
358 /* device already exists */
359 dev = device_find_child(kvm_root, d, match_desc);
360 if (dev) {
361 /* XXX check for hotplug remove */
362 put_device(dev);
363 continue;
364 }
365
366 /* new device */
367 printk(KERN_INFO "Adding new virtio device %p\n", d);
368 add_kvm_device(d, i);
369 }
370}
371
372/*
331 * we emulate the request_irq behaviour on top of s390 extints 373 * we emulate the request_irq behaviour on top of s390 extints
332 */ 374 */
333static void kvm_extint_handler(u16 code) 375static void kvm_extint_handler(u16 code)
334{ 376{
335 struct virtqueue *vq; 377 struct virtqueue *vq;
336 u16 subcode; 378 u16 subcode;
337 int config_changed; 379 u32 param;
338 380
339 subcode = S390_lowcore.cpu_addr; 381 subcode = S390_lowcore.cpu_addr;
340 if ((subcode & 0xff00) != VIRTIO_SUBCODE_64) 382 if ((subcode & 0xff00) != VIRTIO_SUBCODE_64)
@@ -343,18 +385,28 @@ static void kvm_extint_handler(u16 code)
343 /* The LSB might be overloaded, we have to mask it */ 385 /* The LSB might be overloaded, we have to mask it */
344 vq = (struct virtqueue *)(S390_lowcore.ext_params2 & ~1UL); 386 vq = (struct virtqueue *)(S390_lowcore.ext_params2 & ~1UL);
345 387
346 /* We use the LSB of extparam, to decide, if this interrupt is a config 388 /* We use ext_params to decide what this interrupt means */
347 * change or a "standard" interrupt */ 389 param = S390_lowcore.ext_params & VIRTIO_PARAM_MASK;
348 config_changed = S390_lowcore.ext_params & 1;
349 390
350 if (config_changed) { 391 switch (param) {
392 case VIRTIO_PARAM_CONFIG_CHANGED:
393 {
351 struct virtio_driver *drv; 394 struct virtio_driver *drv;
352 drv = container_of(vq->vdev->dev.driver, 395 drv = container_of(vq->vdev->dev.driver,
353 struct virtio_driver, driver); 396 struct virtio_driver, driver);
354 if (drv->config_changed) 397 if (drv->config_changed)
355 drv->config_changed(vq->vdev); 398 drv->config_changed(vq->vdev);
356 } else 399
400 break;
401 }
402 case VIRTIO_PARAM_DEV_ADD:
403 schedule_work(&hotplug_work);
404 break;
405 case VIRTIO_PARAM_VRING_INTERRUPT:
406 default:
357 vring_interrupt(0, vq); 407 vring_interrupt(0, vq);
408 break;
409 }
358} 410}
359 411
360/* 412/*
@@ -383,6 +435,8 @@ static int __init kvm_devices_init(void)
383 435
384 kvm_devices = (void *) real_memory_size; 436 kvm_devices = (void *) real_memory_size;
385 437
438 INIT_WORK(&hotplug_work, hotplug_devices);
439
386 ctl_set_bit(0, 9); 440 ctl_set_bit(0, 9);
387 register_external_interrupt(0x2603, kvm_extint_handler); 441 register_external_interrupt(0x2603, kvm_extint_handler);
388 442