Skip to contentSkip to conversation

kvm: Improving directed yield in PLE handler

RFC PATCH Superseded Raghavendra K T 3 participants 12 messages Jul 12, 2012 – Jul 19, 2012

Subsystems (5)
x86 KVM s390 KVM x86 +1 more subsystems

v3 of 5 Superseded Latest is v5 →

lore
Share & tools
lore
#0 lore raw

v3 cover kvm: Improving directed yield in PLE handler

Currently Pause Loop Exit (PLE) handler is doing directed yield to a random vcpu on pl-exit. We already have filtering while choosing the candidate to yield_to. This change adds more checks while choosing a candidate to yield_to.

On a large vcpu guests, there is a high probability of yielding to the same vcpu who had recently done a pause-loop exit. Such a yield can lead to the vcpu spinning again.

The patchset keeps track of the pause loop exit and gives chance to a vcpu which has:

(a) Not done pause loop exit at all (probably he is preempted lock-holder)

(b) vcpu skipped in last iteration because it did pause loop exit, and probably has become eligible now (next eligible lock holder)

This concept also helps in cpu relax interception cases which use same handler.

Changes since v2:
- Move ple structure to common code (Avi)
- rename pause_loop_exited to cpu_relax_intercepted (Avi)
- add config HAVE_KVM_CPU_RELAX_INTERCEPT (Avi)
- Drop superfluous curly braces (Ingo)

Changes since v1:
- Add more documentation for structure and algorithm and Rename plo ==> ple (Rik).
- change dy_eligible initial value to false. (otherwise very first directed yield will not be skipped. (Nikunj)
- fixup signoff/from issue

Future enhancements:
(1) Currently we have a boolean to decide on eligibility of vcpu. It would be nice if I get feedback on guest (>32 vcpu) whether we can improve better with integer counter. (with counter = say f(log n )).

(2) We have not considered system load during iteration of vcpu. With that information we can limit the scan and also decide whether schedule() is better. [ I am able to use #kicked vcpus to decide on this But may be there are better ideas like information from global loadavg.]

(3) We can exploit this further with PV patches since it also knows about next eligible lock-holder.

Summary: There is a very good improvement for moderate / no over-commit scenario
for kvm based guest on PLE machine.

Results: kernbench improves by around 30%, 6% for 1x,2x
ebizzy improves by around 87%, 23% for 1x,2x

Links
V1: https://lkml.org/lkml/2012/7/9/32

V2: https://lkml.org/lkml/2012/7/10/392

Raghavendra K T (3):
config: Add config to support ple or cpu relax optimzation kvm : Note down when cpu relax intercepted or pause loop exited kvm : Choose a better candidate for directed yield

---
 arch/s390/kvm/Kconfig    |    1 +
 arch/x86/kvm/Kconfig     |    1 +
 include/linux/kvm_host.h |   12 ++++++++++++
 virt/kvm/Kconfig         |    3 +++
 virt/kvm/kvm_main.c      |   38 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 55 insertions(+), 0 deletions(-)
#1 lore raw

RFC PATCH v3 1/3 kvm/config: Add config to support ple or cpu relax optimzation

From: Raghavendra K T <[email protected]>

---

 arch/s390/kvm/Kconfig |    1 +
 arch/x86/kvm/Kconfig  |    1 +
 virt/kvm/Kconfig      |    3 +++
 3 files changed, 5 insertions(+), 0 deletions(-)
arch/s390/kvm/Kconfig +1 -0
index 78eb984..a6e2677 100644
@@ -21,6 +21,7 @@ config KVM
 	depends on HAVE_KVM && EXPERIMENTAL
 	select PREEMPT_NOTIFIERS
 	select ANON_INODES
+	select HAVE_KVM_CPU_RELAX_INTERCEPT
 	---help---
 	  Support hosting paravirtualized guest machines using the SIE
 	  virtualization capability on the mainframe. This should work
arch/x86/kvm/Kconfig +1 -0
index a28f338..45c044f 100644
@@ -37,6 +37,7 @@ config KVM
 	select TASK_DELAY_ACCT
 	select PERF_EVENTS
 	select HAVE_KVM_MSI
+	select HAVE_KVM_CPU_RELAX_INTERCEPT
 	---help---
 	  Support hosting fully virtualized guest machines using hardware
 	  virtualization extensions.  You will need a fairly recent
virt/kvm/Kconfig +3 -0
index 28694f4..d01b24b 100644
@@ -21,3 +21,6 @@ config KVM_ASYNC_PF
 
 config HAVE_KVM_MSI
        bool
+
+config HAVE_KVM_CPU_RELAX_INTERCEPT
+       bool
#2 lore raw

RFC PATCH v3 2/3 kvm: Note down when cpu relax intercepted or pause loop exited

In reply to Raghavendra K T

From: Raghavendra K T <[email protected]>

Noting pause loop exited or cpu relax intercepted vcpu helps in filtering right candidate to yield. Wrong selection of vcpu; i.e., a vcpu that just did a pl-exit or cpu relax intercepted may contribute to performance degradation.

---
v2 patches were

 include/linux/kvm_host.h |   12 ++++++++++++
 virt/kvm/kvm_main.c      |    4 ++++
 2 files changed, 16 insertions(+), 0 deletions(-)
include/linux/kvm_host.h +12 -0
index c446435..4ec1cf0 100644
@@ -183,6 +183,18 @@ struct kvm_vcpu {
 	} async_pf;
 #endif
 
+#ifdef CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT
+	/*
+	 * Cpu relax intercept or pause loop exit optimization
+	 * cpu_relax_intercepted: set when a vcpu does a pause loop exit
+	 *  or cpu relax intercepted.
+	 * dy_eligible: indicates whether vcpu is eligible for directed yield.
+	 */
+	struct {
+		bool cpu_relax_intercepted;
+		bool dy_eligible;
+	} ple;
+#endif
 	struct kvm_vcpu_arch arch;
 };
 
virt/kvm/kvm_main.c +4 -0
index 7e14068..4ec0120 100644
@@ -235,6 +235,8 @@ int kvm_vcpu_init(struct kvm_vcpu *vcpu, struct kvm *kvm, unsigned id)
 		goto fail;
 	}
 	vcpu->run = page_address(page);
