@@ -162,6 +162,21 @@ enum iommu_dev_features {
IOMMU_DEV_FEAT_IOPF,
};
+/**
+ * enum iommu_dma_owner - IOMMU DMA ownership
+ * @DMA_OWNER_DMA_API: Device DMAs are initiated by a kernel driver through
+ * the kernel DMA API.
+ * @DMA_OWNER_PRIVATE_DOMAIN: Device DMAs are initiated by a kernel driver
+ * which provides an UNMANAGED domain.
+ * @DMA_OWNER_PRIVATE_DOMAIN_USER: Device DMAs are initiated by userspace,
+ * kernel ensures that DMAs never go to kernel memory.
+ */
+enum iommu_dma_owner {
+ DMA_OWNER_DMA_API,
+ DMA_OWNER_PRIVATE_DOMAIN,
+ DMA_OWNER_PRIVATE_DOMAIN_USER,
+};
+
#define IOMMU_PASID_INVALID (-1U)
#ifdef CONFIG_IOMMU_API
@@ -681,6 +696,10 @@ struct iommu_sva *iommu_sva_bind_device(struct device *dev,
void iommu_sva_unbind_device(struct iommu_sva *handle);
u32 iommu_sva_get_pasid(struct iommu_sva *handle);
+int iommu_device_set_dma_owner(struct device *dev, enum iommu_dma_owner owner,
+ void *owner_cookie);
+void iommu_device_release_dma_owner(struct device *dev, enum iommu_dma_owner owner);
+
#else /* CONFIG_IOMMU_API */
struct iommu_ops {};
@@ -1081,6 +1100,21 @@ static inline struct iommu_fwspec *dev_iommu_fwspec_get(struct device *dev)
{
return NULL;
}
+
+static inline int iommu_device_set_dma_owner(struct device *dev,
+ enum iommu_dma_owner owner,
+ void *owner_cookie)
+{
+ if (owner != DMA_OWNER_DMA_API)
+ return -EINVAL;
+
+ return 0;
+}
+
+static inline void iommu_device_release_dma_owner(struct device *dev,
+ enum iommu_dma_owner owner)
+{
+}
#endif /* CONFIG_IOMMU_API */
/**
@@ -48,6 +48,9 @@ struct iommu_group {
struct iommu_domain *default_domain;
struct iommu_domain *domain;
struct list_head entry;
+ enum iommu_dma_owner dma_owner;
+ unsigned int owner_cnt;
+ void *owner_cookie;
};
struct group_device {
@@ -3351,3 +3354,95 @@ static ssize_t iommu_group_store_type(struct iommu_group *group,
return ret;
}
+
+static int iommu_group_set_dma_owner(struct iommu_group *group,
+ enum iommu_dma_owner owner,
+ void *owner_cookie)
+{
+ int ret = 0;
+
+ mutex_lock(&group->mutex);
+ if (group->owner_cnt &&
+ (group->dma_owner != owner ||
+ group->owner_cookie != owner_cookie)) {
+ ret = -EBUSY;
+ goto unlock_out;
+ }
+
+ group->dma_owner = owner;
+ group->owner_cookie = owner_cookie;
+ group->owner_cnt++;
+
+unlock_out:
+ mutex_unlock(&group->mutex);
+
+ return ret;
+}
+
+static void iommu_group_release_dma_owner(struct iommu_group *group,
+ enum iommu_dma_owner owner)
+{
+ mutex_lock(&group->mutex);
+ if (WARN_ON(!group->owner_cnt || group->dma_owner != owner))
+ goto unlock_out;
+
+ if (--group->owner_cnt > 0)
+ goto unlock_out;
+
+ group->dma_owner = DMA_OWNER_DMA_API;
+
+unlock_out:
+ mutex_unlock(&group->mutex);
+}
+
+/**
+ * iommu_device_set_dma_owner() - Set DMA ownership of a device
+ * @dev: The device.
+ * @owner: DMA ownership type.
+ * @owner_cookie: Caller specified pointer. Could be used for exclusive
+ * declaration. Could be NULL.
+ *
+ * Set the DMA ownership of a device. The different ownerships are
+ * exclusive. The caller could specify a owner_cookie pointer so that
+ * the same DMA ownership could be exclusive among different owners.
+ */
+int iommu_device_set_dma_owner(struct device *dev, enum iommu_dma_owner owner,
+ void *owner_cookie)
+{
+ struct iommu_group *group = iommu_group_get(dev);
+ int ret;
+
+ if (!group) {
+ if (owner == DMA_OWNER_DMA_API)
+ return 0;
+ else
+ return -ENODEV;
+ }
+
+ ret = iommu_group_set_dma_owner(group, owner, owner_cookie);
+ iommu_group_put(group);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iommu_device_set_dma_owner);
+
+/**
+ * iommu_device_release_dma_owner() - Release DMA ownership of a device
+ * @dev: The device.
+ * @owner: The DMA ownership type.
+ *
+ * Release the DMA ownership claimed by iommu_device_set_dma_owner().
+ */
+void iommu_device_release_dma_owner(struct device *dev, enum iommu_dma_owner owner)
+{
+ struct iommu_group *group = iommu_group_get(dev);
+
+ if (!group) {
+ WARN_ON(owner != DMA_OWNER_DMA_API);
+ return;
+ }
+
+ iommu_group_release_dma_owner(group, owner);
+ iommu_group_put(group);
+}
+EXPORT_SYMBOL_GPL(iommu_device_release_dma_owner);