[PATCH v4 5/9] virt: geniezone: Add irqfd support
AngeloGioacchino Del Regno
angelogioacchino.delregno at collabora.com
Mon Jun 12 00:57:34 PDT 2023
Il 09/06/23 10:52, Yi-De Wu ha scritto:
> From: "Yingshiuan Pan" <yingshiuan.pan at mediatek.com>
>
> irqfd enables other threads than vcpu threads to inject virtual
> interrupt through irqfd asynchronously rather through ioctl interface.
> This interface is necessary for VMM which creates separated thread for
> IO handling or uses vhost devices.
>
> Signed-off-by: Yingshiuan Pan <yingshiuan.pan at mediatek.com>
> Signed-off-by: Liju Chen <liju-clr.chen at mediatek.com>
> Signed-off-by: Yi-De Wu <yi-de.wu at mediatek.com>
> ---
> arch/arm64/geniezone/gzvm_arch_common.h | 6 +
> drivers/virt/geniezone/Makefile | 4 +-
> drivers/virt/geniezone/gzvm_irqfd.c | 537 ++++++++++++++++++++++++
> drivers/virt/geniezone/gzvm_main.c | 3 +-
> drivers/virt/geniezone/gzvm_vcpu.c | 1 +
> drivers/virt/geniezone/gzvm_vm.c | 18 +
> include/linux/gzvm_drv.h | 27 ++
> include/uapi/linux/gzvm.h | 18 +
> 8 files changed, 611 insertions(+), 3 deletions(-)
> create mode 100644 drivers/virt/geniezone/gzvm_irqfd.c
>
> diff --git a/arch/arm64/geniezone/gzvm_arch_common.h b/arch/arm64/geniezone/gzvm_arch_common.h
> index 1b315264bf24..5affa28b935a 100644
> --- a/arch/arm64/geniezone/gzvm_arch_common.h
> +++ b/arch/arm64/geniezone/gzvm_arch_common.h
> @@ -46,6 +46,7 @@ enum {
> #define MT_HVC_GZVM_CREATE_DEVICE GZVM_HCALL_ID(GZVM_FUNC_CREATE_DEVICE)
> #define MT_HVC_GZVM_PROBE GZVM_HCALL_ID(GZVM_FUNC_PROBE)
> #define MT_HVC_GZVM_ENABLE_CAP GZVM_HCALL_ID(GZVM_FUNC_ENABLE_CAP)
> +#define GIC_V3_NR_LRS 16
>
> /**
> * gzvm_hypercall_wrapper()
> @@ -72,6 +73,11 @@ static inline gzvm_vcpu_id_t get_vcpuid_from_tuple(unsigned int tuple)
> return (gzvm_vcpu_id_t)(tuple & 0xffff);
> }
>
> +struct gzvm_vcpu_hwstate {
No documentation?
> + __u32 nr_lrs;
> + __u64 lr[GIC_V3_NR_LRS];
> +};
> +
> static inline unsigned int
> assemble_vm_vcpu_tuple(gzvm_id_t vmid, gzvm_vcpu_id_t vcpuid)
> {
> diff --git a/drivers/virt/geniezone/Makefile b/drivers/virt/geniezone/Makefile
> index 67ba3ed76ea7..aa52cee3ca8e 100644
> --- a/drivers/virt/geniezone/Makefile
> +++ b/drivers/virt/geniezone/Makefile
> @@ -7,5 +7,5 @@
> GZVM_DIR ?= ../../../drivers/virt/geniezone
>
> gzvm-y := $(GZVM_DIR)/gzvm_main.o $(GZVM_DIR)/gzvm_vm.o \
> - $(GZVM_DIR)/gzvm_vcpu.o $(GZVM_DIR)/gzvm_irqchip.o
> -
> + $(GZVM_DIR)/gzvm_vcpu.o $(GZVM_DIR)/gzvm_irqchip.o \
> + $(GZVM_DIR)/gzvm_irqfd.o
> diff --git a/drivers/virt/geniezone/gzvm_irqfd.c b/drivers/virt/geniezone/gzvm_irqfd.c
> new file mode 100644
> index 000000000000..3a395b972fdf
> --- /dev/null
> +++ b/drivers/virt/geniezone/gzvm_irqfd.c
> @@ -0,0 +1,537 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2023 MediaTek Inc.
> + */
> +
> +#include <linux/eventfd.h>
> +#include <linux/syscalls.h>
> +#include <linux/gzvm_drv.h>
> +#include "gzvm_common.h"
> +
> +struct gzvm_irq_ack_notifier {
> + struct hlist_node link;
> + unsigned int gsi;
> + void (*irq_acked)(struct gzvm_irq_ack_notifier *ian);
> +};
> +
> +/**
> + * struct gzvm_kernel_irqfd_resampler - irqfd resampler descriptor.
> + * @gzvm: Poiner to gzvm.
> + * @list_head list: List of resampling struct _irqfd objects sharing this gsi.
This is just `@list`
> + * RCU list modified under gzvm->irqfds.resampler_lock.
> + * @notifier: gzvm irq ack notifier.
> + * @list_head link: Entry in list of gzvm->irqfd.resampler_list.
and this is `@link`.
> + * Use for sharing esamplers among irqfds on the same gsi.
> + * Accessed and modified under gzvm->irqfds.resampler_lock.
> + *
> + * Resampling irqfds are a special variety of irqfds used to emulate
> + * level triggered interrupts. The interrupt is asserted on eventfd
> + * trigger. On acknowledgment through the irq ack notifier, the
> + * interrupt is de-asserted and userspace is notified through the
> + * resamplefd. All resamplers on the same gsi are de-asserted
> + * together, so we don't need to track the state of each individual
> + * user. We can also therefore share the same irq source ID.
> + */
> +struct gzvm_kernel_irqfd_resampler {
> + struct gzvm *gzvm;
> +
> + struct list_head list;
> + struct gzvm_irq_ack_notifier notifier;
> +
> + struct list_head link;
> +};
> +
> +/**
> + * struct gzvm_kernel_irqfd: gzvm kernel irqfd descriptor.
> + * @gzvm: Pointer to gzvm.
> + * @wait: Wait queue entry.
> + * @gsi: Used for level IRQ fast-path.
> + * @resampler: The resampler used by this irqfd (resampler-only).
> + * @resamplefd: Eventfd notified on resample (resampler-only).
> + * @resampler_link: Entry in list of irqfds for a resampler (resampler-only).
> + * @eventfd: Used for setup/shutdown.
You're not documenting `list`, `pt`, `shutdown`: that will trigger a kerneldoc
warning. Please fix.
> + */
> +struct gzvm_kernel_irqfd {
> + struct gzvm *gzvm;
> + wait_queue_entry_t wait;
> +
> + int gsi;
> +
> + struct gzvm_kernel_irqfd_resampler *resampler;
> +
> + struct eventfd_ctx *resamplefd;
> +
> + struct list_head resampler_link;
> +
> + struct eventfd_ctx *eventfd;
> + struct list_head list;
> + poll_table pt;
> + struct work_struct shutdown;
> +};
..snip..
> diff --git a/drivers/virt/geniezone/gzvm_main.c b/drivers/virt/geniezone/gzvm_main.c
> index 230970cb0953..e62c046d76b3 100644
> --- a/drivers/virt/geniezone/gzvm_main.c
> +++ b/drivers/virt/geniezone/gzvm_main.c
> @@ -113,11 +113,12 @@ static int gzvm_drv_probe(void)
> return ret;
> gzvm_debug_dev = &gzvm_dev;
>
> - return 0;
> + return gzvm_drv_irqfd_init();
ret = gzvm_drv_irqfd_init();
if (ret)
return ret;
return 0;
> }
>
> static int gzvm_drv_remove(void)
> {
> + gzvm_drv_irqfd_exit();
> destroy_all_vm();
> misc_deregister(&gzvm_dev);
> return 0;
> diff --git a/drivers/virt/geniezone/gzvm_vcpu.c b/drivers/virt/geniezone/gzvm_vcpu.c
> index 5e9d34fcc1ea..94ead15fea3f 100644
> --- a/drivers/virt/geniezone/gzvm_vcpu.c
> +++ b/drivers/virt/geniezone/gzvm_vcpu.c
> @@ -211,6 +211,7 @@ int gzvm_vm_ioctl_create_vcpu(struct gzvm *gzvm, u32 cpuid)
> ret = -ENOMEM;
> goto free_vcpu;
> }
> + vcpu->hwstate = (void *)vcpu->run + PAGE_SIZE;
> vcpu->vcpuid = cpuid;
> vcpu->gzvm = gzvm;
> mutex_init(&vcpu->lock);
> diff --git a/drivers/virt/geniezone/gzvm_vm.c b/drivers/virt/geniezone/gzvm_vm.c
> index 0cab67b0bdf8..eedc2d5c24ad 100644
> --- a/drivers/virt/geniezone/gzvm_vm.c
> +++ b/drivers/virt/geniezone/gzvm_vm.c
> @@ -361,6 +361,15 @@ static long gzvm_vm_ioctl(struct file *filp, unsigned int ioctl,
> ret = gzvm_vm_ioctl_create_device(gzvm, argp);
> break;
> }
> + case GZVM_IRQFD: {
> + struct gzvm_irqfd data;
> +
> + ret = -EFAULT;
> + if (copy_from_user(&data, argp, sizeof(data)))
if (copy_from_user...) {
ret = -EFAULT;
goto out;
}
> + goto out;
> + ret = gzvm_irqfd(gzvm, &data);
> + break;
> + }
> case GZVM_ENABLE_CAP: {
> struct gzvm_enable_cap cap;
>
> @@ -385,6 +394,7 @@ static void gzvm_destroy_vm(struct gzvm *gzvm)
>
> mutex_lock(&gzvm->lock);
>
> + gzvm_vm_irqfd_release(gzvm);
> gzvm_destroy_vcpus(gzvm);
> gzvm_arch_destroy_vm(gzvm->vm_id);
>
> @@ -430,6 +440,14 @@ static struct gzvm *gzvm_create_vm(unsigned long vm_type)
> gzvm->mm = current->mm;
> mutex_init(&gzvm->lock);
>
> + ret = gzvm_vm_irqfd_init(gzvm);
> + if (ret) {
> + dev_err(gzvm_debug_dev->this_device,
> + "Failed to initialize irqfd\n");
> + kfree(gzvm);
> + return ERR_PTR(ret);
> + }
> +
> mutex_lock(&gzvm_list_lock);
> list_add(&gzvm->vm_list, &gzvm_list);
> mutex_unlock(&gzvm_list_lock);
> diff --git a/include/linux/gzvm_drv.h b/include/linux/gzvm_drv.h
> index 842a026df9f3..54b0a3d443c5 100644
> --- a/include/linux/gzvm_drv.h
> +++ b/include/linux/gzvm_drv.h
> @@ -11,6 +11,7 @@
> #include <linux/mutex.h>
> #include <linux/platform_device.h>
> #include <linux/gzvm.h>
> +#include <linux/srcu.h>
>
> #define MODULE_NAME "gzvm"
> #define GZVM_VCPU_MMAP_SIZE PAGE_SIZE
> @@ -26,6 +27,8 @@
> #define ERR_NOT_SUPPORTED (-24)
> #define ERR_NOT_IMPLEMENTED (-27)
> #define ERR_FAULT (-40)
> +#define GZVM_USERSPACE_IRQ_SOURCE_ID 0
> +#define GZVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID 1
>
> /**
> * The following data structures are for data transferring between driver and
> @@ -69,6 +72,7 @@ struct gzvm_vcpu {
> /* lock of vcpu*/
> struct mutex lock;
> struct gzvm_vcpu_run *run;
> + struct gzvm_vcpu_hwstate *hwstate;
> };
>
> struct gzvm {
> @@ -78,8 +82,23 @@ struct gzvm {
> struct gzvm_memslot memslot[GZVM_MAX_MEM_REGION];
> /* lock for list_add*/
> struct mutex lock;
> +
> + struct {
> + /* lock for irqfds list operation */
> + spinlock_t lock;
> + struct list_head items;
> + struct list_head resampler_list;
> + /* lock for irqfds resampler */
> + struct mutex resampler_lock;
> + } irqfds;
> +
> struct list_head vm_list;
> gzvm_id_t vm_id;
> +
> + struct hlist_head irq_ack_notifier_list;
> + struct srcu_struct irq_srcu;
> + /* lock for irq injection */
> + struct mutex irq_lock;
> };
>
> long gzvm_dev_ioctl_check_extension(struct gzvm *gzvm, unsigned long args);
> @@ -113,6 +132,14 @@ int gzvm_arch_create_device(gzvm_id_t vm_id, struct gzvm_create_device *gzvm_dev
> int gzvm_arch_inject_irq(struct gzvm *gzvm, unsigned int vcpu_idx, u32 irq_type,
> u32 irq, bool level);
>
> +void gzvm_notify_acked_irq(struct gzvm *gzvm, unsigned int gsi);
> +int gzvm_irqfd(struct gzvm *gzvm, struct gzvm_irqfd *args);
> +int gzvm_drv_irqfd_init(void);
> +void gzvm_drv_irqfd_exit(void);
> +int gzvm_vm_irqfd_init(struct gzvm *gzvm);
> +void gzvm_vm_irqfd_release(struct gzvm *gzvm);
> +void gzvm_sync_hwstate(struct gzvm_vcpu *vcpu);
> +
> extern struct miscdevice *gzvm_debug_dev;
>
> #endif /* __GZVM_DRV_H__ */
> diff --git a/include/uapi/linux/gzvm.h b/include/uapi/linux/gzvm.h
> index 279b52192366..3f1e829f855d 100644
> --- a/include/uapi/linux/gzvm.h
> +++ b/include/uapi/linux/gzvm.h
> @@ -224,4 +224,22 @@ struct gzvm_one_reg {
>
> #define GZVM_REG_GENERIC 0x0000000000000000ULL
>
> +#define GZVM_IRQFD_FLAG_DEASSIGN (1 << 0)
This is BIT(0)
> +/**
> + * GZVM_IRQFD_FLAG_RESAMPLE indicates resamplefd is valid and specifies
> + * the irqfd to operate in resampling mode for level triggered interrupt
> + * emulation.
> + */
> +#define GZVM_IRQFD_FLAG_RESAMPLE (1 << 1)
...and this is BIT(1).
> +
> +struct gzvm_irqfd {
...no documentation?
> + __u32 fd;
> + __u32 gsi;
> + __u32 flags;
> + __u32 resamplefd;
> + __u8 pad[16];
> +};
> +
> +#define GZVM_IRQFD _IOW(GZVM_IOC_MAGIC, 0x76, struct gzvm_irqfd)
> +
> #endif /* __GZVM_H__ */
Regards,
Angelo
More information about the linux-arm-kernel
mailing list