Skip to contentSkip to conversation

Paravirt-spinlock implementation for KVM guests (Version 0)

RFC PATCH In review Srivatsa Vaddagiri 5 participants 24 messages Jul 26, 2010 – Aug 3, 2010

Subsystems (6)
x86 KVM Driver core KVM x86 +2 more subsystems
lore
Share & tools
lore
#0 lore raw

cover Paravirt-spinlock implementation for KVM guests (Version 0)

This patch-series implements paravirt-spinlock implementation for KVM guests, based heavily on Xen's implementation. I tried to refactor Xen's spinlock implementation to make it common for both Xen and KVM - but found that few differences between Xen and KVM (Xen has the ability to block on a particular event/irq for example) _and_ the fact that the guest kernel can be compiled to support both Xen and KVM hypervisors (CONFIG_XEN and CONFIG_KVM_GUEST can both be turned on) makes the "common" code a eye-sore. There will have to be:

	if (xen) {
		...
	} else if (kvm) {
		..
	}

or possibly:

alternative(NOP, some_xen_specific_call, ....)

type of code in the common implementation.

For the time-being, I have made this KVM-specific only. At somepoint in future, I hope this can be made common between Xen/KVM.

More background and results for this patch below:

What is the Problem being solved?
=================================

Guest operating system can be preempted by hypervisor at any arbitrary point. There is no mechanism (that I know of) where guest OS can disable preemption for certain periods of time. One noticeable effect of this is with regard to locking. Lets say one virtual-cpu of a guest (VCPUA) grabs a spinlock and before it could relinquish the lock is preempted by hypervisor. The time-of-preemption (when the virtual cpu is off the cpu) can be quite large. In that period, if another of guest OS's virtual cpu (VCPUB) tries grabbing the same lock, it could end up spin-waiting a _long_ time, burning cycles unnecessarily. To add to the woes, VCPUB may actually be waiting for VCPUA to yield before it can run on the same (physical) cpu. This is termed as the "lock-holder preemption" (LHP) problem. The effect of it can be quite serious. For ex:
http://lkml.org/lkml/2010/4/11/108 reported 80% performance degradation because of an issue attributed to LHP problem.

Solutions
=========

There are several solutions to this problem.

a. Realize that a lock-holder could have been preempted, and avoid spin-waiting
too long. Instead, yield cycles (to the lock-holder perhaps). This is a common solution adopted by most paravirtualized-guests (Xen, s390, powerpc).

b. Avoid preempting a lock-holder while its holding a (spin-) lock.

In this scheme, guest OS can hint (set some flag in memory shared with hypervisor) whenever its holding a lock and hypervisor could defer preempting the guest vcpu when its holding a lock. With this scheme, we should never have a lock-acquiring vcpu spin on a preempted vcpu to release its lock. If ever it spins, its because somebody *currently running* is holding the lock - and hence it won't have to spin-wait too long. IOW we are pro-actively trying to prevent the LHP problem from occuring in the first place. This should improve job turnaround time for some workloads. [1] has some results based on this approach.

c. Share run-status of vcpu with guests. This could be used to optimize
routines like mutex_spin_on_owner().

Hypervisor could share run-status of vcpus in guest kernel memory. This would allow us to optimize routines like mutex_spin_on_owner() - we don't spin-wait if we relaize that the target vcpu has been preempted.

a) and c) are about dealing with the LHP problem, while b) is about preventing the problem from happening.

This patch-series is along a). Its based against v2.6.35-rc4 kernel for both guest and host.

I have patches for b) and c) as well - want to send them after more thorough experimentation with various workloads.

Results
=======

Machine : IBM x3650 with 2 Dual-core Intel Xeon (5160) CPUs and 4972MB RAM Kernel for host/guest : 2.6.35-rc4

Test :
Spawn a single guest under KVM with 4VCPUs, 3092MB memory, virtio disk Guest runs kernel compile benchmark as:

time -p make -s -j20 bzImage

for 3 times in a loop.

This is repeated under varios over-commitment scenarios and "vcpu/pcpu pinning configurations"

Overcommit scenarios are :

1x : only guest is running
2x : cpu hogs are started such that (hogs + guest vcpu count)/pcpu = 2 3x : cpu hogs are started such that (hogs + guest vcpu count)/pcpu = 3 4x : cpu hogs are started such that (hogs + guest vcpu count)/pcpu = 4

VCPU/PCPU pinning scenarion:

A : Each of the vcpu of the guest is pinned to a separate pcpu B : No pinning. vcpu could run on any pcpu.
C : The 4 VCPUs of guest pinned to run inside a single dual-core CPU
(cpu 2,3 in this case)

Scenario A:

	W/o patch		W/ Patch		Difference
	Avg  (std-dev)		Avg. (std-dev)
1:	273.270  (1.051)	251.613 (1.155)		7.9%
2:	541.343  (57.317)	443.400 (2.832)		18.1%
3:	819.160  (9.218)	642.393 (2.646)		21.6%

4: 1020.493 (34.743) 839.327 (0.658) 17.8%

Scenario B:

1: 276.947 (0.644) 248.397 (1.563) 10.3%
2: 500.723 (5.694) 438.957 (6.112) 12.33%
3: 725.687 (5.267) 641.317 (3.902) 11.62%
4: 973.910 (21.712) 836.853 (2.754) 14.07%

Scenario C:

1:	564.610  (12.055)    	420.510 (2.598)		25.52%
2:	750.867  (5.139)	618.570 (2.914)		17.61%
3:	950.150  (13.496)      	813.803 (1.872)		14.35%

4: 1125.027 (5.337) 1007.63 (5.707) 10.43%

IMO this is good improvement with the patchset applied.

References
==========

1. http://l4ka.org/publications/2004/Towards-Scalable-Multiprocessor-Virtual-Machin

#1 lore raw

RFC PATCH 1/4 Debugfs support for reading an array of u32-type integers

Debugfs support for reading an array of u32-type integers. This is a rework of what code already exists for Xen.

---
 arch/x86/xen/debugfs.c    |  104 ----------------------------------------
 arch/x86/xen/debugfs.h    |    4 -
 arch/x86/xen/mmu.c        |    2 
 arch/x86/xen/multicalls.c |    6 +-
 arch/x86/xen/spinlock.c   |    6 +-
 fs/debugfs/file.c         |  117 ++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/debugfs.h   |   11 ++++
 7 files changed, 135 insertions(+), 115 deletions(-)
arch/x86/xen/debugfs.c +0 -104
Index: current/arch/x86/xen/debugfs.c
===================================================================
@@ -18,107 +18,3 @@ struct dentry * __init xen_init_debugfs(
 
 	return d_xen_debug;
 }
