irqfd and ioeventfd support for mshv
Subsystems (6)
cover irqfd and ioeventfd support for mshv
This patch series adds irqfd and ioeventfd support for VMMs on Hyper-V. Also adds support for in-kernel MSI irq routing framework. Both these features are inspired from the kvm implementation and all credits to kvm developers.
Patches 1-11 are preparatory patches for enabling irqfd/ioeventfd. Hyper-v features like ports, connections, doorbell etc needs to be enabled to implement irqfd and ioeventfd feature.
Patches 12-15 implements irqfd and ioeventfd, and 16 and 17 implements the in-kernel MSI routing framework.
This patchset is rebased on Nuno's root partition ioctl interface series:
https://lkml.org/lkml/2021/5/28/820
---
Vineeth Pillai (17):
hyperv: Few TLFS definitions
drivers: hv: vmbus: Use TLFS definition for VMBUS_MESSAGE_SINT
acpi: export node_to_pxm
hyperv: Wrapper for setting proximity_domain_info
mshv: SynIC event ring and event flags support
mshv: SynIC port and connection hypercalls
hyperv: Configure SINT for Doorbell
mshv: Port id management
mshv: Doorbell handler in hypercall ISR
mshv: Doorbell register/unregister API
mshv: HvClearVirtualInterrupt hypercall
mshv: Add irqfd support for mshv
mshv: Add ioeventfd support for mshv
mshv: Notifier framework for EOI for level triggered interrupts
mshv: Level-triggered interrupt support for irqfd
mshv: User space controlled MSI irq routing for mshv
mshv: Use in kernel MSI routing for irqfd
arch/x86/hyperv/hv_init.c | 32 +- arch/x86/hyperv/hv_proc.c | 15 +- arch/x86/include/asm/hyperv-tlfs.h | 2 + arch/x86/include/asm/mshyperv.h | 2 + arch/x86/include/uapi/asm/hyperv-tlfs.h | 2 + drivers/acpi/numa/srat.c | 1 + drivers/hv/Kconfig | 1 + drivers/hv/Makefile | 3 +- drivers/hv/hv_call.c | 181 ++++++ drivers/hv/hv_eventfd.c | 723 ++++++++++++++++++++++++ drivers/hv/hv_portid_table.c | 83 +++ drivers/hv/hv_synic.c | 383 +++++++++++-- drivers/hv/hyperv_vmbus.h | 2 +- drivers/hv/mshv.h | 52 ++ drivers/hv/mshv_main.c | 96 +++- drivers/hv/mshv_msi.c | 128 +++++ include/asm-generic/hyperv-tlfs.h | 106 +++- include/asm-generic/mshyperv.h | 14 + include/linux/hyperv.h | 9 - include/linux/mshv.h | 65 ++- include/linux/mshv_eventfd.h | 78 +++ include/uapi/asm-generic/hyperv-tlfs.h | 81 +++ include/uapi/linux/mshv.h | 48 ++ 23 files changed, 2043 insertions(+), 64 deletions(-) create mode 100644 drivers/hv/hv_eventfd.c create mode 100644 drivers/hv/hv_portid_table.c create mode 100644 drivers/hv/mshv_msi.c create mode 100644 include/linux/mshv_eventfd.h
Signature · 1 line
2.25.1
PATCH 1/17 hyperv: Few TLFS definitions
Adding few TLFS definitions which would be used later in this patch series.
--- arch/x86/include/uapi/asm/hyperv-tlfs.h | 2 ++ include/asm-generic/hyperv-tlfs.h | 6 ++++++ include/uapi/asm-generic/hyperv-tlfs.h | 1 + 3 files changed, 9 insertions(+)
arch/x86/include/uapi/asm/hyperv-tlfs.h +2 -0
@@ -1003,6 +1003,8 @@ enum hv_interrupt_type {HV_X64_INTERRUPT_TYPE_MAXIMUM =0x000A }; +#define HV_INTERRUPT_VECTOR_NONE 0xFFFFFFFF +union hv_interrupt_control {struct {__u32 interrupt_type;/* enum hv_interrupt type */
include/asm-generic/hyperv-tlfs.h +6 -0
@@ -249,7 +249,13 @@ enum hv_status {/* Valid SynIC vectors are 16-255. */ #define HV_SYNIC_FIRST_VALID_VECTOR (16) +/* Hyper-V defined statically assigned SINTs */ #define HV_SYNIC_INTERCEPTION_SINT_INDEX 0x00000000 +#define HV_SYNIC_IOMMU_FAULT_SINT_INDEX 0x00000001 +#define HV_SYNIC_VMBUS_SINT_INDEX 0x00000002 +#define HV_SYNIC_HAL_HV_TIMER_SINT_INDEX 0x00000003 +#define HV_SYNIC_HVL_SHARED_SINT_INDEX 0x00000004 +#define HV_SYNIC_FIRST_UNUSED_SINT_INDEX 0x00000005#define HV_SYNIC_CONTROL_ENABLE (1ULL << 0)#define HV_SYNIC_SIMP_ENABLE (1ULL << 0)
include/uapi/asm-generic/hyperv-tlfs.h +1 -0
@@ -10,6 +10,7 @@#define HV_MESSAGE_SIZE (256)#define HV_MESSAGE_PAYLOAD_BYTE_COUNT (240)#define HV_MESSAGE_PAYLOAD_QWORD_COUNT (30) +#define HV_ANY_VP (0xFFFFFFFF)/* Define hypervisor message types. */ enum hv_message_type {
Signature · 1 line
2.25.1
PATCH 2/17 drivers: hv: vmbus: Use TLFS definition for VMBUS_MESSAGE_SINT
In reply to Vineeth PillaiUse the TLFS definition macro for VMBUS_MESSAGE_SINT
--- drivers/hv/hyperv_vmbus.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/hv/hyperv_vmbus.h +1 -1
@@ -112,7 +112,7 @@ enum {VMBUS_EVENT_PORT_ID =2 ,VMBUS_MONITOR_CONNECTION_ID =3 ,VMBUS_MONITOR_PORT_ID =3 , - VMBUS_MESSAGE_SINT = 2, + VMBUS_MESSAGE_SINT = HV_SYNIC_VMBUS_SINT_INDEX, };/*
Signature · 1 line
2.25.1
PATCH 3/17 acpi: export node_to_pxm
In reply to Vineeth PillaiThis is needed in the next patch in the series for a code refactor. No intended change in functionality.
--- drivers/acpi/numa/srat.c | 1 + 1 file changed, 1 insertion(+)
drivers/acpi/numa/srat.c +1 -0
@@ -48,6 +48,7 @@ int node_to_pxm(int node)return PXM_INVAL ;return node_to_pxm_map[node]; } +EXPORT_SYMBOL (node_to_pxm);static void __acpi_map_pxm_to_node (int pxm,int node) {
Signature · 1 line
2.25.1
PATCH 4/17 hyperv: Wrapper for setting proximity_domain_info
In reply to Vineeth PillaiRefactor the code to populate proximity_domain_info from numa node as a wrapper function. This wrapper is needed in future patches in this series. No intended change in functionality.
--- arch/x86/hyperv/hv_proc.c | 15 ++++----------- include/asm-generic/mshyperv.h | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 11 deletions(-)
arch/x86/hyperv/hv_proc.c +4 -11
@@ -3,7 +3,6 @@#include <linux/vmalloc.h> #include <linux/mm.h> #include <linux/clockchips.h> -#include <linux/acpi.h> #include <linux/hyperv.h> #include <linux/slab.h> #include <linux/cpuhotplug.h>
@@ -146,7 +145,6 @@ int hv_call_add_logical_proc(int node, u32 lp_index, u32 apic_id)u64 status;unsigned long flags;int ret =HV_STATUS_SUCCESS ; -int pxm =node_to_pxm (node);/* * When adding a logical processor, the hypervisor may return
@@ -163,10 +161,8 @@ int hv_call_add_logical_proc(int node, u32 lp_index, u32 apic_id) input->lp_index = lp_index; input->apic_id = apic_id; input->flags =0 ; - input->proximity_domain_info.domain_id = pxm; - input->proximity_domain_info.flags.reserved =0 ; - input->proximity_domain_info.flags.proximity_info_valid =1 ; - input->proximity_domain_info.flags.proximity_preferred =1 ; + input->proximity_domain_info = +numa_node_to_proximity_domain_info (node); status =hv_do_hypercall (HVCALL_ADD_LOGICAL_PROCESSOR , input, output);local_irq_restore (flags);
@@ -191,7 +187,6 @@ int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags)u64 status;unsigned long irq_flags;int ret =HV_STATUS_SUCCESS ; -int pxm =node_to_pxm (node);/* Root VPs don't seem to need pages deposited */ if (partition_id != hv_current_partition_id) {
@@ -211,10 +206,8 @@ int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags) input->flags = flags; input->subnode_type = HvSubnodeAny;if (node !=NUMA_NO_NODE ) { - input->proximity_domain_info.domain_id = pxm; - input->proximity_domain_info.flags.reserved =0 ; - input->proximity_domain_info.flags.proximity_info_valid =1 ; - input->proximity_domain_info.flags.proximity_preferred =1 ; + input->proximity_domain_info = +numa_node_to_proximity_domain_info (node); }else { input->proximity_domain_info.as_uint64 =0 ; }
include/asm-generic/mshyperv.h +14 -0
@@ -21,10 +21,24 @@#include <linux/types.h> #include <linux/atomic.h> #include <linux/bitops.h> +#include <acpi/acpi_numa.h> #include <linux/cpumask.h> #include <asm/ptrace.h> #include <asm/hyperv-tlfs.h> +static inline union hv_proximity_domain_info +numa_node_to_proximity_domain_info (int node) +{ +union hv_proximity_domain_info proximity_domain_info; + + proximity_domain_info.domain_id =node_to_pxm (node); + proximity_domain_info.flags.reserved =0 ; + proximity_domain_info.flags.proximity_info_valid =1 ; + proximity_domain_info.flags.proximity_preferred =1 ; + +return proximity_domain_info; +} +struct ms_hyperv_info {u32 features;u32 priv_high;
Signature · 1 line
2.25.1
PATCH 5/17 mshv: SynIC event ring and event flags support
In reply to Vineeth PillaiDoorbell is a mechanism by which a parent partition can register for notification if a specified mmio address is touched by a child partition. Parent partition can setup the notification by specifying mmio address, size of the data written(1/2/4/8 bytes) and optionally the data as well.
Hyper-V makes use of an event ring buffer to signal events. This buffer is implemented as a GPA overlay page. Doorbell notifications are delivered via this event ring buffer.
Enable SynIC event ring buffer.
While at it, enable SynIC event flags. It is a lightweight inter-partition communication mechanism to signal events between partitions or from hypervisor to a partition.
--- arch/x86/include/asm/hyperv-tlfs.h | 2 + drivers/hv/hv_synic.c | 81 +++++++++++++++++++++++++++--- drivers/hv/mshv_main.c | 6 +-- include/asm-generic/hyperv-tlfs.h | 29 +++++++++++ include/linux/mshv.h | 8 ++- 5 files changed, 115 insertions(+), 11 deletions(-)
arch/x86/include/asm/hyperv-tlfs.h +2 -0
@@ -189,6 +189,7 @@ enum hv_isolation_type {#define HV_REGISTER_SIEFP 0x40000082#define HV_REGISTER_SIMP 0x40000083#define HV_REGISTER_EOM 0x40000084 +#define HV_REGISTER_SIRBP 0x40000085#define HV_REGISTER_SINT0 0x40000090#define HV_REGISTER_SINT1 0x40000091#define HV_REGISTER_SINT2 0x40000092
@@ -252,6 +253,7 @@ enum hv_isolation_type {#define HV_X64_MSR_SIEFP HV_REGISTER_SIEFP#define HV_X64_MSR_VP_INDEX HV_REGISTER_VP_INDEX#define HV_X64_MSR_EOM HV_REGISTER_EOM +#define HV_X64_MSR_SIRBP HV_REGISTER_SIRBP#define HV_X64_MSR_SINT0 HV_REGISTER_SINT0#define HV_X64_MSR_SINT15 HV_REGISTER_SINT15#define HV_X64_MSR_CRASH_P0 HV_REGISTER_CRASH_P0
drivers/hv/hv_synic.c +74 -7
124 lines not on this page · raw message
drivers/hv/mshv_main.c +3 -3
@@ -1118,8 +1118,8 @@ __init mshv_init(void)return ret; } - mshv.synic_message_page = alloc_percpu(struct hv_message_page *); -if (!mshv.synic_message_page) { + mshv.synic_pages = alloc_percpu(struct hv_synic_pages); +if (!mshv.synic_pages) {pr_err ("%s: failed to allocate percpu synic page\n" , __func__);misc_deregister (&mshv_dev);return -ENOMEM ;
@@ -1144,7 +1144,7 @@ static void__exit mshv_exit (void ) {cpuhp_remove_state (mshv_cpuhp_online); -free_percpu (mshv.synic_message_page); +free_percpu (mshv.synic_pages);misc_deregister (&mshv_dev); }
include/asm-generic/hyperv-tlfs.h +29 -0
@@ -279,8 +279,23 @@ struct hv_timer_message_payload {__u64 delivery_time;/* When the message was delivered */ } __packed; +/* Define the synthentic interrupt controller event ring format */ +#define HV_SYNIC_EVENT_RING_MESSAGE_COUNT 63 + +struct hv_synic_event_ring { +u8 signal_masked; +u8 ring_full; +u16 reserved_z; +u32 data[HV_SYNIC_EVENT_RING_MESSAGE_COUNT ]; +} __packed; + +struct hv_synic_event_ring_page { +volatile struct hv_synic_event_ring sint_event_ring[HV_SYNIC_SINT_COUNT ]; +}; +/* Define synthetic interrupt controller flag constants. */ #define HV_EVENT_FLAGS_COUNT (256 * 8) +#define HV_EVENT_FLAGS_BYTE_COUNT (256)#define HV_EVENT_FLAGS_LONG_COUNT (256 / sizeof(unsigned long))/*
@@ -304,9 +319,14 @@ union hv_stimer_config {/* Define the synthetic interrupt controller event flags format. */ union hv_synic_event_flags { +unsigned char flags8[HV_EVENT_FLAGS_BYTE_COUNT ];unsigned long flags[HV_EVENT_FLAGS_LONG_COUNT ]; }; +struct hv_synic_event_flags_page { +volatile union hv_synic_event_flags event_flags[HV_SYNIC_SINT_COUNT ]; +}; +/* Define SynIC control register. */ union hv_synic_scontrol {u64 as_uint64;
@@ -349,6 +369,15 @@ union hv_synic_siefp { } __packed; }; +union hv_synic_sirbp { +u64 as_uint64; +struct { +u64 sirbp_enabled:1 ; +u64 preserved:11 ; +u64 base_sirbp_gpa:52 ; + } __packed; +}; +struct hv_vpset {u64 format;u64 valid_bank_mask;
include/linux/mshv.h +7 -1
@@ -49,8 +49,14 @@ struct mshv_partition { } vps; }; +struct hv_synic_pages { +struct hv_message_page *synic_message_page; +struct hv_synic_event_flags_page *synic_event_flags_page; +struct hv_synic_event_ring_page *synic_event_ring_page; +}; +struct mshv { -struct hv_message_page __percpu **synic_message_page; +struct hv_synic_pages __percpu *synic_pages;struct {spinlock_t lock;u64 count;
Signature · 1 line
2.25.1
PATCH 6/17 mshv: SynIC port and connection hypercalls
In reply to Vineeth PillaiHyper-V enables inter-partition communication through the port and connection constructs. More details about ports and connections in TLFS chapter 11.
Implement hypercalls related to ports and connections for enabling inter-partiion communication.
--- drivers/hv/hv_call.c | 161 +++++++++++++++++++++++++ drivers/hv/mshv.h | 12 ++ include/asm-generic/hyperv-tlfs.h | 55 +++++++++ include/linux/hyperv.h | 9 -- include/uapi/asm-generic/hyperv-tlfs.h | 76 ++++++++++++ 5 files changed, 304 insertions(+), 9 deletions(-)
drivers/hv/hv_call.c +161 -0
@@ -742,3 +742,164 @@ int hv_call_translate_virtual_address(return hv_status_to_errno (status); } + +int +hv_call_create_port (u64 port_partition_id,union hv_port_id port_id, +u64 connection_partition_id, +struct hv_port_info *port_info, +u8 port_vtl,u8 min_connection_vtl,int node) +{ +struct hv_create_port *input; +unsigned long flags; +int ret =0 ; +int status; + +do { +local_irq_save (flags); + input = (struct hv_create_port *)(*this_cpu_ptr ( + hyperv_pcpu_input_arg)); +memset (input,0 ,sizeof (*input)); + + input->port_partition_id = port_partition_id; + input->port_id = port_id; + input->connection_partition_id = connection_partition_id; + input->port_info = *port_info; + input->port_vtl = port_vtl; + input->min_connection_vtl = min_connection_vtl; + input->proximity_domain_info = +numa_node_to_proximity_domain_info (node); + status =hv_do_hypercall (HVCALL_CREATE_PORT , input, +NULL ) &HV_HYPERCALL_RESULT_MASK ; +local_irq_restore (flags); +if (status ==HV_STATUS_SUCCESS ) +break ; + +if (status !=HV_STATUS_INSUFFICIENT_MEMORY ) { +pr_err ("%s: %s\n" , + __func__,hv_status_to_string (status)); + ret = -hv_status_to_errno (status); +break ; + } + ret =hv_call_deposit_pages (NUMA_NO_NODE , + port_partition_id,1 ); + + }while (!ret); + +return ret; +} + +int +hv_call_delete_port (u64 port_partition_id,union hv_port_id port_id) +{ +union hv_delete_port input = {0 }; +unsigned long flags; +int status; + +local_irq_save (flags); + input.port_partition_id = port_partition_id; + input.port_id = port_id; + status =hv_do_fast_hypercall16 (HVCALL_DELETE_PORT , + input.as_uint64[0 ], + input.as_uint64[1 ]) & +HV_HYPERCALL_RESULT_MASK ; +local_irq_restore (flags); + +if (status !=HV_STATUS_SUCCESS ) { +pr_err ("%s: %s\n" , __func__,hv_status_to_string (status)); +return -hv_status_to_errno (status); + } + +return 0 ; +} + +int +hv_call_connect_port (u64 port_partition_id,union hv_port_id port_id, +u64 connection_partition_id, +union hv_connection_id connection_id, +struct hv_connection_info *connection_info, +u8 connection_vtl,int node) +{ +struct hv_connect_port *input; +unsigned long flags; +int ret =0 , status; + +do { +local_irq_save (flags); + input = (struct hv_connect_port *)(*this_cpu_ptr ( + hyperv_pcpu_input_arg)); +memset (input,0 ,sizeof (*input)); + input->port_partition_id = port_partition_id; + input->port_id = port_id; + input->connection_partition_id = connection_partition_id; + input->connection_id = connection_id; + input->connection_info = *connection_info; + input->connection_vtl = connection_vtl; + input->proximity_domain_info = +numa_node_to_proximity_domain_info (node); + status =hv_do_hypercall (HVCALL_CONNECT_PORT , input, +NULL ) &HV_HYPERCALL_RESULT_MASK ; + +local_irq_restore (flags); +if (status ==HV_STATUS_SUCCESS ) +break ; + +if (status !=HV_STATUS_INSUFFICIENT_MEMORY ) { +pr_err ("%s: %s\n" , + __func__,hv_status_to_string (status)); + ret = -hv_status_to_errno (status); +break ; + } + ret =hv_call_deposit_pages (NUMA_NO_NODE , + connection_partition_id,1 ); + }while (!ret); + +return ret; +} + +int +hv_call_disconnect_port (u64 connection_partition_id, +union hv_connection_id connection_id) +{ +union hv_disconnect_port input = {0 }; +unsigned long flags; +int status; + +local_irq_save (flags); + input.connection_partition_id = connection_partition_id; + input.connection_id = connection_id; + input.is_doorbell =1 ; + status =hv_do_fast_hypercall16 (HVCALL_DISCONNECT_PORT , + input.as_uint64[0 ], + input.as_uint64[1 ]) & +HV_HYPERCALL_RESULT_MASK ; +local_irq_restore (flags); + +if (status !=HV_STATUS_SUCCESS ) { +pr_err ("%s: %s\n" , __func__,hv_status_to_string (status)); +return -hv_status_to_errno (status); + } + +return 0 ; +} + +int +hv_call_notify_port_ring_empty (u32 sint_index) +{ +union hv_notify_port_ring_empty input = {0 }; +unsigned long flags; +int status; + +local_irq_save (flags); + input.sint_index = sint_index; + status =hv_do_fast_hypercall8 (HVCALL_NOTIFY_PORT_RING_EMPTY , + input.as_uint64) & +HV_HYPERCALL_RESULT_MASK ; +local_irq_restore (flags); + +if (status !=HV_STATUS_SUCCESS ) { +pr_err ("%s: %s\n" , __func__,hv_status_to_string (status)); +return -hv_status_to_errno (status); + } + +return 0 ; +}
drivers/hv/mshv.h +12 -0
@@ -117,4 +117,16 @@ int hv_call_translate_virtual_address(u64 *gpa, union hv_translate_gva_result *result); +int hv_call_create_port (u64 port_partition_id,union hv_port_id port_id, +u64 connection_partition_id,struct hv_port_info *port_info, +u8 port_vtl,u8 min_connection_vtl,int node); +int hv_call_delete_port (u64 port_partition_id,union hv_port_id port_id); +int hv_call_connect_port (u64 port_partition_id,union hv_port_id port_id, +u64 connection_partition_id, +union hv_connection_id connection_id, +struct hv_connection_info *connection_info, +u8 connection_vtl,int node); +int hv_call_disconnect_port (u64 connection_partition_id, +union hv_connection_id connection_id); +int hv_call_notify_port_ring_empty (u32 sint_index);#endif /* _MSHV_H */
include/asm-generic/hyperv-tlfs.h +55 -0
72 lines not on this page · raw message
include/linux/hyperv.h +0 -9
@@ -750,15 +750,6 @@ struct vmbus_close_msg {struct vmbus_channel_close_channel msg; }; -/* Define connection identifier type. */ -union hv_connection_id { -u32 asu32; -struct { -u32 id:24 ; -u32 reserved:8 ; - } u; -}; -enum vmbus_device_type {HV_IDE =0 ,HV_SCSI ,
include/uapi/asm-generic/hyperv-tlfs.h +76 -0
88 lines not on this page · raw message
Signature · 1 line
2.25.1
Re: [PATCH 06/17] mshv: SynIC port and connection hypercalls
Quoted 50 lines · Vineeth Pillai
Hyper-V enables inter-partition communication through the port and connection constructs. More details about ports and connections in TLFS chapter 11.
Implement hypercalls related to ports and connections for enabling inter-partiion communication.
Signed-off-by Vineeth Pillai <[email protected]>--- drivers/hv/hv_call.c | 161 +++++++++++++++++++++++++ drivers/hv/mshv.h | 12 ++ include/asm-generic/hyperv-tlfs.h | 55 +++++++++ include/linux/hyperv.h | 9 -- include/uapi/asm-generic/hyperv-tlfs.h | 76 ++++++++++++ 5 files changed, 304 insertions(+), 9 deletions(-)drivers/hv/hv_call.c +26
-0@@ -742,3 +742,164 @@ int hv_call_translate_virtual_address(return hv_status_to_errno (status); } + +int +hv_call_create_port (u64 port_partition_id,union hv_port_id port_id, +u64 connection_partition_id, +struct hv_port_info *port_info, +u8 port_vtl,u8 min_connection_vtl,int node) +{ +struct hv_create_port *input; +unsigned long flags; +int ret =0 ; +int status; + + do { + local_irq_save(flags); + input = (struct hv_create_port *)(*this_cpu_ptr( + hyperv_pcpu_input_arg)); + memset(input, 0, sizeof(*input)); + + input->port_partition_id = port_partition_id; + input->port_id = port_id; + input->connection_partition_id = connection_partition_id; + input->port_info = *port_info; + input->port_vtl = port_vtl; + input->min_connection_vtl = min_connection_vtl; + input->proximity_domain_info = + numa_node_to_proximity_domain_info(node);
drivers/hv/hv_call.c int hv_call_translate_virtual_address( · line 780
Quoted 10 lines
drivers/hv/hv_call.c +10
-0+ status =hv_do_hypercall (HVCALL_CREATE_PORT , input, +NULL ) &HV_HYPERCALL_RESULT_MASK ; +local_irq_restore (flags); +if (status ==HV_STATUS_SUCCESS ) +break ; + +if (status !=HV_STATUS_INSUFFICIENT_MEMORY ) { +pr_err ("%s: %s\n" , + __func__,hv_status_to_string (status)); + ret = -hv_status_to_errno (status);
In Nuno's "x86/hyperv: convert hyperv statuses to linux error codes" patch, hv_status_to_errno() already returns negatives:
+int hv_status_to_errno(u64 hv_status)
+{
+ switch (hv_result(hv_status)) {
+ case HV_STATUS_SUCCESS:
+ return 0;
+ case HV_STATUS_INVALID_PARAMETER: + case HV_STATUS_UNKNOWN_PROPERTY: + case HV_STATUS_PROPERTY_VALUE_OUT_OF_RANGE: + case HV_STATUS_INVALID_VP_INDEX: + case HV_STATUS_INVALID_REGISTER_VALUE: + case HV_STATUS_INVALID_LP_INDEX:
+ return -EINVAL;
+ case HV_STATUS_ACCESS_DENIED:
+ case HV_STATUS_OPERATION_DENIED:
+ return -EACCES;
+ case HV_STATUS_NOT_ACKNOWLEDGED: + case HV_STATUS_INVALID_VP_STATE: + case HV_STATUS_INVALID_PARTITION_STATE:
+ return -EBADFD;
+ }
+ return -ENOTRECOVERABLE;
+}
+EXPORT_SYMBOL_GPL(hv_status_to_errno);
+
Quoted 339 lines
drivers/hv/hv_call.c +125
-0+break ; + } + ret =hv_call_deposit_pages (NUMA_NO_NODE , + port_partition_id,1 ); + + }while (!ret); + +return ret; +} + +int +hv_call_delete_port (u64 port_partition_id,union hv_port_id port_id) +{ +union hv_delete_port input = {0 }; +unsigned long flags; +int status; + +local_irq_save (flags); + input.port_partition_id = port_partition_id; + input.port_id = port_id; + status =hv_do_fast_hypercall16 (HVCALL_DELETE_PORT , + input.as_uint64[0 ], + input.as_uint64[1 ]) & +HV_HYPERCALL_RESULT_MASK ; +local_irq_restore (flags); + +if (status !=HV_STATUS_SUCCESS ) { +pr_err ("%s: %s\n" , __func__,hv_status_to_string (status)); +return -hv_status_to_errno (status); + } + +return 0 ; +} + +int +hv_call_connect_port (u64 port_partition_id,union hv_port_id port_id, +u64 connection_partition_id, +union hv_connection_id connection_id, +struct hv_connection_info *connection_info, +u8 connection_vtl,int node) +{ +struct hv_connect_port *input; +unsigned long flags; +int ret =0 , status; + +do { +local_irq_save (flags); + input = (struct hv_connect_port *)(*this_cpu_ptr ( + hyperv_pcpu_input_arg)); +memset (input,0 ,sizeof (*input)); + input->port_partition_id = port_partition_id; + input->port_id = port_id; + input->connection_partition_id = connection_partition_id; + input->connection_id = connection_id; + input->connection_info = *connection_info; + input->connection_vtl = connection_vtl; + input->proximity_domain_info = +numa_node_to_proximity_domain_info (node); + status =hv_do_hypercall (HVCALL_CONNECT_PORT , input, +NULL ) &HV_HYPERCALL_RESULT_MASK ; + +local_irq_restore (flags); +if (status ==HV_STATUS_SUCCESS ) +break ; + +if (status !=HV_STATUS_INSUFFICIENT_MEMORY ) { +pr_err ("%s: %s\n" , + __func__,hv_status_to_string (status)); + ret = -hv_status_to_errno (status); +break ; + } + ret =hv_call_deposit_pages (NUMA_NO_NODE , + connection_partition_id,1 ); + }while (!ret); + +return ret; +} + +int +hv_call_disconnect_port (u64 connection_partition_id, +union hv_connection_id connection_id) +{ +union hv_disconnect_port input = {0 }; +unsigned long flags; +int status; + +local_irq_save (flags); + input.connection_partition_id = connection_partition_id; + input.connection_id = connection_id; + input.is_doorbell =1 ; + status =hv_do_fast_hypercall16 (HVCALL_DISCONNECT_PORT , + input.as_uint64[0 ], + input.as_uint64[1 ]) & +HV_HYPERCALL_RESULT_MASK ; +local_irq_restore (flags); + +if (status !=HV_STATUS_SUCCESS ) { +pr_err ("%s: %s\n" , __func__,hv_status_to_string (status)); +return -hv_status_to_errno (status); + } + +return 0 ; +} + +int +hv_call_notify_port_ring_empty (u32 sint_index) +{ +union hv_notify_port_ring_empty input = {0 }; +unsigned long flags; +int status; + +local_irq_save (flags); + input.sint_index = sint_index; + status =hv_do_fast_hypercall8 (HVCALL_NOTIFY_PORT_RING_EMPTY , + input.as_uint64) & +HV_HYPERCALL_RESULT_MASK ; +local_irq_restore (flags); + +if (status !=HV_STATUS_SUCCESS ) { +pr_err ("%s: %s\n" , __func__,hv_status_to_string (status)); +return -hv_status_to_errno (status); + } + +return 0 ; +}drivers/hv/mshv.h +12
-0@@ -117,4 +117,16 @@ int hv_call_translate_virtual_address(u64 *gpa, union hv_translate_gva_result *result); +int hv_call_create_port (u64 port_partition_id,union hv_port_id port_id, +u64 connection_partition_id,struct hv_port_info *port_info, +u8 port_vtl,u8 min_connection_vtl,int node); +int hv_call_delete_port (u64 port_partition_id,union hv_port_id port_id); +int hv_call_connect_port (u64 port_partition_id,union hv_port_id port_id, +u64 connection_partition_id, +union hv_connection_id connection_id, +struct hv_connection_info *connection_info, +u8 connection_vtl,int node); +int hv_call_disconnect_port (u64 connection_partition_id, +union hv_connection_id connection_id); +int hv_call_notify_port_ring_empty (u32 sint_index);#endif /* _MSHV_H */ include/asm-generic/hyperv-tlfs.h +55
-0@@ -159,6 +159,8 @@ struct ms_hyperv_tsc_page {#define HVCALL_GET_VP_REGISTERS 0x0050#define HVCALL_SET_VP_REGISTERS 0x0051#define HVCALL_TRANSLATE_VIRTUAL_ADDRESS 0x0052 +#define HVCALL_DELETE_PORT 0x0058 +#define HVCALL_DISCONNECT_PORT 0x005b#define HVCALL_POST_MESSAGE 0x005c#define HVCALL_SIGNAL_EVENT 0x005d#define HVCALL_POST_DEBUG_DATA 0x0069@@ -168,7 +170,10 @@ struct ms_hyperv_tsc_page {#define HVCALL_MAP_DEVICE_INTERRUPT 0x007c#define HVCALL_UNMAP_DEVICE_INTERRUPT 0x007d#define HVCALL_RETARGET_INTERRUPT 0x007e +#define HVCALL_NOTIFY_PORT_RING_EMPTY 0x008b#define HVCALL_ASSERT_VIRTUAL_INTERRUPT 0x0094 +#define HVCALL_CREATE_PORT 0x0095 +#define HVCALL_CONNECT_PORT 0x0096#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_SPACE 0x00af#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST 0x00b0#define HVCALL_MAP_VP_STATE_PAGE 0x00e1@@ -949,4 +954,54 @@ struct hv_translate_virtual_address_out {u64 gpa_page; } __packed; +struct hv_create_port { +u64 port_partition_id; +union hv_port_id port_id; +u8 port_vtl; +u8 min_connection_vtl; +u16 padding; +u64 connection_partition_id; +struct hv_port_info port_info; +union hv_proximity_domain_info proximity_domain_info; +} __packed; + +union hv_delete_port { +u64 as_uint64[2 ]; +struct { +u64 port_partition_id; +union hv_port_id port_id; +u32 reserved; + } __packed; +}; + +union hv_notify_port_ring_empty { +u64 as_uint64; +struct { +u32 sint_index; +u32 reserved; + } __packed; +}; + +struct hv_connect_port { +u64 connection_partition_id; +union hv_connection_id connection_id; +u8 connection_vtl; +u8 rsvdz0; +u16 rsvdz1; +u64 port_partition_id; +union hv_port_id port_id; +u32 reserved2; +struct hv_connection_info connection_info; +union hv_proximity_domain_info proximity_domain_info; +} __packed; + +union hv_disconnect_port { +u64 as_uint64[2 ]; +struct { +u64 connection_partition_id; +union hv_connection_id connection_id; +u32 is_doorbell:1 ; +u32 reserved:31 ; + } __packed; +};#endif include/linux/hyperv.h +0
-9@@ -750,15 +750,6 @@ struct vmbus_close_msg {struct vmbus_channel_close_channel msg; }; -/* Define connection identifier type. */ -union hv_connection_id { -u32 asu32; -struct { -u32 id:24 ; -u32 reserved:8 ; - } u; -}; -enum vmbus_device_type {HV_IDE =0 ,HV_SCSI ,include/uapi/asm-generic/hyperv-tlfs.h +76
-0@@ -53,6 +53,25 @@ union hv_message_flags { } __packed; }; +enum hv_port_type { +HV_PORT_TYPE_MESSAGE =1 , +HV_PORT_TYPE_EVENT =2 , +HV_PORT_TYPE_MONITOR =3 , +HV_PORT_TYPE_DOORBELL =4 // Root Partition only +}; + + +/* +* Doorbell connection_info flags. +*/ +#define HV_DOORBELL_FLAG_TRIGGER_SIZE_MASK 0x00000007 +#define HV_DOORBELL_FLAG_TRIGGER_SIZE_ANY 0x00000000 +#define HV_DOORBELL_FLAG_TRIGGER_SIZE_BYTE 0x00000001 +#define HV_DOORBELL_FLAG_TRIGGER_SIZE_WORD 0x00000002 +#define HV_DOORBELL_FLAG_TRIGGER_SIZE_DWORD 0x00000003 +#define HV_DOORBELL_FLAG_TRIGGER_SIZE_QWORD 0x00000004 +#define HV_DOORBELL_FLAG_TRIGGER_ANY_VALUE 0x80000000 +/* Define port identifier type. */ union hv_port_id {__u32 asu32;@@ -62,6 +81,63 @@ union hv_port_id { }__packed u; }; +struct hv_port_info { +enum hv_port_type port_type; +__u32 padding; +union { +struct { +__u32 target_sint; +__u32 target_vp; +__u64 rsvdz; + } message_port_info; +struct { +__u32 target_sint; +__u32 target_vp; +__u16 base_flag_number; +__u16 flag_count; +__u32 rsvdz; + } event_port_info; +struct { +__u64 monitor_address; +__u64 rsvdz; + } monitor_port_info; +struct { +__u32 target_sint; +__u32 target_vp; +__u64 rsvdz; + } doorbell_port_info; + }; +}; + +union hv_connection_id { +__u32 asu32; +struct { +__u32 id:24 ; +__u32 reserved:8 ; + } u; +}; + +struct hv_connection_info { +enum hv_port_type port_type; +__u32 padding; +union { +struct { +__u64 rsvdz; + } message_connection_info; +struct { +__u64 rsvdz; + } event_connection_info; +struct { +__u64 monitor_address; + } monitor_connection_info; +struct { +__u64 gpa; +__u64 trigger_value; +__u64 flags; + } doorbell_connection_info; + }; +}; +/* Define synthetic interrupt controller message header. */ struct hv_message_header {__u32 message_type;
Signature · 1 line
Vitaly
Wei Liu
maintainerRe: [PATCH 06/17] mshv: SynIC port and connection hypercalls
Quoted 57 lines · Vitaly Kuznetsov
Quoted 55 lines · Vineeth Pillai
Hyper-V enables inter-partition communication through the port and connection constructs. More details about ports and connections in TLFS chapter 11.
Implement hypercalls related to ports and connections for enabling inter-partiion communication.
Signed-off-by Vineeth Pillai <[email protected]>--- drivers/hv/hv_call.c | 161 +++++++++++++++++++++++++ drivers/hv/mshv.h | 12 ++ include/asm-generic/hyperv-tlfs.h | 55 +++++++++ include/linux/hyperv.h | 9 -- include/uapi/asm-generic/hyperv-tlfs.h | 76 ++++++++++++ 5 files changed, 304 insertions(+), 9 deletions(-)drivers/hv/hv_call.c +31
-0@@ -742,3 +742,164 @@ int hv_call_translate_virtual_address(return hv_status_to_errno (status); } + +int +hv_call_create_port (u64 port_partition_id,union hv_port_id port_id, +u64 connection_partition_id, +struct hv_port_info *port_info, +u8 port_vtl,u8 min_connection_vtl,int node) +{ +struct hv_create_port *input; +unsigned long flags; +int ret =0 ; +int status; + + do { + local_irq_save(flags); + input = (struct hv_create_port *)(*this_cpu_ptr( + hyperv_pcpu_input_arg)); + memset(input, 0, sizeof(*input)); + + input->port_partition_id = port_partition_id; + input->port_id = port_id; + input->connection_partition_id = connection_partition_id; + input->port_info = *port_info; + input->port_vtl = port_vtl; + input->min_connection_vtl = min_connection_vtl; + input->proximity_domain_info = + numa_node_to_proximity_domain_info(node); + status = hv_do_hypercall(HVCALL_CREATE_PORT, input, + NULL) & HV_HYPERCALL_RESULT_MASK; + local_irq_restore(flags); + if (status == HV_STATUS_SUCCESS) + break;
Quoted 8 lines
drivers/hv/hv_call.c int hv_call_translate_virtual_address( · line 780
Quoted 5 lines
drivers/hv/hv_call.c +5
-0+ +if (status !=HV_STATUS_INSUFFICIENT_MEMORY ) { +pr_err ("%s: %s\n" , + __func__,hv_status_to_string (status)); + ret = -hv_status_to_errno (status);In Nuno's "x86/hyperv: convert hyperv statuses to linux error codes" patch,
hv_status_to_errno()already returns negatives:
Yes, this needs to be fixed otherwise one of the following patch has the error handling check reversed.
Wei.
Re: [PATCH 06/17] mshv: SynIC port and connection hypercalls
Quoted 9 lines · Wei Liu
Quoted 7 lines
drivers/hv/hv_call.c int hv_call_translate_virtual_address( · line 780
Quoted 5 lines
drivers/hv/hv_call.c +4
-0+if (status !=HV_STATUS_INSUFFICIENT_MEMORY ) { +pr_err ("%s: %s\n" , + __func__,hv_status_to_string (status)); + ret = -hv_status_to_errno (status);In Nuno's "x86/hyperv: convert hyperv statuses to linux error codes" patch,
hv_status_to_errno()already returns negatives:Yes, this needs to be fixed otherwise one of the following patch has the error handling check reversed.
Sorry I missed replying to this. Thanks Vitaly and Wei, I will have this fixed in the next iteration.
~Vineeth
Wei Liu
maintainerRe: [PATCH 06/17] mshv: SynIC port and connection hypercalls
In reply to Vineeth PillaiQuoted 8 lines · Vineeth Pillai
Hyper-V enables inter-partition communication through the port and connection constructs. More details about ports and connections in TLFS chapter 11.
Implement hypercalls related to ports and connections for enabling inter-partiion communication.
Signed-off-by Vineeth Pillai <[email protected]>
Vineeth, feel free to squash the following patch.
---8<---
From afb9ab422895364216acb4261399f6f5154eea17 Mon Sep 17 00:00:00 2001
From: Wei Liu <[email protected]>
Date: Tue, 29 Jun 2021 12:58:47 +0000
Subject: [PATCH] fixup! mshv: SynIC port and connection hypercalls
--- drivers/hv/hv_call.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
drivers/hv/hv_call.c +5 -5
@@ -797,7 +797,7 @@ hv_call_create_port(u64 port_partition_id, union hv_port_id port_id,if (status !=HV_STATUS_INSUFFICIENT_MEMORY ) {pr_err ("%s: %s\n" , __func__,hv_status_to_string (status)); - ret = -hv_status_to_errno (status); + ret =hv_status_to_errno (status);break ; } ret = hv_call_deposit_pages(NUMA_NO_NODE,
@@ -826,7 +826,7 @@ hv_call_delete_port(u64 port_partition_id, union hv_port_id port_id)if (status !=HV_STATUS_SUCCESS ) {pr_err ("%s: %s\n" , __func__,hv_status_to_string (status)); -return -hv_status_to_errno (status); +return hv_status_to_errno (status); }return 0 ;
@@ -866,7 +866,7 @@ hv_call_connect_port(u64 port_partition_id, union hv_port_id port_id,if (status !=HV_STATUS_INSUFFICIENT_MEMORY ) {pr_err ("%s: %s\n" , __func__,hv_status_to_string (status)); - ret = -hv_status_to_errno (status); + ret =hv_status_to_errno (status);break ; } ret = hv_call_deposit_pages(NUMA_NO_NODE,
@@ -896,7 +896,7 @@ hv_call_disconnect_port(u64 connection_partition_id,if (status !=HV_STATUS_SUCCESS ) {pr_err ("%s: %s\n" , __func__,hv_status_to_string (status)); -return -hv_status_to_errno (status); +return hv_status_to_errno (status); }return 0 ;
@@ -918,7 +918,7 @@ hv_call_notify_port_ring_empty(u32 sint_index)if (status !=HV_STATUS_SUCCESS ) {pr_err ("%s: %s\n" , __func__,hv_status_to_string (status)); -return -hv_status_to_errno (status); +return hv_status_to_errno (status); }return 0 ;
Signature · 1 line
2.30.2
Re: [PATCH 06/17] mshv: SynIC port and connection hypercalls
Quoted 10 lines · Wei Liu
Quoted 8 lines · Vineeth Pillai
Hyper-V enables inter-partition communication through the port and connection constructs. More details about ports and connections in TLFS chapter 11.
Implement hypercalls related to ports and connections for enabling inter-partiion communication.
Signed-off-by Vineeth Pillai <[email protected]>Vineeth, feel free to squash the following patch.
Thanks Wei, I will have this in the next iteration.
~Vineeth
Quoted 61 lines
---8<---
Quoted 1 line
From
afb9ab422895364216acb4261399f6f5154eea17Mon Sep 17 00:00:00 2001From: Wei Liu <[email protected]>
Date: Tue, 29 Jun 2021 12:58:47 +0000
Subject: [PATCH] fixup! mshv: SynIC port and connection hypercallsSigned-off-by Wei Liu <[email protected]>--- drivers/hv/hv_call.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)drivers/hv/hv_call.c +5
-5@@ -797,7 +797,7 @@ hv_call_create_port(u64 port_partition_id, union hv_port_id port_id,if (status !=HV_STATUS_INSUFFICIENT_MEMORY ) {pr_err ("%s: %s\n" , __func__,hv_status_to_string (status)); - ret = -hv_status_to_errno (status); + ret =hv_status_to_errno (status);break ; } ret = hv_call_deposit_pages(NUMA_NO_NODE,@@ -826,7 +826,7 @@ hv_call_delete_port(u64 port_partition_id, union hv_port_id port_id)if (status !=HV_STATUS_SUCCESS ) {pr_err ("%s: %s\n" , __func__,hv_status_to_string (status)); -return -hv_status_to_errno (status); +return hv_status_to_errno (status); }return 0 ;@@ -866,7 +866,7 @@ hv_call_connect_port(u64 port_partition_id, union hv_port_id port_id,if (status !=HV_STATUS_INSUFFICIENT_MEMORY ) {pr_err ("%s: %s\n" , __func__,hv_status_to_string (status)); - ret = -hv_status_to_errno (status); + ret =hv_status_to_errno (status);break ; } ret = hv_call_deposit_pages(NUMA_NO_NODE,@@ -896,7 +896,7 @@ hv_call_disconnect_port(u64 connection_partition_id,if (status !=HV_STATUS_SUCCESS ) {pr_err ("%s: %s\n" , __func__,hv_status_to_string (status)); -return -hv_status_to_errno (status); +return hv_status_to_errno (status); }return 0 ;@@ -918,7 +918,7 @@ hv_call_notify_port_ring_empty(u32 sint_index)if (status !=HV_STATUS_SUCCESS ) {pr_err ("%s: %s\n" , __func__,hv_status_to_string (status)); -return -hv_status_to_errno (status); +return hv_status_to_errno (status); }return 0 ;
Wei Liu
maintainerRe: [PATCH 06/17] mshv: SynIC port and connection hypercalls
In reply to Vineeth PillaiOn Wed, Jun 02, 2021 at 05:20:51PM +0000, Vineeth Pillai wrote:
[...]
Quoted 172 lines
drivers/hv/hv_call.c +21
-0+int +hv_call_notify_port_ring_empty (u32 sint_index) +{ +union hv_notify_port_ring_empty input = {0 }; +unsigned long flags; +int status; + +local_irq_save (flags); + input.sint_index = sint_index; + status =hv_do_fast_hypercall8 (HVCALL_NOTIFY_PORT_RING_EMPTY , + input.as_uint64) & +HV_HYPERCALL_RESULT_MASK ; +local_irq_restore (flags); + +if (status !=HV_STATUS_SUCCESS ) { +pr_err ("%s: %s\n" , __func__,hv_status_to_string (status)); +return -hv_status_to_errno (status); + } + +return 0 ; +}drivers/hv/mshv.h +12
-0@@ -117,4 +117,16 @@ int hv_call_translate_virtual_address(u64 *gpa, union hv_translate_gva_result *result); +int hv_call_create_port (u64 port_partition_id,union hv_port_id port_id, +u64 connection_partition_id,struct hv_port_info *port_info, +u8 port_vtl,u8 min_connection_vtl,int node); +int hv_call_delete_port (u64 port_partition_id,union hv_port_id port_id); +int hv_call_connect_port (u64 port_partition_id,union hv_port_id port_id, +u64 connection_partition_id, +union hv_connection_id connection_id, +struct hv_connection_info *connection_info, +u8 connection_vtl,int node); +int hv_call_disconnect_port (u64 connection_partition_id, +union hv_connection_id connection_id); +int hv_call_notify_port_ring_empty (u32 sint_index);#endif /* _MSHV_H */ include/asm-generic/hyperv-tlfs.h +55
-0@@ -159,6 +159,8 @@ struct ms_hyperv_tsc_page {#define HVCALL_GET_VP_REGISTERS 0x0050#define HVCALL_SET_VP_REGISTERS 0x0051#define HVCALL_TRANSLATE_VIRTUAL_ADDRESS 0x0052 +#define HVCALL_DELETE_PORT 0x0058 +#define HVCALL_DISCONNECT_PORT 0x005b#define HVCALL_POST_MESSAGE 0x005c#define HVCALL_SIGNAL_EVENT 0x005d#define HVCALL_POST_DEBUG_DATA 0x0069@@ -168,7 +170,10 @@ struct ms_hyperv_tsc_page {#define HVCALL_MAP_DEVICE_INTERRUPT 0x007c#define HVCALL_UNMAP_DEVICE_INTERRUPT 0x007d#define HVCALL_RETARGET_INTERRUPT 0x007e +#define HVCALL_NOTIFY_PORT_RING_EMPTY 0x008b#define HVCALL_ASSERT_VIRTUAL_INTERRUPT 0x0094 +#define HVCALL_CREATE_PORT 0x0095 +#define HVCALL_CONNECT_PORT 0x0096#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_SPACE 0x00af#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST 0x00b0#define HVCALL_MAP_VP_STATE_PAGE 0x00e1@@ -949,4 +954,54 @@ struct hv_translate_virtual_address_out {u64 gpa_page; } __packed; +struct hv_create_port { +u64 port_partition_id; +union hv_port_id port_id; +u8 port_vtl; +u8 min_connection_vtl; +u16 padding; +u64 connection_partition_id; +struct hv_port_info port_info; +union hv_proximity_domain_info proximity_domain_info; +} __packed; + +union hv_delete_port { +u64 as_uint64[2 ]; +struct { +u64 port_partition_id; +union hv_port_id port_id; +u32 reserved; + } __packed; +}; + +union hv_notify_port_ring_empty { +u64 as_uint64; +struct { +u32 sint_index; +u32 reserved; + } __packed; +}; + +struct hv_connect_port { +u64 connection_partition_id; +union hv_connection_id connection_id; +u8 connection_vtl; +u8 rsvdz0; +u16 rsvdz1; +u64 port_partition_id; +union hv_port_id port_id; +u32 reserved2; +struct hv_connection_info connection_info; +union hv_proximity_domain_info proximity_domain_info; +} __packed; + +union hv_disconnect_port { +u64 as_uint64[2 ]; +struct { +u64 connection_partition_id; +union hv_connection_id connection_id; +u32 is_doorbell:1 ; +u32 reserved:31 ; + } __packed; +};#endif include/linux/hyperv.h +0
-9@@ -750,15 +750,6 @@ struct vmbus_close_msg {struct vmbus_channel_close_channel msg; }; -/* Define connection identifier type. */ -union hv_connection_id { -u32 asu32; -struct { -u32 id:24 ; -u32 reserved:8 ; - } u; -}; -enum vmbus_device_type {HV_IDE =0 ,HV_SCSI ,include/uapi/asm-generic/hyperv-tlfs.h +19
-0@@ -53,6 +53,25 @@ union hv_message_flags { } __packed; }; +enum hv_port_type { +HV_PORT_TYPE_MESSAGE =1 , +HV_PORT_TYPE_EVENT =2 , +HV_PORT_TYPE_MONITOR =3 , +HV_PORT_TYPE_DOORBELL =4 // Root Partition only +}; + + +/* +* Doorbell connection_info flags. +*/ +#define HV_DOORBELL_FLAG_TRIGGER_SIZE_MASK 0x00000007 +#define HV_DOORBELL_FLAG_TRIGGER_SIZE_ANY 0x00000000 +#define HV_DOORBELL_FLAG_TRIGGER_SIZE_BYTE 0x00000001 +#define HV_DOORBELL_FLAG_TRIGGER_SIZE_WORD 0x00000002 +#define HV_DOORBELL_FLAG_TRIGGER_SIZE_DWORD 0x00000003 +#define HV_DOORBELL_FLAG_TRIGGER_SIZE_QWORD 0x00000004 +#define HV_DOORBELL_FLAG_TRIGGER_ANY_VALUE 0x80000000 +/* Define port identifier type. */ union hv_port_id {__u32 asu32;
include/uapi/asm-generic/hyperv-tlfs.h union hv_port_id { · line 85
Quoted 5 lines
include/uapi/asm-generic/hyperv-tlfs.h +2
-0@@ -62,6 +81,63 @@ union hv_port_id { }__packed u; }; +struct hv_port_info { +enum hv_port_type port_type;
Can you please replace the enum from the input / output structures with __u32? I don't think the C standard specifies the exact size of enum so this is prone to error. You can see in other places in this header when we need to store an enum we use __u32.
Wei.
Wei Liu
maintainerRe: [PATCH 06/17] mshv: SynIC port and connection hypercalls
In reply to Vineeth PillaiQuoted 40 lines · Vineeth Pillai
Hyper-V enables inter-partition communication through the port and connection constructs. More details about ports and connections in TLFS chapter 11.
Implement hypercalls related to ports and connections for enabling inter-partiion communication.
Signed-off-by Vineeth Pillai <[email protected]>--- drivers/hv/hv_call.c | 161 +++++++++++++++++++++++++ drivers/hv/mshv.h | 12 ++ include/asm-generic/hyperv-tlfs.h | 55 +++++++++ include/linux/hyperv.h | 9 -- include/uapi/asm-generic/hyperv-tlfs.h | 76 ++++++++++++ 5 files changed, 304 insertions(+), 9 deletions(-)drivers/hv/hv_call.c +16
-0@@ -742,3 +742,164 @@ int hv_call_translate_virtual_address(return hv_status_to_errno (status); } + +int +hv_call_create_port (u64 port_partition_id,union hv_port_id port_id, +u64 connection_partition_id, +struct hv_port_info *port_info, +u8 port_vtl,u8 min_connection_vtl,int node) +{ +struct hv_create_port *input; +unsigned long flags; +int ret =0 ; +int status; + + do { + local_irq_save(flags); + input = (struct hv_create_port *)(*this_cpu_ptr( + hyperv_pcpu_input_arg));
drivers/hv/hv_call.c int hv_call_translate_virtual_address( · line 770
Quoted 10 lines
drivers/hv/hv_call.c +10
-0+memset (input,0 ,sizeof (*input)); + + input->port_partition_id = port_partition_id; + input->port_id = port_id; + input->connection_partition_id = connection_partition_id; + input->port_info = *port_info; + input->port_vtl = port_vtl; + input->min_connection_vtl = min_connection_vtl; + input->proximity_domain_info = +numa_node_to_proximity_domain_info (node);
This misses the check for NUMA_NO_NODE, so does the function for port connection (see below).
I think it would actually be better to leave the check in numa_node_to_proximity_domain_info to avoid problems like this.
Of course, adapting this approach means some call sites for that function will need to be changed too.
---8<---
From 8705857c62b3e5f13d415736ca8b508c22e3f5ba Mon Sep 17 00:00:00 2001
From: Wei Liu <[email protected]>
Date: Wed, 30 Jun 2021 11:08:31 +0000
Subject: [PATCH] numa_node_to_proximity_domain_info should cope with
NUMA_NO_NODE
--- include/asm-generic/mshyperv.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
include/asm-generic/mshyperv.h +8 -4
@@ -31,10 +31,14 @@ numa_node_to_proximity_domain_info(int node) {union hv_proximity_domain_info proximity_domain_info; - proximity_domain_info.domain_id =node_to_pxm (node); - proximity_domain_info.flags.reserved =0 ; - proximity_domain_info.flags.proximity_info_valid =1 ; - proximity_domain_info.flags.proximity_preferred =1 ; + proximity_domain_info.as_uint64 =0 ; + +if (node !=NUMA_NO_NODE ) { + proximity_domain_info.domain_id =node_to_pxm (node); + proximity_domain_info.flags.reserved =0 ; + proximity_domain_info.flags.proximity_info_valid =1 ; + proximity_domain_info.flags.proximity_preferred =1 ; + }return proximity_domain_info; }
Signature · 4 lines
2.30.2 [...]
Quoted 14 lines
drivers/hv/hv_call.c +14
-0+int +hv_call_connect_port(u64 port_partition_id, union hv_port_id port_id, + u64 connection_partition_id, + union hv_connection_id connection_id, + struct hv_connection_info *connection_info, + u8 connection_vtl, int node) +{ + struct hv_connect_port *input; + unsigned long flags; + int ret = 0, status; + + do { + local_irq_save(flags); + input = (struct hv_connect_port *)(*this_cpu_ptr(
drivers/hv/hv_call.c int hv_call_translate_virtual_address( · line 794
Quoted 10 lines
drivers/hv/hv_call.c +10
-0+ hyperv_pcpu_input_arg)); +memset (input,0 ,sizeof (*input)); + input->port_partition_id = port_partition_id; + input->port_id = port_id; + input->connection_partition_id = connection_partition_id; + input->connection_id = connection_id; + input->connection_info = *connection_info; + input->connection_vtl = connection_vtl; + input->proximity_domain_info = +numa_node_to_proximity_domain_info (node);
Here...
Re: [PATCH 06/17] mshv: SynIC port and connection hypercalls
Quoted 30 lines
Quoted 17 lines
(quoted diff) +16
-0+ +int +hv_call_create_port(u64 port_partition_id, union hv_port_id port_id, + u64 connection_partition_id, + struct hv_port_info *port_info, + u8 port_vtl, u8 min_connection_vtl, int node) +{ + struct hv_create_port *input; + unsigned long flags; + int ret = 0; + int status; + + do { + local_irq_save(flags); + input = (struct hv_create_port *)(*this_cpu_ptr( + hyperv_pcpu_input_arg));Review
Quoted 10 lines
(quoted diff) +10
-0+ memset(input, 0, sizeof(*input)); + + input->port_partition_id = port_partition_id; + input->port_id = port_id; + input->connection_partition_id = connection_partition_id; + input->port_info = *port_info; + input->port_vtl = port_vtl; + input->min_connection_vtl = min_connection_vtl; + input->proximity_domain_info = + numa_node_to_proximity_domain_info(node);This misses the check for
NUMA_NO_NODE, so does the function for port connection (see below).
Quoted 5 lines
I think it would actually be better to leave the check in
numa_node_to_proximity_domain_infoto avoid problems like this.Of course, adapting this approach means some call sites for that function will need to be changed too.
Thanks for catching this and fixing Wei, will roll it into my branch.
~Vineeth
Quoted 36 lines
---8<---
Quoted 1 line
From
8705857c62b3e5f13d415736ca8b508c22e3f5baMon Sep 17 00:00:00 2001From: Wei Liu <[email protected]>
Date: Wed, 30 Jun 2021 11:08:31 +0000
Subject: [PATCH]numa_node_to_proximity_domain_infoshould cope with
NUMA_NO_NODESigned-off-by Wei Liu <[email protected]>--- include/asm-generic/mshyperv.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)include/asm-generic/mshyperv.h +8
-4@@ -31,10 +31,14 @@ numa_node_to_proximity_domain_info(int node) {union hv_proximity_domain_info proximity_domain_info; - proximity_domain_info.domain_id =node_to_pxm (node); - proximity_domain_info.flags.reserved =0 ; - proximity_domain_info.flags.proximity_info_valid =1 ; - proximity_domain_info.flags.proximity_preferred =1 ; + proximity_domain_info.as_uint64 =0 ; + +if (node !=NUMA_NO_NODE ) { + proximity_domain_info.domain_id =node_to_pxm (node); + proximity_domain_info.flags.reserved =0 ; + proximity_domain_info.flags.proximity_info_valid =1 ; + proximity_domain_info.flags.proximity_preferred =1 ; + }return proximity_domain_info; }
PATCH 7/17 hyperv: Configure SINT for Doorbell
In reply to Vineeth PillaiDoorbell is a mechanism by which a parent partition can register for notification if a specified mmio address is touched by a child partition. Parent partition can setup the notification by specifying mmio address, size of the data written(1/2/4/8 bytes) and optionally the data as well.
Doorbell events are delivered by a SynIC interrupt. Configure a SynIC interrupt source for doorbell.
--- drivers/hv/hv_synic.c | 30 +++++++++++++++++++++++++----- include/asm-generic/hyperv-tlfs.h | 15 ++++++++++++++- 2 files changed, 39 insertions(+), 6 deletions(-)
drivers/hv/hv_synic.c +25 -5
@@ -112,6 +112,15 @@ void mshv_isr(void)add_interrupt_randomness (HYPERVISOR_CALLBACK_VECTOR ,0 ); } +static inline bool hv_recommend_using_aeoi (void ) +{ +#ifdef HV_DEPRECATING_AEOI_RECOMMENDED +return !(ms_hyperv.hints &HV_DEPRECATING_AEOI_RECOMMENDED ); +#else +return false; +#endif +} +int mshv_synic_init (unsigned int cpu) {union hv_synic_simp simp;
@@ -166,14 +175,19 @@ int mshv_synic_init(unsigned int cpu) sint.as_uint64 =0 ; sint.vector =HYPERVISOR_CALLBACK_VECTOR ; sint.masked = false; -#ifdef HV_DEPRECATING_AEOI_RECOMMENDED - sint.auto_eoi = !(ms_hyperv.hints &HV_DEPRECATING_AEOI_RECOMMENDED ); -#else - sint.auto_eoi =0 ; -#endif + sint.auto_eoi =hv_recommend_using_aeoi ();hv_set_register (HV_REGISTER_SINT0 +HV_SYNIC_INTERCEPTION_SINT_INDEX , sint.as_uint64); +/* Doorbell SINT */ + sint.as_uint64 =0 ; + sint.vector =HYPERVISOR_CALLBACK_VECTOR ; + sint.masked = false; + sint.as_intercept =1 ; + sint.auto_eoi =hv_recommend_using_aeoi (); +hv_set_register (HV_REGISTER_SINT0 +HV_SYNIC_DOORBELL_SINT_INDEX , + sint.as_uint64); +/* Enable global synic bit */ sctrl.as_uint64 =hv_get_register (HV_REGISTER_SCONTROL ); sctrl.enable =1 ;
@@ -221,6 +235,12 @@ int mshv_synic_cleanup(unsigned int cpu)hv_set_register (HV_REGISTER_SINT0 +HV_SYNIC_INTERCEPTION_SINT_INDEX , sint.as_uint64); +/* Disable Doorbell SINT */ + sint.as_uint64 =hv_get_register (HV_REGISTER_SINT0 +HV_SYNIC_DOORBELL_SINT_INDEX ); + sint.masked = true; +hv_set_register (HV_REGISTER_SINT0 +HV_SYNIC_DOORBELL_SINT_INDEX , + sint.as_uint64); +/* Disable Synic's event ring page */ sirbp.as_uint64 =hv_get_register (HV_REGISTER_SIRBP ); sirbp.sirbp_enabled = false;
include/asm-generic/hyperv-tlfs.h +14 -1
@@ -262,6 +262,9 @@ enum hv_status {#define HV_SYNIC_HVL_SHARED_SINT_INDEX 0x00000004#define HV_SYNIC_FIRST_UNUSED_SINT_INDEX 0x00000005 +/* mshv assigned SINT for doorbell */ +#define HV_SYNIC_DOORBELL_SINT_INDEX HV_SYNIC_FIRST_UNUSED_SINT_INDEX +#define HV_SYNIC_CONTROL_ENABLE (1ULL << 0)#define HV_SYNIC_SIMP_ENABLE (1ULL << 0)#define HV_SYNIC_SIEFP_ENABLE (1ULL << 0)
@@ -284,6 +287,14 @@ struct hv_timer_message_payload {__u64 delivery_time;/* When the message was delivered */ } __packed; +/* +* Message format for notifications delivered via +* intercept message(as_intercept=1) +*/ +struct hv_notification_message_payload { +u32 sint_index; +} __packed; +/* Define the synthentic interrupt controller event ring format */ #define HV_SYNIC_EVENT_RING_MESSAGE_COUNT 63
@@ -350,7 +361,9 @@ union hv_synic_sint { u64 masked:1; u64 auto_eoi:1; u64 polling:1; - u64 reserved2:45; + u64 as_intercept: 1; + u64 proxy: 1; + u64 reserved2:43; } __packed; };
Signature · 1 line
2.25.1
PATCH 8/17 mshv: Port id management
In reply to Vineeth PillaiEach port in the partition should be uniquely identified by an id. Partition is responsible for managing the port id.
--- drivers/hv/Makefile | 2 +- drivers/hv/hv_portid_table.c | 83 ++++++++++++++++++++++++++++++++++++ drivers/hv/mshv.h | 35 +++++++++++++++ drivers/hv/mshv_main.c | 2 + 4 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 drivers/hv/hv_portid_table.c
drivers/hv/Makefile +1 -1
@@ -13,4 +13,4 @@ hv_vmbus-y := vmbus_drv.o \ hv_vmbus-$(CONFIG_HYPERV_TESTING) += hv_debugfs.o hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_fcopy.o hv_utils_transport.o -mshv-y += mshv_main.o hv_call.o hv_synic.o +mshv-y += mshv_main.o hv_call.o hv_synic.o hv_portid_table.o
drivers/hv/hv_portid_table.c new file +83 -0
83 lines not on this page · raw message
drivers/hv/mshv.h +35 -0
@@ -39,6 +39,41 @@ void mshv_isr(void);int mshv_synic_init (unsigned int cpu);int mshv_synic_cleanup (unsigned int cpu); +/* +* Callback for doorbell events. +* NOTE: This is called in interrupt context. Callback +* should defer slow and sleeping logic to later. +*/ +typedef void (*doorbell_cb_t ) (void *); + +/* +* port table information +*/ +struct port_table_info { +struct rcu_head rcu; +enum hv_port_type port_type; +union { +struct { +u64 reserved[2 ]; + } port_message; +struct { +u64 reserved[2 ]; + } port_event; +struct { +u64 reserved[2 ]; + } port_monitor; +struct { +doorbell_cb_t doorbell_cb; +void *data; + } port_doorbell; + }; +}; + +void hv_port_table_fini (void ); +int hv_portid_alloc (struct port_table_info *info); +int hv_portid_lookup (int port_id,struct port_table_info *info); +void hv_portid_free (int port_id); +/* * Hyper-V hypercalls */
drivers/hv/mshv_main.c +2 -0
@@ -1146,6 +1146,8 @@ __exit mshv_exit(void)cpuhp_remove_state (mshv_cpuhp_online);free_percpu (mshv.synic_pages); +hv_port_table_fini (); +misc_deregister (&mshv_dev); }
Signature · 1 line
2.25.1
PATCH 9/17 mshv: Doorbell handler in hypercall ISR
In reply to Vineeth PillaiDoorbell is a mechanism by which a parent partition can register for notification if a specified mmio address is touched by a child partition. Parent partition can setup the notification by specifying mmio address, size of the data written(1/2/4/8 bytes) and optionally the data as well.
Setup doorbell signal to be delivered by intercept interrupt and handle the doorbell signal.
--- arch/x86/hyperv/hv_init.c | 32 ++++- arch/x86/include/asm/mshyperv.h | 2 + drivers/hv/hv_synic.c | 175 +++++++++++++++++++++---- drivers/hv/mshv.h | 2 +- include/uapi/asm-generic/hyperv-tlfs.h | 4 + 5 files changed, 186 insertions(+), 29 deletions(-)
arch/x86/hyperv/hv_init.c +30 -2
@@ -51,6 +51,16 @@ EXPORT_SYMBOL_GPL(hyperv_pcpu_input_arg);void __percpu **hyperv_pcpu_output_arg;EXPORT_SYMBOL_GPL (hyperv_pcpu_output_arg); +/* +* Per-cpu array holding the tail pointer for the SynIC event ring buffer +* for each SINT. +* +* We cannot maintain this in mshv driver because the tail pointer should +* persist even if the mshv driver is unloaded. +*/ +u8 __percpu **hv_synic_eventring_tail; +EXPORT_SYMBOL_GPL (hv_synic_eventring_tail); +u32 hv_max_vp_index;EXPORT_SYMBOL_GPL (hv_max_vp_index);
@@ -58,11 +68,13 @@ static int hv_cpu_init(unsigned int cpu) {u64 msr_vp_index;struct hv_vp_assist_page **hvp = &hv_vp_assist_page[smp_processor_id ()]; +unsigned int order = hv_root_partition ?1 :0 ; +u8 **synic_eventring_tail;void **input_arg;struct page *pg;/* hv_cpu_init() can be called with IRQs disabled from hv_resume() */ - pg =alloc_pages (irqs_disabled () ?GFP_ATOMIC :GFP_KERNEL , hv_root_partition ?1 :0 ); + pg =alloc_pages (irqs_disabled () ?GFP_ATOMIC :GFP_KERNEL , order);if (unlikely (!pg))return -ENOMEM ;
@@ -73,6 +85,14 @@ static int hv_cpu_init(unsigned int cpu) output_arg = (void **)this_cpu_ptr (hyperv_pcpu_output_arg); *output_arg =page_address (pg +1 ); + + synic_eventring_tail = (u8 **)this_cpu_ptr (hv_synic_eventring_tail); + *synic_eventring_tail =kcalloc (HV_SYNIC_SINT_COUNT ,sizeof (u8), +irqs_disabled () ?GFP_ATOMIC :GFP_KERNEL ); +if (unlikely (!*synic_eventring_tail)) { +__free_pages (pg, order); +return -ENOMEM ; + } } msr_vp_index =hv_get_register (HV_REGISTER_VP_INDEX );
@@ -197,6 +217,7 @@ EXPORT_SYMBOL_GPL(clear_hv_tscchange_cb);static int hv_cpu_die (unsigned int cpu) {struct hv_reenlightenment_control re_ctrl; +u8 **synic_eventring_tail;unsigned int new_cpu;unsigned long flags;void **input_arg;
@@ -212,6 +233,10 @@ static int hv_cpu_die(unsigned int cpu) output_arg = (void **)this_cpu_ptr (hyperv_pcpu_output_arg); *output_arg =NULL ; + + synic_eventring_tail = (u8 **)this_cpu_ptr (hv_synic_eventring_tail); +kfree (*synic_eventring_tail); + *synic_eventring_tail =NULL ; }local_irq_restore (flags);
@@ -390,10 +415,13 @@ void __init hyperv_init(void)BUG_ON (hyperv_pcpu_input_arg ==NULL ); -/* Allocate the per-CPU state for output arg for root */ if (hv_root_partition) { +/* Allocate the per-CPU state for output arg for root */ hyperv_pcpu_output_arg = alloc_percpu(void *);BUG_ON (hyperv_pcpu_output_arg ==NULL ); + + hv_synic_eventring_tail = alloc_percpu(u8 *); +BUG_ON (hv_synic_eventring_tail ==NULL ); }/* Allocate percpu VP index */
arch/x86/include/asm/mshyperv.h +2 -0
@@ -39,6 +39,8 @@ extern void *hv_hypercall_pg;extern void __percpu **hyperv_pcpu_input_arg;extern void __percpu **hyperv_pcpu_output_arg; +extern u8 __percpu **hv_synic_eventring_tail; +extern u64 hv_current_partition_id;int hv_call_deposit_pages (int node,u64 partition_id,u32 num_pages);
drivers/hv/hv_synic.c +149 -26
197 lines not on this page · raw message
drivers/hv/mshv.h +1 -1
@@ -44,7 +44,7 @@ int mshv_synic_cleanup(unsigned int cpu);* NOTE: This is called in interrupt context. Callback * should defer slow and sleeping logic to later. */ -typedef void (*doorbell_cb_t ) (void *); +typedef void (*doorbell_cb_t ) (int doorbell_id,void *);/* * port table information
include/uapi/asm-generic/hyperv-tlfs.h +4 -0
@@ -31,6 +31,10 @@ enum hv_message_type {/* Trace buffer complete messages. */ HVMSG_EVENTLOG_BUFFERCOMPLETE =0x80000040 , +/* SynIC intercepts */ +HVMSG_SYNIC_EVENT_INTERCEPT =0x80000060 , +HVMSG_SYNIC_SINT_INTERCEPT =0x80000061 , +/* Platform-specific processor intercept messages. */ HVMSG_X64_IO_PORT_INTERCEPT =0x80010000 , HVMSG_X64_MSR_INTERCEPT = 0x80010001,
Signature · 1 line
2.25.1
PATCH 10/17 mshv: Doorbell register/unregister API
In reply to Vineeth PillaiDoorbell is a mechanism by which a parent partition can register for notification if a specified mmio address is touched by a child partition. Parent partition can setup the notification by specifying mmio address, size of the data written(1/2/4/8 bytes) and optionally the data as well.
APIs for registering and unregistering doorbell notification.
--- drivers/hv/hv_synic.c | 81 +++++++++++++++++++++++++++++++++++++++++++ drivers/hv/mshv.h | 4 +++ 2 files changed, 85 insertions(+)
drivers/hv/hv_synic.c +81 -0
@@ -9,6 +9,7 @@*/ #include <linux/kernel.h> +#include <linux/slab.h> #include <linux/mm.h> #include <linux/io.h> #include <linux/random.h>
@@ -389,3 +390,83 @@ int mshv_synic_cleanup(unsigned int cpu)return 0 ; } + +int +hv_register_doorbell (u64 partition_id,doorbell_cb_t doorbell_cb,void *data, +u64 gpa,u64 val,u64 flags) +{ +struct hv_connection_info connection_info = {0 }; +union hv_connection_id connection_id = {0 }; +struct port_table_info *port_table_info; +struct hv_port_info port_info = {0 }; +union hv_port_id port_id = {0 }; +int ret; + + port_table_info =kmalloc (sizeof (struct port_table_info ), +GFP_KERNEL ); +if (!port_table_info) +return -ENOMEM ; + + port_table_info->port_type =HV_PORT_TYPE_DOORBELL ; + port_table_info->port_doorbell.doorbell_cb = doorbell_cb; + port_table_info->port_doorbell.data = data; + ret =hv_portid_alloc (port_table_info); +if (ret <0 ) { +pr_err ("Failed to create the doorbell port!\n" ); +kfree (port_table_info); +return ret; + } + + port_id.u.id = ret; + port_info.port_type =HV_PORT_TYPE_DOORBELL ; + port_info.doorbell_port_info.target_sint =HV_SYNIC_DOORBELL_SINT_INDEX ; + port_info.doorbell_port_info.target_vp =HV_ANY_VP ; + ret =hv_call_create_port (hv_current_partition_id, port_id, partition_id, + &port_info, +0 ,0 ,NUMA_NO_NODE ); + +if (ret <0 ) { +pr_err ("Failed to create the port!\n" ); +hv_portid_free (port_id.u.id); +return ret; + } + + connection_id.u.id = port_id.u.id; + connection_info.port_type =HV_PORT_TYPE_DOORBELL ; + connection_info.doorbell_connection_info.gpa = gpa; + connection_info.doorbell_connection_info.trigger_value = val; + connection_info.doorbell_connection_info.flags = flags; + + ret =hv_call_connect_port (hv_current_partition_id, port_id, partition_id, + connection_id, &connection_info,0 ,NUMA_NO_NODE ); +if (ret <0 ) { +hv_call_delete_port (hv_current_partition_id, port_id); +hv_portid_free (port_id.u.id); +return ret; + } + +// lets use the port_id as the doorbell_id +return port_id.u.id; +} + +int +hv_unregister_doorbell (u64 partition_id,int doorbell_portid) +{ +int ret =0 ; +union hv_port_id port_id = {0 }; +union hv_connection_id connection_id = {0 }; + + connection_id.u.id = doorbell_portid; + ret =hv_call_disconnect_port (partition_id, connection_id); +if (ret <0 ) +pr_err ("Failed to disconnect the doorbell connection!\n" ); + + port_id.u.id = doorbell_portid; + ret =hv_call_delete_port (hv_current_partition_id, port_id); +if (ret <0 ) +pr_err ("Failed to disconnect the doorbell connection!\n" ); + +hv_portid_free (doorbell_portid); + +return ret; +}
drivers/hv/mshv.h +4 -0
@@ -74,6 +74,10 @@ int hv_portid_alloc(struct port_table_info *info);int hv_portid_lookup (int port_id,struct port_table_info *info);void hv_portid_free (int port_id); +int hv_register_doorbell (u64 partition_id,doorbell_cb_t doorbell_cb, +void *data,u64 gpa,u64 val,u64 flags); +int hv_unregister_doorbell (u64 partition_id,int doorbell_portid); +/* * Hyper-V hypercalls */
Signature · 1 line
2.25.1
PATCH 11/17 mshv: HvClearVirtualInterrupt hypercall
In reply to Vineeth PillaiFor HvX64InterruptTypeExtInt interrupts, before asserting a new interrupt, previous inteerupt's acknowledgment should be cleared by this hypercall HvClearVirtualInterrupt. (TLFS 10.3.2)
This is to be used in a later patch in this series.
--- drivers/hv/hv_call.c | 20 ++++++++++++++++++++ drivers/hv/mshv.h | 1 + include/asm-generic/hyperv-tlfs.h | 1 + 3 files changed, 22 insertions(+)
drivers/hv/hv_call.c +20 -0
@@ -743,6 +743,26 @@ int hv_call_translate_virtual_address( } +int +hv_call_clear_virtual_interrupt (u64 partition_id) +{ +unsigned long flags; +int status; + +local_irq_save (flags); + status =hv_do_fast_hypercall8 (HVCALL_CLEAR_VIRTUAL_INTERRUPT , + partition_id) & +HV_HYPERCALL_RESULT_MASK ; +local_irq_restore (flags); + +if (status !=HV_STATUS_SUCCESS ) { +pr_err ("%s: %s\n" , __func__,hv_status_to_string (status)); +return -hv_status_to_errno (status); + } + +return 0 ; +} + int hv_call_create_port(u64 port_partition_id, union hv_port_id port_id, u64 connection_partition_id,
drivers/hv/mshv.h +1 -0
@@ -117,6 +117,7 @@ int hv_call_assert_virtual_interrupt(u32 vector, u64 dest_addr, union hv_interrupt_control control); +int hv_call_clear_virtual_interrupt (u64 partition_id); int hv_call_get_vp_state( u32 vp_index, u64 partition_id,
include/asm-generic/hyperv-tlfs.h +1 -0
@@ -159,6 +159,7 @@ struct ms_hyperv_tsc_page {#define HVCALL_GET_VP_REGISTERS 0x0050#define HVCALL_SET_VP_REGISTERS 0x0051#define HVCALL_TRANSLATE_VIRTUAL_ADDRESS 0x0052 +#define HVCALL_CLEAR_VIRTUAL_INTERRUPT 0x0056#define HVCALL_DELETE_PORT 0x0058#define HVCALL_DISCONNECT_PORT 0x005b#define HVCALL_POST_MESSAGE 0x005c
Signature · 1 line
2.25.1
PATCH 12/17 mshv: Add irqfd support for mshv
In reply to Vineeth Pillaiirqfd is a mechanism to inject a specific interrupt to a guest using a decoupled eventfd mechnanism: Any legal signal on the irqfd (using eventfd semantics from either userspace or kernel) will translate into an injected interrupt in the guest at the next available interrupt window.
This is the implementation of irqfd feature in kvm:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=721eecbf4fe995ca94a9edec0c9843b1cc0eaaf3
The basic framework code is taken as it its from the kvm implementation. All credit goes to kvm irqfd/ioeventfd developers.
--- drivers/hv/Kconfig | 1 + drivers/hv/Makefile | 3 +- drivers/hv/hv_eventfd.c | 299 +++++++++++++++++++++++++++++++++++ drivers/hv/mshv_main.c | 26 +++ include/linux/mshv.h | 11 ++ include/linux/mshv_eventfd.h | 35 ++++ include/uapi/linux/mshv.h | 15 ++ 7 files changed, 389 insertions(+), 1 deletion(-) create mode 100644 drivers/hv/hv_eventfd.c create mode 100644 include/linux/mshv_eventfd.h
drivers/hv/Kconfig +1 -0
@@ -30,6 +30,7 @@ config HYPERV_BALLOON config HYPERV_ROOT_API tristate "Microsoft Hypervisor root partition interfaces: /dev/mshv" depends on HYPERV + select EVENTFD help Provides access to interfaces for managing guest virtual machines running under the Microsoft Hypervisor.
drivers/hv/Makefile +2 -1
@@ -13,4 +13,5 @@ hv_vmbus-y := vmbus_drv.o \ hv_vmbus-$(CONFIG_HYPERV_TESTING) += hv_debugfs.o hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_fcopy.o hv_utils_transport.o -mshv-y += mshv_main.o hv_call.o hv_synic.o hv_portid_table.o +mshv-y += mshv_main.o hv_call.o hv_synic.o hv_portid_table.o \ + hv_eventfd.o
drivers/hv/hv_eventfd.c new file +299 -0
299 lines not on this page · raw message
drivers/hv/mshv_main.c +26 -0
@@ -19,6 +19,7 @@#include <linux/cpuhotplug.h> #include <linux/random.h> #include <linux/mshv.h> +#include <linux/mshv_eventfd.h> #include <asm/mshyperv.h> #include "mshv.h"
@@ -827,6 +828,18 @@ mshv_partition_ioctl_assert_interrupt(struct mshv_partition *partition, args.control); } +static long +mshv_partition_ioctl_irqfd (struct mshv_partition *partition, +void __user *user_args) +{ +struct mshv_irqfd args; + +if (copy_from_user (&args, user_args,sizeof (args))) +return -EFAULT ; + +return mshv_irqfd (partition, &args); +} +static long mshv_partition_ioctl (struct file *filp,unsigned int ioctl,unsigned long arg) {
@@ -865,6 +878,10 @@ mshv_partition_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) ret =mshv_partition_ioctl_set_property (partition, (void __user *)arg);break ; +case MSHV_IRQFD : + ret =mshv_partition_ioctl_irqfd (partition, + (void __user *)arg); +break ;default : ret = -ENOTTY ; }
@@ -955,6 +972,8 @@ mshv_partition_release(struct inode *inode, struct file *filp) {struct mshv_partition *partition = filp->private_data; +mshv_irqfd_release (partition); +mshv_partition_put (partition);return 0 ;
@@ -1049,6 +1068,8 @@ mshv_ioctl_create_partition(void __user *user_arg)fd_install (fd, file); +mshv_irqfd_init (partition); +return fd; release_file:
@@ -1137,12 +1158,17 @@ __init mshv_init(void) mshv_cpuhp_online = ret;spin_lock_init (&mshv.partitions.lock); +if (mshv_irqfd_wq_init ()) +mshv_irqfd_wq_cleanup (); +return 0 ; }static void __exit mshv_exit(void) { +mshv_irqfd_wq_cleanup (); +cpuhp_remove_state (mshv_cpuhp_online);free_percpu (mshv.synic_pages);
include/linux/mshv.h +11 -0
@@ -47,6 +47,17 @@ struct mshv_partition {u32 count;struct mshv_vp *array[MSHV_MAX_VPS ]; } vps; + +struct { +spinlock_t lock; +struct list_head items; + } irqfds; +}; + +struct mshv_lapic_irq { +u32 vector; +u64 apic_id; +union hv_interrupt_control control; };struct hv_synic_pages {
include/linux/mshv_eventfd.h new file +35 -0
@@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* +* +* irqfd: Allows an fd to be used to inject an interrupt to the guest +* All credit goes to kvm developers. +*/ + +#ifndef __LINUX_MSHV_EVENTFD_H +#define __LINUX_MSHV_EVENTFD_H + +#include <linux/mshv.h> +#include <linux/poll.h> + +struct mshv_kernel_irqfd { +struct mshv_partition *partition; +struct eventfd_ctx *eventfd; +u32 gsi; +struct mshv_lapic_irq lapic_irq; +struct list_head list; +poll_table pt; +wait_queue_head_t *wqh; +wait_queue_entry_t wait; +struct work_struct shutdown; +}; + +int mshv_irqfd (struct mshv_partition *partition, +struct mshv_irqfd *args); + +void mshv_irqfd_init (struct mshv_partition *partition); +void mshv_irqfd_release (struct mshv_partition *partition); + +int mshv_irqfd_wq_init (void ); +void mshv_irqfd_wq_cleanup (void ); + +#endif /* __LINUX_MSHV_EVENTFD_H */
include/uapi/linux/mshv.h +15 -0
@@ -79,6 +79,20 @@ struct mshv_translate_gva {__u64 *gpa; }; +#define MSHV_IRQFD_FLAG_DEASSIGN (1 << 0) + +struct mshv_irqfd { +__u64 apic_id; +__s32 fd; +__u32 gsi; +__u32 vector; +__u32 interrupt_type; +__u32 flags; +__u8 level_triggered; +__u8 logical_dest_mode; +__u8 pad[2 ]; +}; +#define MSHV_IOCTL 0xB8/* mshv device */
@@ -95,6 +109,7 @@ struct mshv_translate_gva { _IOW(MSHV_IOCTL, 0xC, struct mshv_partition_property)#define MSHV_GET_PARTITION_PROPERTY \ _IOWR(MSHV_IOCTL, 0xD, struct mshv_partition_property) +#define MSHV_IRQFD _IOW(MSHV_IOCTL, 0xE, struct mshv_irqfd)/* vp device */ #define MSHV_GET_VP_REGISTERS _IOWR(MSHV_IOCTL, 0x05, struct mshv_vp_registers)
Signature · 1 line
2.25.1
PATCH 13/17 mshv: Add ioeventfd support for mshv
In reply to Vineeth Pillaiioeventfd is a mechanism to register PIO/MMIO regions to trigger an eventfd signal when written to by a guest. Host userspace can register any arbitrary IO address with a corresponding eventfd and then pass the eventfd to a specific end-point of interest for handling.
Basic framework code is taken from kvm implementation. Credit goes to kvm irqfd/ioeventfd developers.
--- drivers/hv/hv_eventfd.c | 248 +++++++++++++++++++++++++++++++++-- drivers/hv/mshv_main.c | 20 ++- include/linux/mshv.h | 4 + include/linux/mshv_eventfd.h | 21 ++- include/uapi/linux/mshv.h | 26 +++- 5 files changed, 300 insertions(+), 19 deletions(-)
drivers/hv/hv_eventfd.c +236 -12
@@ -2,8 +2,8 @@/* * eventfd support for mshv * -* Heavily inspired from KVM implementation of irqfd. The basic framework -* code is taken from the kvm implementation. +* Heavily inspired from KVM implementation of irqfd/ioeventfd. The basic +* framework code is taken from the kvm implementation. * * All credits to kvm developers. */
@@ -210,13 +210,6 @@ mshv_irqfd_assign(struct mshv_partition *partition,return ret; } -void -mshv_irqfd_init (struct mshv_partition *partition) -{ -spin_lock_init (&partition->irqfds.lock); -INIT_LIST_HEAD (&partition->irqfds.items); -} -/* * shutdown any irqfd's that match fd+gsi */
@@ -261,10 +254,10 @@ mshv_irqfd(struct mshv_partition *partition, struct mshv_irqfd *args) }/* -* This function is called as the mshv VM fd is being released. Shutdown all -* irqfds that still remain open +* This function is called as the mshv VM fd is being released. +* Shutdown all irqfds that still remain open */ -void +static void mshv_irqfd_release (struct mshv_partition *partition) {struct mshv_kernel_irqfd *irqfd, *tmp;
@@ -297,3 +290,234 @@ void mshv_irqfd_wq_cleanup(void) {destroy_workqueue (irqfd_cleanup_wq); } + +/* +* -------------------------------------------------------------------- +* ioeventfd: translate a MMIO memory write to an eventfd signal. +* +* userspace can register a MMIO address with an eventfd for receiving +* notification when the memory has been touched. +* +* TODO: Implement eventfd for PIO as well. +* -------------------------------------------------------------------- +*/ + +static void +ioeventfd_release (struct kernel_mshv_ioeventfd *p,u64 partition_id) +{ +if (p->doorbell_id >0 ) +hv_unregister_doorbell (partition_id, p->doorbell_id); +eventfd_ctx_put (p->eventfd); +list_del (&p->list); +kfree (p); +} + +/* MMIO writes trigger an event if the addr/val match */ +static void +ioeventfd_mmio_write (int doorbell_id,void *data) +{ +struct mshv_partition *partition = (struct mshv_partition *)data; +struct kernel_mshv_ioeventfd *p; +unsigned long flags; + +spin_lock_irqsave (&partition->ioeventfds.lock, flags); +list_for_each_entry (p, &partition->ioeventfds.items, list) { +if (p->doorbell_id == doorbell_id) { +eventfd_signal (p->eventfd,1 ); +break ; + } + } +spin_unlock_irqrestore (&partition->ioeventfds.lock, flags); +} + +static bool +ioeventfd_check_collision (struct mshv_partition *partition, +struct kernel_mshv_ioeventfd *p) +{ +struct kernel_mshv_ioeventfd *_p; + +list_for_each_entry (_p, &partition->ioeventfds.items, list) +if (_p->addr == p->addr && _p->length == p->length && + (_p->wildcard || p->wildcard || + _p->datamatch == p->datamatch)) +return true; + +return false; +} + +static int +mshv_assign_ioeventfd (struct mshv_partition *partition, +struct mshv_ioeventfd *args) +{ +struct kernel_mshv_ioeventfd *p; +struct eventfd_ctx *eventfd; +u64 doorbell_flags =0 ; +unsigned long irqflags; +int ret; + +if (args->flags &MSHV_IOEVENTFD_FLAG_PIO ) +return -EOPNOTSUPP ; + +/* must be natural-word sized */ +switch (args->len) { +case 0 : + doorbell_flags =HV_DOORBELL_FLAG_TRIGGER_SIZE_ANY ; +break ; +case 1 : + doorbell_flags =HV_DOORBELL_FLAG_TRIGGER_SIZE_BYTE ; +break ; +case 2 : + doorbell_flags =HV_DOORBELL_FLAG_TRIGGER_SIZE_WORD ; +break ; +case 4 : + doorbell_flags =HV_DOORBELL_FLAG_TRIGGER_SIZE_DWORD ; +break ; +case 8 : + doorbell_flags =HV_DOORBELL_FLAG_TRIGGER_SIZE_QWORD ; +break ; +default : +pr_warn ("ioeventfd: invalid length specified\n" ); +return -EINVAL ; + } + +/* check for range overflow */ +if (args->addr + args->len < args->addr) +return -EINVAL ; + +/* check for extra flags that we don't understand */ +if (args->flags & ~MSHV_IOEVENTFD_VALID_FLAG_MASK ) +return -EINVAL ; + + eventfd =eventfd_ctx_fdget (args->fd); +if (IS_ERR (eventfd)) +return PTR_ERR (eventfd); + + p =kzalloc (sizeof (*p),GFP_KERNEL ); +if (!p) { + ret = -ENOMEM ; + goto fail; + } + +INIT_LIST_HEAD (&p->list); + p->addr = args->addr; + p->length = args->len; + p->eventfd = eventfd; + +/* The datamatch feature is optional, otherwise this is a wildcard */ +if (args->flags &MSHV_IOEVENTFD_FLAG_DATAMATCH ) + p->datamatch = args->datamatch; +else { + p->wildcard = true; + doorbell_flags |=HV_DOORBELL_FLAG_TRIGGER_ANY_VALUE ; + } + +spin_lock_irqsave (&partition->ioeventfds.lock, irqflags); + +if (ioeventfd_check_collision (partition, p)) { + ret = -EEXIST ; + goto unlock_fail; + } + + ret =hv_register_doorbell (partition->id, ioeventfd_mmio_write, + (void *)partition, p->addr, + p->datamatch, doorbell_flags); +if (ret <0 ) { +pr_err ("Failed to register ioeventfd doorbell!\n" ); + goto unlock_fail; + } + + p->doorbell_id = ret; +list_add_tail (&p->list, &partition->ioeventfds.items); + +spin_unlock_irqrestore (&partition->ioeventfds.lock, irqflags); + +return 0 ; + +unlock_fail: +spin_unlock_irqrestore (&partition->ioeventfds.lock, irqflags); + +kfree (p); + +fail: +eventfd_ctx_put (eventfd); + +return ret; +} + +static int +mshv_deassign_ioeventfd (struct mshv_partition *partition, +struct mshv_ioeventfd *args) +{ +struct kernel_mshv_ioeventfd *p, *tmp; +struct eventfd_ctx *eventfd; +unsigned long flags; +int ret = -ENOENT ; + + eventfd =eventfd_ctx_fdget (args->fd); +if (IS_ERR (eventfd)) +return PTR_ERR (eventfd); + +spin_lock_irqsave (&partition->ioeventfds.lock, flags); + +list_for_each_entry_safe (p, tmp, &partition->ioeventfds.items, list) { +bool wildcard = !(args->flags &MSHV_IOEVENTFD_FLAG_DATAMATCH ); + +if (p->eventfd != eventfd || + p->addr != args->addr || + p->length != args->len || + p->wildcard != wildcard) +continue ; + +if (!p->wildcard && p->datamatch != args->datamatch) +continue ; + +ioeventfd_release (p, partition->id); + ret =0 ; +break ; + } + +spin_unlock_irqrestore (&partition->ioeventfds.lock, flags); + +eventfd_ctx_put (eventfd); + +return ret; +} + +int +mshv_ioeventfd (struct mshv_partition *partition, +struct mshv_ioeventfd *args) +{ +/* PIO not yet implemented */ +if (args->flags &MSHV_IOEVENTFD_FLAG_PIO ) +return -EOPNOTSUPP ; + +if (args->flags &MSHV_IOEVENTFD_FLAG_DEASSIGN ) +return mshv_deassign_ioeventfd (partition, args); + +return mshv_assign_ioeventfd (partition, args); +} + +void +mshv_eventfd_init (struct mshv_partition *partition) +{ +spin_lock_init (&partition->irqfds.lock); +INIT_LIST_HEAD (&partition->irqfds.items); + +spin_lock_init (&partition->ioeventfds.lock); +INIT_LIST_HEAD (&partition->ioeventfds.items); +} + +void +mshv_eventfd_release (struct mshv_partition *partition) +{ +struct kernel_mshv_ioeventfd *p, *tmp; +unsigned long flags; + +spin_lock_irqsave (&partition->ioeventfds.lock, flags); +list_for_each_entry_safe (p, tmp, &partition->ioeventfds.items, list) { +ioeventfd_release (p, partition->id); + } +spin_unlock_irqrestore (&partition->ioeventfds.lock, flags); + +mshv_irqfd_release (partition); +}
drivers/hv/mshv_main.c +18 -2
@@ -828,6 +828,18 @@ mshv_partition_ioctl_assert_interrupt(struct mshv_partition *partition, args.control); } +static long +mshv_partition_ioctl_ioeventfd (struct mshv_partition *partition, +void __user *user_args) +{ +struct mshv_ioeventfd args; + +if (copy_from_user (&args, user_args,sizeof (args))) +return -EFAULT ; + +return mshv_ioeventfd (partition, &args); +} +static long mshv_partition_ioctl_irqfd (struct mshv_partition *partition,void __user *user_args)
@@ -882,6 +894,10 @@ mshv_partition_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) ret =mshv_partition_ioctl_irqfd (partition, (void __user *)arg);break ; +case MSHV_IOEVENTFD : + ret =mshv_partition_ioctl_ioeventfd (partition, + (void __user *)arg); +break ;default : ret = -ENOTTY ; }
@@ -972,7 +988,7 @@ mshv_partition_release(struct inode *inode, struct file *filp) {struct mshv_partition *partition = filp->private_data; -mshv_irqfd_release (partition); +mshv_eventfd_release (partition);mshv_partition_put (partition);
@@ -1068,7 +1084,7 @@ mshv_ioctl_create_partition(void __user *user_arg)fd_install (fd, file); -mshv_irqfd_init (partition); +mshv_eventfd_init (partition);return fd;
include/linux/mshv.h +4 -0
@@ -52,6 +52,10 @@ struct mshv_partition {spinlock_t lock;struct list_head items; } irqfds; +struct { +spinlock_t lock; +struct list_head items; + } ioeventfds; };struct mshv_lapic_irq {
include/linux/mshv_eventfd.h +17 -4
@@ -1,7 +1,8 @@/* SPDX-License-Identifier: GPL-2.0-only */ /* * -* irqfd: Allows an fd to be used to inject an interrupt to the guest +* irqfd: Allows an fd to be used to inject an interrupt to the guest. +* ioeventfd: Allow an fd to be used to receive a signal from the guest. * All credit goes to kvm developers. */
@@ -11,6 +12,9 @@#include <linux/mshv.h> #include <linux/poll.h> +void mshv_eventfd_init (struct mshv_partition *partition); +void mshv_eventfd_release (struct mshv_partition *partition); +struct mshv_kernel_irqfd {struct mshv_partition *partition;struct eventfd_ctx *eventfd;
@@ -26,10 +30,19 @@ struct mshv_kernel_irqfd {int mshv_irqfd (struct mshv_partition *partition,struct mshv_irqfd *args); -void mshv_irqfd_init (struct mshv_partition *partition); -void mshv_irqfd_release (struct mshv_partition *partition); -int mshv_irqfd_wq_init (void );void mshv_irqfd_wq_cleanup (void ); +struct kernel_mshv_ioeventfd { +struct list_head list; +u64 addr; +int length; +struct eventfd_ctx *eventfd; +u64 datamatch; +int doorbell_id; +bool wildcard; +}; + +int mshv_ioeventfd (struct mshv_partition *kvm,struct mshv_ioeventfd *args); +#endif /* __LINUX_MSHV_EVENTFD_H */
include/uapi/linux/mshv.h +25 -1
@@ -93,6 +93,29 @@ struct mshv_irqfd {__u8 pad[2 ]; }; +enum { + mshv_ioeventfd_flag_nr_datamatch, + mshv_ioeventfd_flag_nr_pio, + mshv_ioeventfd_flag_nr_deassign, + mshv_ioeventfd_flag_nr_max, +}; + +#define MSHV_IOEVENTFD_FLAG_DATAMATCH (1 << mshv_ioeventfd_flag_nr_datamatch) +#define MSHV_IOEVENTFD_FLAG_PIO (1 << mshv_ioeventfd_flag_nr_pio) +#define MSHV_IOEVENTFD_FLAG_DEASSIGN (1 << mshv_ioeventfd_flag_nr_deassign) + +#define MSHV_IOEVENTFD_VALID_FLAG_MASK ((1 << mshv_ioeventfd_flag_nr_max) - 1) + +struct mshv_ioeventfd { +__u64 datamatch; +__u64 addr;/* legal pio/mmio address */ +__u32 len;/* 1, 2, 4, or 8 bytes */ +__s32 fd; +__u32 flags; +__u8 pad[4 ]; +}; + +#define MSHV_IOCTL 0xB8/* mshv device */
@@ -109,7 +132,8 @@ struct mshv_irqfd { _IOW(MSHV_IOCTL, 0xC, struct mshv_partition_property)#define MSHV_GET_PARTITION_PROPERTY \ _IOWR(MSHV_IOCTL, 0xD, struct mshv_partition_property) -#define MSHV_IRQFD _IOW(MSHV_IOCTL, 0xE, struct mshv_irqfd) +#define MSHV_IRQFD _IOW(MSHV_IOCTL, 0xE, struct mshv_irqfd) +#define MSHV_IOEVENTFD _IOW(MSHV_IOCTL, 0xF, struct mshv_ioeventfd)/* vp device */ #define MSHV_GET_VP_REGISTERS _IOWR(MSHV_IOCTL, 0x05, struct mshv_vp_registers)
Signature · 1 line
2.25.1
PATCH 14/17 mshv: Notifier framework for EOI for level triggered interrupts
In reply to Vineeth PillaiA simple in-kernel notifier framework for EOI. Callbacks can be registered for EOI for level-triggered interrupts. This is to be used for irqfd support for level-triggered interrupts.
--- drivers/hv/hv_eventfd.c | 38 ++++++++++++++++++++++++++++++++++++ drivers/hv/hv_synic.c | 22 +++++++++++++++++++++ drivers/hv/mshv_main.c | 4 ++++ include/linux/mshv.h | 9 +++++++++ include/linux/mshv_eventfd.h | 6 ++++++ 5 files changed, 79 insertions(+)
drivers/hv/hv_eventfd.c +38 -0
@@ -22,6 +22,44 @@static struct workqueue_struct *irqfd_cleanup_wq; +void +mshv_register_irq_ack_notifier (struct mshv_partition *partition, +struct mshv_irq_ack_notifier *mian) +{ +spin_lock (&partition->irq_lock); +hlist_add_head_rcu (&mian->link, &partition->irq_ack_notifier_list); +spin_unlock (&partition->irq_lock); +} + +void +mshv_unregister_irq_ack_notifier (struct mshv_partition *partition, +struct mshv_irq_ack_notifier *mian) +{ +spin_lock (&partition->irq_lock); +hlist_del_init_rcu (&mian->link); +spin_unlock (&partition->irq_lock); +synchronize_rcu (); +} + +bool +mshv_notify_acked_gsi (struct mshv_partition *partition,int gsi) +{ +struct mshv_irq_ack_notifier *mian; +bool acked = false; + +rcu_read_lock (); +hlist_for_each_entry_rcu (mian, &partition->irq_ack_notifier_list, + link) { +if (mian->gsi == gsi) { + mian->irq_acked (mian); + acked = true; + } + } +rcu_read_unlock (); + +return acked; +} +static void irqfd_inject (struct mshv_kernel_irqfd *irqfd) {
drivers/hv/hv_synic.c +22 -0
@@ -14,6 +14,7 @@#include <linux/io.h> #include <linux/random.h> #include <linux/mshv.h> +#include <linux/mshv_eventfd.h> #include <asm/mshyperv.h> #include "mshv.h"
@@ -158,6 +159,27 @@ mshv_intercept_isr(struct hv_message *msg) goto unlock_out; } +if (msg->header.message_type ==HVMSG_X64_APIC_EOI ) { +/* +* Check if this gsi is registered in the +* ack_notifier list and invoke the callback +* if registered. +*/ +struct hv_x64_apic_eoi_message *eoi_msg = + (struct hv_x64_apic_eoi_message *)msg->u.payload; + +/* +* If there is a notifier, the ack callback is supposed +* to handle the VMEXIT. So we need not pass this message +* to vcpu thread. +*/ +if (mshv_notify_acked_gsi (partition, + eoi_msg->interrupt_vector)) { + handled = true; + goto unlock_out; + } + } +/* * Since we directly index the vp, and it has to exist for us to be here * (because the vp is only deleted when the partition is), no additional
drivers/hv/mshv_main.c +4 -0
@@ -1047,6 +1047,10 @@ mshv_ioctl_create_partition(void __user *user_arg)mutex_init (&partition->mutex); +spin_lock_init (&partition->irq_lock); + +INIT_HLIST_HEAD (&partition->irq_ack_notifier_list); + fd =get_unused_fd_flags (O_CLOEXEC );if (fd <0 ) { ret = fd;
include/linux/mshv.h +9 -0
@@ -35,6 +35,12 @@ struct mshv_mem_region {struct page **pages; }; +struct mshv_irq_ack_notifier { +struct hlist_node link; +unsigned int gsi; +void (*irq_acked)(struct mshv_irq_ack_notifier *mian); +}; +struct mshv_partition {u64 id;refcount_t ref_count;
@@ -48,6 +54,9 @@ struct mshv_partition {struct mshv_vp *array[MSHV_MAX_VPS ]; } vps; +spinlock_t irq_lock; +struct hlist_head irq_ack_notifier_list; +struct {spinlock_t lock;struct list_head items;
include/linux/mshv_eventfd.h +6 -0
@@ -15,6 +15,12 @@void mshv_eventfd_init (struct mshv_partition *partition);void mshv_eventfd_release (struct mshv_partition *partition); +void mshv_register_irq_ack_notifier (struct mshv_partition *partition, +struct mshv_irq_ack_notifier *mian); +void mshv_unregister_irq_ack_notifier (struct mshv_partition *partition, +struct mshv_irq_ack_notifier *mian); +bool mshv_notify_acked_gsi (struct mshv_partition *partition,int gsi); +struct mshv_kernel_irqfd {struct mshv_partition *partition;struct eventfd_ctx *eventfd;
Signature · 1 line
2.25.1
PATCH 15/17 mshv: Level-triggered interrupt support for irqfd
In reply to Vineeth PillaiTo emulate level triggered interrupts, add a resample option to MSHV_IRQFD. When specified, a new resamplefd is provided that notifies the user when VM EOI a level-triggered interrupt. Also in this mode, posting of an interrupt through an irqfd only asserts the interrupt.
Inspired from the KVM counterpart:
https://patchwork.kernel.org/project/kvm/patch/[email protected]/
--- drivers/hv/hv_eventfd.c | 118 ++++++++++++++++++++++++++++++++++- drivers/hv/mshv_main.c | 8 +++ include/linux/mshv.h | 4 ++ include/linux/mshv_eventfd.h | 22 +++++++ include/uapi/linux/mshv.h | 4 +- 5 files changed, 154 insertions(+), 2 deletions(-)
drivers/hv/hv_eventfd.c +117 -1
@@ -60,16 +60,66 @@ mshv_notify_acked_gsi(struct mshv_partition *partition, int gsi)return acked; } +static void +irqfd_resampler_ack (struct mshv_irq_ack_notifier *mian) +{ +struct mshv_kernel_irqfd_resampler *resampler; +struct mshv_partition *partition; +struct mshv_kernel_irqfd *irqfd; +int idx; + + resampler =container_of (mian, + struct mshv_kernel_irqfd_resampler, notifier); + partition = resampler->partition; + + idx =srcu_read_lock (&partition->irq_srcu); + +list_for_each_entry_rcu (irqfd, &resampler->list, resampler_link) { +if (irqfd->lapic_irq.control.interrupt_type == +HV_X64_INTERRUPT_TYPE_EXTINT ) +hv_call_clear_virtual_interrupt (partition->id); + +eventfd_signal (irqfd->resamplefd,1 ); + } + +srcu_read_unlock (&partition->irq_srcu, idx); +} +static void irqfd_inject (struct mshv_kernel_irqfd *irqfd) {struct mshv_lapic_irq *irq = &irqfd->lapic_irq; +WARN_ON (irqfd->resampler && + !irq->control.level_triggered);hv_call_assert_virtual_interrupt (irqfd->partition->id, irq->vector, irq->apic_id, irq->control); } +static void +irqfd_resampler_shutdown (struct mshv_kernel_irqfd *irqfd) +{ +struct mshv_kernel_irqfd_resampler *resampler = irqfd->resampler; +struct mshv_partition *partition = resampler->partition; + +mutex_lock (&partition->irqfds.resampler_lock); + +list_del_rcu (&irqfd->resampler_link); +synchronize_srcu (&partition->irq_srcu); + +if (list_empty (&resampler->list)) { +list_del (&resampler->link); +mshv_unregister_irq_ack_notifier (partition, &resampler->notifier); +kfree (resampler); + } + +mutex_unlock (&partition->irqfds.resampler_lock); +} + +/* +* Race-free decouple logic (ordering is critical) +*/ static void irqfd_shutdown (struct work_struct *work) {
@@ -82,6 +132,11 @@ irqfd_shutdown(struct work_struct *work)*/ remove_wait_queue (irqfd->wqh, &irqfd->wait); +if (irqfd->resampler) { +irqfd_resampler_shutdown (irqfd); +eventfd_ctx_put (irqfd->resamplefd); + } +/* * It is now safe to release the object's resources */
@@ -168,7 +223,7 @@ mshv_irqfd_assign(struct mshv_partition *partition, {struct mshv_kernel_irqfd *irqfd, *tmp;struct fd f; -struct eventfd_ctx *eventfd =NULL ; +struct eventfd_ctx *eventfd =NULL , *resamplefd =NULL ;int ret;unsigned int events;
@@ -176,6 +231,10 @@ mshv_irqfd_assign(struct mshv_partition *partition,if (!irqfd)return -ENOMEM ; +if (args->flags &MSHV_IRQFD_FLAG_RESAMPLE && + !args->level_triggered) +return -EINVAL ; + irqfd->partition = partition; irqfd->gsi = args->gsi; irqfd->lapic_irq.vector = args->vector;
@@ -200,6 +259,54 @@ mshv_irqfd_assign(struct mshv_partition *partition, irqfd->eventfd = eventfd; +if (args->flags &MSHV_IRQFD_FLAG_RESAMPLE ) { +struct mshv_kernel_irqfd_resampler *resampler; + + resamplefd =eventfd_ctx_fdget (args->resamplefd); +if (IS_ERR (resamplefd)) { + ret =PTR_ERR (resamplefd); + goto fail; + } + + irqfd->resamplefd = resamplefd; +INIT_LIST_HEAD (&irqfd->resampler_link); + +mutex_lock (&partition->irqfds.resampler_lock); + +list_for_each_entry (resampler, + &partition->irqfds.resampler_list, link) { +if (resampler->notifier.gsi == irqfd->gsi) { + irqfd->resampler = resampler; +break ; + } + } + +if (!irqfd->resampler) { + resampler =kzalloc (sizeof (*resampler), +GFP_KERNEL_ACCOUNT ); +if (!resampler) { + ret = -ENOMEM ; +mutex_unlock (&partition->irqfds.resampler_lock); + goto fail; + } + + resampler->partition = partition; +INIT_LIST_HEAD (&resampler->list); + resampler->notifier.gsi = irqfd->gsi; + resampler->notifier.irq_acked = irqfd_resampler_ack; +INIT_LIST_HEAD (&resampler->link); + +list_add (&resampler->link, &partition->irqfds.resampler_list); +mshv_register_irq_ack_notifier (partition, + &resampler->notifier); + irqfd->resampler = resampler; + } + +list_add_rcu (&irqfd->resampler_link, &irqfd->resampler->list); + +mutex_unlock (&partition->irqfds.resampler_lock); + } +/* * Install our own custom wake-up handling so we are notified via * a callback whenever someone signals the underlying eventfd
@@ -238,6 +345,12 @@ mshv_irqfd_assign(struct mshv_partition *partition,return 0 ; fail: +if (irqfd->resampler) +irqfd_resampler_shutdown (irqfd); + +if (resamplefd && !IS_ERR (resamplefd)) +eventfd_ctx_put (resamplefd); +if (eventfd && !IS_ERR (eventfd))eventfd_ctx_put (eventfd);
@@ -541,6 +654,9 @@ mshv_eventfd_init(struct mshv_partition *partition)spin_lock_init (&partition->irqfds.lock);INIT_LIST_HEAD (&partition->irqfds.items); +INIT_LIST_HEAD (&partition->irqfds.resampler_list); +mutex_init (&partition->irqfds.resampler_lock); +spin_lock_init (&partition->ioeventfds.lock);INIT_LIST_HEAD (&partition->ioeventfds.items); }
drivers/hv/mshv_main.c +8 -0
@@ -990,6 +990,8 @@ mshv_partition_release(struct inode *inode, struct file *filp)mshv_eventfd_release (partition); +cleanup_srcu_struct (&partition->irq_srcu); +mshv_partition_put (partition);return 0 ;
@@ -1088,10 +1090,16 @@ mshv_ioctl_create_partition(void __user *user_arg)fd_install (fd, file); + ret =init_srcu_struct (&partition->irq_srcu); +if (ret) + goto cleanup_irq_srcu; +mshv_eventfd_init (partition);return fd; +cleanup_irq_srcu: +cleanup_srcu_struct (&partition->irq_srcu); release_file: file->f_op->release (file->f_inode, file); finalize_partition:
include/linux/mshv.h +4 -0
@@ -10,6 +10,7 @@#include <linux/mutex.h> #include <linux/semaphore.h> #include <linux/sched.h> +#include <linux/srcu.h> #include <uapi/linux/mshv.h> #define MSHV_MAX_PARTITIONS 128
@@ -55,11 +56,14 @@ struct mshv_partition { } vps;spinlock_t irq_lock; +struct srcu_struct irq_srcu;struct hlist_head irq_ack_notifier_list;struct {spinlock_t lock;struct list_head items; +struct mutex resampler_lock; +struct list_head resampler_list; } irqfds;struct {spinlock_t lock;
include/linux/mshv_eventfd.h +22 -0
@@ -21,6 +21,23 @@ void mshv_unregister_irq_ack_notifier(struct mshv_partition *partition, struct mshv_irq_ack_notifier *mian);bool mshv_notify_acked_gsi (struct mshv_partition *partition,int gsi); +struct mshv_kernel_irqfd_resampler { +struct mshv_partition *partition; +/* +* List of irqfds sharing this gsi. +* Protected by irqfds.resampler_lock +* and irq_srcu. +*/ +struct list_head list; +struct mshv_irq_ack_notifier notifier; +/* +* Entry in the list of partition->irqfd.resampler_list. +* Protected by irqfds.resampler_lock +* +*/ +struct list_head link; +}; +struct mshv_kernel_irqfd {struct mshv_partition *partition;struct eventfd_ctx *eventfd;
@@ -31,6 +48,11 @@ struct mshv_kernel_irqfd {wait_queue_head_t *wqh;wait_queue_entry_t wait;struct work_struct shutdown; + +/* Resampler related */ +struct mshv_kernel_irqfd_resampler *resampler; +struct eventfd_ctx *resamplefd; +struct list_head resampler_link; }; int mshv_irqfd(struct mshv_partition *partition,
include/uapi/linux/mshv.h +3 -1
@@ -80,17 +80,19 @@ struct mshv_translate_gva { };#define MSHV_IRQFD_FLAG_DEASSIGN (1 << 0) +#define MSHV_IRQFD_FLAG_RESAMPLE (1 << 1)struct mshv_irqfd {__u64 apic_id;__s32 fd; +__s32 resamplefd;__u32 gsi;__u32 vector;__u32 interrupt_type;__u32 flags;__u8 level_triggered;__u8 logical_dest_mode; -__u8 pad[2 ]; +__u8 pad[6 ]; };enum {
Signature · 1 line
2.25.1
PATCH 16/17 mshv: User space controlled MSI irq routing for mshv
In reply to Vineeth PillaiImplementation of an in-kernel MSI irq routing mechanism for mshv.
Inspired from the KVM irq routing implementation but adapted only for MSI interrupts.
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=399ec807ddc38ecccf8c06dbde04531cbdc63e11
All credit goes to kvm developers.
--- drivers/hv/Makefile | 2 +- drivers/hv/mshv_main.c | 34 ++++++++++ drivers/hv/mshv_msi.c | 127 ++++++++++++++++++++++++++++++++++++++ include/linux/mshv.h | 27 ++++++++ include/uapi/linux/mshv.h | 13 ++++ 5 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 drivers/hv/mshv_msi.c
drivers/hv/Makefile +1 -1
@@ -14,4 +14,4 @@ hv_vmbus-$(CONFIG_HYPERV_TESTING) += hv_debugfs.o hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_fcopy.o hv_utils_transport.o mshv-y += mshv_main.o hv_call.o hv_synic.o hv_portid_table.o \ - hv_eventfd.o + hv_eventfd.o mshv_msi.o
drivers/hv/mshv_main.c +34 -0
@@ -852,6 +852,35 @@ mshv_partition_ioctl_irqfd(struct mshv_partition *partition,return mshv_irqfd (partition, &args); } +static long +mshv_partition_ioctl_set_msi_routing (struct mshv_partition *partition, +void __user *user_args) +{ +struct mshv_msi_routing_entry *entries =NULL ; +struct mshv_msi_routing args; +long ret; + +if (copy_from_user (&args, user_args,sizeof (args))) +return -EFAULT ; + +if (args.nr >MSHV_MAX_MSI_ROUTES ) +return -EINVAL ; + +if (args.nr) { +struct mshv_msi_routing __user *urouting = user_args; + + entries =vmemdup_user (urouting->entries, +array_size (sizeof (*entries), + args.nr)); +if (IS_ERR (entries)) +return PTR_ERR (entries); + } + ret =mshv_set_msi_routing (partition, entries, args.nr); +kvfree (entries); + +return ret; +} +static long mshv_partition_ioctl (struct file *filp,unsigned int ioctl,unsigned long arg) {
@@ -898,6 +927,10 @@ mshv_partition_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) ret =mshv_partition_ioctl_ioeventfd (partition, (void __user *)arg);break ; +case MSHV_SET_MSI_ROUTING : + ret =mshv_partition_ioctl_set_msi_routing (partition, + (void __user *)arg); +break ;default : ret = -ENOTTY ; }
@@ -965,6 +998,7 @@ destroy_partition(struct mshv_partition *partition)vfree (region->pages); } +mshv_free_msi_routing (partition);kfree (partition); }
drivers/hv/mshv_msi.c new file +127 -0
127 lines not on this page · raw message
include/linux/mshv.h +27 -0
@@ -69,6 +69,7 @@ struct mshv_partition {spinlock_t lock;struct list_head items; } ioeventfds; +struct mshv_msi_routing_table __rcu *msi_routing; };struct mshv_lapic_irq {
@@ -77,6 +78,32 @@ struct mshv_lapic_irq {union hv_interrupt_control control; }; +#define MSHV_MAX_MSI_ROUTES 4096 + +struct mshv_kernel_msi_routing_entry { +u32 entry_valid; +u32 gsi; +u32 address_lo; +u32 address_hi; +u32 data; +}; + +struct mshv_msi_routing_table { +u32 nr_rt_entries; +struct mshv_kernel_msi_routing_entry entries[]; +}; + +int mshv_set_msi_routing (struct mshv_partition *partition, +const struct mshv_msi_routing_entry *entries, +unsigned int nr); +void mshv_free_msi_routing (struct mshv_partition *partition); + +struct mshv_kernel_msi_routing_entry mshv_msi_map_gsi ( +struct mshv_partition *partition,u32 gsi); + +void mshv_set_msi_irq (struct mshv_kernel_msi_routing_entry *e, +struct mshv_lapic_irq *irq); +struct hv_synic_pages {struct hv_message_page *synic_message_page;struct hv_synic_event_flags_page *synic_event_flags_page;
include/uapi/linux/mshv.h +13 -0
@@ -117,6 +117,18 @@ struct mshv_ioeventfd {__u8 pad[4 ]; }; +struct mshv_msi_routing_entry { +__u32 gsi; +__u32 address_lo; +__u32 address_hi; +__u32 data; +}; + +struct mshv_msi_routing { +__u32 nr; +__u32 pad; +struct mshv_msi_routing_entry entries[0 ]; +};#define MSHV_IOCTL 0xB8
@@ -136,6 +148,7 @@ struct mshv_ioeventfd { _IOWR(MSHV_IOCTL, 0xD, struct mshv_partition_property)#define MSHV_IRQFD _IOW(MSHV_IOCTL, 0xE, struct mshv_irqfd)#define MSHV_IOEVENTFD _IOW(MSHV_IOCTL, 0xF, struct mshv_ioeventfd) +#define MSHV_SET_MSI_ROUTING _IOW(MSHV_IOCTL, 0x11, struct mshv_msi_routing)/* vp device */ #define MSHV_GET_VP_REGISTERS _IOWR(MSHV_IOCTL, 0x05, struct mshv_vp_registers)
Signature · 1 line
2.25.1
PATCH 17/17 mshv: Use in kernel MSI routing for irqfd
In reply to Vineeth PillaiUse the in-kernel routing information for irqfd interrupts.
--- drivers/hv/hv_eventfd.c | 94 +++++++++++++++++++++++++++--------- drivers/hv/mshv_msi.c | 1 + include/linux/mshv.h | 2 + include/linux/mshv_eventfd.h | 26 +++++----- include/uapi/linux/mshv.h | 6 --- 5 files changed, 87 insertions(+), 42 deletions(-)
drivers/hv/hv_eventfd.c +70 -24
@@ -88,13 +88,32 @@ irqfd_resampler_ack(struct mshv_irq_ack_notifier *mian)static void irqfd_inject (struct mshv_kernel_irqfd *irqfd) { +struct mshv_partition *partition = irqfd->partition;struct mshv_lapic_irq *irq = &irqfd->lapic_irq; +unsigned int seq; +int idx;WARN_ON (irqfd->resampler && !irq->control.level_triggered); + + idx =srcu_read_lock (&partition->irq_srcu); +if (irqfd->msi_entry.gsi) { +if (!irqfd->msi_entry.entry_valid) { +pr_warn ("Invalid routing info for gsi %u" , + irqfd->msi_entry.gsi); +srcu_read_unlock (&partition->irq_srcu, idx); +return ; + } + +do { + seq =read_seqcount_begin (&irqfd->msi_entry_sc); + }while (read_seqcount_retry (&irqfd->msi_entry_sc, seq)); + } +hv_call_assert_virtual_interrupt (irqfd->partition->id, irq->vector, irq->apic_id, irq->control); +srcu_read_unlock (&partition->irq_srcu, idx); } static void
@@ -206,6 +225,27 @@ irqfd_wakeup(wait_queue_entry_t *wait, unsigned int mode,return 0 ; } +/* Must be called under irqfds.lock */ +static void irqfd_update (struct mshv_partition *partition, +struct mshv_kernel_irqfd *irqfd) +{ +write_seqcount_begin (&irqfd->msi_entry_sc); + irqfd->msi_entry =mshv_msi_map_gsi (partition, irqfd->gsi); +mshv_set_msi_irq (&irqfd->msi_entry, &irqfd->lapic_irq); +write_seqcount_end (&irqfd->msi_entry_sc); +} + +void mshv_irqfd_routing_update (struct mshv_partition *partition) +{ +struct mshv_kernel_irqfd *irqfd; + +spin_lock_irq (&partition->irqfds.lock); +list_for_each_entry (irqfd, &partition->irqfds.items, list) { +irqfd_update (partition, irqfd); + } +spin_unlock_irq (&partition->irqfds.lock); +} +static void irqfd_ptable_queue_proc (struct file *file,wait_queue_head_t *wqh,poll_table *pt)
@@ -221,29 +261,23 @@ static int mshv_irqfd_assign(struct mshv_partition *partition, struct mshv_irqfd *args) { + struct eventfd_ctx *eventfd = NULL, *resamplefd = NULL;struct mshv_kernel_irqfd *irqfd, *tmp; +unsigned int events;struct fd f; -struct eventfd_ctx *eventfd =NULL , *resamplefd =NULL ;int ret; -unsigned int events; +int idx; irqfd =kzalloc (sizeof (*irqfd),GFP_KERNEL );if (!irqfd)return -ENOMEM ; -if (args->flags &MSHV_IRQFD_FLAG_RESAMPLE && - !args->level_triggered) -return -EINVAL ; - irqfd->partition = partition; irqfd->gsi = args->gsi; - irqfd->lapic_irq.vector = args->vector; - irqfd->lapic_irq.apic_id = args->apic_id; - irqfd->lapic_irq.control.interrupt_type = args->interrupt_type; - irqfd->lapic_irq.control.level_triggered = args->level_triggered; - irqfd->lapic_irq.control.logical_dest_mode = args->logical_dest_mode;INIT_LIST_HEAD (&irqfd->list);INIT_WORK (&irqfd->shutdown, irqfd_shutdown); +seqcount_spinlock_init (&irqfd->msi_entry_sc, + &partition->irqfds.lock); f =fdget (args->fd);if (!f.file) {
@@ -259,6 +293,31 @@ mshv_irqfd_assign(struct mshv_partition *partition, irqfd->eventfd = eventfd; + +spin_lock_irq (&partition->irqfds.lock); + idx =srcu_read_lock (&partition->irq_srcu); +irqfd_update (partition, irqfd); +srcu_read_unlock (&partition->irq_srcu, idx); + +if (args->flags &MSHV_IRQFD_FLAG_RESAMPLE && + !irqfd->lapic_irq.control.level_triggered) { +spin_unlock_irq (&partition->irqfds.lock); + ret = -EINVAL ; + goto fail; + } + + ret =0 ; +list_for_each_entry (tmp, &partition->irqfds.items, list) { +if (irqfd->eventfd != tmp->eventfd) +continue ; +/* This fd is used for another irq already. */ + ret = -EBUSY ; +spin_unlock_irq (&partition->irqfds.lock); + goto fail; + } +list_add_tail (&irqfd->list, &partition->irqfds.items); +spin_unlock_irq (&partition->irqfds.lock); +if (args->flags &MSHV_IRQFD_FLAG_RESAMPLE ) {struct mshv_kernel_irqfd_resampler *resampler;
@@ -314,19 +373,6 @@ mshv_irqfd_assign(struct mshv_partition *partition,init_waitqueue_func_entry (&irqfd->wait, irqfd_wakeup);init_poll_funcptr (&irqfd->pt, irqfd_ptable_queue_proc); -spin_lock_irq (&partition->irqfds.lock); - ret =0 ; -list_for_each_entry (tmp, &partition->irqfds.items, list) { -if (irqfd->eventfd != tmp->eventfd) -continue ; -/* This fd is used for another irq already. */ - ret = -EBUSY ; -spin_unlock_irq (&partition->irqfds.lock); - goto fail; - } -list_add_tail (&irqfd->list, &partition->irqfds.items); -spin_unlock_irq (&partition->irqfds.lock); -/* * Check if there was an event already pending on the eventfd * before we registered, and trigger it as if we didn't miss it.
drivers/hv/mshv_msi.c +1 -0
@@ -71,6 +71,7 @@ int mshv_set_msi_routing(struct mshv_partition *partition,spin_lock (&partition->irq_lock); old =rcu_dereference_protected (partition->msi_routing,1 );rcu_assign_pointer (partition->msi_routing, new); +mshv_irqfd_routing_update (partition);spin_unlock (&partition->irq_lock);synchronize_srcu_expedited (&partition->irq_srcu);
include/linux/mshv.h +2 -0
@@ -104,6 +104,8 @@ struct mshv_kernel_msi_routing_entry mshv_msi_map_gsi(void mshv_set_msi_irq (struct mshv_kernel_msi_routing_entry *e,struct mshv_lapic_irq *irq); +void mshv_irqfd_routing_update (struct mshv_partition *partition); +struct hv_synic_pages {struct hv_message_page *synic_message_page;struct hv_synic_event_flags_page *synic_event_flags_page;
include/linux/mshv_eventfd.h +14 -12
@@ -39,20 +39,22 @@ struct mshv_kernel_irqfd_resampler { };struct mshv_kernel_irqfd { -struct mshv_partition *partition; -struct eventfd_ctx *eventfd; -u32 gsi; -struct mshv_lapic_irq lapic_irq; -struct list_head list; -poll_table pt; -wait_queue_head_t *wqh; -wait_queue_entry_t wait; -struct work_struct shutdown; +struct mshv_partition *partition; +struct eventfd_ctx *eventfd; +struct mshv_kernel_msi_routing_entry msi_entry; +seqcount_spinlock_t msi_entry_sc; +u32 gsi; +struct mshv_lapic_irq lapic_irq; +struct list_head list; +poll_table pt; +wait_queue_head_t *wqh; +wait_queue_entry_t wait; +struct work_struct shutdown;/* Resampler related */ -struct mshv_kernel_irqfd_resampler *resampler; -struct eventfd_ctx *resamplefd; -struct list_head resampler_link; +struct mshv_kernel_irqfd_resampler *resampler; +struct eventfd_ctx *resamplefd; +struct list_head resampler_link; }; int mshv_irqfd(struct mshv_partition *partition,
include/uapi/linux/mshv.h +0 -6
@@ -83,16 +83,10 @@ struct mshv_translate_gva {#define MSHV_IRQFD_FLAG_RESAMPLE (1 << 1)struct mshv_irqfd { -__u64 apic_id;__s32 fd;__s32 resamplefd;__u32 gsi; -__u32 vector; -__u32 interrupt_type;__u32 flags; -__u8 level_triggered; -__u8 logical_dest_mode; -__u8 pad[6 ]; };enum {
Signature · 1 line
2.25.1