Skip to contentSkip to conversation

KVM: pvmmu breakage with gcc 4.3.0

PATCH Marcelo Tosatti 7 participants 13 messages Jun 26, 2008 – Jun 29, 2008

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

Some pvmmu functions store their commands on stack, and newer GCC versions conclude that these commands are unused.

So stick an inline asm statement to convince the compiler otherwise.

arch/x86/kernel/kvm.c +6 -0
index 8b7a3cf..c892752 100644
@@ -55,6 +55,12 @@ static void kvm_mmu_op(void *buffer, unsigned len)
 	int r;
 	unsigned long a1, a2;
 
+	/*
+   	 * GCC 4.3.0 concludes that on-stack kvm_mmu_op* is unused and
+   	 * optimizes its initialization away.
+ 	 */
+        asm ("" : : "p" (buffer));
+
 	do {
 		a1 = __pa(buffer);
 		a2 = 0;   /* on i386 __pa() always returns <4G */
#1 lore raw
Quoted 5 lines · Marcelo Tosatti

Some pvmmu functions store their commands on stack, and newer GCC versions conclude that these commands are unused.

So stick an inline asm statement to convince the compiler otherwise.

I think a better fix is to add a "memory " clobber to the hypercalls. This isn't really a GCC bug since it doesn't realize that hypercalls can touch memory.

See the attached patch.

Avi: please push this for 2.6.26 if possible.

Regards,

Anthony Liguori

Quoted 21 lines
arch/x86/kernel/kvm.c +6 -1
index 8b7a3cf..c892752 100644
@@ -55,6 +55,12 @@ static void kvm_mmu_op(void *buffer, unsigned len)
 	int r;
 	unsigned long a1, a2;
 
+	/*
+   	 * GCC 4.3.0 concludes that on-stack kvm_mmu_op* is unused and
+   	 * optimizes its initialization away.
+ 	 */
+        asm ("" : : "p" (buffer));
+
 	do {
 		a1 = __pa(buffer);
 		a2 = 0;   /* on i386 __pa() always returns <4G */
--
Quoted 4 lines

To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

Subject: [PATCH] Add memory clobber to hypercalls
Cc: Avi Kivity <[email protected]>
Cc: Marcelo Tosatti <[email protected]>

Hypercalls can modify arbitrary regions of memory. Make sure to indicate this in the clobber list. This fixes a hang when using KVM_GUEST kernel built with GCC 4.3.0.

This was originally spotted and analyzed by Marcelo.

include/asm-x86/kvm_para.h +10 -5
index bfd9900..76f3921 100644
@@ -71,7 +71,8 @@ static inline long kvm_hypercall0(unsigned int nr)
 	long ret;
 	asm volatile(KVM_HYPERCALL
 		     : "=a"(ret)
-		     : "a"(nr));
+		     : "a"(nr)
+		     : "memory");
 	return ret;
 }
 
@@ -80,7 +81,8 @@ static inline long kvm_hypercall1(unsigned int nr, unsigned long p1)
 	long ret;
 	asm volatile(KVM_HYPERCALL
 		     : "=a"(ret)
-		     : "a"(nr), "b"(p1));
+		     : "a"(nr), "b"(p1)
+		     : "memory");
 	return ret;
 }
 
@@ -90,7 +92,8 @@ static inline long kvm_hypercall2(unsigned int nr, unsigned long p1,
 	long ret;
 	asm volatile(KVM_HYPERCALL
 		     : "=a"(ret)
-		     : "a"(nr), "b"(p1), "c"(p2));
+		     : "a"(nr), "b"(p1), "c"(p2)
+		     : "memory");
 	return ret;
 }
 
@@ -100,7 +103,8 @@ static inline long kvm_hypercall3(unsigned int nr, unsigned long p1,
 	long ret;
 	asm volatile(KVM_HYPERCALL
 		     : "=a"(ret)
-		     : "a"(nr), "b"(p1), "c"(p2), "d"(p3));
+		     : "a"(nr), "b"(p1), "c"(p2), "d"(p3)
+		     : "memory");
 	return ret;
 }
 
@@ -111,7 +115,8 @@ static inline long kvm_hypercall4(unsigned int nr, unsigned long p1,
 	long ret;
 	asm volatile(KVM_HYPERCALL
 		     : "=a"(ret)
-		     : "a"(nr), "b"(p1), "c"(p2), "d"(p3), "S"(p4));
+		     : "a"(nr), "b"(p1), "c"(p2), "d"(p3), "S"(p4)
+		     : "memory");
 	return ret;
 }
 
#2 lore raw

Am Donnerstag, 26. Juni 2008 schrieb Anthony Liguori:

