diff mbox

KVM: Qemu: Flush i-cache after ide-dma operation in IA64

Message ID 10C63FAD690C13458F0B32BCED571F140F98ED4B@pdsmsx502.ccr.corp.intel.com (mailing list archive)
State Accepted
Headers show

Commit Message

Zhang, Yang April 2, 2009, 2:01 a.m. UTC
The data from dma will include instructions. In order to exeuting the right
instruction, we should to flush the i-cache to ensure those data can be see 
by cpu.

Signed-off-by: Xiantao Zhang <xiantao.zhang@intel.com>
Signed-off-by: Yang Zhang <yang.zhang@intel.com>
---

Comments

Avi Kivity April 2, 2009, 8:55 a.m. UTC | #1
Zhang, Yang wrote:
> The data from dma will include instructions. In order to exeuting the right
> instruction, we should to flush the i-cache to ensure those data can be see 
> by cpu.
>
>
>
> diff --git a/qemu/cache-utils.h b/qemu/cache-utils.h
> index b45fde4..5e11d12 100644
> --- a/qemu/cache-utils.h
> +++ b/qemu/cache-utils.h
> @@ -33,8 +33,22 @@ static inline void flush_icache_range(unsigned long start, unsigned long stop)
>      asm volatile ("sync" : : : "memory");
>      asm volatile ("isync" : : : "memory");
>  }
> +#define qemu_sync_idcache flush_icache_range
> +#else
>  
> +#ifdef __ia64__
> +static inline void qemu_sync_idcache(unsigned long start, unsigned long stop)
> +{
> +    while (start < stop) {
> +	    asm volatile ("fc %0" :: "r"(start));
> +	    start += 32;
> +    }
> +    asm volatile (";;sync.i;;srlz.i;;");
> +}
>   

What about smp?

I'm surprised the guest doesn't do this by itself?

>  
>  void pstrcpy(char *buf, int buf_size, const char *str)
> @@ -215,6 +216,8 @@ void qemu_iovec_from_buffer(QEMUIOVector *qiov, const void *buf, size_t count)
>          if (copy > qiov->iov[i].iov_len)
>              copy = qiov->iov[i].iov_len;
>          memcpy(qiov->iov[i].iov_base, p, copy);
> +        qemu_sync_idcache((unsigned long)qiov->iov[i].iov_base, 
> +                    (unsigned long)(qiov->iov[i].iov_base + copy));
>          p     += copy;
>          count -= copy;
>      }
>   

This is the wrong place to put this.  Once we stop bouncing 
scatter/gather DMA, we will no longer call this function.

The correct place is either in the device code itself, or in the dma api 
(dma-helpers.c).
tgingold@free.fr April 2, 2009, 3:41 p.m. UTC | #2
Quoting Avi Kivity <avi@redhat.com>:

> Zhang, Yang wrote:
> > The data from dma will include instructions. In order to exeuting the right
> > instruction, we should to flush the i-cache to ensure those data can be see
> > by cpu.
> >
> >
> >
> > diff --git a/qemu/cache-utils.h b/qemu/cache-utils.h
> > index b45fde4..5e11d12 100644
> > --- a/qemu/cache-utils.h
> > +++ b/qemu/cache-utils.h
> > @@ -33,8 +33,22 @@ static inline void flush_icache_range(unsigned long
> start, unsigned long stop)
> >      asm volatile ("sync" : : : "memory");
> >      asm volatile ("isync" : : : "memory");
> >  }
> > +#define qemu_sync_idcache flush_icache_range
> > +#else
> >
> > +#ifdef __ia64__
> > +static inline void qemu_sync_idcache(unsigned long start, unsigned long
> stop)
> > +{
> > +    while (start < stop) {
> > +	    asm volatile ("fc %0" :: "r"(start));
> > +	    start += 32;
> > +    }
> > +    asm volatile (";;sync.i;;srlz.i;;");
> > +}
> >

As I hit the same issue a year ago, here is my understanding:

> What about smp?

fc will broadcast to the coherence domain the cache invalidation.  So it is
SMP-ready for usual machines.

> I'm surprised the guest doesn't do this by itself?

It doesn't had to do it.  The PCI transaction will automatically invalidate
caches - but qemu doesn't emulate this (and doesn't need to do on x86).

Tristan.
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Avi Kivity April 2, 2009, 3:53 p.m. UTC | #3
tgingold@free.fr wrote:
>   
>> What about smp?
>>     
>
> fc will broadcast to the coherence domain the cache invalidation.  So it is
> SMP-ready for usual machines.
>
>   

Interesting.

>> I'm surprised the guest doesn't do this by itself?
>>     
>
> It doesn't had to do it.  The PCI transaction will automatically invalidate
> caches - but qemu doesn't emulate this (and doesn't need to do on x86).
>   

So any DMA on ia64 will flush the instruction caches?!
Avi Kivity April 2, 2009, 3:59 p.m. UTC | #4
Avi Kivity wrote:
>>>  
>>
>> It doesn't had to do it.  The PCI transaction will automatically 
>> invalidate
>> caches - but qemu doesn't emulate this (and doesn't need to do on x86).
>>   
>
> So any DMA on ia64 will flush the instruction caches?!
>

