hyperv/vmbus: Implement a new set of APIs for post message Hypercall

And use this new APIs for Initial Contact post message Hypercall.
More post message Hypercalls will be converted.

MFC after:	1 week
Sponsored by:	Microsoft OSTC
Differential Revision:	https://reviews.freebsd.org/D6830
This commit is contained in:
Sepherosa Ziehau 2016-07-11 04:52:11 +00:00
parent 391adcc526
commit 236764b1c2
10 changed files with 516 additions and 232 deletions

View File

@ -77,10 +77,6 @@ typedef uint8_t hv_bool_uint8_t;
#define HV_VMBUS_VERSION_WIN8 ((2 << 16) | (4))
#define HV_VMBUS_VERSION_WIN8_1 ((3 << 16) | (0))
#define HV_VMBUS_VERSION_INVALID -1
#define HV_VMBUS_VERSION_CURRENT HV_VMBUS_VERSION_WIN8_1
/*
* Make maximum size of pipe payload of 16K
*/
@ -537,20 +533,6 @@ typedef struct {
uint32_t child_rel_id;
} __packed hv_vmbus_channel_relid_released;
typedef struct {
hv_vmbus_channel_msg_header header;
uint32_t vmbus_version_requested;
uint32_t padding2;
uint64_t interrupt_page;
uint64_t monitor_page_1;
uint64_t monitor_page_2;
} __packed hv_vmbus_channel_initiate_contact;
typedef struct {
hv_vmbus_channel_msg_header header;
hv_bool_uint8_t version_supported;
} __packed hv_vmbus_channel_version_response;
typedef hv_vmbus_channel_msg_header hv_vmbus_channel_unload;
#define HW_MACADDR_LEN 6

View File

