Merge tag 'iommu-updates-v6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/joro...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 9 Nov 2023 21:37:28 +0000 (13:37 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 9 Nov 2023 21:37:28 +0000 (13:37 -0800)
Pull iommu updates from Joerg Roedel:
 "Core changes:
   - Make default-domains mandatory for all IOMMU drivers
   - Remove group refcounting
   - Add generic_single_device_group() helper and consolidate drivers
   - Cleanup map/unmap ops
   - Scaling improvements for the IOVA rcache depot
   - Convert dart & iommufd to the new domain_alloc_paging()

  ARM-SMMU:
   - Device-tree binding update:
       - Add qcom,sm7150-smmu-v2 for Adreno on SM7150 SoC
   - SMMUv2:
       - Support for Qualcomm SDM670 (MDSS) and SM7150 SoCs
   - SMMUv3:
       - Large refactoring of the context descriptor code to move the CD
         table into the master, paving the way for '->set_dev_pasid()'
         support on non-SVA domains
   - Minor cleanups to the SVA code

  Intel VT-d:
   - Enable debugfs to dump domain attached to a pasid
   - Remove an unnecessary inline function

  AMD IOMMU:
   - Initial patches for SVA support (not complete yet)

  S390 IOMMU:
   - DMA-API conversion and optimized IOTLB flushing

  And some smaller fixes and improvements"

* tag 'iommu-updates-v6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu: (102 commits)
  iommu/dart: Remove the force_bypass variable
  iommu/dart: Call apple_dart_finalize_domain() as part of alloc_paging()
  iommu/dart: Convert to domain_alloc_paging()
  iommu/dart: Move the blocked domain support to a global static
  iommu/dart: Use static global identity domains
  iommufd: Convert to alloc_domain_paging()
  iommu/vt-d: Use ops->blocked_domain
  iommu/vt-d: Update the definition of the blocking domain
  iommu: Move IOMMU_DOMAIN_BLOCKED global statics to ops->blocked_domain
  Revert "iommu/vt-d: Remove unused function"
  iommu/amd: Remove DMA_FQ type from domain allocation path
  iommu: change iommu_map_sgtable to return signed values
  iommu/virtio: Add __counted_by for struct viommu_request and use struct_size()
  iommu/vt-d: debugfs: Support dumping a specified page table
  iommu/vt-d: debugfs: Create/remove debugfs file per {device, pasid}
  iommu/vt-d: debugfs: Dump entry pointing to huge page
  iommu/vt-d: Remove unused function
  iommu/arm-smmu-v3-sva: Remove bond refcount
  iommu/arm-smmu-v3-sva: Remove unused iommu_sva handle
  iommu/arm-smmu-v3: Rename cdcfg to cd_table
  ...

12 files changed:
1  2 
Documentation/admin-guide/kernel-parameters.txt
arch/arm/configs/multi_v7_defconfig
arch/powerpc/kernel/iommu.c
arch/s390/pci/pci.c
drivers/iommu/Kconfig
drivers/iommu/amd/Kconfig
drivers/iommu/amd/amd_iommu_types.h
drivers/iommu/amd/iommu.c
drivers/iommu/intel/iommu.c
drivers/iommu/intel/iommu.h
drivers/iommu/iommufd/selftest.c
include/linux/iommu.h

Simple merge
Simple merge
Simple merge
index ee9e2a2edbf563efa22ad2902ac9d5d23077c073,3199fd54b462c9f74da83ddefce63405e225fc3b..7673bb82945b6cbf08ee1d4d44c196dc46875c5a
@@@ -95,7 -91,7 +95,7 @@@ config IOMMU_DEBUGF
  choice
        prompt "IOMMU default domain type"
        depends on IOMMU_API
-       default IOMMU_DEFAULT_DMA_LAZY if X86
 -      default IOMMU_DEFAULT_DMA_LAZY if X86 || IA64 || S390
++      default IOMMU_DEFAULT_DMA_LAZY if X86 || S390
        default IOMMU_DEFAULT_DMA_STRICT
        help
          Choose the type of IOMMU domain used to manage DMA API usage by
@@@ -150,7 -146,7 +150,7 @@@ config OF_IOMM
  
  # IOMMU-agnostic DMA-mapping layer
  config IOMMU_DMA
-       def_bool ARM64 || X86
 -      def_bool ARM64 || IA64 || X86 || S390
++      def_bool ARM64 || X86 || S390
        select DMA_OPS
        select IOMMU_API
        select IOMMU_IOVA
Simple merge
Simple merge
index b399c57413784688e69beaa54f2fed5b23d0462c,089886485895bcef70b69141560bc558dfa84eda..fcc987f5d4edc3ae87335fceed57eb26d7ba9b00
@@@ -66,9 -63,7 +64,8 @@@ LIST_HEAD(hpet_map)
  LIST_HEAD(acpihid_map);
  
  const struct iommu_ops amd_iommu_ops;
 +const struct iommu_dirty_ops amd_dirty_ops;
  
- static ATOMIC_NOTIFIER_HEAD(ppr_notifier);
  int amd_iommu_max_glx_val = -1;
  
  /*
@@@ -1607,14 -1735,9 +1737,12 @@@ static void set_dte_entry(struct amd_io
        if (ats)
                flags |= DTE_FLAG_IOTLB;
  
-       if (ppr) {
-               if (iommu_feature(iommu, FEATURE_EPHSUP))
-                       pte_root |= 1ULL << DEV_ENTRY_PPR;
-       }
+       if (ppr)
+               pte_root |= 1ULL << DEV_ENTRY_PPR;
  
 +      if (domain->dirty_tracking)
 +              pte_root |= DTE_FLAG_HAD;
 +
        if (domain->flags & PD_IOMMUV2_MASK) {
                u64 gcr3 = iommu_virt_to_phys(domain->gcr3_tbl);
                u64 glx  = domain->glx;
index d1037280abf7a2bc4fd51d5e6de5ce0932c66424,d5d191a71fe0d595e59f8de641fb0c9a1f1ffa28..3531b956556c7df268ee32a5053a755d5ce0630b
@@@ -4061,57 -4056,9 +4059,57 @@@ static struct iommu_domain *intel_iommu
        return NULL;
  }
  
 +static struct iommu_domain *
 +intel_iommu_domain_alloc_user(struct device *dev, u32 flags,
 +                            struct iommu_domain *parent,
 +                            const struct iommu_user_data *user_data)
 +{
 +      struct device_domain_info *info = dev_iommu_priv_get(dev);
 +      bool dirty_tracking = flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING;
 +      bool nested_parent = flags & IOMMU_HWPT_ALLOC_NEST_PARENT;
 +      struct intel_iommu *iommu = info->iommu;
 +      struct iommu_domain *domain;
 +
 +      /* Must be NESTING domain */
 +      if (parent) {
 +              if (!nested_supported(iommu) || flags)
 +                      return ERR_PTR(-EOPNOTSUPP);
 +              return intel_nested_domain_alloc(parent, user_data);
 +      }
 +
 +      if (flags &
 +          (~(IOMMU_HWPT_ALLOC_NEST_PARENT | IOMMU_HWPT_ALLOC_DIRTY_TRACKING)))
 +              return ERR_PTR(-EOPNOTSUPP);
 +      if (nested_parent && !nested_supported(iommu))
 +              return ERR_PTR(-EOPNOTSUPP);
 +      if (user_data || (dirty_tracking && !ssads_supported(iommu)))
 +              return ERR_PTR(-EOPNOTSUPP);
 +
 +      /*
 +       * domain_alloc_user op needs to fully initialize a domain before
 +       * return, so uses iommu_domain_alloc() here for simple.
 +       */
 +      domain = iommu_domain_alloc(dev->bus);
 +      if (!domain)
 +              return ERR_PTR(-ENOMEM);
 +
 +      if (nested_parent)
 +              to_dmar_domain(domain)->nested_parent = true;
 +
 +      if (dirty_tracking) {
 +              if (to_dmar_domain(domain)->use_first_level) {
 +                      iommu_domain_free(domain);
 +                      return ERR_PTR(-EOPNOTSUPP);
 +              }
 +              domain->dirty_ops = &intel_dirty_ops;
 +      }
 +
 +      return domain;
 +}
 +
  static void intel_iommu_domain_free(struct iommu_domain *domain)
  {
-       if (domain != &si_domain->domain && domain != &blocking_domain)
+       if (domain != &si_domain->domain)
                domain_exit(to_dmar_domain(domain));
  }
  
@@@ -4847,84 -4793,8 +4853,85 @@@ static void *intel_iommu_hw_info(struc
        return vtd;
  }
  
 +static int intel_iommu_set_dirty_tracking(struct iommu_domain *domain,
 +                                        bool enable)
 +{
 +      struct dmar_domain *dmar_domain = to_dmar_domain(domain);
 +      struct device_domain_info *info;
 +      int ret;
 +
 +      spin_lock(&dmar_domain->lock);
 +      if (dmar_domain->dirty_tracking == enable)
 +              goto out_unlock;
 +
 +      list_for_each_entry(info, &dmar_domain->devices, link) {
 +              ret = intel_pasid_setup_dirty_tracking(info->iommu,
 +                                                     info->domain, info->dev,
 +                                                     IOMMU_NO_PASID, enable);
 +              if (ret)
 +                      goto err_unwind;
 +      }
 +
 +      dmar_domain->dirty_tracking = enable;
 +out_unlock:
 +      spin_unlock(&dmar_domain->lock);
 +
 +      return 0;
 +
 +err_unwind:
 +      list_for_each_entry(info, &dmar_domain->devices, link)
 +              intel_pasid_setup_dirty_tracking(info->iommu, dmar_domain,
 +                                               info->dev, IOMMU_NO_PASID,
 +                                               dmar_domain->dirty_tracking);
 +      spin_unlock(&dmar_domain->lock);
 +      return ret;
 +}
 +
 +static int intel_iommu_read_and_clear_dirty(struct iommu_domain *domain,
 +                                          unsigned long iova, size_t size,
 +                                          unsigned long flags,
 +                                          struct iommu_dirty_bitmap *dirty)
 +{
 +      struct dmar_domain *dmar_domain = to_dmar_domain(domain);
 +      unsigned long end = iova + size - 1;
 +      unsigned long pgsize;
 +
 +      /*
 +       * IOMMUFD core calls into a dirty tracking disabled domain without an
 +       * IOVA bitmap set in order to clean dirty bits in all PTEs that might
 +       * have occurred when we stopped dirty tracking. This ensures that we
 +       * never inherit dirtied bits from a previous cycle.
 +       */
 +      if (!dmar_domain->dirty_tracking && dirty->bitmap)
 +              return -EINVAL;
 +
 +      do {
 +              struct dma_pte *pte;
 +              int lvl = 0;
 +
 +              pte = pfn_to_dma_pte(dmar_domain, iova >> VTD_PAGE_SHIFT, &lvl,
 +                                   GFP_ATOMIC);
 +              pgsize = level_size(lvl) << VTD_PAGE_SHIFT;
 +              if (!pte || !dma_pte_present(pte)) {
 +                      iova += pgsize;
 +                      continue;
 +              }
 +
 +              if (dma_sl_pte_test_and_clear_dirty(pte, flags))
 +                      iommu_dirty_bitmap_record(dirty, iova, pgsize);
 +              iova += pgsize;
 +      } while (iova < end);
 +
 +      return 0;
 +}
 +
 +const struct iommu_dirty_ops intel_dirty_ops = {
 +      .set_dirty_tracking = intel_iommu_set_dirty_tracking,
 +      .read_and_clear_dirty = intel_iommu_read_and_clear_dirty,
 +};
 +
  const struct iommu_ops intel_iommu_ops = {
+       .blocked_domain         = &blocking_domain,
        .capable                = intel_iommu_capable,
        .hw_info                = intel_iommu_hw_info,
        .domain_alloc           = intel_iommu_domain_alloc,
Simple merge
index d43a87737c1e88bd8b2eff67822f5cc8b2b6be6e,ee6079847091027eed02fde1f64503a03592a6af..5d93434003d8ad666af55e212372e37b81c06895
@@@ -240,81 -155,6 +235,72 @@@ static struct iommu_domain *mock_domain
        return &mock->domain;
  }
  
- static struct iommu_domain *mock_domain_alloc(unsigned int iommu_domain_type)
- {
-       if (iommu_domain_type == IOMMU_DOMAIN_BLOCKED)
-               return &mock_blocking_domain;
-       if (iommu_domain_type == IOMMU_DOMAIN_UNMANAGED)
-               return mock_domain_alloc_paging(NULL);
-       return NULL;
- }
 +static struct iommu_domain *
 +__mock_domain_alloc_nested(struct mock_iommu_domain *mock_parent,
 +                         const struct iommu_hwpt_selftest *user_cfg)
 +{
 +      struct mock_iommu_domain_nested *mock_nested;
 +      int i;
 +
 +      mock_nested = kzalloc(sizeof(*mock_nested), GFP_KERNEL);
 +      if (!mock_nested)
 +              return ERR_PTR(-ENOMEM);
 +      mock_nested->parent = mock_parent;
 +      mock_nested->domain.ops = &domain_nested_ops;
 +      mock_nested->domain.type = IOMMU_DOMAIN_NESTED;
 +      for (i = 0; i < MOCK_NESTED_DOMAIN_IOTLB_NUM; i++)
 +              mock_nested->iotlb[i] = user_cfg->iotlb;
 +      return &mock_nested->domain;
 +}
 +
 +static struct iommu_domain *
 +mock_domain_alloc_user(struct device *dev, u32 flags,
 +                     struct iommu_domain *parent,
 +                     const struct iommu_user_data *user_data)
 +{
 +      struct mock_iommu_domain *mock_parent;
 +      struct iommu_hwpt_selftest user_cfg;
 +      int rc;
 +
 +      /* must be mock_domain */
 +      if (!parent) {
 +              struct mock_dev *mdev = container_of(dev, struct mock_dev, dev);
 +              bool has_dirty_flag = flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING;
 +              bool no_dirty_ops = mdev->flags & MOCK_FLAGS_DEVICE_NO_DIRTY;
 +              struct iommu_domain *domain;
 +
 +              if (flags & (~(IOMMU_HWPT_ALLOC_NEST_PARENT |
 +                             IOMMU_HWPT_ALLOC_DIRTY_TRACKING)))
 +                      return ERR_PTR(-EOPNOTSUPP);
 +              if (user_data || (has_dirty_flag && no_dirty_ops))
 +                      return ERR_PTR(-EOPNOTSUPP);
 +              domain = mock_domain_alloc_paging(NULL);
 +              if (!domain)
 +                      return ERR_PTR(-ENOMEM);
 +              if (has_dirty_flag)
 +                      container_of(domain, struct mock_iommu_domain, domain)
 +                              ->domain.dirty_ops = &dirty_ops;
 +              return domain;
 +      }
 +
 +      /* must be mock_domain_nested */
 +      if (user_data->type != IOMMU_HWPT_DATA_SELFTEST || flags)
 +              return ERR_PTR(-EOPNOTSUPP);
 +      if (!parent || parent->ops != mock_ops.default_domain_ops)
 +              return ERR_PTR(-EINVAL);
 +
 +      mock_parent = container_of(parent, struct mock_iommu_domain, domain);
 +      if (!mock_parent)
 +              return ERR_PTR(-EINVAL);
 +
 +      rc = iommu_copy_struct_from_user(&user_cfg, user_data,
 +                                       IOMMU_HWPT_DATA_SELFTEST, iotlb);
 +      if (rc)
 +              return ERR_PTR(rc);
 +
 +      return __mock_domain_alloc_nested(mock_parent, &user_cfg);
 +}
 +
  static void mock_domain_free(struct iommu_domain *domain)
  {
        struct mock_iommu_domain *mock =
@@@ -432,28 -272,9 +418,20 @@@ static phys_addr_t mock_domain_iova_to_
  
  static bool mock_domain_capable(struct device *dev, enum iommu_cap cap)
  {
 -      return cap == IOMMU_CAP_CACHE_COHERENCY;
 +      struct mock_dev *mdev = container_of(dev, struct mock_dev, dev);
 +
 +      switch (cap) {
 +      case IOMMU_CAP_CACHE_COHERENCY:
 +              return true;
 +      case IOMMU_CAP_DIRTY_TRACKING:
 +              return !(mdev->flags & MOCK_FLAGS_DEVICE_NO_DIRTY);
 +      default:
 +              break;
 +      }
 +
 +      return false;
  }
  
- static void mock_domain_set_plaform_dma_ops(struct device *dev)
- {
-       /*
-        * mock doesn't setup default domains because we can't hook into the
-        * normal probe path
-        */
- }
  static struct iommu_device mock_iommu_device = {
  };
  
@@@ -466,10 -293,8 +450,9 @@@ static const struct iommu_ops mock_ops 
        .owner = THIS_MODULE,
        .pgsize_bitmap = MOCK_IO_PAGE_SIZE,
        .hw_info = mock_domain_hw_info,
-       .domain_alloc = mock_domain_alloc,
+       .domain_alloc_paging = mock_domain_alloc_paging,
 +      .domain_alloc_user = mock_domain_alloc_user,
        .capable = mock_domain_capable,
-       .set_platform_dma_ops = mock_domain_set_plaform_dma_ops,
        .device_group = generic_device_group,
        .probe_device = mock_probe_device,
        .default_domain_ops =
index 8fb1b41b4d1580a4c5c3f45c4fb7fb557dca464d,ddc25d2391063b6f154810a3666a5692afa6e178..ec289c1016f5f24884bfc27a812898efe14cac60
@@@ -66,10 -64,8 +66,11 @@@ struct iommu_domain_geometry 
  #define __IOMMU_DOMAIN_DMA_FQ (1U << 3)  /* DMA-API uses flush queue    */
  
  #define __IOMMU_DOMAIN_SVA    (1U << 4)  /* Shared process address space */
+ #define __IOMMU_DOMAIN_PLATFORM       (1U << 5)
  
 +#define __IOMMU_DOMAIN_NESTED (1U << 6)  /* User-managed address space nested
 +                                            on a stage-2 translation        */
 +
  #define IOMMU_DOMAIN_ALLOC_FLAGS ~__IOMMU_DOMAIN_DMA_FQ
  /*
   * This are the possible domain-types
@@@ -96,7 -94,7 +99,8 @@@
                                 __IOMMU_DOMAIN_DMA_API |       \
                                 __IOMMU_DOMAIN_DMA_FQ)
  #define IOMMU_DOMAIN_SVA      (__IOMMU_DOMAIN_SVA)
+ #define IOMMU_DOMAIN_PLATFORM (__IOMMU_DOMAIN_PLATFORM)
 +#define IOMMU_DOMAIN_NESTED   (__IOMMU_DOMAIN_NESTED)
  
  struct iommu_domain {
        unsigned type;
@@@ -327,19 -238,9 +331,21 @@@ static inline int __iommu_copy_struct_f
   *           op is allocated in the iommu driver and freed by the caller after
   *           use. The information type is one of enum iommu_hw_info_type defined
   *           in include/uapi/linux/iommufd.h.
 - * @domain_alloc: allocate iommu domain
 + * @domain_alloc: allocate and return an iommu domain if success. Otherwise
 + *                NULL is returned. The domain is not fully initialized until
 + *                the caller iommu_domain_alloc() returns.
 + * @domain_alloc_user: Allocate an iommu domain corresponding to the input
 + *                     parameters as defined in include/uapi/linux/iommufd.h.
 + *                     Unlike @domain_alloc, it is called only by IOMMUFD and
 + *                     must fully initialize the new domain before return.
 + *                     Upon success, if the @user_data is valid and the @parent
 + *                     points to a kernel-managed domain, the new domain must be
 + *                     IOMMU_DOMAIN_NESTED type; otherwise, the @parent must be
 + *                     NULL while the @user_data can be optionally provided, the
 + *                     new domain must support __IOMMU_DOMAIN_PAGING.
 + *                     Upon failure, ERR_PTR must be returned.
+  * @domain_alloc_paging: Allocate an iommu_domain that can be used for
+  *                       UNMANAGED, DMA, and DMA_FQ domain types.
   * @probe_device: Add device to iommu driver handling
   * @release_device: Remove device from iommu driver handling
   * @probe_finalize: Do final setup work after the device is added to an IOMMU
@@@ -372,9 -277,7 +382,10 @@@ struct iommu_ops 
  
        /* Domain allocation and freeing by the iommu driver */
        struct iommu_domain *(*domain_alloc)(unsigned iommu_domain_type);
 +      struct iommu_domain *(*domain_alloc_user)(
 +              struct device *dev, u32 flags, struct iommu_domain *parent,
 +              const struct iommu_user_data *user_data);
+       struct iommu_domain *(*domain_alloc_paging)(struct device *dev);
  
        struct iommu_device *(*probe_device)(struct device *dev);
        void (*release_device)(struct device *dev);