debuggers.hg

changeset 22738:66e806289464

EPT/VT-d: bug fix for EPT/VT-d table sharing

This patch makes following changes: 1) Moves EPT/VT-d sharing
initialization back to when it is actually needed to make sure
vmx_ept_vpid_cap has been initialized. 2) added page order parameter
to iommu_pte_flush() to tell VT-d what size of page to flush. 3)
added hap_2mb flag to ease performance studies between base 4KB EPT
size and when 2MB and 1GB page size support are enabled.

Signed-off-by: Allen Kay <allen.m.kay@intel.com>
author Keir Fraser <keir@xen.org>
date Mon Jan 10 08:40:32 2011 +0000 (2011-01-10)
parents 946d84529a07
children 08bb0eefe871
files xen/arch/x86/mm/hap/p2m-ept.c xen/arch/x86/mm/p2m.c xen/drivers/passthrough/vtd/iommu.c xen/include/xen/iommu.h
line diff
     1.1 --- a/xen/arch/x86/mm/hap/p2m-ept.c	Sat Jan 08 11:07:18 2011 +0000
     1.2 +++ b/xen/arch/x86/mm/hap/p2m-ept.c	Mon Jan 10 08:40:32 2011 +0000
     1.3 @@ -451,12 +451,12 @@ out:
     1.4      if ( rv && iommu_enabled && need_iommu(p2m->domain) && need_modify_vtd_table )
     1.5      {
     1.6          if ( iommu_hap_pt_share )
     1.7 -            iommu_pte_flush(d, gfn, (u64*)ept_entry, vtd_pte_present);
     1.8 +            iommu_pte_flush(d, gfn, (u64*)ept_entry, order, vtd_pte_present);
     1.9          else
    1.10          {
    1.11              if ( p2mt == p2m_ram_rw )
    1.12              {
    1.13 -                if ( order == EPT_TABLE_ORDER )
    1.14 +                if ( order > 0 )
    1.15                  {
    1.16                      for ( i = 0; i < (1 << order); i++ )
    1.17                          iommu_map_page(
    1.18 @@ -469,7 +469,7 @@ out:
    1.19              }
    1.20              else
    1.21              {
    1.22 -                if ( order == EPT_TABLE_ORDER )
    1.23 +                if ( order > 0 )
    1.24                  {
    1.25                      for ( i = 0; i < (1 << order); i++ )
    1.26                          iommu_unmap_page(p2m->domain, gfn - offset + i);
     2.1 --- a/xen/arch/x86/mm/p2m.c	Sat Jan 08 11:07:18 2011 +0000
     2.2 +++ b/xen/arch/x86/mm/p2m.c	Mon Jan 10 08:40:32 2011 +0000
     2.3 @@ -43,6 +43,9 @@
     2.4  static bool_t __read_mostly opt_hap_1gb = 1;
     2.5  boolean_param("hap_1gb", opt_hap_1gb);
     2.6  
     2.7 +static bool_t __read_mostly opt_hap_2mb = 1;
     2.8 +boolean_param("hap_2mb", opt_hap_2mb);
     2.9 +
    2.10  /* Printouts */
    2.11  #define P2M_PRINTK(_f, _a...)                                \
    2.12      debugtrace_printk("p2m: %s(): " _f, __func__, ##_a)
    2.13 @@ -1779,7 +1782,7 @@ int set_p2m_entry(struct p2m_domain *p2m
    2.14              order = ( (((gfn | mfn_x(mfn) | todo) & ((1ul << 18) - 1)) == 0) &&
    2.15                        hvm_hap_has_1gb(d) && opt_hap_1gb ) ? 18 :
    2.16                        ((((gfn | mfn_x(mfn) | todo) & ((1ul << 9) - 1)) == 0) &&
    2.17 -                      hvm_hap_has_2mb(d)) ? 9 : 0;
    2.18 +                      hvm_hap_has_2mb(d) && opt_hap_2mb) ? 9 : 0;
    2.19          else
    2.20              order = 0;
    2.21  
     3.1 --- a/xen/drivers/passthrough/vtd/iommu.c	Sat Jan 08 11:07:18 2011 +0000
     3.2 +++ b/xen/drivers/passthrough/vtd/iommu.c	Mon Jan 10 08:40:32 2011 +0000
     3.3 @@ -518,24 +518,9 @@ static int inline iommu_flush_iotlb_dsi(
     3.4      return status;
     3.5  }
     3.6  
     3.7 -static int inline get_alignment(u64 base, unsigned int size)
     3.8 -{
     3.9 -    int t = 0;
    3.10 -    u64 end;
    3.11 -
    3.12 -    end = base + size - 1;
    3.13 -    while ( base != end )
    3.14 -    {
    3.15 -        t++;
    3.16 -        base >>= 1;
    3.17 -        end >>= 1;
    3.18 -    }
    3.19 -    return t;
    3.20 -}
    3.21 -
    3.22  static int inline iommu_flush_iotlb_psi(
    3.23      struct iommu *iommu, u16 did, u64 addr, unsigned int pages,
    3.24 -    int flush_non_present_entry, int flush_dev_iotlb)
    3.25 +    int order, int flush_non_present_entry, int flush_dev_iotlb)
    3.26  {
    3.27      unsigned int align;
    3.28      struct iommu_flush *flush = iommu_get_flush(iommu);
    3.29 @@ -548,17 +533,12 @@ static int inline iommu_flush_iotlb_psi(
    3.30      if ( !cap_pgsel_inv(iommu->cap) )
    3.31          return iommu_flush_iotlb_dsi(iommu, did, flush_non_present_entry, flush_dev_iotlb);
    3.32  
    3.33 -    /*
    3.34 -     * PSI requires page size is 2 ^ x, and the base address is naturally
    3.35 -     * aligned to the size
    3.36 -     */
    3.37 -    align = get_alignment(addr >> PAGE_SHIFT_4K, pages);
    3.38      /* Fallback to domain selective flush if size is too big */
    3.39 -    if ( align > cap_max_amask_val(iommu->cap) )
    3.40 +    if ( order > cap_max_amask_val(iommu->cap) )
    3.41          return iommu_flush_iotlb_dsi(iommu, did, flush_non_present_entry, flush_dev_iotlb);
    3.42  
    3.43 -    addr >>= PAGE_SHIFT_4K + align;
    3.44 -    addr <<= PAGE_SHIFT_4K + align;
    3.45 +    addr >>= PAGE_SHIFT_4K + order;
    3.46 +    addr <<= PAGE_SHIFT_4K + order;
    3.47  
    3.48      /* apply platform specific errata workarounds */
    3.49      vtd_ops_preamble_quirk(iommu);
    3.50 @@ -634,8 +614,8 @@ static void dma_pte_clear_one(struct dom
    3.51              iommu_domid= domain_iommu_domid(domain, iommu);
    3.52              if ( iommu_domid == -1 )
    3.53                  continue;
    3.54 -            if ( iommu_flush_iotlb_psi(iommu, iommu_domid,
    3.55 -                                       addr, 1, 0, flush_dev_iotlb) )
    3.56 +            if ( iommu_flush_iotlb_psi(iommu, iommu_domid, addr,
    3.57 +                                       1, 0, 0, flush_dev_iotlb) )
    3.58                  iommu_flush_write_buffer(iommu);
    3.59          }
    3.60      }
    3.61 @@ -1710,7 +1690,7 @@ static int intel_iommu_map_page(
    3.62          if ( iommu_domid == -1 )
    3.63              continue;
    3.64          if ( iommu_flush_iotlb_psi(iommu, iommu_domid,
    3.65 -                                   (paddr_t)gfn << PAGE_SHIFT_4K, 1,
    3.66 +                                   (paddr_t)gfn << PAGE_SHIFT_4K, 1, 0,
    3.67                                     !dma_pte_present(old), flush_dev_iotlb) )
    3.68              iommu_flush_write_buffer(iommu);
    3.69      }
    3.70 @@ -1729,7 +1709,8 @@ static int intel_iommu_unmap_page(struct
    3.71      return 0;
    3.72  }
    3.73  
    3.74 -void iommu_pte_flush(struct domain *d, u64 gfn, u64 *pte, int present)
    3.75 +void iommu_pte_flush(struct domain *d, u64 gfn, u64 *pte,
    3.76 +                     int order, int present)
    3.77  {
    3.78      struct acpi_drhd_unit *drhd;
    3.79      struct iommu *iommu = NULL;
    3.80 @@ -1751,7 +1732,7 @@ void iommu_pte_flush(struct domain *d, u
    3.81              continue;
    3.82          if ( iommu_flush_iotlb_psi(iommu, iommu_domid,
    3.83                                     (paddr_t)gfn << PAGE_SHIFT_4K, 1,
    3.84 -                                   !present, flush_dev_iotlb) )
    3.85 +                                   order, !present, flush_dev_iotlb) )
    3.86              iommu_flush_write_buffer(iommu);
    3.87      }
    3.88  }
    3.89 @@ -1769,6 +1750,28 @@ static int vtd_ept_page_compatible(struc
    3.90      return 1;
    3.91  }
    3.92  
    3.93 +static bool_t vtd_ept_share(void)
    3.94 +{
    3.95 +    struct acpi_drhd_unit *drhd;
    3.96 +    struct iommu *iommu;
    3.97 +    bool_t share = TRUE;
    3.98 +
    3.99 +    /* sharept defaults to 0 for now, default to 1 when feature matures */
   3.100 +    if ( !sharept )
   3.101 +        share = FALSE;
   3.102 +
   3.103 +    /*
   3.104 +     * Determine whether EPT and VT-d page tables can be shared or not.
   3.105 +     */
   3.106 +    for_each_drhd_unit ( drhd )
   3.107 +    {
   3.108 +        iommu = drhd->iommu;
   3.109 +        if ( !vtd_ept_page_compatible(drhd->iommu) )
   3.110 +            share = FALSE;
   3.111 +    }
   3.112 +    return share;
   3.113 +}
   3.114 +
   3.115  /*
   3.116   * set VT-d page table directory to EPT table if allowed
   3.117   */
   3.118 @@ -1779,11 +1782,13 @@ void iommu_set_pgd(struct domain *d)
   3.119  
   3.120      ASSERT( is_hvm_domain(d) && d->arch.hvm_domain.hap_enabled );
   3.121  
   3.122 -    if ( !iommu_hap_pt_share )
   3.123 -        return;
   3.124 -
   3.125 +    iommu_hap_pt_share = vtd_ept_share();
   3.126      pgd_mfn = pagetable_get_mfn(p2m_get_pagetable(p2m_get_hostp2m(d)));
   3.127      hd->pgd_maddr = pagetable_get_paddr(pagetable_from_mfn(pgd_mfn));
   3.128 +
   3.129 +    dprintk(XENLOG_INFO VTDPREFIX,
   3.130 +            "VT-d page table %s with EPT table\n",
   3.131 +            iommu_hap_pt_share ? "shares" : "not sharing");
   3.132  }
   3.133  
   3.134  static int domain_rmrr_mapped(struct domain *d,
   3.135 @@ -2036,27 +2041,6 @@ static int init_vtd_hw(void)
   3.136          }
   3.137      }
   3.138      iommu_flush_all();
   3.139 -
   3.140 -    /*
   3.141 -     * Determine whether EPT and VT-d page tables can be shared or not.
   3.142 -     */
   3.143 -    iommu_hap_pt_share = TRUE;
   3.144 -    for_each_drhd_unit ( drhd )
   3.145 -    {
   3.146 -        iommu = drhd->iommu;
   3.147 -        if ( (drhd->iommu->nr_pt_levels != VTD_PAGE_TABLE_LEVEL_4) ||
   3.148 -              !vtd_ept_page_compatible(drhd->iommu) )
   3.149 -            iommu_hap_pt_share = FALSE;
   3.150 -    }
   3.151 -
   3.152 -    /* keep boot flag sharept as safe fallback. remove after feature matures */
   3.153 -    if ( !sharept )
   3.154 -        iommu_hap_pt_share = FALSE;
   3.155 -
   3.156 -    dprintk(XENLOG_INFO VTDPREFIX,
   3.157 -            "VT-d page table %sshared with EPT table\n",
   3.158 -            iommu_hap_pt_share ? "" : "not ");
   3.159 -
   3.160      return 0;
   3.161  }
   3.162  
     4.1 --- a/xen/include/xen/iommu.h	Sat Jan 08 11:07:18 2011 +0000
     4.2 +++ b/xen/include/xen/iommu.h	Mon Jan 10 08:40:32 2011 +0000
     4.3 @@ -85,7 +85,7 @@ int iommu_get_device_group(struct domain
     4.4  int iommu_map_page(struct domain *d, unsigned long gfn, unsigned long mfn,
     4.5                     unsigned int flags);
     4.6  int iommu_unmap_page(struct domain *d, unsigned long gfn);
     4.7 -void iommu_pte_flush(struct domain *d, u64 gfn, u64 *pte, int present);
     4.8 +void iommu_pte_flush(struct domain *d, u64 gfn, u64 *pte, int order, int present);
     4.9  void iommu_set_pgd(struct domain *d);
    4.10  void iommu_domain_teardown(struct domain *d);
    4.11  int hvm_do_IRQ_dpci(struct domain *d, unsigned int irq);