@ -40,29 +40,30 @@
* Internal functions
*/
typedef void (*vmbus_msg_handler)(const hv_vmbus_channel_msg_header *msg);
typedef struct hv_vmbus_channel_msg_table_entry {
hv_vmbus_channel_msg_type messageType;
vmbus_msg_handler messageHandler;
void (*messageHandler)
(struct vmbus_softc *sc,
const struct vmbus_message *msg);
} hv_vmbus_channel_msg_table_entry;
static void vmbus_channel_on_offer_internal(void *context);
static void vmbus_channel_on_offer_rescind_internal(void *context);
static void vmbus_channel_on_offer(const hv_vmbus_channel_msg_header *hdr);
static void vmbus_channel_on_open_result(
const hv_vmbus_channel_msg_header *hdr);
static void vmbus_channel_on_offer_rescind(
const hv_vmbus_channel_msg_header *hdr);
static void vmbus_channel_on_gpadl_created(
const hv_vmbus_channel_msg_header *hdr);
static void vmbus_channel_on_gpadl_torndown(
const hv_vmbus_channel_msg_header *hdr);
static void vmbus_channel_on_offers_delivered(
const hv_vmbus_channel_msg_header *hdr);
static void vmbus_channel_on_version_response(
const hv_vmbus_channel_msg_header *hdr);
static void vmbus_channel_on_offer(struct vmbus_softc *,
const struct vmbus_message *);
static void vmbus_channel_on_open_result(struct vmbus_softc *,
const struct vmbus_message *);
static void vmbus_channel_on_offer_rescind(struct vmbus_softc *,
const struct vmbus_message *);
static void vmbus_channel_on_gpadl_created(struct vmbus_softc *,
const struct vmbus_message *);
static void vmbus_channel_on_gpadl_torndown(struct vmbus_softc *,
const struct vmbus_message *);
static void vmbus_channel_on_offers_delivered(struct vmbus_softc *,
const struct vmbus_message *);
static void vmbus_channel_on_version_response(struct vmbus_softc *,
const struct vmbus_message *);
/**
* Channel message dispatch table
@ -398,8 +399,11 @@ vmbus_channel_select_defcpu(struct hv_vmbus_channel *channel)
* object to process the offer synchronously
*/
static void
vmbus_channel_on_offer(const hv_vmbus_channel_msg_header *hdr)
vmbus_channel_on_offer(struct vmbus_softc *sc, const struct vmbus_message *msg)
{
const hv_vmbus_channel_msg_header *hdr =
(const hv_vmbus_channel_msg_header *)msg->msg_data;
const hv_vmbus_channel_offer_channel *offer;
hv_vmbus_channel_offer_channel *copied;
@ -476,8 +480,12 @@ vmbus_channel_on_offer_internal(void* context)
* synchronously
*/
static void
vmbus_channel_on_offer_rescind(const hv_vmbus_channel_msg_header *hdr)
vmbus_channel_on_offer_rescind(struct vmbus_softc *sc,
const struct vmbus_message *msg)
{
const hv_vmbus_channel_msg_header *hdr =
(const hv_vmbus_channel_msg_header *)msg->msg_data;
const hv_vmbus_channel_rescind_offer *rescind;
hv_vmbus_channel* channel;
@ -508,8 +516,8 @@ vmbus_channel_on_offer_rescind_internal(void *context)
* @brief Invoked when all offers have been delivered.
*/
static void
vmbus_channel_on_offers_delivered(
const hv_vmbus_channel_msg_header *hdr __unused)
vmbus_channel_on_offers_delivered(struct vmbus_softc *sc __unused,
const struct vmbus_message *msg __unused)
{
mtx_lock(&vmbus_chwait_lock);
@ -526,8 +534,12 @@ vmbus_channel_on_offers_delivered(
* response and signal the requesting thread.
*/
static void
vmbus_channel_on_open_result(const hv_vmbus_channel_msg_header *hdr)
vmbus_channel_on_open_result(struct vmbus_softc *sc,
const struct vmbus_message *msg)
{
const hv_vmbus_channel_msg_header *hdr =
(const hv_vmbus_channel_msg_header *)msg->msg_data;
const hv_vmbus_channel_open_result *result;
hv_vmbus_channel_msg_info* msg_info;
hv_vmbus_channel_msg_header* requestHeader;
@ -568,8 +580,12 @@ vmbus_channel_on_open_result(const hv_vmbus_channel_msg_header *hdr)
* response and signal the requesting thread.
*/
static void
vmbus_channel_on_gpadl_created(const hv_vmbus_channel_msg_header *hdr)
vmbus_channel_on_gpadl_created(struct vmbus_softc *sc,
const struct vmbus_message *msg)
{
const hv_vmbus_channel_msg_header *hdr =
(const hv_vmbus_channel_msg_header *)msg->msg_data;
const hv_vmbus_channel_gpadl_created *gpadl_created;
hv_vmbus_channel_msg_info* msg_info;
hv_vmbus_channel_msg_header* request_header;
@ -610,8 +626,12 @@ vmbus_channel_on_gpadl_created(const hv_vmbus_channel_msg_header *hdr)
* response and signal the requesting thread
*/
static void
vmbus_channel_on_gpadl_torndown(const hv_vmbus_channel_msg_header *hdr)
vmbus_channel_on_gpadl_torndown(struct vmbus_softc *sc,
const struct vmbus_message *msg)
{
const hv_vmbus_channel_msg_header *hdr =
(const hv_vmbus_channel_msg_header *)msg->msg_data;
const hv_vmbus_channel_gpadl_torndown *gpadl_torndown;
hv_vmbus_channel_msg_info* msg_info;
hv_vmbus_channel_msg_header* requestHeader;
@ -647,39 +667,11 @@ vmbus_channel_on_gpadl_torndown(const hv_vmbus_channel_msg_header *hdr)
mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
}
/**
* @brief Version response handler.
*
* This is invoked when we received a response
* to our initiate contact request. Find the matching request, copy th
* response and signal the requesting thread.
*/
static void
vmbus_channel_on_version_response(const hv_vmbus_channel_msg_header *hdr)
vmbus_channel_on_version_response(struct vmbus_softc *sc,
const struct vmbus_message *msg)
{
hv_vmbus_channel_msg_info* msg_info;
hv_vmbus_channel_msg_header* requestHeader;
hv_vmbus_channel_initiate_contact* initiate;
const hv_vmbus_channel_version_response *versionResponse;
versionResponse = (const hv_vmbus_channel_version_response *)hdr;
mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
msg_list_entry) {
requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg;
if (requestHeader->message_type
== HV_CHANNEL_MESSAGE_INITIATED_CONTACT) {
initiate =
(hv_vmbus_channel_initiate_contact*) requestHeader;
memcpy(&msg_info->response.version_response,
versionResponse,
sizeof(hv_vmbus_channel_version_response));
sema_post(&msg_info->wait_sema);
}
}
mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
vmbus_msghc_wakeup(sc, msg);
}
/**
@ -865,5 +857,5 @@ vmbus_chan_msgproc(struct vmbus_softc *sc, const struct vmbus_message *msg)
entry = &g_channel_message_table[msg_type];
if (entry->messageHandler)
entry->messageHandler(hdr);
entry->messageHandler(sc, msg);
}

View File

@ -39,6 +39,7 @@
#include <vm/pmap.h>
#include <dev/hyperv/vmbus/hv_vmbus_priv.h>
#include <dev/hyperv/vmbus/hyperv_reg.h>
#include <dev/hyperv/vmbus/vmbus_reg.h>
#include <dev/hyperv/vmbus/vmbus_var.h>
@ -49,97 +50,7 @@ hv_vmbus_connection hv_vmbus_g_connection =
{ .connect_state = HV_DISCONNECTED,
.next_gpadl_handle = 0xE1E10, };
uint32_t hv_vmbus_protocal_version = HV_VMBUS_VERSION_WS2008;
static uint32_t
hv_vmbus_get_next_version(uint32_t current_ver)
{
switch (current_ver) {
case (HV_VMBUS_VERSION_WIN7):
return(HV_VMBUS_VERSION_WS2008);
case (HV_VMBUS_VERSION_WIN8):
return(HV_VMBUS_VERSION_WIN7);
case (HV_VMBUS_VERSION_WIN8_1):
return(HV_VMBUS_VERSION_WIN8);
case (HV_VMBUS_VERSION_WS2008):
default:
return(HV_VMBUS_VERSION_INVALID);
}
}
/**
* Negotiate the highest supported hypervisor version.
*/
static int
hv_vmbus_negotiate_version(struct vmbus_softc *sc,
hv_vmbus_channel_msg_info *msg_info, uint32_t version)
{
int ret = 0;
hv_vmbus_channel_initiate_contact *msg;
sema_init(&msg_info->wait_sema, 0, "Msg Info Sema");
msg = (hv_vmbus_channel_initiate_contact*) msg_info->msg;
msg->header.message_type = HV_CHANNEL_MESSAGE_INITIATED_CONTACT;
msg->vmbus_version_requested = version;
msg->interrupt_page = sc->vmbus_evtflags_dma.hv_paddr;
msg->monitor_page_1 = sc->vmbus_mnf1_dma.hv_paddr;
msg->monitor_page_2 = sc->vmbus_mnf2_dma.hv_paddr;
/**
* Add to list before we send the request since we may receive the
* response before returning from this routine
*/
mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
TAILQ_INSERT_TAIL(
&hv_vmbus_g_connection.channel_msg_anchor,
msg_info,
msg_list_entry);
mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
ret = hv_vmbus_post_message(
msg,
sizeof(hv_vmbus_channel_initiate_contact));
if (ret != 0) {
mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
TAILQ_REMOVE(
&hv_vmbus_g_connection.channel_msg_anchor,
msg_info,
msg_list_entry);
mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
return (ret);
}
/**
* Wait for the connection response
*/
ret = sema_timedwait(&msg_info->wait_sema, 5 * hz); /* KYS 5 seconds */
mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
TAILQ_REMOVE(
&hv_vmbus_g_connection.channel_msg_anchor,
msg_info,
msg_list_entry);
mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
/**
* Check if successful
*/
if (msg_info->response.version_response.version_supported) {
hv_vmbus_g_connection.connect_state = HV_CONNECTED;
} else {
ret = ECONNREFUSED;
}
return (ret);
}
uint32_t hv_vmbus_protocal_version;
/**
* Send a connect request on the partition service connection
@ -147,10 +58,6 @@ hv_vmbus_negotiate_version(struct vmbus_softc *sc,
int
hv_vmbus_connect(struct vmbus_softc *sc)
{
int ret = 0;
uint32_t version;
hv_vmbus_channel_msg_info* msg_info = NULL;
/**
* Make sure we are not connecting or connected
*/
@ -171,60 +78,12 @@ hv_vmbus_connect(struct vmbus_softc *sc)
mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel",
NULL, MTX_DEF);
msg_info = (hv_vmbus_channel_msg_info*)
malloc(sizeof(hv_vmbus_channel_msg_info) +
sizeof(hv_vmbus_channel_initiate_contact),
M_DEVBUF, M_WAITOK | M_ZERO);
hv_vmbus_g_connection.channels = malloc(sizeof(hv_vmbus_channel*) *
VMBUS_CHAN_MAX, M_DEVBUF, M_WAITOK | M_ZERO);
/*
* Find the highest vmbus version number we can support.
*/
version = HV_VMBUS_VERSION_CURRENT;
do {
ret = hv_vmbus_negotiate_version(sc, msg_info, version);
if (ret == EWOULDBLOCK) {
/*
* We timed out.
*/
goto cleanup;
}
if (hv_vmbus_g_connection.connect_state == HV_CONNECTED)
break;
version = hv_vmbus_get_next_version(version);
} while (version != HV_VMBUS_VERSION_INVALID);
hv_vmbus_protocal_version = version;
if (bootverbose)
printf("VMBUS: Protocol Version: %d.%d\n",
version >> 16, version & 0xFFFF);
sema_destroy(&msg_info->wait_sema);
free(msg_info, M_DEVBUF);
hv_vmbus_g_connection.connect_state = HV_CONNECTED;
return (0);
/*
* Cleanup after failure!
*/
cleanup:
hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
mtx_destroy(&hv_vmbus_g_connection.channel_lock);
mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
if (msg_info) {
sema_destroy(&msg_info->wait_sema);
free(msg_info, M_DEVBUF);
}
free(hv_vmbus_g_connection.channels, M_DEVBUF);
return (ret);
}
/**
@ -330,7 +189,7 @@ int hv_vmbus_post_message(void *buffer, size_t bufferLen)
*/
for (retries = 0; retries < 20; retries++) {
ret = hv_vmbus_post_msg_via_msg_ipc(connId,
VMBUS_MSGTYPE_CHANNEL, buffer, bufferLen);
HYPERV_MSGTYPE_CHANNEL, buffer, bufferLen);
if (ret == HV_STATUS_SUCCESS)
return (0);

View File

@ -95,7 +95,6 @@ typedef union {
hv_vmbus_channel_open_result open_result;
hv_vmbus_channel_gpadl_torndown gpadl_torndown;
hv_vmbus_channel_gpadl_created gpadl_created;
hv_vmbus_channel_version_response version_response;
} hv_vmbus_channel_msg_response;
/*

View File

@ -118,6 +118,13 @@ hv_vmbus_do_hypercall(uint64_t value, void *input, void *output)
in_paddr, out_paddr);
}
uint64_t
hypercall_post_message(bus_addr_t msg_paddr)
{
return hypercall_md(hypercall_context.hc_addr,
HYPERCALL_POST_MESSAGE, msg_paddr, 0);
}
/**
* @brief Post a message using the hypervisor message IPC.
* (This involves a hypercall.)

View File

@ -29,6 +29,8 @@
#ifndef _HYPERV_REG_H_
#define _HYPERV_REG_H_
#include <sys/param.h>
/*
* Hyper-V Synthetic MSRs
*/
@ -130,4 +132,41 @@
#define CPUID_LEAF_HV_LIMITS 0x40000005
#define CPUID_LEAF_HV_HWFEATURES 0x40000006
/*
* Hyper-V message types
*/
#define HYPERV_MSGTYPE_NONE 0
#define HYPERV_MSGTYPE_CHANNEL 1
#define HYPERV_MSGTYPE_TIMER_EXPIRED 0x80000010
/*
* Hypercall status codes
*/
#define HYPERCALL_STATUS_SUCCESS 0x0000
/*
* Hypercall input values
*/
#define HYPERCALL_POST_MESSAGE 0x005c
/*
* Hypercall input parameters
*/
/*
* HYPERCALL_POST_MESSAGE
*/
#define HYPERCALL_POSTMSGIN_DSIZE_MAX 240
#define HYPERCALL_POSTMSGIN_SIZE 256
#define HYPERCALL_POSTMSGIN_ALIGN 8
struct hypercall_postmsg_in {
uint32_t hc_connid;
uint32_t hc_rsvd;
uint32_t hc_msgtype; /* HYPERV_MSGTYPE_ */
uint32_t hc_dsize;
uint8_t hc_data[HYPERCALL_POSTMSGIN_DSIZE_MAX];
} __packed;
CTASSERT(sizeof(struct hypercall_postmsg_in) == HYPERCALL_POSTMSGIN_SIZE);
#endif /* !_HYPERV_REG_H_ */

View File

@ -38,4 +38,6 @@
extern u_int hyperv_features;
extern u_int hyperv_recommends;
uint64_t hypercall_post_message(bus_addr_t msg_paddr);
#endif /* !_HYPERV_VAR_H_ */

View File

@ -69,10 +69,354 @@ __FBSDID("$FreeBSD$");
#include <contrib/dev/acpica/include/acpi.h>
#include "acpi_if.h"
/*
* NOTE: DO NOT CHANGE THESE
*/
#define VMBUS_CONNID_MESSAGE 1
#define VMBUS_CONNID_EVENT 2
struct vmbus_msghc {
struct hypercall_postmsg_in *mh_inprm;
struct hypercall_postmsg_in mh_inprm_save;
struct hyperv_dma mh_inprm_dma;
struct vmbus_message *mh_resp;
struct vmbus_message mh_resp0;
};
struct vmbus_msghc_ctx {
struct vmbus_msghc *mhc_free;
struct mtx mhc_free_lock;
uint32_t mhc_flags;
struct vmbus_msghc *mhc_active;
struct mtx mhc_active_lock;
};
#define VMBUS_MSGHC_CTXF_DESTROY 0x0001
static int vmbus_init(struct vmbus_softc *);
static int vmbus_init_contact(struct vmbus_softc *,
uint32_t);
static struct vmbus_msghc_ctx *vmbus_msghc_ctx_create(bus_dma_tag_t);
static void vmbus_msghc_ctx_destroy(
struct vmbus_msghc_ctx *);
static void vmbus_msghc_ctx_free(struct vmbus_msghc_ctx *);
static struct vmbus_msghc *vmbus_msghc_alloc(bus_dma_tag_t);
static void vmbus_msghc_free(struct vmbus_msghc *);
static struct vmbus_msghc *vmbus_msghc_get1(struct vmbus_msghc_ctx *,
uint32_t);
struct vmbus_softc *vmbus_sc;
extern inthand_t IDTVEC(vmbus_isr);
static const uint32_t vmbus_version[] = {
HV_VMBUS_VERSION_WIN8_1,
HV_VMBUS_VERSION_WIN8,
HV_VMBUS_VERSION_WIN7,
HV_VMBUS_VERSION_WS2008
};
static struct vmbus_msghc *
vmbus_msghc_alloc(bus_dma_tag_t parent_dtag)
{
struct vmbus_msghc *mh;
mh = malloc(sizeof(*mh), M_DEVBUF, M_WAITOK | M_ZERO);
mh->mh_inprm = hyperv_dmamem_alloc(parent_dtag,
HYPERCALL_POSTMSGIN_ALIGN, 0, HYPERCALL_POSTMSGIN_SIZE,
&mh->mh_inprm_dma, BUS_DMA_WAITOK);
if (mh->mh_inprm == NULL) {
free(mh, M_DEVBUF);
return NULL;
}
return mh;
}
static void
vmbus_msghc_free(struct vmbus_msghc *mh)
{
hyperv_dmamem_free(&mh->mh_inprm_dma, mh->mh_inprm);
free(mh, M_DEVBUF);
}
static void
vmbus_msghc_ctx_free(struct vmbus_msghc_ctx *mhc)
{
KASSERT(mhc->mhc_active == NULL, ("still have active msg hypercall"));
KASSERT(mhc->mhc_free == NULL, ("still have hypercall msg"));
mtx_destroy(&mhc->mhc_free_lock);
mtx_destroy(&mhc->mhc_active_lock);
free(mhc, M_DEVBUF);
}
static struct vmbus_msghc_ctx *
vmbus_msghc_ctx_create(bus_dma_tag_t parent_dtag)
{
struct vmbus_msghc_ctx *mhc;
mhc = malloc(sizeof(*mhc), M_DEVBUF, M_WAITOK | M_ZERO);
mtx_init(&mhc->mhc_free_lock, "vmbus msghc free", NULL, MTX_DEF);
mtx_init(&mhc->mhc_active_lock, "vmbus msghc act", NULL, MTX_DEF);
mhc->mhc_free = vmbus_msghc_alloc(parent_dtag);
if (mhc->mhc_free == NULL) {
vmbus_msghc_ctx_free(mhc);
return NULL;
}
return mhc;
}
static struct vmbus_msghc *
vmbus_msghc_get1(struct vmbus_msghc_ctx *mhc, uint32_t dtor_flag)
{
struct vmbus_msghc *mh;
mtx_lock(&mhc->mhc_free_lock);
while ((mhc->mhc_flags & dtor_flag) == 0 && mhc->mhc_free == NULL) {
mtx_sleep(&mhc->mhc_free, &mhc->mhc_free_lock, 0,
"gmsghc", 0);
}
if (mhc->mhc_flags & dtor_flag) {
/* Being destroyed */
mh = NULL;
} else {
mh = mhc->mhc_free;
KASSERT(mh != NULL, ("no free hypercall msg"));
KASSERT(mh->mh_resp == NULL,
("hypercall msg has pending response"));
mhc->mhc_free = NULL;
}
mtx_unlock(&mhc->mhc_free_lock);
return mh;
}
struct vmbus_msghc *
vmbus_msghc_get(struct vmbus_softc *sc, size_t dsize)
{
struct hypercall_postmsg_in *inprm;
struct vmbus_msghc *mh;
if (dsize > HYPERCALL_POSTMSGIN_DSIZE_MAX)
return NULL;
mh = vmbus_msghc_get1(sc->vmbus_msg_hc, VMBUS_MSGHC_CTXF_DESTROY);
if (mh == NULL)
return NULL;
inprm = mh->mh_inprm;
memset(inprm, 0, HYPERCALL_POSTMSGIN_SIZE);
inprm->hc_connid = VMBUS_CONNID_MESSAGE;
inprm->hc_msgtype = HYPERV_MSGTYPE_CHANNEL;
inprm->hc_dsize = dsize;
return mh;
}
void
vmbus_msghc_put(struct vmbus_softc *sc, struct vmbus_msghc *mh)
{
struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc;
KASSERT(mhc->mhc_active == NULL, ("msg hypercall is active"));
mh->mh_resp = NULL;
mtx_lock(&mhc->mhc_free_lock);
KASSERT(mhc->mhc_free == NULL, ("has free hypercall msg"));
mhc->mhc_free = mh;
mtx_unlock(&mhc->mhc_free_lock);
wakeup(&mhc->mhc_free);
}
void *
vmbus_msghc_dataptr(struct vmbus_msghc *mh)
{
return mh->mh_inprm->hc_data;
}
static void
vmbus_msghc_ctx_destroy(struct vmbus_msghc_ctx *mhc)
{
struct vmbus_msghc *mh;
mtx_lock(&mhc->mhc_free_lock);
mhc->mhc_flags |= VMBUS_MSGHC_CTXF_DESTROY;
mtx_unlock(&mhc->mhc_free_lock);
wakeup(&mhc->mhc_free);
mh = vmbus_msghc_get1(mhc, 0);
if (mh == NULL)
panic("can't get msghc");
vmbus_msghc_free(mh);
vmbus_msghc_ctx_free(mhc);
}
int
vmbus_msghc_exec_noresult(struct vmbus_msghc *mh)
{
sbintime_t time = SBT_1MS;
int i;
/*
* Save the input parameter so that we could restore the input
* parameter if the Hypercall failed.
*
* XXX
* Is this really necessary?! i.e. Will the Hypercall ever
* overwrite the input parameter?
*/
memcpy(&mh->mh_inprm_save, mh->mh_inprm, HYPERCALL_POSTMSGIN_SIZE);
/*
* In order to cope with transient failures, e.g. insufficient
* resources on host side, we retry the post message Hypercall
* several times. 20 retries seem sufficient.
*/
#define HC_RETRY_MAX 20
for (i = 0; i < HC_RETRY_MAX; ++i) {
uint64_t status;
status = hypercall_post_message(mh->mh_inprm_dma.hv_paddr);
if (status == HYPERCALL_STATUS_SUCCESS)
return 0;
pause_sbt("hcpmsg", time, 0, C_HARDCLOCK);
if (time < SBT_1S * 2)
time *= 2;
/* Restore input parameter and try again */
memcpy(mh->mh_inprm, &mh->mh_inprm_save,
HYPERCALL_POSTMSGIN_SIZE);
}
#undef HC_RETRY_MAX
return EIO;
}
int
vmbus_msghc_exec(struct vmbus_softc *sc, struct vmbus_msghc *mh)
{
struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc;
int error;
KASSERT(mh->mh_resp == NULL, ("hypercall msg has pending response"));
mtx_lock(&mhc->mhc_active_lock);
KASSERT(mhc->mhc_active == NULL, ("pending active msg hypercall"));
mhc->mhc_active = mh;
mtx_unlock(&mhc->mhc_active_lock);
error = vmbus_msghc_exec_noresult(mh);
if (error) {
mtx_lock(&mhc->mhc_active_lock);
KASSERT(mhc->mhc_active == mh, ("msghc mismatch"));
mhc->mhc_active = NULL;
mtx_unlock(&mhc->mhc_active_lock);
}
return error;
}
const struct vmbus_message *
vmbus_msghc_wait_result(struct vmbus_softc *sc, struct vmbus_msghc *mh)
{
struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc;
mtx_lock(&mhc->mhc_active_lock);
KASSERT(mhc->mhc_active == mh, ("msghc mismatch"));
while (mh->mh_resp == NULL) {
mtx_sleep(&mhc->mhc_active, &mhc->mhc_active_lock, 0,
"wmsghc", 0);
}
mhc->mhc_active = NULL;
mtx_unlock(&mhc->mhc_active_lock);
return mh->mh_resp;
}
void
vmbus_msghc_wakeup(struct vmbus_softc *sc, const struct vmbus_message *msg)
{
struct vmbus_msghc_ctx *mhc = sc->vmbus_msg_hc;
struct vmbus_msghc *mh;
mtx_lock(&mhc->mhc_active_lock);
mh = mhc->mhc_active;
KASSERT(mh != NULL, ("no pending msg hypercall"));
memcpy(&mh->mh_resp0, msg, sizeof(mh->mh_resp0));
mh->mh_resp = &mh->mh_resp0;
mtx_unlock(&mhc->mhc_active_lock);
wakeup(&mhc->mhc_active);
}
static int
vmbus_init_contact(struct vmbus_softc *sc, uint32_t version)
{
struct vmbus_chanmsg_init_contact *req;
const struct vmbus_chanmsg_version_resp *resp;
const struct vmbus_message *msg;
struct vmbus_msghc *mh;
int error, supp = 0;
mh = vmbus_msghc_get(sc, sizeof(*req));
if (mh == NULL)
return ENXIO;
req = vmbus_msghc_dataptr(mh);
req->chm_hdr.chm_type = VMBUS_CHANMSG_TYPE_INIT_CONTACT;
req->chm_ver = version;
req->chm_evtflags = sc->vmbus_evtflags_dma.hv_paddr;
req->chm_mnf1 = sc->vmbus_mnf1_dma.hv_paddr;
req->chm_mnf2 = sc->vmbus_mnf2_dma.hv_paddr;
error = vmbus_msghc_exec(sc, mh);
if (error) {
vmbus_msghc_put(sc, mh);
return error;
}
msg = vmbus_msghc_wait_result(sc, mh);
resp = (const struct vmbus_chanmsg_version_resp *)msg->msg_data;
supp = resp->chm_supp;
vmbus_msghc_put(sc, mh);
return (supp ? 0 : EOPNOTSUPP);
}
static int
vmbus_init(struct vmbus_softc *sc)
{
int i;
for (i = 0; i < nitems(vmbus_version); ++i) {
int error;
error = vmbus_init_contact(sc, vmbus_version[i]);
if (!error) {
hv_vmbus_protocal_version = vmbus_version[i];
device_printf(sc->vmbus_dev, "version %u.%u\n",
(hv_vmbus_protocal_version >> 16),
(hv_vmbus_protocal_version & 0xffff));
return 0;
}
}
return ENXIO;
}
static void
vmbus_msg_task(void *xsc, int pending __unused)
{
@ -81,19 +425,19 @@ vmbus_msg_task(void *xsc, int pending __unused)
msg = VMBUS_PCPU_GET(sc, message, curcpu) + VMBUS_SINT_MESSAGE;
for (;;) {
if (msg->msg_type == VMBUS_MSGTYPE_NONE) {
if (msg->msg_type == HYPERV_MSGTYPE_NONE) {
/* No message */
break;
} else if (msg->msg_type == VMBUS_MSGTYPE_CHANNEL) {
} else if (msg->msg_type == HYPERV_MSGTYPE_CHANNEL) {
/* Channel message */
vmbus_chan_msgproc(sc,
__DEVOLATILE(const struct vmbus_message *, msg));
}
msg->msg_type = VMBUS_MSGTYPE_NONE;
msg->msg_type = HYPERV_MSGTYPE_NONE;
/*
* Make sure the write to msg_type (i.e. set to
* VMBUS_MSGTYPE_NONE) happens before we read the
* HYPERV_MSGTYPE_NONE) happens before we read the
* msg_flags and EOMing. Otherwise, the EOMing will
* not deliver any more messages since there is no
* empty slot
@ -127,14 +471,14 @@ vmbus_handle_intr1(struct vmbus_softc *sc, struct trapframe *frame, int cpu)
* TODO: move this to independent IDT vector.
*/
msg = msg_base + VMBUS_SINT_TIMER;
if (msg->msg_type == VMBUS_MSGTYPE_TIMER_EXPIRED) {
msg->msg_type = VMBUS_MSGTYPE_NONE;
if (msg->msg_type == HYPERV_MSGTYPE_TIMER_EXPIRED) {
msg->msg_type = HYPERV_MSGTYPE_NONE;
vmbus_et_intr(frame);
/*
* Make sure the write to msg_type (i.e. set to
* VMBUS_MSGTYPE_NONE) happens before we read the
* HYPERV_MSGTYPE_NONE) happens before we read the
* msg_flags and EOMing. Otherwise, the EOMing will
* not deliver any more messages since there is no
* empty slot
@ -166,7 +510,7 @@ vmbus_handle_intr1(struct vmbus_softc *sc, struct trapframe *frame, int cpu)
* Check messages. Mainly management stuffs; ultra low rate.
*/
msg = msg_base + VMBUS_SINT_MESSAGE;
if (__predict_false(msg->msg_type != VMBUS_MSGTYPE_NONE)) {
if (__predict_false(msg->msg_type != HYPERV_MSGTYPE_NONE)) {
taskqueue_enqueue(VMBUS_PCPU_GET(sc, message_tq, cpu),
VMBUS_PCPU_PTR(sc, message_task, cpu));
}
@ -619,6 +963,16 @@ vmbus_bus_init(void)
return (0);
sc->vmbus_flags |= VMBUS_FLAG_ATTACHED;
/*
* Create context for "post message" Hypercalls
*/
sc->vmbus_msg_hc = vmbus_msghc_ctx_create(
bus_get_dma_tag(sc->vmbus_dev));
if (sc->vmbus_msg_hc == NULL) {
ret = ENXIO;
goto cleanup;
}
/*
* Allocate DMA stuffs.
*/
@ -648,6 +1002,10 @@ vmbus_bus_init(void)
if (ret != 0)
goto cleanup;
ret = vmbus_init(sc);
if (ret != 0)
goto cleanup;
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;
@ -665,6 +1023,10 @@ vmbus_bus_init(void)
cleanup:
vmbus_intr_teardown(sc);
vmbus_dma_free(sc);
if (sc->vmbus_msg_hc != NULL) {
vmbus_msghc_ctx_destroy(sc->vmbus_msg_hc);
sc->vmbus_msg_hc = NULL;
}
return (ret);
}
@ -737,6 +1099,11 @@ vmbus_detach(device_t dev)
vmbus_intr_teardown(sc);
vmbus_dma_free(sc);
if (sc->vmbus_msg_hc != NULL) {
vmbus_msghc_ctx_destroy(sc->vmbus_msg_hc);
sc->vmbus_msg_hc = NULL;
}
return (0);
}

View File

@ -39,7 +39,7 @@
#define VMBUS_MSG_SIZE 256
struct vmbus_message {
uint32_t msg_type; /* VMBUS_MSGTYPE_ */
uint32_t msg_type; /* HYPERV_MSGTYPE_ */
uint8_t msg_dsize; /* data size */
uint8_t msg_flags; /* VMBUS_MSGFLAG_ */
uint16_t msg_rsvd;
@ -48,10 +48,6 @@ struct vmbus_message {
} __packed;
CTASSERT(sizeof(struct vmbus_message) == VMBUS_MSG_SIZE);
#define VMBUS_MSGTYPE_NONE 0
#define VMBUS_MSGTYPE_CHANNEL 1
#define VMBUS_MSGTYPE_TIMER_EXPIRED 0x80000010
#define VMBUS_MSGFLAG_PENDING 0x01
/*
@ -81,4 +77,34 @@ CTASSERT(sizeof(struct vmbus_evtflags) == VMBUS_EVTFLAGS_SIZE);
#define VMBUS_CHAN_MAX_COMPAT 256
#define VMBUS_CHAN_MAX (VMBUS_EVTFLAG_LEN * VMBUS_EVTFLAGS_MAX)
/*
* Channel messages
* - Embedded in vmbus_message.msg_data, e.g. response.
* - Embedded in hypercall_postmsg_in.hc_data, e.g. request.
*/
#define VMBUS_CHANMSG_TYPE_INIT_CONTACT 14 /* REQ */
#define VMBUS_CHANMSG_TYPE_VERSION_RESP 15 /* RESP */
struct vmbus_chanmsg_hdr {
uint32_t chm_type; /* VMBUS_CHANMSG_TYPE_ */
uint32_t chm_rsvd;
} __packed;
/* VMBUS_CHANMSG_TYPE_INIT_CONTACT */
struct vmbus_chanmsg_init_contact {
struct vmbus_chanmsg_hdr chm_hdr;
uint32_t chm_ver;
uint32_t chm_rsvd;
uint64_t chm_evtflags;
uint64_t chm_mnf1;
uint64_t chm_mnf2;
} __packed;
/* VMBUS_CHANMSG_TYPE_VERSION_RESP */
struct vmbus_chanmsg_version_resp {
struct vmbus_chanmsg_hdr chm_hdr;
uint8_t chm_supp;
} __packed;
#endif /* !_VMBUS_REG_H_ */

View File

@ -69,6 +69,7 @@ struct vmbus_softc {
u_long *vmbus_rx_evtflags;
/* compat evtflgs from host */
struct vmbus_msghc_ctx *vmbus_msg_hc;
struct vmbus_pcpu_data vmbus_pcpu[MAXCPU];
/* Rarely used fields */
@ -108,6 +109,7 @@ vmbus_get_device(void)
struct hv_vmbus_channel;
struct trapframe;
struct vmbus_message;
struct vmbus_msghc;
void vmbus_on_channel_open(const struct hv_vmbus_channel *);
void vmbus_event_proc(struct vmbus_softc *, int);
@ -118,4 +120,13 @@ void vmbus_et_intr(struct trapframe *);
void vmbus_chan_msgproc(struct vmbus_softc *, const struct vmbus_message *);
struct vmbus_msghc *vmbus_msghc_get(struct vmbus_softc *, size_t);
void vmbus_msghc_put(struct vmbus_softc *, struct vmbus_msghc *);
void *vmbus_msghc_dataptr(struct vmbus_msghc *);
int vmbus_msghc_exec_noresult(struct vmbus_msghc *);
int vmbus_msghc_exec(struct vmbus_softc *, struct vmbus_msghc *);
const struct vmbus_message *vmbus_msghc_wait_result(struct vmbus_softc *,
struct vmbus_msghc *);
void vmbus_msghc_wakeup(struct vmbus_softc *, const struct vmbus_message *);
#endif /* !_VMBUS_VAR_H_ */