Quoted 10 lines
Quoted 5 lines · Marcelo Tosatti

Some pvmmu functions store their commands on stack, and newer GCC versions conclude that these commands are unused.

So stick an inline asm statement to convince the compiler otherwise.

I think a better fix is to add a "memory " clobber to the hypercalls. This isn't really a GCC bug since it doesn't realize that hypercalls can touch memory.

Thats what we do on asm-s390/kvm_para.h. Our hypercalls do have a memory clobber.

#3 lore raw
In reply to Anthony Liguori
Quoted 1 line · Anthony Liguori

I think a better fix is to add a "memory " clobber to the hypercalls.

This should definitely be done, but I don't see that this is a sure way to get that initializers that are otherwise unreferenced to be stored in their locations. IOW, it appears to me that GCC might still decide to optimize them away, because it doesn't see that the values can be used.

Now, Marcelo's patch appears to have been the result of a misunderstanding. I meant to tell him that a simpler "p" notation was *not* enough to make the problem go away. I thought a better approach was to have __pa do something along the lines of

({ char *__buf = (char*)buffer; asm ("" : : "o"(*__buf)); ... })

to make it clear that physical addresses are used.

Anyhow, for now, it appears that a "memory" clobber is not only required for correct semantics, but also enough to fix the problem. Let's just hope GCC doesn't get too smart.

Signature · 4 lines
Alexandre Oliva         http://www.lsd.ic.unicamp.br/~oliva/
Free Software Evangelist  oliva@{lsd.ic.unicamp.br, gnu.org}
FSFLA Board Member       ¡Sé Libre! => http://www.fsfla.org/
Red Hat Compiler Engineer   aoliva@{redhat.com, gcc.gnu.org}
#4 lore raw
Quoted 8 lines · Alexandre Oliva

I thought a better approach
was to have __pa do something along the lines of

({ char *__buf = (char*)buffer; asm ("" : : "o"(*__buf)); ... })

to make it clear that physical addresses are used.

I agree, I think such a patch is important for future-proofing Linux against increasing gcc optimization capabilities.

Signature · 1 line
error compiling committee.c: too many arguments to function
#5 lore raw
In reply to Marcelo Tosatti
Quoted 13 lines · Marcelo Tosatti

Some pvmmu functions store their commands on stack, and newer GCC versions conclude that these commands are unused.

So stick an inline asm statement to convince the compiler otherwise.

arch/x86/kernel/kvm.c +0 -0
index 8b7a3cf..c892752 100644

arch/x86/kernel/kvm.c static void kvm_mmu_op(void *buffer, unsigned len) · line 64

Quoted 10 lines
arch/x86/kernel/kvm.c +6 -0
@@ -55,6 +55,12 @@ static void kvm_mmu_op(void *buffer, unsigned len)
 	int r;
 	unsigned long a1, a2;
 
+	/*
+   	 * GCC 4.3.0 concludes that on-stack kvm_mmu_op* is unused and
+   	 * optimizes its initialization away.
+ 	 */
+        asm ("" : : "p" (buffer));
+
 

I don't think "p" should force the contents into memory? Perhaps "m"(*(char *)buffer)?

Anthony, I don't see why a memory clobber would tell gcc that the variables is actually used. The problem is with the void * -> unsigned long cast (__pa), once that happens gcc loses track. It's probably needed anyway since hypercalls _do_ clobber memory.

Signature · 1 line
Do not meddle in the internals of kernels, for they are subtle and quick to panic.
#6 lore raw
Quoted 27 lines · Avi Kivity
Quoted 13 lines · Marcelo Tosatti

Some pvmmu functions store their commands on stack, and newer GCC versions conclude that these commands are unused.

So stick an inline asm statement to convince the compiler otherwise.

arch/x86/kernel/kvm.c +0 -0
index 8b7a3cf..c892752 100644

arch/x86/kernel/kvm.c static void kvm_mmu_op(void *buffer, unsigned len) · line 64

Quoted 10 lines
arch/x86/kernel/kvm.c +6 -0
@@ -55,6 +55,12 @@ static void kvm_mmu_op(void *buffer, unsigned len)
     int r;
     unsigned long a1, a2;
 
+    /*
+        * GCC 4.3.0 concludes that on-stack kvm_mmu_op* is unused and
+        * optimizes its initialization away.
+      */
+        asm ("" : : "p" (buffer));
+
 

I don't think "p" should force the contents into memory? Perhaps "m"(*(char *)buffer)?

Quoted 1 line

Anthony, I don't see why a memory clobber would tell gcc that the variables is actually used.

It doesn't, but it seems to me that it should force GCC to assume everything is live. It's a big stick to hit the problem with but it seems like the right thing semantically.

