Commit 6152e2ae4344ec8c849393da3f76f2263cc55766

Authored by aliguori
1 parent d3f8d37f

kvm: improve handling of overlapping slots (Jan Kiszka)

This reworks the slot management to handle more patterns of
cpu_register_physical_memory*, finally allowing to reset KVM guests (so
far address remapping on reset broke the slot management).

We could actually handle all possible ones without failing, but a KVM
kernel bug in older versions would force us to track all previous
fragmentations and maintain them (as that bug prevents registering
larger slots that overlap also deleted ones). To remain backward
compatible but avoid overly complicated workarounds, we apply a simpler
workaround that covers all currently used patterns.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@7139 c046a42c-6fe2-441c-8c8c-71466251a162
Showing 1 changed file with 114 additions and 52 deletions
kvm-all.c
... ... @@ -98,19 +98,31 @@ static KVMSlot *kvm_lookup_matching_slot(KVMState *s,
98 98 return NULL;
99 99 }
100 100  
101   -static KVMSlot *kvm_lookup_slot(KVMState *s, target_phys_addr_t start_addr)
  101 +/*
  102 + * Find overlapping slot with lowest start address
  103 + */
  104 +static KVMSlot *kvm_lookup_overlapping_slot(KVMState *s,
  105 + target_phys_addr_t start_addr,
  106 + target_phys_addr_t end_addr)
