diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index b5b322ce16df..0fd50d4cb573 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -72,7 +72,8 @@ module_param(max_version, uint, S_IRUGO); MODULE_PARM_DESC(max_version, "Maximal VMBus protocol version which can be negotiated"); -int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version) +static int vmbus_try_connection_id(struct vmbus_channel_msginfo *msginfo, + u32 version, u32 connection_id) { int ret = 0; struct vmbus_channel_initiate_contact *msg; @@ -87,20 +88,20 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version) msg->vmbus_version_requested = 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, - * 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 - * tell the host explicitly that we still use VMBUS_MESSAGE_SINT(2) for - * compatibility. + * For VMBus protocol 5.0 (VERSION_WIN10_V5) and higher, use the + * caller-supplied connection_id for the Initiate Contact message so + * the caller can implement the required retry scheme. For subsequent + * messages, use the Message Connection ID field in the host-returned + * Version Response message. With VERSION_WIN10_V5 and higher, we don't + * use msg->interrupt_page, but tell the host explicitly that we still + * use VMBUS_MESSAGE_SINT(2) for compatibility. * * On old hosts, we should always use VMBUS_MESSAGE_CONNECTION_ID (1). */ if (version >= VERSION_WIN10_V5) { 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 { msg->interrupt_page = virt_to_phys(vmbus_connection.int_page); vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID; @@ -165,6 +166,22 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version) return ret; } +int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version) +{ + int ret; + + /* Try the redirect ID first for VTL2 with VMBus protocol 5.0+. */ + if (version >= VERSION_WIN10_V5 && ms_hyperv.vtl == 2) { + ret = vmbus_try_connection_id(msginfo, version, + VMBUS_MESSAGE_CONNECTION_ID_REDIRECT); + if (ret != -ENXIO) + return ret; + } + + return vmbus_try_connection_id(msginfo, version, + VMBUS_MESSAGE_CONNECTION_ID_4); +} + /* * vmbus_connect - Sends a connect request on the partition service connection */ @@ -457,18 +474,10 @@ int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep) switch (ret) { case HV_STATUS_INVALID_CONNECTION_ID: - /* - * See vmbus_negotiate_version(): VMBus protocol 5.0 - * and higher require that we must use - * VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate - * Contact message, but on old hosts that only - * support VMBus protocol 4.0 or lower, here we get - * HV_STATUS_INVALID_CONNECTION_ID and we should - * return an error immediately without retrying. - */ + /* Allow INITIATE_CONTACT to try another connection ID. */ hdr = buffer; if (hdr->msgtype == CHANNELMSG_INITIATE_CONTACT) - return -EINVAL; + return -ENXIO; /* * We could get this if we send messages too * frequently. diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index eb8bdd8bb1f5..33923621a5a3 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -110,6 +110,8 @@ struct hv_input_post_message { enum { VMBUS_MESSAGE_CONNECTION_ID = 1, VMBUS_MESSAGE_CONNECTION_ID_4 = 4, + /* VTL2 redirect connection ID for INITIATE_CONTACT. */ + VMBUS_MESSAGE_CONNECTION_ID_REDIRECT = 0x800074, VMBUS_MESSAGE_PORT_ID = 1, VMBUS_EVENT_CONNECTION_ID = 2, VMBUS_EVENT_PORT_ID = 2,