Or maybe, the host kernel will do it after the transaction completes?  
In our case the lack of zero-copy means the host is invalidating the 
wrong addresses (memcpy source) and leaving the real destination intact.
Zhang, Xiantao April 3, 2009, 1:13 a.m. UTC | #5
Avi Kivity wrote:
> tgingold@free.fr wrote:
>> 
>>> What about smp?
>>> 
>> 
>> fc will broadcast to the coherence domain the cache invalidation. 
>> So it is SMP-ready for usual machines. 
>> 
>> 
> 
> Interesting.
> 
>>> I'm surprised the guest doesn't do this by itself?
>>> 
>> 
>> It doesn't had to do it.  The PCI transaction will automatically
>> invalidate caches - but qemu doesn't emulate this (and doesn't need
>> to do on x86). 
>> 
> 
> So any DMA on ia64 will flush the instruction caches?

Yes, physical DMA should do this, but for virtual DMA operation emulated by Qemu should use explict intrusctions(fc, sync.i) to get it happen, because the data transferred by virtual DMA maybe used as instrustion streams by guest. 
Xiantao--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Zhang, Xiantao April 3, 2009, 1:22 a.m. UTC | #6
Avi Kivity wrote:
> Avi Kivity wrote:
>>>> 
>>> 
>>> It doesn't had to do it.  The PCI transaction will automatically
>>> invalidate caches - but qemu doesn't emulate this (and doesn't need
>>> to do on x86). 
>>> 
>> 
>> So any DMA on ia64 will flush the instruction caches?!
>> 
> 
> Or maybe, the host kernel will do it after the transaction completes?

Host kernel doesn't do anything about cache flush after DMA, since it thinks platform guarantees that. 

> In our case the lack of zero-copy means the host is invalidating the
> wrong addresses (memcpy source) and leaving the real destination
> intact. 

We just need to sync the target address(destination address), because only its physical address belongs to guest, and likely to be the DMA target address of guest. 

Xiantao
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Zhang, Xiantao April 3, 2009, 1:31 a.m. UTC | #7
Avi Kivity wrote:
> Zhang, Yang wrote:
>> The data from dma will include instructions. In order to exeuting
>> the right 
>> instruction, we should to flush the i-cache to ensure those data can
>> be see 
>> by cpu.
>> 
>> 
>> 
>> diff --git a/qemu/cache-utils.h b/qemu/cache-utils.h
>> index b45fde4..5e11d12 100644
>> --- a/qemu/cache-utils.h
>> +++ b/qemu/cache-utils.h
>> @@ -33,8 +33,22 @@ static inline void flush_icache_range(unsigned
>>      long start, unsigned long stop) asm volatile ("sync" : : :
>>      "memory"); asm volatile ("isync" : : : "memory");
>>  }
>> +#define qemu_sync_idcache flush_icache_range
>> +#else
>> 
>> +#ifdef __ia64__
>> +static inline void qemu_sync_idcache(unsigned long start, unsigned
>> long stop) +{ +    while (start < stop) {
>> +	    asm volatile ("fc %0" :: "r"(start));
>> +	    start += 32;
>> +    }
>> +    asm volatile (";;sync.i;;srlz.i;;");
>> +}
>> 
> 
> What about smp?
> 
> I'm surprised the guest doesn't do this by itself?
> 
>> 
>>  void pstrcpy(char *buf, int buf_size, const char *str)
>> @@ -215,6 +216,8 @@ void qemu_iovec_from_buffer(QEMUIOVector *qiov,
>>          const void *buf, size_t count) if (copy >
>>              qiov->iov[i].iov_len) copy = qiov->iov[i].iov_len;
>>          memcpy(qiov->iov[i].iov_base, p, copy);
>> +        qemu_sync_idcache((unsigned long)qiov->iov[i].iov_base,
>> +                    (unsigned long)(qiov->iov[i].iov_base + copy));
>>          p     += copy; count -= copy;
>>      }
>> 
> 
> This is the wrong place to put this.  Once we stop bouncing
> scatter/gather DMA, we will no longer call this function.

This patch intends to fix the issue before adopting scatter/gather DMA mode. But if we want to keep this funtion, had better to pick it to avoid such issues in future. 

> The correct place is either in the device code itself, or in the dma
> api (dma-helpers.c).

Maybe dma-helpers.c 
Xiantao--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Avi Kivity April 3, 2009, 11:06 a.m. UTC | #8
Zhang, Xiantao wrote:
>> This is the wrong place to put this.  Once we stop bouncing
>> scatter/gather DMA, we will no longer call this function.
>>     
>
> This patch intends to fix the issue before adopting scatter/gather DMA mode. But if we want to keep this funtion, had better to pick it to avoid such issues in future. 
>
>   

This function is the wrong place, it just happens to be called.

>> The correct place is either in the device code itself, or in the dma
>> api (dma-helpers.c).
>>     
>
> Maybe dma-helpers.c 

