KVM: optimize memslots searching and cache GPN to GFN
Subsystems (8)
cover KVM: optimize memslots searching and cache GPN to GFN
I noticed paging64/32_walk_addr_generic and gfn_to_memslot are very hot in kvm modules:
# perf report
# Events: 38K cycles
#
# Overhead Command Shared Object Symbol
# ........ ............... .................. ......
#
6.73% qemu-system-x86 [kernel.kallsyms] [k] native_read_tsc
4.51% qemu-system-x86 [kernel.kallsyms] [k] delay_tsc
3.93% qemu-system-x86 [kvm] [k] paging64_walk_addr_generic
3.91% qemu-system-x86 [kernel.kallsyms] [k] arch_local_irq_restore
3.28% qemu-system-x86 [kvm_intel] [k] vmx_vcpu_run
3.12% qemu-system-x86 [kernel.kallsyms] [k] lock_acquire
3.10% qemu-system-x86 [kernel.kallsyms] [k] paravirt_read_tsc
2.70% qemu-system-x86 [kernel.kallsyms] [k] copy_user_generic_string
2.30% qemu-system-x86 [kernel.kallsyms] [k] check_flags
2.15% qemu-system-x86 [kernel.kallsyms] [k] do_raw_spin_lock
2.03% qemu-system-x86 [kvm] [k] gfn_to_memslotThe aim of this patchset is to reduce the overload of these two functions, it does:
- sort memslots by ->base_gfn and use binary search to search the right slot which is implemented by patch 1 ~ patch 7
- cache guest page number to frame number to reduce *_walk_addr_generic called, and patch 7 does it.
After the patchset:
# Events: 36K cycles
#
# Overhead Command Shared Object Symbol
# ........ ............... .................. ......
#
5.64% qemu-system-x86 [kernel.kallsyms] [k] native_read_tsc
4.27% qemu-system-x86 [kvm_intel] [k] vmx_vcpu_run
3.85% qemu-system-x86 [kernel.kallsyms] [k] delay_tsc
3.32% qemu-system-x86 [kernel.kallsyms] [k] arch_local_irq_restore
2.68% qemu-system-x86 [kernel.kallsyms] [k] paravirt_read_tsc
2.41% qemu-system-x86 [kernel.kallsyms] [k] lock_acquire
2.25% qemu-system-x86 [kernel.kallsyms] [k] copy_user_generic_string
2.14% qemu-system-x86 [kvm] [k] kvm_mmu_pte_write
1.82% qemu-system-x86 [kernel.kallsyms] [k] check_flags
1.80% qemu-system-x86 [kvm] [k] paging64_walk_addr_generic
[......]
[......]
0.81% qemu-system-x86 [kvm] [k] gfn_to_memslotThere is the Unixbecn(the parameter is -c 10 fsdisk) test report:
Before patchset:
2090.5 1882.5 1845.2
After vtlb patch:
2237.4 2151.8 2118.0
After memslots sorted patches:
2392.5 2485.9 2416.1
PATCH 1/7 KVM: cleanup memslot_id function
We can get memslot id from memslot->id directly
--- include/linux/kvm_host.h | 7 ++++++- virt/kvm/kvm_main.c | 17 ----------------- 2 files changed, 6 insertions(+), 18 deletions(-)
include/linux/kvm_host.h +6 -1
@@ -365,7 +365,6 @@ pfn_t gfn_to_pfn_prot(struct kvm *kvm, gfn_t gfn, bool write_fault, bool *writable);pfn_t gfn_to_pfn_memslot (struct kvm *kvm,struct kvm_memory_slot *slot,gfn_t gfn); -int memslot_id (struct kvm *kvm,gfn_t gfn);void kvm_release_pfn_dirty (pfn_t );void kvm_release_pfn_clean (pfn_t pfn);void kvm_set_pfn_dirty (pfn_t pfn);
@@ -388,6 +387,12 @@ int kvm_gfn_to_hva_cache_init(struct kvm *kvm, struct gfn_to_hva_cache *ghc,int kvm_clear_guest_page (struct kvm *kvm,gfn_t gfn,int offset,int len);int kvm_clear_guest (struct kvm *kvm,gpa_t gpa,unsigned long len);struct kvm_memory_slot *gfn_to_memslot (struct kvm *kvm,gfn_t gfn); + +static inline int memslot_id (struct kvm *kvm,gfn_t gfn) +{ +return gfn_to_memslot (kvm, gfn)->id; +} +int kvm_is_visible_gfn (struct kvm *kvm,gfn_t gfn);unsigned long kvm_host_page_size (struct kvm *kvm,gfn_t gfn);void mark_page_dirty (struct kvm *kvm,gfn_t gfn);
virt/kvm/kvm_main.c +0 -17
@@ -997,23 +997,6 @@ out:return size; } -int memslot_id (struct kvm *kvm,gfn_t gfn) -{ -int i; -struct kvm_memslots *slots =kvm_memslots (kvm); -struct kvm_memory_slot *memslot =NULL ; - -for (i =0 ; i < slots->nmemslots; ++i) { - memslot = &slots->memslots[i]; - -if (gfn >= memslot->base_gfn - && gfn < memslot->base_gfn + memslot->npages) -break ; - } - -return memslot - slots->memslots; -} -static unsigned long gfn_to_hva_many (struct kvm_memory_slot *slot,gfn_t gfn,gfn_t *nr_pages) {
Signature · 1 line
1.7.4
PATCH 2/7 KVM: introduce KVM_MEM_SLOTS_NUM macro
In reply to Xiao GuangrongIntroduce KVM_MEM_SLOTS_NUM macro to instead ofKVM_MEMORY_SLOTS + KVM_PRIVATE_MEM_SLOTS
--- arch/x86/include/asm/kvm_host.h | 3 ++- arch/x86/kvm/mmu.c | 2 +- include/linux/kvm_host.h | 7 +++++-- virt/kvm/kvm_main.c | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-)
arch/x86/include/asm/kvm_host.h +2 -1
@@ -30,6 +30,7 @@#define KVM_MEMORY_SLOTS 32/* memory slots that does not exposed to userspace */ #define KVM_PRIVATE_MEM_SLOTS 4 +#define KVM_MEM_SLOTS_NUM (KVM_MEMORY_SLOTS + KVM_PRIVATE_MEM_SLOTS)#define KVM_PIO_PAGE_OFFSET 1#define KVM_COALESCED_MMIO_PAGE_OFFSET 2
@@ -207,7 +208,7 @@ struct kvm_mmu_page {* One bit set per slot which has memory * in this shadow page. */ -DECLARE_BITMAP (slot_bitmap,KVM_MEMORY_SLOTS +KVM_PRIVATE_MEM_SLOTS ); +DECLARE_BITMAP (slot_bitmap,KVM_MEM_SLOTS_NUM );bool multimapped;/* More than one parent_pte? */ bool unsync;int root_count;/* Currently serving as active root */
arch/x86/kvm/mmu.c +1 -1
@@ -1056,7 +1056,7 @@ static struct kvm_mmu_page *kvm_mmu_alloc_page(struct kvm_vcpu *vcpu, PAGE_SIZE);set_page_private (virt_to_page (sp->spt), (unsigned long )sp);list_add (&sp->link, &vcpu->kvm->arch.active_mmu_pages); -bitmap_zero (sp->slot_bitmap,KVM_MEMORY_SLOTS +KVM_PRIVATE_MEM_SLOTS ); +bitmap_zero (sp->slot_bitmap,KVM_MEM_SLOTS_NUM ); sp->multimapped =0 ; sp->parent_pte = parent_pte;kvm_mod_used_mmu_pages (vcpu->kvm,+1 );
include/linux/kvm_host.h +5 -2
@@ -217,11 +217,14 @@ struct kvm_irq_routing_table {};#endif +#ifndef KVM_MEM_SLOTS_NUM +#define KVM_MEM_SLOTS_NUM (KVM_MEMORY_SLOTS + KVM_PRIVATE_MEM_SLOTS) +#endif +struct kvm_memslots {int nmemslots;u64 generation; -struct kvm_memory_slot memslots[KVM_MEMORY_SLOTS + -KVM_PRIVATE_MEM_SLOTS ]; +struct kvm_memory_slot memslots[KVM_MEM_SLOTS_NUM ]; };struct kvm {
virt/kvm/kvm_main.c +1 -1
@@ -651,7 +651,7 @@ int __kvm_set_memory_region(struct kvm *kvm, goto out;if (user_alloc && (mem->userspace_addr & (PAGE_SIZE -1 ))) goto out; -if (mem->slot >=KVM_MEMORY_SLOTS +KVM_PRIVATE_MEM_SLOTS ) +if (mem->slot >=KVM_MEM_SLOTS_NUM ) goto out;if (mem->guest_phys_addr + mem->memory_size < mem->guest_phys_addr) goto out;
Signature · 1 line
1.7.4
PATCH 1/3 KVM: introduce memslots_updated function
In reply to Xiao GuangrongIntroduce memslots_updated function which is called after memslots updated
--- arch/x86/kvm/x86.c | 2 +- include/linux/kvm_host.h | 1 + virt/kvm/kvm_main.c | 15 +++++++++------ 3 files changed, 11 insertions(+), 7 deletions(-)
arch/x86/kvm/x86.c +1 -1
@@ -3252,7 +3252,7 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, goto out;memcpy (slots, kvm->memslots,sizeof (struct kvm_memslots )); slots->memslots[log->slot].dirty_bitmap = dirty_bitmap; - slots->generation++; +memslots_updated (slots, log->slot); old_slots = kvm->memslots;rcu_assign_pointer (kvm->memslots, slots);
include/linux/kvm_host.h +1 -0
@@ -331,6 +331,7 @@ int is_error_pfn(pfn_t pfn);int is_hwpoison_pfn (pfn_t pfn);int is_fault_pfn (pfn_t pfn);int kvm_is_error_hva (unsigned long addr); +void memslots_updated (struct kvm_memslots *slots,int slot_id);int kvm_set_memory_region (struct kvm *kvm,struct kvm_userspace_memory_region *mem,int user_alloc);
virt/kvm/kvm_main.c +9 -6
@@ -623,6 +623,13 @@ static int kvm_create_dirty_bitmap(struct kvm_memory_slot *memslot) }#endif /* !CONFIG_S390 */ +void memslots_updated (struct kvm_memslots *slots,int slot_id) +{ +if (slot_id >= slots->nmemslots) + slots->nmemslots = slot_id +1 ; + slots->generation++; +} +/* * Allocate some memory and give it an address in the guest physical address * space.
@@ -768,10 +775,8 @@ skip_lpage:if (!slots) goto out_free;memcpy (slots, kvm->memslots,sizeof (struct kvm_memslots )); -if (mem->slot >= slots->nmemslots) - slots->nmemslots = mem->slot +1 ; - slots->generation++; slots->memslots[mem->slot].flags |=KVM_MEMSLOT_INVALID ; +memslots_updated (slots, mem->slot); old_memslots = kvm->memslots;rcu_assign_pointer (kvm->memslots, slots);
@@ -803,9 +808,6 @@ skip_lpage:if (!slots) goto out_free;memcpy (slots, kvm->memslots,sizeof (struct kvm_memslots )); -if (mem->slot >= slots->nmemslots) - slots->nmemslots = mem->slot +1 ; - slots->generation++;/* actual memory is freed via old in kvm_free_physmem_slot below */ if (!npages) {
@@ -816,6 +818,7 @@ skip_lpage: } slots->memslots[mem->slot] = new; +memslots_updated (slots, mem->slot); old_memslots = kvm->memslots;rcu_assign_pointer (kvm->memslots, slots);synchronize_srcu_expedited (&kvm->srcu);
Signature · 1 line
1.7.4
PATCH 4/7 KVM: sort memslots and use binary search to search the right slot
In reply to Xiao GuangrongSort memslots then search the slot with binary search to speed up the slot searching
--- include/linux/kvm_host.h | 2 + virt/kvm/kvm_main.c | 85 ++++++++++++++++++++++++++++++++++------------ 2 files changed, 65 insertions(+), 22 deletions(-)
include/linux/kvm_host.h +2 -0
@@ -223,8 +223,10 @@ struct kvm_irq_routing_table {};struct kvm_memslots {int nmemslots; +int used_slots;u64 generation;struct kvm_memory_slot memslots[KVM_MEM_SLOTS_NUM ]; +struct kvm_memory_slot *slots_sort[KVM_MEM_SLOTS_NUM ]; };struct kvm {
virt/kvm/kvm_main.c +63 -22
116 lines not on this page · raw message
Signature · 1 line
1.7.4
Re: [PATCH 4/7] KVM: sort memslots and use binary search to search the right slot
Quoted 3 lines · Xiao Guangrong
Sort memslots then search the slot with binary search to speed up the slot searching
I'm not sure if a binary search is the right algorithm here. It introduces a lot of branches which may be mispredicted.
Options we've discussed are:
- Sort slots by size, use linear search (so the largest slots are found quickly)
- Weighted balanced tree http://en.wikipedia.org/wiki/Weight-balanced_tree, use weight == slot size
Both options still make the miss case (mmio) slow. We could cache the result of a miss in an spte by using a reserved bit, and checking the page fault error code (or seeing if we get an ept violation or ept misconfiguration), so if we get repeated mmio on a page, we don't need to search the slot list/tree.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 4/7] KVM: sort memslots and use binary search to search the right slot
Quoted 8 lines · Avi Kivity
Quoted 3 lines · Xiao Guangrong
Sort memslots then search the slot with binary search to speed up the slot searching
I'm not sure if a binary search is the right algorithm here. It introduces a lot of branches which may be mispredicted.
Quoted 6 lines
Options we've discussed are:
- Sort slots by size, use linear search (so the largest slots are found quickly)
- Weighted balanced tree
http://en.wikipedia.org/wiki/Weight-balanced_tree, use weight == slot size
I've got an implementation using a weight balanced tree working now. I need to do some testing to see if I can detect any performance difference from the current unsorted, linear array.
Quoted 5 lines
Both options still make the miss case (mmio) slow. We could cache the result of a miss in an spte by using a reserved bit, and checking the page fault error code (or seeing if we get an ept violation or ept misconfiguration), so if we get repeated mmio on a page, we don't need to search the slot list/tree.
I haven't started on this idea yet. Thanks,
Alex
cover Weight-balanced binary tree + KVM growable memory slots using wbtree
This series introduces a new weight-balanced binary tree (wbtree) for general use. It's largely leveraged from the rbtree, copying it's rotate functions, while introducing different rebalance and erase functions. This tree is particularly useful for managing memory ranges, where it's desirable to have the most likely targets (the largest ranges) at the top of each subtree.
Patches 2 & 3 go on to convert the KVM memory slots to a growable array and make use of wbtree for efficient managment. Trying to exercise the worst case for this data structure, I ran netperf TCP_RR on an emulated rtl8139 NIC connected directly to the host via a tap. Both qemu-kvm and the netserver on the host were pinned to optimal CPUs with taskset. This series resulted in a 3% improvement for this test.
Note that part of why this series is RFC is that the print_tree function in the last patch is debug code that generates output for dot. You can copy the output to a file and run:
dot -Tpdf foo.dot > foo.pdf
to generate a nice diagram of the tree currently in use. I'll follow-up with a few examples. Thanks,
Alex
---
Alex Williamson (3):
kvm: Use weight-balanced tree for memory slot management
kvm: Allow memory slot array to grow on demand Weight-balanced tree
arch/ia64/include/asm/kvm_host.h | 4 - arch/ia64/kvm/kvm-ia64.c | 2 arch/powerpc/include/asm/kvm_host.h | 3 arch/s390/include/asm/kvm_host.h | 3 arch/x86/include/asm/kvm_host.h | 5 - arch/x86/include/asm/vmx.h | 6 - arch/x86/kvm/mmu.c | 32 ++++- arch/x86/kvm/x86.c | 6 - include/linux/kvm_host.h | 25 ++++ include/linux/wbtree.h | 55 +++++++++ lib/Makefile | 3 lib/wbtree.c | 170 ++++++++++++++++++++++++++ virt/kvm/kvm_main.c | 226 +++++++++++++++++++++++++++-------- 13 files changed, 464 insertions(+), 76 deletions(-) create mode 100644 include/linux/wbtree.h create mode 100644 lib/wbtree.c
RFC PATCH 1/3 Weight-balanced tree
---
include/linux/wbtree.h | 55 ++++++++++++++++ lib/Makefile | 3 + lib/wbtree.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 227 insertions(+), 1 deletions(-) create mode 100644 include/linux/wbtree.h create mode 100644 lib/wbtree.c
include/linux/wbtree.h new file +55 -0
@@ -0,0 +1,55 @@ +/* +* Weight-balanced binary tree +* +* The balance of this tree is based on search probability. The +* heaviest weighted nodes (the ones we're most likely to hit), are +* at the top of each subtree. +* +* Copywrite (C) 2011 Red Hat, Inc. +* +* Authors: +* Alex Williamson <[email protected]> +* +* This work is licensed under the terms of the GNU GPL, version 2. See +* the COPYING file in the top-level directory. +*/ +#ifndef _LINUX_WBTREE_H +#define _LINUX_WBTREE_H + +#include <linux/kernel.h> +#include <linux/stddef.h> + +struct wb_node { +struct wb_node *wb_parent; +struct wb_node *wb_left; +struct wb_node *wb_right; +unsigned long wb_weight; +}; + +struct wb_root { +struct wb_node *wb_node; +}; + +#define WB_ROOT (struct wb_root) { NULL, } +#define wb_entry (ptr, type, member) container_of(ptr, type, member) + +extern void wb_rebalance (struct wb_node *node,struct wb_root *root); +extern void wb_erase (struct wb_node *node,struct wb_root *root); +extern struct wb_node *wb_first (struct wb_root *root); +extern struct wb_node *wb_last (struct wb_root *root); +extern struct wb_node *wb_next (struct wb_node *node); +extern struct wb_node *wb_prev (struct wb_node *node); + +static inline void wb_set_weight (struct wb_node *node,unsigned long weight) +{ + node->wb_weight = weight; +} + +static inline void wb_link_node (struct wb_node *node,struct wb_node *parent, +struct wb_node **wb_link) +{ + node->wb_left = node->wb_right =NULL ; + node->wb_parent = parent; + *wb_link = node; +} +#endif /* _LINUX_WBTREE_H */
lib/Makefile +2 -1
@@ -21,7 +21,8 @@ lib-y += kobject.o kref.o klist.o obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \ bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \ - string_helpers.o gcd.o lcm.o list_sort.o uuid.o flex_array.o + string_helpers.o gcd.o lcm.o list_sort.o uuid.o flex_array.o \ + wbtree.o ifeq ($(CONFIG_DEBUG_KOBJECT),y) CFLAGS_kobject.o += -DDEBUG
lib/wbtree.c new file +170 -0
170 lines not on this page · raw message
Re: [RFC PATCH 1/3] Weight-balanced tree
Quoted 10 lines · Alex Williamson
Signed-off-by Alex Williamson<[email protected]>---
include/linux/wbtree.h | 55 ++++++++++++++++ lib/Makefile | 3 + lib/wbtree.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 227 insertions(+), 1 deletions(-)create mode 100644
include/linux/wbtree.h
create mode 100644lib/wbtree.c
Andrew, can we add something like this to the tree? Can it go through the kvm tree?
Quoted 66 lines
include/linux/wbtree.h new file +55
-0@@ -0,0 +1,55 @@ +/* +* Weight-balanced binary tree +* +* The balance of this tree is based on search probability. The +* heaviest weighted nodes (the ones we're most likely to hit), are +* at the top of each subtree. +* +* Copywrite (C) 2011 Red Hat, Inc. +* +* Authors: +* Alex Williamson<[email protected]> +* +* This work is licensed under the terms of the GNU GPL, version 2. See +* the COPYING file in the top-level directory. +*/ +#ifndef _LINUX_WBTREE_H +#define _LINUX_WBTREE_H + +#include <linux/kernel.h> +#include <linux/stddef.h> + +struct wb_node { +struct wb_node *wb_parent; +struct wb_node *wb_left; +struct wb_node *wb_right; +unsigned long wb_weight; +}; + +struct wb_root { +struct wb_node *wb_node; +}; + +#define WB_ROOT (struct wb_root) { NULL, } +#define wb_entry (ptr, type, member) container_of(ptr, type, member) + +extern void wb_rebalance (struct wb_node *node,struct wb_root *root); +extern void wb_erase (struct wb_node *node,struct wb_root *root); +extern struct wb_node *wb_first (struct wb_root *root); +extern struct wb_node *wb_last (struct wb_root *root); +extern struct wb_node *wb_next (struct wb_node *node); +extern struct wb_node *wb_prev (struct wb_node *node); + +static inline void wb_set_weight (struct wb_node *node,unsigned long weight) +{ + node->wb_weight = weight; +} + +static inline void wb_link_node (struct wb_node *node,struct wb_node *parent, +struct wb_node **wb_link) +{ + node->wb_left = node->wb_right =NULL ; + node->wb_parent = parent; + *wb_link = node; +} +#endif /* _LINUX_WBTREE_H */ lib/Makefile +0
-0
lib/Makefile lib-y += kobject.o kref.o klist.o · line 26
Quoted 7 lines
lib/Makefile +2
-1@@ -21,7 +21,8 @@ lib-y += kobject.o kref.o klist.o obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \ bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \ - string_helpers.o gcd.o lcm.o list_sort.o uuid.o flex_array.o + string_helpers.o gcd.o lcm.o list_sort.o uuid.o flex_array.o \ + wbtree.o
obj-$(CONFIG_WEIGHT_BALANCED_TREE) += wbtree.o
then kvm can select it, and the impact on non-kvm kernels is removed.
Quoted 179 lines
lib/Makefile +0
-0ifeq ($(CONFIG_DEBUG_KOBJECT),y) CFLAGS_kobject.o += -DDEBUGlib/wbtree.c new file +170
-0@@ -0,0 +1,170 @@ +/* +* Weight-balanced binary tree +* +* The balance of this tree is based on search probability. The +* heaviest weighted nodes (the ones we're most likely to hit), are +* at the top of each subtree. +* +* Copywrite (C) 2011 Red Hat, Inc. +* +* Authors: +* Alex Williamson<[email protected]> +* +* This work is licensed under the terms of the GNU GPL, version 2. See +* the COPYING file in the top-level directory. +*/ +#include <linux/wbtree.h> +#include <linux/module.h> + +static void __wb_rotate_left (struct wb_node *node,struct wb_root *root) +{ +struct wb_node *right = node->wb_right; +struct wb_node *parent = node->wb_parent; + +if ((node->wb_right = right->wb_left)) + right->wb_left->wb_parent = node; + right->wb_left = node; + + right->wb_parent = parent; + +if (parent) { +if (node == parent->wb_left) + parent->wb_left = right; +else + parent->wb_right = right; + }else + root->wb_node = right; + + node->wb_parent = right; +} + +static void __wb_rotate_right (struct wb_node *node,struct wb_root *root) +{ +struct wb_node *left = node->wb_left; +struct wb_node *parent = node->wb_parent; + +if ((node->wb_left = left->wb_right)) + left->wb_right->wb_parent = node; + left->wb_right = node; + + left->wb_parent = parent; + +if (parent) { +if (node == parent->wb_right) + parent->wb_right = left; +else + parent->wb_left = left; + }else + root->wb_node = left; + + node->wb_parent = left; +} + +void wb_rebalance (struct wb_node *node,struct wb_root *root) +{ +while (node->wb_parent&& node->wb_parent->wb_weight< node->wb_weight) { +if (node == node->wb_parent->wb_left) +__wb_rotate_right (node->wb_parent, root); +else +__wb_rotate_left (node->wb_parent, root); + } +} +EXPORT_SYMBOL (wb_rebalance); + +void wb_erase (struct wb_node *node,struct wb_root *root) +{ +while (node->wb_left || node->wb_right) { +if (!node->wb_left) +__wb_rotate_left (node, root); +else if (!node->wb_right) +__wb_rotate_right (node, root); +else { +if (node->wb_left->wb_weight> + node->wb_right->wb_weight) +__wb_rotate_right (node, root); +else +__wb_rotate_left (node, root); + } + } + +if (node->wb_parent) { +if (node->wb_parent->wb_left == node) + node->wb_parent->wb_left =NULL ; +else + node->wb_parent->wb_right =NULL ; + }else + root->wb_node =NULL ; +} +EXPORT_SYMBOL (wb_erase); + +struct wb_node *wb_first (struct wb_root *root) +{ +struct wb_node *node; + + node = root->wb_node; +if (!node) +return NULL ; + +while (node->wb_left) + node = node->wb_left; + +return node; +} +EXPORT_SYMBOL (wb_first); + +struct wb_node *wb_last (struct wb_root *root) +{ +struct wb_node *node; + + node = root->wb_node; +if (!node) +return NULL ; + +while (node->wb_right) + node = node->wb_right; + +return node; +} +EXPORT_SYMBOL (wb_last); + +struct wb_node *wb_next (struct wb_node *node) +{ +struct wb_node *parent; + +if (node->wb_parent == node) +return NULL ; + +if (node->wb_right) { + node = node->wb_right; +while (node->wb_left) + node = node->wb_left; +return node; + } + +while ((parent = node->wb_parent)&& node == parent->wb_right) + node = parent; + +return parent; +} +EXPORT_SYMBOL (wb_next); + +struct wb_node *wb_prev (struct wb_node *node) +{ +struct wb_node *parent; + +if (node->wb_parent == node) +return NULL ; + +if (node->wb_left) { + node = node->wb_left; +while (node->wb_right) + node = node->wb_right; +return node; + } + +while ((parent = node->wb_parent)&& node == parent->wb_left) + node = parent; + +return parent; +} +EXPORT_SYMBOL (wb_prev);
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [RFC PATCH 1/3] Weight-balanced tree
Quoted 91 lines · Avi Kivity
Quoted 10 lines · Alex Williamson
Signed-off-by Alex Williamson<[email protected]>---
include/linux/wbtree.h | 55 ++++++++++++++++ lib/Makefile | 3 + lib/wbtree.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 227 insertions(+), 1 deletions(-)create mode 100644
include/linux/wbtree.h
create mode 100644lib/wbtree.cAndrew, can we add something like this to the tree? Can it go through the kvm tree?
Quoted 66 lines
include/linux/wbtree.h new file +55
-0@@ -0,0 +1,55 @@ +/* +* Weight-balanced binary tree +* +* The balance of this tree is based on search probability. The +* heaviest weighted nodes (the ones we're most likely to hit), are +* at the top of each subtree. +* +* Copywrite (C) 2011 Red Hat, Inc. +* +* Authors: +* Alex Williamson<[email protected]> +* +* This work is licensed under the terms of the GNU GPL, version 2. See +* the COPYING file in the top-level directory. +*/ +#ifndef _LINUX_WBTREE_H +#define _LINUX_WBTREE_H + +#include <linux/kernel.h> +#include <linux/stddef.h> + +struct wb_node { +struct wb_node *wb_parent; +struct wb_node *wb_left; +struct wb_node *wb_right; +unsigned long wb_weight; +}; + +struct wb_root { +struct wb_node *wb_node; +}; + +#define WB_ROOT (struct wb_root) { NULL, } +#define wb_entry (ptr, type, member) container_of(ptr, type, member) + +extern void wb_rebalance (struct wb_node *node,struct wb_root *root); +extern void wb_erase (struct wb_node *node,struct wb_root *root); +extern struct wb_node *wb_first (struct wb_root *root); +extern struct wb_node *wb_last (struct wb_root *root); +extern struct wb_node *wb_next (struct wb_node *node); +extern struct wb_node *wb_prev (struct wb_node *node); + +static inline void wb_set_weight (struct wb_node *node,unsigned long weight) +{ + node->wb_weight = weight; +} + +static inline void wb_link_node (struct wb_node *node,struct wb_node *parent, +struct wb_node **wb_link) +{ + node->wb_left = node->wb_right =NULL ; + node->wb_parent = parent; + *wb_link = node; +} +#endif /* _LINUX_WBTREE_H */ lib/Makefile +0
-0lib/Makefile lib-y += kobject.o kref.o klist.o · line 26
Quoted 7 lines
lib/Makefile +2
-1@@ -21,7 +21,8 @@ lib-y += kobject.o kref.o klist.o obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \ bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \ - string_helpers.o gcd.o lcm.o list_sort.o uuid.o flex_array.o + string_helpers.o gcd.o lcm.o list_sort.o uuid.o flex_array.o \ + wbtree.oobj-$(CONFIG_WEIGHT_BALANCED_TREE) += wbtree.o
Quoted 1 line
then kvm can select it, and the impact on non-kvm kernels is removed.
Then we'd have issues trying to build an external kvm module for a pre-existing non-kvm kernel. Do we care? If we were to take such a path, I think the default should be on, kvm would depend on it, but we could add an option to disable it for EMBEDDED/EXPERT. Thanks,
Alex
Re: [RFC PATCH 1/3] Weight-balanced tree
Quoted 7 lines · Alex Williamson
Quoted 4 lines
obj-$(CONFIG_WEIGHT_BALANCED_TREE) += wbtree.o
then kvm can select it, and the impact on non-kvm kernels is removed.
Then we'd have issues trying to build an external kvm module for a pre-existing non-kvm kernel. Do we care?
Officially, no.
What we typically do in these cases is copy the code into the kvm-kmod compatibility layer and compile it if the kernel doesn't supply it (like all older kernels regardless of config).
Quoted 3 lines
If we were to take such a
path, I think the default should be on, kvm would depend on it, but we could add an option to disable it for EMBEDDED/EXPERT. Thanks,
That would work as well.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [RFC PATCH 1/3] Weight-balanced tree
Quoted 15 lines · Avi Kivity
Quoted 7 lines · Alex Williamson
Quoted 4 lines
obj-$(CONFIG_WEIGHT_BALANCED_TREE) += wbtree.o
then kvm can select it, and the impact on non-kvm kernels is removed.
Then we'd have issues trying to build an external kvm module for a pre-existing non-kvm kernel. Do we care?
Officially, no.
What we typically do in these cases is copy the code into the kvm-kmod compatibility layer and compile it if the kernel doesn't supply it (like all older kernels regardless of config).
Quoted 5 lines
Quoted 3 lines
If we were to take such a
path, I think the default should be on, kvm would depend on it, but we could add an option to disable it for EMBEDDED/EXPERT. Thanks,That would work as well.
In retrospect, I suppose it doesn't really make sense to have it compiled in "just in case". I'll follow your first suggestion. Thanks,
Alex
Andrew Morton
maintainerRe: [RFC PATCH 1/3] Weight-balanced tree
In reply to Alex WilliamsonQuoted 40 lines · On Tue, 22 Feb 2011 11:55:05 -0700 Alex Williamson…
Signed-off-by Alex Williamson <[email protected]>---
include/linux/wbtree.h | 55 ++++++++++++++++ lib/Makefile | 3 + lib/wbtree.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 227 insertions(+), 1 deletions(-) create mode 100644 include/linux/wbtree.h create mode 100644 lib/wbtree.cinclude/linux/wbtree.h new file +24
-0@@ -0,0 +1,55 @@ +/* +* Weight-balanced binary tree +* +* The balance of this tree is based on search probability. The +* heaviest weighted nodes (the ones we're most likely to hit), are +* at the top of each subtree. +* +* Copywrite (C) 2011 Red Hat, Inc. +* +* Authors: +* Alex Williamson <[email protected]> +* +* This work is licensed under the terms of the GNU GPL, version 2. See +* the COPYING file in the top-level directory. +*/ +#ifndef _LINUX_WBTREE_H +#define _LINUX_WBTREE_H + +#include <linux/kernel.h> +#include <linux/stddef.h> + +struct wb_node { + struct wb_node *wb_parent; + struct wb_node *wb_left;
include/linux/wbtree.h line 34
Quoted 10 lines
include/linux/wbtree.h new file +10
-0+struct wb_node *wb_right; +unsigned long wb_weight; +}; + +struct wb_root { +struct wb_node *wb_node; +}; + +#define WB_ROOT (struct wb_root) { NULL, } +#define wb_entry (ptr, type, member) container_of(ptr, type, member)
Please implement this as a C function. That way we get typechecking which container_of() is unable to provide.
Otherwise the code looks OK to me, apart from the total lack of documentation. Please document at least all the exported interfaces. The documentation should be designed to tell people how to maintain this code, and how to use this code reliably and well from other subsystems. Don't fall into the trap of just dumbly filling out templates.
The documentation should also carefully explain the locking requirements which these functions place upon callers. (rwlocks used how? rcu?)
Once that is all squared away, please merge the code via the KVM tree.
RFC PATCH 2/3 kvm: Allow memory slot array to grow on demand
In reply to Alex WilliamsonRemove fixed KVM_MEMORY_SLOTS limit, allowing the slot array to grow on demand. Private slots are now allocated at the front instead of the end. Only x86 seems to use private slots, so this is now zero for all other archs. The memslots pointer is already updated using rcu, so changing the size off the array when it's replaces is straight forward. x86 also keeps a bitmap of slots used by a kvm_mmu_page, which requires a shadow tlb flush whenever we increase the number of slots. This forces the pages to be rebuilt with the new bitmap size.
---
arch/ia64/include/asm/kvm_host.h | 4 -- arch/ia64/kvm/kvm-ia64.c | 2 + arch/powerpc/include/asm/kvm_host.h | 3 -- arch/s390/include/asm/kvm_host.h | 3 -- arch/x86/include/asm/kvm_host.h | 5 +-- arch/x86/include/asm/vmx.h | 6 ++- arch/x86/kvm/mmu.c | 32 +++++++++++++++-- arch/x86/kvm/x86.c | 6 ++- include/linux/kvm_host.h | 22 +++++++++++- virt/kvm/kvm_main.c | 65 +++++++++++++++++++++++++---------- 10 files changed, 103 insertions(+), 45 deletions(-)
arch/ia64/include/asm/kvm_host.h +0 -4
@@ -23,10 +23,6 @@#ifndef __ASM_KVM_HOST_H#define __ASM_KVM_HOST_H -#define KVM_MEMORY_SLOTS 32 -/* memory slots that does not exposed to userspace */ -#define KVM_PRIVATE_MEM_SLOTS 4 -#define KVM_COALESCED_MMIO_PAGE_OFFSET 1/* define exit reasons from vmm to kvm*/
arch/ia64/kvm/kvm-ia64.c +1 -1
@@ -1814,7 +1814,7 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,mutex_lock (&kvm->slots_lock); r = -EINVAL ; -if (log->slot >=KVM_MEMORY_SLOTS ) +if (log->slot >= kvm->memslots->nmemslots) goto out; memslot = &kvm->memslots->memslots[log->slot];
arch/powerpc/include/asm/kvm_host.h +0 -3
@@ -29,9 +29,6 @@#include <asm/kvm_asm.h> #define KVM_MAX_VCPUS 1 -#define KVM_MEMORY_SLOTS 32 -/* memory slots that does not exposed to userspace */ -#define KVM_PRIVATE_MEM_SLOTS 4#define KVM_COALESCED_MMIO_PAGE_OFFSET 1
arch/s390/include/asm/kvm_host.h +0 -3
@@ -20,9 +20,6 @@#include <asm/cpu.h> #define KVM_MAX_VCPUS 64 -#define KVM_MEMORY_SLOTS 32 -/* memory slots that does not exposed to userspace */ -#define KVM_PRIVATE_MEM_SLOTS 4struct sca_entry {atomic_t scn;
arch/x86/include/asm/kvm_host.h +2 -3
@@ -27,9 +27,8 @@#include <asm/msr-index.h> #define KVM_MAX_VCPUS 64 -#define KVM_MEMORY_SLOTS 32/* memory slots that does not exposed to userspace */ -#define KVM_PRIVATE_MEM_SLOTS 4 +#define KVM_PRIVATE_MEM_SLOTS 3#define KVM_PIO_PAGE_OFFSET 1#define KVM_COALESCED_MMIO_PAGE_OFFSET 2
@@ -207,7 +206,7 @@ struct kvm_mmu_page {* One bit set per slot which has memory * in this shadow page. */ -DECLARE_BITMAP (slot_bitmap,KVM_MEMORY_SLOTS +KVM_PRIVATE_MEM_SLOTS ); +unsigned long *slot_bitmap;bool multimapped;/* More than one parent_pte? */ bool unsync;int root_count;/* Currently serving as active root */
arch/x86/include/asm/vmx.h +3 -3
@@ -370,9 +370,9 @@ enum vmcs_field {#define AR_RESERVD_MASK 0xfffe0f00 -#define TSS_PRIVATE_MEMSLOT (KVM_MEMORY_SLOTS + 0) -#define APIC_ACCESS_PAGE_PRIVATE_MEMSLOT (KVM_MEMORY_SLOTS + 1) -#define IDENTITY_PAGETABLE_PRIVATE_MEMSLOT (KVM_MEMORY_SLOTS + 2) +#define TSS_PRIVATE_MEMSLOT 0 +#define APIC_ACCESS_PAGE_PRIVATE_MEMSLOT 1 +#define IDENTITY_PAGETABLE_PRIVATE_MEMSLOT 2#define VMX_NR_VPIDS (1 << 16)#define VMX_VPID_EXTENT_SINGLE_CONTEXT 1
arch/x86/kvm/mmu.c +28 -4
@@ -1029,9 +1029,13 @@ static inline void kvm_mod_used_mmu_pages(struct kvm *kvm, int nr)static void kvm_mmu_free_page (struct kvm *kvm,struct kvm_mmu_page *sp) { +struct kvm_memslots *slots =kvm_memslots (kvm); +ASSERT (is_empty_shadow_page (sp->spt));hlist_del (&sp->hash_link);list_del (&sp->link); +if (unlikely (slots->nmemslots >sizeof (sp->slot_bitmap) *8 )) +kfree (sp->slot_bitmap);__free_page (virt_to_page (sp->spt));if (!sp->role.direct)__free_page (virt_to_page (sp->gfns));
@@ -1048,6 +1052,7 @@ static struct kvm_mmu_page *kvm_mmu_alloc_page(struct kvm_vcpu *vcpu, u64 *parent_pte, int direct) {struct kvm_mmu_page *sp; +struct kvm_memslots *slots =kvm_memslots (vcpu->kvm); sp =mmu_memory_cache_alloc (&vcpu->arch.mmu_page_header_cache,sizeof *sp); sp->spt =mmu_memory_cache_alloc (&vcpu->arch.mmu_page_cache,PAGE_SIZE );
@@ -1056,7 +1061,16 @@ static struct kvm_mmu_page *kvm_mmu_alloc_page(struct kvm_vcpu *vcpu, PAGE_SIZE);set_page_private (virt_to_page (sp->spt), (unsigned long )sp);list_add (&sp->link, &vcpu->kvm->arch.active_mmu_pages); -bitmap_zero (sp->slot_bitmap,KVM_MEMORY_SLOTS +KVM_PRIVATE_MEM_SLOTS ); + +if (unlikely (slots->nmemslots >sizeof (sp->slot_bitmap) *8 )) { + sp->slot_bitmap =kzalloc (sizeof (long ) * +BITS_TO_LONGS (slots->nmemslots), +GFP_KERNEL ); +if (!sp->slot_bitmap) +return NULL ; + }else +bitmap_zero ((void *)&sp->slot_bitmap, slots->nmemslots); + sp->multimapped =0 ; sp->parent_pte = parent_pte;kvm_mod_used_mmu_pages (vcpu->kvm,+1 );
@@ -1817,8 +1831,12 @@ static void page_header_update_slot(struct kvm *kvm, void *pte, gfn_t gfn) {int slot =memslot_id (kvm, gfn);struct kvm_mmu_page *sp =page_header (__pa (pte)); +struct kvm_memslots *slots =kvm_memslots (kvm); -__set_bit (slot, sp->slot_bitmap); +if (likely (slots->nmemslots <=sizeof (sp->slot_bitmap) *8 )) +__set_bit (slot, (void *)&sp->slot_bitmap); +else +__set_bit (slot, sp->slot_bitmap); }static void mmu_convert_notrap (struct kvm_mmu_page *sp)
@@ -3530,13 +3548,19 @@ int kvm_mmu_setup(struct kvm_vcpu *vcpu)void kvm_mmu_slot_remove_write_access (struct kvm *kvm,int slot) {struct kvm_mmu_page *sp; +struct kvm_memslots *slots =kvm_memslots (kvm);list_for_each_entry (sp, &kvm->arch.active_mmu_pages, link) {int i;u64 *pt; -if (!test_bit (slot, sp->slot_bitmap)) -continue ; +if (likely (slots->nmemslots <=sizeof (sp->slot_bitmap) *8 )) { +if (!test_bit (slot, (void *)&sp->slot_bitmap)) +continue ; + }else { +if (!test_bit (slot, sp->slot_bitmap)) +continue ; + } pt = sp->spt;for (i =0 ; i <PT64_ENT_PER_PAGE ; ++i) {
arch/x86/kvm/x86.c +3 -3
@@ -1978,7 +1978,7 @@ int kvm_dev_ioctl_check_extension(long ext) r =KVM_MAX_VCPUS ;break ;case KVM_CAP_NR_MEMSLOTS : - r =KVM_MEMORY_SLOTS ; + r =KVM_MAX_MEM_SLOTS -KVM_PRIVATE_MEM_SLOTS ;break ;case KVM_CAP_PV_MMU :/* obsolete */ r =0 ;
@@ -3201,7 +3201,7 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,mutex_lock (&kvm->slots_lock); r = -EINVAL ; -if (log->slot >=KVM_MEMORY_SLOTS ) +if (log->slot >= kvm->memslots->nmemslots) goto out; memslot = &kvm->memslots->memslots[log->slot];
@@ -6068,7 +6068,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,int map_flags =MAP_PRIVATE |MAP_ANONYMOUS ;/* Prevent internal slot pages from being moved by fork()/COW. */ -if (memslot->id >=KVM_MEMORY_SLOTS ) +if (memslot->id <KVM_PRIVATE_MEM_SLOTS ) map_flags =MAP_SHARED |MAP_ANONYMOUS ;/*To keep backward compatibility with older userspace,
include/linux/kvm_host.h +20 -2
@@ -28,6 +28,25 @@#include <asm/kvm_host.h> /* +* Private slots are not exposed to userspace. These are filled at the +* front of the slot array with the userspace visible 0 index starting +* immediately following. +*/ +#ifndef KVM_PRIVATE_MEM_SLOTS +#define KVM_PRIVATE_MEM_SLOTS 0 +#endif + +/* +* Protect from malicious userspace by putting an upper bound on the number +* of memory slots. This is an arbitrarily large number that still allows +* us to make pseudo-guarantees about supporting 64 assigned devices with +* plenty of slots left over. +*/ +#ifndef KVM_MAX_MEM_SLOTS +#define KVM_MAX_MEM_SLOTS 512 +#endif + +/* * vcpu->requests bit members */ #define KVM_REQ_TLB_FLUSH 0
@@ -206,8 +225,7 @@ struct kvm_irq_routing_table {};struct kvm_memslots {int nmemslots;u64 generation; -struct kvm_memory_slot memslots[KVM_MEMORY_SLOTS + -KVM_PRIVATE_MEM_SLOTS ]; +struct kvm_memory_slot memslots[]; };struct kvm {
virt/kvm/kvm_main.c +46 -19
155 lines not on this page · raw message
Re: [RFC PATCH 2/3] kvm: Allow memory slot array to grow on demand
Quoted 15 lines · Alex Williamson
Remove fixed
KVM_MEMORY_SLOTSlimit, allowing the slot array to grow on demand. Private slots are now allocated at the front instead of the end. Only x86 seems to use private slots, so this is now zero for all other archs. The memslots pointer is already updated using rcu, so changing the size off the array when it's replaces is straight forward. x86 also keeps a bitmap of slots used by akvm_mmu_page, which requires a shadow tlb flush whenever we increase the number of slots. This forces the pages to be rebuilt with the new bitmap size.#define KVM_PIO_PAGE_OFFSET 1 #define KVM_COALESCED_MMIO_PAGE_OFFSET 2
arch/x86/include/asm/kvm_host.h struct kvm_mmu_page { · line 209
Quoted 5 lines
arch/x86/include/asm/kvm_host.h +1
-1@@ -207,7 +206,7 @@ struct kvm_mmu_page {* One bit set per slot which has memory * in this shadow page. */ -DECLARE_BITMAP (slot_bitmap,KVM_MEMORY_SLOTS +KVM_PRIVATE_MEM_SLOTS ); +unsigned long *slot_bitmap;
What about
union {
DECLARE_BITMAP(direct_slot_bitmap, BITS_PER_LONG);
unsigned long *indirect_slot_bitmap;
};to make the hackery below more explicit?
Quoted 23 lines
arch/x86/kvm/mmu.c +5
-0static void kvm_mmu_free_page (struct kvm *kvm,struct kvm_mmu_page *sp) { +struct kvm_memslots *slots =kvm_memslots (kvm); +ASSERT (is_empty_shadow_page (sp->spt));hlist_del (&sp->hash_link);list_del (&sp->link); +if (unlikely (slots->nmemslots>sizeof (sp->slot_bitmap) *8 )) +kfree (sp->slot_bitmap);__free_page (virt_to_page (sp->spt));if (!sp->role.direct)__free_page (virt_to_page (sp->gfns));@@ -1048,6 +1052,7 @@ static struct kvm_mmu_page *kvm_mmu_alloc_page(struct kvm_vcpu *vcpu, u64 *parent_pte, int direct) {struct kvm_mmu_page *sp; +struct kvm_memslots *slots =kvm_memslots (vcpu->kvm); sp =mmu_memory_cache_alloc (&vcpu->arch.mmu_page_header_cache,sizeof *sp); sp->spt =mmu_memory_cache_alloc (&vcpu->arch.mmu_page_cache,PAGE_SIZE );@@ -1056,7 +1061,16 @@ static struct kvm_mmu_page *kvm_mmu_alloc_page(struct kvm_vcpu *vcpu, PAGE_SIZE);
arch/x86/kvm/mmu.c static struct kvm_mmu_page *kvm_mmu_alloc_page(struct kvm_vcpu *vcpu, · line 1070
Quoted 10 lines
arch/x86/kvm/mmu.c +7
-1set_page_private (virt_to_page (sp->spt), (unsigned long )sp);list_add (&sp->link,&vcpu->kvm->arch.active_mmu_pages); -bitmap_zero (sp->slot_bitmap,KVM_MEMORY_SLOTS +KVM_PRIVATE_MEM_SLOTS ); + +if (unlikely (slots->nmemslots>sizeof (sp->slot_bitmap) *8 )) { + sp->slot_bitmap =kzalloc (sizeof (long ) * +BITS_TO_LONGS (slots->nmemslots), +GFP_KERNEL ); +if (!sp->slot_bitmap) +return NULL ;
We don't support failing kvm_mmu_get_page(). See mmu_memory_cache_alloc() and mmu_topup_memory_caches().
Quoted 18 lines
arch/x86/kvm/mmu.c +3
-0+ }else +bitmap_zero ((void *)&sp->slot_bitmap, slots->nmemslots); +static void
mmu_convert_notrap(struct kvm_mmu_page *sp)arch/x86/kvm/mmu.c +1
-0@@ -3530,13 +3548,19 @@ int kvm_mmu_setup(struct kvm_vcpu *vcpu)void kvm_mmu_slot_remove_write_access (struct kvm *kvm,int slot) {struct kvm_mmu_page *sp; +struct kvm_memslots *slots =kvm_memslots (kvm);list_for_each_entry (sp,&kvm->arch.active_mmu_pages, link) {int i;u64 *pt;
arch/x86/kvm/mmu.c int kvm_mmu_setup(struct kvm_vcpu *vcpu) · line 3563
Quoted 10 lines
arch/x86/kvm/mmu.c +7
-2-if (!test_bit (slot, sp->slot_bitmap)) -continue ; +if (likely (slots->nmemslots<=sizeof (sp->slot_bitmap) *8 )) { +if (!test_bit (slot, (void *)&sp->slot_bitmap)) +continue ; + }else { +if (!test_bit (slot, sp->slot_bitmap)) +continue ; + }
That likely() would fail 100% for certain guests.
Neater to write
slot_bitmap = sp_slot_bitmap(sp);
if (!test_bit(slot, sp_slot_bitmap))
continue;include/linux/kvm_host.h line 47
Quoted 10 lines
include/linux/kvm_host.h +10
-0+ +/* +* Protect from malicious userspace by putting an upper bound on the number +* of memory slots. This is an arbitrarily large number that still allows +* us to make pseudo-guarantees about supporting 64 assigned devices with +* plenty of slots left over. +*/ +#ifndef KVM_MAX_MEM_SLOTS +#define KVM_MAX_MEM_SLOTS 512 +#endif
The increase should be in a separate patch (after we optimize the search-fail case).
Quoted 24 lines
if (!npages) { r = -ENOMEM;virt/kvm/kvm_main.c +11
-4- slots =kzalloc (sizeof (struct kvm_memslots ),GFP_KERNEL ); + + nmemslots = (mem->slot>= kvm->memslots->nmemslots) ? + mem->slot +1 : kvm->memslots->nmemslots; + + slots =kzalloc (sizeof (struct kvm_memslots ) + + nmemslots *sizeof (struct kvm_memory_slot ), +GFP_KERNEL );if (!slots) goto out_free; -memcpy (slots, kvm->memslots,sizeof (struct kvm_memslots )); -if (mem->slot>= slots->nmemslots) - slots->nmemslots = mem->slot +1 ; +memcpy (slots, kvm->memslots, +sizeof (struct kvm_memslots ) + kvm->memslots->nmemslots * +sizeof (struct kvm_memory_slot )); + slots->nmemslots = nmemslots; slots->generation++; slots->memslots[mem->slot].flags |=KVM_MEMSLOT_INVALID ;
virt/kvm/kvm_main.c skip_lpage: · line 803
Quoted 8 lines
virt/kvm/kvm_main.c +4
-1@@ -787,12 +797,21 @@ skip_lpage: } r = -ENOMEM ; - slots =kzalloc (sizeof (struct kvm_memslots ),GFP_KERNEL ); + +if (mem->slot>= kvm->memslots->nmemslots) { + nmemslots = mem->slot +1 ; + flush = true;
Isn't flush here a little too agressive? Shouldn't we flush only if we cross the BITS_PER_LONG threshold?
virt/kvm/kvm_main.c skip_lpage: · line 809
Quoted 6 lines
virt/kvm/kvm_main.c +6
-0+ }else + nmemslots = kvm->memslots->nmemslots; + + slots =kzalloc (sizeof (struct kvm_memslots ) + + nmemslots *sizeof (struct kvm_memory_slot ), +GFP_KERNEL );
Code duplication -> helper.
Quoted 12 lines
virt/kvm/kvm_main.c +3
-3if (!slots) goto out_free; -memcpy (slots, kvm->memslots,sizeof (struct kvm_memslots )); -if (mem->slot>= slots->nmemslots) - slots->nmemslots = mem->slot +1 ; +memcpy (slots, kvm->memslots,sizeof (struct kvm_memslots ) + + kvm->memslots->nmemslots *sizeof (struct kvm_memory_slot )); + slots->nmemslots = nmemslots; slots->generation++;/* actual memory is freed via old in kvm_free_physmem_slot below */
virt/kvm/kvm_main.c skip_lpage: · line 832
Quoted 6 lines
virt/kvm/kvm_main.c +3
-0@@ -808,6 +827,9 @@ skip_lpage:rcu_assign_pointer (kvm->memslots, slots);synchronize_srcu_expedited (&kvm->srcu); +if (flush) +kvm_arch_flush_shadow (kvm); +
Need to flush before rcu_assign_pointer() so kvm_mmu_free_page() sees the old slot count.
But even that is insufficient since we'll create direct and indirect slot bitmaps concurrently. Need to store whether the bitmap is direct or not in kvm_mmu_page.
virt/kvm/kvm_main.c static long kvm_vm_ioctl(struct file *filp, · line 1858
Quoted 6 lines
virt/kvm/kvm_main.c +2
-0@@ -1832,6 +1854,8 @@ static long kvm_vm_ioctl(struct file *filp, sizeof kvm_userspace_mem)) goto out; + kvm_userspace_mem.slot +=KVM_PRIVATE_MEM_SLOTS ; +
Slightly uneasy about this, but no real objection.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [RFC PATCH 2/3] kvm: Allow memory slot array to grow on demand
Quoted 24 lines · Avi Kivity
Quoted 15 lines · Alex Williamson
Remove fixed
KVM_MEMORY_SLOTSlimit, allowing the slot array to grow on demand. Private slots are now allocated at the front instead of the end. Only x86 seems to use private slots, so this is now zero for all other archs. The memslots pointer is already updated using rcu, so changing the size off the array when it's replaces is straight forward. x86 also keeps a bitmap of slots used by akvm_mmu_page, which requires a shadow tlb flush whenever we increase the number of slots. This forces the pages to be rebuilt with the new bitmap size.#define KVM_PIO_PAGE_OFFSET 1 #define KVM_COALESCED_MMIO_PAGE_OFFSET 2arch/x86/include/asm/kvm_host.h struct kvm_mmu_page { · line 209
Quoted 5 lines
arch/x86/include/asm/kvm_host.h +1
-1@@ -207,7 +206,7 @@ struct kvm_mmu_page {* One bit set per slot which has memory * in this shadow page. */ -DECLARE_BITMAP (slot_bitmap,KVM_MEMORY_SLOTS +KVM_PRIVATE_MEM_SLOTS ); +unsigned long *slot_bitmap;What about
Quoted 6 lines
union { DECLARE_BITMAP(direct_slot_bitmap, BITS_PER_LONG); unsigned long *indirect_slot_bitmap; };to make the hackery below more explicit?
Yeah, it need something to make the hackery go down easier. I was actually thinking about:
unsigned long *slot_bitmap; DECLARE_BITMAP(direct_slot_bitmap, BITS_PER_LONG);
Where we'd then just set:
slot_bitmap = &direct_slot_bitmap;
It wastes 8 bytes, and pushes the cache a little harder, but still helps the locality and makes the usage more consistent.
Quoted 29 lines
Quoted 29 lines
arch/x86/kvm/mmu.c +7
-1static void kvm_mmu_free_page (struct kvm *kvm,struct kvm_mmu_page *sp) { +struct kvm_memslots *slots =kvm_memslots (kvm); +ASSERT (is_empty_shadow_page (sp->spt));hlist_del (&sp->hash_link);list_del (&sp->link); +if (unlikely (slots->nmemslots>sizeof (sp->slot_bitmap) *8 )) +kfree (sp->slot_bitmap);__free_page (virt_to_page (sp->spt));if (!sp->role.direct)__free_page (virt_to_page (sp->gfns));@@ -1048,6 +1052,7 @@ static struct kvm_mmu_page *kvm_mmu_alloc_page(struct kvm_vcpu *vcpu, u64 *parent_pte, int direct) {struct kvm_mmu_page *sp; +struct kvm_memslots *slots =kvm_memslots (vcpu->kvm); sp =mmu_memory_cache_alloc (&vcpu->arch.mmu_page_header_cache,sizeof *sp); sp->spt =mmu_memory_cache_alloc (&vcpu->arch.mmu_page_cache,PAGE_SIZE );@@ -1056,7 +1061,16 @@ static struct kvm_mmu_page *kvm_mmu_alloc_page(struct kvm_vcpu *vcpu, PAGE_SIZE);set_page_private (virt_to_page (sp->spt), (unsigned long )sp);list_add (&sp->link,&vcpu->kvm->arch.active_mmu_pages); -bitmap_zero (sp->slot_bitmap,KVM_MEMORY_SLOTS +KVM_PRIVATE_MEM_SLOTS ); + +if (unlikely (slots->nmemslots>sizeof (sp->slot_bitmap) *8 )) {
Quoted 8 lines
arch/x86/kvm/mmu.c static struct kvm_mmu_page *kvm_mmu_alloc_page(struct kvm_vcpu *vcpu, · line 1070
Quoted 5 lines
arch/x86/kvm/mmu.c +5
-0+ sp->slot_bitmap =kzalloc (sizeof (long ) * +BITS_TO_LONGS (slots->nmemslots), +GFP_KERNEL ); +if (!sp->slot_bitmap) +return NULL ;We don't support failing
kvm_mmu_get_page(). Seemmu_memory_cache_alloc()andmmu_topup_memory_caches().
Hmm, apparently my search stopped at __direct_map() calling kvm_mmu_get_page() and handling an error.
Quoted 31 lines
Quoted 18 lines
arch/x86/kvm/mmu.c +3
-0+ }else +bitmap_zero ((void *)&sp->slot_bitmap, slots->nmemslots); +static void
mmu_convert_notrap(struct kvm_mmu_page *sp)arch/x86/kvm/mmu.c +1
-0@@ -3530,13 +3548,19 @@ int kvm_mmu_setup(struct kvm_vcpu *vcpu)void kvm_mmu_slot_remove_write_access (struct kvm *kvm,int slot) {struct kvm_mmu_page *sp; +struct kvm_memslots *slots =kvm_memslots (kvm);list_for_each_entry (sp,&kvm->arch.active_mmu_pages, link) {int i;u64 *pt;arch/x86/kvm/mmu.c int kvm_mmu_setup(struct kvm_vcpu *vcpu) · line 3563
Quoted 10 lines
arch/x86/kvm/mmu.c +7
-2-if (!test_bit (slot, sp->slot_bitmap)) -continue ; +if (likely (slots->nmemslots<=sizeof (sp->slot_bitmap) *8 )) { +if (!test_bit (slot, (void *)&sp->slot_bitmap)) +continue ; + }else { +if (!test_bit (slot, sp->slot_bitmap)) +continue ; + }That
likely()would fail 100% for certain guests.
Quoted 5 lines
Neater to write
slot_bitmap = sp_slot_bitmap(sp); if (!test_bit(slot, sp_slot_bitmap)) continue;
OK
Quoted 5 lines
Quoted 5 lines
include/linux/kvm_host.h +5
-0+ +/* +* Protect from malicious userspace by putting an upper bound on the number +* of memory slots. This is an arbitrarily large number that still allows +* us to make pseudo-guarantees about supporting 64 assigned devices with
Quoted 8 lines
include/linux/kvm_host.h line 47
Quoted 5 lines
+ * plenty of slots left over.
+ */include/linux/kvm_host.h +3
-0+#ifndef KVM_MAX_MEM_SLOTS +#define KVM_MAX_MEM_SLOTS 512 +#endif The increase should be in a separate patch (after we optimize the search-fail case).
Ok, I'll make this be 32 + PRIVATE_SLOTS for now
Quoted 26 lines
Quoted 26 lines
if (!npages) { r = -ENOMEM;virt/kvm/kvm_main.c +11
-4- slots =kzalloc (sizeof (struct kvm_memslots ),GFP_KERNEL ); + + nmemslots = (mem->slot>= kvm->memslots->nmemslots) ? + mem->slot +1 : kvm->memslots->nmemslots; + + slots =kzalloc (sizeof (struct kvm_memslots ) + + nmemslots *sizeof (struct kvm_memory_slot ), +GFP_KERNEL );if (!slots) goto out_free; -memcpy (slots, kvm->memslots,sizeof (struct kvm_memslots )); -if (mem->slot>= slots->nmemslots) - slots->nmemslots = mem->slot +1 ; +memcpy (slots, kvm->memslots, +sizeof (struct kvm_memslots ) + kvm->memslots->nmemslots * +sizeof (struct kvm_memory_slot )); + slots->nmemslots = nmemslots; slots->generation++; slots->memslots[mem->slot].flags |=KVM_MEMSLOT_INVALID ;@@ -787,12 +797,21 @@ skip_lpage: }
Quoted 9 lines
virt/kvm/kvm_main.c skip_lpage: · line 803
Quoted 6 lines
virt/kvm/kvm_main.c +4
-1r = -ENOMEM ; - slots =kzalloc (sizeof (struct kvm_memslots ),GFP_KERNEL ); + +if (mem->slot>= kvm->memslots->nmemslots) { + nmemslots = mem->slot +1 ; + flush = true;Isn't flush here a little too agressive? Shouldn't we flush only if we cross the
BITS_PER_LONGthreshold?
Perhaps, but is that overly exploiting our knowledge about the bitmap implementation? I figured better to error too aggressively than too lazy since this is a rare event already.
Quoted 31 lines
virt/kvm/kvm_main.c skip_lpage: · line 809
Quoted 6 lines
virt/kvm/kvm_main.c +6
-0+ }else + nmemslots = kvm->memslots->nmemslots; + + slots =kzalloc (sizeof (struct kvm_memslots ) + + nmemslots *sizeof (struct kvm_memory_slot ), +GFP_KERNEL );Code duplication -> helper.
Quoted 12 lines
virt/kvm/kvm_main.c +3
-3if (!slots) goto out_free; -memcpy (slots, kvm->memslots,sizeof (struct kvm_memslots )); -if (mem->slot>= slots->nmemslots) - slots->nmemslots = mem->slot +1 ; +memcpy (slots, kvm->memslots,sizeof (struct kvm_memslots ) + + kvm->memslots->nmemslots *sizeof (struct kvm_memory_slot )); + slots->nmemslots = nmemslots; slots->generation++;/* actual memory is freed via old in kvm_free_physmem_slot below */ virt/kvm/kvm_main.c skip_lpage: · line 832
Quoted 6 lines
virt/kvm/kvm_main.c +3
-0@@ -808,6 +827,9 @@ skip_lpage:rcu_assign_pointer (kvm->memslots, slots);synchronize_srcu_expedited (&kvm->srcu); +if (flush) +kvm_arch_flush_shadow (kvm); +Need to flush before
rcu_assign_pointer()sokvm_mmu_free_page()sees the old slot count.
Quoted 3 lines
But even that is insufficient since we'll create direct and indirect slot bitmaps concurrently. Need to store whether the bitmap is direct or not in
kvm_mmu_page.
Ick. Ok, I'll investigate.
Quoted 8 lines
virt/kvm/kvm_main.c static long kvm_vm_ioctl(struct file *filp, · line 1858
Quoted 6 lines
virt/kvm/kvm_main.c +2
-0@@ -1832,6 +1854,8 @@ static long kvm_vm_ioctl(struct file *filp, sizeof kvm_userspace_mem)) goto out; + kvm_userspace_mem.slot +=KVM_PRIVATE_MEM_SLOTS ; +Slightly uneasy about this, but no real objection.
If you have better ideas, let me know. This reminds me to ask about this chunk:
@@ -671,7 +674,7 @@ int __kvm_set_memory_region(struct kvm *kvm,
/* Check for overlaps */
r = -EEXIST;
- for (i = 0; i < KVM_MEMORY_SLOTS; ++i) {
+ for (i = KVM_PRIVATE_MEM_SLOTS; i < kvm->memslots->nmemslots; ++i) {
struct kvm_memory_slot *s = &kvm->memslots->memslots[i];
if (s == memslot || !s->npages)I kept the same behavior as previous, but it highlights that we're not checking for overlaps between private slots and anything else. Existing bug? Thanks,
Alex
Re: [RFC PATCH 2/3] kvm: Allow memory slot array to grow on demand
Quoted 22 lines · Alex Williamson
Quoted 7 lines
Quoted 6 lines
@@ -207,7 +206,7 @@ struct
kvm_mmu_page{
* One bit set per slot which has memory
* in this shadow page.
*/
-DECLARE_BITMAP(slot_bitmap, KVM_MEMORY_SLOTS + KVM_PRIVATE_MEM_SLOTS);
+ unsigned long*slot_bitmap;Quoted 8 lines
What about
union { DECLARE_BITMAP(direct_slot_bitmap, BITS_PER_LONG); unsigned long *indirect_slot_bitmap; };to make the hackery below more explicit?
Yeah, it need something to make the hackery go down easier. I was actually thinking about:
unsigned long *slot_bitmap; DECLARE_BITMAP(direct_slot_bitmap, BITS_PER_LONG);
Quoted 6 lines
Where we'd then just set:
slot_bitmap =&direct_slot_bitmap;It wastes 8 bytes, and pushes the cache a little harder, but still helps the locality and makes the usage more consistent.
unsigned long *sp_slot_bitmap(struct kvm_mmu_page *sp) { ... }
gives you the best of both worlds.
Quoted 6 lines
Quoted 3 lines
We don't support failing
kvm_mmu_get_page(). Seemmu_memory_cache_alloc()andmmu_topup_memory_caches().Hmm, apparently my search stopped at
__direct_map()callingkvm_mmu_get_page()and handling an error.
That's dead code (was there from the very first commit into mmu.c).
Quoted 8 lines
Quoted 8 lines
Quoted 7 lines
r = -ENOMEM;- slots =
kzalloc(sizeof(struct kvm_memslots), GFP_KERNEL);
+
+ if (mem->slot>=kvm->memslots->nmemslots) {
+ nmemslots =mem->slot+ 1;
+ flush = true;
Quoted 6 lines
Quoted 2 lines
Isn't flush here a little too agressive? Shouldn't we flush only if we cross the
BITS_PER_LONGthreshold?Perhaps, but is that overly exploiting our knowledge about the bitmap implementation? I figured better to error too aggressively than too lazy since this is a rare event already.
I'm worried about the screen-clearing using the vga window at 0xa[08]000. If that works without too much flushing, then we're fine.
On second thoughts we're likely fine even if we do flush, since it's in a tight loop so it takes very little work to reestablish the dropped sptes.
Quoted 22 lines
Quoted 8 lines
Quoted 6 lines
@@ -1832,6 +1854,8 @@ static long kvm_vm_ioctl(struct file *filp,
sizeofkvm_userspace_mem))
goto out;+ kvm_userspace_mem.slot +=
KVM_PRIVATE_MEM_SLOTS;
+Slightly uneasy about this, but no real objection.
If you have better ideas, let me know. This reminds me to ask about this chunk:
virt/kvm/kvm_main.c +1
-1@@ -671,7 +674,7 @@ int __kvm_set_memory_region(struct kvm *kvm,/* Check for overlaps */ r = -EEXIST ; -for (i =0 ; i<KVM_MEMORY_SLOTS ; ++i) { +for (i =KVM_PRIVATE_MEM_SLOTS ; i< kvm->memslots->nmemslots; ++i) {struct kvm_memory_slot *s =&kvm->memslots->memslots[i];if (s == memslot || !s->npages)
Quoted 3 lines
I kept the same behavior as previous, but it highlights that we're not checking for overlaps between private slots and anything else. Existing bug? Thanks,
Yes, possibly serious. Who knows what happens if we create a page using one slot and remove it via another?
Let's go write some Visual Basic.
Signature · 1 line
error compiling committee.c: too many arguments to function
RFC PATCH 3/3 kvm: Use weight-balanced tree for memory slot management
In reply to Alex WilliamsonRather than search linearly through an unordered slot array, we can embed a tree node into each element. We can still do RCU tree updates by applying the pointer offset to each element in the new copy of the tree.
---
include/linux/kvm_host.h | 3 + virt/kvm/kvm_main.c | 163 +++++++++++++++++++++++++++++++++++++--------- 2 files changed, 135 insertions(+), 31 deletions(-)
include/linux/kvm_host.h +3 -0
@@ -18,6 +18,7 @@#include <linux/msi.h> #include <linux/slab.h> #include <linux/rcupdate.h> +#include <linux/wbtree.h> #include <asm/signal.h> #include <linux/kvm.h>
@@ -171,6 +172,7 @@ struct kvm_lpage_info { };struct kvm_memory_slot { +struct wb_node wb_node;gfn_t base_gfn;unsigned long npages;unsigned long flags;
@@ -225,6 +227,7 @@ struct kvm_irq_routing_table {};struct kvm_memslots {int nmemslots;u64 generation; +struct wb_root wb_root;struct kvm_memory_slot memslots[]; };
virt/kvm/kvm_main.c +132 -31
211 lines not on this page · raw message
Re: [RFC PATCH 0/3] Weight-balanced binary tree + KVM growable memory slots using wbtree
In reply to Alex WilliamsonQuoted 15 lines · Alex Williamson
This series introduces a new weight-balanced binary tree (wbtree) for general use. It's largely leveraged from the rbtree, copying it's rotate functions, while introducing different rebalance and erase functions. This tree is particularly useful for managing memory ranges, where it's desirable to have the most likely targets (the largest ranges) at the top of each subtree.
Patches 2 & 3 go on to convert the KVM memory slots to a growable array and make use of wbtree for efficient managment. Trying to exercise the worst case for this data structure, I ran netperf
TCP_RRon an emulated rtl8139 NIC connected directly to the host via a tap. Both qemu-kvm and the netserver on the host were pinned to optimal CPUs with taskset. This series resulted in a 3% improvement for this test.
Quoted 8 lines
Note that part of why this series is RFC is that the
print_treefunction in the last patch is debug code that generates output for dot. You can copy the output to a file and run:dot -Tpdf foo.dot > foo.pdf
to generate a nice diagram of the tree currently in use. I'll follow-up with a few examples. Thanks,
Attached are examples of the memory slot tree for 2G, 4G, and 8G guests. Thanks,
Alex
Re: [RFC PATCH 0/3] Weight-balanced binary tree + KVM growable memory slots using wbtree
In reply to Alex WilliamsonQuoted 7 lines · Alex Williamson
This series introduces a new weight-balanced binary tree (wbtree) for general use. It's largely leveraged from the rbtree, copying it's rotate functions, while introducing different rebalance and erase functions. This tree is particularly useful for managing memory ranges, where it's desirable to have the most likely targets (the largest ranges) at the top of each subtree.
Quoted 7 lines
Patches 2 & 3 go on to convert the KVM memory slots to a growable array and make use of wbtree for efficient managment. Trying to exercise the worst case for this data structure, I ran netperf
TCP_RRon an emulated rtl8139 NIC connected directly to the host via a tap. Both qemu-kvm and the netserver on the host were pinned to optimal CPUs with taskset. This series resulted in a 3% improvement for this test.
Marcelo asked about kernbench with ept=0 on this series. Using a 4-way, 10G guest with pinned vcpus, build in tmpfs, 10x optimal run, I got:
before (stdev) after (stdev) %
--------+-----------------+----------------+-------
Elapsed | 42.809 (0.19) | 42.305 (0.23) | -1.18 User | 115.709 (0.22) | 114.720 (0.31) | -0.85 System | 41.605 (0.14) | 40.924 (0.20) | -1.64 %cpu | 366.9 (1.45) | 367.6 (1.51) | 0.19 context | 7272.3 (68.6) | 7249.5 (97.8) | -0.31 sleeps | 14826.2 (110.6) | 14798.5 (63.0) | -0.19
So, a small but measurable gain. Also, sorry for forgetting to address Marcelo's comments from the original version of patch 2/3, I'll pick them up in the next round. Thanks,
Alex
Re: [RFC PATCH 0/3] Weight-balanced binary tree + KVM growable memory slots using wbtree
In reply to Alex WilliamsonQuoted 7 lines · Alex Williamson
This series introduces a new weight-balanced binary tree (wbtree) for general use. It's largely leveraged from the rbtree, copying it's rotate functions, while introducing different rebalance and erase functions. This tree is particularly useful for managing memory ranges, where it's desirable to have the most likely targets (the largest ranges) at the top of each subtree.
Quoted 8 lines
Patches 2& 3 go on to convert the KVM memory slots to a growable array and make use of wbtree for efficient managment. Trying to exercise the worst case for this data structure, I ran netperf
TCP_RRon an emulated rtl8139 NIC connected directly to the host via a tap. Both qemu-kvm and the netserver on the host were pinned to optimal CPUs with taskset. This series resulted in a 3% improvement for this test.
In this case, I think most of the faults (at least after the guest was warmed up) missed the tree completely. In this case a weight balanced tree is hardly optimal (it is optimized for hits), so I think you'll see a bigger gain from the mmio fault optimization. You'll probably see most of the gain running mmu intensive tests with ept=0.
Quoted 11 lines
Note that part of why this series is RFC is that the
print_treefunction in the last patch is debug code that generates output for dot. You can copy the output to a file and run:dot -Tpdf foo.dot> foo.pdf
to generate a nice diagram of the tree currently in use. I'll follow-up with a few examples. Thanks,
Alex
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [RFC PATCH 0/3] Weight-balanced binary tree + KVM growable memory slots using wbtree
Quoted 17 lines · Avi Kivity
Quoted 7 lines · Alex Williamson
This series introduces a new weight-balanced binary tree (wbtree) for general use. It's largely leveraged from the rbtree, copying it's rotate functions, while introducing different rebalance and erase functions. This tree is particularly useful for managing memory ranges, where it's desirable to have the most likely targets (the largest ranges) at the top of each subtree.
Quoted 8 lines
Patches 2& 3 go on to convert the KVM memory slots to a growable array and make use of wbtree for efficient managment. Trying to exercise the worst case for this data structure, I ran netperf
TCP_RRon an emulated rtl8139 NIC connected directly to the host via a tap. Both qemu-kvm and the netserver on the host were pinned to optimal CPUs with taskset. This series resulted in a 3% improvement for this test.
Quoted 2 lines
In this case, I think most of the faults (at least after the guest was warmed up) missed the tree completely.
Except for the mmio faults for the NIC, which will traverse the entire depth of that branch of the tree for every access.
Quoted 4 lines
In this case a weight balanced
tree is hardly optimal (it is optimized for hits), so I think you'll see a bigger gain from the mmio fault optimization. You'll probably see most of the gain running mmu intensive tests with ept=0.
Right, the gain expected by this test is that we're only traversing 6-7 tree nodes until we don't find a match, versus the full 32 entries of the original memslot array. So it's effectively comparing worst case scenarios for both data structures.
Hopefully the followup with kernbench run with ept=0 show that there's also still a benefit in the data match scenario. The existing array ends up being nearly optimal for memory hits since it registers memory from 1M - 3.5G in slot0 and 4G - 10.5G in slot1. For the tree, we jump straight to the bigger slot. I'll run one more set of kernbench tests with the original code, just reversing slots 0&1 to see if we take much of a hit from the tree overhead. Thanks,
Alex
Re: [RFC PATCH 0/3] Weight-balanced binary tree + KVM growable memory slots using wbtree
Quoted 34 lines · Alex Williamson
Quoted 17 lines · Avi Kivity
Quoted 7 lines · Alex Williamson
This series introduces a new weight-balanced binary tree (wbtree) for general use. It's largely leveraged from the rbtree, copying it's rotate functions, while introducing different rebalance and erase functions. This tree is particularly useful for managing memory ranges, where it's desirable to have the most likely targets (the largest ranges) at the top of each subtree.
Quoted 8 lines
Patches 2& 3 go on to convert the KVM memory slots to a growable array and make use of wbtree for efficient managment. Trying to exercise the worst case for this data structure, I ran netperf
TCP_RRon an emulated rtl8139 NIC connected directly to the host via a tap. Both qemu-kvm and the netserver on the host were pinned to optimal CPUs with taskset. This series resulted in a 3% improvement for this test.Quoted 2 lines
In this case, I think most of the faults (at least after the guest was warmed up) missed the tree completely.
Except for the mmio faults for the NIC, which will traverse the entire depth of that branch of the tree for every access.
Quoted 4 lines
In this case a weight balanced
tree is hardly optimal (it is optimized for hits), so I think you'll see a bigger gain from the mmio fault optimization. You'll probably see most of the gain running mmu intensive tests with ept=0.Right, the gain expected by this test is that we're only traversing 6-7 tree nodes until we don't find a match, versus the full 32 entries of the original memslot array. So it's effectively comparing worst case scenarios for both data structures.
Quoted 7 lines
Hopefully the followup with kernbench run with ept=0 show that there's also still a benefit in the data match scenario. The existing array ends up being nearly optimal for memory hits since it registers memory from 1M - 3.5G in slot0 and 4G - 10.5G in slot1. For the tree, we jump straight to the bigger slot. I'll run one more set of kernbench tests with the original code, just reversing slots 0&1 to see if we take much of a hit from the tree overhead. Thanks,
I had forgotten about <1M mem, so actually the slot configuration was:
0: <1M
1: 1M - 3.5G
2: 4G+
I stacked the deck in favor of the static array (0: 4G+, 1: 1M-3.5G, 2:
<1M), and got these kernbench results:
base (stdev) reorder (stdev) wbtree (stdev)
--------+-----------------+----------------+----------------+
Elapsed | 42.809 (0.19) | 42.160 (0.22) | 42.305 (0.23) | User | 115.709 (0.22) | 114.358 (0.40) | 114.720 (0.31) | System | 41.605 (0.14) | 40.741 (0.22) | 40.924 (0.20) | %cpu | 366.9 (1.45) | 367.4 (1.17) | 367.6 (1.51) | context | 7272.3 (68.6) | 7248.1 (89.7) | 7249.5 (97.8) | sleeps | 14826.2 (110.6) | 14780.7 (86.9) | 14798.5 (63.0) |
So, wbtree is only slightly behind reordering, and the standard deviation suggests the runs are mostly within the noise of each other. Thanks,
Alex
Re: [RFC PATCH 0/3] Weight-balanced binary tree + KVM growable memory slots using wbtree
Quoted 18 lines · Alex Williamson
I had forgotten about<1M mem, so actually the slot configuration was:
0:<1M
1: 1M - 3.5G
2: 4G+I stacked the deck in favor of the static array (0: 4G+, 1: 1M-3.5G, 2:
<1M), and got these kernbench results:base (stdev) reorder (stdev) wbtree (stdev)
--------+-----------------+----------------+----------------+Elapsed | 42.809 (0.19) | 42.160 (0.22) | 42.305 (0.23) | User | 115.709 (0.22) | 114.358 (0.40) | 114.720 (0.31) | System | 41.605 (0.14) | 40.741 (0.22) | 40.924 (0.20) | %cpu | 366.9 (1.45) | 367.4 (1.17) | 367.6 (1.51) | context | 7272.3 (68.6) | 7248.1 (89.7) | 7249.5 (97.8) | sleeps | 14826.2 (110.6) | 14780.7 (86.9) | 14798.5 (63.0) |
Quoted 3 lines
So, wbtree is only slightly behind reordering, and the standard deviation suggests the runs are mostly within the noise of each other. Thanks,
Doesn't this indicate we should use reordering, instead of a new data structure?
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [RFC PATCH 0/3] Weight-balanced binary tree + KVM growable memory slots using wbtree
Quoted 19 lines · Avi Kivity
Quoted 18 lines · Alex Williamson
I had forgotten about<1M mem, so actually the slot configuration was:
0:<1M
1: 1M - 3.5G
2: 4G+I stacked the deck in favor of the static array (0: 4G+, 1: 1M-3.5G, 2:
<1M), and got these kernbench results:base (stdev) reorder (stdev) wbtree (stdev)
--------+-----------------+----------------+----------------+Elapsed | 42.809 (0.19) | 42.160 (0.22) | 42.305 (0.23) | User | 115.709 (0.22) | 114.358 (0.40) | 114.720 (0.31) | System | 41.605 (0.14) | 40.741 (0.22) | 40.924 (0.20) | %cpu | 366.9 (1.45) | 367.4 (1.17) | 367.6 (1.51) | context | 7272.3 (68.6) | 7248.1 (89.7) | 7249.5 (97.8) | sleeps | 14826.2 (110.6) | 14780.7 (86.9) | 14798.5 (63.0) |
Quoted 6 lines
Quoted 3 lines
So, wbtree is only slightly behind reordering, and the standard deviation suggests the runs are mostly within the noise of each other. Thanks,
Doesn't this indicate we should use reordering, instead of a new data structure?
The original problem that brought this on was scaling. The re-ordered array still has O(N) scaling while the tree should have ~O(logN) (note that it currently doesn't because it needs a compaction algorithm added after insert and remove). So yes, it's hard to beat the results of a test that hammers on the first couple entries of a sorted array, but I think the tree has better than current performance and more predictable when scaled performance.
If we knew when we were searching for which type of data, it would perhaps be nice if we could use a sorted array for guest memory (since it's nicely bounded into a small number of large chunks), and a tree for mmio (where we expect the scaling to be a factor). Thanks,
Alex
Re: [RFC PATCH 0/3] Weight-balanced binary tree + KVM growable memory slots using wbtree
Quoted 27 lines · Alex Williamson
Quoted 19 lines · Avi Kivity
Quoted 18 lines · Alex Williamson
I had forgotten about<1M mem, so actually the slot configuration was:
0:<1M
1: 1M - 3.5G
2: 4G+I stacked the deck in favor of the static array (0: 4G+, 1: 1M-3.5G, 2:
<1M), and got these kernbench results:base (stdev) reorder (stdev) wbtree (stdev)
--------+-----------------+----------------+----------------+Elapsed | 42.809 (0.19) | 42.160 (0.22) | 42.305 (0.23) | User | 115.709 (0.22) | 114.358 (0.40) | 114.720 (0.31) | System | 41.605 (0.14) | 40.741 (0.22) | 40.924 (0.20) | %cpu | 366.9 (1.45) | 367.4 (1.17) | 367.6 (1.51) | context | 7272.3 (68.6) | 7248.1 (89.7) | 7249.5 (97.8) | sleeps | 14826.2 (110.6) | 14780.7 (86.9) | 14798.5 (63.0) |Quoted 6 lines
Quoted 3 lines
So, wbtree is only slightly behind reordering, and the standard deviation suggests the runs are mostly within the noise of each other. Thanks,
Doesn't this indicate we should use reordering, instead of a new data structure?
Quoted 7 lines
The original problem that brought this on was scaling. The re-ordered array still has O(N) scaling while the tree should have ~O(logN) (note that it currently doesn't because it needs a compaction algorithm added after insert and remove). So yes, it's hard to beat the results of a test that hammers on the first couple entries of a sorted array, but I think the tree has better than current performance and more predictable when scaled performance.
Scaling doesn't matter, only actual performance. Even a guest with 512 slots would still hammer only on the first few slots, since these will contain the bulk of memory.
Quoted 4 lines
If we knew when we were searching for which type of data, it would perhaps be nice if we could use a sorted array for guest memory (since it's nicely bounded into a small number of large chunks), and a tree for mmio (where we expect the scaling to be a factor). Thanks,
We have three types of memory:
- RAM - a few large slots
- mapped mmio (for device assignment) - possible many small slots
- non-mapped mmio (for emulated devices) - no slots
The first two are handled in exactly the same way - they're just memory slots. We expect a lot more hits into the RAM slots, since they're much bigger. But by far the majority of faults will be for the third category - mapped memory will be hit once per page, then handled by hardware until Linux memory management does something about the page, which should hopefully be rare (with device assignment, rare == never, since those pages are pinned).
Therefore our optimization priorities should be
- complete miss into the slot list
- hit into the RAM slots
- hit into the other slots (trailing far behind)
Of course worst-case performance matters. For example, we might (not sure) be searching the list with the mmu spinlock held.
I think we still have a bit to go before we can justify the new data structure.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [RFC PATCH 0/3] Weight-balanced binary tree + KVM growable memory slots using wbtree
Quoted 36 lines · Avi Kivity
Quoted 27 lines · Alex Williamson
Quoted 19 lines · Avi Kivity
Quoted 18 lines · Alex Williamson
I had forgotten about<1M mem, so actually the slot configuration was:
0:<1M
1: 1M - 3.5G
2: 4G+I stacked the deck in favor of the static array (0: 4G+, 1: 1M-3.5G, 2:
<1M), and got these kernbench results:base (stdev) reorder (stdev) wbtree (stdev)
--------+-----------------+----------------+----------------+Elapsed | 42.809 (0.19) | 42.160 (0.22) | 42.305 (0.23) | User | 115.709 (0.22) | 114.358 (0.40) | 114.720 (0.31) | System | 41.605 (0.14) | 40.741 (0.22) | 40.924 (0.20) | %cpu | 366.9 (1.45) | 367.4 (1.17) | 367.6 (1.51) | context | 7272.3 (68.6) | 7248.1 (89.7) | 7249.5 (97.8) | sleeps | 14826.2 (110.6) | 14780.7 (86.9) | 14798.5 (63.0) |Quoted 6 lines
Quoted 3 lines
So, wbtree is only slightly behind reordering, and the standard deviation suggests the runs are mostly within the noise of each other. Thanks,
Doesn't this indicate we should use reordering, instead of a new data structure?
Quoted 7 lines
The original problem that brought this on was scaling. The re-ordered array still has O(N) scaling while the tree should have ~O(logN) (note that it currently doesn't because it needs a compaction algorithm added after insert and remove). So yes, it's hard to beat the results of a test that hammers on the first couple entries of a sorted array, but I think the tree has better than current performance and more predictable when scaled performance.
Quoted 3 lines
Scaling doesn't matter, only actual performance. Even a guest with 512 slots would still hammer only on the first few slots, since these will contain the bulk of memory.
It seems like we need a good mixed workload benchmark. So far we've only tested worst case, with a pure emulated I/O test, and best case, with a pure memory test. Ordering an array only helps the latter, and only barely beats the tree, so I suspect overall performance would be better with a tree.
Quoted 19 lines
Quoted 4 lines
If we knew when we were searching for which type of data, it would perhaps be nice if we could use a sorted array for guest memory (since it's nicely bounded into a small number of large chunks), and a tree for mmio (where we expect the scaling to be a factor). Thanks,
We have three types of memory:
- RAM - a few large slots
- mapped mmio (for device assignment) - possible many small slots
- non-mapped mmio (for emulated devices) - no slotsThe first two are handled in exactly the same way - they're just memory slots. We expect a lot more hits into the RAM slots, since they're much bigger. But by far the majority of faults will be for the third category - mapped memory will be hit once per page, then handled by hardware until Linux memory management does something about the page, which should hopefully be rare (with device assignment, rare == never, since those pages are pinned).
Quoted 2 lines
Therefore our optimization priorities should be
- complete miss into the slot list
The tree is obviously the most time and space efficient for this and the netperf results show a pretty clear win here. I think it's really only a question of whether we'd be ok with slow, cache thrashing, searches here if we could effectively cache the result for next time as you've suggested. Even then, it seems like steady state performance would be prone to unusual slowdowns (ex. have to flush sptes on every add, what's the regeneration time to replace all those slow lookups?).
Quoted 1 line
- hit into the RAM slots
It's really just the indirection of the tree and slightly larger element size that gives the sorted array an edge here.
Quoted 1 line
- hit into the other slots (trailing far behind)
Obviously an array sucks at this.
Quoted 5 lines
Of course worst-case performance matters. For example, we might (not sure) be searching the list with the mmu spinlock held.
I think we still have a bit to go before we can justify the new data structure.
Suggestions for a mixed workload benchmark? What else would you like to see? Thanks,
Alex
Re: [RFC PATCH 0/3] Weight-balanced binary tree + KVM growable memory slots using wbtree
Quoted 13 lines · Alex Williamson
Quoted 9 lines
Quoted 8 lines
The original problem that brought this on was scaling. The re-ordered array still has O(N) scaling while the tree should have ~O(logN) (note that it currently doesn't because it needs a compaction algorithm added after insert and remove). So yes, it's hard to beat the results of a test that hammers on the first couple entries of a sorted array, but I think the tree has better than current performance and more predictable when scaled performance.
Quoted 3 lines
Scaling doesn't matter, only actual performance. Even a guest with 512 slots would still hammer only on the first few slots, since these will contain the bulk of memory.
Quoted 5 lines
It seems like we need a good mixed workload benchmark. So far we've only tested worst case, with a pure emulated I/O test, and best case, with a pure memory test. Ordering an array only helps the latter, and only barely beats the tree, so I suspect overall performance would be better with a tree.
But if we cache the missed-all-memslots result in the spte, we eliminate the worst case, and are left with just the best case.
Quoted 19 lines
Quoted 19 lines
Quoted 4 lines
If we knew when we were searching for which type of data, it would perhaps be nice if we could use a sorted array for guest memory (since it's nicely bounded into a small number of large chunks), and a tree for mmio (where we expect the scaling to be a factor). Thanks,
We have three types of memory:
- RAM - a few large slots
- mapped mmio (for device assignment) - possible many small slots
- non-mapped mmio (for emulated devices) - no slotsThe first two are handled in exactly the same way - they're just memory slots. We expect a lot more hits into the RAM slots, since they're much bigger. But by far the majority of faults will be for the third category - mapped memory will be hit once per page, then handled by hardware until Linux memory management does something about the page, which should hopefully be rare (with device assignment, rare == never, since those pages are pinned).
Quoted 8 lines
Quoted 2 lines
Therefore our optimization priorities should be
- complete miss into the slot listThe tree is obviously the most time and space efficient for this and the netperf results show a pretty clear win here. I think it's really only a question of whether we'd be ok with slow, cache thrashing, searches here if we could effectively cache the result for next time as you've suggested.
Yes.
Quoted 3 lines
Even then, it seems like steady state performance would be prone to unusual slowdowns (ex. have to flush sptes on every add, what's the regeneration time to replace all those slow lookups?).
Those sptes would be flushed very rarely (never in normal operation). When we add a slot, yes we drop those sptes, but also a few million other sptes, so it's lost in the noise (regeneration time is just the slot lookup itself).
Quoted 9 lines
Quoted 1 line
- hit into the RAM slots
It's really just the indirection of the tree and slightly larger element size that gives the sorted array an edge here.
Quoted 1 line
- hit into the other slots (trailing far behind)
Obviously an array sucks at this.
Quoted 8 lines
Quoted 5 lines
Of course worst-case performance matters. For example, we might (not sure) be searching the list with the mmu spinlock held.
I think we still have a bit to go before we can justify the new data structure.
Suggestions for a mixed workload benchmark? What else would you like to see? Thanks,
The problem here is that all workloads will cache all memslots very quickly into sptes and all lookups will be misses. There are two cases where we have lookups that hit the memslots structure: ept=0, and host swap. Neither are things we want to optimize too heavily.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [RFC PATCH 0/3] Weight-balanced binary tree + KVM growable memory slots using wbtree
Quoted 14 lines · Avi Kivity
Quoted 13 lines · Alex Williamson
Quoted 12 lines
Quoted 8 lines
The original problem that brought this on was scaling. The re-ordered array still has O(N) scaling while the tree should have ~O(logN) (note that it currently doesn't because it needs a compaction algorithm added after insert and remove). So yes, it's hard to beat the results of a test that hammers on the first couple entries of a sorted array, but I think the tree has better than current performance and more predictable when scaled performance.
Scaling doesn't matter, only actual performance. Even a guest with 512 slots would still hammer only on the first few slots, since these will contain the bulk of memory.
Quoted 8 lines
Quoted 5 lines
It seems like we need a good mixed workload benchmark. So far we've only tested worst case, with a pure emulated I/O test, and best case, with a pure memory test. Ordering an array only helps the latter, and only barely beats the tree, so I suspect overall performance would be better with a tree.
But if we cache the missed-all-memslots result in the spte, we eliminate the worst case, and are left with just the best case.
There's potentially a lot of entries between best case and worst case.
Quoted 54 lines
Quoted 19 lines
Quoted 19 lines
Quoted 4 lines
If we knew when we were searching for which type of data, it would perhaps be nice if we could use a sorted array for guest memory (since it's nicely bounded into a small number of large chunks), and a tree for mmio (where we expect the scaling to be a factor). Thanks,
We have three types of memory:
- RAM - a few large slots
- mapped mmio (for device assignment) - possible many small slots
- non-mapped mmio (for emulated devices) - no slotsThe first two are handled in exactly the same way - they're just memory slots. We expect a lot more hits into the RAM slots, since they're much bigger. But by far the majority of faults will be for the third category - mapped memory will be hit once per page, then handled by hardware until Linux memory management does something about the page, which should hopefully be rare (with device assignment, rare == never, since those pages are pinned).
Quoted 8 lines
Quoted 2 lines
Therefore our optimization priorities should be
- complete miss into the slot listThe tree is obviously the most time and space efficient for this and the netperf results show a pretty clear win here. I think it's really only a question of whether we'd be ok with slow, cache thrashing, searches here if we could effectively cache the result for next time as you've suggested.
Yes.
Quoted 3 lines
Even then, it seems like steady state performance would be prone to unusual slowdowns (ex. have to flush sptes on every add, what's the regeneration time to replace all those slow lookups?).
Those sptes would be flushed very rarely (never in normal operation). When we add a slot, yes we drop those sptes, but also a few million other sptes, so it's lost in the noise (regeneration time is just the slot lookup itself).
Quoted 15 lines
Quoted 1 line
- hit into the RAM slots
It's really just the indirection of the tree and slightly larger element size that gives the sorted array an edge here.
Quoted 1 line
- hit into the other slots (trailing far behind)
Obviously an array sucks at this.
Quoted 5 lines
Of course worst-case performance matters. For example, we might (not sure) be searching the list with the mmu spinlock held.
I think we still have a bit to go before we can justify the new data structure.
Quoted 7 lines
Quoted 2 lines
Suggestions for a mixed workload benchmark? What else would you like to see? Thanks,
The problem here is that all workloads will cache all memslots very quickly into sptes and all lookups will be misses. There are two cases where we have lookups that hit the memslots structure: ept=0, and host swap. Neither are things we want to optimize too heavily.
Which seems to suggest that:
A. making those misses fast = win
B. making those misses fast + caching misses = win++ C. we don't care if the sorted array is subtly faster for ept=0
Sound right? So is the question whether cached misses alone gets us 99% of the improvement since hits are already getting cached in sptes for cases we care about? Xiao, it sounded like you might have already started the caching misses approach. I'd be interested to incorporate and test if you have anything working. Thanks,
Alex
Re: [RFC PATCH 0/3] Weight-balanced binary tree + KVM growable memory slots using wbtree
Quoted 10 lines · Alex Williamson
Quoted 8 lines
Quoted 5 lines
It seems like we need a good mixed workload benchmark. So far we've only tested worst case, with a pure emulated I/O test, and best case, with a pure memory test. Ordering an array only helps the latter, and only barely beats the tree, so I suspect overall performance would be better with a tree.
But if we cache the missed-all-memslots result in the spte, we eliminate the worst case, and are left with just the best case.
There's potentially a lot of entries between best case and worst case.
The mid case is where we have a lot of small slots which are continuously flushed. That would be (ept=0 && new mappings continuously established) || (lots of small mappings && lots of host paging activity). I don't know of any guests that continuously reestablish BAR mappings; and host paging activity doesn't apply to device assignment. What are we left with?
Quoted 8 lines
Quoted 5 lines
The problem here is that all workloads will cache all memslots very quickly into sptes and all lookups will be misses. There are two cases where we have lookups that hit the memslots structure: ept=0, and host swap. Neither are things we want to optimize too heavily.
Which seems to suggest that:
Quoted 7 lines
A. making those misses fast = win
B. making those misses fast + caching misses = win++ C. we don't care if the sorted array is subtly faster for ept=0Sound right? So is the question whether cached misses alone gets us 99% of the improvement since hits are already getting cached in sptes for cases we care about?
Yes, that's my feeling. Caching those misses is a lot more important than speeding them up, since the cache will stay valid for long periods, and since the hit rate will be very high.
Cache+anything=O(1)
no-cache+tree=O(log(n))
no-cache+array=O(n)
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [RFC PATCH 0/3] Weight-balanced binary tree + KVM growable memory slots using wbtree
In reply to Avi KivityQuoted 59 lines · Avi Kivity
Quoted 27 lines · Alex Williamson
Quoted 19 lines · Avi Kivity
Quoted 18 lines · Alex Williamson
I had forgotten about<1M mem, so actually the slot configuration was:
0:<1M
1: 1M - 3.5G
2: 4G+I stacked the deck in favor of the static array (0: 4G+, 1: 1M-3.5G, 2:
<1M), and got these kernbench results:base (stdev) reorder (stdev) wbtree (stdev)
--------+-----------------+----------------+----------------+Elapsed | 42.809 (0.19) | 42.160 (0.22) | 42.305 (0.23) | User | 115.709 (0.22) | 114.358 (0.40) | 114.720 (0.31) | System | 41.605 (0.14) | 40.741 (0.22) | 40.924 (0.20) | %cpu | 366.9 (1.45) | 367.4 (1.17) | 367.6 (1.51) | context | 7272.3 (68.6) | 7248.1 (89.7) | 7249.5 (97.8) | sleeps | 14826.2 (110.6) | 14780.7 (86.9) | 14798.5 (63.0) |Quoted 6 lines
Quoted 3 lines
So, wbtree is only slightly behind reordering, and the standard deviation suggests the runs are mostly within the noise of each other. Thanks,
Doesn't this indicate we should use reordering, instead of a new data structure?
Quoted 7 lines
The original problem that brought this on was scaling. The re-ordered array still has O(N) scaling while the tree should have ~O(logN) (note that it currently doesn't because it needs a compaction algorithm added after insert and remove). So yes, it's hard to beat the results of a test that hammers on the first couple entries of a sorted array, but I think the tree has better than current performance and more predictable when scaled performance.
Scaling doesn't matter, only actual performance. Even a guest with 512 slots would still hammer only on the first few slots, since these will contain the bulk of memory.
Quoted 4 lines
If we knew when we were searching for which type of data, it would perhaps be nice if we could use a sorted array for guest memory (since it's nicely bounded into a small number of large chunks), and a tree for mmio (where we expect the scaling to be a factor). Thanks,
We have three types of memory:
- RAM - a few large slots
- mapped mmio (for device assignment) - possible many small slots
- non-mapped mmio (for emulated devices) - no slotsThe first two are handled in exactly the same way - they're just memory slots. We expect a lot more hits into the RAM slots, since they're much bigger. But by far the majority of faults will be for the third category - mapped memory will be hit once per page, then handled by hardware until Linux memory management does something about the page, which should hopefully be rare (with device assignment, rare == never, since those pages are pinned).
Quoted 4 lines
Therefore our optimization priorities should be
- complete miss into the slot list
- hit into the RAM slots
- hit into the other slots (trailing far behind)
Whatever ordering considered optimal in one workload can be suboptimal in another. The binary search reduces the number of slots inspected in the average case. Using slot size as weight favours probability.
Quoted 5 lines
Of course worst-case performance matters. For example, we might (not sure) be searching the list with the mmu spinlock held.
I think we still have a bit to go before we can justify the new data structure.
Intensive IDE disk IO on guest with lots of assigned network devices, 3% improvement on netperf with rtl8139, 1% improvement on kernbench?
Fail to see justification for not using it.
Re: [RFC PATCH 0/3] Weight-balanced binary tree + KVM growable memory slots using wbtree
Quoted 60 lines · Marcelo Tosatti
Quoted 59 lines · Avi Kivity
Quoted 27 lines · Alex Williamson
Quoted 19 lines · Avi Kivity
Quoted 18 lines · Alex Williamson
I had forgotten about<1M mem, so actually the slot configuration was:
0:<1M
1: 1M - 3.5G
2: 4G+I stacked the deck in favor of the static array (0: 4G+, 1: 1M-3.5G, 2:
<1M), and got these kernbench results:base (stdev) reorder (stdev) wbtree (stdev)
--------+-----------------+----------------+----------------+Elapsed | 42.809 (0.19) | 42.160 (0.22) | 42.305 (0.23) | User | 115.709 (0.22) | 114.358 (0.40) | 114.720 (0.31) | System | 41.605 (0.14) | 40.741 (0.22) | 40.924 (0.20) | %cpu | 366.9 (1.45) | 367.4 (1.17) | 367.6 (1.51) | context | 7272.3 (68.6) | 7248.1 (89.7) | 7249.5 (97.8) | sleeps | 14826.2 (110.6) | 14780.7 (86.9) | 14798.5 (63.0) |Quoted 6 lines
Quoted 3 lines
So, wbtree is only slightly behind reordering, and the standard deviation suggests the runs are mostly within the noise of each other. Thanks,
Doesn't this indicate we should use reordering, instead of a new data structure?
Quoted 7 lines
The original problem that brought this on was scaling. The re-ordered array still has O(N) scaling while the tree should have ~O(logN) (note that it currently doesn't because it needs a compaction algorithm added after insert and remove). So yes, it's hard to beat the results of a test that hammers on the first couple entries of a sorted array, but I think the tree has better than current performance and more predictable when scaled performance.
Scaling doesn't matter, only actual performance. Even a guest with 512 slots would still hammer only on the first few slots, since these will contain the bulk of memory.
Quoted 4 lines
If we knew when we were searching for which type of data, it would perhaps be nice if we could use a sorted array for guest memory (since it's nicely bounded into a small number of large chunks), and a tree for mmio (where we expect the scaling to be a factor). Thanks,
We have three types of memory:
- RAM - a few large slots
- mapped mmio (for device assignment) - possible many small slots
- non-mapped mmio (for emulated devices) - no slotsThe first two are handled in exactly the same way - they're just memory slots. We expect a lot more hits into the RAM slots, since they're much bigger. But by far the majority of faults will be for the third category - mapped memory will be hit once per page, then handled by hardware until Linux memory management does something about the page, which should hopefully be rare (with device assignment, rare == never, since those pages are pinned).
Quoted 8 lines
Quoted 4 lines
Therefore our optimization priorities should be
- complete miss into the slot list
- hit into the RAM slots
- hit into the other slots (trailing far behind)Whatever ordering considered optimal in one workload can be suboptimal in another. The binary search reduces the number of slots inspected in the average case. Using slot size as weight favours probability.
It's really difficult to come up with a workload that causes many hits to small slots.
Quoted 10 lines
Quoted 5 lines
Of course worst-case performance matters. For example, we might (not sure) be searching the list with the mmu spinlock held.
I think we still have a bit to go before we can justify the new data structure.
Intensive IDE disk IO on guest with lots of assigned network devices, 3% improvement on netperf with rtl8139, 1% improvement on kernbench?
Fail to see justification for not using it.
By itself it's great, but the miss cache will cause the code to be called very rarely. So I prefer the sorted array which is simpler (and faster for the few-large-slots case).
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [RFC PATCH 0/3] Weight-balanced binary tree + KVM growable memory slots using wbtree
In reply to Alex WilliamsonQuoted 18 lines · Alex Williamson
Quoted 17 lines · Avi Kivity
Quoted 15 lines · Alex Williamson
This series introduces a new weight-balanced binary tree (wbtree) for general use. It's largely leveraged from the rbtree, copying it's rotate functions, while introducing different rebalance and erase functions. This tree is particularly useful for managing memory ranges, where it's desirable to have the most likely targets (the largest ranges) at the top of each subtree.
Patches 2& 3 go on to convert the KVM memory slots to a growable
array and make use of wbtree for efficient managment. Trying to exercise the worst case for this data structure, I ran netperfTCP_RRon an emulated rtl8139 NIC connected directly to the host via a tap. Both qemu-kvm and the netserver on the host were pinned to optimal CPUs with taskset. This series resulted in a 3% improvement for this test.
Quoted 5 lines
Quoted 2 lines
In this case, I think most of the faults (at least after the guest was warmed up) missed the tree completely.
Except for the mmio faults for the NIC, which will traverse the entire depth of that branch of the tree for every access.
That is exactly what I meant: most of the faults cause the search to fail. What we want here is to cache the success/fail result of the search so we don't do it in the first place.
Quoted 9 lines
Quoted 4 lines
In this case a weight balanced
tree is hardly optimal (it is optimized for hits), so I think you'll see a bigger gain from the mmio fault optimization. You'll probably see most of the gain running mmu intensive tests with ept=0.Right, the gain expected by this test is that we're only traversing 6-7 tree nodes until we don't find a match, versus the full 32 entries of the original memslot array. So it's effectively comparing worst case scenarios for both data structures.
If we optimized the linear list we'd sort it by size, descending, and limit it by the number of instantiated slots, which I think would beat the tree.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 4/7] KVM: sort memslots and use binary search to search the right slot
In reply to Avi KivityQuoted 5 lines · Avi Kivity
Quoted 3 lines · Xiao Guangrong
Sort memslots then search the slot with binary search to speed up the slot searching
Quoted 7 lines
I'm not sure if a binary search is the right algorithm here. It introduces a lot of branches which may be mispredicted.
Options we've discussed are:
- Sort slots by size, use linear search (so the largest slots are found quickly)
- Weighted balanced tree http://en.wikipedia.org/wiki/Weight-balanced_tree, use weight == slot size
Yeah, there are the better chooses.
Quoted 2 lines
Both options still make the miss case (mmio) slow. We could cache the result of a miss in an spte by using a reserved bit, and checking the page fault error code (or seeing if we get an ept violation or ept misconfiguration), so if we get repeated mmio on a page, we don't need to search the slot list/tree.
Sounds good! Will do it, thanks!
PATCH 5/7 KVM: cache the last used slot
In reply to Xiao GuangrongCache the last used slot, the hit ratio is more than 35% during my test
--- include/linux/kvm_host.h | 1 + virt/kvm/kvm_main.c | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-)
include/linux/kvm_host.h +1 -0
@@ -225,6 +225,7 @@ struct kvm_memslots {int nmemslots;int used_slots;u64 generation; +struct kvm_memory_slot *slot_cache;struct kvm_memory_slot memslots[KVM_MEM_SLOTS_NUM ];struct kvm_memory_slot *slots_sort[KVM_MEM_SLOTS_NUM ]; };
virt/kvm/kvm_main.c +9 -2
@@ -643,15 +643,21 @@ static struct kvm_memory_slot *search_memslots(struct kvm_memslots *slots, gfn_t gfn) {int start =0 , end = slots->used_slots, idx; -struct kvm_memory_slot *memslot; +struct kvm_memory_slot *memslot = slots->slot_cache; + +if (memslot && gfn >= memslot->base_gfn && + gfn < memslot->base_gfn + memslot->npages) +return memslot;while (start < end) { idx = (start + end) /2 ; memslot = slots->slots_sort[idx];if (gfn >= memslot->base_gfn && - gfn < memslot->base_gfn + memslot->npages) + gfn < memslot->base_gfn + memslot->npages) { + slots->slot_cache = memslot;return memslot; + }if (memslot->base_gfn < gfn) start = idx +1 ;
@@ -684,6 +690,7 @@ void memslots_updated(struct kvm_memslots *slots, int slot_id)if (slot_id >= slots->nmemslots) slots->nmemslots = slot_id +1 ; slots->generation++; + slots->slot_cache =NULL ;sort_memslots (slots); }
Signature · 1 line
1.7.4
Re: [PATCH 5/7] KVM: cache the last used slot
Quoted 2 lines · Xiao Guangrong
Cache the last used slot, the hit ratio is more than 35% during my test
If we sort the slots by size, or use a weighted balanced tree, this should come for free.
Signature · 1 line
error compiling committee.c: too many arguments to function
PATCH 6/7 KVM: cleanup traversal used slots
In reply to Xiao GuangrongWe can get all used slots from memslots->slots_sort[], then memslots->nmemslots is only used when memslots is sorted, this operation is not frequency i think, so it can be removed
--- arch/ia64/kvm/kvm-ia64.c | 3 +-- arch/x86/kvm/mmu.c | 9 +++++---- arch/x86/kvm/x86.c | 2 +- include/linux/kvm_host.h | 7 +++++-- virt/kvm/iommu.c | 13 +++++++------ virt/kvm/kvm_main.c | 15 +++++++-------- 6 files changed, 26 insertions(+), 23 deletions(-)
arch/ia64/kvm/kvm-ia64.c +1 -2
@@ -1369,8 +1369,7 @@ static void kvm_release_vm_pages(struct kvm *kvm)unsigned long base_gfn; slots =kvm_memslots (kvm); -for (i =0 ; i < slots->nmemslots; i++) { - memslot = &slots->memslots[i]; +kvm_for_each_memslot (slots, memslot, i) { base_gfn = memslot->base_gfn;for (j =0 ; j < memslot->npages; j++) {
arch/x86/kvm/mmu.c +5 -4
@@ -874,11 +874,11 @@ static int kvm_handle_hva(struct kvm *kvm, unsigned long hva,int ret;int retval =0 ;struct kvm_memslots *slots; +struct kvm_memory_slot *memslot; slots =kvm_memslots (kvm); -for (i =0 ; i < slots->nmemslots; i++) { -struct kvm_memory_slot *memslot = &slots->memslots[i]; +kvm_for_each_memslot (slots, memslot, i) {unsigned long start = memslot->userspace_addr;unsigned long end;
@@ -3671,11 +3671,12 @@ unsigned int kvm_mmu_calculate_mmu_pages(struct kvm *kvm)unsigned int nr_mmu_pages;unsigned int nr_pages =0 ;struct kvm_memslots *slots; +struct kvm_memory_slot *memslot; slots =kvm_memslots (kvm); -for (i =0 ; i < slots->nmemslots; i++) - nr_pages += slots->memslots[i].npages; +kvm_for_each_memslot (slots, memslot, i) + nr_pages += memslot->npages; nr_mmu_pages = nr_pages *KVM_PERMILLE_MMU_PAGES /1000 ; nr_mmu_pages = max(nr_mmu_pages,
arch/x86/kvm/x86.c +1 -1
@@ -3252,7 +3252,7 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, goto out;memcpy (slots, kvm->memslots,sizeof (struct kvm_memslots )); slots->memslots[log->slot].dirty_bitmap = dirty_bitmap; -memslots_updated (slots, log->slot); +memslots_updated (slots); old_slots = kvm->memslots;rcu_assign_pointer (kvm->memslots, slots);
include/linux/kvm_host.h +5 -2
@@ -222,7 +222,6 @@ struct kvm_irq_routing_table {};#endif struct kvm_memslots { -int nmemslots;int used_slots;u64 generation;struct kvm_memory_slot *slot_cache;
@@ -302,6 +301,10 @@ static inline struct kvm_vcpu *kvm_get_vcpu(struct kvm *kvm, int i) idx <atomic_read (&kvm->online_vcpus) && vcpup; \ vcpup = kvm_get_vcpu(kvm, ++idx)) +#define kvm_for_each_memslot (memslots, memslot, i) \ + for (i = 0; i < (memslots)->used_slots && \ + ({memslot = (memslots)->slots_sort[i]; 1; }); i++) +int kvm_vcpu_init (struct kvm_vcpu *vcpu,struct kvm *kvm,unsigned id);void kvm_vcpu_uninit (struct kvm_vcpu *vcpu);
@@ -334,7 +337,7 @@ int is_error_pfn(pfn_t pfn);int is_hwpoison_pfn (pfn_t pfn);int is_fault_pfn (pfn_t pfn);int kvm_is_error_hva (unsigned long addr); -void memslots_updated (struct kvm_memslots *slots,int slot_id); +void memslots_updated (struct kvm_memslots *slots);int kvm_set_memory_region (struct kvm *kvm,struct kvm_userspace_memory_region *mem,int user_alloc);
virt/kvm/iommu.c +7 -6
@@ -128,12 +128,13 @@ static int kvm_iommu_map_memslots(struct kvm *kvm) {int i, idx, r =0 ;struct kvm_memslots *slots; +struct kvm_memory_slot *memslot; idx =srcu_read_lock (&kvm->srcu); slots =kvm_memslots (kvm); -for (i =0 ; i < slots->nmemslots; i++) { - r =kvm_iommu_map_pages (kvm, &slots->memslots[i]); +kvm_for_each_memslot (slots, memslot, i) { + r =kvm_iommu_map_pages (kvm, memslot);if (r)break ; }
@@ -289,14 +290,14 @@ static int kvm_iommu_unmap_memslots(struct kvm *kvm) {int i, idx;struct kvm_memslots *slots; +struct kvm_memory_slot *memslot; idx =srcu_read_lock (&kvm->srcu); slots =kvm_memslots (kvm); -for (i =0 ; i < slots->nmemslots; i++) { -kvm_iommu_put_pages (kvm, slots->memslots[i].base_gfn, - slots->memslots[i].npages); - } +kvm_for_each_memslot (slots, memslot, i) +kvm_iommu_put_pages (kvm, memslot->base_gfn, memslot->npages); +srcu_read_unlock (&kvm->srcu, idx);return 0 ;
virt/kvm/kvm_main.c +7 -8
@@ -544,9 +544,10 @@ void kvm_free_physmem(struct kvm *kvm) {int i;struct kvm_memslots *slots = kvm->memslots; +struct kvm_memory_slot *memslot; -for (i =0 ; i < slots->nmemslots; ++i) -kvm_free_physmem_slot (&slots->memslots[i],NULL ); +kvm_for_each_memslot (slots, memslot, i) +kvm_free_physmem_slot (memslot,NULL );kfree (kvm->memslots); }
@@ -673,7 +674,7 @@ static void sort_memslots(struct kvm_memslots *slots)int i, num =0 ;struct kvm_memory_slot *memslot; -for (i =0 ; i < slots->nmemslots; i++) { +for (i =0 ; i <KVM_MEM_SLOTS_NUM ; i++) { memslot = &slots->memslots[i];if (!memslot->npages)continue ;
@@ -685,10 +686,8 @@ static void sort_memslots(struct kvm_memslots *slots)sort (slots->slots_sort, num,sizeof (memslot), cmp_memslot,NULL ); } -void memslots_updated (struct kvm_memslots *slots,int slot_id) +void memslots_updated (struct kvm_memslots *slots) { -if (slot_id >= slots->nmemslots) - slots->nmemslots = slot_id +1 ; slots->generation++; slots->slot_cache =NULL ;sort_memslots (slots);
@@ -840,7 +839,7 @@ skip_lpage: goto out_free;memcpy (slots, kvm->memslots,sizeof (struct kvm_memslots )); slots->memslots[mem->slot].flags |=KVM_MEMSLOT_INVALID ; -memslots_updated (slots, mem->slot); +memslots_updated (slots); old_memslots = kvm->memslots;rcu_assign_pointer (kvm->memslots, slots);
@@ -882,7 +881,7 @@ skip_lpage: } slots->memslots[mem->slot] = new; -memslots_updated (slots, mem->slot); +memslots_updated (slots); old_memslots = kvm->memslots;rcu_assign_pointer (kvm->memslots, slots);synchronize_srcu_expedited (&kvm->srcu);
Signature · 1 line
1.7.4
PATCH 7/7 KVM: MMU: cache guest page number to guest frame number
In reply to Xiao GuangrongCache guest page number to guest frame number to avoid walk guest page table frequently, the 'vtlb' idea is from Xen.
Note:
we can't use vtlb in ept guests since the guest tlb invalid operation is not intercept(reload CR3, invlpg), also can't used in L2 nnpt guest for the same reason, but we can used it to cache L1's npt page table.
--- arch/x86/include/asm/kvm_host.h | 10 ++++- arch/x86/kvm/mmu.c | 94 +++++++++++++++++++++++++++++++++++++- arch/x86/kvm/mmutrace.h | 79 ++++++++++++++++++++++++++++++++ arch/x86/kvm/paging_tmpl.h | 19 +++++++- 4 files changed, 196 insertions(+), 6 deletions(-)
arch/x86/include/asm/kvm_host.h +9 -1
@@ -234,6 +234,13 @@ struct kvm_pio_request {int size; }; +#define VTLB_ENTRIES (1 << 4) +struct vtlb_entry { +gfn_t page_number; +gfn_t frame_number; +u32 access; +}; +/* * x86 supports 3 paging modes (4-level 64-bit, 3-level 64-bit, and 2-level * 32-bit). The kvm_mmu structure abstracts the details of the current mmu
@@ -267,8 +274,9 @@ struct kvm_mmu {u64 rsvd_bits_mask[2 ][4 ];bool nx; - +bool vtlb_enabled;u64 pdptrs[4 ];/* pae */ +struct vtlb_entry vtlb[VTLB_ENTRIES ]; };struct kvm_vcpu_arch {
arch/x86/kvm/mmu.c +91 -3
146 lines not on this page · raw message
arch/x86/kvm/mmutrace.h +79 -0
85 lines not on this page · raw message
arch/x86/kvm/paging_tmpl.h +17 -2
@@ -261,6 +261,7 @@ walk: walker->pt_access = pt_access; walker->pte_access = pte_access; +vtlb_insert (mmu, addr, walker->gfn, pte_access);pgprintk ("%s: pte %llx pte_access %x pt_access %x\n" , __func__, (u64 )pte, pte_access, pt_access);return 1 ;
@@ -691,12 +692,19 @@ static gpa_t FNAME(gva_to_gpa)(struct kvm_vcpu *vcpu, gva_t vaddr, u32 access, {struct guest_walker walker;gpa_t gpa =UNMAPPED_GVA ; +gfn_t gfn;int r; + gfn =vtlb_lookup (&vcpu->arch.mmu, vaddr, access); +if (gfn !=INVALID_PAGE ) + goto success; + r =FNAME (walk_addr)(&walker, vcpu, vaddr, access);if (r) { - gpa =gfn_to_gpa (walker.gfn); + gfn = walker.gfn; +success: + gpa =gfn_to_gpa (gfn); gpa |= vaddr & ~PAGE_MASK ; }else if (exception) *exception = walker.fault;
@@ -710,12 +718,19 @@ static gpa_t FNAME(gva_to_gpa_nested)(struct kvm_vcpu *vcpu, gva_t vaddr, {struct guest_walker walker;gpa_t gpa =UNMAPPED_GVA ; +gfn_t gfn;int r; + gfn =vtlb_lookup (&vcpu->arch.nested_mmu, vaddr, access); +if (gfn !=INVALID_PAGE ) + goto success; + r =FNAME (walk_addr_nested)(&walker, vcpu, vaddr, access);if (r) { - gpa =gfn_to_gpa (walker.gfn); + gfn = walker.gfn; +success: + gpa =gfn_to_gpa (gfn); gpa |= vaddr & ~PAGE_MASK ; }else if (exception) *exception = walker.fault;
Signature · 1 line
1.7.4
Re: [PATCH 7/7] KVM: MMU: cache guest page number to guest frame number
Quoted 8 lines · Xiao Guangrong
Cache guest page number to guest frame number to avoid walk guest page table frequently, the 'vtlb' idea is from Xen.
Note:
we can't use vtlb in ept guests since the guest tlb invalid operation is notintercept(reload CR3, invlpg), also can't used in L2 nnpt guest for the same reason, but we can used it to cache L1's npt page table.
I'm not so hot about introducing a new mechanism strictly for older hosts... EPT exists in three generations of Intel processors now (Sandy Bridge, Westmere, and Nehalem), and NPT is significantly older.
I'd rather make the guest page table walk faster. I believe that things like get_user() can be significantly faster than copy_from_user() on older hosts, and a four-level page table walk shouldn't be that slow.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 7/7] KVM: MMU: cache guest page number to guest frame number
Quoted 4 lines · Avi Kivity
Quoted 3 lines · Xiao Guangrong
Cache guest page number to guest frame number to avoid walk guest page table frequently, the 'vtlb' idea is from Xen.
Quoted 8 lines
Quoted 5 lines
Note:
we can't use vtlb in ept guests since the guest tlb invalid operation is notintercept(reload CR3, invlpg), also can't used in L2 nnpt guest for the same reason, but we can used it to cache L1's npt page table.I'm not so hot about introducing a new mechanism strictly for older hosts... EPT exists in three generations of Intel processors now (Sandy Bridge, Westmere, and Nehalem), and NPT is significantly older.
Um...so, do we should stop the new features for softmmu, only bug fix is welcome? :-)
Re: [PATCH 7/7] KVM: MMU: cache guest page number to guest frame number
Quoted 11 lines · Xiao Guangrong
Quoted 10 lines · Avi Kivity
Quoted 8 lines · Xiao Guangrong
Cache guest page number to guest frame number to avoid walk guest page table frequently, the 'vtlb' idea is from Xen.
Note:
we can't use vtlb in ept guests since the guest tlb invalid operation is notintercept(reload CR3, invlpg), also can't used in L2 nnpt guest for the same reason, but we can used it to cache L1's npt page table.
Quoted 4 lines
Quoted 2 lines
I'm not so hot about introducing a new mechanism strictly for older hosts... EPT exists in three generations of Intel processors now (Sandy Bridge, Westmere, and Nehalem), and NPT is significantly older.
Um...so, do we should stop the new features for softmmu, only bug fix is welcome? :-)
No. There is always a tradeoff between features and complexity. What I'm saying is that I want to shift the tradeoff, for older processors, towards reducing complexity. An improvement that is very simple, or gives very large gains, will be accepted. A complex improvement that gives small gains may be rejected (but if it's for newer processors, it may be accepted). It's a way for the maintainers to manage the ever growing complexity.
Signature · 1 line
error compiling committee.c: too many arguments to function