Quoted 2 lines

The problem is with the void * -> unsigned long cast (__pa), once
that happens gcc loses track. It's probably needed anyway since hypercalls _do_ clobber memory.

Right, it's not telling GCC that we touch a particular variable, but rather that we may have touched any variable.

Regards,

Anthony Liguori

#7 lore raw
Quoted 28 lines · Anthony Liguori
Quoted 27 lines · Avi Kivity
Quoted 13 lines · Marcelo Tosatti

Some pvmmu functions store their commands on stack, and newer GCC versions conclude that these commands are unused.

So stick an inline asm statement to convince the compiler otherwise.

arch/x86/kernel/kvm.c +0 -0
index 8b7a3cf..c892752 100644

arch/x86/kernel/kvm.c static void kvm_mmu_op(void *buffer, unsigned len) · line 64

Quoted 10 lines
arch/x86/kernel/kvm.c +6 -0
@@ -55,6 +55,12 @@ static void kvm_mmu_op(void *buffer, unsigned len)
     int r;
     unsigned long a1, a2;
 
+    /*
+        * GCC 4.3.0 concludes that on-stack kvm_mmu_op* is unused and
+        * optimizes its initialization away.
+      */
+        asm ("" : : "p" (buffer));
+
 

I don't think "p" should force the contents into memory? Perhaps "m"(*(char *)buffer)?

Quoted 4 lines
Quoted 1 line

Anthony, I don't see why a memory clobber would tell gcc that the variables is actually used.

It doesn't, but it seems to me that it should force GCC to assume everything is live. It's a big stick to hit the problem with but it seems like the right thing semantically.

In general the right stick to hit a compiler with is 'volatile,' which should tell the compiler that outside forces are in play. Without really digging into the code I don't want to suggest just where this should be used, but clearly Christian had the same thought.

Quoted 5 lines
Quoted 2 lines

The problem is with the void * -> unsigned long cast (__pa), once
that happens gcc loses track. It's probably needed anyway since hypercalls _do_ clobber memory.

Right, it's not telling GCC that we touch a particular variable, but rather that we may have touched any variable.

Signature · 3 lines
Bill Davidsen <[email protected]>
  "We have more to fear from the bungling of the incompetent than from
the machinations of the wicked."  - from Slashdot
#8 lore raw
In reply to Avi Kivity

Am Donnerstag, 26. Juni 2008 schrieb Avi Kivity:

Quoted 7 lines

I don't think "p" should force the contents into memory? Perhaps "m"(*(char *)buffer)?

Anthony, I don't see why a memory clobber would tell gcc that the variables is actually used. The problem is with the void * -> unsigned long cast (__pa), once that happens gcc loses track. It's probably needed anyway since hypercalls _do_ clobber memory.

I I think about that again, the correct solution should be to use 2 input constraints for parameters together with the memory clobber on hypercall.

I think something like the following covers all cases:

static inline long kvm_hypercall1(unsigned int nr, unsigned long p1)

{
	long ret;
	asm volatile(KVM_HYPERCALL
		     : "=a"(ret)
		     : "a"(nr), "b"(p1), "m"(*(char *) p1)
		     : "memory" );
	return ret;
}

The address and the memory content of p1 (if it is a pointer) is not ommitted by gcc. Furthermore, the memory clobbering nature of a hypercall is specified as well.

Christian

#9 lore raw
Quoted 22 lines · Christian Borntraeger

Am Donnerstag, 26. Juni 2008 schrieb Avi Kivity:

Quoted 4 lines

I don't think "p" should force the contents into memory? Perhaps "m"(*(char *)buffer)?

Anthony, I don't see why a memory clobber would tell gcc that the variables is actually used. The problem is with the void * -> unsigned long cast (__pa), once that happens gcc loses track. It's probably needed anyway since hypercalls _do_ clobber memory.

I I think about that again, the correct solution should be to use 2 input constraints for parameters together with the memory clobber on hypercall.

I think something like the following covers all cases:

static inline long kvm_hypercall1(unsigned int nr, unsigned long p1)

{
	long ret;
	asm volatile(KVM_HYPERCALL
		     : "=a"(ret)
		     : "a"(nr), "b"(p1), "m"(*(char *) p1)
		     : "memory" );
	return ret;
}
Quoted 2 lines

The address and the memory content of p1 (if it is a pointer) is not ommitted by gcc. Furthermore, the memory clobbering nature of a hypercall is specified as well.

Isn't that redundant though? Shouldn't a memory clobber force GCC to assume that anything that's reachable is live?

Regards,

Anthony Liguori

Quoted 2 lines

Christian

