[PATCH v4 3/5] virt: acrn: support asyncio in ioeventfd


Conghui Chen
 

ioeventfd is a mechanism to register PIO/MMIO regions to trigger an
eventfd signal when written to by a User VM. It is used by vhost devices
automatically and can be used by virtio devices when configured. In
ACRN, the User VM would not return until the register access is
finished, as for ioeventfd, until the eventfd signal is triggered. To
speeds up the process, asyncio is used, so the User VM can return directly
from hypervisor and do not need to wait for Service VM kernel to process.

Signed-off-by: Conghui <conghui.chen@...>
---
drivers/virt/acrn/hypercall.h | 17 +++++++++++++
drivers/virt/acrn/ioeventfd.c | 48 ++++++++++++++++++++++++++++++++---
include/uapi/linux/acrn.h | 15 +++++++++++
3 files changed, 76 insertions(+), 4 deletions(-)

diff --git a/drivers/virt/acrn/hypercall.h b/drivers/virt/acrn/hypercall.h
index 4811003289ba..594badb919e3 100644
--- a/drivers/virt/acrn/hypercall.h
+++ b/drivers/virt/acrn/hypercall.h
@@ -32,6 +32,8 @@
#define HC_ID_IOREQ_BASE 0x30UL
#define HC_SET_IOREQ_BUFFER _HC_ID(HC_ID, HC_ID_IOREQ_BASE + 0x00)
#define HC_NOTIFY_REQUEST_FINISH _HC_ID(HC_ID, HC_ID_IOREQ_BASE + 0x01)
+#define HC_ASYNCIO_ASSIGN _HC_ID(HC_ID, HC_ID_IOREQ_BASE + 0x02)
+#define HC_ASYNCIO_DEASSIGN _HC_ID(HC_ID, HC_ID_IOREQ_BASE + 0x03)

#define HC_ID_MEM_BASE 0x40UL
#define HC_VM_SET_MEMORY_REGIONS _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x02)
@@ -188,6 +190,21 @@ static inline long hcall_set_sbuf(u64 vmid, u64 buffer)
return acrn_hypercall2(HC_SET_SBUF, vmid, buffer);
}

+/**
+ * hcall_asyncio_assign() - Assign asyncio requests.
+ * @vmid: User VM ID
+ * @info_pa: Service VM GPA of the asyncio request info
+ *
+ * Return: 0 on success, <0 on failure
+ */
+static inline long hcall_asyncio_assign(u64 vmid, u64 info_pa)
+{
+ return acrn_hypercall2(HC_ASYNCIO_ASSIGN, vmid, info_pa);
+}
+static inline long hcall_asyncio_deassign(u64 vmid, u64 info_pa)
+{
+ return acrn_hypercall2(HC_ASYNCIO_DEASSIGN, vmid, info_pa);
+}
/**
* hcall_notify_req_finish() - Notify ACRN Hypervisor of I/O request completion.
* @vmid: User VM ID
diff --git a/drivers/virt/acrn/ioeventfd.c b/drivers/virt/acrn/ioeventfd.c
index ac4037e9f947..5aad5cc3e67c 100644
--- a/drivers/virt/acrn/ioeventfd.c
+++ b/drivers/virt/acrn/ioeventfd.c
@@ -10,6 +10,7 @@
*/

#include <linux/eventfd.h>
+#include <linux/io.h>
#include <linux/slab.h>

#include "acrn_drv.h"
@@ -170,6 +171,38 @@ static int acrn_ioeventfd_deassign(struct acrn_vm *vm,
return 0;
}

+static int acrn_asyncio_ioeventfd_setup(struct acrn_vm *vm, bool is_assign,
+ struct acrn_ioeventfd *args)
+{
+ struct eventfd_ctx *eventfd;
+ struct acrn_asyncio_reqinfo *asyncio_info;
+ int ret;
+
+ asyncio_info = kzalloc(sizeof(*asyncio_info), GFP_KERNEL);
+ if (!asyncio_info)
+ return -ENOMEM;
+
+ eventfd = eventfd_ctx_fdget(args->fd);
+ if (IS_ERR(eventfd))
+ return PTR_ERR(eventfd);
+
+ asyncio_info->addr = args->addr;
+ asyncio_info->fd = (u64)eventfd;
+ if (args->flags & ACRN_IOEVENTFD_FLAG_PIO)
+ asyncio_info->type = ACRN_ASYNC_TYPE_PIO;
+ else
+ asyncio_info->type = ACRN_ASYNC_TYPE_MMIO;
+ if (is_assign)
+ ret = hcall_asyncio_assign(vm->vmid, virt_to_phys(asyncio_info));
+ else
+ ret = hcall_asyncio_deassign(vm->vmid, virt_to_phys(asyncio_info));
+ if (ret < 0) {
+ dev_err(acrn_dev.this_device, "Failed to setup asyncio: base = 0x%llx!\n", args->addr);
+ }
+ kfree(asyncio_info);
+ return ret;
+}
+
static struct hsm_ioeventfd *hsm_ioeventfd_match(struct acrn_vm *vm, u64 addr,
u64 data, int len, int type)
{
@@ -233,10 +266,17 @@ int acrn_ioeventfd_config(struct acrn_vm *vm, struct acrn_ioeventfd *args)
{
int ret;

- if (args->flags & ACRN_IOEVENTFD_FLAG_DEASSIGN)
- ret = acrn_ioeventfd_deassign(vm, args);
- else
- ret = acrn_ioeventfd_assign(vm, args);
+ if (args->flags & ACRN_IOEVENTFD_FLAG_DEASSIGN) {
+ if (args->flags & ACRN_IOEVENTFD_FLAG_ASYNCIO)
+ ret = acrn_asyncio_ioeventfd_setup(vm, false, args);
+ else
+ ret = acrn_ioeventfd_deassign(vm, args);
+ } else {
+ if (args->flags & ACRN_IOEVENTFD_FLAG_ASYNCIO)
+ ret = acrn_asyncio_ioeventfd_setup(vm, true, args);
+ else
+ ret = acrn_ioeventfd_assign(vm, args);
+ }

return ret;
}
diff --git a/include/uapi/linux/acrn.h b/include/uapi/linux/acrn.h
index 5a0120afa5ef..8363926328e4 100644
--- a/include/uapi/linux/acrn.h
+++ b/include/uapi/linux/acrn.h
@@ -479,6 +479,20 @@ struct acrn_vdev {
__u8 args[128];
};

+#define ACRN_ASYNC_TYPE_PIO 0x01U
+#define ACRN_ASYNC_TYPE_MMIO 0x02U
+
+/*struct acrn_asyncio_reqinfo - Data for setting asyncio request of User VM
+ * @type: IO type, ACRN_ASYNC_PIO/ACRN_ASYNC_MMIO
+ * @addr: IO base address
+ * @fd: eventfd of this asynicio.
+ */
+struct acrn_asyncio_reqinfo {
+ u32 type;
+ u64 addr;
+ u64 fd;
+};
+
#define SBUF_MAGIC 0x5aa57aa71aa13aa3
#define SBUF_MAX_SIZE (1ULL << 22)
#define SBUF_HEAD_SIZE 64
@@ -585,6 +599,7 @@ enum acrn_pm_cmd_type {
#define ACRN_IOEVENTFD_FLAG_PIO 0x01
#define ACRN_IOEVENTFD_FLAG_DATAMATCH 0x02
#define ACRN_IOEVENTFD_FLAG_DEASSIGN 0x04
+#define ACRN_IOEVENTFD_FLAG_ASYNCIO 0x08
/**
* struct acrn_ioeventfd - Data to operate a &struct hsm_ioeventfd
* @fd: The fd of eventfd associated with a hsm_ioeventfd
--
2.25.1


Li, Fei1
 

On 2022-09-13 at 11:29:12 +0800, Conghui Chen wrote:
ioeventfd is a mechanism to register PIO/MMIO regions to trigger an
eventfd signal when written to by a User VM. It is used by vhost devices
automatically and can be used by virtio devices when configured. In
ACRN, the User VM would not return until the register access is
finished, as for ioeventfd, until the eventfd signal is triggered. To
speeds up the process, asyncio is used, so the User VM can return directly
from hypervisor and do not need to wait for Service VM kernel to process.

Signed-off-by: Conghui <conghui.chen@...>
---
drivers/virt/acrn/hypercall.h | 17 +++++++++++++
drivers/virt/acrn/ioeventfd.c | 48 ++++++++++++++++++++++++++++++++---
include/uapi/linux/acrn.h | 15 +++++++++++
3 files changed, 76 insertions(+), 4 deletions(-)

diff --git a/drivers/virt/acrn/hypercall.h b/drivers/virt/acrn/hypercall.h
index 4811003289ba..594badb919e3 100644
--- a/drivers/virt/acrn/hypercall.h
+++ b/drivers/virt/acrn/hypercall.h
@@ -32,6 +32,8 @@
#define HC_ID_IOREQ_BASE 0x30UL
#define HC_SET_IOREQ_BUFFER _HC_ID(HC_ID, HC_ID_IOREQ_BASE + 0x00)
#define HC_NOTIFY_REQUEST_FINISH _HC_ID(HC_ID, HC_ID_IOREQ_BASE + 0x01)
+#define HC_ASYNCIO_ASSIGN _HC_ID(HC_ID, HC_ID_IOREQ_BASE + 0x02)
+#define HC_ASYNCIO_DEASSIGN _HC_ID(HC_ID, HC_ID_IOREQ_BASE + 0x03)

#define HC_ID_MEM_BASE 0x40UL
#define HC_VM_SET_MEMORY_REGIONS _HC_ID(HC_ID, HC_ID_MEM_BASE + 0x02)
@@ -188,6 +190,21 @@ static inline long hcall_set_sbuf(u64 vmid, u64 buffer)
return acrn_hypercall2(HC_SET_SBUF, vmid, buffer);
}

+/**
+ * hcall_asyncio_assign() - Assign asyncio requests.
+ * @vmid: User VM ID
+ * @info_pa: Service VM GPA of the asyncio request info
+ *
+ * Return: 0 on success, <0 on failure
+ */
+static inline long hcall_asyncio_assign(u64 vmid, u64 info_pa)
+{
+ return acrn_hypercall2(HC_ASYNCIO_ASSIGN, vmid, info_pa);
+}
+static inline long hcall_asyncio_deassign(u64 vmid, u64 info_pa)
+{
+ return acrn_hypercall2(HC_ASYNCIO_DEASSIGN, vmid, info_pa);
+}
/**
* hcall_notify_req_finish() - Notify ACRN Hypervisor of I/O request completion.
* @vmid: User VM ID
diff --git a/drivers/virt/acrn/ioeventfd.c b/drivers/virt/acrn/ioeventfd.c
index ac4037e9f947..5aad5cc3e67c 100644
--- a/drivers/virt/acrn/ioeventfd.c
+++ b/drivers/virt/acrn/ioeventfd.c
@@ -10,6 +10,7 @@
*/

#include <linux/eventfd.h>
+#include <linux/io.h>
#include <linux/slab.h>

#include "acrn_drv.h"
@@ -170,6 +171,38 @@ static int acrn_ioeventfd_deassign(struct acrn_vm *vm,
return 0;
}

+static int acrn_asyncio_ioeventfd_setup(struct acrn_vm *vm, bool is_assign,
+ struct acrn_ioeventfd *args)
+{
+ struct eventfd_ctx *eventfd;
+ struct acrn_asyncio_reqinfo *asyncio_info;
+ int ret;
+
+ asyncio_info = kzalloc(sizeof(*asyncio_info), GFP_KERNEL);
+ if (!asyncio_info)
+ return -ENOMEM;
+
+ eventfd = eventfd_ctx_fdget(args->fd);
+ if (IS_ERR(eventfd))
+ return PTR_ERR(eventfd);
+
+ asyncio_info->addr = args->addr;
+ asyncio_info->fd = (u64)eventfd;
+ if (args->flags & ACRN_IOEVENTFD_FLAG_PIO)
+ asyncio_info->type = ACRN_ASYNC_TYPE_PIO;
+ else
+ asyncio_info->type = ACRN_ASYNC_TYPE_MMIO;
+ if (is_assign)
+ ret = hcall_asyncio_assign(vm->vmid, virt_to_phys(asyncio_info));
+ else
+ ret = hcall_asyncio_deassign(vm->vmid, virt_to_phys(asyncio_info));
+ if (ret < 0) {
+ dev_err(acrn_dev.this_device, "Failed to setup asyncio: base = 0x%llx!\n", args->addr);
+ }
+ kfree(asyncio_info);
+ return ret;
+}
+
static struct hsm_ioeventfd *hsm_ioeventfd_match(struct acrn_vm *vm, u64 addr,
u64 data, int len, int type)
{
@@ -233,10 +266,17 @@ int acrn_ioeventfd_config(struct acrn_vm *vm, struct acrn_ioeventfd *args)
{
int ret;

- if (args->flags & ACRN_IOEVENTFD_FLAG_DEASSIGN)
- ret = acrn_ioeventfd_deassign(vm, args);
- else
- ret = acrn_ioeventfd_assign(vm, args);
+ if (args->flags & ACRN_IOEVENTFD_FLAG_DEASSIGN) {
+ if (args->flags & ACRN_IOEVENTFD_FLAG_ASYNCIO)
+ ret = acrn_asyncio_ioeventfd_setup(vm, false, args);
Maybe it's better to define assign/deassign for asyncio, same as ioeventfd ?
+ else
+ ret = acrn_ioeventfd_deassign(vm, args);
+ } else {
+ if (args->flags & ACRN_IOEVENTFD_FLAG_ASYNCIO)
+ ret = acrn_asyncio_ioeventfd_setup(vm, true, args);
+ else
+ ret = acrn_ioeventfd_assign(vm, args);
+ }

return ret;
}
diff --git a/include/uapi/linux/acrn.h b/include/uapi/linux/acrn.h
index 5a0120afa5ef..8363926328e4 100644
--- a/include/uapi/linux/acrn.h
+++ b/include/uapi/linux/acrn.h
@@ -479,6 +479,20 @@ struct acrn_vdev {
__u8 args[128];
};

+#define ACRN_ASYNC_TYPE_PIO 0x01U
+#define ACRN_ASYNC_TYPE_MMIO 0x02U
+
+/*struct acrn_asyncio_reqinfo - Data for setting asyncio request of User VM
+ * @type: IO type, ACRN_ASYNC_PIO/ACRN_ASYNC_MMIO
+ * @addr: IO base address
+ * @fd: eventfd of this asynicio.
+ */
+struct acrn_asyncio_reqinfo {
+ u32 type;
+ u64 addr;
+ u64 fd;
+};
+
#define SBUF_MAGIC 0x5aa57aa71aa13aa3
#define SBUF_MAX_SIZE (1ULL << 22)
#define SBUF_HEAD_SIZE 64
@@ -585,6 +599,7 @@ enum acrn_pm_cmd_type {
#define ACRN_IOEVENTFD_FLAG_PIO 0x01
#define ACRN_IOEVENTFD_FLAG_DATAMATCH 0x02
#define ACRN_IOEVENTFD_FLAG_DEASSIGN 0x04
+#define ACRN_IOEVENTFD_FLAG_ASYNCIO 0x08
/**
* struct acrn_ioeventfd - Data to operate a &struct hsm_ioeventfd
* @fd: The fd of eventfd associated with a hsm_ioeventfd
--
2.25.1