Externd the IOMMUFD framework to provide a user space API for responding to page faults. Signed-off-by: Yi Liu <yi.l.liu@xxxxxxxxx> Signed-off-by: Lu Baolu <baolu.lu@xxxxxxxxxxxxxxx> --- drivers/iommu/iommufd/iommufd_private.h | 1 + include/uapi/linux/iommufd.h | 23 +++++++++++ drivers/iommu/iommufd/hw_pagetable.c | 54 +++++++++++++++++++++++++ drivers/iommu/iommufd/main.c | 3 ++ 4 files changed, 81 insertions(+) diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h index 6da0ba9421d0..0985e83a611f 100644 --- a/drivers/iommu/iommufd/iommufd_private.h +++ b/drivers/iommu/iommufd/iommufd_private.h @@ -288,6 +288,7 @@ void iommufd_hw_pagetable_destroy(struct iommufd_object *obj); void iommufd_hw_pagetable_abort(struct iommufd_object *obj); int iommufd_hwpt_alloc(struct iommufd_ucmd *ucmd); int iommufd_hwpt_invalidate(struct iommufd_ucmd *ucmd); +int iommufd_hwpt_page_response(struct iommufd_ucmd *ucmd); static inline void iommufd_hw_pagetable_put(struct iommufd_ctx *ictx, struct iommufd_hw_pagetable *hwpt) diff --git a/include/uapi/linux/iommufd.h b/include/uapi/linux/iommufd.h index 63863e21d043..65bb856dd8fb 100644 --- a/include/uapi/linux/iommufd.h +++ b/include/uapi/linux/iommufd.h @@ -50,6 +50,7 @@ enum { IOMMUFD_CMD_HWPT_INVALIDATE, IOMMUFD_CMD_DEVICE_SET_DATA, IOMMUFD_CMD_DEVICE_UNSET_DATA, + IOMMUFD_CMD_PAGE_RESPONSE, }; /** @@ -779,4 +780,26 @@ struct iommu_device_unset_data { __u32 dev_id; }; #define IOMMU_DEVICE_UNSET_DATA _IO(IOMMUFD_TYPE, IOMMUFD_CMD_DEVICE_UNSET_DATA) + +/** + * struct iommu_hwpt_page_response - ioctl(IOMMUFD_CMD_PAGE_RESPONSE) + * @size: sizeof(struct iommu_hwpt_page_response) + * @flags: encodes whether the corresponding fields are valid + * (IOMMU_PGFAULT_FLAGS_* values) + * @hwpt_id: hwpt ID of target hardware page table for the response + * @dev_id: device ID of target device for the response + * @pasid: Process Address Space ID + * @grpid: Page Request Group Index + * @code: response code from &enum iommu_page_response_code + */ +struct iommu_hwpt_page_response { + __u32 size; + __u32 flags; + __u32 hwpt_id; + __u32 dev_id; + __u32 pasid; + __u32 grpid; + __u32 code; +}; +#define IOMMU_PAGE_RESPONSE _IO(IOMMUFD_TYPE, IOMMUFD_CMD_PAGE_RESPONSE) #endif diff --git a/drivers/iommu/iommufd/hw_pagetable.c b/drivers/iommu/iommufd/hw_pagetable.c index 09377a98069b..c1f3ebdce796 100644 --- a/drivers/iommu/iommufd/hw_pagetable.c +++ b/drivers/iommu/iommufd/hw_pagetable.c @@ -521,3 +521,57 @@ static void hw_pagetable_fault_free(struct hw_pgtable_fault *fault) eventfd_ctx_put(fault->trigger); kfree(fault); } + +int iommufd_hwpt_page_response(struct iommufd_ucmd *ucmd) +{ + struct iommu_hwpt_page_response *cmd = ucmd->cmd; + struct iommu_page_response resp = {}; + struct iommufd_fault *curr, *next; + struct iommufd_hw_pagetable *hwpt; + struct iommufd_device *idev; + int rc = -EINVAL; + + hwpt = iommufd_get_hwpt(ucmd, cmd->hwpt_id); + if (IS_ERR(hwpt)) + return rc; + + if (!hwpt->parent || !hwpt->fault) + goto out_put_hwpt; + + idev = iommufd_get_device(ucmd, cmd->dev_id); + if (IS_ERR(idev)) + goto out_put_hwpt; + + mutex_lock(&hwpt->fault->mutex); + list_for_each_entry_safe(curr, next, &hwpt->fault->response, item) { + if (curr->dev != idev->dev || curr->fault.grpid != cmd->grpid) + continue; + + if ((cmd->flags & IOMMU_PGFAULT_FLAGS_PASID_VALID) && + cmd->pasid != curr->fault.pasid) + break; + + if ((curr->fault.flags & IOMMU_PGFAULT_FLAGS_RESP_NEEDS_PASID) && + !(cmd->flags & IOMMU_PGFAULT_FLAGS_PASID_VALID)) + break; + + resp.version = IOMMU_PAGE_RESP_VERSION_1; + resp.pasid = cmd->pasid; + resp.grpid = cmd->grpid; + resp.code = cmd->code; + if (curr->fault.flags & IOMMU_FAULT_PAGE_REQUEST_PASID_VALID) + resp.flags = IOMMU_PAGE_RESP_PASID_VALID; + + rc = iommu_page_response(idev->dev, &resp); + list_del_init(&curr->item); + kfree(curr); + break; + } + mutex_unlock(&hwpt->fault->mutex); + + iommufd_put_object(&idev->obj); +out_put_hwpt: + iommufd_put_object(&hwpt->obj); + + return rc; +} diff --git a/drivers/iommu/iommufd/main.c b/drivers/iommu/iommufd/main.c index bb39dc6f3b27..0c8988808f43 100644 --- a/drivers/iommu/iommufd/main.c +++ b/drivers/iommu/iommufd/main.c @@ -279,6 +279,7 @@ union ucmd_buffer { struct iommu_ioas_unmap unmap; struct iommu_option option; struct iommu_vfio_ioas vfio_ioas; + struct iommu_hwpt_page_response resp; #ifdef CONFIG_IOMMUFD_TEST struct iommu_test_cmd test; #endif @@ -335,6 +336,8 @@ static const struct iommufd_ioctl_op iommufd_ioctl_ops[] = { struct iommu_device_set_data, data_len), IOCTL_OP(IOMMU_DEVICE_UNSET_DATA, iommufd_device_unset_data, struct iommu_device_unset_data, dev_id), + IOCTL_OP(IOMMU_PAGE_RESPONSE, iommufd_hwpt_page_response, struct iommu_hwpt_page_response, + code), #ifdef CONFIG_IOMMUFD_TEST IOCTL_OP(IOMMU_TEST_CMD, iommufd_test, struct iommu_test_cmd, last), #endif -- 2.34.1