#10 lore raw
In reply to Christian Borntraeger
Quoted 12 lines · Christian Borntraeger

Am Donnerstag, 26. Juni 2008 schrieb Avi Kivity:

Quoted 4 lines

I don't think "p" should force the contents into memory? Perhaps "m"(*(char *)buffer)?

Anthony, I don't see why a memory clobber would tell gcc that the variables is actually used. The problem is with the void * -> unsigned long cast (__pa), once that happens gcc loses track. It's probably needed anyway since hypercalls _do_ clobber memory.

I I think about that again, the correct solution should be to use 2 input constraints for parameters together with the memory clobber on hypercall.

I think something like the following covers all cases:

static inline long kvm_hypercall1(unsigned int nr, unsigned long p1)

Quoted 9 lines
{
	long ret;
	asm volatile(KVM_HYPERCALL
		     : "=a"(ret)
		     : "a"(nr), "b"(p1), "m"(*(char *) p1)
		     : "memory" );
	return ret;
}

I don't know exactly what the rules are when you cast from pointer to unsigned long. What I'm concerned about is that if there are a few layers of indirection, GCC won't be able to propagate the liveness of the pointer p1. It has to be able to figure out that p1 here was really that point on the stack. From the perspective of being conservative, it certainly can't hurt, but I'm not sure it solves the problem by itself.

This is why I think the "memory" constraint is really the right solution. That should force GCC to be very conservative about syncing everything to memory.

Regards,

Anthony Liguori

Quoted 8 lines

The address and the memory content of p1 (if it is a pointer) is not ommitted by gcc. Furthermore, the memory clobbering nature of a hypercall is specified as well.

Christian
--
To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#11 lore raw

Am Donnerstag, 26. Juni 2008 schrieb Anthony Liguori:

Quoted 35 lines
Quoted 17 lines · Christian Borntraeger

Am Donnerstag, 26. Juni 2008 schrieb Avi Kivity:

Quoted 8 lines

I don't think "p" should force the contents into memory? Perhaps "m"(*(char *)buffer)?

Anthony, I don't see why a memory clobber would tell gcc that the variables is actually used. The problem is with the void * -> unsigned long cast (__pa), once that happens gcc loses track. It's probably needed anyway since hypercalls _do_ clobber memory.

I I think about that again, the correct solution should be to use 2 input constraints for parameters together with the memory clobber on hypercall.

I think something like the following covers all cases:

static inline long kvm_hypercall1(unsigned int nr, unsigned long p1)

Quoted 9 lines
{
	long ret;
	asm volatile(KVM_HYPERCALL
		     : "=a"(ret)
		     : "a"(nr), "b"(p1), "m"(*(char *) p1)
		     : "memory" );
	return ret;
}

I don't know exactly what the rules are when you cast from pointer to unsigned long. What I'm concerned about is that if there are a few layers of indirection, GCC won't be able to propagate the liveness of the pointer p1. It has to be able to figure out that p1 here was really that point on the stack. From the perspective of being conservative, it certainly can't hurt, but I'm not sure it solves the problem by itself.

Quoted 3 lines

This is why I think the "memory" constraint is really the right solution. That should force GCC to be very conservative about syncing everything to memory.

The thing is, that your patch has "memory" in the clobber list. I dont know if clobber constraints give any guarantees about input constraints. Thats why I think that "b" plus "m" as input constraints are better. Does gcc actually accept "memory" as input contraint?

I think I have to ask our compiler developers, but in 5 minutes I will be offline until monday...

#12 lore raw
In reply to Christian Borntraeger
Quoted 22 lines · Christian Borntraeger

Am Donnerstag, 26. Juni 2008 schrieb Avi Kivity:

Quoted 4 lines

I don't think "p" should force the contents into memory? Perhaps "m"(*(char *)buffer)?

Anthony, I don't see why a memory clobber would tell gcc that the variables is actually used. The problem is with the void * -> unsigned long cast (__pa), once that happens gcc loses track. It's probably needed anyway since hypercalls _do_ clobber memory.

I I think about that again, the correct solution should be to use 2 input constraints for parameters together with the memory clobber on hypercall.

I think something like the following covers all cases:

static inline long kvm_hypercall1(unsigned int nr, unsigned long p1)

{
	long ret;
	asm volatile(KVM_HYPERCALL
		     : "=a"(ret)
		     : "a"(nr), "b"(p1), "m"(*(char *) p1)
		     : "memory" );
	return ret;
}
Quoted 2 lines

The address and the memory content of p1 (if it is a pointer) is not ommitted by gcc. Furthermore, the memory clobbering nature of a hypercall is specified as well.

That only works if p1 is a virtual address. x86 (and ppc) hypercalls use physical addresses.

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