diff options
| author | Sasha Levin <levinsasha928@gmail.com> | 2011-07-27 09:00:48 -0400 |
|---|---|---|
| committer | Avi Kivity <avi@redhat.com> | 2011-09-25 12:17:59 -0400 |
| commit | 743eeb0b01d2fbf4154bf87bff1ebb6fb18aeb7a (patch) | |
| tree | 5392464930f7e77131d65f32ba96ce4665307629 | |
| parent | 0d460ffc0956d2dbe12ca9f5f6aa0f8701ea9d73 (diff) | |
KVM: Intelligent device lookup on I/O bus
Currently the method of dealing with an IO operation on a bus (PIO/MMIO)
is to call the read or write callback for each device registered
on the bus until we find a device which handles it.
Since the number of devices on a bus can be significant due to ioeventfds
and coalesced MMIO zones, this leads to a lot of overhead on each IO
operation.
Instead of registering devices, we now register ranges which points to
a device. Lookup is done using an efficient bsearch instead of a linear
search.
Performance test was conducted by comparing exit count per second with
200 ioeventfds created on one byte and the guest is trying to access a
different byte continuously (triggering usermode exits).
Before the patch the guest has achieved 259k exits per second, after the
patch the guest does 274k exits per second.
Cc: Avi Kivity <avi@redhat.com>
Cc: Marcelo Tosatti <mtosatti@redhat.com>
Signed-off-by: Sasha Levin <levinsasha928@gmail.com>
Signed-off-by: Avi Kivity <avi@redhat.com>
| -rw-r--r-- | arch/x86/kvm/i8254.c | 6 | ||||
| -rw-r--r-- | arch/x86/kvm/i8259.c | 108 | ||||
| -rw-r--r-- | arch/x86/kvm/irq.h | 4 | ||||
| -rw-r--r-- | arch/x86/kvm/x86.c | 6 | ||||
| -rw-r--r-- | include/linux/kvm_host.h | 18 | ||||
| -rw-r--r-- | virt/kvm/coalesced_mmio.c | 3 | ||||
| -rw-r--r-- | virt/kvm/eventfd.c | 3 | ||||
| -rw-r--r-- | virt/kvm/ioapic.c | 3 | ||||
| -rw-r--r-- | virt/kvm/kvm_main.c | 112 |
9 files changed, 216 insertions, 47 deletions
diff --git a/arch/x86/kvm/i8254.c b/arch/x86/kvm/i8254.c index efad72385058..76e3f1cd0369 100644 --- a/arch/x86/kvm/i8254.c +++ b/arch/x86/kvm/i8254.c | |||
| @@ -713,14 +713,16 @@ struct kvm_pit *kvm_create_pit(struct kvm *kvm, u32 flags) | |||
| 713 | kvm_register_irq_mask_notifier(kvm, 0, &pit->mask_notifier); | 713 | kvm_register_irq_mask_notifier(kvm, 0, &pit->mask_notifier); |
| 714 | 714 | ||
| 715 | kvm_iodevice_init(&pit->dev, &pit_dev_ops); | 715 | kvm_iodevice_init(&pit->dev, &pit_dev_ops); |
| 716 | ret = kvm_io_bus_register_dev(kvm, KVM_PIO_BUS, &pit->dev); | 716 | ret = kvm_io_bus_register_dev(kvm, KVM_PIO_BUS, KVM_PIT_BASE_ADDRESS, |
| 717 | KVM_PIT_MEM_LENGTH, &pit->dev); | ||
| 717 | if (ret < 0) | 718 | if (ret < 0) |
| 718 | goto fail; | 719 | goto fail; |
| 719 | 720 | ||
| 720 | if (flags & KVM_PIT_SPEAKER_DUMMY) { | 721 | if (flags & KVM_PIT_SPEAKER_DUMMY) { |
| 721 | kvm_iodevice_init(&pit->speaker_dev, &speaker_dev_ops); | 722 | kvm_iodevice_init(&pit->speaker_dev, &speaker_dev_ops); |
| 722 | ret = kvm_io_bus_register_dev(kvm, KVM_PIO_BUS, | 723 | ret = kvm_io_bus_register_dev(kvm, KVM_PIO_BUS, |
| 723 | &pit->speaker_dev); | 724 | KVM_SPEAKER_BASE_ADDRESS, 4, |
| 725 | &pit->speaker_dev); | ||
| 724 | if (ret < 0) | 726 | if (ret < 0) |
| 725 | goto fail_unregister; | 727 | goto fail_unregister; |
| 726 | } | 728 | } |
diff --git a/arch/x86/kvm/i8259.c b/arch/x86/kvm/i8259.c index 19fe855e7953..6b869ce0cc19 100644 --- a/arch/x86/kvm/i8259.c +++ b/arch/x86/kvm/i8259.c | |||
| @@ -459,15 +459,9 @@ static int picdev_in_range(gpa_t addr) | |||
| 459 | } | 459 | } |
| 460 | } | 460 | } |
| 461 | 461 | ||
| 462 | static inline struct kvm_pic *to_pic(struct kvm_io_device *dev) | 462 | static int picdev_write(struct kvm_pic *s, |
| 463 | { | ||
| 464 | return container_of(dev, struct kvm_pic, dev); | ||
| 465 | } | ||
| 466 | |||
| 467 | static int picdev_write(struct kvm_io_device *this, | ||
| 468 | gpa_t addr, int len, const void *val) | 463 | gpa_t addr, int len, const void *val) |
| 469 | { | 464 | { |
| 470 | struct kvm_pic *s = to_pic(this); | ||
| 471 | unsigned char data = *(unsigned char *)val; | 465 | unsigned char data = *(unsigned char *)val; |
| 472 | if (!picdev_in_range(addr)) | 466 | if (!picdev_in_range(addr)) |
| 473 | return -EOPNOTSUPP; | 467 | return -EOPNOTSUPP; |
| @@ -494,10 +488,9 @@ static int picdev_write(struct kvm_io_device *this, | |||
| 494 | return 0; | 488 | return 0; |
| 495 | } | 489 | } |
| 496 | 490 | ||
| 497 | static int picdev_read(struct kvm_io_device *this, | 491 | static int picdev_read(struct kvm_pic *s, |
| 498 | gpa_t addr, int len, void *val) | 492 | gpa_t addr, int len, void *val) |
| 499 | { | 493 | { |
| 500 | struct kvm_pic *s = to_pic(this); | ||
| 501 | unsigned char data = 0; | 494 | unsigned char data = 0; |
| 502 | if (!picdev_in_range(addr)) | 495 | if (!picdev_in_range(addr)) |
| 503 | return -EOPNOTSUPP; | 496 | return -EOPNOTSUPP; |
| @@ -525,6 +518,48 @@ static int picdev_read(struct kvm_io_device *this, | |||
| 525 | return 0; | 518 | return 0; |
| 526 | } | 519 | } |
| 527 | 520 | ||
| 521 | static int picdev_master_write(struct kvm_io_device *dev, | ||
| 522 | gpa_t addr, int len, const void *val) | ||
| 523 | { | ||
| 524 | return picdev_write(container_of(dev, struct kvm_pic, dev_master), | ||
| 525 | addr, len, val); | ||
| 526 | } | ||
| 527 | |||
| 528 | static int picdev_master_read(struct kvm_io_device *dev, | ||
| 529 | gpa_t addr, int len, void *val) | ||
| 530 | { | ||
| 531 | return picdev_read(container_of(dev, struct kvm_pic, dev_master), | ||
| 532 | addr, len, val); | ||
| 533 | } | ||
| 534 | |||
| 535 | static int picdev_slave_write(struct kvm_io_device *dev, | ||
| 536 | gpa_t addr, int len, const void *val) | ||
| 537 | { | ||
| 538 | return picdev_write(container_of(dev, struct kvm_pic, dev_slave), | ||
| 539 | addr, len, val); | ||
| 540 | } | ||
| 541 | |||
| 542 | static int picdev_slave_read(struct kvm_io_device *dev, | ||
| 543 | gpa_t addr, int len, void *val) | ||
| 544 | { | ||
| 545 | return picdev_read(container_of(dev, struct kvm_pic, dev_slave), | ||
| 546 | addr, len, val); | ||
| 547 | } | ||
| 548 | |||
| 549 | static int picdev_eclr_write(struct kvm_io_device *dev, | ||
| 550 | gpa_t addr, int len, const void *val) | ||
| 551 | { | ||
| 552 | return picdev_write(container_of(dev, struct kvm_pic, dev_eclr), | ||
| 553 | addr, len, val); | ||
| 554 | } | ||
| 555 | |||
| 556 | static int picdev_eclr_read(struct kvm_io_device *dev, | ||
| 557 | gpa_t addr, int len, void *val) | ||
| 558 | { | ||
| 559 | return picdev_read(container_of(dev, struct kvm_pic, dev_eclr), | ||
| 560 | addr, len, val); | ||
| 561 | } | ||
| 562 | |||
| 528 | /* | 563 | /* |
| 529 | * callback when PIC0 irq status changed | 564 | * callback when PIC0 irq status changed |
| 530 | */ | 565 | */ |
| @@ -537,9 +572,19 @@ static void pic_irq_request(struct kvm *kvm, int level) | |||
| 537 | s->output = level; | 572 | s->output = level; |
| 538 | } | 573 | } |
| 539 | 574 | ||
| 540 | static const struct kvm_io_device_ops picdev_ops = { | 575 | static const struct kvm_io_device_ops picdev_master_ops = { |
| 541 | .read = picdev_read, | 576 | .read = picdev_master_read, |
| 542 | .write = picdev_write, | 577 | .write = picdev_master_write, |
| 578 | }; | ||
| 579 | |||
| 580 | static const struct kvm_io_device_ops picdev_slave_ops = { | ||
| 581 | .read = picdev_slave_read, | ||
| 582 | .write = picdev_slave_write, | ||
| 583 | }; | ||
| 584 | |||
| 585 | static const struct kvm_io_device_ops picdev_eclr_ops = { | ||
| 586 | .read = picdev_eclr_read, | ||
| 587 | .write = picdev_eclr_write, | ||
| 543 | }; | 588 | }; |
| 544 | 589 | ||
| 545 | struct kvm_pic *kvm_create_pic(struct kvm *kvm) | 590 | struct kvm_pic *kvm_create_pic(struct kvm *kvm) |
| @@ -560,16 +605,39 @@ struct kvm_pic *kvm_create_pic(struct kvm *kvm) | |||
| 560 | /* | 605 | /* |
| 561 | * Initialize PIO device | 606 | * Initialize PIO device |
| 562 | */ | 607 | */ |
| 563 | kvm_iodevice_init(&s->dev, &picdev_ops); | 608 | kvm_iodevice_init(&s->dev_master, &picdev_master_ops); |
| 609 | kvm_iodevice_init(&s->dev_slave, &picdev_slave_ops); | ||
| 610 | kvm_iodevice_init(&s->dev_eclr, &picdev_eclr_ops); | ||
| 564 | mutex_lock(&kvm->slots_lock); | 611 | mutex_lock(&kvm->slots_lock); |
| 565 | ret = kvm_io_bus_register_dev(kvm, KVM_PIO_BUS, &s->dev); | 612 | ret = kvm_io_bus_register_dev(kvm, KVM_PIO_BUS, 0x20, 2, |
| 613 | &s->dev_master); | ||
| 614 | if (ret < 0) | ||
| 615 | goto fail_unlock; | ||
| 616 | |||
| 617 | ret = kvm_io_bus_register_dev(kvm, KVM_PIO_BUS, 0xa0, 2, &s->dev_slave); | ||
| 618 | if (ret < 0) | ||
| 619 | goto fail_unreg_2; | ||
| 620 | |||
| 621 | ret = kvm_io_bus_register_dev(kvm, KVM_PIO_BUS, 0x4d0, 2, &s->dev_eclr); | ||
| 622 | if (ret < 0) | ||
| 623 | goto fail_unreg_1; | ||
| 624 | |||
| 566 | mutex_unlock(&kvm->slots_lock); | 625 | mutex_unlock(&kvm->slots_lock); |
| 567 | if (ret < 0) { | ||
| 568 | kfree(s); | ||
| 569 | return NULL; | ||
| 570 | } | ||
| 571 | 626 | ||
| 572 | return s; | 627 | return s; |
| 628 | |||
| 629 | fail_unreg_1: | ||
| 630 | kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS, &s->dev_slave); | ||
| 631 | |||
| 632 | fail_unreg_2: | ||
| 633 | kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS, &s->dev_master); | ||
| 634 | |||
| 635 | fail_unlock: | ||
| 636 | mutex_unlock(&kvm->slots_lock); | ||
| 637 | |||
| 638 | kfree(s); | ||
| 639 | |||
| 640 | return NULL; | ||
| 573 | } | 641 | } |
| 574 | 642 | ||
| 575 | void kvm_destroy_pic(struct kvm *kvm) | 643 | void kvm_destroy_pic(struct kvm *kvm) |
| @@ -577,7 +645,9 @@ void kvm_destroy_pic(struct kvm *kvm) | |||
| 577 | struct kvm_pic *vpic = kvm->arch.vpic; | 645 | struct kvm_pic *vpic = kvm->arch.vpic; |
| 578 | 646 | ||
| 579 | if (vpic) { | 647 | if (vpic) { |
| 580 | kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS, &vpic->dev); | 648 | kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS, &vpic->dev_master); |
| 649 | kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS, &vpic->dev_slave); | ||
| 650 | kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS, &vpic->dev_eclr); | ||
| 581 | kvm->arch.vpic = NULL; | 651 | kvm->arch.vpic = NULL; |
| 582 | kfree(vpic); | 652 | kfree(vpic); |
| 583 | } | 653 | } |
diff --git a/arch/x86/kvm/irq.h b/arch/x86/kvm/irq.h index 53e2d084bffb..2086f2bfba33 100644 --- a/arch/x86/kvm/irq.h +++ b/arch/x86/kvm/irq.h | |||
| @@ -66,7 +66,9 @@ struct kvm_pic { | |||
| 66 | struct kvm *kvm; | 66 | struct kvm *kvm; |
| 67 | struct kvm_kpic_state pics[2]; /* 0 is master pic, 1 is slave pic */ | 67 | struct kvm_kpic_state pics[2]; /* 0 is master pic, 1 is slave pic */ |
| 68 | int output; /* intr from master PIC */ | 68 | int output; /* intr from master PIC */ |
| 69 | struct kvm_io_device dev; | 69 | struct kvm_io_device dev_master; |
| 70 | struct kvm_io_device dev_slave; | ||
| 71 | struct kvm_io_device dev_eclr; | ||
| 70 | void (*ack_notifier)(void *opaque, int irq); | 72 | void (*ack_notifier)(void *opaque, int irq); |
| 71 | unsigned long irq_states[16]; | 73 | unsigned long irq_states[16]; |
| 72 | }; | 74 | }; |
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 6cb353c83a12..d28dff749dfd 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c | |||
| @@ -3562,7 +3562,11 @@ long kvm_arch_vm_ioctl(struct file *filp, | |||
| 3562 | if (r) { | 3562 | if (r) { |
| 3563 | mutex_lock(&kvm->slots_lock); | 3563 | mutex_lock(&kvm->slots_lock); |
| 3564 | kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS, | 3564 | kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS, |
| 3565 | &vpic->dev); | 3565 | &vpic->dev_master); |
| 3566 | kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS, | ||
| 3567 | &vpic->dev_slave); | ||
| 3568 | kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS, | ||
| 3569 | &vpic->dev_eclr); | ||
| 3566 | mutex_unlock(&kvm->slots_lock); | 3570 | mutex_unlock(&kvm->slots_lock); |
| 3567 | kfree(vpic); | 3571 | kfree(vpic); |
| 3568 | goto create_irqchip_unlock; | 3572 | goto create_irqchip_unlock; |
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index ff4d4062af9d..d0e42f30edf6 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h | |||
| @@ -55,16 +55,16 @@ struct kvm; | |||
| 55 | struct kvm_vcpu; | 55 | struct kvm_vcpu; |
| 56 | extern struct kmem_cache *kvm_vcpu_cache; | 56 | extern struct kmem_cache *kvm_vcpu_cache; |
| 57 | 57 | ||
| 58 | /* | 58 | struct kvm_io_range { |
| 59 | * It would be nice to use something smarter than a linear search, TBD... | 59 | gpa_t addr; |
| 60 | * Thankfully we dont expect many devices to register (famous last words :), | 60 | int len; |
| 61 | * so until then it will suffice. At least its abstracted so we can change | 61 | struct kvm_io_device *dev; |
| 62 | * in one place. | 62 | }; |
| 63 | */ | 63 | |
| 64 | struct kvm_io_bus { | 64 | struct kvm_io_bus { |
| 65 | int dev_count; | 65 | int dev_count; |
| 66 | #define NR_IOBUS_DEVS 300 | 66 | #define NR_IOBUS_DEVS 300 |
| 67 | struct kvm_io_device *devs[NR_IOBUS_DEVS]; | 67 | struct kvm_io_range range[NR_IOBUS_DEVS]; |
| 68 | }; | 68 | }; |
| 69 | 69 | ||
| 70 | enum kvm_bus { | 70 | enum kvm_bus { |
| @@ -77,8 +77,8 @@ int kvm_io_bus_write(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, | |||
| 77 | int len, const void *val); | 77 | int len, const void *val); |
| 78 | int kvm_io_bus_read(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, int len, | 78 | int kvm_io_bus_read(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, int len, |
| 79 | void *val); | 79 | void *val); |
| 80 | int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, | 80 | int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, |
| 81 | struct kvm_io_device *dev); | 81 | int len, struct kvm_io_device *dev); |
| 82 | int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx, | 82 | int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx, |
| 83 | struct kvm_io_device *dev); | 83 | struct kvm_io_device *dev); |
| 84 | 84 | ||
diff --git a/virt/kvm/coalesced_mmio.c b/virt/kvm/coalesced_mmio.c index 2316ec1aadc4..a6ec206f36ba 100644 --- a/virt/kvm/coalesced_mmio.c +++ b/virt/kvm/coalesced_mmio.c | |||
| @@ -141,7 +141,8 @@ int kvm_vm_ioctl_register_coalesced_mmio(struct kvm *kvm, | |||
| 141 | dev->zone = *zone; | 141 | dev->zone = *zone; |
| 142 | 142 | ||
| 143 | mutex_lock(&kvm->slots_lock); | 143 | mutex_lock(&kvm->slots_lock); |
| 144 | ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, &dev->dev); | 144 | ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, zone->addr, |
| 145 | zone->size, &dev->dev); | ||
| 145 | if (ret < 0) | 146 | if (ret < 0) |
| 146 | goto out_free_dev; | 147 | goto out_free_dev; |
| 147 | list_add_tail(&dev->list, &kvm->coalesced_zones); | 148 | list_add_tail(&dev->list, &kvm->coalesced_zones); |
diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c index 73358d256fa2..f59c1e8de7a2 100644 --- a/virt/kvm/eventfd.c +++ b/virt/kvm/eventfd.c | |||
| @@ -586,7 +586,8 @@ kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args) | |||
| 586 | 586 | ||
| 587 | kvm_iodevice_init(&p->dev, &ioeventfd_ops); | 587 | kvm_iodevice_init(&p->dev, &ioeventfd_ops); |
| 588 | 588 | ||
| 589 | ret = kvm_io_bus_register_dev(kvm, bus_idx, &p->dev); | 589 | ret = kvm_io_bus_register_dev(kvm, bus_idx, p->addr, p->length, |
| 590 | &p->dev); | ||
| 590 | if (ret < 0) | 591 | if (ret < 0) |
| 591 | goto unlock_fail; | 592 | goto unlock_fail; |
| 592 | 593 | ||
diff --git a/virt/kvm/ioapic.c b/virt/kvm/ioapic.c index 8df1ca104a7f..3eed61eb4867 100644 --- a/virt/kvm/ioapic.c +++ b/virt/kvm/ioapic.c | |||
| @@ -394,7 +394,8 @@ int kvm_ioapic_init(struct kvm *kvm) | |||
| 394 | kvm_iodevice_init(&ioapic->dev, &ioapic_mmio_ops); | 394 | kvm_iodevice_init(&ioapic->dev, &ioapic_mmio_ops); |
| 395 | ioapic->kvm = kvm; | 395 | ioapic->kvm = kvm; |
| 396 | mutex_lock(&kvm->slots_lock); | 396 | mutex_lock(&kvm->slots_lock); |
| 397 | ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, &ioapic->dev); | 397 | ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, ioapic->base_address, |
| 398 | IOAPIC_MEM_LENGTH, &ioapic->dev); | ||
| 398 | mutex_unlock(&kvm->slots_lock); | 399 | mutex_unlock(&kvm->slots_lock); |
| 399 | if (ret < 0) { | 400 | if (ret < 0) { |
| 400 | kvm->arch.vioapic = NULL; | 401 | kvm->arch.vioapic = NULL; |
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index aefdda390f5e..d9cfb782cb81 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c | |||
| @@ -47,6 +47,8 @@ | |||
| 47 | #include <linux/srcu.h> | 47 | #include <linux/srcu.h> |
| 48 | #include <linux/hugetlb.h> | 48 | #include <linux/hugetlb.h> |
| 49 | #include <linux/slab.h> | 49 | #include <linux/slab.h> |
| 50 | #include <linux/sort.h> | ||
| 51 | #include <linux/bsearch.h> | ||
| 50 | 52 | ||
| 51 | #include <asm/processor.h> | 53 | #include <asm/processor.h> |
| 52 | #include <asm/io.h> | 54 | #include <asm/io.h> |
| @@ -2391,24 +2393,92 @@ static void kvm_io_bus_destroy(struct kvm_io_bus *bus) | |||
| 2391 | int i; | 2393 | int i; |
| 2392 | 2394 | ||
| 2393 | for (i = 0; i < bus->dev_count; i++) { | 2395 | for (i = 0; i < bus->dev_count; i++) { |
| 2394 | struct kvm_io_device *pos = bus->devs[i]; | 2396 | struct kvm_io_device *pos = bus->range[i].dev; |
| 2395 | 2397 | ||
| 2396 | kvm_iodevice_destructor(pos); | 2398 | kvm_iodevice_destructor(pos); |
| 2397 | } | 2399 | } |
| 2398 | kfree(bus); | 2400 | kfree(bus); |
| 2399 | } | 2401 | } |
| 2400 | 2402 | ||
| 2403 | int kvm_io_bus_sort_cmp(const void *p1, const void *p2) | ||
| 2404 | { | ||
| 2405 | const struct kvm_io_range *r1 = p1; | ||
| 2406 | const struct kvm_io_range *r2 = p2; | ||
| 2407 | |||
| 2408 | if (r1->addr < r2->addr) | ||
| 2409 | return -1; | ||
| 2410 | if (r1->addr + r1->len > r2->addr + r2->len) | ||
| 2411 | return 1; | ||
| 2412 | return 0; | ||
| 2413 | } | ||
| 2414 | |||
| 2415 | int kvm_io_bus_insert_dev(struct kvm_io_bus *bus, struct kvm_io_device *dev, | ||
| 2416 | gpa_t addr, int len) | ||
| 2417 | { | ||
| 2418 | if (bus->dev_count == NR_IOBUS_DEVS) | ||
| 2419 | return -ENOSPC; | ||
| 2420 | |||
| 2421 | bus->range[bus->dev_count++] = (struct kvm_io_range) { | ||
| 2422 | .addr = addr, | ||
| 2423 | .len = len, | ||
| 2424 | .dev = dev, | ||
| 2425 | }; | ||
| 2426 | |||
| 2427 | sort(bus->range, bus->dev_count, sizeof(struct kvm_io_range), | ||
| 2428 | kvm_io_bus_sort_cmp, NULL); | ||
| 2429 | |||
| 2430 | return 0; | ||
| 2431 | } | ||
| 2432 | |||
| 2433 | int kvm_io_bus_get_first_dev(struct kvm_io_bus *bus, | ||
| 2434 | gpa_t addr, int len) | ||
| 2435 | { | ||
| 2436 | struct kvm_io_range *range, key; | ||
| 2437 | int off; | ||
| 2438 | |||
| 2439 | key = (struct kvm_io_range) { | ||
| 2440 | .addr = addr, | ||
| 2441 | .len = len, | ||
| 2442 | }; | ||
| 2443 | |||
| 2444 | range = bsearch(&key, bus->range, bus->dev_count, | ||
| 2445 | sizeof(struct kvm_io_range), kvm_io_bus_sort_cmp); | ||
| 2446 | if (range == NULL) | ||
| 2447 | return -ENOENT; | ||
| 2448 | |||
| 2449 | off = range - bus->range; | ||
| 2450 | |||
| 2451 | while (off > 0 && kvm_io_bus_sort_cmp(&key, &bus->range[off-1]) == 0) | ||
| 2452 | off--; | ||
| 2453 | |||
| 2454 | return off; | ||
| 2455 | } | ||
| 2456 | |||
| 2401 | /* kvm_io_bus_write - called under kvm->slots_lock */ | 2457 | /* kvm_io_bus_write - called under kvm->slots_lock */ |
| 2402 | int kvm_io_bus_write(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, | 2458 | int kvm_io_bus_write(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, |
| 2403 | int len, const void *val) | 2459 | int len, const void *val) |
| 2404 | { | 2460 | { |
| 2405 | int i; | 2461 | int idx; |
| 2406 | struct kvm_io_bus *bus; | 2462 | struct kvm_io_bus *bus; |
| 2463 | struct kvm_io_range range; | ||
| 2464 | |||
| 2465 | range = (struct kvm_io_range) { | ||
| 2466 | .addr = addr, | ||
| 2467 | .len = len, | ||
| 2468 | }; | ||
| 2407 | 2469 | ||
| 2408 | bus = srcu_dereference(kvm->buses[bus_idx], &kvm->srcu); | 2470 | bus = srcu_dereference(kvm->buses[bus_idx], &kvm->srcu); |
| 2409 | for (i = 0; i < bus->dev_count; i++) | 2471 | idx = kvm_io_bus_get_first_dev(bus, addr, len); |
| 2410 | if (!kvm_iodevice_write(bus->devs[i], addr, len, val)) | 2472 | if (idx < 0) |
| 2473 | return -EOPNOTSUPP; | ||
| 2474 | |||
| 2475 | while (idx < bus->dev_count && | ||
| 2476 | kvm_io_bus_sort_cmp(&range, &bus->range[idx]) == 0) { | ||
| 2477 | if (!kvm_iodevice_write(bus->range[idx].dev, addr, len, val)) | ||
| 2411 | return 0; | 2478 | return 0; |
| 2479 | idx++; | ||
| 2480 | } | ||
| 2481 | |||
| 2412 | return -EOPNOTSUPP; | 2482 | return -EOPNOTSUPP; |
| 2413 | } | 2483 | } |
| 2414 | 2484 | ||
| @@ -2416,19 +2486,33 @@ int kvm_io_bus_write(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, | |||
| 2416 | int kvm_io_bus_read(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, | 2486 | int kvm_io_bus_read(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, |
| 2417 | int len, void *val) | 2487 | int len, void *val) |
| 2418 | { | 2488 | { |
| 2419 | int i; | 2489 | int idx; |
| 2420 | struct kvm_io_bus *bus; | 2490 | struct kvm_io_bus *bus; |
| 2491 | struct kvm_io_range range; | ||
| 2492 | |||
| 2493 | range = (struct kvm_io_range) { | ||
| 2494 | .addr = addr, | ||
| 2495 | .len = len, | ||
| 2496 | }; | ||
| 2421 | 2497 | ||
| 2422 | bus = srcu_dereference(kvm->buses[bus_idx], &kvm->srcu); | 2498 | bus = srcu_dereference(kvm->buses[bus_idx], &kvm->srcu); |
| 2423 | for (i = 0; i < bus->dev_count; i++) | 2499 | idx = kvm_io_bus_get_first_dev(bus, addr, len); |
| 2424 | if (!kvm_iodevice_read(bus->devs[i], addr, len, val)) | 2500 | if (idx < 0) |
| 2501 | return -EOPNOTSUPP; | ||
| 2502 | |||
| 2503 | while (idx < bus->dev_count && | ||
| 2504 | kvm_io_bus_sort_cmp(&range, &bus->range[idx]) == 0) { | ||
| 2505 | if (!kvm_iodevice_read(bus->range[idx].dev, addr, len, val)) | ||
| 2425 | return 0; | 2506 | return 0; |
| 2507 | idx++; | ||
| 2508 | } | ||
| 2509 | |||
| 2426 | return -EOPNOTSUPP; | 2510 | return -EOPNOTSUPP; |
| 2427 | } | 2511 | } |
| 2428 | 2512 | ||
| 2429 | /* Caller must hold slots_lock. */ | 2513 | /* Caller must hold slots_lock. */ |
| 2430 | int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, | 2514 | int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, gpa_t addr, |
| 2431 | struct kvm_io_device *dev) | 2515 | int len, struct kvm_io_device *dev) |
| 2432 | { | 2516 | { |
| 2433 | struct kvm_io_bus *new_bus, *bus; | 2517 | struct kvm_io_bus *new_bus, *bus; |
| 2434 | 2518 | ||
| @@ -2440,7 +2524,7 @@ int kvm_io_bus_register_dev(struct kvm *kvm, enum kvm_bus bus_idx, | |||
| 2440 | if (!new_bus) | 2524 | if (!new_bus) |
| 2441 | return -ENOMEM; | 2525 | return -ENOMEM; |
| 2442 | memcpy(new_bus, bus, sizeof(struct kvm_io_bus)); | 2526 | memcpy(new_bus, bus, sizeof(struct kvm_io_bus)); |
| 2443 | new_bus->devs[new_bus->dev_count++] = dev; | 2527 | kvm_io_bus_insert_dev(new_bus, dev, addr, len); |
| 2444 | rcu_assign_pointer(kvm->buses[bus_idx], new_bus); | 2528 | rcu_assign_pointer(kvm->buses[bus_idx], new_bus); |
| 2445 | synchronize_srcu_expedited(&kvm->srcu); | 2529 | synchronize_srcu_expedited(&kvm->srcu); |
| 2446 | kfree(bus); | 2530 | kfree(bus); |
| @@ -2464,9 +2548,13 @@ int kvm_io_bus_unregister_dev(struct kvm *kvm, enum kvm_bus bus_idx, | |||
| 2464 | 2548 | ||
| 2465 | r = -ENOENT; | 2549 | r = -ENOENT; |
| 2466 | for (i = 0; i < new_bus->dev_count; i++) | 2550 | for (i = 0; i < new_bus->dev_count; i++) |
| 2467 | if (new_bus->devs[i] == dev) { | 2551 | if (new_bus->range[i].dev == dev) { |
| 2468 | r = 0; | 2552 | r = 0; |
| 2469 | new_bus->devs[i] = new_bus->devs[--new_bus->dev_count]; | 2553 | new_bus->dev_count--; |
| 2554 | new_bus->range[i] = new_bus->range[new_bus->dev_count]; | ||
| 2555 | sort(new_bus->range, new_bus->dev_count, | ||
| 2556 | sizeof(struct kvm_io_range), | ||
| 2557 | kvm_io_bus_sort_cmp, NULL); | ||
| 2470 | break; | 2558 | break; |
| 2471 | } | 2559 | } |
| 2472 | 2560 | ||
