[PATCH 20/22] iommufd: Introduce vIOMMU option via IOMMU_OPTION ioctl

Suravee Suthikulpanit
2026-03-30T08:44:28Z

Introduce a new iommufd_option enum value, IOMMU_OPTION_VIOMMU, to
support vIOMMU-specific options. Extend the existing IOMMU_OPTION
ioctl to accept key-value style input, enabling access to data arrays
such as hardware registers.

Additionally, introduce struct iommufd_viommu_ops with set_option
and get_option callbacks to support IOMMUFD vIOMMU options.

Signed-off-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com>
---
drivers/iommu/iommufd/ioas.c | 2 +-
drivers/iommu/iommufd/iommufd_private.h | 1 +
drivers/iommu/iommufd/main.c | 6 +++---
drivers/iommu/iommufd/viommu.c | 18 ++++++++++++++++++
include/linux/iommufd.h | 5 +++++
include/uapi/linux/iommufd.h | 8 ++++++--
6 files changed, 34 insertions(+), 6 deletions(-)

diff --git a/drivers/iommu/iommufd/ioas.c b/drivers/iommu/iommufd/ioas.c
index f4721afedadc..95547630fd3a 100644
--- a/drivers/iommu/iommufd/ioas.c
+++ b/drivers/iommu/iommufd/ioas.c
@@ -643,7 +643,7 @@ int iommufd_ioas_option(struct iommufd_ucmd *ucmd)
struct iommufd_ioas *ioas;
int rc = 0;

- if (cmd->__reserved)
+ if (cmd->key)
return -EOPNOTSUPP;

ioas = iommufd_get_ioas(ucmd->ictx, cmd->object_id);
diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h
index eb6d1a70f673..0306f2b8236f 100644
--- a/drivers/iommu/iommufd/iommufd_private.h
+++ b/drivers/iommu/iommufd/iommufd_private.h
@@ -698,6 +698,7 @@ void iommufd_vdevice_destroy(struct iommufd_object *obj);
void iommufd_vdevice_abort(struct iommufd_object *obj);
int iommufd_hw_queue_alloc_ioctl(struct iommufd_ucmd *ucmd);
void iommufd_hw_queue_destroy(struct iommufd_object *obj);
+int iommufd_viommu_option(struct iommufd_ucmd *ucmd);

static inline struct iommufd_vdevice *
iommufd_get_vdevice(struct iommufd_ctx *ictx, u32 id)
diff --git a/drivers/iommu/iommufd/main.c b/drivers/iommu/iommufd/main.c
index 5cc4b08c25f5..ae36a6e8042d 100644
--- a/drivers/iommu/iommufd/main.c
+++ b/drivers/iommu/iommufd/main.c
@@ -391,9 +391,6 @@ static int iommufd_option(struct iommufd_ucmd *ucmd)
struct iommu_option *cmd = ucmd->cmd;
int rc;

- if (cmd->__reserved)
- return -EOPNOTSUPP;
-
switch (cmd->option_id) {
case IOMMU_OPTION_RLIMIT_MODE:
rc = iommufd_option_rlimit_mode(cmd, ucmd->ictx);
@@ -401,6 +398,9 @@ static int iommufd_option(struct iommufd_ucmd *ucmd)
case IOMMU_OPTION_HUGE_PAGES:
rc = iommufd_ioas_option(ucmd);
break;
+ case IOMMU_OPTION_VIOMMU:
+ rc = iommufd_viommu_option(ucmd);
+ break;
default:
return -EOPNOTSUPP;
}
diff --git a/drivers/iommu/iommufd/viommu.c b/drivers/iommu/iommufd/viommu.c
index 59b31b3e36be..51585337b8e4 100644
--- a/drivers/iommu/iommufd/viommu.c
+++ b/drivers/iommu/iommufd/viommu.c
@@ -477,3 +477,21 @@ int iommufd_hw_queue_alloc_ioctl(struct iommufd_ucmd *ucmd)
iommufd_put_object(ucmd->ictx, &viommu->obj);
return rc;
}
+
+int iommufd_viommu_option(struct iommufd_ucmd *ucmd)
+{
+ int ret = -EOPNOTSUPP;
+ struct iommu_option *cmd = ucmd->cmd;
+ struct iommufd_viommu *viommu = iommufd_get_viommu(ucmd, cmd->object_id);
+
+ if (cmd->op == IOMMU_OPTION_OP_SET) {
+ if (!viommu->ops->set_option)
+ return ret;
+ ret = viommu->ops->set_option(viommu, cmd->key, cmd->val64);
+ } else if (cmd->op == IOMMU_OPTION_OP_GET) {
+ if (!viommu->ops->get_option)
+ return ret;
+ ret = viommu->ops->get_option(viommu, cmd->key, &cmd->val64);
+ }
+ return ret;
+}
diff --git a/include/linux/iommufd.h b/include/linux/iommufd.h
index c0030677e13c..8096b7e59088 100644
--- a/include/linux/iommufd.h
+++ b/include/linux/iommufd.h
@@ -183,6 +183,9 @@ struct iommufd_hw_queue {
* @hw_queue_init: Similar to hw_queue_init_phys, but driver providing this op
* indicates that HW accesses the guest queue memory via
* @hw_queue->baseaddr.
+ * @set_option: Set the key-value option for the specified vIOMMU.
+ * @get_option: Get the key-value option for the specified vIOMMU.
+ * On success, the value is returned via the provided value.
*/
struct iommufd_viommu_ops {
void (*destroy)(struct iommufd_viommu *viommu);
@@ -198,6 +201,8 @@ struct iommufd_viommu_ops {
int (*hw_queue_init_phys)(struct iommufd_hw_queue *hw_queue, u32 index,
phys_addr_t base_addr_pa);
int (*hw_queue_init)(struct iommufd_hw_queue *hw_queue, u32 index);
+ int (*set_option)(struct iommufd_viommu *viommu, u16 key, u64 value);
+ int (*get_option)(struct iommufd_viommu *viommu, u16 key, u64 *value);
};

#if IS_ENABLED(CONFIG_IOMMUFD)
diff --git a/include/uapi/linux/iommufd.h b/include/uapi/linux/iommufd.h
index f71a4b979d52..922bb7511045 100644
--- a/include/uapi/linux/iommufd.h
+++ b/include/uapi/linux/iommufd.h
@@ -306,10 +306,14 @@ struct iommu_ioas_unmap {
* iommu mappings. Value 0 disables combining, everything is mapped to
* PAGE_SIZE. This can be useful for benchmarking. This is a per-IOAS
* option, the object_id must be the IOAS ID.
+ * @IOMMU_KEY_VAL_OPTION_VIOMMU:
+ * Specifies key-value option for vIOMMU object. The caller must specify
+ * vIOMMU ID for object_id. The allowed ops are set and get.
*/
enum iommufd_option {
IOMMU_OPTION_RLIMIT_MODE = 0,
IOMMU_OPTION_HUGE_PAGES = 1,
+ IOMMU_OPTION_VIOMMU = 2,
};

/**
@@ -328,7 +332,7 @@ enum iommufd_option_ops {
* @size: sizeof(struct iommu_option)
* @option_id: One of enum iommufd_option
* @op: One of enum iommufd_option_ops
- * @__reserved: Must be 0
+ * @key: Option key to match with the value
* @object_id: ID of the object if required
* @val64: Option value to set or value returned on get
*
@@ -340,7 +344,7 @@ struct iommu_option {
__u32 size;
__u32 option_id;
__u16 op;
- __u16 __reserved;
+ __u16 key;
__u32 object_id;
__aligned_u64 val64;
};
--
2.34.1