-
-struct array_data
-{
-	void *array;
-	unsigned elements;
-};
-
-static int u32_array_open(struct inode *inode, struct file *file)
-{
-	file->private_data = NULL;
-	return nonseekable_open(inode, file);
-}
-
-static size_t format_array(char *buf, size_t bufsize, const char *fmt,
-			   u32 *array, unsigned array_size)
-{
-	size_t ret = 0;
-	unsigned i;
-
-	for(i = 0; i < array_size; i++) {
-		size_t len;
-
-		len = snprintf(buf, bufsize, fmt, array[i]);
-		len++;	/* ' ' or '\n' */
-		ret += len;
-
-		if (buf) {
-			buf += len;
-			bufsize -= len;
-			buf[-1] = (i == array_size-1) ? '\n' : ' ';
-		}
-	}
-
-	ret++;		/* \0 */
-	if (buf)
-		*buf = '\0';
-
-	return ret;
-}
-
-static char *format_array_alloc(const char *fmt, u32 *array, unsigned array_size)
-{
-	size_t len = format_array(NULL, 0, fmt, array, array_size);
-	char *ret;
-
-	ret = kmalloc(len, GFP_KERNEL);
-	if (ret == NULL)
-		return NULL;
-
-	format_array(ret, len, fmt, array, array_size);
-	return ret;
-}
-
-static ssize_t u32_array_read(struct file *file, char __user *buf, size_t len,
-			      loff_t *ppos)
-{
-	struct inode *inode = file->f_path.dentry->d_inode;
-	struct array_data *data = inode->i_private;
-	size_t size;
-
-	if (*ppos == 0) {
-		if (file->private_data) {
-			kfree(file->private_data);
-			file->private_data = NULL;
-		}
-
-		file->private_data = format_array_alloc("%u", data->array, data->elements);
-	}
-
-	size = 0;
-	if (file->private_data)
-		size = strlen(file->private_data);
-
-	return simple_read_from_buffer(buf, len, ppos, file->private_data, size);
-}
-
-static int xen_array_release(struct inode *inode, struct file *file)
-{
-	kfree(file->private_data);
-
-	return 0;
-}
-
-static const struct file_operations u32_array_fops = {
-	.owner	= THIS_MODULE,
-	.open	= u32_array_open,
-	.release= xen_array_release,
-	.read	= u32_array_read,
-};
-
-struct dentry *xen_debugfs_create_u32_array(const char *name, mode_t mode,
-					    struct dentry *parent,
-					    u32 *array, unsigned elements)
-{
-	struct array_data *data = kmalloc(sizeof(*data), GFP_KERNEL);
-
-	if (data == NULL)
-		return NULL;
-
-	data->array = array;
-	data->elements = elements;
-
-	return debugfs_create_file(name, mode, parent, data, &u32_array_fops);
-}
arch/x86/xen/debugfs.h +0 -4
Index: current/arch/x86/xen/debugfs.h
===================================================================
@@ -3,8 +3,4 @@
 
 struct dentry * __init xen_init_debugfs(void);
 
-struct dentry *xen_debugfs_create_u32_array(const char *name, mode_t mode,
-					    struct dentry *parent,
-					    u32 *array, unsigned elements);
-
 #endif /* _XEN_DEBUGFS_H */
arch/x86/xen/mmu.c +1 -1
Index: current/arch/x86/xen/mmu.c
===================================================================
@@ -1983,7 +1983,7 @@ static int __init xen_mmu_debugfs(void)
 	debugfs_create_u32("mmu_update", 0444, d_mmu_debug, &mmu_stats.mmu_update);
 	debugfs_create_u32("mmu_update_extended", 0444, d_mmu_debug,
 			   &mmu_stats.mmu_update_extended);
