@@ -162,6 +162,23 @@ enum iommu_dev_features {
IOMMU_DEV_FEAT_IOPF,
};
+/**
+ * enum iommu_dma_owner - IOMMU DMA ownership
+ * @DMA_OWNER_NONE: No 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_NONE,
+ DMA_OWNER_DMA_API,
+ DMA_OWNER_PRIVATE_DOMAIN,
+ DMA_OWNER_PRIVATE_DOMAIN_USER,
+};
+
#define IOMMU_PASID_INVALID (-1U)
#ifdef CONFIG_IOMMU_API
@@ -681,6 +698,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 +1102,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;
+ refcount_t owner_cnt;
+ void *owner_cookie;
};
struct group_device {
@@ -621,6 +624,7 @@ struct iommu_group *iommu_group_alloc(void)
INIT_LIST_HEAD(&group->devices);
INIT_LIST_HEAD(&group->entry);
BLOCKING_INIT_NOTIFIER_HEAD(&group->notifier);
+ group->dma_owner = DMA_OWNER_NONE;
ret = ida_simple_get(&iommu_group_ida, 0, 0, GFP_KERNEL);
if (ret < 0) {
@@ -3351,3 +3355,92 @@ 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)
+{
+ if (refcount_inc_not_zero(&group->owner_cnt)) {
+ if (group->dma_owner != owner ||
+ group->owner_cookie != owner_cookie) {
+ refcount_dec(&group->owner_cnt);
+ return -EBUSY;
+ }
+
+ return 0;
+ }
+
+ group->dma_owner = owner;
+ group->owner_cookie = owner_cookie;
+ refcount_set(&group->owner_cnt, 1);
+
+ return 0;
+}
+
+static void __iommu_group_release_dma_owner(struct iommu_group *group,
+ enum iommu_dma_owner owner)
+{
+ if (WARN_ON(group->dma_owner != owner))
+ return;
+
+ if (!refcount_dec_and_test(&group->owner_cnt))
+ return;
+
+ group->dma_owner = DMA_OWNER_NONE;
+}
+
+/**
+ * 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;
+ }
+
+ mutex_lock(&group->mutex);
+ ret = __iommu_group_set_dma_owner(group, owner, owner_cookie);
+ mutex_unlock(&group->mutex);
+ 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;
+ }
+
+ mutex_lock(&group->mutex);
+ __iommu_group_release_dma_owner(group, owner);
+ mutex_unlock(&group->mutex);
+ iommu_group_put(group);
+}
+EXPORT_SYMBOL_GPL(iommu_device_release_dma_owner);