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/x86/kvm/i8259.c | |
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/x86/kvm/i8259.c')
-rw-r--r-- | arch/x86/kvm/i8259.c | 108 |
1 files changed, 89 insertions, 19 deletions
diff --git a/arch/x86/kvm/i8259.c b/arch/x86/kvm/i8259.c index 19fe855e795..6b869ce0cc1 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 | } |