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 /arch | |
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>
Diffstat (limited to 'arch')
-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 |
4 files changed, 101 insertions, 23 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; |