Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 75 additions & 21 deletions drivers/hv/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,22 +71,22 @@ module_param(max_version, uint, S_IRUGO);
MODULE_PARM_DESC(max_version,
"Maximal VMBus protocol version which can be negotiated");

/*
* User requested connection id used to connect to the host. Useful for testing
* or when running a vmbus server on a non-standard connection id.
*/
static uint message_connection_id;

module_param(message_connection_id, uint, 0444);
MODULE_PARM_DESC(message_connection_id,
"The VMBus message connection id used to communicate with the Host");
/* Array of connection IDs to try in order: standard first, then redirect */
static const u32 connection_ids[] = {
VMBUS_MESSAGE_CONNECTION_ID_4,
VMBUS_MESSAGE_CONNECTION_ID_REDIRECT
};

int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version,
u32 connection_id)
{
int ret = 0;
struct vmbus_channel_initiate_contact *msg;
unsigned long flags;

pr_info("VMBus: Starting negotiation - version 0x%x, connection_id 0x%x\n",
version, connection_id);

init_completion(&msginfo->waitevent);

msg = (struct vmbus_channel_initiate_contact *)msginfo->msg;
Expand All @@ -97,7 +97,7 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)

/*
* VMBus protocol 5.0 (VERSION_WIN10_V5) and higher require that we must
* use VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate Contact Message,
* use connection_id for the Initiate Contact Message,
* and for subsequent messages, we must use the Message Connection ID
* field in the host-returned Version Response Message. And, with
* VERSION_WIN10_V5 and higher, we don't use msg->interrupt_page, but we
Expand All @@ -107,17 +107,18 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
* On old hosts, we should always use VMBUS_MESSAGE_CONNECTION_ID (1).
*/
if (version >= VERSION_WIN10_V5) {
pr_info("VMBus: Using modern protocol (v5+) with connection_id 0x%x\n",
connection_id);
msg->msg_sint = VMBUS_MESSAGE_SINT;
msg->msg_vtl = ms_hyperv.vtl;
vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID_4;
vmbus_connection.msg_conn_id = connection_id;
} else {
pr_info("VMBus: Using legacy protocol with connection_id %d\n",
VMBUS_MESSAGE_CONNECTION_ID);
msg->interrupt_page = virt_to_phys(vmbus_connection.int_page);
vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID;
}

if (message_connection_id > 0)
vmbus_connection.msg_conn_id = message_connection_id;

/*
* shared_gpa_boundary is zero in non-SNP VMs, so it's safe to always
* bitwise OR it
Expand All @@ -144,8 +145,10 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
true);

trace_vmbus_negotiate_version(msg, ret);
pr_info("VMBus: Posted negotiation message, ret=%d\n", ret);

if (ret != 0) {
pr_err("VMBus: Failed to post negotiation message, ret=%d\n", ret);
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
list_del(&msginfo->msglistentry);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock,
Expand All @@ -162,12 +165,16 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)

/* Check if successful */
if (msginfo->response.version_response.version_supported) {
pr_info("VMBus: Version 0x%x supported with connection_id 0x%x\n",
version, connection_id);
vmbus_connection.conn_state = CONNECTED;

if (version >= VERSION_WIN10_V5)
vmbus_connection.msg_conn_id =
msginfo->response.version_response.msg_conn_id;
} else {
pr_info("VMBus: Version 0x%x rejected with connection_id 0x%x\n",
version, connection_id);
return -ECONNREFUSED;
}

Expand All @@ -183,6 +190,10 @@ int vmbus_connect(void)
int i, ret = 0;
__u32 version;

pr_info("VMBus: Starting VMBus connection process\n");
pr_info("VMBus: Available connection IDs: 0x%x (standard), 0x%x (redirect)\n",
connection_ids[0], connection_ids[1]);