102 107 {
  108 + KVMSlot *found = NULL;
103 109 int i;
104 110  
105 111 for (i = 0; i < ARRAY_SIZE(s->slots); i++) {
106 112 KVMSlot *mem = &s->slots[i];
107 113  
108   - if (start_addr >= mem->start_addr &&
109   - start_addr < (mem->start_addr + mem->memory_size))
110   - return mem;
  114 + if (mem->memory_size == 0 ||
  115 + (found && found->start_addr < mem->start_addr)) {
  116 + continue;
  117 + }
  118 +
  119 + if (end_addr > mem->start_addr &&
  120 + start_addr < mem->start_addr + mem->memory_size) {
  121 + found = mem;
  122 + }
111 123 }
112 124  
113   - return NULL;
  125 + return found;
114 126 }
115 127  
116 128 static int kvm_set_user_memory_region(KVMState *s, KVMSlot *slot)
... ... @@ -567,7 +579,8 @@ void kvm_set_phys_mem(target_phys_addr_t start_addr,
567 579 {
568 580 KVMState *s = kvm_state;
569 581 ram_addr_t flags = phys_offset & ~TARGET_PAGE_MASK;
570   - KVMSlot *mem;
  582 + KVMSlot *mem, old;
  583 + int err;
571 584  
572 585 if (start_addr & ~TARGET_PAGE_MASK) {
573 586 fprintf(stderr, "Only page-aligned memory slots supported\n");
... ... @@ -577,55 +590,100 @@ void kvm_set_phys_mem(target_phys_addr_t start_addr,
577 590 /* KVM does not support read-only slots */
578 591 phys_offset &= ~IO_MEM_ROM;
579 592  
580   - mem = kvm_lookup_slot(s, start_addr);
581   - if (mem) {
582   - if (flags >= IO_MEM_UNASSIGNED) {
583   - mem->memory_size = 0;
584   - mem->start_addr = start_addr;
585   - mem->phys_offset = 0;
586   - mem->flags = 0;
587   -
588   - kvm_set_user_memory_region(s, mem);
589   - } else if (start_addr >= mem->start_addr &&
590   - (start_addr + size) <= (mem->start_addr +
591   - mem->memory_size)) {
592   - KVMSlot slot;
593   - target_phys_addr_t mem_start;
594   - ram_addr_t mem_size, mem_offset;
595   -
596   - /* Not splitting */
597   - if ((phys_offset - (start_addr - mem->start_addr)) ==
598   - mem->phys_offset)
599   - return;
600   -
601   - /* unregister whole slot */
602   - memcpy(&slot, mem, sizeof(slot));
603   - mem->memory_size = 0;
604   - kvm_set_user_memory_region(s, mem);
605   -
606   - /* register prefix slot */
607   - mem_start = slot.start_addr;
608   - mem_size = start_addr - slot.start_addr;
609   - mem_offset = slot.phys_offset;
610   - if (mem_size)
611   - kvm_set_phys_mem(mem_start, mem_size, mem_offset);
612   -
613   - /* register new slot */
614   - kvm_set_phys_mem(start_addr, size, phys_offset);
615   -
616   - /* register suffix slot */
617   - mem_start = start_addr + size;
618   - mem_offset += mem_size + size;
619   - mem_size = slot.memory_size - mem_size - size;
620   - if (mem_size)
621   - kvm_set_phys_mem(mem_start, mem_size, mem_offset);
  593 + while (1) {
  594 + mem = kvm_lookup_overlapping_slot(s, start_addr, start_addr + size);
  595 + if (!mem) {
  596 + break;
  597 + }
622 598  
  599 + if (flags < IO_MEM_UNASSIGNED && start_addr >= mem->start_addr &&
  600 + (start_addr + size <= mem->start_addr + mem->memory_size) &&
  601 + (phys_offset - start_addr == mem->phys_offset - mem->start_addr)) {
  602 + /* The new slot fits into the existing one and comes with
  603 + * identical parameters - nothing to be done. */
623 604 return;
624   - } else {
625   - printf("Registering overlapping slot\n");
  605 + }
  606 +
  607 + old = *mem;
  608 +
  609 + /* unregister the overlapping slot */
  610 + mem->memory_size = 0;
  611 + err = kvm_set_user_memory_region(s, mem);
  612 + if (err) {
  613 + fprintf(stderr, "%s: error unregistering overlapping slot: %s\n",
  614 + __func__, strerror(-err));
626 615 abort();
627 616 }
  617 +
  618 + /* Workaround for older KVM versions: we can't join slots, even not by
  619 + * unregistering the previous ones and then registering the larger
  620 + * slot. We have to maintain the existing fragmentation. Sigh.
  621 + *
  622 + * This workaround assumes that the new slot starts at the same
  623 + * address as the first existing one. If not or if some overlapping
  624 + * slot comes around later, we will fail (not seen in practice so far)
  625 + * - and actually require a recent KVM version. */
  626 + if (old.start_addr == start_addr && old.memory_size < size &&
  627 + flags < IO_MEM_UNASSIGNED) {
  628 + mem = kvm_alloc_slot(s);
  629 + mem->memory_size = old.memory_size;
  630 + mem->start_addr = old.start_addr;
  631 + mem->phys_offset = old.phys_offset;
  632 + mem->flags = 0;
  633 +
  634 + err = kvm_set_user_memory_region(s, mem);
  635 + if (err) {
  636 + fprintf(stderr, "%s: error updating slot: %s\n", __func__,
  637 + strerror(-err));
  638 + abort();
  639 + }
  640 +
  641 + start_addr += old.memory_size;
  642 + phys_offset += old.memory_size;
  643 + size -= old.memory_size;
  644 + continue;
  645 + }
  646 +
  647 + /* register prefix slot */
  648 + if (old.start_addr < start_addr) {
  649 + mem = kvm_alloc_slot(s);
  650 + mem->memory_size = start_addr - old.start_addr;
  651 + mem->start_addr = old.start_addr;
  652 + mem->phys_offset = old.phys_offset;
  653 + mem->flags = 0;
  654 +
  655 + err = kvm_set_user_memory_region(s, mem);
  656 + if (err) {
  657 + fprintf(stderr, "%s: error registering prefix slot: %s\n",
  658 + __func__, strerror(-err));
  659 + abort();
  660 + }
  661 + }
  662 +
  663 + /* register suffix slot */
  664 + if (old.start_addr + old.memory_size > start_addr + size) {
  665 + ram_addr_t size_delta;
  666 +
  667 + mem = kvm_alloc_slot(s);
  668 + mem->start_addr = start_addr + size;
  669 + size_delta = mem->start_addr - old.start_addr;
  670 + mem->memory_size = old.memory_size - size_delta;
  671 + mem->phys_offset = old.phys_offset + size_delta;
  672 + mem->flags = 0;
  673 +
  674 + err = kvm_set_user_memory_region(s, mem);
  675 + if (err) {
  676 + fprintf(stderr, "%s: error registering suffix slot: %s\n",
  677 + __func__, strerror(-err));
  678 + abort();
  679 + }
  680 + }
628 681 }
  682 +
  683 + /* in case the KVM bug workaround already "consumed" the new slot */
  684 + if (!size)
  685 + return;
  686 +
629 687 /* KVM does not need to know about this memory */
630 688 if (flags >= IO_MEM_UNASSIGNED)
631 689 return;
... ... @@ -636,8 +694,12 @@ void kvm_set_phys_mem(target_phys_addr_t start_addr,
636 694 mem->phys_offset = phys_offset;
637 695 mem->flags = 0;
638 696  
639   - kvm_set_user_memory_region(s, mem);
640   - /* FIXME deal with errors */
  697 + err = kvm_set_user_memory_region(s, mem);
  698 + if (err) {
  699 + fprintf(stderr, "%s: error registering slot: %s\n", __func__,
  700 + strerror(-err));
  701 + abort();
  702 + }
641 703 }
642 704  
643 705 int kvm_ioctl(KVMState *s, int type, ...)
... ...