> From: Lu Baolu [mailto:baolu...@linux.intel.com]
> Sent: Thursday, August 30, 2018 9:35 AM
> 
> In scalable mode, pasid structure is a two level table with
> a pasid directory table and a pasid table. Any pasid entry
> can be identified by a pasid value in below way.
> 
>    1
>    9                       6 5      0
>     .-----------------------.-------.
>     |              PASID    |       |
>     '-----------------------'-------'    .-------------.
>              |                    |      |             |
>              |                    |      |             |
>              |                    |      |             |
>              |     .-----------.  |      .-------------.
>              |     |           |  |----->| PASID Entry |
>              |     |           |  |      '-------------'
>              |     |           |  |Plus  |             |
>              |     .-----------.  |      |             |
>              |---->| DIR Entry |-------->|             |
>              |     '-----------'         '-------------'
> .---------.  |Plus |           |
> | Context |  |     |           |
> |  Entry  |------->|           |
> '---------'        '-----------'
> 
> This changes the pasid table APIs to support scalable mode
> PASID directory and PASID table. It also adds a helper to
> get the PASID table entry according to the pasid value.
> 
> Cc: Ashok Raj <ashok....@intel.com>
> Cc: Jacob Pan <jacob.jun....@linux.intel.com>
> Cc: Kevin Tian <kevin.t...@intel.com>
> Cc: Liu Yi L <yi.l....@intel.com>
> Signed-off-by: Sanjay Kumar <sanjay.k.ku...@intel.com>
> Signed-off-by: Lu Baolu <baolu...@linux.intel.com>
> Reviewed-by: Ashok Raj <ashok....@intel.com>
> ---
>  drivers/iommu/intel-iommu.c |  2 +-
>  drivers/iommu/intel-pasid.c | 72 ++++++++++++++++++++++++++++++++----
> -
>  drivers/iommu/intel-pasid.h | 10 +++++-
>  drivers/iommu/intel-svm.c   |  6 +---
>  4 files changed, 74 insertions(+), 16 deletions(-)
> 
> diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
> index 5845edf4dcf9..b0da4f765274 100644
> --- a/drivers/iommu/intel-iommu.c
> +++ b/drivers/iommu/intel-iommu.c
> @@ -2507,7 +2507,7 @@ static struct dmar_domain
> *dmar_insert_one_dev_info(struct intel_iommu *iommu,
>       if (dev)
>               dev->archdata.iommu = info;
> 
> -     if (dev && dev_is_pci(dev) && info->pasid_supported) {
> +     if (dev && dev_is_pci(dev) && sm_supported(iommu)) {

worthy of a comment here that PASID table now is mandatory in
scalable mode, instead of optional for 1st level usage before.

>               ret = intel_pasid_alloc_table(dev);
>               if (ret) {
>                       __dmar_remove_one_dev_info(info);
> diff --git a/drivers/iommu/intel-pasid.c b/drivers/iommu/intel-pasid.c
> index fe95c9bd4d33..d6e90cd5b062 100644
> --- a/drivers/iommu/intel-pasid.c
> +++ b/drivers/iommu/intel-pasid.c
> @@ -127,8 +127,7 @@ int intel_pasid_alloc_table(struct device *dev)
>       int ret, order;
> 
>       info = dev->archdata.iommu;
> -     if (WARN_ON(!info || !dev_is_pci(dev) ||
> -                 !info->pasid_supported || info->pasid_table))
> +     if (WARN_ON(!info || !dev_is_pci(dev) || info->pasid_table))
>               return -EINVAL;

following same logic should you check sm_supported here?

> 
>       /* DMA alias device already has a pasid table, use it: */
> @@ -143,8 +142,9 @@ int intel_pasid_alloc_table(struct device *dev)
>               return -ENOMEM;
>       INIT_LIST_HEAD(&pasid_table->dev);
> 
> -     size = sizeof(struct pasid_entry);
> +     size = sizeof(struct pasid_dir_entry);
>       count = min_t(int, pci_max_pasids(to_pci_dev(dev)),
> intel_pasid_max_id);
> +     count >>= PASID_PDE_SHIFT;
>       order = get_order(size * count);
>       pages = alloc_pages_node(info->iommu->node,
>                                GFP_ATOMIC | __GFP_ZERO,
> @@ -154,7 +154,7 @@ int intel_pasid_alloc_table(struct device *dev)
> 
>       pasid_table->table = page_address(pages);
>       pasid_table->order = order;
> -     pasid_table->max_pasid = count;
> +     pasid_table->max_pasid = count << PASID_PDE_SHIFT;

are you sure of that count is PDE_SHIFT aligned? otherwise >>
then << would lose some bits. If sure, then better add some check.

> 
>  attach_out:
>       device_attach_pasid_table(info, pasid_table);
> @@ -162,14 +162,33 @@ int intel_pasid_alloc_table(struct device *dev)
>       return 0;
>  }
> 
> +/* Get PRESENT bit of a PASID directory entry. */
> +static inline bool
> +pasid_pde_is_present(struct pasid_dir_entry *pde)
> +{
> +     return READ_ONCE(pde->val) & PASID_PTE_PRESENT;

curious why adding READ_ONCE specifically for PASID structure,
but not used for any other existing vtd structures? Is it to address
some specific requirement on PASID structure as defined in spec?

> +}
> +
> +/* Get PASID table from a PASID directory entry. */
> +static inline struct pasid_entry *
> +get_pasid_table_from_pde(struct pasid_dir_entry *pde)
> +{
> +     if (!pasid_pde_is_present(pde))
> +             return NULL;
> +
> +     return phys_to_virt(READ_ONCE(pde->val) & PDE_PFN_MASK);
> +}
> +
>  void intel_pasid_free_table(struct device *dev)
>  {
>       struct device_domain_info *info;
>       struct pasid_table *pasid_table;
> +     struct pasid_dir_entry *dir;
> +     struct pasid_entry *table;
> +     int i, max_pde;
> 
>       info = dev->archdata.iommu;
> -     if (!info || !dev_is_pci(dev) ||
> -         !info->pasid_supported || !info->pasid_table)
> +     if (!info || !dev_is_pci(dev) || !info->pasid_table)
>               return;
> 
>       pasid_table = info->pasid_table;
> @@ -178,6 +197,14 @@ void intel_pasid_free_table(struct device *dev)
>       if (!list_empty(&pasid_table->dev))
>               return;
> 
> +     /* Free scalable mode PASID directory tables: */
> +     dir = pasid_table->table;
> +     max_pde = pasid_table->max_pasid >> PASID_PDE_SHIFT;
> +     for (i = 0; i < max_pde; i++) {
> +             table = get_pasid_table_from_pde(&dir[i]);
> +             free_pgtable_page(table);
> +     }
> +
>       free_pages((unsigned long)pasid_table->table, pasid_table->order);
>       kfree(pasid_table);
>  }
> @@ -206,17 +233,37 @@ int intel_pasid_get_dev_max_id(struct device
> *dev)
> 
>  struct pasid_entry *intel_pasid_get_entry(struct device *dev, int pasid)
>  {
> +     struct device_domain_info *info;
>       struct pasid_table *pasid_table;
> +     struct pasid_dir_entry *dir;
>       struct pasid_entry *entries;
> +     int dir_index, index;
> 
>       pasid_table = intel_pasid_get_table(dev);
>       if (WARN_ON(!pasid_table || pasid < 0 ||
>                   pasid >= intel_pasid_get_dev_max_id(dev)))
>               return NULL;
> 
> -     entries = pasid_table->table;
> +     dir = pasid_table->table;
> +     info = dev->archdata.iommu;
> +     dir_index = pasid >> PASID_PDE_SHIFT;
> +     index = pasid & PASID_PTE_MASK;
> +
> +     spin_lock(&pasid_lock);
> +     entries = get_pasid_table_from_pde(&dir[dir_index]);
> +     if (!entries) {
> +             entries = alloc_pgtable_page(info->iommu->node);
> +             if (!entries) {
> +                     spin_unlock(&pasid_lock);
> +                     return NULL;
> +             }
> +
> +             WRITE_ONCE(dir[dir_index].val,
> +                        (u64)virt_to_phys(entries) | PASID_PTE_PRESENT);
> +     }
> +     spin_unlock(&pasid_lock);
> 
> -     return &entries[pasid];
> +     return &entries[index];
>  }
> 
>  /*
> @@ -224,7 +271,14 @@ struct pasid_entry *intel_pasid_get_entry(struct
> device *dev, int pasid)
>   */
>  static inline void pasid_clear_entry(struct pasid_entry *pe)
>  {
> -     WRITE_ONCE(pe->val, 0);
> +     WRITE_ONCE(pe->val[0], 0);
> +     WRITE_ONCE(pe->val[1], 0);
> +     WRITE_ONCE(pe->val[2], 0);
> +     WRITE_ONCE(pe->val[3], 0);
> +     WRITE_ONCE(pe->val[4], 0);
> +     WRITE_ONCE(pe->val[5], 0);
> +     WRITE_ONCE(pe->val[6], 0);
> +     WRITE_ONCE(pe->val[7], 0);

memset?

>  }
> 
>  void intel_pasid_clear_entry(struct device *dev, int pasid)
> diff --git a/drivers/iommu/intel-pasid.h b/drivers/iommu/intel-pasid.h
> index 1c05ed6fc5a5..12f480c2bb8b 100644
> --- a/drivers/iommu/intel-pasid.h
> +++ b/drivers/iommu/intel-pasid.h
> @@ -12,11 +12,19 @@
> 
>  #define PASID_MIN                    0x1
>  #define PASID_MAX                    0x100000
> +#define PASID_PTE_MASK                       0x3F
> +#define PASID_PTE_PRESENT            1
> +#define PDE_PFN_MASK                 PAGE_MASK
> +#define PASID_PDE_SHIFT                      6
> 
> -struct pasid_entry {
> +struct pasid_dir_entry {
>       u64 val;
>  };
> 
> +struct pasid_entry {
> +     u64 val[8];
> +};
> +
>  /* The representative of a PASID table */
>  struct pasid_table {
>       void                    *table;         /* pasid table pointer */
> diff --git a/drivers/iommu/intel-svm.c b/drivers/iommu/intel-svm.c
> index 4a03e5090952..6c0bd9ee9602 100644
> --- a/drivers/iommu/intel-svm.c
> +++ b/drivers/iommu/intel-svm.c
> @@ -65,8 +65,6 @@ int intel_svm_init(struct intel_iommu *iommu)
> 
>       order = get_order(sizeof(struct pasid_entry) * iommu->pasid_max);
>       if (ecap_dis(iommu->ecap)) {
> -             /* Just making it explicit... */
> -             BUILD_BUG_ON(sizeof(struct pasid_entry) != sizeof(struct
> pasid_state_entry));
>               pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, order);
>               if (pages)
>                       iommu->pasid_state_table = page_address(pages);
> @@ -406,9 +404,7 @@ int intel_svm_bind_mm(struct device *dev, int
> *pasid, int flags, struct svm_dev_
>                       pasid_entry_val |= PASID_ENTRY_FLPM_5LP;
> 
>               entry = intel_pasid_get_entry(dev, svm->pasid);
> -             entry->val = pasid_entry_val;
> -
> -             wmb();
> +             WRITE_ONCE(entry->val[0], pasid_entry_val);
> 
>               /*
>                * Flush PASID cache when a PASID table entry becomes
> --
> 2.17.1

_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu

Reply via email to