diff mbox

[Xen-devel,v4,18/21] xen/arm: p2m: Clean cache PT when the IOMMU doesn't support coherent walk

Message ID 1398172475-27873-19-git-send-email-julien.grall@linaro.org
State Superseded, archived
Headers show

Commit Message

Julien Grall April 22, 2014, 1:14 p.m. UTC
Some IOMMU doesn't suppport coherent PT walk. When the p2m is shared with
the CPU, Xen has to make sure the PT changes have reached the memory.

Introduce new IOMMU callback that will retrieve the IOMMU feature for a
specified domain.

On ARM, the platform can contain multiple IOMMUs. Each of them may not
have the same set of feature. The domain parameter will be used to get the
set of features for IOMMUs used by this domain.

Signed-off-by: Julien Grall <julien.grall@linaro.org>

---
    Changes in v4:
        - Patch added
---
 xen/arch/arm/p2m.c              |   24 ++++++++++++++++++------
 xen/drivers/passthrough/iommu.c |   11 +++++++++++
 xen/include/xen/iommu.h         |    5 +++++
 3 files changed, 34 insertions(+), 6 deletions(-)

Comments

Ian Campbell April 28, 2014, 2:09 p.m. UTC | #1
On Tue, 2014-04-22 at 14:14 +0100, Julien Grall wrote:
> Some IOMMU doesn't suppport coherent PT walk. When the p2m is shared with
> the CPU, Xen has to make sure the PT changes have reached the memory.
> 
> Introduce new IOMMU callback that will retrieve the IOMMU feature for a
> specified domain.
> 
> On ARM, the platform can contain multiple IOMMUs. Each of them may not
> have the same set of feature. The domain parameter will be used to get the
> set of features for IOMMUs used by this domain.
> 
> Signed-off-by: Julien Grall <julien.grall@linaro.org>
> 
> ---
>     Changes in v4:
>         - Patch added
> ---
>  xen/arch/arm/p2m.c              |   24 ++++++++++++++++++------
>  xen/drivers/passthrough/iommu.c |   11 +++++++++++
>  xen/include/xen/iommu.h         |    5 +++++
>  3 files changed, 34 insertions(+), 6 deletions(-)
> 
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index 21219de..996d2bd 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -274,6 +274,18 @@ enum p2m_operation {
>      CACHEFLUSH,
>  };
>  
> +static void unmap_coherent_domain_page(struct domain *d, const void *va)
> +{
> +    /* Some IOMMU doesn't support coherent PT walk. When the p2m is

"don't support"

> +     * shared with the CPU, Xen has to make sure that the PT changes have
> +     * reached the memory
> +     */
> +    if ( need_iommu(d) && !iommu_has_feature(d, IOMMU_FEAT_COHERENT_WALK) )
> +        clean_xen_dcache_va_range(va, PAGE_SIZE);

This is a fairly large hammer when you might only have touched a few
bytes, if any, in the page.

Wouldn't it be better to do this is write_pte, or just after the calls
to write_pte?

iommu_has_feature has a lot of callbacks -- worth calling once in
apply_p2m_changes?

Ian
Julien Grall April 28, 2014, 2:46 p.m. UTC | #2
On 04/28/2014 03:09 PM, Ian Campbell wrote:
>>  xen/arch/arm/p2m.c              |   24 ++++++++++++++++++------
>>  xen/drivers/passthrough/iommu.c |   11 +++++++++++
>>  xen/include/xen/iommu.h         |    5 +++++
>>  3 files changed, 34 insertions(+), 6 deletions(-)
>>
>> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
>> index 21219de..996d2bd 100644
>> --- a/xen/arch/arm/p2m.c
>> +++ b/xen/arch/arm/p2m.c
>> @@ -274,6 +274,18 @@ enum p2m_operation {
>>      CACHEFLUSH,
>>  };
>>  
>> +static void unmap_coherent_domain_page(struct domain *d, const void *va)
>> +{
>> +    /* Some IOMMU doesn't support coherent PT walk. When the p2m is
> 
> "don't support"
> 
>> +     * shared with the CPU, Xen has to make sure that the PT changes have
>> +     * reached the memory
>> +     */
>> +    if ( need_iommu(d) && !iommu_has_feature(d, IOMMU_FEAT_COHERENT_WALK) )
>> +        clean_xen_dcache_va_range(va, PAGE_SIZE);
> 
> This is a fairly large hammer when you might only have touched a few
> bytes, if any, in the page.
> 
> Wouldn't it be better to do this is write_pte, or just after the calls
> to write_pte?

I didn't think that the cache flush on a page was divided in small chunks.

> iommu_has_feature has a lot of callbacks -- worth calling once in
> apply_p2m_changes?

I will create a write_pte_coherent helpers which will take in parameter
a boolean to know if we need to flush the cache or not.

Regards,
Julien Grall April 28, 2014, 4:34 p.m. UTC | #3
I forgot to cc Jan and Xiantao on this patch.

On 04/22/2014 02:14 PM, Julien Grall wrote:
> Some IOMMU doesn't suppport coherent PT walk. When the p2m is shared with
> the CPU, Xen has to make sure the PT changes have reached the memory.
> 
> Introduce new IOMMU callback that will retrieve the IOMMU feature for a
> specified domain.
> 
> On ARM, the platform can contain multiple IOMMUs. Each of them may not
> have the same set of feature. The domain parameter will be used to get the
> set of features for IOMMUs used by this domain.
> 
> Signed-off-by: Julien Grall <julien.grall@linaro.org>
> 
> ---
>     Changes in v4:
>         - Patch added
> ---
>  xen/arch/arm/p2m.c              |   24 ++++++++++++++++++------
>  xen/drivers/passthrough/iommu.c |   11 +++++++++++
>  xen/include/xen/iommu.h         |    5 +++++
>  3 files changed, 34 insertions(+), 6 deletions(-)
> 
> diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
> index 21219de..996d2bd 100644
> --- a/xen/arch/arm/p2m.c
> +++ b/xen/arch/arm/p2m.c
> @@ -274,6 +274,18 @@ enum p2m_operation {
>      CACHEFLUSH,
>  };
>  
> +static void unmap_coherent_domain_page(struct domain *d, const void *va)
> +{
> +    /* Some IOMMU doesn't support coherent PT walk. When the p2m is
> +     * shared with the CPU, Xen has to make sure that the PT changes have
> +     * reached the memory
> +     */
> +    if ( need_iommu(d) && !iommu_has_feature(d, IOMMU_FEAT_COHERENT_WALK) )
> +        clean_xen_dcache_va_range(va, PAGE_SIZE);
> +
> +    unmap_domain_page(va);
> +}
> +
>  static int apply_p2m_changes(struct domain *d,
>                       enum p2m_operation op,
>                       paddr_t start_gpaddr,
> @@ -301,7 +313,7 @@ static int apply_p2m_changes(struct domain *d,
>      {
>          if ( cur_first_page != p2m_first_level_index(addr) )
>          {
> -            if ( first ) unmap_domain_page(first);
> +            if ( first ) unmap_coherent_domain_page(d, first);
>              first = p2m_map_first(p2m, addr);
>              if ( !first )
>              {
> @@ -331,7 +343,7 @@ static int apply_p2m_changes(struct domain *d,
>  
>          if ( cur_first_offset != first_table_offset(addr) )
>          {
> -            if (second) unmap_domain_page(second);
> +            if (second) unmap_coherent_domain_page(d, second);
>              second = map_domain_page(first[first_table_offset(addr)].p2m.base);
>              cur_first_offset = first_table_offset(addr);
>          }
> @@ -357,7 +369,7 @@ static int apply_p2m_changes(struct domain *d,
>          if ( cur_second_offset != second_table_offset(addr) )
>          {
>              /* map third level */
> -            if (third) unmap_domain_page(third);
> +            if (third) unmap_coherent_domain_page(d, third);
>              third = map_domain_page(second[second_table_offset(addr)].p2m.base);
>              cur_second_offset = second_table_offset(addr);
>          }
> @@ -480,9 +492,9 @@ static int apply_p2m_changes(struct domain *d,
>      rc = 0;
>  
>  out:
> -    if (third) unmap_domain_page(third);
> -    if (second) unmap_domain_page(second);
> -    if (first) unmap_domain_page(first);
> +    if (third) unmap_coherent_domain_page(d, third);
> +    if (second) unmap_coherent_domain_page(d, second);
> +    if (first) unmap_coherent_domain_page(d, first);
>  
>      spin_unlock(&p2m->lock);
>  
> diff --git a/xen/drivers/passthrough/iommu.c b/xen/drivers/passthrough/iommu.c
> index f93dc79..f24fb46 100644
> --- a/xen/drivers/passthrough/iommu.c
> +++ b/xen/drivers/passthrough/iommu.c
> @@ -344,6 +344,17 @@ void iommu_crash_shutdown(void)
>      iommu_enabled = iommu_intremap = 0;
>  }
>  
> +bool_t iommu_has_feature(struct domain *d, uint32_t feature)
> +{
> +    const struct iommu_ops *ops = domain_hvm_iommu(d)->platform_ops;
> +    uint32_t features = 0;
> +
> +    if ( iommu_enabled && ops && ops->features )
> +        features = ops->features(d);
> +
> +    return !!(features & feature);
> +}
> +
>  static void iommu_dump_p2m_table(unsigned char key)
>  {
>      struct domain *d;
> diff --git a/xen/include/xen/iommu.h b/xen/include/xen/iommu.h
> index e119379..9ad909f 100644
> --- a/xen/include/xen/iommu.h
> +++ b/xen/include/xen/iommu.h
> @@ -67,6 +67,10 @@ int iommu_map_page(struct domain *d, unsigned long gfn, unsigned long mfn,
>                     unsigned int flags);
>  int iommu_unmap_page(struct domain *d, unsigned long gfn);
>  
> +#define IOMMU_FEAT_COHERENT_WALK (1U<<0)
> +bool_t iommu_has_feature(struct domain *d, uint32_t feature);
> +
> +
>  #ifdef HAS_PCI
>  void pt_pci_init(void);
>  
> @@ -139,6 +143,7 @@ struct iommu_ops {
>      void (*iotlb_flush)(struct domain *d, unsigned long gfn, unsigned int page_count);
>      void (*iotlb_flush_all)(struct domain *d);
>      void (*dump_p2m_table)(struct domain *d);
> +    uint32_t (*features)(struct domain *d);
>  };
>  
>  void iommu_suspend(void);
>
Jan Beulich April 29, 2014, 7:40 a.m. UTC | #4
>>> On 22.04.14 at 15:14, <julien.grall@linaro.org> wrote:
> +bool_t iommu_has_feature(struct domain *d, uint32_t feature)
> +{
> +    const struct iommu_ops *ops = domain_hvm_iommu(d)->platform_ops;
> +    uint32_t features = 0;
> +
> +    if ( iommu_enabled && ops && ops->features )
> +        features = ops->features(d);
> +
> +    return !!(features & feature);
> +}

That's slightly strange a feature check: Passing in a bit mask allows
the caller to set more than one bit, with obvious ambiguity in what
this would mean. I'd suggest making this a bit position (with individual
bits defined via enumeration), at once hiding from the generic caller
whether eventually there might be more than 32 features defined.

Jan
Julien Grall May 2, 2014, 3:15 p.m. UTC | #5
Hi Jan,

On 04/29/2014 08:40 AM, Jan Beulich wrote:
>>>> On 22.04.14 at 15:14, <julien.grall@linaro.org> wrote:
>> +bool_t iommu_has_feature(struct domain *d, uint32_t feature)
>> +{
>> +    const struct iommu_ops *ops = domain_hvm_iommu(d)->platform_ops;
>> +    uint32_t features = 0;
>> +
>> +    if ( iommu_enabled && ops && ops->features )
>> +        features = ops->features(d);
>> +
>> +    return !!(features & feature);
>> +}
> 
> That's slightly strange a feature check: Passing in a bit mask allows
> the caller to set more than one bit, with obvious ambiguity in what
> this would mean. I'd suggest making this a bit position (with individual
> bits defined via enumeration), at once hiding from the generic caller
> whether eventually there might be more than 32 features defined.

I will create a separate patch to add iommu_has_feature. It will be more
clearer.

Regards,
diff mbox

Patch

diff --git a/xen/arch/arm/p2m.c b/xen/arch/arm/p2m.c
index 21219de..996d2bd 100644
--- a/xen/arch/arm/p2m.c
+++ b/xen/arch/arm/p2m.c
@@ -274,6 +274,18 @@  enum p2m_operation {
     CACHEFLUSH,
 };
 
+static void unmap_coherent_domain_page(struct domain *d, const void *va)
+{
+    /* Some IOMMU doesn't support coherent PT walk. When the p2m is
+     * shared with the CPU, Xen has to make sure that the PT changes have
+     * reached the memory
+     */
+    if ( need_iommu(d) && !iommu_has_feature(d, IOMMU_FEAT_COHERENT_WALK) )
+        clean_xen_dcache_va_range(va, PAGE_SIZE);
+
+    unmap_domain_page(va);
+}
+
 static int apply_p2m_changes(struct domain *d,
                      enum p2m_operation op,
                      paddr_t start_gpaddr,
@@ -301,7 +313,7 @@  static int apply_p2m_changes(struct domain *d,
     {
         if ( cur_first_page != p2m_first_level_index(addr) )
         {
-            if ( first ) unmap_domain_page(first);
+            if ( first ) unmap_coherent_domain_page(d, first);
             first = p2m_map_first(p2m, addr);
             if ( !first )
             {
@@ -331,7 +343,7 @@  static int apply_p2m_changes(struct domain *d,
 
         if ( cur_first_offset != first_table_offset(addr) )
         {
-            if (second) unmap_domain_page(second);
+            if (second) unmap_coherent_domain_page(d, second);
             second = map_domain_page(first[first_table_offset(addr)].p2m.base);
             cur_first_offset = first_table_offset(addr);
         }
@@ -357,7 +369,7 @@  static int apply_p2m_changes(struct domain *d,
         if ( cur_second_offset != second_table_offset(addr) )
         {
             /* map third level */
-            if (third) unmap_domain_page(third);
+            if (third) unmap_coherent_domain_page(d, third);
             third = map_domain_page(second[second_table_offset(addr)].p2m.base);
             cur_second_offset = second_table_offset(addr);
         }
@@ -480,9 +492,9 @@  static int apply_p2m_changes(struct domain *d,
     rc = 0;
 
 out:
-    if (third) unmap_domain_page(third);
-    if (second) unmap_domain_page(second);
-    if (first) unmap_domain_page(first);
+    if (third) unmap_coherent_domain_page(d, third);
+    if (second) unmap_coherent_domain_page(d, second);
+    if (first) unmap_coherent_domain_page(d, first);
 
     spin_unlock(&p2m->lock);
 
diff --git a/xen/drivers/passthrough/iommu.c b/xen/drivers/passthrough/iommu.c
index f93dc79..f24fb46 100644
--- a/xen/drivers/passthrough/iommu.c
+++ b/xen/drivers/passthrough/iommu.c
@@ -344,6 +344,17 @@  void iommu_crash_shutdown(void)
     iommu_enabled = iommu_intremap = 0;
 }
 
+bool_t iommu_has_feature(struct domain *d, uint32_t feature)
+{
+    const struct iommu_ops *ops = domain_hvm_iommu(d)->platform_ops;
+    uint32_t features = 0;
+
+    if ( iommu_enabled && ops && ops->features )
+        features = ops->features(d);
+
+    return !!(features & feature);
+}
+
 static void iommu_dump_p2m_table(unsigned char key)
 {
     struct domain *d;
diff --git a/xen/include/xen/iommu.h b/xen/include/xen/iommu.h
index e119379..9ad909f 100644
--- a/xen/include/xen/iommu.h
+++ b/xen/include/xen/iommu.h
@@ -67,6 +67,10 @@  int iommu_map_page(struct domain *d, unsigned long gfn, unsigned long mfn,
                    unsigned int flags);
 int iommu_unmap_page(struct domain *d, unsigned long gfn);
 
+#define IOMMU_FEAT_COHERENT_WALK (1U<<0)
+bool_t iommu_has_feature(struct domain *d, uint32_t feature);
+
+
 #ifdef HAS_PCI
 void pt_pci_init(void);
 
@@ -139,6 +143,7 @@  struct iommu_ops {
     void (*iotlb_flush)(struct domain *d, unsigned long gfn, unsigned int page_count);
     void (*iotlb_flush_all)(struct domain *d);
     void (*dump_p2m_table)(struct domain *d);
+    uint32_t (*features)(struct domain *d);
 };
 
 void iommu_suspend(void);