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:
parent
391adcc526
commit
236764b1c2
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
||||
/*
|
||||
|
@ -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.)
|
||||
|
@ -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_ */
|
||||
|
@ -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_ */
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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_ */
|
||||
|
@ -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_ */
|
||||
|
Loading…
Reference in New Issue
Block a user