MFC 299927,300101,300102,300105,300107
299927 hyperv/vmbus: Use atomic_testandclear Prepare to use unsigned long for event channel bit array. Sponsored by: Microsoft OSTC Differential Revision: https://reviews.freebsd.org/D6382 300101 hyperv/vmbus: Use unsigned long for event bits. And move base channel id calculation out of inner loop. This prepares for more event processing optimization. MFC after: 1 week Sponsored by: Microsoft OSTC Differential Revision: https://reviews.freebsd.org/D6384 300102 hyperv/vmbus: Reduce the # of event loops by recording event flag count Use vmbus softc to save vmbus per-cpu data. More stuffs will be moved into vmbus softc. MFC after: 1 week Sponsored by: Microsoft OSTC Differential Revision: https://reviews.freebsd.org/D6403 300105 hyperv/vmbus: Use atomic swap and flsl to process event flags Greatly reduce the locked instructions and reduce number of inner loops. MFC after: 1 week Sponsored by: Microsoft OSTC Differential Revision: https://reviews.freebsd.org/D6404 300107 hyperv/vmbus: Avoid two unnecessary protocol checks on isr handling path MFC after: 1 week Sponsored by: Microsoft OSTC Differential Revision: https://reviews.freebsd.org/D6405
This commit is contained in:
parent
ee9748f41f
commit
8fb54509bd
@ -42,7 +42,8 @@ __FBSDID("$FreeBSD$");
|
||||
#include <vm/vm_param.h>
|
||||
#include <vm/pmap.h>
|
||||
|
||||
#include "hv_vmbus_priv.h"
|
||||
#include <dev/hyperv/vmbus/hv_vmbus_priv.h>
|
||||
#include <dev/hyperv/vmbus/vmbus_var.h>
|
||||
|
||||
static int vmbus_channel_create_gpadl_header(
|
||||
/* must be phys and virt contiguous*/
|
||||
@ -199,6 +200,8 @@ hv_vmbus_channel_open(
|
||||
new_channel->on_channel_callback = pfn_on_channel_callback;
|
||||
new_channel->channel_callback_context = context;
|
||||
|
||||
vmbus_on_channel_open(new_channel);
|
||||
|
||||
new_channel->rxq = hv_vmbus_g_context.hv_event_queue[new_channel->target_cpu];
|
||||
TASK_INIT(&new_channel->channel_task, 0, VmbusProcessChannelEvent, new_channel);
|
||||
|
||||
|
@ -36,11 +36,13 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <machine/bus.h>
|
||||
#include <machine/atomic.h>
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
#include <vm/pmap.h>
|
||||
|
||||
#include "hv_vmbus_priv.h"
|
||||
#include <dev/hyperv/vmbus/hv_vmbus_priv.h>
|
||||
#include <dev/hyperv/vmbus/vmbus_var.h>
|
||||
|
||||
/*
|
||||
* Globals
|
||||
@ -290,76 +292,71 @@ hv_vmbus_disconnect(void) {
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for events
|
||||
*/
|
||||
void
|
||||
hv_vmbus_on_events(int cpu)
|
||||
static __inline void
|
||||
vmbus_event_flags_proc(unsigned long *event_flags, int flag_cnt)
|
||||
{
|
||||
int bit;
|
||||
int dword;
|
||||
void *page_addr;
|
||||
uint32_t* recv_interrupt_page = NULL;
|
||||
int rel_id;
|
||||
int maxdword;
|
||||
hv_vmbus_synic_event_flags *event;
|
||||
int f;
|
||||
|
||||
KASSERT(cpu <= mp_maxid, ("VMBUS: hv_vmbus_on_events: "
|
||||
"cpu out of range!"));
|
||||
for (f = 0; f < flag_cnt; ++f) {
|
||||
uint32_t rel_id_base;
|
||||
unsigned long flags;
|
||||
int bit;
|
||||
|
||||
page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu];
|
||||
event = (hv_vmbus_synic_event_flags *)
|
||||
page_addr + HV_VMBUS_MESSAGE_SINT;
|
||||
if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) ||
|
||||
(hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)) {
|
||||
maxdword = HV_MAX_NUM_CHANNELS_SUPPORTED >> 5;
|
||||
/*
|
||||
* receive size is 1/2 page and divide that by 4 bytes
|
||||
*/
|
||||
if (synch_test_and_clear_bit(0, &event->flags32[0])) {
|
||||
recv_interrupt_page =
|
||||
hv_vmbus_g_connection.recv_interrupt_page;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* On Host with Win8 or above, the event page can be
|
||||
* checked directly to get the id of the channel
|
||||
* that has the pending interrupt.
|
||||
*/
|
||||
maxdword = HV_EVENT_FLAGS_DWORD_COUNT;
|
||||
recv_interrupt_page = event->flags32;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check events
|
||||
*/
|
||||
for (dword = 0; dword < maxdword; dword++) {
|
||||
if (recv_interrupt_page[dword] == 0)
|
||||
if (event_flags[f] == 0)
|
||||
continue;
|
||||
|
||||
for (bit = 0; bit < HV_CHANNEL_DWORD_LEN; bit++) {
|
||||
if (synch_test_and_clear_bit(bit,
|
||||
(uint32_t *)&recv_interrupt_page[dword])) {
|
||||
struct hv_vmbus_channel *channel;
|
||||
flags = atomic_swap_long(&event_flags[f], 0);
|
||||
rel_id_base = f << HV_CHANNEL_ULONG_SHIFT;
|
||||
|
||||
rel_id = (dword << 5) + bit;
|
||||
channel =
|
||||
hv_vmbus_g_connection.channels[rel_id];
|
||||
while ((bit = ffsl(flags)) != 0) {
|
||||
struct hv_vmbus_channel *channel;
|
||||
uint32_t rel_id;
|
||||
|
||||
/* if channel is closed or closing */
|
||||
if (channel == NULL || channel->rxq == NULL)
|
||||
continue;
|
||||
--bit; /* NOTE: ffsl is 1-based */
|
||||
flags &= ~(1UL << bit);
|
||||
|
||||
if (channel->batched_reading) {
|
||||
hv_ring_buffer_read_begin(
|
||||
&channel->inbound);
|
||||
}
|
||||
taskqueue_enqueue(channel->rxq,
|
||||
&channel->channel_task);
|
||||
}
|
||||
}
|
||||
rel_id = rel_id_base + bit;
|
||||
channel = hv_vmbus_g_connection.channels[rel_id];
|
||||
|
||||
/* if channel is closed or closing */
|
||||
if (channel == NULL || channel->rxq == NULL)
|
||||
continue;
|
||||
|
||||
if (channel->batched_reading)
|
||||
hv_ring_buffer_read_begin(&channel->inbound);
|
||||
taskqueue_enqueue(channel->rxq, &channel->channel_task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
vmbus_event_proc(struct vmbus_softc *sc, int cpu)
|
||||
{
|
||||
hv_vmbus_synic_event_flags *event;
|
||||
|
||||
event = ((hv_vmbus_synic_event_flags *)
|
||||
hv_vmbus_g_context.syn_ic_event_page[cpu]) + HV_VMBUS_MESSAGE_SINT;
|
||||
|
||||
/*
|
||||
* On Host with Win8 or above, the event page can be checked directly
|
||||
* to get the id of the channel that has the pending interrupt.
|
||||
*/
|
||||
vmbus_event_flags_proc(event->flagsul,
|
||||
VMBUS_SC_PCPU_GET(sc, event_flag_cnt, cpu));
|
||||
}
|
||||
|
||||
void
|
||||
vmbus_event_proc_compat(struct vmbus_softc *sc __unused, int cpu)
|
||||
{
|
||||
hv_vmbus_synic_event_flags *event;
|
||||
|
||||
event = ((hv_vmbus_synic_event_flags *)
|
||||
hv_vmbus_g_context.syn_ic_event_page[cpu]) + HV_VMBUS_MESSAGE_SINT;
|
||||
|
||||
if (atomic_testandclear_int(&event->flags32[0], 0)) {
|
||||
vmbus_event_flags_proc(
|
||||
hv_vmbus_g_connection.recv_interrupt_page,
|
||||
HV_MAX_NUM_CHANNELS_SUPPORTED >> HV_CHANNEL_ULONG_SHIFT);
|
||||
}
|
||||
}
|
||||
|
||||
@ -414,3 +411,30 @@ hv_vmbus_set_event(hv_vmbus_channel *channel) {
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
void
|
||||
vmbus_on_channel_open(const struct hv_vmbus_channel *chan)
|
||||
{
|
||||
volatile int *flag_cnt_ptr;
|
||||
int flag_cnt;
|
||||
|
||||
flag_cnt = (chan->offer_msg.child_rel_id / HV_CHANNEL_ULONG_LEN) + 1;
|
||||
flag_cnt_ptr = VMBUS_PCPU_PTR(event_flag_cnt, chan->target_cpu);
|
||||
|
||||
for (;;) {
|
||||
int old_flag_cnt;
|
||||
|
||||
old_flag_cnt = *flag_cnt_ptr;
|
||||
if (old_flag_cnt >= flag_cnt)
|
||||
break;
|
||||
if (atomic_cmpset_int(flag_cnt_ptr, old_flag_cnt, flag_cnt)) {
|
||||
if (bootverbose) {
|
||||
printf("VMBUS: channel%u update "
|
||||
"cpu%d flag_cnt to %d\n",
|
||||
chan->offer_msg.child_rel_id,
|
||||
chan->target_cpu, flag_cnt);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,11 +60,14 @@ __FBSDID("$FreeBSD$");
|
||||
#include <machine/apicvar.h>
|
||||
|
||||
#include <dev/hyperv/include/hyperv.h>
|
||||
#include "hv_vmbus_priv.h"
|
||||
#include <dev/hyperv/vmbus/hv_vmbus_priv.h>
|
||||
#include <dev/hyperv/vmbus/vmbus_var.h>
|
||||
|
||||
#include <contrib/dev/acpica/include/acpi.h>
|
||||
#include "acpi_if.h"
|
||||
|
||||
struct vmbus_softc *vmbus_sc;
|
||||
|
||||
static device_t vmbus_devp;
|
||||
static int vmbus_inited;
|
||||
static hv_setup_args setup_args; /* only CPU 0 supported at this time */
|
||||
@ -143,6 +146,7 @@ vmbus_msg_swintr(void *arg, int pending __unused)
|
||||
static inline int
|
||||
hv_vmbus_isr(struct trapframe *frame)
|
||||
{
|
||||
struct vmbus_softc *sc = vmbus_get_softc();
|
||||
int cpu;
|
||||
hv_vmbus_message* msg;
|
||||
void* page_addr;
|
||||
@ -154,8 +158,7 @@ hv_vmbus_isr(struct trapframe *frame)
|
||||
* before checking for messages. This is the way they do it
|
||||
* in Windows when running as a guest in Hyper-V
|
||||
*/
|
||||
|
||||
hv_vmbus_on_events(cpu);
|
||||
sc->vmbus_event_proc(sc, cpu);
|
||||
|
||||
/* Check if there are actual msgs to be process */
|
||||
page_addr = hv_vmbus_g_context.syn_ic_msg_page[cpu];
|
||||
@ -455,6 +458,7 @@ vmbus_cpuset_setthread_task(void *xmask, int pending __unused)
|
||||
static int
|
||||
vmbus_bus_init(void)
|
||||
{
|
||||
struct vmbus_softc *sc;
|
||||
int i, j, n, ret;
|
||||
char buf[MAXCOMLEN + 1];
|
||||
cpuset_t cpu_mask;
|
||||
@ -463,6 +467,7 @@ vmbus_bus_init(void)
|
||||
return (0);
|
||||
|
||||
vmbus_inited = 1;
|
||||
sc = vmbus_get_softc();
|
||||
|
||||
ret = hv_vmbus_init();
|
||||
|
||||
@ -559,6 +564,12 @@ vmbus_bus_init(void)
|
||||
if (ret != 0)
|
||||
goto cleanup1;
|
||||
|
||||
if (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008 ||
|
||||
hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)
|
||||
sc->vmbus_event_proc = vmbus_event_proc_compat;
|
||||
else
|
||||
sc->vmbus_event_proc = vmbus_event_proc;
|
||||
|
||||
hv_vmbus_request_channel_offers();
|
||||
|
||||
vmbus_scan();
|
||||
@ -593,12 +604,26 @@ vmbus_bus_init(void)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static void
|
||||
vmbus_event_proc_dummy(struct vmbus_softc *sc __unused, int cpu __unused)
|
||||
{
|
||||
}
|
||||
|
||||
static int
|
||||
vmbus_attach(device_t dev)
|
||||
{
|
||||
if(bootverbose)
|
||||
device_printf(dev, "VMBUS: attach dev: %p\n", dev);
|
||||
|
||||
vmbus_devp = dev;
|
||||
vmbus_sc = device_get_softc(dev);
|
||||
|
||||
/*
|
||||
* Event processing logic will be configured:
|
||||
* - After the vmbus protocol version negotiation.
|
||||
* - Before we request channel offers.
|
||||
*/
|
||||
vmbus_sc->vmbus_event_proc = vmbus_event_proc_dummy;
|
||||
|
||||
/*
|
||||
* If the system has already booted and thread
|
||||
@ -616,7 +641,7 @@ vmbus_attach(device_t dev)
|
||||
static void
|
||||
vmbus_init(void)
|
||||
{
|
||||
if (vm_guest != VM_GUEST_HV)
|
||||
if (vm_guest != VM_GUEST_HV || vmbus_get_softc() == NULL)
|
||||
return;
|
||||
|
||||
/*
|
||||
@ -720,9 +745,11 @@ static device_method_t vmbus_methods[] = {
|
||||
|
||||
{ 0, 0 } };
|
||||
|
||||
static char driver_name[] = "vmbus";
|
||||
static driver_t vmbus_driver = { driver_name, vmbus_methods,0, };
|
||||
|
||||
static driver_t vmbus_driver = {
|
||||
"vmbus",
|
||||
vmbus_methods,
|
||||
sizeof(struct vmbus_softc)
|
||||
};
|
||||
|
||||
devclass_t vmbus_devclass;
|
||||
|
||||
|
@ -57,10 +57,18 @@ typedef uint16_t hv_vmbus_status;
|
||||
#define HV_EVENT_FLAGS_COUNT (256 * 8)
|
||||
#define HV_EVENT_FLAGS_BYTE_COUNT (256)
|
||||
#define HV_EVENT_FLAGS_DWORD_COUNT (256 / sizeof(uint32_t))
|
||||
#define HV_EVENT_FLAGS_ULONG_COUNT (256 / sizeof(unsigned long))
|
||||
|
||||
/**
|
||||
* max channel count <== event_flags_dword_count * bit_of_dword
|
||||
*/
|
||||
#ifdef __LP64__
|
||||
#define HV_CHANNEL_ULONG_LEN (64)
|
||||
#define HV_CHANNEL_ULONG_SHIFT (6)
|
||||
#else
|
||||
#define HV_CHANNEL_ULONG_LEN (32)
|
||||
#define HV_CHANNEL_ULONG_SHIFT (5)
|
||||
#endif
|
||||
#define HV_CHANNEL_DWORD_LEN (32)
|
||||
#define HV_CHANNEL_MAX_COUNT \
|
||||
((HV_EVENT_FLAGS_DWORD_COUNT) * HV_CHANNEL_DWORD_LEN)
|
||||
@ -575,7 +583,9 @@ typedef struct {
|
||||
typedef union {
|
||||
uint8_t flags8[HV_EVENT_FLAGS_BYTE_COUNT];
|
||||
uint32_t flags32[HV_EVENT_FLAGS_DWORD_COUNT];
|
||||
unsigned long flagsul[HV_EVENT_FLAGS_ULONG_COUNT];
|
||||
} hv_vmbus_synic_event_flags;
|
||||
CTASSERT(sizeof(hv_vmbus_synic_event_flags) == HV_EVENT_FLAGS_BYTE_COUNT);
|
||||
|
||||
#define HV_X64_CPUID_MIN (0x40000005)
|
||||
#define HV_X64_CPUID_MAX (0x4000ffff)
|
||||
@ -743,7 +753,6 @@ int hv_vmbus_connect(void);
|
||||
int hv_vmbus_disconnect(void);
|
||||
int hv_vmbus_post_message(void *buffer, size_t buf_size);
|
||||
int hv_vmbus_set_event(hv_vmbus_channel *channel);
|
||||
void hv_vmbus_on_events(int cpu);
|
||||
|
||||
/**
|
||||
* Event Timer interfaces
|
||||
|
62
sys/dev/hyperv/vmbus/vmbus_var.h
Normal file
62
sys/dev/hyperv/vmbus/vmbus_var.h
Normal file
@ -0,0 +1,62 @@
|
||||
/*-
|
||||
* Copyright (c) 2016 Microsoft Corp.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice unmodified, this list of conditions, and the following
|
||||
* disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _VMBUS_VAR_H_
|
||||
#define _VMBUS_VAR_H_
|
||||
|
||||
#include <sys/param.h>
|
||||
|
||||
struct vmbus_pcpu_data {
|
||||
int event_flag_cnt; /* # of event flags */
|
||||
} __aligned(CACHE_LINE_SIZE);
|
||||
|
||||
struct vmbus_softc {
|
||||
void (*vmbus_event_proc)(struct vmbus_softc *, int);
|
||||
struct vmbus_pcpu_data vmbus_pcpu[MAXCPU];
|
||||
};
|
||||
|
||||
extern struct vmbus_softc *vmbus_sc;
|
||||
|
||||
static __inline struct vmbus_softc *
|
||||
vmbus_get_softc(void)
|
||||
{
|
||||
return vmbus_sc;
|
||||
}
|
||||
|
||||
#define VMBUS_SC_PCPU_GET(sc, field, cpu) (sc)->vmbus_pcpu[(cpu)].field
|
||||
#define VMBUS_SC_PCPU_PTR(sc, field, cpu) &(sc)->vmbus_pcpu[(cpu)].field
|
||||
#define VMBUS_PCPU_GET(field, cpu) \
|
||||
VMBUS_SC_PCPU_GET(vmbus_get_softc(), field, (cpu))
|
||||
#define VMBUS_PCPU_PTR(field, cpu) \
|
||||
VMBUS_SC_PCPU_PTR(vmbus_get_softc(), field, (cpu))
|
||||
|
||||
void vmbus_on_channel_open(const struct hv_vmbus_channel *);
|
||||
void vmbus_event_proc(struct vmbus_softc *, int);
|
||||
void vmbus_event_proc_compat(struct vmbus_softc *, int);
|
||||
|
||||
#endif /* !_VMBUS_VAR_H_ */
|
Loading…
Reference in New Issue
Block a user