Please send a patch with this in dma-helpers.c.
Hollis Blanchard April 6, 2009, 4:31 p.m. UTC | #9
On Thu, 2009-04-02 at 10:01 +0800, Zhang, Yang wrote:
> The data from dma will include instructions. In order to exeuting the right
> instruction, we should to flush the i-cache to ensure those data can be see 
> by cpu.
> 
> Signed-off-by: Xiantao Zhang <xiantao.zhang@intel.com>
> Signed-off-by: Yang Zhang <yang.zhang@intel.com>
> ---
> 
> 
> diff --git a/qemu/cache-utils.h b/qemu/cache-utils.h
> index b45fde4..5e11d12 100644
> --- a/qemu/cache-utils.h
> +++ b/qemu/cache-utils.h
> @@ -33,8 +33,22 @@ static inline void flush_icache_range(unsigned long start, unsigned long stop)
>      asm volatile ("sync" : : : "memory");
>      asm volatile ("isync" : : : "memory");
>  }
> +#define qemu_sync_idcache flush_icache_range
> +#else
>  
> +#ifdef __ia64__
> +static inline void qemu_sync_idcache(unsigned long start, unsigned long stop)
> +{
> +    while (start < stop) {
> +	    asm volatile ("fc %0" :: "r"(start));
> +	    start += 32;
> +    }
> +    asm volatile (";;sync.i;;srlz.i;;");
> +}
>  #else
> +static inline void qemu_sync_idcache(unsigned long start, unsigned long stop)
> +#endif
> +
>  #define qemu_cache_utils_init(envp) do { (void) (envp); } while (0)
>  #endif

You already have flush_icache_range() in qemu/target-ia64/fake-exec.c,
so this is redundant. Moving that to cache-utils.h might make sense, but
this should be discussed on qemu-devel.

Also, flush_icache_range() is already called from
cpu_physical_memory_rw(). It would be helpful to include a comment in
this commit explaining why this path is different. (I can see that it
is, but only because I went hunting myself.)

> diff --git a/qemu/cutils.c b/qemu/cutils.c
> index 5b36cc6..7b57173 100644
> --- a/qemu/cutils.c
> +++ b/qemu/cutils.c
> @@ -23,6 +23,7 @@
>   */
>  #include "qemu-common.h"
>  #include "host-utils.h"
> +#include "cache-utils.h"
>  #include <assert.h>
>  
>  void pstrcpy(char *buf, int buf_size, const char *str)
> @@ -215,6 +216,8 @@ void qemu_iovec_from_buffer(QEMUIOVector *qiov, const void *buf, size_t count)
>          if (copy > qiov->iov[i].iov_len)
>              copy = qiov->iov[i].iov_len;
>          memcpy(qiov->iov[i].iov_base, p, copy);
> +        qemu_sync_idcache((unsigned long)qiov->iov[i].iov_base, 
> +                    (unsigned long)(qiov->iov[i].iov_base + copy));
>          p     += copy;
>          count -= copy;
>      }

This is way too generic a call for this location. Other architectures
also need to synchronize L1 caches sometimes, but they don't need to do
it here. You need to comment and guard this call better (probably using
some combination of kvm_enabled() and ifdefs).
diff mbox

Patch

diff --git a/qemu/cache-utils.h b/qemu/cache-utils.h
index b45fde4..5e11d12 100644
--- a/qemu/cache-utils.h
+++ b/qemu/cache-utils.h
@@ -33,8 +33,22 @@  static inline void flush_icache_range(unsigned long start, unsigned long stop)
     asm volatile ("sync" : : : "memory");
     asm volatile ("isync" : : : "memory");
 }
+#define qemu_sync_idcache flush_icache_range
+#else
 
+#ifdef __ia64__
+static inline void qemu_sync_idcache(unsigned long start, unsigned long stop)
+{
+    while (start < stop) {
+	    asm volatile ("fc %0" :: "r"(start));
+	    start += 32;
+    }
+    asm volatile (";;sync.i;;srlz.i;;");
+}
 #else
+static inline void qemu_sync_idcache(unsigned long start, unsigned long stop)
+#endif
+
 #define qemu_cache_utils_init(envp) do { (void) (envp); } while (0)
 #endif
 
diff --git a/qemu/cutils.c b/qemu/cutils.c
index 5b36cc6..7b57173 100644
--- a/qemu/cutils.c
+++ b/qemu/cutils.c
@@ -23,6 +23,7 @@ 
  */
 #include "qemu-common.h"
 #include "host-utils.h"
+#include "cache-utils.h"
 #include <assert.h>
 
 void pstrcpy(char *buf, int buf_size, const char *str)
@@ -215,6 +216,8 @@  void qemu_iovec_from_buffer(QEMUIOVector *qiov, const void *buf, size_t count)
         if (copy > qiov->iov[i].iov_len)
             copy = qiov->iov[i].iov_len;
         memcpy(qiov->iov[i].iov_base, p, copy);
+        qemu_sync_idcache((unsigned long)qiov->iov[i].iov_base, 
+                    (unsigned long)(qiov->iov[i].iov_base + copy));
         p     += copy;
         count -= copy;
     }