Move definitions related to the Hyper-V SynIC event flags to a header where they can be consumed by userspace. While doing so, also clean up their use by switching to standard bitops and struct-based dereferencing. The latter is also done for message pages. Signed-off-by: Roman Kagan <rkagan@xxxxxxxxxxxxx> --- arch/x86/include/uapi/asm/hyperv.h | 13 +++++++++ drivers/hv/hyperv_vmbus.h | 24 ++-------------- drivers/hv/channel_mgmt.c | 10 +++---- drivers/hv/connection.c | 47 ++++++++++--------------------- drivers/hv/vmbus_drv.c | 57 ++++++++++++++------------------------ 5 files changed, 54 insertions(+), 97 deletions(-) diff --git a/arch/x86/include/uapi/asm/hyperv.h b/arch/x86/include/uapi/asm/hyperv.h index 6098ab5..af542a3 100644 --- a/arch/x86/include/uapi/asm/hyperv.h +++ b/arch/x86/include/uapi/asm/hyperv.h @@ -363,4 +363,17 @@ struct hv_timer_message_payload { #define HV_STIMER_AUTOENABLE (1ULL << 3) #define HV_STIMER_SINT(config) (__u8)(((config) >> 16) & 0x0F) +/* Define synthetic interrupt controller flag constants. */ +#define HV_EVENT_FLAGS_COUNT (256 * 8) + +/* Define the synthetic interrupt controller event flags format. */ +struct hv_synic_event_flags { + __u64 flags[HV_EVENT_FLAGS_COUNT / 64]; +}; + +/* Define the synthetic interrupt flags page layout. */ +struct hv_synic_event_flags_page { + struct hv_synic_event_flags sintevent_flags[HV_SYNIC_SINT_COUNT]; +}; + #endif diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 4516498..4fab154 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -26,7 +26,6 @@ #define _HYPERV_VMBUS_H #include <linux/list.h> -#include <asm/sync_bitops.h> #include <linux/atomic.h> #include <linux/hyperv.h> @@ -75,11 +74,6 @@ enum hv_cpuid_function { #define HV_ANY_VP (0xFFFFFFFF) -/* Define synthetic interrupt controller flag constants. */ -#define HV_EVENT_FLAGS_COUNT (256 * 8) -#define HV_EVENT_FLAGS_BYTE_COUNT (256) -#define HV_EVENT_FLAGS_DWORD_COUNT (256 / sizeof(u32)) - /* Define invalid partition identifier. */ #define HV_PARTITION_ID_INVALID ((u64)0x0) @@ -146,20 +140,6 @@ union hv_timer_config { }; }; -/* Define the number of message buffers associated with each port. */ -#define HV_PORT_MESSAGE_BUFFER_COUNT (16) - -/* Define the synthetic interrupt controller event flags format. */ -union hv_synic_event_flags { - u8 flags8[HV_EVENT_FLAGS_BYTE_COUNT]; - u32 flags32[HV_EVENT_FLAGS_DWORD_COUNT]; -}; - -/* Define the synthetic interrupt flags page layout. */ -struct hv_synic_event_flags_page { - union hv_synic_event_flags sintevent_flags[HV_SYNIC_SINT_COUNT]; -}; - /* Define SynIC control register. */ union hv_synic_scontrol { u64 as_uint64; @@ -434,8 +414,8 @@ struct hv_context { bool synic_initialized; - void *synic_message_page[NR_CPUS]; - void *synic_event_page[NR_CPUS]; + struct hv_message_page *synic_message_page[NR_CPUS]; + struct hv_synic_event_flags_page *synic_event_page[NR_CPUS]; /* * Hypervisor's notion of virtual processor ID is different from * Linux' notion of CPU ID. This information can only be retrieved diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 26b4192..49eaae2 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -654,7 +654,6 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type) static void vmbus_wait_for_unload(void) { int cpu; - void *page_addr; struct hv_message *msg; struct vmbus_channel_message_header *hdr; u32 message_type; @@ -673,9 +672,8 @@ static void vmbus_wait_for_unload(void) break; for_each_online_cpu(cpu) { - page_addr = hv_context.synic_message_page[cpu]; - msg = (struct hv_message *)page_addr + - VMBUS_MESSAGE_SINT; + msg = &hv_context.synic_message_page[cpu]-> + sint_message[VMBUS_MESSAGE_SINT]; message_type = READ_ONCE(msg->header.message_type); if (message_type == HVMSG_NONE) @@ -699,8 +697,8 @@ static void vmbus_wait_for_unload(void) * messages after we reconnect. */ for_each_online_cpu(cpu) { - page_addr = hv_context.synic_message_page[cpu]; - msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; + msg = &hv_context.synic_message_page[cpu]-> + sint_message[VMBUS_MESSAGE_SINT]; msg->header.message_type = HVMSG_NONE; } } diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index 6ce8b87..aaa2103 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -381,17 +381,12 @@ static void process_chn_event(u32 relid) */ void vmbus_on_event(unsigned long data) { - u32 dword; - u32 maxdword; - int bit; - u32 relid; - u32 *recv_int_page = NULL; - void *page_addr; + u32 relid, max_relid; + unsigned long *recv_int_page; int cpu = smp_processor_id(); - union hv_synic_event_flags *event; if (vmbus_proto_version < VERSION_WIN8) { - maxdword = MAX_NUM_CHANNELS_SUPPORTED >> 5; + max_relid = MAX_NUM_CHANNELS_SUPPORTED; recv_int_page = vmbus_connection.recv_int_page; } else { /* @@ -399,36 +394,22 @@ void vmbus_on_event(unsigned long data) * can be directly checked to get the id of the channel * that has the interrupt pending. */ - maxdword = HV_EVENT_FLAGS_DWORD_COUNT; - page_addr = hv_context.synic_event_page[cpu]; - event = (union hv_synic_event_flags *)page_addr + - VMBUS_MESSAGE_SINT; - recv_int_page = event->flags32; + struct hv_synic_event_flags *event = + &hv_context.synic_event_page[cpu]-> + sintevent_flags[VMBUS_MESSAGE_SINT]; + max_relid = HV_EVENT_FLAGS_COUNT; + recv_int_page = (unsigned long *)event->flags; } - - /* Check events */ if (!recv_int_page) return; - for (dword = 0; dword < maxdword; dword++) { - if (!recv_int_page[dword]) - continue; - for (bit = 0; bit < 32; bit++) { - if (sync_test_and_clear_bit(bit, - (unsigned long *)&recv_int_page[dword])) { - relid = (dword << 5) + bit; - - if (relid == 0) - /* - * Special case - vmbus - * channel protocol msg - */ - continue; - - process_chn_event(relid); - } - } + + /* relid == 0 is vmbus channel protocol msg */ + relid = 1; + for_each_set_bit_from(relid, recv_int_page, max_relid) { + clear_bit(relid, recv_int_page); + process_chn_event(relid); } } diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 230c62e..13dd210 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -872,9 +872,8 @@ static void hv_process_timer_expiration(struct hv_message *msg, int cpu) void vmbus_on_msg_dpc(unsigned long data) { int cpu = smp_processor_id(); - void *page_addr = hv_context.synic_message_page[cpu]; - struct hv_message *msg = (struct hv_message *)page_addr + - VMBUS_MESSAGE_SINT; + struct hv_message *msg = &hv_context.synic_message_page[cpu]-> + sint_message[VMBUS_MESSAGE_SINT]; struct vmbus_channel_message_header *hdr; struct vmbus_channel_message_table_entry *entry; struct onmessage_work_context *ctx; @@ -911,54 +910,40 @@ void vmbus_on_msg_dpc(unsigned long data) static void vmbus_isr(void) { int cpu = smp_processor_id(); - void *page_addr; struct hv_message *msg; - union hv_synic_event_flags *event; - bool handled = false; + struct hv_synic_event_flags *event; - page_addr = hv_context.synic_event_page[cpu]; - if (page_addr == NULL) + if (!hv_context.synic_event_page[cpu]) return; - event = (union hv_synic_event_flags *)page_addr + - VMBUS_MESSAGE_SINT; + event = &hv_context.synic_event_page[cpu]-> + sintevent_flags[VMBUS_MESSAGE_SINT]; /* * Check for events before checking for messages. This is the order * in which events and messages are checked in Windows guests on * Hyper-V, and the Windows team suggested we do the same. */ - if ((vmbus_proto_version == VERSION_WS2008) || - (vmbus_proto_version == VERSION_WIN7)) { - + /* On win8 and above the channel interrupts are signaled directly in + * the event page and will be checked in the .event_dpc + */ + if (vmbus_proto_version >= VERSION_WIN8 || /* Since we are a child, we only need to check bit 0 */ - if (sync_test_and_clear_bit(0, - (unsigned long *) &event->flags32[0])) { - handled = true; - } - } else { - /* - * Our host is win8 or above. The signaling mechanism - * has changed and we can directly look at the event page. - * If bit n is set then we have an interrup on the channel - * whose id is n. - */ - handled = true; - } - - if (handled) + test_and_clear_bit(0, (unsigned long *)event->flags)) tasklet_schedule(hv_context.event_dpc[cpu]); - - page_addr = hv_context.synic_message_page[cpu]; - msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; + msg = &hv_context.synic_message_page[cpu]-> + sint_message[VMBUS_MESSAGE_SINT]; /* Check if there are actual msgs to be processed */ - if (msg->header.message_type != HVMSG_NONE) { - if (msg->header.message_type == HVMSG_TIMER_EXPIRED) - hv_process_timer_expiration(msg, cpu); - else - tasklet_schedule(hv_context.msg_dpc[cpu]); + switch (READ_ONCE(msg->header.message_type)) { + case HVMSG_NONE: + break; + case HVMSG_TIMER_EXPIRED: + hv_process_timer_expiration(msg, cpu); + break; + default: + tasklet_schedule(hv_context.msg_dpc[cpu]); } add_interrupt_randomness(HYPERVISOR_CALLBACK_VECTOR, 0); -- 2.9.3 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html