@@ -178,22 +178,50 @@ int kvm_get_max_memslots(void)
{
KVMState *s = KVM_STATE(current_accel());
- return s->nr_slots;
+ return s->max_slots;
}
-/* Called with KVMMemoryListener.slots_lock held */
+/* Called with kvm_slots_lock()'ed */
static KVMSlot *kvm_get_free_slot(KVMMemoryListener *kml)
{
KVMState *s = kvm_state;
+ KVMSlot *new_slots;
int i;
+ int new_nr, old_nr;
- for (i = 0; i < s->nr_slots; i++) {
+ for (i = 0; i < kml->nr_slots; i++) {
if (kml->slots[i].memory_size == 0) {
return &kml->slots[i];
}
}
- return NULL;
+ /* Already reached maximum, no more can expand */
+ if (kml->nr_slots >= s->max_slots) {
+ return NULL;
+ }
+
+ new_nr = 2 * kml->nr_slots;
+ new_nr = MIN(new_nr, s->max_slots);
+ /* It might overflow */
+ if (new_nr < 0 || new_nr <= kml->nr_slots) {
+ return NULL;
+ }
+
+ new_slots = g_try_new0(KVMSlot, new_nr);
+ if (!new_slots) {
+ return NULL;
+ }
+
+ memcpy(new_slots, kml->slots, kml->nr_slots * sizeof(KVMSlot));
+ old_nr = kml->nr_slots;
+ kml->nr_slots = new_nr;
+ g_free(kml->slots);
+ kml->slots = new_slots;
+ for (i = old_nr; i < kml->nr_slots; i++) {
+ kml->slots[i].slot = i;
+ }
+
+ return &kml->slots[old_nr];
}
bool kvm_has_free_slot(MachineState *ms)
@@ -226,10 +254,9 @@ static KVMSlot *kvm_lookup_matching_slot(KVMMemoryListener *kml,
hwaddr start_addr,
hwaddr size)
{
- KVMState *s = kvm_state;
int i;
- for (i = 0; i < s->nr_slots; i++) {
+ for (i = 0; i < kml->nr_slots; i++) {
KVMSlot *mem = &kml->slots[i];
if (start_addr == mem->start_addr && size == mem->memory_size) {
@@ -271,7 +298,7 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram,
int i, ret = 0;
kvm_slots_lock();
- for (i = 0; i < s->nr_slots; i++) {
+ for (i = 0; i < kml->nr_slots; i++) {
KVMSlot *mem = &kml->slots[i];
if (ram >= mem->ram && ram < mem->ram + mem->memory_size) {
@@ -1002,7 +1029,7 @@ static int kvm_physical_log_clear(KVMMemoryListener *kml,
kvm_slots_lock();
- for (i = 0; i < s->nr_slots; i++) {
+ for (i = 0; i < kml->nr_slots; i++) {
mem = &kml->slots[i];
/* Discard slots that are empty or do not overlap the section */
if (!mem->memory_size ||
@@ -1566,7 +1593,6 @@ static void kvm_log_sync(MemoryListener *listener,
static void kvm_log_sync_global(MemoryListener *l)
{
KVMMemoryListener *kml = container_of(l, KVMMemoryListener, listener);
- KVMState *s = kvm_state;
KVMSlot *mem;
int i;
@@ -1578,7 +1604,7 @@ static void kvm_log_sync_global(MemoryListener *l)
* only a few used slots (small VMs).
*/
kvm_slots_lock();
- for (i = 0; i < s->nr_slots; i++) {
+ for (i = 0; i < kml->nr_slots; i++) {
mem = &kml->slots[i];
if (mem->memory_size && mem->flags & KVM_MEM_LOG_DIRTY_PAGES) {
kvm_slot_sync_dirty_pages(mem);
@@ -1687,10 +1713,11 @@ void kvm_memory_listener_register(KVMState *s, KVMMemoryListener *kml,
{
int i;
- kml->slots = g_new0(KVMSlot, s->nr_slots);
+ kml->slots = g_new0(KVMSlot, KVM_DEF_NR_SLOTS);
+ kml->nr_slots = KVM_DEF_NR_SLOTS;
kml->as_id = as_id;
- for (i = 0; i < s->nr_slots; i++) {
+ for (i = 0; i < kml->nr_slots; i++) {
kml->slots[i].slot = i;
}
@@ -2427,11 +2454,11 @@ static int kvm_init(MachineState *ms)
}
kvm_immediate_exit = kvm_check_extension(s, KVM_CAP_IMMEDIATE_EXIT);
- s->nr_slots = kvm_check_extension(s, KVM_CAP_NR_MEMSLOTS);
+ s->max_slots = kvm_check_extension(s, KVM_CAP_NR_MEMSLOTS);
/* If unspecified, use the default value */
- if (!s->nr_slots) {
- s->nr_slots = 32;
+ if (s->max_slots <= 0) {
+ s->max_slots = KVM_DEF_NR_SLOTS;
}
s->nr_as = kvm_check_extension(s, KVM_CAP_MULTI_ADDRESS_SPACE);
@@ -37,8 +37,10 @@ typedef struct KVMMemoryUpdate {
MemoryRegionSection section;
} KVMMemoryUpdate;
+#define KVM_DEF_NR_SLOTS 32
typedef struct KVMMemoryListener {
MemoryListener listener;
+ int nr_slots;
KVMSlot *slots;
int as_id;
QSIMPLEQ_HEAD(, KVMMemoryUpdate) transaction_add;
@@ -69,7 +71,7 @@ struct KVMState
{
AccelState parent_obj;
- int nr_slots;
+ int max_slots;
int fd;
int vmfd;
int coalesced_mmio;
Corrects QEMU to properly use what KVM_CAP_NR_MEMSLOTS means, i.e. the maximum user memslots KVM supports. 1. Rename KVMState::nr_slots --> max_slots. 2. Remember nr_slots in each KML. This also decouples each KML, e.g. x86's two KMLs don't need to have same size of slots[]. 3. Change back initial slot[] size to 32, increase it dynamically (exponentially) till the maximum number KVM supports. 32 should suites almost all normal guests. Background: Since KVM commit 4fc096a99e01d ("KVM: Raise the maximum number of user memslots"), KVM_CAP_NR_MEMSLOTS returns 32764 (SHRT_MAX - 3), which is a huge increase from previous 509 (x86). This change based on the fact that KVM alloc memslots dynamically. But QEMU allocates that huge number of user memslots statically. This is indeed unwanted by both sides. It makes: 1. Memory waste. Allocates (SHRT_MAX - 3) * sizeof(KVMSlot), while a typical VM needs far less, e.g. my VM just reached 9th as the highest mem slot ever used. x86 further, has 2 kmls; approx. 2 x 2MB for each VM. 2. Time waste. Several KML Slot functions go through the whole KML::slots[], (SHRT_MAX - 3) makes it far longer than necessary, e.g. kvm_lookup_matching_slot(), kvm_physical_memory_addr_from_host(), kvm_physical_log_clear(), kvm_log_sync_global(). Functional Test: Temporarily set KVM_DEF_NR_SLOTS = 8, let it go through slot[] dynamic increase, VM launched and works well. Performance improvement: Func time (ns) of kvm_lookup_matching_slot(), for example, Before After sample count 5874 6812 mean 11403 5867 min 1775 1722 max 784949 30263 Signed-off-by: Robert Hoo <robert.hoo.linux@gmail.com> --- Resend: Add stats about kvm_lookup_matching_slot() for example. CC kvm mail list per get_maintainer.pl suggests. I believe this benefits Live Migration, but not devices at hand to do the system level test. accel/kvm/kvm-all.c | 57 +++++++++++++++++++++++++++++----------- include/sysemu/kvm_int.h | 4 ++- 2 files changed, 45 insertions(+), 16 deletions(-) base-commit: c6f3cbca32bde9ee94d9949aa63e8a7ef2d7bc5b