FROMLIST: virt: gunyah: Add ioeventfd
Allow userspace to attach an ioeventfd to an mmio address within the guest. Co-developed-by: Prakruthi Deepak Heragu <quic_pheragu@quicinc.com> Change-Id: I9565c47de3cba3e76528e36088d773b3ce31178e Signed-off-by: Prakruthi Deepak Heragu <quic_pheragu@quicinc.com> Signed-off-by: Elliot Berman <quic_eberman@quicinc.com> Bug: 268234781 Link: https://lore.kernel.org/all/20230304010632.2127470-26-quic_eberman@quicinc.com/
This commit is contained in:
parent
45831b2fd5
commit
7ac3fef966
5 changed files with 165 additions and 1 deletions
|
|
@ -124,7 +124,7 @@ the VM starts.
|
|||
The possible types are documented below:
|
||||
|
||||
.. kernel-doc:: include/uapi/linux/gunyah.h
|
||||
:identifiers: GH_FN_VCPU gh_fn_vcpu_arg GH_FN_IRQFD gh_fn_irqfd_arg
|
||||
:identifiers: GH_FN_VCPU gh_fn_vcpu_arg GH_FN_IRQFD gh_fn_irqfd_arg GH_FN_IOEVENTFD gh_fn_ioeventfd_arg
|
||||
|
||||
Gunyah VCPU API Descriptions
|
||||
----------------------------
|
||||
|
|
|
|||
|
|
@ -35,3 +35,12 @@ config GUNYAH_IRQFD
|
|||
on Gunyah virtual machine.
|
||||
|
||||
Say Y/M here if unsure and you want to support Gunyah VMMs.
|
||||
|
||||
config GUNYAH_IOEVENTFD
|
||||
tristate "Gunyah ioeventfd interface"
|
||||
depends on GUNYAH
|
||||
help
|
||||
Enable kernel support for creating ioeventfds which can alert userspace
|
||||
when a Gunyah virtual machine accesses a memory address.
|
||||
|
||||
Say Y/M here if unsure and you want to support Gunyah VMMs.
|
||||
|
|
|
|||
|
|
@ -8,3 +8,4 @@ obj-$(CONFIG_GUNYAH) += gunyah_rsc_mgr.o
|
|||
|
||||
obj-$(CONFIG_GUNYAH_VCPU) += gunyah_vcpu.o
|
||||
obj-$(CONFIG_GUNYAH_IRQFD) += gunyah_irqfd.o
|
||||
obj-$(CONFIG_GUNYAH_IOEVENTFD) += gunyah_ioeventfd.o
|
||||
|
|
|
|||
117
drivers/virt/gunyah/gunyah_ioeventfd.c
Normal file
117
drivers/virt/gunyah/gunyah_ioeventfd.c
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/eventfd.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/gunyah.h>
|
||||
#include <linux/gunyah_vm_mgr.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/printk.h>
|
||||
|
||||
#include <uapi/linux/gunyah.h>
|
||||
|
||||
struct gh_ioeventfd {
|
||||
struct gh_vm_function_instance *f;
|
||||
struct gh_vm_io_handler io_handler;
|
||||
|
||||
struct eventfd_ctx *ctx;
|
||||
};
|
||||
|
||||
static int gh_write_ioeventfd(struct gh_vm_io_handler *io_dev, u64 addr, u32 len, u64 data)
|
||||
{
|
||||
struct gh_ioeventfd *iofd = container_of(io_dev, struct gh_ioeventfd, io_handler);
|
||||
|
||||
eventfd_signal(iofd->ctx, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct gh_vm_io_handler_ops io_ops = {
|
||||
.write = gh_write_ioeventfd,
|
||||
};
|
||||
|
||||
static long gh_ioeventfd_bind(struct gh_vm_function_instance *f)
|
||||
{
|
||||
const struct gh_fn_ioeventfd_arg *args = f->argp;
|
||||
struct eventfd_ctx *ctx = NULL;
|
||||
struct gh_ioeventfd *iofd;
|
||||
int ret;
|
||||
|
||||
if (f->arg_size != sizeof(*args))
|
||||
return -EINVAL;
|
||||
|
||||
/* must be natural-word sized, or 0 to ignore length */
|
||||
switch (args->len) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* check for range overflow */
|
||||
if (args->addr + args->len < args->addr)
|
||||
return -EINVAL;
|
||||
|
||||
/* ioeventfd with no length can't be combined with DATAMATCH */
|
||||
if (!args->len && (args->flags & GH_IOEVENTFD_DATAMATCH))
|
||||
return -EINVAL;
|
||||
|
||||
/* All other flag bits are reserved for future use */
|
||||
if (args->flags & ~GH_IOEVENTFD_DATAMATCH)
|
||||
return -EINVAL;
|
||||
|
||||
ctx = eventfd_ctx_fdget(args->fd);
|
||||
if (IS_ERR(ctx))
|
||||
return PTR_ERR(ctx);
|
||||
|
||||
iofd = kzalloc(sizeof(*iofd), GFP_KERNEL);
|
||||
if (!iofd) {
|
||||
ret = -ENOMEM;
|
||||
goto err_eventfd;
|
||||
}
|
||||
|
||||
f->data = iofd;
|
||||
iofd->f = f;
|
||||
|
||||
iofd->ctx = ctx;
|
||||
|
||||
if (args->flags & GH_IOEVENTFD_DATAMATCH) {
|
||||
iofd->io_handler.datamatch = true;
|
||||
iofd->io_handler.len = args->len;
|
||||
iofd->io_handler.data = args->datamatch;
|
||||
}
|
||||
iofd->io_handler.addr = args->addr;
|
||||
iofd->io_handler.ops = &io_ops;
|
||||
|
||||
ret = gh_vm_add_io_handler(f->ghvm, &iofd->io_handler);
|
||||
if (ret)
|
||||
goto err_io_dev_add;
|
||||
|
||||
return 0;
|
||||
|
||||
err_io_dev_add:
|
||||
kfree(iofd);
|
||||
err_eventfd:
|
||||
eventfd_ctx_put(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gh_ioevent_unbind(struct gh_vm_function_instance *f)
|
||||
{
|
||||
struct gh_ioeventfd *iofd = f->data;
|
||||
|
||||
eventfd_ctx_put(iofd->ctx);
|
||||
gh_vm_remove_io_handler(iofd->f->ghvm, &iofd->io_handler);
|
||||
kfree(iofd);
|
||||
}
|
||||
|
||||
DECLARE_GH_VM_FUNCTION_INIT(ioeventfd, GH_FN_IOEVENTFD,
|
||||
gh_ioeventfd_bind, gh_ioevent_unbind);
|
||||
MODULE_DESCRIPTION("Gunyah ioeventfds");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -89,6 +89,23 @@ struct gh_vm_dtb_config {
|
|||
*/
|
||||
#define GH_FN_IRQFD 2
|
||||
|
||||
/**
|
||||
* GH_FN_IOEVENTFD - register ioeventfd to trigger when VM faults on parameter
|
||||
*
|
||||
* gh_fn_desc is filled with gh_fn_ioeventfd_arg
|
||||
*
|
||||
* Attaches an ioeventfd to a legal mmio address within the guest. A guest write
|
||||
* in the registered address will signal the provided event instead of triggering
|
||||
* an exit on the GH_VCPU_RUN ioctl.
|
||||
*
|
||||
* If GH_IOEVENTFD_DATAMATCH flag is set, the event will be signaled only if the
|
||||
* written value to the registered address is equal to datamatch in
|
||||
* struct gh_fn_ioeventfd_arg.
|
||||
*
|
||||
* Return: 0
|
||||
*/
|
||||
#define GH_FN_IOEVENTFD 3
|
||||
|
||||
#define GH_FN_MAX_ARG_SIZE 256
|
||||
|
||||
/**
|
||||
|
|
@ -118,6 +135,26 @@ struct gh_fn_irqfd_arg {
|
|||
|
||||
#define GH_IOEVENTFD_DATAMATCH (1UL << 0)
|
||||
|
||||
/**
|
||||
* struct gh_fn_ioeventfd_arg - Arguments to create an ioeventfd function
|
||||
* @datamatch: data used when GH_IOEVENTFD_DATAMATCH is set
|
||||
* @addr: Address in guest memory
|
||||
* @len: Length of access
|
||||
* @fd: When ioeventfd is matched, this eventfd is written
|
||||
* @flags: If GH_IOEVENTFD_DATAMATCH flag is set, the event will be signaled
|
||||
* only if the written value to the registered address is equal to
|
||||
* @datamatch
|
||||
* @padding: padding bytes
|
||||
*/
|
||||
struct gh_fn_ioeventfd_arg {
|
||||
__u64 datamatch;
|
||||
__u64 addr; /* legal mmio address */
|
||||
__u32 len; /* 1, 2, 4, or 8 bytes; or 0 to ignore length */
|
||||
__s32 fd;
|
||||
__u32 flags;
|
||||
__u32 padding;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct gh_fn_desc - Arguments to create a VM function
|
||||
* @type: Type of the function. See GH_FN_* macro for supported types
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue