[RFC PATCHES 11/17] iommufd: Deliver fault messages to user space

From: Lu Baolu
Date: Tue May 30 2023 - 01:41:11 EST


Provide a read-only file interface that allows user space to obtain fault
messages by sequentially reading the file. User space can determine whether
all fault messages have been read by comparing the provided read buffer
with the actually returned data length. Once a fault is read by the user,
it will be moved from the pending list to the waiting-for-response list.

Signed-off-by: Yi Liu <yi.l.liu@xxxxxxxxx>
Signed-off-by: Lu Baolu <baolu.lu@xxxxxxxxxxxxxxx>
---
drivers/iommu/iommufd/iommufd_private.h | 2 +
drivers/iommu/iommufd/hw_pagetable.c | 66 +++++++++++++++++++++++++
2 files changed, 68 insertions(+)

diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h
index 67e5aa0f996e..6da0ba9421d0 100644
--- a/drivers/iommu/iommufd/iommufd_private.h
+++ b/drivers/iommu/iommufd/iommufd_private.h
@@ -242,6 +242,8 @@ struct hw_pgtable_fault {
struct list_head response;
struct eventfd_ctx *trigger;
bool user_pasid_table;
+ struct file *fault_file;
+ int fault_fd;
};

struct iommufd_fault {
diff --git a/drivers/iommu/iommufd/hw_pagetable.c b/drivers/iommu/iommufd/hw_pagetable.c
index ca3e4d92f2aa..09377a98069b 100644
--- a/drivers/iommu/iommufd/hw_pagetable.c
+++ b/drivers/iommu/iommufd/hw_pagetable.c
@@ -4,6 +4,8 @@
*/
#include <linux/iommu.h>
#include <linux/eventfd.h>
+#include <linux/file.h>
+#include <linux/anon_inodes.h>
#include <uapi/linux/iommufd.h>

#include "../iommu-priv.h"
@@ -310,6 +312,8 @@ int iommufd_hwpt_alloc(struct iommufd_ucmd *ucmd)
iommu_domain_set_iopf_handler(hwpt->domain,
iommufd_hw_pagetable_iopf_handler,
hwpt);
+
+ cmd->out_fault_fd = hwpt->fault->fault_fd;
}

cmd->out_hwpt_id = hwpt->obj.id;
@@ -421,6 +425,62 @@ iommufd_hw_pagetable_iopf_handler(struct iommu_fault *fault,
return IOMMU_PAGE_RESP_ASYNC;
}

+static ssize_t hwpt_fault_fops_read(struct file *filep, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct hw_pgtable_fault *fault = filep->private_data;
+ size_t fault_size = sizeof(struct iommu_fault);
+ struct iommufd_fault *ifault;
+ size_t done = 0;
+
+ if (ppos || count % fault_size)
+ return -ESPIPE;
+
+ mutex_lock(&fault->mutex);
+ while (!list_empty(&fault->deliver) && count > done) {
+ ifault = list_first_entry(&fault->deliver, struct iommufd_fault, item);
+ if (copy_to_user(buf + done, &ifault->fault, fault_size))
+ break;
+ done += fault_size;
+ list_del_init(&ifault->item);
+ if (ifault->fault.flags & IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE)
+ list_add_tail(&ifault->item, &fault->response);
+ else
+ kfree(ifault);
+ }
+ mutex_unlock(&fault->mutex);
+
+ return done;
+}
+
+static const struct file_operations hwpt_fault_fops = {
+ .owner = THIS_MODULE,
+ .read = hwpt_fault_fops_read,
+};
+
+static int hw_pagetable_get_fault_fd(struct hw_pgtable_fault *fault)
+{
+ struct file *filep;
+ int fdno;
+
+ fdno = get_unused_fd_flags(O_CLOEXEC);
+ if (fdno < 0)
+ return fdno;
+
+ filep = anon_inode_getfile("[iommufd-pgfault]", &hwpt_fault_fops,
+ fault, O_RDONLY);
+ if (IS_ERR(filep)) {
+ put_unused_fd(fdno);
+ return PTR_ERR(filep);
+ }
+
+ fd_install(fdno, filep);
+ fault->fault_file = filep;
+ fault->fault_fd = fdno;
+
+ return 0;
+}
+
static struct hw_pgtable_fault *hw_pagetable_fault_alloc(int eventfd)
{
struct hw_pgtable_fault *fault;
@@ -440,8 +500,14 @@ static struct hw_pgtable_fault *hw_pagetable_fault_alloc(int eventfd)
goto out_free;
}

+ rc = hw_pagetable_get_fault_fd(fault);
+ if (rc)
+ goto out_put_eventfd;
+
return fault;

+out_put_eventfd:
+ eventfd_ctx_put(fault->trigger);
out_free:
kfree(fault);
return ERR_PTR(rc);
--
2.34.1