-	xen_debugfs_create_u32_array("mmu_update_histo", 0444, d_mmu_debug,
+	debugfs_create_u32_array("mmu_update_histo", 0444, d_mmu_debug,
 				     mmu_stats.mmu_update_histo, 20);
 
 	debugfs_create_u32("set_pte_at", 0444, d_mmu_debug, &mmu_stats.set_pte_at);
arch/x86/xen/multicalls.c +3 -3
Index: current/arch/x86/xen/multicalls.c
===================================================================
@@ -269,11 +269,11 @@ static int __init xen_mc_debugfs(void)
 	debugfs_create_u32("hypercalls", 0444, d_mc_debug, &mc_stats.hypercalls);
 	debugfs_create_u32("arg_total", 0444, d_mc_debug, &mc_stats.arg_total);
 
-	xen_debugfs_create_u32_array("batch_histo", 0444, d_mc_debug,
+	debugfs_create_u32_array("batch_histo", 0444, d_mc_debug,
 				     mc_stats.histo, MC_BATCH);
-	xen_debugfs_create_u32_array("hypercall_histo", 0444, d_mc_debug,
+	debugfs_create_u32_array("hypercall_histo", 0444, d_mc_debug,
 				     mc_stats.histo_hypercalls, NHYPERCALLS);
-	xen_debugfs_create_u32_array("flush_reasons", 0444, d_mc_debug,
+	debugfs_create_u32_array("flush_reasons", 0444, d_mc_debug,
 				     mc_stats.flush, FL_N_REASONS);
 
 	return 0;
arch/x86/xen/spinlock.c +3 -3
Index: current/arch/x86/xen/spinlock.c
===================================================================
@@ -423,11 +423,11 @@ static int __init xen_spinlock_debugfs(v
 	debugfs_create_u64("time_total", 0444, d_spin_debug,
 			   &spinlock_stats.time_total);
 
-	xen_debugfs_create_u32_array("histo_total", 0444, d_spin_debug,
+	debugfs_create_u32_array("histo_total", 0444, d_spin_debug,
 				     spinlock_stats.histo_spin_total, HISTO_BUCKETS + 1);
-	xen_debugfs_create_u32_array("histo_spinning", 0444, d_spin_debug,
+	debugfs_create_u32_array("histo_spinning", 0444, d_spin_debug,
 				     spinlock_stats.histo_spin_spinning, HISTO_BUCKETS + 1);
-	xen_debugfs_create_u32_array("histo_blocked", 0444, d_spin_debug,
+	debugfs_create_u32_array("histo_blocked", 0444, d_spin_debug,
 				     spinlock_stats.histo_spin_blocked, HISTO_BUCKETS + 1);
 
 	return 0;
fs/debugfs/file.c +117 -0

126 lines not on this page · raw message

include/linux/debugfs.h +11 -0
Index: current/include/linux/debugfs.h
===================================================================
@@ -74,6 +74,10 @@ struct dentry *debugfs_create_blob(const
 				  struct dentry *parent,
 				  struct debugfs_blob_wrapper *blob);
 
+struct dentry *debugfs_create_u32_array(const char *name, mode_t mode,
+					    struct dentry *parent,
+					    u32 *array, unsigned elements);
+
 bool debugfs_initialized(void);
 
 #else
@@ -188,6 +192,13 @@ static inline struct dentry *debugfs_cre
 	return ERR_PTR(-ENODEV);
 }
 
+struct dentry *debugfs_create_u32_array(const char *name, mode_t mode,
+					    struct dentry *parent,
+					    u32 *array, unsigned elements)
+{
+	return ERR_PTR(-ENODEV);
+}
+
 static inline bool debugfs_initialized(void)
 {
 	return false;
#2 lore raw

RFC PATCH 2/4 Add yield hypercall for KVM guests

In reply to Srivatsa Vaddagiri

Add KVM hypercall for yielding vcpu timeslice.

---
 arch/x86/include/asm/kvm_para.h |    1 +
 arch/x86/kvm/x86.c              |    7 ++++++-
 include/linux/kvm.h             |    1 +
 include/linux/kvm_para.h        |    1 +
 4 files changed, 9 insertions(+), 1 deletion(-)
arch/x86/include/asm/kvm_para.h +1 -0
Index: current/arch/x86/include/asm/kvm_para.h
===================================================================
@@ -16,6 +16,7 @@
 #define KVM_FEATURE_CLOCKSOURCE		0
 #define KVM_FEATURE_NOP_IO_DELAY	1
 #define KVM_FEATURE_MMU_OP		2
+#define KVM_FEATURE_YIELD		4
 /* This indicates that the new set of kvmclock msrs
  * are available. The use of 0x11 and 0x12 is deprecated
  */
arch/x86/kvm/x86.c +6 -1
Index: current/arch/x86/kvm/x86.c
===================================================================
@@ -1618,6 +1618,7 @@ int kvm_dev_ioctl_check_extension(long e
 	case KVM_CAP_PCI_SEGMENT:
 	case KVM_CAP_DEBUGREGS:
 	case KVM_CAP_X86_ROBUST_SINGLESTEP:
+	case KVM_CAP_YIELD_HYPERCALL:
 		r = 1;
 		break;
 	case KVM_CAP_COALESCED_MMIO:
@@ -1993,7 +1994,8 @@ static void do_cpuid_ent(struct kvm_cpui
 		entry->eax = (1 << KVM_FEATURE_CLOCKSOURCE) |
 			     (1 << KVM_FEATURE_NOP_IO_DELAY) |
 			     (1 << KVM_FEATURE_CLOCKSOURCE2) |
-			     (1 << KVM_FEATURE_CLOCKSOURCE_STABLE_BIT);
+			     (1 << KVM_FEATURE_CLOCKSOURCE_STABLE_BIT) |
+			     (1 << KVM_FEATURE_YIELD);
 		entry->ebx = 0;
 		entry->ecx = 0;
 		entry->edx = 0;
@@ -4245,6 +4247,9 @@ int kvm_emulate_hypercall(struct kvm_vcp
 	case KVM_HC_MMU_OP:
 		r = kvm_pv_mmu_op(vcpu, a0, hc_gpa(vcpu, a1, a2), &ret);
 		break;
+	case KVM_HC_YIELD:
+		ret = 0;
+		yield();
 	default:
 		ret = -KVM_ENOSYS;
 		break;
include/linux/kvm.h +1 -0
Index: current/include/linux/kvm.h
===================================================================
@@ -524,6 +524,7 @@ struct kvm_enable_cap {
 #define KVM_CAP_PPC_OSI 52
 #define KVM_CAP_PPC_UNSET_IRQ 53
 #define KVM_CAP_ENABLE_CAP 54
+#define KVM_CAP_YIELD_HYPERCALL	55
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
include/linux/kvm_para.h +1 -0
Index: current/include/linux/kvm_para.h
===================================================================
@@ -17,6 +17,7 @@
 
 #define KVM_HC_VAPIC_POLL_IRQ		1
 #define KVM_HC_MMU_OP			2
+#define KVM_HC_YIELD                    3
 
 /*
  * hypercalls use architecture specific
#3 lore raw

Re: [PATCH RFC 2/4] Add yield hypercall for KVM guests

Quoted 1 line · Srivatsa Vaddagiri

Add KVM hypercall for yielding vcpu timeslice.

Can you do a directed yield?

J

Quoted 78 lines
---
  arch/x86/include/asm/kvm_para.h |    1 +
  arch/x86/kvm/x86.c              |    7 ++++++-
  include/linux/kvm.h             |    1 +
  include/linux/kvm_para.h        |    1 +
  4 files changed, 9 insertions(+), 1 deletion(-)

Index: current/arch/x86/include/asm/kvm_para.h
===================================================================

arch/x86/include/asm/kvm_para.h +1 -0
@@ -16,6 +16,7 @@
  #define KVM_FEATURE_CLOCKSOURCE		0
  #define KVM_FEATURE_NOP_IO_DELAY	1
  #define KVM_FEATURE_MMU_OP		2
+#define KVM_FEATURE_YIELD		4
  /* This indicates that the new set of kvmclock msrs
   * are available. The use of 0x11 and 0x12 is deprecated
   */

Index: current/arch/x86/kvm/x86.c
===================================================================

arch/x86/kvm/x86.c +6 -1
@@ -1618,6 +1618,7 @@ int kvm_dev_ioctl_check_extension(long e
  	case KVM_CAP_PCI_SEGMENT:
  	case KVM_CAP_DEBUGREGS:
  	case KVM_CAP_X86_ROBUST_SINGLESTEP:
+	case KVM_CAP_YIELD_HYPERCALL:
  		r = 1;
  		break;
  	case KVM_CAP_COALESCED_MMIO:
@@ -1993,7 +1994,8 @@ static void do_cpuid_ent(struct kvm_cpui
  		entry->eax = (1<<  KVM_FEATURE_CLOCKSOURCE) |
  			     (1<<  KVM_FEATURE_NOP_IO_DELAY) |
  			     (1<<  KVM_FEATURE_CLOCKSOURCE2) |
-			     (1<<  KVM_FEATURE_CLOCKSOURCE_STABLE_BIT);
+			     (1<<  KVM_FEATURE_CLOCKSOURCE_STABLE_BIT) |
+			     (1<<  KVM_FEATURE_YIELD);
  		entry->ebx = 0;
  		entry->ecx = 0;
  		entry->edx = 0;
@@ -4245,6 +4247,9 @@ int kvm_emulate_hypercall(struct kvm_vcp
  	case KVM_HC_MMU_OP:
  		r = kvm_pv_mmu_op(vcpu, a0, hc_gpa(vcpu, a1, a2),&ret);
  		break;
+	case KVM_HC_YIELD:
+		ret = 0;
+		yield();
  	default:
  		ret = -KVM_ENOSYS;
  		break;

Index: current/include/linux/kvm.h
===================================================================

include/linux/kvm.h +1 -0
@@ -524,6 +524,7 @@ struct kvm_enable_cap {
  #define KVM_CAP_PPC_OSI 52
  #define KVM_CAP_PPC_UNSET_IRQ 53
  #define KVM_CAP_ENABLE_CAP 54
+#define KVM_CAP_YIELD_HYPERCALL	55
 
  #ifdef KVM_CAP_IRQ_ROUTING
 

Index: current/include/linux/kvm_para.h
===================================================================

include/linux/kvm_para.h +1 -0
@@ -17,6 +17,7 @@
 
  #define KVM_HC_VAPIC_POLL_IRQ		1
  #define KVM_HC_MMU_OP			2
+#define KVM_HC_YIELD                    3
 
  /*
   * hypercalls use architecture specific
#4 lore raw

Re: [PATCH RFC 2/4] Add yield hypercall for KVM guests

Quoted 4 lines · Jeremy Fitzhardinge
Quoted 1 line · Srivatsa Vaddagiri

Add KVM hypercall for yielding vcpu timeslice.

Can you do a directed yield?

We don't have that support yet in Linux scheduler. Also I feel it would be more useful when the target vcpu and yielding vcpu are on the same physical cpu, rather than when they are on separate cpus. With latter, yielding (or donating) timeslice need not ensure that target vcpu runs immediately and also I suspect fairness issues needs to be tackled as well (large number of waiters shouldn't boot a lock-holders time slice too much that it gets a larger share).

- vatsa

#5 lore raw

Re: [PATCH RFC 2/4] Add yield hypercall for KVM guests

Quoted 5 lines · Srivatsa Vaddagiri
Quoted 3 lines · Jeremy Fitzhardinge
Quoted 1 line · Srivatsa Vaddagiri

Add KVM hypercall for yielding vcpu timeslice.

Can you do a directed yield?

We don't have that support yet in Linux scheduler.

If you think it's useful, it would be good to design it into the interface, and fall back to ordinary yield if the host doesn't support it.

A big advantage of directed yield vs yield is that you conserve resources within a VM; a simple yield will cause the guest to drop its share of cpu to other guest.

Made up example:

- 2 vcpu guest with 10% contention
- lock hold time 10us every 100us
- timeslice 1ms

Ideally this guest can consume 190% cpu (sleeping whenever there is contention). But if we yield when we detect contention, then we sleep for 1ms, and utilization drops to around 100%-150% (a vcpu will usually fall asleep soon within a few 100us periods).

Quoted 4 lines

Also I feel it would be more
useful when the target vcpu and yielding vcpu are on the same physical cpu, rather than when they are on separate cpus. With latter, yielding (or donating) timeslice need not ensure that target vcpu runs immediately

Donate at least the amount needed to wake up the other vcpu, we can calculate it during wakeup.

Quoted 3 lines

and also I suspect fairness issues needs to be tackled as well (large number of waiters shouldn't boot a lock-holders time slice too much that it gets a larger share).

I feel ordinary yield suffers from fairness a lot more.

Signature · 1 line
error compiling committee.c: too many arguments to function
#6 lore raw

Re: [PATCH RFC 2/4] Add yield hypercall for KVM guests

Quoted 10 lines · Avi Kivity
Quoted 2 lines
Quoted 1 line

Can you do a directed yield?

We don't have that support yet in Linux scheduler.

If you think it's useful, it would be good to design it into the interface, and fall back to ordinary yield if the host doesn't support it.

A big advantage of directed yield vs yield is that you conserve resources within a VM; a simple yield will cause the guest to drop its share of cpu to other guest.

Hmm .. I see possibility of modifying yield to reclaim its "lost" timeslice when its scheduled next as well. Basically remember what timeslice we have given up and add that as its "bonus" when it runs next. That would keep the dynamics of yield donation/reclaim local to the (physical) cpu and IMHO is less complex than dealing with directed yield between tasks located across different physical cpus. That would also address the fairness issue with yield you are pointing at?

- vatsa

#7 lore raw

Re: [PATCH RFC 2/4] Add yield hypercall for KVM guests

Quoted 12 lines · Srivatsa Vaddagiri
Quoted 10 lines · Avi Kivity
Quoted 2 lines
Quoted 1 line

Can you do a directed yield?

We don't have that support yet in Linux scheduler.

If you think it's useful, it would be good to design it into the interface, and fall back to ordinary yield if the host doesn't support it.

A big advantage of directed yield vs yield is that you conserve resources within a VM; a simple yield will cause the guest to drop its share of cpu to other guest.

Quoted 6 lines

Hmm .. I see possibility of modifying yield to reclaim its "lost" timeslice when its scheduled next as well. Basically remember what timeslice we have given up and add that as its "bonus" when it runs next. That would keep the dynamics of yield donation/reclaim local to the (physical) cpu and IMHO is less complex than dealing with directed yield between tasks located across different physical cpus. That would also address the fairness issue with yield you are pointing at?

Basically with directed yield, we need to deal with these issues:

- Timeslice inflation of target (lock-holder) vcpu affecting fair-time of other guests vcpus.
- Intra-VM fairness - different vcpus could get different fair-time, depending on how much of a lock-holder/spinner a vcpu is

By simply educating yield to reclaim its lost share, I feel we can avoid these complexities and get most of the benefit of yield-on-contention.

CCing other shceduler experts for their opinion of directed yield.

- vatsa

#8 lore raw

Re: [PATCH RFC 2/4] Add yield hypercall for KVM guests

In reply to Jeremy Fitzhardinge
Quoted 5 lines · Jeremy Fitzhardinge
Quoted 1 line · Srivatsa Vaddagiri

Add KVM hypercall for yielding vcpu timeslice.

Can you do a directed yield?

A problem with directed yield is figuring out who to yield to. One idea is to look for a random vcpu that is not running and donate some runtime to it. In the best case, it's the lock holder and we cause it to start running. Middle case it's not the lock holder, but we lose enough runtime to stop running, so at least we don't waste cpu. Worst case we continue running not having woken the lock holder. Spin again, yield again hoping to find the right vcpu.

Signature · 1 line
error compiling committee.c: too many arguments to function
#9 lore raw

Re: [PATCH RFC 2/4] Add yield hypercall for KVM guests

Quoted 7 lines · Avi Kivity
Quoted 5 lines · Jeremy Fitzhardinge
Quoted 1 line · Srivatsa Vaddagiri

Add KVM hypercall for yielding vcpu timeslice.

Can you do a directed yield?

Quoted 7 lines

A problem with directed yield is figuring out who to yield to. One idea is to look for a random vcpu that is not running and donate some runtime to it. In the best case, it's the lock holder and we cause it to start running. Middle case it's not the lock holder, but we lose enough runtime to stop running, so at least we don't waste cpu. Worst case we continue running not having woken the lock holder. Spin again, yield again hoping to find the right vcpu.

It's been quite some time, but played with directed yielding for Xen[1] and we were looking to model the POWER directed yield (H_CONFER) where the lock holding vcpu was indiciated in the spinlock. When acquiring the lock, record the vcpu id. When another vcpu attempts to acquire the lock if it can't it can yield its time to the lock holder.

1. http://lists.xensource.com/archives/html/xen-devel/2005-05/msg00776.html

Signature · 4 lines
Ryan Harper
Software Engineer; Linux Technology Center
IBM Corp., Austin, Tx
[email protected]
#10 lore raw

Re: [PATCH RFC 2/4] Add yield hypercall for KVM guests

Quoted 8 lines · Ryan Harper
Quoted 8 lines

A problem with directed yield is figuring out who to yield to. One idea is to look for a random vcpu that is not running and donate some runtime to it. In the best case, it's the lock holder and we cause it to start running. Middle case it's not the lock holder, but we lose enough runtime to stop running, so at least we don't waste cpu. Worst case we continue running not having woken the lock holder. Spin again, yield again hoping to find the right vcpu.

Quoted 5 lines

It's been quite some time, but played with directed yielding for Xen[1] and we were looking to model the POWER directed yield (H_CONFER) where the lock holding vcpu was indiciated in the spinlock. When acquiring the lock, record the vcpu id. When another vcpu attempts to acquire the lock if it can't it can yield its time to the lock holder.

No reason why we can't have something similar.

We can take the lock and set the owner atomically:

LOCK_PREFIX "cmpxchg %1, %0"
: "=m"(lock) : "r"(raw_smp_processor_id() | SPIN_LOCK_BIAS),
"a"((u16)0) : "memory"

Signature · 1 line
error compiling committee.c: too many arguments to function
#11 lore raw

Re: [PATCH RFC 2/4] Add yield hypercall for KVM guests

In reply to Avi Kivity
Quoted 8 lines · Avi Kivity
Quoted 5 lines · Jeremy Fitzhardinge
Quoted 1 line · Srivatsa Vaddagiri

Add KVM hypercall for yielding vcpu timeslice.

Can you do a directed yield?

A problem with directed yield is figuring out who to yield to. One idea is to look for a random vcpu that is not running and donate some runtime to it. In the best case, it's the lock holder and we cause it to start running. Middle case it's not the lock holder, but we lose enough runtime to stop running, so at least we don't waste cpu. Worst case we continue running not having woken the lock holder. Spin again, yield again hoping to find the right vcpu.

That can help with lockholder preemption, but on unlock you need to wake up exactly the right vcpu - the next in the ticket queue - in order to avoid burning masses of cpu. If each cpu records what lock it is spinning on and what its ticket is in a percpu variable, then the unlocker can search for the next person to kick.

J

#12 lore raw

RFC PATCH 3/4 Paravirtualized spinlock implementation for KVM guests

In reply to Srivatsa Vaddagiri

Paravirtual spinlock implementation for KVM guests, based heavily on Xen guest's spinlock implementation.

---
 arch/x86/Kconfig         |    8 +
 arch/x86/kernel/head64.c |    3 
 arch/x86/kernel/kvm.c    |  293 +++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/kvm_para.h |    8 +
 4 files changed, 312 insertions(+)
arch/x86/Kconfig +8 -0
Index: current/arch/x86/Kconfig
===================================================================
@@ -551,6 +551,14 @@ config KVM_GUEST
 	  This option enables various optimizations for running under the KVM
 	  hypervisor.
 
+config KVM_DEBUG_FS
+	bool "Enable debug information to be collected for KVM guests"
+	default n
+	depends on KVM_GUEST && EXPERIMENTAL
+	---help---
+	  This option will collect various debug information to be collected
+	  and displayed in debugfs of guest kernel.
+
 source "arch/x86/lguest/Kconfig"
 
 config PARAVIRT
arch/x86/kernel/head64.c +3 -0
Index: current/arch/x86/kernel/head64.c
===================================================================
@@ -12,6 +12,7 @@
 #include <linux/percpu.h>
 #include <linux/start_kernel.h>
 #include <linux/io.h>
+#include <linux/kvm_para.h>
 
 #include <asm/processor.h>
 #include <asm/proto.h>
@@ -113,6 +114,8 @@ void __init x86_64_start_reservations(ch
 
 	reserve_ebda_region();
 
+	kvm_guest_early_init();
+
 	/*
 	 * At this point everything still needed from the boot loader
 	 * or BIOS or kernel text should be early reserved or marked not
arch/x86/kernel/kvm.c +293 -0

302 lines not on this page · raw message

include/linux/kvm_para.h +8 -0
Index: current/include/linux/kvm_para.h
===================================================================
@@ -27,8 +27,16 @@
 #ifdef __KERNEL__
 #ifdef CONFIG_KVM_GUEST
 void __init kvm_guest_init(void);
+
+#ifdef CONFIG_PARAVIRT_SPINLOCKS
+void __init kvm_guest_early_init(void);
+#else
+#define kvm_guest_early_init() do { } while (0)
+#endif
+
 #else
 #define kvm_guest_init() do { } while (0)
+#define kvm_guest_early_init() do { } while (0)
 #endif
 
 static inline int kvm_para_has_feature(unsigned int feature)
#13 lore raw

Re: [PATCH RFC 3/4] Paravirtualized spinlock implementation for KVM guests

Quoted 11 lines · Srivatsa Vaddagiri

Paravirtual spinlock implementation for KVM guests, based heavily on Xen guest's spinlock implementation.

arch/x86/kernel/kvm.c +7 -0
+
+static struct spinlock_stats
+{
+	u64 taken;
+	u32 taken_slow;
+
+	u64 released;

arch/x86/kernel/kvm.c void __init kvm_guest_init(void) · line 263

Quoted 10 lines
arch/x86/kernel/kvm.c +10 -0
+
+#define HISTO_BUCKETS	30
+	u32 histo_spin_total[HISTO_BUCKETS+1];
+	u32 histo_spin_spinning[HISTO_BUCKETS+1];
+	u32 histo_spin_blocked[HISTO_BUCKETS+1];
+
+	u64 time_total;
+	u64 time_spinning;
+	u64 time_blocked;
+} spinlock_stats;

Could these be replaced by tracepoints when starting to spin/stopping spinning etc? Then userspace can reconstruct the histogram as well as see which locks are involved and what call paths.

Quoted 54 lines
arch/x86/kernel/kvm.c +54 -0
+struct kvm_spinlock {
+	unsigned char lock;		/* 0 ->  free; 1 ->  locked */
+	unsigned short spinners;	/* count of waiting cpus */
+};
+
+/*
+ * Mark a cpu as interested in a lock.  Returns the CPU's previous
+ * lock of interest, in case we got preempted by an interrupt.
+ */
+static inline void spinning_lock(struct kvm_spinlock *pl)
+{
+	asm(LOCK_PREFIX " incw %0"
+	    : "+m" (pl->spinners) : : "memory");
+}
+
+/*
+ * Mark a cpu as no longer interested in a lock.  Restores previous
+ * lock of interest (NULL for none).
+ */
+static inline void unspinning_lock(struct kvm_spinlock *pl)
+{
+	asm(LOCK_PREFIX " decw %0"
+	    : "+m" (pl->spinners) : : "memory");
+}
+
+static int kvm_spin_is_locked(struct arch_spinlock *lock)
+{
+	struct kvm_spinlock *sl = (struct kvm_spinlock *)lock;
+
+	return sl->lock != 0;
+}
+
+static int kvm_spin_is_contended(struct arch_spinlock *lock)
+{
+	struct kvm_spinlock *sl = (struct kvm_spinlock *)lock;
+
+	/* Not strictly true; this is only the count of contended
+	   lock-takers entering the slow path. */
+	return sl->spinners != 0;
+}
+
+static int kvm_spin_trylock(struct arch_spinlock *lock)
+{
+	struct kvm_spinlock *sl = (struct kvm_spinlock *)lock;
+	u8 old = 1;
+
+	asm("xchgb %b0,%1"
+	    : "+q" (old), "+m" (sl->lock) : : "memory");
+
+	return old == 0;
+}
+
+static noinline int kvm_spin_lock_slow(struct arch_spinlock *lock)
+{

arch/x86/kernel/kvm.c void __init kvm_guest_init(void) · line 453

Quoted 10 lines
arch/x86/kernel/kvm.c +10 -0
+	struct kvm_spinlock *sl = (struct kvm_spinlock *)lock;
+	u64 start;
+
+	ADD_STATS(taken_slow, 1);
+
+	/* announce we're spinning */
+	spinning_lock(sl);
+
+	start = spin_time_start();
+	kvm_hypercall0(KVM_HC_YIELD);

Oh. This isn't really a yield since we expect to be woken up? It's more of a sleep.

We already have a sleep hypercall, it's called HLT. If we can use it, the thing can work on older hosts. It's tricky though:

- if interrupts were enabled before we started spinning, sleep with interrupts enabled. This also allows the spinner to switch to another process if some completion comes along so it's a good idea anyway. Wake up sends an IPI.
- if not, we need to use NMI to wake up. This is somewhat icky since there's no atomic "enable NMI and sleep" instruction, so we have to handle the case of the wake up arriving before HLT (can be done by examining RIP and seeing if it's in the critical section).

Signature · 1 line
error compiling committee.c: too many arguments to function
#14 lore raw

Re: [PATCH RFC 3/4] Paravirtualized spinlock implementation for KVM guests

Quoted 16 lines · Avi Kivity
Quoted 15 lines · Srivatsa Vaddagiri

Paravirtual spinlock implementation for KVM guests, based heavily on Xen guest's spinlock implementation.

arch/x86/kernel/kvm.c +11 -0
+
+static struct spinlock_stats
+{
+    u64 taken;
+    u32 taken_slow;
+
+    u64 released;
+
+#define HISTO_BUCKETS    30
+    u32 histo_spin_total[HISTO_BUCKETS+1];
+    u32 histo_spin_spinning[HISTO_BUCKETS+1];
Quoted 8 lines

arch/x86/kernel/kvm.c void __init kvm_guest_init(void) · line 263

Quoted 6 lines
arch/x86/kernel/kvm.c +6 -0
+    u32 histo_spin_blocked[HISTO_BUCKETS+1];
+
+    u64 time_total;
+    u64 time_spinning;
+    u64 time_blocked;
+} spinlock_stats;

Could these be replaced by tracepoints when starting to spin/stopping spinning etc? Then userspace can reconstruct the histogram as well as see which locks are involved and what call paths.

Unfortunately not; the tracing code uses spinlocks.

(TBH I haven't actually tried, but I did give the code an eyeball to this end.)

J

#15 lore raw

Re: [PATCH RFC 3/4] Paravirtualized spinlock implementation for KVM guests

Quoted 26 lines · Jeremy Fitzhardinge
Quoted 16 lines · Avi Kivity
Quoted 15 lines · Srivatsa Vaddagiri

Paravirtual spinlock implementation for KVM guests, based heavily on Xen guest's spinlock implementation.

arch/x86/kernel/kvm.c +11 -0
+
+static struct spinlock_stats
+{
+    u64 taken;
+    u32 taken_slow;
+
+    u64 released;
+
+#define HISTO_BUCKETS    30
+    u32 histo_spin_total[HISTO_BUCKETS+1];
+    u32 histo_spin_spinning[HISTO_BUCKETS+1];
Quoted 8 lines

arch/x86/kernel/kvm.c void __init kvm_guest_init(void) · line 263

Quoted 6 lines
arch/x86/kernel/kvm.c +6 -0
+    u32 histo_spin_blocked[HISTO_BUCKETS+1];
+
+    u64 time_total;
+    u64 time_spinning;
+    u64 time_blocked;
+} spinlock_stats;

Could these be replaced by tracepoints when starting to spin/stopping spinning etc? Then userspace can reconstruct the histogram as well as see which locks are involved and what call paths.

Quoted 3 lines

Unfortunately not; the tracing code uses spinlocks.

(TBH I haven't actually tried, but I did give the code an eyeball to this end.)

Hm. The tracing code already uses a specialized lock (arch_spinlock_t), perhaps we can make this lock avoid the tracing?

It's really sad, btw, there's all those nice lockless ring buffers and then a spinlock for ftrace_vbprintk(), instead of a per-cpu buffer.

Signature · 2 lines
I have a truly marvellous patch that fixes the bug which this
signature is too narrow to contain.
#16 lore raw

Re: [PATCH RFC 3/4] Paravirtualized spinlock implementation for KVM guests

Quoted 27 lines · Avi Kivity
Quoted 26 lines · Jeremy Fitzhardinge
Quoted 24 lines · Avi Kivity
Quoted 11 lines · Srivatsa Vaddagiri

Paravirtual spinlock implementation for KVM guests, based heavily on Xen guest's spinlock implementation.

arch/x86/kernel/kvm.c +7 -0
+
+static struct spinlock_stats
+{
+    u64 taken;
+    u32 taken_slow;
+
+    u64 released;

arch/x86/kernel/kvm.c void __init kvm_guest_init(void) · line 263

Quoted 10 lines
arch/x86/kernel/kvm.c +10 -0
+
+#define HISTO_BUCKETS    30
+    u32 histo_spin_total[HISTO_BUCKETS+1];
+    u32 histo_spin_spinning[HISTO_BUCKETS+1];
+    u32 histo_spin_blocked[HISTO_BUCKETS+1];
+
+    u64 time_total;
+    u64 time_spinning;
+    u64 time_blocked;
+} spinlock_stats;

Could these be replaced by tracepoints when starting to spin/stopping spinning etc? Then userspace can reconstruct the histogram as well as see which locks are involved and what call paths.

Quoted 5 lines
Quoted 3 lines

Unfortunately not; the tracing code uses spinlocks.

(TBH I haven't actually tried, but I did give the code an eyeball to this end.)

Hm. The tracing code already uses a specialized lock (arch_spinlock_t), perhaps we can make this lock avoid the tracing?

That's not really a specialized lock; that's just the naked architecture-provided spinlock implementation, without all the lockdep, etc, etc stuff layered on top. All these changes are at a lower level, so giving tracing its own type of spinlock amounts to making the architectures provide two complete spinlock implementations. We could make tracing use, for example, an rwlock so long as we promise not to put tracing in the rwlock implementation - but that's hardly elegant.

Quoted 1 line

It's really sad, btw, there's all those nice lockless ring buffers and then a spinlock for ftrace_vbprintk(), instead of a per-cpu buffer.

Sad indeed.

J

#17 lore raw

Re: [PATCH RFC 3/4] Paravirtualized spinlock implementation for KVM guests

In reply to Srivatsa Vaddagiri

arch/x86/kernel/kvm.c void __init kvm_guest_init(void) · line 516

Quoted 12 lines · Srivatsa Vaddagiri

Paravirtual spinlock implementation for KVM guests, based heavily on Xen guest's spinlock implementation.

arch/x86/kernel/kvm.c +9 -0
+static void kvm_spin_unlock(struct arch_spinlock *lock)
+{
+	struct kvm_spinlock *sl = (struct kvm_spinlock *)lock;
+
+	ADD_STATS(released, 1);
+
+	smp_wmb();		/* make sure no writes get moved after unlock */
+	sl->lock = 0;		/* release lock */
+}

Wait, no wakeup?

So it is a yield, not a sleep. I'm worried it could seriously impact fairness when one non-contending guest (or non-pv) is overcommitted together with a spin-yield guest.

Signature · 1 line
error compiling committee.c: too many arguments to function
#18 lore raw

RFC PATCH 4/4 Add yield hypercall support in Qemu

In reply to Srivatsa Vaddagiri

Add yield hypercall support in Qemu.

---
 kvm/include/linux/kvm.h        |    1 +
 kvm/include/x86/asm/kvm_para.h |    1 +
 target-i386/kvm.c              |    3 +++
 3 files changed, 5 insertions(+)
kvm/include/linux/kvm.h +1 -0
Index: qemu-kvm/kvm/include/linux/kvm.h
===================================================================
@@ -499,6 +499,7 @@ struct kvm_ioeventfd {
 #define KVM_CAP_PPC_SEGSTATE 43
 
 #define KVM_CAP_PCI_SEGMENT 47
+#define KVM_CAP_YIELD_HYPERCALL 55
 
 #ifdef KVM_CAP_IRQ_ROUTING
 
kvm/include/x86/asm/kvm_para.h +1 -0
Index: qemu-kvm/kvm/include/x86/asm/kvm_para.h
===================================================================
@@ -15,6 +15,7 @@
 #define KVM_FEATURE_CLOCKSOURCE		0
 #define KVM_FEATURE_NOP_IO_DELAY	1
 #define KVM_FEATURE_MMU_OP		2
+#define KVM_FEATURE_YIELD              4
 
 #define MSR_KVM_WALL_CLOCK  0x11
 #define MSR_KVM_SYSTEM_TIME 0x12
target-i386/kvm.c +3 -0
Index: qemu-kvm/target-i386/kvm.c
===================================================================
@@ -147,6 +147,9 @@ struct kvm_para_features {
 #ifdef KVM_CAP_PV_MMU
         { KVM_CAP_PV_MMU, KVM_FEATURE_MMU_OP },
 #endif
+#ifdef KVM_CAP_YIELD_HYPERCALL
+	{ KVM_CAP_YIELD_HYPERCALL, KVM_FEATURE_YIELD },
+#endif
         { -1, -1 }
 };
 
#19 lore raw
In reply to Srivatsa Vaddagiri
Quoted 2 lines · Srivatsa Vaddagiri

This patch-series implements paravirt-spinlock implementation for KVM guests, based heavily on Xen's implementation.

Excellent! I think this problem has been ignored for too long.

Quoted 14 lines

I tried to refactor Xen's spinlock
implementation to make it common for both Xen and KVM - but found that few differences between Xen and KVM (Xen has the ability to block on a particular event/irq for example) _and_ the fact that the guest kernel can be compiled to support both Xen and KVM hypervisors (CONFIG_XEN and CONFIG_KVM_GUEST can both be turned on) makes the "common" code a eye-sore. There will have to be:

	if (xen) {
		...
	} else if (kvm) {
		..
	}
Quoted 5 lines

or possibly:

alternative(NOP, some_xen_specific_call, ....)

type of code in the common implementation.

No, that doesn't look like a good approach. It suggests the apparently commonality isn't really there.

Quoted 2 lines

For the time-being, I have made this KVM-specific only. At somepoint in future, I hope this can be made common between Xen/KVM.

Did you see the patch series I posted a couple of weeks ago to revamp pv spinlocks? Specifically, I dropped the notion of pv spinlocks in which the entire spinlock implementation is replaced, and added pv ticketlocks where the ticketlock algorithm is always used for the fastpath, but it calls out to pvop calls for the slowpath (a long spin, or unlocking a lock with waiters). It significantly reduces the amount of hypervisor-specific code.

You can see the current patches in

git://git.kernel.org/pub/scm/linux/kernel/git/jeremy/xen.git
xen/pvticketlock-git

Quoted 5 lines

More background and results for this patch below:

What is the Problem being solved?
=================================

Quoted 13 lines

Guest operating system can be preempted by hypervisor at any arbitrary point. There is no mechanism (that I know of) where guest OS can disable preemption for certain periods of time. One noticeable effect of this is with regard to locking. Lets say one virtual-cpu of a guest (VCPUA) grabs a spinlock and before it could relinquish the lock is preempted by hypervisor. The time-of-preemption (when the virtual cpu is off the cpu) can be quite large. In that period, if another of guest OS's virtual cpu (VCPUB) tries grabbing the same lock, it could end up spin-waiting a _long_ time, burning cycles unnecessarily. To add to the woes, VCPUB may actually be waiting for VCPUA to yield before it can run on the same (physical) cpu. This is termed as the "lock-holder preemption" (LHP) problem. The effect of it can be quite serious. For ex:
http://lkml.org/lkml/2010/4/11/108 reported 80% performance degradation because of an issue attributed to LHP problem.

That's not actually the real problem. It's *a* problem, but insignificant compared to the ticketlock-specific "next-in-line vcpu scheduler bunfight" problem - lock holder preemption is a misnomer. Fortunately the solutions to both are (nearly) the same.

See Thomas Friebel's talk "Prevent Guests from Spinning Around (http://www.xen.org/files/xensummitboston08/LHP.pdf).

Quoted 11 lines

Solutions
=========

There are several solutions to this problem.

a. Realize that a lock-holder could have been preempted, and avoid spin-waiting
too long. Instead, yield cycles (to the lock-holder perhaps). This is a common solution adopted by most paravirtualized-guests (Xen, s390, powerpc).

b. Avoid preempting a lock-holder while its holding a (spin-) lock.

Quoted 9 lines

In this scheme, guest OS can hint (set some flag in memory shared with hypervisor) whenever its holding a lock and hypervisor could defer preempting the guest vcpu when its holding a lock. With this scheme, we should never have a lock-acquiring vcpu spin on a preempted vcpu to release its lock. If ever it spins, its because somebody *currently running* is holding the lock - and hence it won't have to spin-wait too long. IOW we are pro-actively trying to prevent the LHP problem from occuring in the first place. This should improve job turnaround time for some workloads. [1] has some results based on this approach.

This doesn't actually help the problem mentioned above, because it's not a problem with the lock holder getting preempted, but what happens once the lock has been released.

Quoted 30 lines

c. Share run-status of vcpu with guests. This could be used to optimize
routines like mutex_spin_on_owner().

Hypervisor could share run-status of vcpus in guest kernel memory. This would allow us to optimize routines like mutex_spin_on_owner() - we don't spin-wait if we relaize that the target vcpu has been preempted.

a) and c) are about dealing with the LHP problem, while b) is about preventing the problem from happening.

Scenario A:

	W/o patch		W/ Patch		Difference
	Avg  (std-dev)		Avg. (std-dev)
1:	273.270  (1.051)	251.613 (1.155)		7.9%
2:	541.343  (57.317)	443.400 (2.832)		18.1%
3:	819.160  (9.218)	642.393 (2.646)		21.6%

4: 1020.493 (34.743) 839.327 (0.658) 17.8%

Scenario B:

1: 276.947 (0.644) 248.397 (1.563) 10.3%
2: 500.723 (5.694) 438.957 (6.112) 12.33%
3: 725.687 (5.267) 641.317 (3.902) 11.62%
4: 973.910 (21.712) 836.853 (2.754) 14.07%

Scenario C:

Quoted 7 lines
1:	564.610  (12.055)    	420.510 (2.598)		25.52%
2:	750.867  (5.139)	618.570 (2.914)		17.61%
3:	950.150  (13.496)      	813.803 (1.872)		14.35%

4: 1125.027 (5.337) 1007.63 (5.707) 10.43%

IMO this is good improvement with the patchset applied.

Those are consistent with the results I get too.

J

#20 lore raw
Quoted 26 lines · Jeremy Fitzhardinge
Quoted 14 lines

I tried to refactor Xen's spinlock
implementation to make it common for both Xen and KVM - but found that few differences between Xen and KVM (Xen has the ability to block on a particular event/irq for example) _and_ the fact that the guest kernel can be compiled to support both Xen and KVM hypervisors (CONFIG_XEN and CONFIG_KVM_GUEST can both be turned on) makes the "common" code a eye-sore. There will have to be:

	if (xen) {
		...
	} else if (kvm) {
		..
	}
Quoted 5 lines

or possibly:

alternative(NOP, some_xen_specific_call, ....)

type of code in the common implementation.

No, that doesn't look like a good approach. It suggests the apparently commonality isn't really there.

Quoted 2 lines

For the time-being, I have made this KVM-specific only. At somepoint in future, I hope this can be made common between Xen/KVM.

Quoted 7 lines

Did you see the patch series I posted a couple of weeks ago to revamp pv spinlocks? Specifically, I dropped the notion of pv spinlocks in which the entire spinlock implementation is replaced, and added pv ticketlocks where the ticketlock algorithm is always used for the fastpath, but it calls out to pvop calls for the slowpath (a long spin, or unlocking a lock with waiters). It significantly reduces the amount of hypervisor-specific code.

Hmmm interesting - I will go thr' it in detail.

Quoted 4 lines

You can see the current patches in

git://git.kernel.org/pub/scm/linux/kernel/git/jeremy/xen.git
xen/pvticketlock-git

[snip]

Quoted 7 lines

That's not actually the real problem. It's *a* problem, but insignificant compared to the ticketlock-specific "next-in-line vcpu scheduler bunfight" problem - lock holder preemption is a misnomer. Fortunately the solutions to both are (nearly) the same.

See Thomas Friebel's talk "Prevent Guests from Spinning Around
(http://www.xen.org/files/xensummitboston08/LHP.pdf).

Yes I had seen Thomas's slides reporting huge degradation in performance with tick spinlock.

Quoted 12 lines
Quoted 11 lines

b. Avoid preempting a lock-holder while its holding a (spin-) lock.

In this scheme, guest OS can hint (set some flag in memory shared with hypervisor) whenever its holding a lock and hypervisor could defer preempting the guest vcpu when its holding a lock. With this scheme, we should never have a lock-acquiring vcpu spin on a preempted vcpu to release its lock. If ever it spins, its because somebody *currently running* is holding the lock - and hence it won't have to spin-wait too long. IOW we are pro-actively trying to prevent the LHP problem from occuring in the first place. This should improve job turnaround time for some workloads. [1] has some results based on this approach.

Quoted 3 lines

This doesn't actually help the problem mentioned above, because it's not a problem with the lock holder getting preempted, but what happens once the lock has been released.

Good point. I agree that the latter problem needs more attention, given a ticket-type implementation of spinlocks. Have you considered possible solutions for unmodified guests, which have similar ticket-type lock implementations? Not sure if that's important enough to investigate solutions like gang scheduling ..

- vatsa

#21 lore raw
In reply to Srivatsa Vaddagiri
Quoted 15 lines · Srivatsa Vaddagiri

This patch-series implements paravirt-spinlock implementation for KVM guests, based heavily on Xen's implementation. I tried to refactor Xen's spinlock implementation to make it common for both Xen and KVM - but found that few differences between Xen and KVM (Xen has the ability to block on a particular event/irq for example) _and_ the fact that the guest kernel can be compiled to support both Xen and KVM hypervisors (CONFIG_XEN and CONFIG_KVM_GUEST can both be turned on) makes the "common" code a eye-sore. There will have to be:

	if (xen) {
		...
	} else if (kvm) {
		..
	}
Quoted 3 lines

or possibly:

alternative(NOP, some_xen_specific_call, ....)

Why not utilize the pvops path?

#22 lore raw
Quoted 16 lines · Konrad Rzeszutek Wilk
Quoted 15 lines · Srivatsa Vaddagiri

This patch-series implements paravirt-spinlock implementation for KVM guests, based heavily on Xen's implementation. I tried to refactor Xen's spinlock implementation to make it common for both Xen and KVM - but found that few differences between Xen and KVM (Xen has the ability to block on a particular event/irq for example) _and_ the fact that the guest kernel can be compiled to support both Xen and KVM hypervisors (CONFIG_XEN and CONFIG_KVM_GUEST can both be turned on) makes the "common" code a eye-sore. There will have to be:

	if (xen) {
		...
	} else if (kvm) {
		..
	}
Quoted 5 lines
Quoted 3 lines

or possibly:

alternative(NOP, some_xen_specific_call, ....)

Why not utilize the pvops path?

Duh. You did use it. Sorry about the noise.

#23 lore raw
In reply to Srivatsa Vaddagiri
Quoted 15 lines · Srivatsa Vaddagiri

This patch-series implements paravirt-spinlock implementation for KVM guests, based heavily on Xen's implementation. I tried to refactor Xen's spinlock implementation to make it common for both Xen and KVM - but found that few differences between Xen and KVM (Xen has the ability to block on a particular event/irq for example) _and_ the fact that the guest kernel can be compiled to support both Xen and KVM hypervisors (CONFIG_XEN and CONFIG_KVM_GUEST can both be turned on) makes the "common" code a eye-sore. There will have to be:

	if (xen) {
		...
	} else if (kvm) {
		..
	}
Quoted 5 lines

or possibly:

alternative(NOP, some_xen_specific_call, ....)

type of code in the common implementation.

I do think things are pretty common. If that is the only issue, you can use a plain function vector, no?

Signature · 1 line
error compiling committee.c: too many arguments to function