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