diff options
Diffstat (limited to 'drivers/s390/kvm/kvm_virtio.c')
-rw-r--r-- | drivers/s390/kvm/kvm_virtio.c | 79 |
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 | */ |
34 | static void *kvm_devices; | 35 | static void *kvm_devices; |
36 | struct work_struct hotplug_work; | ||
35 | 37 | ||
36 | struct kvm_device { | 38 | struct 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 | */ | ||
335 | static 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 | */ | ||
346 | static 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 | */ |
333 | static void kvm_extint_handler(u16 code) | 376 | static 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(); |