/* Initialize the vmbus connection */
vmbus_connection.conn_state = CONNECTING;
vmbus_connection.work_queue = create_workqueue("hv_vmbus_con");
Expand Down Expand Up @@ -290,10 +301,16 @@ int vmbus_connect(void)
* Negotiate a compatible VMBUS version number with the
* host. We start with the highest number we can support
* and work our way down until we negotiate a compatible
* version.
* version. For each version, we try standard connection ID first,
* then VMBus redirect if the standard fails.
*/

pr_info("VMBus: Starting connection negotiation with %d versions and %d connection IDs\n",
(int)ARRAY_SIZE(vmbus_versions), (int)ARRAY_SIZE(connection_ids));

for (i = 0; ; i++) {
int conn_id_idx;

if (i == ARRAY_SIZE(vmbus_versions)) {
ret = -EDOM;
goto cleanup;
Expand All @@ -303,14 +320,51 @@ int vmbus_connect(void)
if (version > max_version)
continue;

ret = vmbus_negotiate_version(msginfo, version);
if (ret == -ETIMEDOUT)
goto cleanup;

if (vmbus_connection.conn_state == CONNECTED)
pr_info("VMBus: Trying version 0x%x (%d.%d)\n", version,
version >> 16, version & 0xFFFF);

/* Try each connection ID for this version */
for (conn_id_idx = 0; conn_id_idx < ARRAY_SIZE(connection_ids); conn_id_idx++) {
pr_info("VMBus: Attempting connection_id 0x%x (%s)\n",
connection_ids[conn_id_idx],
conn_id_idx == 0 ? "standard" : "redirect");

ret = vmbus_negotiate_version(msginfo, version,
connection_ids[conn_id_idx]);
if (ret == -ETIMEDOUT) {
pr_err("VMBus: Negotiation timed out for connection_id 0x%x\n",
connection_ids[conn_id_idx]);
goto cleanup;
}

if (vmbus_connection.conn_state == CONNECTED) {
pr_info("VMBus: Successfully connected with version 0x%x, connection_id 0x%x\n",
version, connection_ids[conn_id_idx]);
break;
}

pr_info("VMBus: Failed to connect with connection_id 0x%x, trying next\n",
connection_ids[conn_id_idx]);
}

if (vmbus_connection.conn_state == CONNECTED) {
pr_info("VMBus: Connection established successfully!\n");
break;
}

pr_info("VMBus: All connection IDs failed for version 0x%x, trying next version\n",
version);
}

if (vmbus_connection.conn_state != CONNECTED) {
pr_err("VMBus: Failed to establish connection with any version/connection_id combination\n");
ret = -EDOM;
goto cleanup;
}

pr_info("VMBus: Final connection established - version: 0x%x, connection_id: 0x%x\n",
version, vmbus_connection.msg_conn_id);

if (hv_is_isolation_supported() && version < VERSION_WIN10_V5_2) {
pr_err("Invalid VMBus version %d.%d (expected >= %d.%d) from the host supporting isolation\n",
version >> 16, version & 0xFFFF, VERSION_WIN10_V5_2 >> 16, VERSION_WIN10_V5_2 & 0xFFFF);
Expand Down
4 changes: 3 additions & 1 deletion drivers/hv/hyperv_vmbus.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ struct hv_input_post_message {
enum {
VMBUS_MESSAGE_CONNECTION_ID = 1,
VMBUS_MESSAGE_CONNECTION_ID_4 = 4,
VMBUS_MESSAGE_CONNECTION_ID_REDIRECT = 0x800074,
VMBUS_MESSAGE_PORT_ID = 1,
VMBUS_EVENT_CONNECTION_ID = 2,
VMBUS_EVENT_PORT_ID = 2,
Expand Down Expand Up @@ -305,7 +306,8 @@ struct vmbus_msginfo {

extern struct vmbus_connection vmbus_connection;

int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version);
int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version,
u32 connection_id);

static inline void vmbus_send_interrupt(u32 relid)
{
Expand Down