ia64/xen-unstable

changeset 19810:aa472909b39c

vtd: IO NUMA support

This patch adds VT-d RHSA processing for IO NUMA support. The basic
idea is to parse ACPI RHSA structure to obtain VT-d HW to proximity
domain mapping. This mapping is then used when allocating pages for
Vt-d HW data structures.

Signed-off-by: Allen Kay <allen.m.kay@intel.com>
author Keir Fraser <keir.fraser@citrix.com>
date Tue Jun 23 11:14:24 2009 +0100 (2009-06-23)
parents 71c67be96ef6
children bfbd7a85d2f8
files xen/drivers/passthrough/vtd/dmar.c xen/drivers/passthrough/vtd/dmar.h xen/drivers/passthrough/vtd/extern.h xen/drivers/passthrough/vtd/ia64/vtd.c xen/drivers/passthrough/vtd/intremap.c xen/drivers/passthrough/vtd/iommu.c xen/drivers/passthrough/vtd/qinval.c xen/drivers/passthrough/vtd/vtd.h xen/drivers/passthrough/vtd/x86/vtd.c xen/include/xen/acpi.h
line diff
     1.1 --- a/xen/drivers/passthrough/vtd/dmar.c	Tue Jun 23 11:13:29 2009 +0100
     1.2 +++ b/xen/drivers/passthrough/vtd/dmar.c	Tue Jun 23 11:14:24 2009 +0100
     1.3 @@ -42,6 +42,7 @@
     1.4  LIST_HEAD(acpi_drhd_units);
     1.5  LIST_HEAD(acpi_rmrr_units);
     1.6  LIST_HEAD(acpi_atsr_units);
     1.7 +LIST_HEAD(acpi_rhsa_units);
     1.8  
     1.9  u8 dmar_host_address_width;
    1.10  
    1.11 @@ -127,6 +128,20 @@ struct acpi_drhd_unit * ioapic_to_drhd(u
    1.12      return NULL;
    1.13  }
    1.14  
    1.15 +struct acpi_drhd_unit * iommu_to_drhd(struct iommu *iommu)
    1.16 +{
    1.17 +    struct acpi_drhd_unit *drhd;
    1.18 +
    1.19 +    if ( iommu == NULL )
    1.20 +        return NULL;
    1.21 +
    1.22 +    list_for_each_entry( drhd, &acpi_drhd_units, list )
    1.23 +        if ( drhd->iommu == iommu )
    1.24 +            return drhd;
    1.25 +
    1.26 +    return NULL;
    1.27 +}
    1.28 +
    1.29  struct iommu * ioapic_to_iommu(unsigned int apic_id)
    1.30  {
    1.31      struct acpi_drhd_unit *drhd;
    1.32 @@ -157,6 +172,9 @@ struct acpi_drhd_unit * acpi_find_matche
    1.33      struct acpi_drhd_unit *include_all = NULL;
    1.34      int i;
    1.35  
    1.36 +    if ( pdev == NULL )
    1.37 +        return NULL;
    1.38 +
    1.39      if (pdev->info.is_extfn) {
    1.40          bus = pdev->bus;
    1.41          devfn = 0;
    1.42 @@ -199,6 +217,21 @@ struct acpi_atsr_unit * acpi_find_matche
    1.43      return all_ports;
    1.44  }
    1.45  
    1.46 +struct acpi_rhsa_unit * drhd_to_rhsa(struct acpi_drhd_unit *drhd)
    1.47 +{
    1.48 +    struct acpi_rhsa_unit *rhsa;
    1.49 +
    1.50 +    if ( drhd == NULL )
    1.51 +        return NULL;
    1.52 +
    1.53 +    list_for_each_entry ( rhsa, &acpi_rhsa_units, list )
    1.54 +    {
    1.55 +        if ( rhsa->address == drhd->address )
    1.56 +            return rhsa;
    1.57 +    }
    1.58 +    return NULL;
    1.59 +}
    1.60 +
    1.61  /*
    1.62   * Count number of devices in device scope.  Do not include PCI sub
    1.63   * hierarchies.
    1.64 @@ -453,6 +486,25 @@ acpi_parse_one_atsr(struct acpi_dmar_ent
    1.65      return ret;
    1.66  }
    1.67  
    1.68 +static int __init
    1.69 +acpi_parse_one_rhsa(struct acpi_dmar_entry_header *header)
    1.70 +{
    1.71 +    struct acpi_table_rhsa *rhsa = (struct acpi_table_rhsa *)header;
    1.72 +    struct acpi_rhsa_unit *rhsau;
    1.73 +    int ret = 0;
    1.74 +
    1.75 +    rhsau = xmalloc(struct acpi_rhsa_unit);
    1.76 +    if ( !rhsau )
    1.77 +        return -ENOMEM;
    1.78 +    memset(rhsau, 0, sizeof(struct acpi_rhsa_unit));
    1.79 +
    1.80 +    rhsau->address = rhsa->address;
    1.81 +    rhsau->domain = rhsa->domain;
    1.82 +    list_add_tail(&rhsau->list, &acpi_rhsa_units);
    1.83 +
    1.84 +    return ret;
    1.85 +}
    1.86 +
    1.87  static int __init acpi_parse_dmar(struct acpi_table_header *table)
    1.88  {
    1.89      struct acpi_table_dmar *dmar;
    1.90 @@ -492,6 +544,10 @@ static int __init acpi_parse_dmar(struct
    1.91              dprintk(XENLOG_INFO VTDPREFIX, "found ACPI_DMAR_ATSR\n");
    1.92              ret = acpi_parse_one_atsr(entry_header);
    1.93              break;
    1.94 +        case ACPI_DMAR_RHSA:
    1.95 +            dprintk(XENLOG_INFO VTDPREFIX, "found ACPI_DMAR_RHSA\n");
    1.96 +            ret = acpi_parse_one_rhsa(entry_header);
    1.97 +            break;
    1.98          default:
    1.99              dprintk(XENLOG_WARNING VTDPREFIX, "Unknown DMAR structure type\n");
   1.100              ret = -EINVAL;
     2.1 --- a/xen/drivers/passthrough/vtd/dmar.h	Tue Jun 23 11:13:29 2009 +0100
     2.2 +++ b/xen/drivers/passthrough/vtd/dmar.h	Tue Jun 23 11:14:24 2009 +0100
     2.3 @@ -69,6 +69,11 @@ struct acpi_atsr_unit {
     2.4      u8     all_ports:1;
     2.5  };
     2.6  
     2.7 +struct acpi_rhsa_unit {
     2.8 +    struct list_head list;
     2.9 +    u64    address;
    2.10 +    u32    domain;
    2.11 +};
    2.12  
    2.13  #define for_each_drhd_unit(drhd) \
    2.14      list_for_each_entry(drhd, &acpi_drhd_units, list)
     3.1 --- a/xen/drivers/passthrough/vtd/extern.h	Tue Jun 23 11:13:29 2009 +0100
     3.2 +++ b/xen/drivers/passthrough/vtd/extern.h	Tue Jun 23 11:14:24 2009 +0100
     3.3 @@ -45,6 +45,8 @@ int iommu_flush_iec_global(struct iommu 
     3.4  int iommu_flush_iec_index(struct iommu *iommu, u8 im, u16 iidx);
     3.5  struct iommu * ioapic_to_iommu(unsigned int apic_id);
     3.6  struct acpi_drhd_unit * ioapic_to_drhd(unsigned int apic_id);
     3.7 +struct acpi_drhd_unit * iommu_to_drhd(struct iommu *iommu);
     3.8 +struct acpi_rhsa_unit * drhd_to_rhsa(struct acpi_drhd_unit *drhd);
     3.9  void clear_fault_bits(struct iommu *iommu);
    3.10  int ats_device(int seg, int bus, int devfn);
    3.11  int enable_ats_device(int seg, int bus, int devfn);
     4.1 --- a/xen/drivers/passthrough/vtd/ia64/vtd.c	Tue Jun 23 11:13:29 2009 +0100
     4.2 +++ b/xen/drivers/passthrough/vtd/ia64/vtd.c	Tue Jun 23 11:14:24 2009 +0100
     4.3 @@ -46,25 +46,6 @@ void unmap_vtd_domain_page(void *va)
     4.4      unmap_domain_page(va);
     4.5  }
     4.6  
     4.7 -/* Allocate page table, return its machine address */
     4.8 -u64 alloc_pgtable_maddr(struct domain *d, unsigned long npages)
     4.9 -{
    4.10 -    struct page_info *pg;
    4.11 -    u64 *vaddr;
    4.12 -
    4.13 -    pg = alloc_domheap_pages(NULL, get_order_from_pages(npages),
    4.14 -                             d ? MEMF_node(domain_to_node(d)) : 0);
    4.15 -    vaddr = map_domain_page(page_to_mfn(pg));
    4.16 -    if ( !vaddr )
    4.17 -        return 0;
    4.18 -    memset(vaddr, 0, PAGE_SIZE * npages);
    4.19 -
    4.20 -    iommu_flush_cache_page(vaddr, npages);
    4.21 -    unmap_domain_page(vaddr);
    4.22 -
    4.23 -    return page_to_maddr(pg);
    4.24 -}
    4.25 -
    4.26  void free_pgtable_maddr(u64 maddr)
    4.27  {
    4.28      if ( maddr != 0 )
     5.1 --- a/xen/drivers/passthrough/vtd/intremap.c	Tue Jun 23 11:13:29 2009 +0100
     5.2 +++ b/xen/drivers/passthrough/vtd/intremap.c	Tue Jun 23 11:14:24 2009 +0100
     5.3 @@ -614,6 +614,7 @@ void msi_msg_write_remap_rte(
     5.4  
     5.5  int enable_intremap(struct iommu *iommu)
     5.6  {
     5.7 +    struct acpi_drhd_unit *drhd;
     5.8      struct ir_ctrl *ir_ctrl;
     5.9      u32 sts, gcmd;
    5.10      unsigned long flags;
    5.11 @@ -623,7 +624,8 @@ int enable_intremap(struct iommu *iommu)
    5.12      ir_ctrl = iommu_ir_ctrl(iommu);
    5.13      if ( ir_ctrl->iremap_maddr == 0 )
    5.14      {
    5.15 -        ir_ctrl->iremap_maddr = alloc_pgtable_maddr(NULL, 1);
    5.16 +        drhd = iommu_to_drhd(iommu);
    5.17 +        ir_ctrl->iremap_maddr = alloc_pgtable_maddr(drhd, 1);
    5.18          if ( ir_ctrl->iremap_maddr == 0 )
    5.19          {
    5.20              dprintk(XENLOG_WARNING VTDPREFIX,
     6.1 --- a/xen/drivers/passthrough/vtd/iommu.c	Tue Jun 23 11:13:29 2009 +0100
     6.2 +++ b/xen/drivers/passthrough/vtd/iommu.c	Tue Jun 23 11:14:24 2009 +0100
     6.3 @@ -38,6 +38,7 @@
     6.4  
     6.5  #define domain_iommu_domid(d) ((d)->arch.hvm_domain.hvm_iommu.iommu_domid)
     6.6  
     6.7 +int nr_iommus;
     6.8  static spinlock_t domid_bitmap_lock;    /* protect domain id bitmap */
     6.9  static int domid_bitmap_size;           /* domain id bitmap size in bits */
    6.10  static unsigned long *domid_bitmap;     /* iommu domain id bitmap */
    6.11 @@ -136,10 +137,37 @@ void iommu_flush_cache_page(void *addr, 
    6.12      __iommu_flush_cache(addr, PAGE_SIZE_4K * npages);
    6.13  }
    6.14  
    6.15 -int nr_iommus;
    6.16 +/* Allocate page table, return its machine address */
    6.17 +u64 alloc_pgtable_maddr(struct acpi_drhd_unit *drhd, unsigned long npages)
    6.18 +{
    6.19 +    struct acpi_rhsa_unit *rhsa;
    6.20 +    struct page_info *pg;
    6.21 +    u64 *vaddr;
    6.22 +
    6.23 +    rhsa = drhd_to_rhsa(drhd);
    6.24 +    if ( !rhsa )
    6.25 +        dprintk(XENLOG_INFO VTDPREFIX,
    6.26 +                "IOMMU: RHSA == NULL, IO NUMA memory allocation disabled\n");
    6.27 +
    6.28 +    pg = alloc_domheap_pages(NULL, get_order_from_pages(npages),
    6.29 +                             rhsa ? rhsa->domain : 0);
    6.30 +    if ( !pg )
    6.31 +        return 0;
    6.32 +    vaddr = map_domain_page(page_to_mfn(pg));
    6.33 +    if ( !vaddr )
    6.34 +        return 0;
    6.35 +    memset(vaddr, 0, PAGE_SIZE * npages);
    6.36 +
    6.37 +    iommu_flush_cache_page(vaddr, npages);
    6.38 +    unmap_domain_page(vaddr);
    6.39 +
    6.40 +    return page_to_maddr(pg);
    6.41 +}
    6.42 +
    6.43  /* context entry handling */
    6.44  static u64 bus_to_context_maddr(struct iommu *iommu, u8 bus)
    6.45  {
    6.46 +    struct acpi_drhd_unit *drhd;
    6.47      struct root_entry *root, *root_entries;
    6.48      u64 maddr;
    6.49  
    6.50 @@ -148,7 +176,8 @@ static u64 bus_to_context_maddr(struct i
    6.51      root = &root_entries[bus];
    6.52      if ( !root_present(*root) )
    6.53      {
    6.54 -        maddr = alloc_pgtable_maddr(NULL, 1);
    6.55 +        drhd = iommu_to_drhd(iommu);
    6.56 +        maddr = alloc_pgtable_maddr(drhd, 1);
    6.57          if ( maddr == 0 )
    6.58          {
    6.59              unmap_vtd_domain_page(root_entries);
    6.60 @@ -165,6 +194,8 @@ static u64 bus_to_context_maddr(struct i
    6.61  
    6.62  static u64 addr_to_dma_page_maddr(struct domain *domain, u64 addr, int alloc)
    6.63  {
    6.64 +    struct acpi_drhd_unit *drhd;
    6.65 +    struct pci_dev *pdev;
    6.66      struct hvm_iommu *hd = domain_hvm_iommu(domain);
    6.67      int addr_width = agaw_to_width(hd->agaw);
    6.68      struct dma_pte *parent, *pte = NULL;
    6.69 @@ -176,8 +207,12 @@ static u64 addr_to_dma_page_maddr(struct
    6.70      addr &= (((u64)1) << addr_width) - 1;
    6.71      ASSERT(spin_is_locked(&hd->mapping_lock));
    6.72      if ( hd->pgd_maddr == 0 )
    6.73 -        if ( !alloc || ((hd->pgd_maddr = alloc_pgtable_maddr(domain, 1)) == 0) )
    6.74 +    {
    6.75 +        pdev = pci_get_pdev_by_domain(domain, -1, -1);
    6.76 +        drhd = acpi_find_matched_drhd_unit(pdev);
    6.77 +        if ( !alloc || ((hd->pgd_maddr = alloc_pgtable_maddr(drhd, 1)) == 0) )
    6.78              goto out;
    6.79 +    }
    6.80  
    6.81      parent = (struct dma_pte *)map_vtd_domain_page(hd->pgd_maddr);
    6.82      while ( level > 1 )
    6.83 @@ -189,9 +224,13 @@ static u64 addr_to_dma_page_maddr(struct
    6.84          {
    6.85              if ( !alloc )
    6.86                  break;
    6.87 -            maddr = alloc_pgtable_maddr(domain, 1);
    6.88 +
    6.89 +            pdev = pci_get_pdev_by_domain(domain, -1, -1);
    6.90 +            drhd = acpi_find_matched_drhd_unit(pdev);
    6.91 +            maddr = alloc_pgtable_maddr(drhd, 1);
    6.92              if ( !maddr )
    6.93                  break;
    6.94 +
    6.95              dma_set_pte_addr(*pte, maddr);
    6.96              vaddr = map_vtd_domain_page(maddr);
    6.97  
    6.98 @@ -548,13 +587,18 @@ static void iommu_free_pagetable(u64 pt_
    6.99  
   6.100  static int iommu_set_root_entry(struct iommu *iommu)
   6.101  {
   6.102 +    struct acpi_drhd_unit *drhd;
   6.103      u32 sts;
   6.104      unsigned long flags;
   6.105  
   6.106      spin_lock(&iommu->lock);
   6.107  
   6.108      if ( iommu->root_maddr == 0 )
   6.109 -        iommu->root_maddr = alloc_pgtable_maddr(NULL, 1);
   6.110 +    {
   6.111 +        drhd = iommu_to_drhd(iommu);
   6.112 +        iommu->root_maddr = alloc_pgtable_maddr(drhd, 1);
   6.113 +    }
   6.114 +
   6.115      if ( iommu->root_maddr == 0 )
   6.116      {
   6.117          spin_unlock(&iommu->lock);
     7.1 --- a/xen/drivers/passthrough/vtd/qinval.c	Tue Jun 23 11:13:29 2009 +0100
     7.2 +++ b/xen/drivers/passthrough/vtd/qinval.c	Tue Jun 23 11:14:24 2009 +0100
     7.3 @@ -416,6 +416,7 @@ static int flush_iotlb_qi(
     7.4  
     7.5  int enable_qinval(struct iommu *iommu)
     7.6  {
     7.7 +    struct acpi_drhd_unit *drhd;
     7.8      struct qi_ctrl *qi_ctrl;
     7.9      struct iommu_flush *flush;
    7.10      u32 sts;
    7.11 @@ -428,7 +429,8 @@ int enable_qinval(struct iommu *iommu)
    7.12  
    7.13      if ( qi_ctrl->qinval_maddr == 0 )
    7.14      {
    7.15 -        qi_ctrl->qinval_maddr = alloc_pgtable_maddr(NULL, NUM_QINVAL_PAGES);
    7.16 +        drhd = iommu_to_drhd(iommu);
    7.17 +        qi_ctrl->qinval_maddr = alloc_pgtable_maddr(drhd, NUM_QINVAL_PAGES);
    7.18          if ( qi_ctrl->qinval_maddr == 0 )
    7.19          {
    7.20              dprintk(XENLOG_WARNING VTDPREFIX,
     8.1 --- a/xen/drivers/passthrough/vtd/vtd.h	Tue Jun 23 11:13:29 2009 +0100
     8.2 +++ b/xen/drivers/passthrough/vtd/vtd.h	Tue Jun 23 11:14:24 2009 +0100
     8.3 @@ -101,7 +101,7 @@ unsigned int get_cache_line_size(void);
     8.4  void cacheline_flush(char *);
     8.5  void flush_all_cache(void);
     8.6  void *map_to_nocache_virt(int nr_iommus, u64 maddr);
     8.7 -u64 alloc_pgtable_maddr(struct domain *d, unsigned long npages);
     8.8 +u64 alloc_pgtable_maddr(struct acpi_drhd_unit *drhd, unsigned long npages);
     8.9  void free_pgtable_maddr(u64 maddr);
    8.10  void *map_vtd_domain_page(u64 maddr);
    8.11  void unmap_vtd_domain_page(void *va);
     9.1 --- a/xen/drivers/passthrough/vtd/x86/vtd.c	Tue Jun 23 11:13:29 2009 +0100
     9.2 +++ b/xen/drivers/passthrough/vtd/x86/vtd.c	Tue Jun 23 11:14:24 2009 +0100
     9.3 @@ -44,27 +44,6 @@ void unmap_vtd_domain_page(void *va)
     9.4      unmap_domain_page(va);
     9.5  }
     9.6  
     9.7 -/* Allocate page table, return its machine address */
     9.8 -u64 alloc_pgtable_maddr(struct domain *d, unsigned long npages)
     9.9 -{
    9.10 -    struct page_info *pg;
    9.11 -    u64 *vaddr;
    9.12 -    unsigned long mfn;
    9.13 -
    9.14 -    pg = alloc_domheap_pages(NULL, get_order_from_pages(npages),
    9.15 -                             d ? MEMF_node(domain_to_node(d)) : 0);
    9.16 -    if ( !pg )
    9.17 -        return 0;
    9.18 -    mfn = page_to_mfn(pg);
    9.19 -    vaddr = map_domain_page(mfn);
    9.20 -    memset(vaddr, 0, PAGE_SIZE * npages);
    9.21 -
    9.22 -    iommu_flush_cache_page(vaddr, npages);
    9.23 -    unmap_domain_page(vaddr);
    9.24 -
    9.25 -    return (u64)mfn << PAGE_SHIFT_4K;
    9.26 -}
    9.27 -
    9.28  void free_pgtable_maddr(u64 maddr)
    9.29  {
    9.30      if ( maddr != 0 )
    10.1 --- a/xen/include/xen/acpi.h	Tue Jun 23 11:13:29 2009 +0100
    10.2 +++ b/xen/include/xen/acpi.h	Tue Jun 23 11:14:24 2009 +0100
    10.3 @@ -197,6 +197,7 @@ enum acpi_dmar_entry_type {
    10.4  	ACPI_DMAR_DRHD = 0,
    10.5  	ACPI_DMAR_RMRR,
    10.6  	ACPI_DMAR_ATSR,
    10.7 +	ACPI_DMAR_RHSA,
    10.8  	ACPI_DMAR_ENTRY_COUNT
    10.9  };
   10.10  
   10.11 @@ -224,6 +225,12 @@ struct acpi_table_atsr {
   10.12          u16     segment;
   10.13  } __attribute__ ((packed));
   10.14  
   10.15 +struct acpi_table_rhsa {
   10.16 +        struct  acpi_dmar_entry_header header;
   10.17 +        u32     domain;
   10.18 +        u64     address; /* register base address for this drhd */
   10.19 +} __attribute__ ((packed));
   10.20 +
   10.21  enum acpi_dev_scope_type {
   10.22  	ACPI_DEV_ENDPOINT=0x01,	/* PCI Endpoing device */
   10.23  	ACPI_DEV_P2PBRIDGE,	/* PCI-PCI Bridge */