@@ -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 {
@@ -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);