[PATCH RFC 3/9] iommufd: Add external HWPT support
From: Jacob Pan
Date: Fri Sep 25 2026 - 15:09:58 EST
Add IOMMU_HWPT_DATA_EXTERNAL and an IOMMUFD_OBJ_HWPT_EXTERNAL object for
externally managed attachable domains created under a vIOMMU.
Route vIOMMU child HWPT allocation by data type, add the driver
alloc_domain_external callback, and allow device attach/replace to
consume external HWPTs.
Assisted-by: GPT-5.6 Sol
Signed-off-by: Jacob Pan <jacob.pan@xxxxxxxxxxxxxxxxxxx>
---
drivers/iommu/iommufd/device.c | 9 +--
drivers/iommu/iommufd/hw_pagetable.c | 88 +++++++++++++++++++++++--
drivers/iommu/iommufd/iommufd_private.h | 7 ++
drivers/iommu/iommufd/main.c | 4 ++
include/linux/iommufd.h | 11 ++++
include/uapi/linux/iommufd.h | 17 +++++
6 files changed, 126 insertions(+), 10 deletions(-)
diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c
index 5c4b06eda546..f99b4730994c 100644
--- a/drivers/iommu/iommufd/device.c
+++ b/drivers/iommu/iommufd/device.c
@@ -1026,6 +1026,7 @@ static int iommufd_device_change_pt(struct iommufd_device *idev,
return PTR_ERR(pt_obj);
switch (pt_obj->type) {
+ case IOMMUFD_OBJ_HWPT_EXTERNAL:
case IOMMUFD_OBJ_HWPT_NESTED:
case IOMMUFD_OBJ_HWPT_PAGING: {
struct iommufd_hw_pagetable *hwpt =
@@ -1066,8 +1067,8 @@ static int iommufd_device_change_pt(struct iommufd_device *idev,
* iommufd_device_attach - Connect a device/pasid to an iommu_domain
* @idev: device to attach
* @pasid: pasid to attach
- * @pt_id: Input an IOMMUFD_OBJ_IOAS, or IOMMUFD_OBJ_HWPT_PAGING
- * Output the IOMMUFD_OBJ_HWPT_PAGING ID
+ * @pt_id: Input an IOMMUFD_OBJ_IOAS, IOMMUFD_OBJ_HWPT_PAGING, or
+ * IOMMUFD_OBJ_HWPT_EXTERNAL. Output the HWPT ID
*
* This connects the device/pasid to an iommu_domain, either automatically
* or manually selected. Once this completes the device could do DMA with
@@ -1099,8 +1100,8 @@ EXPORT_SYMBOL_NS_GPL(iommufd_device_attach, "IOMMUFD");
* iommufd_device_replace - Change the device/pasid's iommu_domain
* @idev: device to change
* @pasid: pasid to change
- * @pt_id: Input an IOMMUFD_OBJ_IOAS, or IOMMUFD_OBJ_HWPT_PAGING
- * Output the IOMMUFD_OBJ_HWPT_PAGING ID
+ * @pt_id: Input an IOMMUFD_OBJ_IOAS, IOMMUFD_OBJ_HWPT_PAGING, or
+ * IOMMUFD_OBJ_HWPT_EXTERNAL. Output the HWPT ID
*
* This is the same as::
*
diff --git a/drivers/iommu/iommufd/hw_pagetable.c b/drivers/iommu/iommufd/hw_pagetable.c
index ef6e119c2a75..324a81d9bae4 100644
--- a/drivers/iommu/iommufd/hw_pagetable.c
+++ b/drivers/iommu/iommufd/hw_pagetable.c
@@ -77,6 +77,20 @@ void iommufd_hwpt_nested_abort(struct iommufd_object *obj)
iommufd_hwpt_nested_destroy(obj);
}
+void iommufd_hwpt_external_destroy(struct iommufd_object *obj)
+{
+ struct iommufd_hwpt_external *hwpt_external =
+ container_of(obj, struct iommufd_hwpt_external, common.obj);
+
+ __iommufd_hwpt_destroy(&hwpt_external->common);
+ refcount_dec(&hwpt_external->viommu->obj.users);
+}
+
+void iommufd_hwpt_external_abort(struct iommufd_object *obj)
+{
+ iommufd_hwpt_external_destroy(obj);
+}
+
static int
iommufd_hwpt_paging_enforce_cc(struct iommufd_hwpt_paging *hwpt_paging)
{
@@ -306,6 +320,8 @@ iommufd_viommu_alloc_hwpt_nested(struct iommufd_viommu *viommu, u32 flags,
return ERR_PTR(-EOPNOTSUPP);
if (!user_data->len)
return ERR_PTR(-EOPNOTSUPP);
+ if (!viommu->hwpt)
+ return ERR_PTR(-EINVAL);
if (!viommu->ops || !viommu->ops->alloc_domain_nested)
return ERR_PTR(-EOPNOTSUPP);
@@ -342,6 +358,53 @@ iommufd_viommu_alloc_hwpt_nested(struct iommufd_viommu *viommu, u32 flags,
return ERR_PTR(rc);
}
+static struct iommufd_hwpt_external *
+iommufd_viommu_alloc_hwpt_external(struct iommufd_viommu *viommu, u32 flags,
+ const struct iommu_user_data *user_data)
+{
+ struct iommufd_hwpt_external *hwpt_external;
+ struct iommufd_hw_pagetable *hwpt;
+ int rc;
+
+ if (flags)
+ return ERR_PTR(-EOPNOTSUPP);
+ if (!user_data->len)
+ return ERR_PTR(-EOPNOTSUPP);
+ if (!viommu->ops || !viommu->ops->alloc_domain_external)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ hwpt_external = __iommufd_object_alloc(viommu->ictx, hwpt_external,
+ IOMMUFD_OBJ_HWPT_EXTERNAL,
+ common.obj);
+ if (IS_ERR(hwpt_external))
+ return ERR_CAST(hwpt_external);
+ hwpt = &hwpt_external->common;
+
+ hwpt_external->viommu = viommu;
+ refcount_inc(&viommu->obj.users);
+
+ hwpt->domain = viommu->ops->alloc_domain_external(viommu, flags,
+ user_data);
+ if (IS_ERR(hwpt->domain)) {
+ rc = PTR_ERR(hwpt->domain);
+ hwpt->domain = NULL;
+ goto out_abort;
+ }
+ hwpt->domain->iommufd_hwpt = hwpt;
+ hwpt->domain->owner = viommu->iommu_dev->ops;
+ hwpt->domain->cookie_type = IOMMU_COOKIE_IOMMUFD;
+
+ if (WARN_ON_ONCE(hwpt->domain->type != IOMMU_DOMAIN_EXTERNAL)) {
+ rc = -EOPNOTSUPP;
+ goto out_abort;
+ }
+ return hwpt_external;
+
+out_abort:
+ iommufd_object_abort_and_destroy(viommu->ictx, &hwpt->obj);
+ return ERR_PTR(rc);
+}
+
int iommufd_hwpt_alloc(struct iommufd_ucmd *ucmd)
{
struct iommu_hwpt_alloc *cmd = ucmd->cmd;
@@ -399,6 +462,7 @@ int iommufd_hwpt_alloc(struct iommufd_ucmd *ucmd)
}
hwpt = &hwpt_nested->common;
} else if (pt_obj->type == IOMMUFD_OBJ_VIOMMU) {
+ struct iommufd_hwpt_external *hwpt_external;
struct iommufd_hwpt_nested *hwpt_nested;
struct iommu_device *iommu_dev;
struct iommufd_viommu *viommu;
@@ -409,13 +473,25 @@ int iommufd_hwpt_alloc(struct iommufd_ucmd *ucmd)
rc = -EINVAL;
goto out_unlock;
}
- hwpt_nested = iommufd_viommu_alloc_hwpt_nested(
- viommu, cmd->flags, &user_data);
- if (IS_ERR(hwpt_nested)) {
- rc = PTR_ERR(hwpt_nested);
- goto out_unlock;
+ if (cmd->data_type == IOMMU_HWPT_DATA_EXTERNAL) {
+ hwpt_external =
+ iommufd_viommu_alloc_hwpt_external(viommu,
+ cmd->flags,
+ &user_data);
+ if (IS_ERR(hwpt_external)) {
+ rc = PTR_ERR(hwpt_external);
+ goto out_unlock;
+ }
+ hwpt = &hwpt_external->common;
+ } else {
+ hwpt_nested = iommufd_viommu_alloc_hwpt_nested(
+ viommu, cmd->flags, &user_data);
+ if (IS_ERR(hwpt_nested)) {
+ rc = PTR_ERR(hwpt_nested);
+ goto out_unlock;
+ }
+ hwpt = &hwpt_nested->common;
}
- hwpt = &hwpt_nested->common;
} else {
rc = -EINVAL;
goto out_put_pt;
diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h
index eb2e85b27e42..8b7aaaaba194 100644
--- a/drivers/iommu/iommufd/iommufd_private.h
+++ b/drivers/iommu/iommufd/iommufd_private.h
@@ -403,6 +403,11 @@ struct iommufd_hwpt_nested {
struct iommufd_viommu *viommu;
};
+struct iommufd_hwpt_external {
+ struct iommufd_hw_pagetable common;
+ struct iommufd_viommu *viommu;
+};
+
static inline bool hwpt_is_paging(struct iommufd_hw_pagetable *hwpt)
{
return hwpt->obj.type == IOMMUFD_OBJ_HWPT_PAGING;
@@ -465,6 +470,8 @@ void iommufd_hwpt_paging_destroy(struct iommufd_object *obj);
void iommufd_hwpt_paging_abort(struct iommufd_object *obj);
void iommufd_hwpt_nested_destroy(struct iommufd_object *obj);
void iommufd_hwpt_nested_abort(struct iommufd_object *obj);
+void iommufd_hwpt_external_destroy(struct iommufd_object *obj);
+void iommufd_hwpt_external_abort(struct iommufd_object *obj);
int iommufd_hwpt_alloc(struct iommufd_ucmd *ucmd);
int iommufd_hwpt_invalidate(struct iommufd_ucmd *ucmd);
diff --git a/drivers/iommu/iommufd/main.c b/drivers/iommu/iommufd/main.c
index 9a921b153162..6835bb87916a 100644
--- a/drivers/iommu/iommufd/main.c
+++ b/drivers/iommu/iommufd/main.c
@@ -736,6 +736,10 @@ static const struct iommufd_object_ops iommufd_object_ops[] = {
.destroy = iommufd_hwpt_nested_destroy,
.abort = iommufd_hwpt_nested_abort,
},
+ [IOMMUFD_OBJ_HWPT_EXTERNAL] = {
+ .destroy = iommufd_hwpt_external_destroy,
+ .abort = iommufd_hwpt_external_abort,
+ },
[IOMMUFD_OBJ_IOAS] = {
.destroy = iommufd_ioas_destroy,
},
diff --git a/include/linux/iommufd.h b/include/linux/iommufd.h
index 6e7efe83bc5d..ce740a2d1588 100644
--- a/include/linux/iommufd.h
+++ b/include/linux/iommufd.h
@@ -31,6 +31,7 @@ enum iommufd_object_type {
IOMMUFD_OBJ_DEVICE,
IOMMUFD_OBJ_HWPT_PAGING,
IOMMUFD_OBJ_HWPT_NESTED,
+ IOMMUFD_OBJ_HWPT_EXTERNAL,
IOMMUFD_OBJ_IOAS,
IOMMUFD_OBJ_ACCESS,
IOMMUFD_OBJ_FAULT,
@@ -149,6 +150,12 @@ struct iommufd_hw_queue {
* must be defined in include/uapi/linux/iommufd.h.
* It must fully initialize the new iommu_domain before
* returning. Upon failure, ERR_PTR must be returned.
+ * @alloc_domain_external: Allocate a IOMMU_DOMAIN_EXTERNAL on a vIOMMU that holds
+ * the VM identity for an external attach domain. @user_data must
+ * be defined in
+ * include/uapi/linux/iommufd.h. It must fully initialize
+ * the new iommu_domain before returning. Upon failure,
+ * ERR_PTR must be returned.
* @cache_invalidate: Flush hardware cache used by a vIOMMU. It can be used for
* any IOMMU hardware specific cache: TLB and device cache.
* The @array passes in the cache invalidation requests, in
@@ -186,6 +193,10 @@ struct iommufd_viommu_ops {
struct iommu_domain *(*alloc_domain_nested)(
struct iommufd_viommu *viommu, u32 flags,
const struct iommu_user_data *user_data);
+ struct iommu_domain *
+ (*alloc_domain_external)(struct iommufd_viommu *viommu,
+ u32 flags,
+ const struct iommu_user_data *user_data);
int (*cache_invalidate)(struct iommufd_viommu *viommu,
struct iommu_user_data_array *array);
const size_t vdevice_size;
diff --git a/include/uapi/linux/iommufd.h b/include/uapi/linux/iommufd.h
index bffa25c5267e..fa6185f04c1c 100644
--- a/include/uapi/linux/iommufd.h
+++ b/include/uapi/linux/iommufd.h
@@ -506,18 +506,31 @@ struct iommu_hwpt_amd_guest {
__aligned_u64 dte[4];
};
+/**
+ * struct iommu_hwpt_external - external attach domain
+ * (IOMMU_HWPT_DATA_EXTERNAL)
+ * @flags: Must be 0
+ * @__reserved: Must be 0
+ */
+struct iommu_hwpt_external {
+ __u32 flags;
+ __u32 __reserved;
+};
+
/**
* enum iommu_hwpt_data_type - IOMMU HWPT Data Type
* @IOMMU_HWPT_DATA_NONE: no data
* @IOMMU_HWPT_DATA_VTD_S1: Intel VT-d stage-1 page table
* @IOMMU_HWPT_DATA_ARM_SMMUV3: ARM SMMUv3 Context Descriptor Table
* @IOMMU_HWPT_DATA_AMD_GUEST: AMD IOMMU guest page table
+ * @IOMMU_HWPT_DATA_EXTERNAL: external attach domain
*/
enum iommu_hwpt_data_type {
IOMMU_HWPT_DATA_NONE = 0,
IOMMU_HWPT_DATA_VTD_S1 = 1,
IOMMU_HWPT_DATA_ARM_SMMUV3 = 2,
IOMMU_HWPT_DATA_AMD_GUEST = 3,
+ IOMMU_HWPT_DATA_EXTERNAL = 4,
};
/**
@@ -552,6 +565,10 @@ enum iommu_hwpt_data_type {
* via @dev_id and the vIOMMU via @pt_id must be associated to the same IOMMU
* instance.
*
+ * An external attach HWPT can be created from a given vIOMMU via @pt_id. In this
+ * case, @data_type selects the external attach domain type, such as
+ * IOMMU_HWPT_DATA_EXTERNAL, and the created HWPT is not IOAS-backed.
+ *
* If the @data_type is set to IOMMU_HWPT_DATA_NONE, @data_len and
* @data_uptr should be zero. Otherwise, both @data_len and @data_uptr
* must be given.
--
2.43.0