From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1LtJu8-0000B5-Jp for qemu-devel@nongnu.org; Mon, 13 Apr 2009 07:00:44 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1LtJu3-00008e-KA for qemu-devel@nongnu.org; Mon, 13 Apr 2009 07:00:43 -0400 Received: from [199.232.76.173] (port=60230 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1LtJu3-00008Y-FC for qemu-devel@nongnu.org; Mon, 13 Apr 2009 07:00:39 -0400 Received: from fmmailgate02.web.de ([217.72.192.227]:45077) by monty-python.gnu.org with esmtp (Exim 4.60) (envelope-from ) id 1LtJu2-0004W7-EI for qemu-devel@nongnu.org; Mon, 13 Apr 2009 07:00:39 -0400 Received: from smtp05.web.de (fmsmtp05.dlan.cinetic.de [172.20.4.166]) by fmmailgate02.web.de (Postfix) with ESMTP id 7F599FCE569C for ; Mon, 13 Apr 2009 13:00:36 +0200 (CEST) Received: from [88.66.126.154] (helo=[192.168.1.123]) by smtp05.web.de with asmtp (TLSv1:AES256-SHA:256) (WEB.DE 4.110 #277) id 1LtJu0-0006ZP-00 for qemu-devel@nongnu.org; Mon, 13 Apr 2009 13:00:36 +0200 Message-ID: <49E31B4F.90201@web.de> Date: Mon, 13 Apr 2009 13:00:31 +0200 From: Jan Kiszka MIME-Version: 1.0 References: <20090411172025.32383.77687.stgit@mchn012c.ww002.siemens.net> <20090411172026.32383.83446.stgit@mchn012c.ww002.siemens.net> In-Reply-To: <20090411172026.32383.83446.stgit@mchn012c.ww002.siemens.net> Content-Type: multipart/signed; micalg=pgp-sha1; protocol="application/pgp-signature"; boundary="------------enig0357B4909D1FB908C77E9D51" Sender: jan.kiszka@web.de Subject: [Qemu-devel] [PATCH 5/7 v2] kvm: improve handling of overlapping slots Reply-To: qemu-devel@nongnu.org List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org This is an OpenPGP/MIME signed message (RFC 2440 and 3156) --------------enig0357B4909D1FB908C77E9D51 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable This reworks the slot management to handle all patterns of cpu_register_physical_memory*, finally allowing to reset KVM guests (so far address remapping on reset broke the slot management). Nevertheless, KVM kernel bug in older versions forces us to track previousfragmentations and maintain them (as that bug prevents registering larger slots that overlap also deleted ones). When affected KVM versions are detected, we apply a workaround that covers all currently used patterns. Changes in v2: - detect KVM kernel bug and apply the workaround only when needed Signed-off-by: Jan Kiszka --- kvm-all.c | 176 +++++++++++++++++++++++++++++++++++++++++++------------= ------ 1 files changed, 124 insertions(+), 52 deletions(-) diff --git a/kvm-all.c b/kvm-all.c index 3e4e421..9e9b462 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -57,6 +57,7 @@ struct KVMState int fd; int vmfd; int coalesced_mmio; + int broken_set_mem_region; #ifdef KVM_CAP_SET_GUEST_DEBUG struct kvm_sw_breakpoint_head kvm_sw_breakpoints; #endif @@ -98,19 +99,31 @@ static KVMSlot *kvm_lookup_matching_slot(KVMState *s,= return NULL; } =20 -static KVMSlot *kvm_lookup_slot(KVMState *s, target_phys_addr_t start_ad= dr) +/* + * Find overlapping slot with lowest start address + */ +static KVMSlot *kvm_lookup_overlapping_slot(KVMState *s, + target_phys_addr_t start_add= r, + target_phys_addr_t end_addr)= { + KVMSlot *found =3D NULL; int i; =20 for (i =3D 0; i < ARRAY_SIZE(s->slots); i++) { KVMSlot *mem =3D &s->slots[i]; =20 - if (start_addr >=3D mem->start_addr && - start_addr < (mem->start_addr + mem->memory_size)) - return mem; + if (mem->memory_size =3D=3D 0 || + (found && found->start_addr < mem->start_addr)) { + continue; + } + + if (end_addr > mem->start_addr && + start_addr < mem->start_addr + mem->memory_size) { + found =3D mem; + } } =20 - return NULL; + return found; } =20 static int kvm_set_user_memory_region(KVMState *s, KVMSlot *slot) @@ -386,6 +399,14 @@ int kvm_init(int smp_cpus) s->coalesced_mmio =3D ret; #endif =20 + s->broken_set_mem_region =3D 1; +#ifdef KVM_CAP_JOIN_MEMORY_REGIONS_WORKS + ret =3D kvm_ioctl(s, KVM_CHECK_EXTENSION, KVM_CAP_JOIN_MEMORY_REGION= S_WORKS); + if (ret > 0) { + s->broken_set_mem_region =3D 0; + } +#endif + ret =3D kvm_arch_init(s, smp_cpus); if (ret < 0) goto err; @@ -567,7 +588,8 @@ void kvm_set_phys_mem(target_phys_addr_t start_addr, { KVMState *s =3D kvm_state; ram_addr_t flags =3D phys_offset & ~TARGET_PAGE_MASK; - KVMSlot *mem; + KVMSlot *mem, old; + int err; =20 if (start_addr & ~TARGET_PAGE_MASK) { fprintf(stderr, "Only page-aligned memory slots supported\n"); @@ -577,55 +599,101 @@ void kvm_set_phys_mem(target_phys_addr_t start_add= r, /* KVM does not support read-only slots */ phys_offset &=3D ~IO_MEM_ROM; =20 - mem =3D kvm_lookup_slot(s, start_addr); - if (mem) { - if (flags >=3D IO_MEM_UNASSIGNED) { - mem->memory_size =3D 0; - mem->start_addr =3D start_addr; - mem->phys_offset =3D 0; - mem->flags =3D 0; - - kvm_set_user_memory_region(s, mem); - } else if (start_addr >=3D mem->start_addr && - (start_addr + size) <=3D (mem->start_addr + - mem->memory_size)) { - KVMSlot slot; - target_phys_addr_t mem_start; - ram_addr_t mem_size, mem_offset; - - /* Not splitting */ - if ((phys_offset - (start_addr - mem->start_addr)) =3D=3D=20 - mem->phys_offset) - return; - - /* unregister whole slot */ - memcpy(&slot, mem, sizeof(slot)); - mem->memory_size =3D 0; - kvm_set_user_memory_region(s, mem); - - /* register prefix slot */ - mem_start =3D slot.start_addr; - mem_size =3D start_addr - slot.start_addr; - mem_offset =3D slot.phys_offset; - if (mem_size) - kvm_set_phys_mem(mem_start, mem_size, mem_offset); - - /* register new slot */ - kvm_set_phys_mem(start_addr, size, phys_offset); - - /* register suffix slot */ - mem_start =3D start_addr + size; - mem_offset +=3D mem_size + size; - mem_size =3D slot.memory_size - mem_size - size; - if (mem_size) - kvm_set_phys_mem(mem_start, mem_size, mem_offset); + while (1) { + mem =3D kvm_lookup_overlapping_slot(s, start_addr, start_addr + = size); + if (!mem) { + break; + } =20 + if (flags < IO_MEM_UNASSIGNED && start_addr >=3D mem->start_addr= && + (start_addr + size <=3D mem->start_addr + mem->memory_size) = && + (phys_offset - start_addr =3D=3D mem->phys_offset - mem->sta= rt_addr)) { + /* The new slot fits into the existing one and comes with + * identical parameters - nothing to be done. */ return; - } else { - printf("Registering overlapping slot\n"); + } + + old =3D *mem; + + /* unregister the overlapping slot */ + mem->memory_size =3D 0; + err =3D kvm_set_user_memory_region(s, mem); + if (err) { + fprintf(stderr, "%s: error unregistering overlapping slot: %= s\n", + __func__, strerror(-err)); abort(); } + + /* Workaround for older KVM versions: we can't join slots, even = not by + * unregistering the previous ones and then registering the larg= er + * slot. We have to maintain the existing fragmentation. Sigh. + * + * This workaround assumes that the new slot starts at the same + * address as the first existing one. If not or if some overlapp= ing + * slot comes around later, we will fail (not seen in practice s= o far) + * - and actually require a recent KVM version. */ + if (s->broken_set_mem_region && + old.start_addr =3D=3D start_addr && old.memory_size < size &= & + flags < IO_MEM_UNASSIGNED) { + mem =3D kvm_alloc_slot(s); + mem->memory_size =3D old.memory_size; + mem->start_addr =3D old.start_addr; + mem->phys_offset =3D old.phys_offset; + mem->flags =3D 0; + + err =3D kvm_set_user_memory_region(s, mem); + if (err) { + fprintf(stderr, "%s: error updating slot: %s\n", __func_= _, + strerror(-err)); + abort(); + } + + start_addr +=3D old.memory_size; + phys_offset +=3D old.memory_size; + size -=3D old.memory_size; + continue; + } + + /* register prefix slot */ + if (old.start_addr < start_addr) { + mem =3D kvm_alloc_slot(s); + mem->memory_size =3D start_addr - old.start_addr; + mem->start_addr =3D old.start_addr; + mem->phys_offset =3D old.phys_offset; + mem->flags =3D 0; + + err =3D kvm_set_user_memory_region(s, mem); + if (err) { + fprintf(stderr, "%s: error registering prefix slot: %s\n= ", + __func__, strerror(-err)); + abort(); + } + } + + /* register suffix slot */ + if (old.start_addr + old.memory_size > start_addr + size) { + ram_addr_t size_delta; + + mem =3D kvm_alloc_slot(s); + mem->start_addr =3D start_addr + size; + size_delta =3D mem->start_addr - old.start_addr; + mem->memory_size =3D old.memory_size - size_delta; + mem->phys_offset =3D old.phys_offset + size_delta; + mem->flags =3D 0; + + err =3D kvm_set_user_memory_region(s, mem); + if (err) { + fprintf(stderr, "%s: error registering suffix slot: %s\n= ", + __func__, strerror(-err)); + abort(); + } + } } + + /* in case the KVM bug workaround already "consumed" the new slot */= + if (!size) + return; + /* KVM does not need to know about this memory */ if (flags >=3D IO_MEM_UNASSIGNED) return; @@ -636,8 +704,12 @@ void kvm_set_phys_mem(target_phys_addr_t start_addr,= mem->phys_offset =3D phys_offset; mem->flags =3D 0; =20 - kvm_set_user_memory_region(s, mem); - /* FIXME deal with errors */ + err =3D kvm_set_user_memory_region(s, mem); + if (err) { + fprintf(stderr, "%s: error registering slot: %s\n", __func__, + strerror(-err)); + abort(); + } } =20 int kvm_ioctl(KVMState *s, int type, ...) --------------enig0357B4909D1FB908C77E9D51 Content-Type: application/pgp-signature; name="signature.asc" Content-Description: OpenPGP digital signature Content-Disposition: attachment; filename="signature.asc" -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.9 (GNU/Linux) Comment: Using GnuPG with SUSE - http://enigmail.mozdev.org iEYEARECAAYFAknjG1MACgkQniDOoMHTA+kJeACcDfXX2qg55PEC00fT1A/O8sQ9 qvcAnik+O9U1v0oXnWie7NaFUn80/TkO =6C1x -----END PGP SIGNATURE----- --------------enig0357B4909D1FB908C77E9D51--