From: Takuya Yoshikawa <yoshikawa.takuya@xxxxxxxxxxxxx> lpage_info is created for each large level even when the memory slot is not for RAM. This means that when we add one slot for a PCI device, we end up allocating at least KVM_NR_PAGE_SIZES - 1 pages by vmalloc(): this problem will become severer if we support more guests with more devices in the future. Although it is not easy to differentiate RAM slots from others, we can avoid wasting pages by making KVM_NR_PAGE_SIZES - 1 lpage_info arrays coalesce into one and using kmalloc() when the result is small enough. Signed-off-by: Takuya Yoshikawa <yoshikawa.takuya@xxxxxxxxxxxxx> --- arch/x86/kvm/x86.c | 56 ++++++++++++++++++++++++++++++--------------------- 1 files changed, 33 insertions(+), 23 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 4de705c..716d543 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -6300,35 +6300,52 @@ void kvm_arch_free_memslot(struct kvm_memory_slot *free, { int i; - for (i = 0; i < KVM_NR_PAGE_SIZES - 1; ++i) { - if (!dont || free->arch.lpage_info[i] != dont->arch.lpage_info[i]) { - vfree(free->arch.lpage_info[i]); - free->arch.lpage_info[i] = NULL; - } - } + if (dont && free->arch.lpage_info[0] == dont->arch.lpage_info[0]) + return; + + if (is_vmalloc_addr(free->arch.lpage_info[0])) + vfree(free->arch.lpage_info[0]); + else + kfree(free->arch.lpage_info[0]); + + for (i = 0; i < KVM_NR_PAGE_SIZES - 1; ++i) + free->arch.lpage_info[i] = NULL; } int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages) { int i; + int level; + int total_size = 0; + int lpages[KVM_NR_PAGE_SIZES - 1]; for (i = 0; i < KVM_NR_PAGE_SIZES - 1; ++i) { - unsigned long ugfn; - int lpages; - int level = i + 2; + level = i + 2; + lpages[i] = gfn_to_index(slot->base_gfn + npages - 1, + slot->base_gfn, level) + 1; + total_size += lpages[i] * sizeof(*slot->arch.lpage_info[0]); + } - lpages = gfn_to_index(slot->base_gfn + npages - 1, - slot->base_gfn, level) + 1; + if (total_size > PAGE_SIZE) + slot->arch.lpage_info[0] = vzalloc(total_size); + else + slot->arch.lpage_info[0] = kzalloc(total_size, GFP_KERNEL); + if (!slot->arch.lpage_info[0]) + return -ENOMEM; + + for (i = 1; i < KVM_NR_PAGE_SIZES - 1; ++i) slot->arch.lpage_info[i] = - vzalloc(lpages * sizeof(*slot->arch.lpage_info[i])); - if (!slot->arch.lpage_info[i]) - goto out_free; + slot->arch.lpage_info[i - 1] + lpages[i - 1]; + + for (i = 0; i < KVM_NR_PAGE_SIZES - 1; ++i) { + unsigned long ugfn; + level = i + 2; if (slot->base_gfn & (KVM_PAGES_PER_HPAGE(level) - 1)) slot->arch.lpage_info[i][0].write_count = 1; if ((slot->base_gfn + npages) & (KVM_PAGES_PER_HPAGE(level) - 1)) - slot->arch.lpage_info[i][lpages - 1].write_count = 1; + slot->arch.lpage_info[i][lpages[i] - 1].write_count = 1; ugfn = slot->userspace_addr >> PAGE_SHIFT; /* * If the gfn and userspace address are not aligned wrt each @@ -6339,19 +6356,12 @@ int kvm_arch_create_memslot(struct kvm_memory_slot *slot, unsigned long npages) !kvm_largepages_enabled()) { unsigned long j; - for (j = 0; j < lpages; ++j) + for (j = 0; j < lpages[i]; ++j) slot->arch.lpage_info[i][j].write_count = 1; } } return 0; - -out_free: - for (i = 0; i < KVM_NR_PAGE_SIZES - 1; ++i) { - vfree(slot->arch.lpage_info[i]); - slot->arch.lpage_info[i] = NULL; - } - return -ENOMEM; } int kvm_arch_prepare_memory_region(struct kvm *kvm, -- 1.7.5.4 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html