+	vcpu->ple.cpu_relax_intercepted = false;
+	vcpu->ple.dy_eligible = false;
 
 	r = kvm_arch_vcpu_init(vcpu);
 	if (r < 0)
@@ -1577,6 +1579,7 @@ void kvm_vcpu_on_spin(struct kvm_vcpu *me)
 	int pass;
 	int i;
 
+	me->ple.cpu_relax_intercepted = true;
 	/*
 	 * We boost the priority of a VCPU that is runnable but not
 	 * currently running, because it got preempted by something
@@ -1602,6 +1605,7 @@ void kvm_vcpu_on_spin(struct kvm_vcpu *me)
 			}
 		}
 	}
+	me->ple.cpu_relax_intercepted = false;
 }
 EXPORT_SYMBOL_GPL(kvm_vcpu_on_spin);
 
#3 lore raw

Re: [PATCH RFC V3 2/3] kvm: Note down when cpu relax intercepted or pause loop exited

Quoted 1 line · Raghavendra K T

+#ifdef CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT

[...]

Quoted 5 lines
include/linux/kvm_host.h +5 -0
+	struct {
+		bool cpu_relax_intercepted;
+		bool dy_eligible;
+	} ple;
+#endif

[...]

virt/kvm/kvm_main.c int kvm_vcpu_init(struct kvm_vcpu *vcpu, struct kvm *kvm, unsigned id) · line 239

Quoted 4 lines
virt/kvm/kvm_main.c +2 -0
 	}
 	vcpu->run = page_address(page);
+	vcpu->ple.cpu_relax_intercepted = false;
+	vcpu->ple.dy_eligible = false;

This struct is only defined if CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT is set, but here it is always accessed. Will break on !x86 && !s390.

virt/kvm/kvm_main.c void kvm_vcpu_on_spin(struct kvm_vcpu *me) · line 1582

Quoted 8 lines
virt/kvm/kvm_main.c +1 -0
 	r = kvm_arch_vcpu_init(vcpu);
 	if (r < 0)
@@ -1577,6 +1579,7 @@ void kvm_vcpu_on_spin(struct kvm_vcpu *me)
 	int pass;
 	int i;
 
+	me->ple.cpu_relax_intercepted = true;

dito

virt/kvm/kvm_main.c void kvm_vcpu_on_spin(struct kvm_vcpu *me) · line 1608

Quoted 8 lines
virt/kvm/kvm_main.c +1 -0
 	/*
 	 * We boost the priority of a VCPU that is runnable but not
 	 * currently running, because it got preempted by something
@@ -1602,6 +1605,7 @@ void kvm_vcpu_on_spin(struct kvm_vcpu *me)
 			}
 		}
 	}
+	me->ple.cpu_relax_intercepted = false;

again.

maybe define static inline access functions in kvm_host.h that are no-ops if CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT is not set.

#4 lore raw

Re: [PATCH RFC V3 2/3] kvm: Note down when cpu relax intercepted or pause loop exited

Quoted 9 lines · Christian Borntraeger
Quoted 1 line · Raghavendra K T

+#ifdef CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT

[...]

Quoted 5 lines
include/linux/kvm_host.h +5 -0
+	struct {
+		bool cpu_relax_intercepted;
+		bool dy_eligible;
+	} ple;
+#endif

[...]

Quoted 7 lines

virt/kvm/kvm_main.c int kvm_vcpu_init(struct kvm_vcpu *vcpu, struct kvm *kvm, unsigned id) · line 239

Quoted 4 lines
  	}
  	vcpu->run = page_address(page);
virt/kvm/kvm_main.c +2 -0
+	vcpu->ple.cpu_relax_intercepted = false;
+	vcpu->ple.dy_eligible = false;

This struct is only defined if CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT is set, but here it is always accessed. Will break on !x86&& !s390.

Yes! I forgot about archs in init function.
How about having
#ifdef CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT
vcpu->ple.cpu_relax_intercepted = false;
vcpu->ple.dy_eligible = false;
#endif

This would solve all the problem.

Quoted 10 lines

virt/kvm/kvm_main.c void kvm_vcpu_on_spin(struct kvm_vcpu *me) · line 1582

Quoted 8 lines
  	r = kvm_arch_vcpu_init(vcpu);
  	if (r<  0)
virt/kvm/kvm_main.c +1 -0
@@ -1577,6 +1579,7 @@ void kvm_vcpu_on_spin(struct kvm_vcpu *me)
  	int pass;
  	int i;
 
+	me->ple.cpu_relax_intercepted = true;

dito

currently vcpu_on_spin is used only by x86 and s390. so if some other arch in future uses vcpu_on_spin, I believe they also have to enable CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT
what do you think?

otherwise we have to add hook everywhere

Quoted 14 lines

virt/kvm/kvm_main.c void kvm_vcpu_on_spin(struct kvm_vcpu *me) · line 1608

Quoted 8 lines
virt/kvm/kvm_main.c +1 -0
  	/*
  	 * We boost the priority of a VCPU that is runnable but not
  	 * currently running, because it got preempted by something
@@ -1602,6 +1605,7 @@ void kvm_vcpu_on_spin(struct kvm_vcpu *me)
  			}
  		}
  	}
+	me->ple.cpu_relax_intercepted = false;

again.

maybe define static inline access functions in kvm_host.h that are no-ops if CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT is not set.

#5 lore raw

Re: [PATCH RFC V3 2/3] kvm: Note down when cpu relax intercepted or pause loop exited

Quoted 8 lines · Raghavendra K T

Yes! I forgot about archs in init function.
How about having
#ifdef CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT
vcpu->ple.cpu_relax_intercepted = false;
vcpu->ple.dy_eligible = false;
#endif

This would solve all the problem.

No, you need to mask all places....

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

      r = kvm_arch_vcpu_init(vcpu);
      if (r<  0)
virt/kvm/kvm_main.c +0 -0
@@ -1577,6 +1579,7 @@ void kvm_vcpu_on_spin(struct kvm_vcpu *me)
      int pass;
      int i;
 
Quoted 8 lines
Quoted 3 lines

virt/kvm/kvm_main.c void kvm_vcpu_on_spin(struct kvm_vcpu *me) · line 1582

Quoted 1 line
virt/kvm/kvm_main.c +1 -0
+    me->ple.cpu_relax_intercepted = true;

dito

currently vcpu_on_spin is used only by x86 and s390. so if some other arch in future uses vcpu_on_spin, I believe they also have to enable CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT
what do you think?

...because this function is compiled no matter if called or not.

Quoted 3 lines
Quoted 3 lines

maybe define static inline access functions in kvm_host.h that are no-ops if CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT is not set.

As I already said, can you have a look at using access functions?

#6 lore raw

Re: [PATCH RFC V3 2/3] kvm: Note down when cpu relax intercepted or pause loop exited

Quoted 5 lines · Christian Borntraeger
Quoted 2 lines · Raghavendra K T
Quoted 2 lines

maybe define static inline access functions in kvm_host.h that are no-ops if CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT is not set.

As I already said, can you have a look at using access functions?

Yes. will do.

#7 lore raw

Re: [PATCH RFC V3 2/3] kvm: Note down when cpu relax intercepted or pause loop exited

In reply to Christian Borntraeger
Quoted 9 lines · Raghavendra K T
Quoted 1 line

+#ifdef CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT

[...]

Quoted 5 lines
include/linux/kvm_host.h +5 -0
+	struct {
+		bool cpu_relax_intercepted;
+		bool dy_eligible;
+	} ple;
+#endif

[...]

Quoted 7 lines

virt/kvm/kvm_main.c int kvm_vcpu_init(struct kvm_vcpu *vcpu, struct kvm *kvm, unsigned id) · line 239

Quoted 4 lines
virt/kvm/kvm_main.c +2 -0
 	}
 	vcpu->run = page_address(page);
+	vcpu->ple.cpu_relax_intercepted = false;
+	vcpu->ple.dy_eligible = false;

This struct is only defined if CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT is set, but here it is always accessed. Will break on !x86 && !s390.

How about moving this struct definition outside the CONFIG. i.e it would be available by default.
If any arch cares to use vcpu_on_spin(), they would get the benefit by default.

This would avoid all the CONFIG magic that we would have to do otherwise.

Signature · 2 lines
Thanks and Regards
Srikar
#8 lore raw

Re: [PATCH RFC V3 2/3] kvm: Note down when cpu relax intercepted or pause loop exited

Quoted 17 lines · Srikar Dronamraju
Quoted 9 lines · Raghavendra K T
Quoted 1 line

+#ifdef CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT

[...]

Quoted 5 lines
include/linux/kvm_host.h +5 -0
+	struct {
+		bool cpu_relax_intercepted;
+		bool dy_eligible;
+	} ple;
+#endif

[...]

Quoted 7 lines

virt/kvm/kvm_main.c int kvm_vcpu_init(struct kvm_vcpu *vcpu, struct kvm *kvm, unsigned id) · line 239

Quoted 4 lines
  	}
  	vcpu->run = page_address(page);
virt/kvm/kvm_main.c +2 -0
+	vcpu->ple.cpu_relax_intercepted = false;
+	vcpu->ple.dy_eligible = false;

This struct is only defined if CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT is set, but here it is always accessed. Will break on !x86&& !s390.

Quoted 8 lines

How about moving this struct definition outside the CONFIG. i.e it would be available by default.
If any arch cares to use vcpu_on_spin(), they would get the benefit by default.

This would avoid all the CONFIG magic that we would have to do otherwise.

Okay, after discussing with Christian,
- even if ppc uses vcpu_on spin we will still be left with ia64 (though broken currently) and arm (is on way).
- those who want to opt-out of this optimization but still wish to use vcpu_spin, we have flexibility.

So with that in mind I am spinning V4 with CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT. Let us see how it goes.

#9 lore raw

RFC PATCH v3 3/3 kvm: Choose better candidate for directed yield

In reply to Raghavendra K T

From: Raghavendra K T <[email protected]>

Currently, on a large vcpu guests, there is a high probability of yielding to the same vcpu who had recently done a pause-loop exit or cpu relax intercepted. Such a yield can lead to the vcpu spinning again and hence degrade the performance.

The patchset keeps track of the pause loop exit/cpu relax interception and gives chance to a vcpu which:
(a) Has not done pause loop exit or cpu relax intercepted at all (probably he is preempted lock-holder)
(b) Was skipped in last iteration because it did pause loop exit or cpu relax intercepted, and probably has become eligible now (next eligible lock holder)

---
v2 patches were:

 virt/kvm/kvm_main.c |   34 ++++++++++++++++++++++++++++++++++
 1 files changed, 34 insertions(+), 0 deletions(-)
virt/kvm/kvm_main.c +34 -0
index 4ec0120..50f6e60 100644
@@ -1570,6 +1570,38 @@ bool kvm_vcpu_yield_to(struct kvm_vcpu *target)
 }
 EXPORT_SYMBOL_GPL(kvm_vcpu_yield_to);
 
+/*
+ * Helper that checks whether a VCPU is eligible for directed yield.
+ * Most eligible candidate to yield is decided by following heuristics:
+ *
+ *  (a) VCPU which has not done pl-exit or cpu relax intercepted recently
+ *  (preempted lock holder), indicated by @cpu_relax_intercepted.
+ *  Set at the beiginning and cleared at the end of interception/PLE handler.
+ *
+ *  (b) VCPU which has done pl-exit/ cpu relax intercepted but did not get
+ *  chance last time (mostly it has become eligible now since we have probably
+ *  yielded to lockholder in last iteration. This is done by toggling
+ *  @dy_eligible each time a VCPU checked for eligibility.)
+ *
+ *  Yielding to a recently pl-exited/cpu relax intercepted VCPU before yielding
+ *  to preempted lock-holder could result in wrong VCPU selection and CPU
+ *  burning. Giving priority for a potential lock-holder increases lock
+ *  progress.
+ */
+bool kvm_vcpu_check_and_update_eligible(struct kvm_vcpu *vcpu)
+{
+	bool eligible;
+
+	eligible = !vcpu->ple.cpu_relax_intercepted ||
+			(vcpu->ple.cpu_relax_intercepted &&
+			 vcpu->ple.dy_eligible);
+
+	if (vcpu->ple.cpu_relax_intercepted)
+		vcpu->ple.dy_eligible = !vcpu->ple.dy_eligible;
+
+	return eligible;
+}
+
 void kvm_vcpu_on_spin(struct kvm_vcpu *me)
 {
 	struct kvm *kvm = me->kvm;
@@ -1598,6 +1630,8 @@ void kvm_vcpu_on_spin(struct kvm_vcpu *me)
 				continue;
 			if (waitqueue_active(&vcpu->wq))
 				continue;
+			if (!kvm_vcpu_check_and_update_eligible(vcpu))
+				continue;
 			if (kvm_vcpu_yield_to(vcpu)) {
 				kvm->last_boosted_vcpu = i;
 				yielded = 1;
#10 lore raw
In reply to Raghavendra K T
Quoted 12 lines · Raghavendra K T

Currently Pause Loop Exit (PLE) handler is doing directed yield to a random vcpu on pl-exit. We already have filtering while choosing the candidate to yield_to. This change adds more checks while choosing a candidate to yield_to.

On a large vcpu guests, there is a high probability of yielding to the same vcpu who had recently done a pause-loop exit. Such a yield can lead to the vcpu spinning again.

The patchset keeps track of the pause loop exit and gives chance to a vcpu which has:

Quoted 6 lines

(a) Not done pause loop exit at all (probably he is preempted lock-holder)

(b) vcpu skipped in last iteration because it did pause loop exit, and probably has become eligible now (next eligible lock holder)

This concept also helps in cpu relax interception cases which use same handler.

The patches are tested on x86 only since I don't have access to s390 machine. Please let me know if changes are ok on s390.

#11 lore raw

v3 cover kvm: Improving directed yield in PLE handler

From: Raghavendra K T <[email protected]>

Currently, on a large vcpu guests, there is a high probability of yielding to the same vcpu who had recently done a pause-loop exit or cpu relax intercepted. Such a yield can lead to the vcpu spinning again and hence degrade the performance.

The patchset keeps track of the pause loop exit/cpu relax interception and gives chance to a vcpu which:
(a) Has not done pause loop exit or cpu relax intercepted at all (probably he is preempted lock-holder)
(b) Was skipped in last iteration because it did pause loop exit or cpu relax intercepted, and probably has become eligible now (next eligible lock holder)

---
V2 was:

Changelog: Added comment on locking as suggested by Avi

 include/linux/kvm_host.h |    5 +++++
 virt/kvm/kvm_main.c      |   42 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 47 insertions(+), 0 deletions(-)
include/linux/kvm_host.h +5 -0
index 34ce296..952427d 100644
@@ -923,6 +923,11 @@ static inline void kvm_vcpu_set_dy_eligible(struct kvm_vcpu *vcpu, bool val)
 {
 }
 
+static inline bool kvm_vcpu_eligible_for_directed_yield(struct kvm_vcpu *vcpu)
+{
+	return true;
+}
+
 #endif /* CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT */
 #endif
 
virt/kvm/kvm_main.c +42 -0

60 lines not on this page · raw message