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,19 +98,31 @@ static KVMSlot *kvm_lookup_matching_slot(KVMState *s,
98 return NULL; 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 int i; 109 int i;
104 110
105 for (i = 0; i < ARRAY_SIZE(s->slots); i++) { 111 for (i = 0; i < ARRAY_SIZE(s->slots); i++) {
106 KVMSlot *mem = &s->slots[i]; 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 static int kvm_set_user_memory_region(KVMState *s, KVMSlot *slot) 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,7 +579,8 @@ void kvm_set_phys_mem(target_phys_addr_t start_addr,
567 { 579 {
568 KVMState *s = kvm_state; 580 KVMState *s = kvm_state;
569 ram_addr_t flags = phys_offset & ~TARGET_PAGE_MASK; 581 ram_addr_t flags = phys_offset & ~TARGET_PAGE_MASK;
570 - KVMSlot *mem; 582 + KVMSlot *mem, old;
  583 + int err;
571 584
572 if (start_addr & ~TARGET_PAGE_MASK) { 585 if (start_addr & ~TARGET_PAGE_MASK) {
573 fprintf(stderr, "Only page-aligned memory slots supported\n"); 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,55 +590,100 @@ void kvm_set_phys_mem(target_phys_addr_t start_addr,
577 /* KVM does not support read-only slots */ 590 /* KVM does not support read-only slots */
578 phys_offset &= ~IO_MEM_ROM; 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 return; 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 abort(); 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 /* KVM does not need to know about this memory */ 687 /* KVM does not need to know about this memory */
630 if (flags >= IO_MEM_UNASSIGNED) 688 if (flags >= IO_MEM_UNASSIGNED)
631 return; 689 return;
@@ -636,8 +694,12 @@ void kvm_set_phys_mem(target_phys_addr_t start_addr, @@ -636,8 +694,12 @@ void kvm_set_phys_mem(target_phys_addr_t start_addr,
636 mem->phys_offset = phys_offset; 694 mem->phys_offset = phys_offset;
637 mem->flags = 0; 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 int kvm_ioctl(KVMState *s, int type, ...) 705 int kvm_ioctl(KVMState *s, int type, ...)