unify remote request and kvm_vcpu_kick IPI mechanism
PATCH 1/5 KVM: move kvm_vcpu_kick to virt/kvm/kvm_main.c
In reply to Marcelo TosattiAvoids code duplication.
arch/ia64/kvm/kvm-ia64.c +0 -15
@@ -1857,21 +1857,6 @@ void kvm_arch_hardware_unsetup(void) { } -void kvm_vcpu_kick (struct kvm_vcpu *vcpu) -{ -int me; -int cpu = vcpu->cpu; - -if (waitqueue_active (&vcpu->wq)) -wake_up_interruptible (&vcpu->wq); - - me =get_cpu (); -if (cpu != me && (unsigned ) cpu < nr_cpu_ids &&cpu_online (cpu)) -if (!test_and_set_bit (KVM_REQ_KICK , &vcpu->requests)) -smp_send_reschedule (cpu); -put_cpu (); -} -int kvm_apic_set_irq (struct kvm_vcpu *vcpu,struct kvm_lapic_irq *irq) {return __apic_accept_irq (vcpu, irq->vector);
arch/x86/kvm/x86.c +0 -17
@@ -4914,23 +4914,6 @@ int kvm_arch_vcpu_runnable(struct kvm_vc kvm_cpu_has_interrupt(vcpu)); } -void kvm_vcpu_kick (struct kvm_vcpu *vcpu) -{ -int me; -int cpu = vcpu->cpu; - -if (waitqueue_active (&vcpu->wq)) { -wake_up_interruptible (&vcpu->wq); - ++vcpu->stat.halt_wakeup; - } - - me =get_cpu (); -if (cpu != me && (unsigned )cpu < nr_cpu_ids &&cpu_online (cpu)) -if (!test_and_set_bit (KVM_REQ_KICK , &vcpu->requests)) -smp_send_reschedule (cpu); -put_cpu (); -} -int kvm_arch_interrupt_allowed (struct kvm_vcpu *vcpu) {return kvm_x86_ops->interrupt_allowed (vcpu);
virt/kvm/kvm_main.c +17 -0
@@ -117,6 +117,23 @@ void vcpu_put(struct kvm_vcpu *vcpu)mutex_unlock (&vcpu->mutex); } +void kvm_vcpu_kick (struct kvm_vcpu *vcpu) +{ +int me; +int cpu = vcpu->cpu; + +if (waitqueue_active (&vcpu->wq)) { +wake_up_interruptible (&vcpu->wq); + ++vcpu->stat.halt_wakeup; + } + + me =get_cpu (); +if (cpu != me && (unsigned )cpu < nr_cpu_ids &&cpu_online (cpu)) +if (!test_and_set_bit (KVM_REQ_KICK , &vcpu->requests)) +smp_send_reschedule (cpu); +put_cpu (); +} +static void ack_flush (void *_completed) { }
PATCH 2/5 KVM: reintroduce guest mode bit and unify remote request code
In reply to Marcelo TosattiSplit KVM_REQ_KICKED in two bits: KVM_VCPU_KICKED, to indicate whether a vcpu has been IPI'ed, and KVM_VCPU_GUEST_MODE, in a separate vcpu_state variable.
Unify remote requests with kvm_vcpu_kick.
Synchronous requests wait on KVM_VCPU_GUEST_MODE, via wait_on_bit/wake_up_bit.
arch/x86/kvm/x86.c +10 -4
@@ -3586,11 +3586,14 @@ static int vcpu_enter_guest(struct kvm_vlocal_irq_disable (); -clear_bit (KVM_REQ_KICK , &vcpu->requests); -smp_mb__after_clear_bit (); +set_bit (KVM_VCPU_GUEST_MODE , &vcpu->vcpu_state); +barrier ();if (vcpu->requests ||need_resched () ||signal_pending (current)) { -set_bit (KVM_REQ_KICK , &vcpu->requests); +clear_bit (KVM_VCPU_KICKED , &vcpu->vcpu_state); +clear_bit (KVM_VCPU_GUEST_MODE , &vcpu->vcpu_state); +smp_mb__after_clear_bit (); +wake_up_bit (&vcpu->vcpu_state,KVM_VCPU_GUEST_MODE );local_irq_enable ();preempt_enable (); r =1 ;
@@ -3642,7 +3645,10 @@ static int vcpu_enter_guest(struct kvm_vset_debugreg (vcpu->arch.host_dr6,6 );set_debugreg (vcpu->arch.host_dr7,7 ); -set_bit (KVM_REQ_KICK , &vcpu->requests); +clear_bit (KVM_VCPU_KICKED , &vcpu->vcpu_state); +clear_bit (KVM_VCPU_GUEST_MODE , &vcpu->vcpu_state); +smp_mb__after_clear_bit (); +wake_up_bit (&vcpu->vcpu_state,KVM_VCPU_GUEST_MODE );local_irq_enable (); ++vcpu->stat.exits;
include/linux/kvm_host.h +5 -0
@@ -42,6 +42,9 @@#define KVM_USERSPACE_IRQ_SOURCE_ID 0 +#define KVM_VCPU_GUEST_MODE 0 +#define KVM_VCPU_KICKED 1 +struct kvm ;struct kvm_vcpu ;extern struct kmem_cache *kvm_vcpu_cache;
@@ -83,6 +86,7 @@ struct kvm_vcpu {int cpu;struct kvm_run *run;unsigned long requests; +unsigned long vcpu_state;unsigned long guest_debug;int fpu_active;int guest_fpu_loaded;
@@ -362,6 +366,7 @@ void kvm_arch_sync_events(struct kvm *kvint kvm_cpu_has_pending_timer (struct kvm_vcpu *vcpu);void kvm_vcpu_kick (struct kvm_vcpu *vcpu); +void kvm_vcpu_ipi (struct kvm_vcpu *vcpu);int kvm_is_mmio_pfn (pfn_t pfn);
virt/kvm/kvm_main.c +38 -6
62 lines not on this page · raw message
arch/ia64/kvm/kvm-ia64.c +7 -3
@@ -655,12 +655,13 @@ again: host_ctx =kvm_get_host_context (vcpu); guest_ctx =kvm_get_guest_context (vcpu); -clear_bit (KVM_REQ_KICK , &vcpu->requests); - r =kvm_vcpu_pre_transition (vcpu);if (r <0 ) goto vcpu_run_fail; +set_bit (KVM_VCPU_GUEST_MODE , &vcpu->vcpu_state); +barrier (); +up_read (&vcpu->kvm->slots_lock);kvm_guest_enter ();
@@ -672,7 +673,10 @@ again:kvm_vcpu_post_transition (vcpu); vcpu->arch.launched =1 ; -set_bit (KVM_REQ_KICK , &vcpu->requests); +clear_bit (KVM_VCPU_KICKED , &vcpu->vcpu_state); +clear_bit (KVM_VCPU_GUEST_MODE , &vcpu->vcpu_state); +smp_mb__after_clear_bit (); +wake_up_bit (&vcpu->vcpu_state,KVM_VCPU_GUEST_MODE );local_irq_enable ();/*
PATCH 3/5 KVM: switch REQ_TLB_FLUSH/REQ_MMU_RELOAD to kvm_vcpus_request
In reply to Marcelo Tosattivirt/kvm/kvm_main.c +3 -3
@@ -202,13 +202,13 @@ static void kvm_vcpus_request(struct kvmvoid kvm_flush_remote_tlbs (struct kvm *kvm) { -if (make_all_cpus_request (kvm,KVM_REQ_TLB_FLUSH )) - ++kvm->stat.remote_tlb_flush; +kvm_vcpus_request (kvm,KVM_REQ_TLB_FLUSH ); + ++kvm->stat.remote_tlb_flush; }void kvm_reload_remote_mmus (struct kvm *kvm) { -make_all_cpus_request (kvm,KVM_REQ_MMU_RELOAD ); +kvm_vcpus_request (kvm,KVM_REQ_MMU_RELOAD ); }int kvm_vcpu_init (struct kvm_vcpu *vcpu,struct kvm *kvm,unsigned id)
PATCH 5/5 KVM: x86: drop duplicat kvm_flush_remote_tlbs
In reply to Marcelo Tosattikvm_mmu_slot_remove_write_access already calls it.
arch/x86/kvm/x86.c +0 -2
@@ -2148,7 +2148,6 @@ int kvm_vm_ioctl_get_dirty_log(struct kvspin_lock (&kvm->mmu_lock);kvm_mmu_slot_remove_write_access (kvm, log->slot);spin_unlock (&kvm->mmu_lock); -kvm_flush_remote_tlbs (kvm); memslot = &kvm->memslots[log->slot]; n =ALIGN (memslot->npages,BITS_PER_LONG ) /8 ;memset (memslot->dirty_bitmap,0 , n);
@@ -4904,7 +4903,6 @@ int kvm_arch_set_memory_region(struct kvkvm_mmu_slot_remove_write_access (kvm, mem->slot);spin_unlock (&kvm->mmu_lock); -kvm_flush_remote_tlbs (kvm);return 0 ; }
cover unify remote request and kvm_vcpu_kick IPI mechanism
Unify remote request (TLB_FLUSH and MMU_RELOAD) with kvm_vcpu_kick mechanism.
The new wait_on_bit based scheme also allows finer optimization of different request types, and makes future enhancements easier (such as request to a single vcpu, or subset of vcpus).
kernel compilation is 2.5 - 3% faster with shadow paging.
See individual patches for details.
PATCH 4/5 KVM: remove make_all_cpus_request
Obsoleted by kvm_vcpus_request
virt/kvm/kvm_main.c +0 -35
@@ -142,40 +142,6 @@ void kvm_vcpu_ipi(struct kvm_vcpu *vcpu)put_cpu (); } -static void ack_flush (void *_completed) -{ -} - -static bool make_all_cpus_request (struct kvm *kvm,unsigned int req) -{ -int i, cpu, me; -cpumask_var_t cpus; -bool called = true; -struct kvm_vcpu *vcpu; - -if (alloc_cpumask_var (&cpus,GFP_ATOMIC )) -cpumask_clear (cpus); - -spin_lock (&kvm->requests_lock); - me =smp_processor_id (); -kvm_for_each_vcpu (i, vcpu, kvm) { -if (test_and_set_bit (req, &vcpu->requests)) -continue ; - cpu = vcpu->cpu; -if (cpus !=NULL && cpu !=-1 && cpu != me) -cpumask_set_cpu (cpu, cpus); - } -if (unlikely (cpus ==NULL )) -smp_call_function_many (cpu_online_mask, ack_flush,NULL ,1 ); -else if (!cpumask_empty (cpus)) -smp_call_function_many (cpus, ack_flush,NULL ,1 ); -else - called = false; -spin_unlock (&kvm->requests_lock); -free_cpumask_var (cpus); -return called; -} -static int kvm_req_wait (void *unused) {cpu_relax ();
@@ -415,7 +381,6 @@ static struct kvm *kvm_create_vm(void) kvm->mm = current->mm;atomic_inc (&kvm->mm->mm_count);spin_lock_init (&kvm->mmu_lock); -spin_lock_init (&kvm->requests_lock);kvm_io_bus_init (&kvm->pio_bus);kvm_eventfd_init (kvm);mutex_init (&kvm->lock);
include/linux/kvm_host.h +0 -1
@@ -157,7 +157,6 @@ struct kvm_irq_routing_table {};struct kvm {spinlock_t mmu_lock; -spinlock_t requests_lock;struct rw_semaphore slots_lock;struct mm_struct *mm;/* userspace tied to this vm */ int nmemslots;
Re: [patch 2/5] KVM: reintroduce guest mode bit and unify remote request code
In reply to Marcelo TosattiQuoted 9 lines · Marcelo Tosatti
Split
KVM_REQ_KICKEDin two bits:KVM_VCPU_KICKED, to indicate whether a vcpu has been IPI'ed, andKVM_VCPU_GUEST_MODE, in a separatevcpu_statevariable.Unify remote requests with
kvm_vcpu_kick.Synchronous requests wait on
KVM_VCPU_GUEST_MODE, via wait_on_bit/wake_up_bit.
I did miss guest_mode.
include/linux/kvm_host.h struct kvm_vcpu { · line 89
Quoted 2 lines
include/linux/kvm_host.h +1
-0+unsigned long vcpu_state;
Why not bool guest_mode? Saves two atomics per exit.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [patch 2/5] KVM: reintroduce guest mode bit and unify remote request code
In reply to Marcelo TosattiQuoted 39 lines · Marcelo Tosatti
virt/kvm/kvm_main.c +27
-3+} + +void kvm_vcpu_ipi (struct kvm_vcpu *vcpu) +{ +int me; +int cpu = vcpu->cpu; me =get_cpu (); -if (cpu != me&& (unsigned )cpu< nr_cpu_ids&&cpu_online (cpu)) -if (!test_and_set_bit (KVM_REQ_KICK ,&vcpu->requests)) -smp_send_reschedule (cpu); +if (cpu != me&& (unsigned )cpu< nr_cpu_ids&&cpu_online (cpu)) { +if (test_bit (KVM_VCPU_GUEST_MODE ,&vcpu->vcpu_state)) { +if (!test_and_set_bit (KVM_VCPU_KICKED , + &vcpu->vcpu_state)) +smp_send_reschedule (cpu); + } + }put_cpu (); }@@ -168,6 +176,30 @@ static bool make_all_cpus_request(structreturn called; } +static int kvm_req_wait (void *unused) +{ +cpu_relax (); +return 0 ; +} + +static void kvm_vcpu_request (struct kvm_vcpu *vcpu,unsigned int req) +{ +set_bit (req,&vcpu->requests); +barrier (); +kvm_vcpu_ipi (vcpu); +wait_on_bit (&vcpu->vcpu_state,KVM_VCPU_GUEST_MODE , kvm_req_wait, +TASK_UNINTERRUPTIBLE ); +}
virt/kvm/kvm_main.c static bool make_all_cpus_request(struct · line 202
Quoted 10 lines
virt/kvm/kvm_main.c +9
-0+ +static void kvm_vcpus_request (struct kvm *kvm,unsigned int req) +{ +int i; +struct kvm_vcpu *vcpu; + +kvm_for_each_vcpu (i, vcpu, kvm) +kvm_vcpu_request (vcpu, req); +}
Gleb notes there are two problems here: instead of using a multicast IPI, you're sending multiple unicast IPIs. Second, you're serializing the waiting. It would be better to batch-send the IPIs, then batch-wait for results.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [patch 2/5] KVM: reintroduce guest mode bit and unify remote request code
In reply to Avi KivityQuoted 11 lines · Avi Kivity
Quoted 9 lines · Marcelo Tosatti
Split
KVM_REQ_KICKEDin two bits:KVM_VCPU_KICKED, to indicate whether a vcpu has been IPI'ed, andKVM_VCPU_GUEST_MODE, in a separatevcpu_statevariable.Unify remote requests with
kvm_vcpu_kick.Synchronous requests wait on
KVM_VCPU_GUEST_MODE, via wait_on_bit/wake_up_bit.
Quoted 6 lines
I did miss
guest_mode.include/linux/kvm_host.h struct kvm_vcpu { · line 89
Quoted 2 lines
include/linux/kvm_host.h +1
-0+unsigned long vcpu_state;Why not bool
guest_mode? Saves two atomics per exit.
It must be atomic since GUEST_MODE / VCPU_KICKED bits are manipulated by multiple CPU's.
Re: [patch 2/5] KVM: reintroduce guest mode bit and unify remote request code
In reply to Avi KivityQuoted 48 lines · Avi Kivity
Quoted 46 lines · Marcelo Tosatti
virt/kvm/kvm_main.c +34
-3+} + +void kvm_vcpu_ipi (struct kvm_vcpu *vcpu) +{ +int me; +int cpu = vcpu->cpu; me =get_cpu (); -if (cpu != me&& (unsigned )cpu< nr_cpu_ids&&cpu_online (cpu)) -if (!test_and_set_bit (KVM_REQ_KICK ,&vcpu->requests)) -smp_send_reschedule (cpu); +if (cpu != me&& (unsigned )cpu< nr_cpu_ids&&cpu_online (cpu)) { +if (test_bit (KVM_VCPU_GUEST_MODE ,&vcpu->vcpu_state)) { +if (!test_and_set_bit (KVM_VCPU_KICKED , + &vcpu->vcpu_state)) +smp_send_reschedule (cpu); + } + }put_cpu (); }@@ -168,6 +176,30 @@ static bool make_all_cpus_request(structreturn called; } +static int kvm_req_wait (void *unused) +{ +cpu_relax (); +return 0 ; +} + +static void kvm_vcpu_request (struct kvm_vcpu *vcpu,unsigned int req) +{ +set_bit (req,&vcpu->requests); +barrier (); +kvm_vcpu_ipi (vcpu); +wait_on_bit (&vcpu->vcpu_state,KVM_VCPU_GUEST_MODE , kvm_req_wait, +TASK_UNINTERRUPTIBLE ); +} + +static void kvm_vcpus_request (struct kvm *kvm,unsigned int req) +{ +int i; +struct kvm_vcpu *vcpu; + +kvm_for_each_vcpu (i, vcpu, kvm)
Quoted 8 lines
virt/kvm/kvm_main.c static bool make_all_cpus_request(struct
Quoted 3 lines
virt/kvm/kvm_main.c +2
-0+kvm_vcpu_request (vcpu, req); +}Gleb notes there are two problems here: instead of using a multicast IPI, you're sending multiple unicast IPIs. Second, you're serializing the waiting. It would be better to batch-send the IPIs, then batch-wait for results.
Right. Playing with multiple variants of batched send/wait but so far haven't been able to see significant improvements for REQ_FLUSH/REQ_RELOAD.
Batched send will probably be more visible in guest IPI emulation.
Note however that even with multiple unicast IPIs this change collapses kvm_vcpu_kick with the remote requests, so you decrease the number of IPI's.
Was hoping to include these changes incrementally?
void kvm_vcpus_request(struct kvm *kvm, unsigned int req) {
- int i;
+ int i, me, cpu;
struct kvm_vcpu *vcpu;
+ cpumask_var_t wait_cpus, kick_cpus;
+
+ if (alloc_cpumask_var(&wait_cpus, GFP_ATOMIC))
+ cpumask_clear(wait_cpus);
+
+ if (alloc_cpumask_var(&kick_cpus, GFP_ATOMIC))
+ cpumask_clear(kick_cpus);
+
+ me = get_cpu();
+ kvm_for_each_vcpu(i, vcpu, kvm) {
+ set_bit(req, &vcpu->requests);
+ barrier();
+ cpu = vcpu->cpu;
+ if (test_bit(KVM_VCPU_GUEST_MODE, &vcpu->vcpu_state)) {
+ if (cpu != -1 && cpu != me) {
+ if (wait_cpus != NULL)
+ cpumask_set_cpu(cpu, wait_cpus);
+ if (kick_cpus != NULL)
+ if (!test_and_set_bit(KVM_VCPU_KICKED,
+ &vcpu->vcpu_state))
+ cpumask_set_cpu(cpu, kick_cpus);
+ }
+ }
+ }
+ if (unlikely(kick_cpus == NULL))
+ smp_call_function_many(cpu_online_mask, ack_flush,
+ NULL, 1);
+ else if (!cpumask_empty(kick_cpus))
+ smp_send_reschedule_many(kick_cpus);
kvm_for_each_vcpu(i, vcpu, kvm)
- kvm_vcpu_request(vcpu, req);
+ if (cpumask_test_cpu(vcpu->cpu, wait_cpus))
+ if (test_bit(req, &vcpu->requests))
+ wait_on_bit(&vcpu->vcpu_state, KVM_VCPU_GUEST_MODE,
+ kvm_req_wait, TASK_UNINTERRUPTIBLE);
+ put_cpu();
+
+ free_cpumask_var(wait_cpus);
+ free_cpumask_var(kick_cpus);
}
Re: [patch 2/5] KVM: reintroduce guest mode bit and unify remote request code
In reply to Marcelo TosattiQuoted 5 lines · Marcelo Tosatti
Quoted 2 lines
Why not bool
guest_mode? Saves two atomics per exit.It must be atomic since
GUEST_MODE/VCPU_KICKEDbits are manipulated by multiple CPU's.
bools are atomic.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [patch 2/5] KVM: reintroduce guest mode bit and unify remote request code
Quoted 8 lines · Avi Kivity
Quoted 5 lines · Marcelo Tosatti
Quoted 2 lines
Why not bool
guest_mode? Saves two atomics per exit.It must be atomic since
GUEST_MODE/VCPU_KICKEDbits are manipulated by multiple CPU's.bools are atomic.
OK.
- VCPU_KICKED requires test_and_set. GUEST_MODE/VCPU_KICKED accesses must not be reordered.
(OK, could have GUEST_MODE in a bool even so, but its easier to read by keeping them together, at least to me).
- Its easier to cacheline align with longs rather than bools?
- From testing it seems the LOCK prefix is not heavy, as long as its cpu local (probably due to 7.1.4 Effects of a LOCK Operation on Internal Processor Caches?).
BTW,
7.1.2.2 Software Controlled Bus Locking
Software should access semaphores (shared memory used for signalling between multiple processors) using identical addresses and operand lengths. For example, if one processor accesses a semaphore using a word access, other processors should not access the semaphore using a byte access.
The bit operations use 32-bit access, but the vcpu->requests check in vcpu_enter_guest uses 64-bit access.
Is that safe?
RFC 1/1 KVM: x86: conditionally acquire/release slots_lock on entry/exit
In reply to Marcelo Tosattiperf report shows heavy overhead from down/up of slots_lock.
Attempted to remove slots_lock by having vcpus stop on a synchronization point, but this introduced further complexity (a vcpu can be scheduled out before reaching the synchronization point, and can sched back in at points which are slots_lock protected, etc).
This patch changes vcpu_enter_guest to conditionally release/acquire slots_lock in case a vcpu state bit is set.
vmexit performance improves by 5-10% on UP guest.
arch/x86/kvm/vmx.c +2 -2
@@ -2169,7 +2169,7 @@ static int alloc_apic_access_page(structstruct kvm_userspace_memory_region kvm_userspace_mem;int r =0 ; -down_write (&kvm->slots_lock); +kvm_grab_global_lock (kvm);if (kvm->arch.apic_access_page) goto out; kvm_userspace_mem.slot =APIC_ACCESS_PAGE_PRIVATE_MEMSLOT ;
@@ -2191,7 +2191,7 @@ static int alloc_identity_pagetable(strustruct kvm_userspace_memory_region kvm_userspace_mem;int r =0 ; -down_write (&kvm->slots_lock); +kvm_grab_global_lock (kvm);if (kvm->arch.ept_identity_pagetable) goto out; kvm_userspace_mem.slot =IDENTITY_PAGETABLE_PRIVATE_MEMSLOT ;
arch/x86/kvm/x86.c +11 -8
@@ -1926,7 +1926,7 @@ static int kvm_vm_ioctl_set_nr_mmu_pagesif (kvm_nr_mmu_pages <KVM_MIN_ALLOC_MMU_PAGES )return -EINVAL ; -down_write (&kvm->slots_lock); +kvm_grab_global_lock (kvm);spin_lock (&kvm->mmu_lock);kvm_mmu_change_mmu_pages (kvm, kvm_nr_mmu_pages);
@@ -1982,7 +1982,7 @@ static int kvm_vm_ioctl_set_memory_alias < alias->target_phys_addr) goto out; -down_write (&kvm->slots_lock); +kvm_grab_global_lock (kvm);spin_lock (&kvm->mmu_lock); p = &kvm->arch.aliases[alias->slot];
@@ -2137,7 +2137,7 @@ int kvm_vm_ioctl_get_dirty_log(struct kvstruct kvm_memory_slot *memslot;int is_dirty =0 ; -down_write (&kvm->slots_lock); +kvm_grab_global_lock (kvm); r =kvm_get_dirty_log (kvm, log, &is_dirty);if (r)
@@ -2253,7 +2253,7 @@ long kvm_arch_vm_ioctl(struct file *filp sizeof(struct kvm_pit_config))) goto out; create_pit: -down_write (&kvm->slots_lock); +kvm_grab_global_lock (kvm); r = -EEXIST ;if (kvm->arch.vpit) goto create_pit_unlock;
@@ -3548,7 +3548,7 @@ static void inject_pending_event(structstatic int vcpu_enter_guest (struct kvm_vcpu *vcpu) { -int r; +int r, dropped_slots_lock =0 ;bool req_int_win = !irqchip_in_kernel (vcpu->kvm) && vcpu->run->request_interrupt_window;
@@ -3616,7 +3616,10 @@ static int vcpu_enter_guest(struct kvm_vkvm_lapic_sync_to_vapic (vcpu); } -up_read (&vcpu->kvm->slots_lock); +if (unlikely (test_bit (KVM_VCPU_DROP_LOCK , &vcpu->vcpu_state))) { + dropped_slots_lock =1 ; +up_read (&vcpu->kvm->slots_lock); + }kvm_guest_enter ();
@@ -3668,8 +3671,8 @@ static int vcpu_enter_guest(struct kvm_vpreempt_enable (); -down_read (&vcpu->kvm->slots_lock); - +if (dropped_slots_lock) +down_read (&vcpu->kvm->slots_lock);/* * Profile KVM exit RIPs: */
include/linux/kvm_host.h +2 -0
@@ -44,6 +44,7 @@#define KVM_VCPU_GUEST_MODE 0#define KVM_VCPU_KICKED 1 +#define KVM_VCPU_DROP_LOCK 2struct kvm ;struct kvm_vcpu ;
@@ -408,6 +409,7 @@ void kvm_unregister_irq_ack_notifier(str struct kvm_irq_ack_notifier *kian);int kvm_request_irq_source_id (struct kvm *kvm);void kvm_free_irq_source_id (struct kvm *kvm,int irq_source_id); +void kvm_grab_global_lock (struct kvm *kvm);/* For vcpu->arch.iommu_flags */ #define KVM_IOMMU_CACHE_COHERENCY 0x1
virt/kvm/coalesced_mmio.c +2 -2
@@ -117,7 +117,7 @@ int kvm_vm_ioctl_register_coalesced_mmioif (dev ==NULL )return -EINVAL ; -down_write (&kvm->slots_lock); +kvm_grab_global_lock (kvm);if (dev->nb_zones >=KVM_COALESCED_MMIO_ZONE_MAX ) {up_write (&kvm->slots_lock);return -ENOBUFS ;
@@ -140,7 +140,7 @@ int kvm_vm_ioctl_unregister_coalesced_mmif (dev ==NULL )return -EINVAL ; -down_write (&kvm->slots_lock); +kvm_grab_global_lock (kvm); i = dev->nb_zones;while (i) {
virt/kvm/eventfd.c +2 -2
@@ -498,7 +498,7 @@ kvm_assign_ioeventfd(struct kvm *kvm, stelse p->wildcard = true; -down_write (&kvm->slots_lock); +kvm_grab_global_lock (kvm);/* Verify that there isnt a match already */ if (ioeventfd_check_collision (kvm, p)) {
@@ -541,7 +541,7 @@ kvm_deassign_ioeventfd(struct kvm *kvm,if (IS_ERR (eventfd))return PTR_ERR (eventfd); -down_write (&kvm->slots_lock); +kvm_grab_global_lock (kvm);list_for_each_entry_safe (p, tmp, &kvm->ioeventfds, list) {bool wildcard = !(args->flags &KVM_IOEVENTFD_FLAG_DATAMATCH );
virt/kvm/kvm_main.c +19 -3
@@ -787,6 +787,22 @@ void kvm_reload_remote_mmus(struct kvm *kvm_vcpus_request (kvm,KVM_REQ_MMU_RELOAD ); } +void kvm_grab_global_lock (struct kvm *kvm) +{ +int i; +struct kvm_vcpu *vcpu; + +kvm_for_each_vcpu (i, vcpu, kvm) { +set_bit (KVM_VCPU_DROP_LOCK , &vcpu->vcpu_state); +barrier (); +kvm_vcpu_ipi (vcpu); + } +down_write (&kvm->slots_lock); +kvm_for_each_vcpu (i, vcpu, kvm) +clear_bit (KVM_VCPU_DROP_LOCK , &vcpu->vcpu_state); +} +EXPORT_SYMBOL_GPL (kvm_grab_global_lock); +int kvm_vcpu_init (struct kvm_vcpu *vcpu,struct kvm *kvm,unsigned id) {struct page *page;
@@ -1286,7 +1302,7 @@ int kvm_set_memory_region(struct kvm *kv {int r; -down_write (&kvm->slots_lock); +kvm_grab_global_lock (kvm); r =__kvm_set_memory_region (kvm, mem, user_alloc);up_write (&kvm->slots_lock);return r;
@@ -2556,7 +2572,7 @@ int kvm_io_bus_register_dev(struct kvm * {int ret; -down_write (&kvm->slots_lock); +kvm_grab_global_lock (kvm); ret =__kvm_io_bus_register_dev (bus, dev);up_write (&kvm->slots_lock);
@@ -2579,7 +2595,7 @@ void kvm_io_bus_unregister_dev(struct kvstruct kvm_io_bus *bus, struct kvm_io_device *dev) { - down_write(&kvm->slots_lock); +kvm_grab_global_lock (kvm );__kvm_io_bus_unregister_dev (bus, dev);up_write (&kvm->slots_lock); }
Re: [RFC] KVM: x86: conditionally acquire/release slots_lock on entry/exit
Quoted 7 lines · Marcelo Tosatti
perf report shows heavy overhead from down/up of
slots_lock.Attempted to remove
slots_lockby having vcpus stop on a synchronization point, but this introduced further complexity (a vcpu can be scheduled out before reaching the synchronization point, and can sched back in at points which areslots_lockprotected, etc).
Quoted 5 lines
This patch changes
vcpu_enter_guestto conditionally release/acquireslots_lockin case a vcpu state bit is set.vmexit performance improves by 5-10% on UP guest.
Sorry, it looks pretty complex. Have you considered using srcu? It seems to me down/up_read() can be replaced by srcu_read_lock/unlock(), and after proper conversion of memslots and io_bus to rcu_assign_pointer(), we can just add synchronize_srcu() immediately after changing stuff (of course mmu_lock still needs to be held when updating slots).
Signature · 2 lines
I have a truly marvellous patch that fixes the bug which this signature is too narrow to contain.
Re: [RFC] KVM: x86: conditionally acquire/release slots_lock on entry/exit
Quoted 8 lines · Avi Kivity
Quoted 7 lines · Marcelo Tosatti
perf report shows heavy overhead from down/up of
slots_lock.Attempted to remove
slots_lockby having vcpus stop on a synchronization point, but this introduced further complexity (a vcpu can be scheduled out before reaching the synchronization point, and can sched back in at points which areslots_lockprotected, etc).
Quoted 7 lines
Quoted 5 lines
This patch changes
vcpu_enter_guestto conditionally release/acquireslots_lockin case a vcpu state bit is set.vmexit performance improves by 5-10% on UP guest.
Sorry, it looks pretty complex.
Why?
Quoted 5 lines
Have you considered using srcu? It seems to me down/up_read() can be replaced by srcu_read_lock/unlock(), and after proper conversion of memslots and
io_bustorcu_assign_pointer(), we can just addsynchronize_srcu()immediately after changing stuff (of coursemmu_lockstill needs to be held when updating slots).
I don't see RCU as being suitable because in certain operations you want to stop writers (on behalf of vcpus), do something, and let them continue afterwards. The dirty log, for example. Or any operation that wants to modify lockless vcpu specific data.
Also, synchronize_srcu() is limited to preemptible sections.
io_bus could use RCU, but I think being able to stop vcpus is also a different requirement. Do you have any suggestion on how to do it in a better way?
Re: [RFC] KVM: x86: conditionally acquire/release slots_lock on entry/exit
Quoted 14 lines · Marcelo Tosatti
Quoted 12 lines · Avi Kivity
Quoted 10 lines · Marcelo Tosatti
perf report shows heavy overhead from down/up of
slots_lock.Attempted to remove
slots_lockby having vcpus stop on a synchronization point, but this introduced further complexity (a vcpu can be scheduled out before reaching the synchronization point, and can sched back in at points which areslots_lockprotected, etc).This patch changes
vcpu_enter_guestto conditionally release/acquireslots_lockin case a vcpu state bit is set.
Quoted 7 lines
Quoted 5 lines
Quoted 3 lines
vmexit performance improves by 5-10% on UP guest.
Sorry, it looks pretty complex.
Why?
There's a new locking protocol in there. I prefer sticking with the existing kernel plumbing, or it gets more and more difficult knowing who protects what and in what order you can do things.
Quoted 11 lines
Quoted 6 lines
Have you considered using srcu? It seems to me down/up_read() can be replaced by srcu_read_lock/unlock(), and after proper conversion of memslots and
io_bustorcu_assign_pointer(), we can just addsynchronize_srcu()immediately after changing stuff (of coursemmu_lockstill needs to be held when updating slots).I don't see RCU as being suitable because in certain operations you want to stop writers (on behalf of vcpus), do something, and let them continue afterwards. The dirty log, for example. Or any operation that wants to modify lockless vcpu specific data.
kvm_flush_remote_tlbs() (which you'd call after mmu operations), will get cpus out of guest mode, and synchronize_srcu() will wait for them to drop the srcu "read lock". So it really happens naturally: do an RCU update, send some request to all vcpus, synchronize_srcu(), done.
Quoted 6 lines
Also,
synchronize_srcu()is limited to preemptible sections.
io_buscould use RCU, but I think being able to stop vcpus is also a different requirement. Do you have any suggestion on how to do it in a better way?
We don't need to stop vcpus, just kick them out of guest mode to let them notice the new data. SRCU does that well.
Signature · 2 lines
I have a truly marvellous patch that fixes the bug which this signature is too narrow to contain.
Re: [patch 2/5] KVM: reintroduce guest mode bit and unify remote request code
In reply to Marcelo TosattiQuoted 11 lines · Marcelo Tosatti
Quoted 9 lines · Avi Kivity
Quoted 7 lines · Marcelo Tosatti
Quoted 3 lines
Why not bool
guest_mode? Saves two atomics per exit.It must be atomic since
GUEST_MODE/VCPU_KICKEDbits are manipulated by multiple CPU's.
Quoted 7 lines
Quoted 2 lines
bools are atomic.
OK.
-
VCPU_KICKEDrequirestest_and_set. GUEST_MODE/VCPU_KICKED accesses must not be reordered.
Why do we need both, btw? Set your vcpu->requests bit, if guest_mode is true, clear it and IPI. So guest_mode=false means, we might be in guest mode but if so we're due for a kick anyway.
Quoted 5 lines
(OK, could have
GUEST_MODEin a bool even so, but its easier to read by keeping them together, at least to me).- Its easier to cacheline align with longs rather than bools?
To cacheline align we need to pack everything important at the front of the structure.
Quoted 4 lines
- From testing it seems the LOCK prefix is not heavy, as long as its cpu local (probably due to 7.1.4 Effects of a LOCK Operation on Internal Processor Caches?).
Yes, in newer processors atomics are not nearly as expensive as they used to be.
Quoted 4 lines
BTW,
7.1.2.2 Software Controlled Bus Locking
Quoted 9 lines
Software should access semaphores (shared memory used for signalling between multiple processors) using identical addresses and operand lengths. For example, if one processor accesses a semaphore using a word access, other processors should not access the semaphore using a byte access.
The bit operations use 32-bit access, but the
vcpu->requestscheck invcpu_enter_guestuses 64-bit access.
That's true, and bitops sometimes even uses byte operations.
Quoted 2 lines
Is that safe?
My guess yes, but not efficient.
Signature · 2 lines
I have a truly marvellous patch that fixes the bug which this signature is too narrow to contain.
Re: [patch 2/5] KVM: reintroduce guest mode bit and unify remote request code
Quoted 2 lines · Avi Kivity
Why do we need both, btw? Set your
vcpu->requestsbit, ifguest_modeis true, clear it and IPI. So guest_mode=false means, we might be in guest mode but if so we're due for a kick anyway.
No, this introduces a race.
Signature · 2 lines
I have a truly marvellous patch that fixes the bug which this signature is too narrow to contain.
Re: [RFC] KVM: x86: conditionally acquire/release slots_lock on entry/exit
In reply to Avi KivityQuoted 46 lines · Avi Kivity
Quoted 14 lines · Marcelo Tosatti
Quoted 12 lines · Avi Kivity
Quoted 10 lines · Marcelo Tosatti
perf report shows heavy overhead from down/up of
slots_lock.Attempted to remove
slots_lockby having vcpus stop on a synchronization point, but this introduced further complexity (a vcpu can be scheduled out before reaching the synchronization point, and can sched back in at points which areslots_lockprotected, etc).This patch changes
vcpu_enter_guestto conditionally release/acquireslots_lockin case a vcpu state bit is set.Quoted 7 lines
Quoted 5 lines
Quoted 3 lines
vmexit performance improves by 5-10% on UP guest.
Sorry, it looks pretty complex.
Why?
There's a new locking protocol in there. I prefer sticking with the existing kernel plumbing, or it gets more and more difficult knowing who protects what and in what order you can do things.
Quoted 11 lines
Quoted 6 lines
Have you considered using srcu? It seems to me down/up_read() can be replaced by srcu_read_lock/unlock(), and after proper conversion of memslots and
io_bustorcu_assign_pointer(), we can just addsynchronize_srcu()immediately after changing stuff (of coursemmu_lockstill needs to be held when updating slots).I don't see RCU as being suitable because in certain operations you want to stop writers (on behalf of vcpus), do something, and let them continue afterwards. The dirty log, for example. Or any operation that wants to modify lockless vcpu specific data.
kvm_flush_remote_tlbs()(which you'd call after mmu operations), will get cpus out of guest mode, andsynchronize_srcu()will wait for them to drop the srcu "read lock". So it really happens naturally: do an RCU update, send some request to all vcpus,synchronize_srcu(), done.Quoted 2 lines
Also,
synchronize_srcu()is limited to preemptible sections.
Quoted 7 lines
Quoted 4 lines
io_buscould use RCU, but I think being able to stop vcpus is also a different requirement. Do you have any suggestion on how to do it in a better way?We don't need to stop vcpus, just kick them out of guest mode to let them notice the new data. SRCU does that well.
Two problems:
1. The removal of memslots/aliases and zapping of mmu (to remove any shadow pages with stale sp->gfn) must be atomic from the POV of the pagefault path. Otherwise something like this can happen:
fault path set_memory_region
walk_addr fetches and validatestable_gfns
delete memslot synchronize_srcu
fetch creates shadow
page with nonexistant sp->gfn
OR
mmu_alloc_roots path set_memory_region
delete memslotroot_gfn = vcpu->arch.cr3 << PAGE_SHIFTmmu_check_root(root_gfn) synchronize_rcukvm_mmu_get_page()
kvm_mmu_zap_all
Accesses between kvm_mmu_get_page and kvm_mmu_zap_all window can see shadow pages with stale gfn.
But, if you still think its worthwhile to use RCU, at least handling gfn_to_memslot / unalias_gfn errors _and_ adding mmu_notifier_retry invalidation to set_memory_region path will be necessary (so that gfn_to_pfn validation, in the fault path, is discarded in case of memslot/alias update).
2. Another complication is that memslot creation and kvm_iommu_map_pages are not atomic.
create memslotsynchronize_srcu
<----- vcpu grabs gfn reference without
iommu mapping.kvm_iommu_map_pages
Which can be solved by changing kvm_iommu_map_pages (and new gfn_to_pfn helper) to use base_gfn,npages,hva information from somewhere else other than visible kvm->memslots (so that when the slot becomes visible its already iommu mapped).
So it appears to me using RCU introduces more complications / subtle details than mutual exclusion here. The new request bit which the original patch introduces is limited to enabling/disabling conditional acquision of slots_lock (calling it a "new locking protocol" is unfair) to improve write acquision latency.
Better ideas/directions welcome.
Re: [RFC] KVM: x86: conditionally acquire/release slots_lock on entry/exit
Quoted 4 lines · Marcelo Tosatti
Quoted 4 lines
We don't need to stop vcpus, just kick them out of guest mode to let them notice the new data. SRCU does that well.
Quoted 8 lines
Two problems:
1. The removal of memslots/aliases and zapping of mmu (to remove any shadow pages with stale
sp->gfn) must be atomic from the POV of the pagefault path. Otherwise something like this can happen:fault path
set_memory_region
srcu_read_lock()
Quoted 7 lines
walk_addrfetches and validatestable_gfns
delete memslotsynchronize_srcufetch creates shadow
srcu_read_unlock()
Quoted 2 lines
page with nonexistant
sp->gfn
I think synchronize_srcu() will be deferred until the fault path is complete (and srcu_read_unlock() runs). Copying someone who knows for sure.
Quoted 4 lines
OR
mmu_alloc_rootspathset_memory_region
srcu_read_lock()
Quoted 6 lines
delete memslot
root_gfn= vcpu->arch.cr3<<PAGE_SHIFTmmu_check_root(root_gfn)synchronize_rcukvm_mmu_get_page()
srcu_read_unlock()
Quoted 2 lines
kvm_mmu_zap_all
Ditto, srcu_read_lock() protects us.
Quoted 9 lines
Accesses between
kvm_mmu_get_pageandkvm_mmu_zap_allwindow can see shadow pages with stale gfn.But, if you still think its worthwhile to use RCU, at least handling
gfn_to_memslot/unalias_gfnerrors _and_ addingmmu_notifier_retryinvalidation toset_memory_regionpath will be necessary (so thatgfn_to_pfnvalidation, in the fault path, is discarded in case of memslot/alias update).
It really is worthwhile to reuse complex infrastructure instead of writing new infrastructure.
Quoted 9 lines
2. Another complication is that memslot creation and
kvm_iommu_map_pagesare not atomic.create memslot
synchronize_srcu
<----- vcpu grabs gfn reference without
iommu mapping.kvm_iommu_map_pages
Quoted 5 lines
Which can be solved by changing
kvm_iommu_map_pages(and newgfn_to_pfnhelper) to use base_gfn,npages,hva information from somewhere else other than visiblekvm->memslots(so that when the slot becomes visible its already iommu mapped).
Yes. It can accept a memslots structure instead of deriving it from kvm->memslots. Then we do a rcu_assign_pointer() to switch the tables.
Quoted 6 lines
So it appears to me using RCU introduces more complications / subtle details than mutual exclusion here. The new request bit which the original patch introduces is limited to enabling/disabling conditional acquision of
slots_lock(calling it a "new locking protocol" is unfair) to improve write acquision latency.
It's true that it is not a new locking protocol. But I feel it is worthwhile to try to use rcu for this; at least it will make it easier for newcomers (provided they understand rcu).
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [RFC] KVM: x86: conditionally acquire/release slots_lock on entry/exit
Quoted 24 lines · Avi Kivity
Quoted 4 lines · Marcelo Tosatti
Quoted 4 lines
We don't need to stop vcpus, just kick them out of guest mode to let them notice the new data. SRCU does that well.
Quoted 8 lines
Two problems:
1. The removal of memslots/aliases and zapping of mmu (to remove any shadow pages with stale
sp->gfn) must be atomic from the POV of the pagefault path. Otherwise something like this can happen:fault path
set_memory_region
srcu_read_lock()Quoted 7 lines
walk_addrfetches and validatestable_gfns
delete memslotsynchronize_srcufetch creates shadow
Quoted 8 lines
srcu_read_unlock()Quoted 2 lines
page with nonexistant
sp->gfnI think
synchronize_srcu()will be deferred until the fault path is complete (andsrcu_read_unlock()runs). Copying someone who knows for sure.
Yes, synchronize_srcu() will block until srcu_read_unlock() in this scenario, assuming that the same srcu_struct is used by both.
Quoted 14 lines
Quoted 4 lines
OR
mmu_alloc_rootspathset_memory_region
srcu_read_lock()Quoted 6 lines
delete memslot
root_gfn= vcpu->arch.cr3<<PAGE_SHIFTmmu_check_root(root_gfn)synchronize_rcukvm_mmu_get_page()
Quoted 6 lines
srcu_read_unlock()Quoted 2 lines
kvm_mmu_zap_allDitto,
srcu_read_lock()protects us.
Yep!
Quoted 11 lines
Quoted 8 lines
Accesses between
kvm_mmu_get_pageandkvm_mmu_zap_allwindow can see shadow pages with stale gfn.But, if you still think its worthwhile to use RCU, at least handling
gfn_to_memslot/unalias_gfnerrors _and_ addingmmu_notifier_retryinvalidation toset_memory_regionpath will be necessary (so thatgfn_to_pfnvalidation, in the fault path, is discarded in case of memslot/alias update).It really is worthwhile to reuse complex infrastructure instead of writing new infrastructure.
Marcelo, in your first example, is your concern that the fault path needs to detect the memslot deletion? Or that the use of sp->gfn "leaks" out of the SRCU read-side critical section?
Thanx, Paul
Quoted 32 lines
Quoted 9 lines
2. Another complication is that memslot creation and
kvm_iommu_map_pagesare not atomic.create memslot
synchronize_srcu
<----- vcpu grabs gfn reference without
iommu mapping.kvm_iommu_map_pagesQuoted 4 lines
Which can be solved by changing
kvm_iommu_map_pages(and newgfn_to_pfnhelper) to use base_gfn,npages,hva information from somewhere else other than visiblekvm->memslots(so that when the slot becomes visible its already iommu mapped).Yes. It can accept a memslots structure instead of deriving it from
kvm->memslots. Then we do arcu_assign_pointer()to switch the tables.Quoted 6 lines
So it appears to me using RCU introduces more complications / subtle details than mutual exclusion here. The new request bit which the original patch introduces is limited to enabling/disabling conditional acquision of
slots_lock(calling it a "new locking protocol" is unfair) to improve write acquision latency.It's true that it is not a new locking protocol. But I feel it is worthwhile to try to use rcu for this; at least it will make it easier for newcomers (provided they understand rcu).
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [RFC] KVM: x86: conditionally acquire/release slots_lock on entry/exit
Quoted 30 lines · Paul E. McKenney
Quoted 29 lines · Avi Kivity
Quoted 4 lines · Marcelo Tosatti
Quoted 4 lines
We don't need to stop vcpus, just kick them out of guest mode to let them notice the new data. SRCU does that well.
Quoted 8 lines
Two problems:
1. The removal of memslots/aliases and zapping of mmu (to remove any shadow pages with stale
sp->gfn) must be atomic from the POV of the pagefault path. Otherwise something like this can happen:fault path
set_memory_region
srcu_read_lock()Quoted 7 lines
walk_addrfetches and validatestable_gfns
delete memslotsynchronize_srcufetch creates shadow
srcu_read_unlock()Quoted 2 lines
page with nonexistant
sp->gfn
Quoted 6 lines
Quoted 3 lines
I think
synchronize_srcu()will be deferred until the fault path is complete (andsrcu_read_unlock()runs). Copying someone who knows for sure.Yes,
synchronize_srcu()will block untilsrcu_read_unlock()in this scenario, assuming that the samesrcu_structis used by both.
Right it will. But this does not stop the fault path from creating shadow pages with stale sp->gfn (the only way to do that would be mutual exclusion AFAICS).
Quoted 14 lines
Quoted 14 lines
Quoted 4 lines
OR
mmu_alloc_rootspathset_memory_region
srcu_read_lock()Quoted 6 lines
delete memslot
root_gfn= vcpu->arch.cr3<<PAGE_SHIFTmmu_check_root(root_gfn)synchronize_rcukvm_mmu_get_page()
Quoted 8 lines
Quoted 6 lines
srcu_read_unlock()Quoted 2 lines
kvm_mmu_zap_allDitto,
srcu_read_lock()protects us.Yep!
The RCU read-protected side does not stop a new memslots pointer from being assigned (with rcu_assign_pointer), does it?
Quoted 9 lines
Quoted 9 lines
Quoted 8 lines
Accesses between
kvm_mmu_get_pageandkvm_mmu_zap_allwindow can see shadow pages with stale gfn.But, if you still think its worthwhile to use RCU, at least handling
gfn_to_memslot/unalias_gfnerrors _and_ addingmmu_notifier_retryinvalidation toset_memory_regionpath will be necessary (so thatgfn_to_pfnvalidation, in the fault path, is discarded in case of memslot/alias update).
Quoted 5 lines
Quoted 2 lines
It really is worthwhile to reuse complex infrastructure instead of writing new infrastructure.
Marcelo, in your first example, is your concern that the fault path needs to detect the memslot deletion?
Yes, it needs to invalidate the leakage, which in this case is a shadow page data structure which was created containing information from a now deleted memslot.
Quoted 2 lines
Or that the use of
sp->gfn"leaks" out of the SRCU read-side critical section?
Yes, use of a stale sp->gfn leaks outside of the SRCU read side critical section and currently the rest of the code is not ready to deal with that... but it will have to.
Quoted 18 lines
Thanx, Paul
Quoted 16 lines
Quoted 9 lines
2. Another complication is that memslot creation and
kvm_iommu_map_pagesare not atomic.create memslot
synchronize_srcu
<----- vcpu grabs gfn reference without
iommu mapping.kvm_iommu_map_pagesQuoted 4 lines
Which can be solved by changing
kvm_iommu_map_pages(and newgfn_to_pfnhelper) to use base_gfn,npages,hva information from somewhere else other than visiblekvm->memslots(so that when the slot becomes visible its already iommu mapped).Yes. It can accept a memslots structure instead of deriving it from
kvm->memslots. Then we do arcu_assign_pointer()to switch the tables.
Alright.
Quoted 10 lines
Quoted 10 lines
Quoted 6 lines
So it appears to me using RCU introduces more complications / subtle details than mutual exclusion here. The new request bit which the original patch introduces is limited to enabling/disabling conditional acquision of
slots_lock(calling it a "new locking protocol" is unfair) to improve write acquision latency.It's true that it is not a new locking protocol. But I feel it is worthwhile to try to use rcu for this; at least it will make it easier for newcomers (provided they understand rcu).
Sure.
Re: [RFC] KVM: x86: conditionally acquire/release slots_lock on entry/exit
Quoted 5 lines · Marcelo Tosatti
Quoted 5 lines
Quoted 5 lines
I think
synchronize_srcu()will be deferred until the fault path is complete (andsrcu_read_unlock()runs). Copying someone who knows for sure.
Quoted 7 lines
Quoted 3 lines
Yes,
synchronize_srcu()will block untilsrcu_read_unlock()in this scenario, assuming that the samesrcu_structis used by both.Right it will. But this does not stop the fault path from creating shadow pages with stale
sp->gfn(the only way to do that would be mutual exclusion AFAICS).
So we put the kvm_mmu_zap_pages() call as part of the synchronize_srcu() callback to take advantage of the srcu guarantees. We know that when when the callback is called all new reads see the new slots and all old readers have completed.
Quoted 4 lines
The RCU read-protected side does not stop a new memslots pointer from being assigned (with
rcu_assign_pointer), does it?
It doesn't. It only gives you a point in time where you know no one is using the old pointer, but before it has been deleted.
Signature · 2 lines
I have a truly marvellous patch that fixes the bug which this signature is too narrow to contain.
Re: [RFC] KVM: x86: conditionally acquire/release slots_lock on entry/exit
Quoted 5 lines · Avi Kivity
Quoted 3 lines
Right it will. But this does not stop the fault path from creating shadow pages with stale
sp->gfn(the only way to do that would be mutual exclusion AFAICS).So we put the
kvm_mmu_zap_pages()call as part of thesynchronize_srcu()callback to take advantage of the srcu guarantees. We know that when when the callback is called all new reads see the new slots and all old readers have completed.
I think I see your concern - assigning sp->gfn leaks information out of the srcu critical section.
Two ways out:
1) copy kvm->slots into sp->slots and use it when dropping the shadow page. Intrusive and increases shadow footprint.
1b) Instead of sp->slots, use a 1-bit generation counter. Even uglier but reduces the shadow footprint.
2) instead of removing the slot in rcu_assign_pointer(), mark it invalid. gfn_to_page() will fail on such slots but the teardown paths (like unaccount_shadow) continue to work. One we've zapped the mmu we drop the slot completely (can do in place, no need to rcu_assign_pointer).
Signature · 1 line
error compiling committee.c: too many arguments to function