[PATCH] KVM: Avoid wasting pages for small lpage_info arrays

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux