]> Git Repo - J-linux.git/commitdiff
Merge branch 'iommu/iommufd/paging-domain-alloc' into iommu/next
authorWill Deacon <[email protected]>
Fri, 12 Jul 2024 15:57:47 +0000 (16:57 +0100)
committerWill Deacon <[email protected]>
Fri, 12 Jul 2024 15:57:47 +0000 (16:57 +0100)
* iommu/iommufd/paging-domain-alloc:
  RDMA/usnic: Use iommu_paging_domain_alloc()
  wifi: ath11k: Use iommu_paging_domain_alloc()
  wifi: ath10k: Use iommu_paging_domain_alloc()
  drm/msm: Use iommu_paging_domain_alloc()
  vhost-vdpa: Use iommu_paging_domain_alloc()
  vfio/type1: Use iommu_paging_domain_alloc()
  iommufd: Use iommu_paging_domain_alloc()
  iommu: Add iommu_paging_domain_alloc() interface

1  2 
drivers/iommu/iommu.c
drivers/iommu/iommufd/hw_pagetable.c
include/linux/iommu.h

diff --combined drivers/iommu/iommu.c
index aaa6fc8f2705c84c5b17522f7032d191dbdee5a2,e03c71a34347eb84c9997b02c92a728e2cf9c742..ed6c5cb60c5aeecff72490e037a70d9fba0553ce
@@@ -510,6 -510,7 +510,6 @@@ DEFINE_MUTEX(iommu_probe_device_lock)
  static int __iommu_probe_device(struct device *dev, struct list_head *group_list)
  {
        const struct iommu_ops *ops;
 -      struct iommu_fwspec *fwspec;
        struct iommu_group *group;
        struct group_device *gdev;
        int ret;
         * be present, and that any of their registered instances has suitable
         * ops for probing, and thus cheekily co-opt the same mechanism.
         */
 -      fwspec = dev_iommu_fwspec_get(dev);
 -      if (fwspec && fwspec->ops)
 -              ops = fwspec->ops;
 -      else
 -              ops = iommu_ops_from_fwnode(NULL);
 -
 +      ops = iommu_fwspec_ops(dev_iommu_fwspec_get(dev));
        if (!ops)
                return -ENODEV;
        /*
@@@ -2010,6 -2016,10 +2010,10 @@@ static int __iommu_domain_alloc_dev(str
        return 0;
  }
  
+ /*
+  * The iommu ops in bus has been retired. Do not use this interface in
+  * new drivers.
+  */
  struct iommu_domain *iommu_domain_alloc(const struct bus_type *bus)
  {
        const struct iommu_ops *ops = NULL;
  }
  EXPORT_SYMBOL_GPL(iommu_domain_alloc);
  
+ /**
+  * iommu_paging_domain_alloc() - Allocate a paging domain
+  * @dev: device for which the domain is allocated
+  *
+  * Allocate a paging domain which will be managed by a kernel driver. Return
+  * allocated domain if successful, or a ERR pointer for failure.
+  */
+ struct iommu_domain *iommu_paging_domain_alloc(struct device *dev)
+ {
+       if (!dev_has_iommu(dev))
+               return ERR_PTR(-ENODEV);
+       return __iommu_domain_alloc(dev_iommu_ops(dev), dev, IOMMU_DOMAIN_UNMANAGED);
+ }
+ EXPORT_SYMBOL_GPL(iommu_paging_domain_alloc);
  void iommu_domain_free(struct iommu_domain *domain)
  {
        if (domain->type == IOMMU_DOMAIN_SVA)
@@@ -2816,16 -2842,13 +2836,16 @@@ const struct iommu_ops *iommu_ops_from_
        return ops;
  }
  
 -int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
 -                    const struct iommu_ops *ops)
 +int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode)
  {
 +      const struct iommu_ops *ops = iommu_ops_from_fwnode(iommu_fwnode);
        struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
  
 +      if (!ops)
 +              return -EPROBE_DEFER;
 +
        if (fwspec)
 -              return ops == fwspec->ops ? 0 : -EINVAL;
 +              return ops == iommu_fwspec_ops(fwspec) ? 0 : -EINVAL;
  
        if (!dev_iommu_get(dev))
                return -ENOMEM;
        if (!fwspec)
                return -ENOMEM;
  
 -      of_node_get(to_of_node(iommu_fwnode));
 +      fwnode_handle_get(iommu_fwnode);
        fwspec->iommu_fwnode = iommu_fwnode;
 -      fwspec->ops = ops;
        dev_iommu_fwspec_set(dev, fwspec);
        return 0;
  }
@@@ -3348,17 -3372,16 +3368,17 @@@ static void __iommu_remove_group_pasid(
   * @domain: the iommu domain.
   * @dev: the attached device.
   * @pasid: the pasid of the device.
 + * @handle: the attach handle.
   *
   * Return: 0 on success, or an error.
   */
  int iommu_attach_device_pasid(struct iommu_domain *domain,
 -                            struct device *dev, ioasid_t pasid)
 +                            struct device *dev, ioasid_t pasid,
 +                            struct iommu_attach_handle *handle)
  {
        /* Caller must be a probed driver on dev */
        struct iommu_group *group = dev->iommu_group;
        struct group_device *device;
 -      void *curr;
        int ret;
  
        if (!domain->ops->set_dev_pasid)
                }
        }
  
 -      curr = xa_cmpxchg(&group->pasid_array, pasid, NULL, domain, GFP_KERNEL);
 -      if (curr) {
 -              ret = xa_err(curr) ? : -EBUSY;
 +      if (handle)
 +              handle->domain = domain;
 +
 +      ret = xa_insert(&group->pasid_array, pasid, handle, GFP_KERNEL);
 +      if (ret)
                goto out_unlock;
 -      }
  
        ret = __iommu_set_group_pasid(domain, group, pasid);
        if (ret)
@@@ -3412,11 -3434,46 +3432,11 @@@ void iommu_detach_device_pasid(struct i
  
        mutex_lock(&group->mutex);
        __iommu_remove_group_pasid(group, pasid, domain);
 -      WARN_ON(xa_erase(&group->pasid_array, pasid) != domain);
 +      xa_erase(&group->pasid_array, pasid);
        mutex_unlock(&group->mutex);
  }
  EXPORT_SYMBOL_GPL(iommu_detach_device_pasid);
  
 -/*
 - * iommu_get_domain_for_dev_pasid() - Retrieve domain for @pasid of @dev
 - * @dev: the queried device
 - * @pasid: the pasid of the device
 - * @type: matched domain type, 0 for any match
 - *
 - * This is a variant of iommu_get_domain_for_dev(). It returns the existing
 - * domain attached to pasid of a device. Callers must hold a lock around this
 - * function, and both iommu_attach/detach_dev_pasid() whenever a domain of
 - * type is being manipulated. This API does not internally resolve races with
 - * attach/detach.
 - *
 - * Return: attached domain on success, NULL otherwise.
 - */
 -struct iommu_domain *iommu_get_domain_for_dev_pasid(struct device *dev,
 -                                                  ioasid_t pasid,
 -                                                  unsigned int type)
 -{
 -      /* Caller must be a probed driver on dev */
 -      struct iommu_group *group = dev->iommu_group;
 -      struct iommu_domain *domain;
 -
 -      if (!group)
 -              return NULL;
 -
 -      xa_lock(&group->pasid_array);
 -      domain = xa_load(&group->pasid_array, pasid);
 -      if (type && domain && domain->type != type)
 -              domain = ERR_PTR(-EBUSY);
 -      xa_unlock(&group->pasid_array);
 -
 -      return domain;
 -}
 -EXPORT_SYMBOL_GPL(iommu_get_domain_for_dev_pasid);
 -
  ioasid_t iommu_alloc_global_pasid(struct device *dev)
  {
        int ret;
@@@ -3443,137 -3500,3 +3463,137 @@@ void iommu_free_global_pasid(ioasid_t p
        ida_free(&iommu_global_pasid_ida, pasid);
  }
  EXPORT_SYMBOL_GPL(iommu_free_global_pasid);
 +
 +/**
 + * iommu_attach_handle_get - Return the attach handle
 + * @group: the iommu group that domain was attached to
 + * @pasid: the pasid within the group
 + * @type: matched domain type, 0 for any match
 + *
 + * Return handle or ERR_PTR(-ENOENT) on none, ERR_PTR(-EBUSY) on mismatch.
 + *
 + * Return the attach handle to the caller. The life cycle of an iommu attach
 + * handle is from the time when the domain is attached to the time when the
 + * domain is detached. Callers are required to synchronize the call of
 + * iommu_attach_handle_get() with domain attachment and detachment. The attach
 + * handle can only be used during its life cycle.
 + */
 +struct iommu_attach_handle *
 +iommu_attach_handle_get(struct iommu_group *group, ioasid_t pasid, unsigned int type)
 +{
 +      struct iommu_attach_handle *handle;
 +
 +      xa_lock(&group->pasid_array);
 +      handle = xa_load(&group->pasid_array, pasid);
 +      if (!handle)
 +              handle = ERR_PTR(-ENOENT);
 +      else if (type && handle->domain->type != type)
 +              handle = ERR_PTR(-EBUSY);
 +      xa_unlock(&group->pasid_array);
 +
 +      return handle;
 +}
 +EXPORT_SYMBOL_NS_GPL(iommu_attach_handle_get, IOMMUFD_INTERNAL);
 +
 +/**
 + * iommu_attach_group_handle - Attach an IOMMU domain to an IOMMU group
 + * @domain: IOMMU domain to attach
 + * @group: IOMMU group that will be attached
 + * @handle: attach handle
 + *
 + * Returns 0 on success and error code on failure.
 + *
 + * This is a variant of iommu_attach_group(). It allows the caller to provide
 + * an attach handle and use it when the domain is attached. This is currently
 + * used by IOMMUFD to deliver the I/O page faults.
 + */
 +int iommu_attach_group_handle(struct iommu_domain *domain,
 +                            struct iommu_group *group,
 +                            struct iommu_attach_handle *handle)
 +{
 +      int ret;
 +
 +      if (handle)
 +              handle->domain = domain;
 +
 +      mutex_lock(&group->mutex);
 +      ret = xa_insert(&group->pasid_array, IOMMU_NO_PASID, handle, GFP_KERNEL);
 +      if (ret)
 +              goto err_unlock;
 +
 +      ret = __iommu_attach_group(domain, group);
 +      if (ret)
 +              goto err_erase;
 +      mutex_unlock(&group->mutex);
 +
 +      return 0;
 +err_erase:
 +      xa_erase(&group->pasid_array, IOMMU_NO_PASID);
 +err_unlock:
 +      mutex_unlock(&group->mutex);
 +      return ret;
 +}
 +EXPORT_SYMBOL_NS_GPL(iommu_attach_group_handle, IOMMUFD_INTERNAL);
 +
 +/**
 + * iommu_detach_group_handle - Detach an IOMMU domain from an IOMMU group
 + * @domain: IOMMU domain to attach
 + * @group: IOMMU group that will be attached
 + *
 + * Detach the specified IOMMU domain from the specified IOMMU group.
 + * It must be used in conjunction with iommu_attach_group_handle().
 + */
 +void iommu_detach_group_handle(struct iommu_domain *domain,
 +                             struct iommu_group *group)
 +{
 +      mutex_lock(&group->mutex);
 +      __iommu_group_set_core_domain(group);
 +      xa_erase(&group->pasid_array, IOMMU_NO_PASID);
 +      mutex_unlock(&group->mutex);
 +}
 +EXPORT_SYMBOL_NS_GPL(iommu_detach_group_handle, IOMMUFD_INTERNAL);
 +
 +/**
 + * iommu_replace_group_handle - replace the domain that a group is attached to
 + * @group: IOMMU group that will be attached to the new domain
 + * @new_domain: new IOMMU domain to replace with
 + * @handle: attach handle
 + *
 + * This is a variant of iommu_group_replace_domain(). It allows the caller to
 + * provide an attach handle for the new domain and use it when the domain is
 + * attached.
 + */
 +int iommu_replace_group_handle(struct iommu_group *group,
 +                             struct iommu_domain *new_domain,
 +                             struct iommu_attach_handle *handle)
 +{
 +      void *curr;
 +      int ret;
 +
 +      if (!new_domain)
 +              return -EINVAL;
 +
 +      mutex_lock(&group->mutex);
 +      if (handle) {
 +              ret = xa_reserve(&group->pasid_array, IOMMU_NO_PASID, GFP_KERNEL);
 +              if (ret)
 +                      goto err_unlock;
 +      }
 +
 +      ret = __iommu_group_set_domain(group, new_domain);
 +      if (ret)
 +              goto err_release;
 +
 +      curr = xa_store(&group->pasid_array, IOMMU_NO_PASID, handle, GFP_KERNEL);
 +      WARN_ON(xa_is_err(curr));
 +
 +      mutex_unlock(&group->mutex);
 +
 +      return 0;
 +err_release:
 +      xa_release(&group->pasid_array, IOMMU_NO_PASID);
 +err_unlock:
 +      mutex_unlock(&group->mutex);
 +      return ret;
 +}
 +EXPORT_SYMBOL_NS_GPL(iommu_replace_group_handle, IOMMUFD_INTERNAL);
index 6d5b2fffeea057095305af708a3e1e67fa39d600,cbddfa3ca95aa8ad5f40d8ea63dd40c9d5d4a292..9020da52c10f4fa886d0919391b2a572566bb78d
@@@ -114,9 -114,6 +114,9 @@@ iommufd_hwpt_paging_alloc(struct iommuf
                return ERR_PTR(-EOPNOTSUPP);
        if (flags & ~valid_flags)
                return ERR_PTR(-EOPNOTSUPP);
 +      if ((flags & IOMMU_HWPT_ALLOC_DIRTY_TRACKING) &&
 +          !device_iommu_capable(idev->dev, IOMMU_CAP_DIRTY_TRACKING))
 +              return ERR_PTR(-EOPNOTSUPP);
  
        hwpt_paging = __iommufd_object_alloc(
                ictx, hwpt_paging, IOMMUFD_OBJ_HWPT_PAGING, common.obj);
                }
                hwpt->domain->owner = ops;
        } else {
-               hwpt->domain = iommu_domain_alloc(idev->dev->bus);
-               if (!hwpt->domain) {
-                       rc = -ENOMEM;
+               hwpt->domain = iommu_paging_domain_alloc(idev->dev);
+               if (IS_ERR(hwpt->domain)) {
+                       rc = PTR_ERR(hwpt->domain);
+                       hwpt->domain = NULL;
                        goto out_abort;
                }
        }
diff --combined include/linux/iommu.h
index 60194d38343a817667801407c00fee932c39d2b5,58ea0935d35532defd31cf751092a72a791dfeaf..d87f9cbfc01e757bfe484981f4df7f3fe8ef7d7c
@@@ -127,7 -127,7 +127,7 @@@ struct iopf_group 
        /* list node for iommu_fault_param::faults */
        struct list_head pending_node;
        struct work_struct work;
 -      struct iommu_domain *domain;
 +      struct iommu_attach_handle *attach_handle;
        /* The device's fault data parameter. */
        struct iommu_fault_param *fault_param;
  };
@@@ -317,9 -317,6 +317,9 @@@ enum iommu_dev_features 
  #define IOMMU_PASID_INVALID   (-1U)
  typedef unsigned int ioasid_t;
  
 +/* Read but do not clear any dirty bits */
 +#define IOMMU_DIRTY_NO_CLEAR (1 << 0)
 +
  #ifdef CONFIG_IOMMU_API
  
  /**
@@@ -356,6 -353,9 +356,6 @@@ struct iommu_dirty_bitmap 
        struct iommu_iotlb_gather *gather;
  };
  
 -/* Read but do not clear any dirty bits */
 -#define IOMMU_DIRTY_NO_CLEAR (1 << 0)
 -
  /**
   * struct iommu_dirty_ops - domain specific dirty tracking operations
   * @set_dirty_tracking: Enable or Disable dirty tracking on the iommu domain
@@@ -547,10 -547,6 +547,10 @@@ static inline int __iommu_copy_struct_f
   * @default_domain: If not NULL this will always be set as the default domain.
   *                  This should be an IDENTITY/BLOCKED/PLATFORM domain.
   *                  Do not use in new drivers.
 + * @user_pasid_table: IOMMU driver supports user-managed PASID table. There is
 + *                    no user domain for each PASID and the I/O page faults are
 + *                    forwarded through the user domain attached to the device
 + *                    RID.
   */
  struct iommu_ops {
        bool (*capable)(struct device *dev, enum iommu_cap);
        struct iommu_domain *blocked_domain;
        struct iommu_domain *release_domain;
        struct iommu_domain *default_domain;
 +      u8 user_pasid_table:1;
  };
  
  /**
@@@ -785,6 -780,7 +785,7 @@@ extern bool iommu_present(const struct 
  extern bool device_iommu_capable(struct device *dev, enum iommu_cap cap);
  extern bool iommu_group_has_isolated_msi(struct iommu_group *group);
  extern struct iommu_domain *iommu_domain_alloc(const struct bus_type *bus);
+ struct iommu_domain *iommu_paging_domain_alloc(struct device *dev);
  extern void iommu_domain_free(struct iommu_domain *domain);
  extern int iommu_attach_device(struct iommu_domain *domain,
                               struct device *dev);
@@@ -973,6 -969,7 +974,6 @@@ extern struct iommu_group *generic_sing
  
  /**
   * struct iommu_fwspec - per-device IOMMU instance data
 - * @ops: ops for this device's IOMMU
   * @iommu_fwnode: firmware handle for this device's IOMMU
   * @flags: IOMMU_FWSPEC_* flags
   * @num_ids: number of associated device IDs
   * consumers.
   */
  struct iommu_fwspec {
 -      const struct iommu_ops  *ops;
        struct fwnode_handle    *iommu_fwnode;
        u32                     flags;
        unsigned int            num_ids;
  /* ATS is supported */
  #define IOMMU_FWSPEC_PCI_RC_ATS                       (1 << 0)
  
 +/*
 + * An iommu attach handle represents a relationship between an iommu domain
 + * and a PASID or RID of a device. It is allocated and managed by the component
 + * that manages the domain and is stored in the iommu group during the time the
 + * domain is attached.
 + */
 +struct iommu_attach_handle {
 +      struct iommu_domain             *domain;
 +};
 +
  /**
   * struct iommu_sva - handle to a device-mm bond
   */
  struct iommu_sva {
 +      struct iommu_attach_handle      handle;
        struct device                   *dev;
 -      struct iommu_domain             *domain;
 -      struct list_head                handle_item;
        refcount_t                      users;
  };
  
  struct iommu_mm_data {
        u32                     pasid;
        struct list_head        sva_domains;
 -      struct list_head        sva_handles;
  };
  
 -int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode,
 -                    const struct iommu_ops *ops);
 +int iommu_fwspec_init(struct device *dev, struct fwnode_handle *iommu_fwnode);
  void iommu_fwspec_free(struct device *dev);
  int iommu_fwspec_add_ids(struct device *dev, const u32 *ids, int num_ids);
 -const struct iommu_ops *iommu_ops_from_fwnode(const struct fwnode_handle *fwnode);
  
  static inline struct iommu_fwspec *dev_iommu_fwspec_get(struct device *dev)
  {
@@@ -1061,10 -1053,12 +1062,10 @@@ int iommu_device_claim_dma_owner(struc
  void iommu_device_release_dma_owner(struct device *dev);
  
  int iommu_attach_device_pasid(struct iommu_domain *domain,
 -                            struct device *dev, ioasid_t pasid);
 +                            struct device *dev, ioasid_t pasid,
 +                            struct iommu_attach_handle *handle);
  void iommu_detach_device_pasid(struct iommu_domain *domain,
                               struct device *dev, ioasid_t pasid);
 -struct iommu_domain *
 -iommu_get_domain_for_dev_pasid(struct device *dev, ioasid_t pasid,
 -                             unsigned int type);
  ioasid_t iommu_alloc_global_pasid(struct device *dev);
  void iommu_free_global_pasid(ioasid_t pasid);
  #else /* CONFIG_IOMMU_API */
@@@ -1093,6 -1087,11 +1094,11 @@@ static inline struct iommu_domain *iomm
        return NULL;
  }
  
+ static inline struct iommu_domain *iommu_paging_domain_alloc(struct device *dev)
+ {
+       return ERR_PTR(-ENODEV);
+ }
  static inline void iommu_domain_free(struct iommu_domain *domain)
  {
  }
@@@ -1322,7 -1321,8 +1328,7 @@@ static inline void iommu_device_unlink(
  }
  
  static inline int iommu_fwspec_init(struct device *dev,
 -                                  struct fwnode_handle *iommu_fwnode,
 -                                  const struct iommu_ops *ops)
 +                                  struct fwnode_handle *iommu_fwnode)
  {
        return -ENODEV;
  }
@@@ -1337,6 -1337,12 +1343,6 @@@ static inline int iommu_fwspec_add_ids(
        return -ENODEV;
  }
  
 -static inline
 -const struct iommu_ops *iommu_ops_from_fwnode(const struct fwnode_handle *fwnode)
 -{
 -      return NULL;
 -}
 -
  static inline int
  iommu_dev_enable_feature(struct device *dev, enum iommu_dev_features feat)
  {
@@@ -1388,8 -1394,7 +1394,8 @@@ static inline int iommu_device_claim_dm
  }
  
  static inline int iommu_attach_device_pasid(struct iommu_domain *domain,
 -                                          struct device *dev, ioasid_t pasid)
 +                                          struct device *dev, ioasid_t pasid,
 +                                          struct iommu_attach_handle *handle)
  {
        return -ENODEV;
  }
@@@ -1399,6 -1404,13 +1405,6 @@@ static inline void iommu_detach_device_
  {
  }
  
 -static inline struct iommu_domain *
 -iommu_get_domain_for_dev_pasid(struct device *dev, ioasid_t pasid,
 -                             unsigned int type)
 -{
 -      return NULL;
 -}
 -
  static inline ioasid_t iommu_alloc_global_pasid(struct device *dev)
  {
        return IOMMU_PASID_INVALID;
@@@ -1521,6 -1533,8 +1527,6 @@@ struct iommu_sva *iommu_sva_bind_device
                                        struct mm_struct *mm);
  void iommu_sva_unbind_device(struct iommu_sva *handle);
  u32 iommu_sva_get_pasid(struct iommu_sva *handle);
 -struct iommu_domain *iommu_sva_domain_alloc(struct device *dev,
 -                                          struct mm_struct *mm);
  #else
  static inline struct iommu_sva *
  iommu_sva_bind_device(struct device *dev, struct mm_struct *mm)
@@@ -1545,6 -1559,12 +1551,6 @@@ static inline u32 mm_get_enqcmd_pasid(s
  }
  
  static inline void mm_pasid_drop(struct mm_struct *mm) {}
 -
 -static inline struct iommu_domain *
 -iommu_sva_domain_alloc(struct device *dev, struct mm_struct *mm)
 -{
 -      return NULL;
 -}
  #endif /* CONFIG_IOMMU_SVA */
  
  #ifdef CONFIG_IOMMU_IOPF
This page took 0.080043 seconds and 4 git commands to generate.