With VMBus protocol 5.0, we're able to better support new features, e.g.
running two or more VMBus drivers simultaneously in a single VM -- note:
we can't simply load the current VMBus driver twice, instead, a secondary
VMBus driver must be implemented.

This patch adds the support for the new VMBus protocol, which is available
on new Windows hosts, by:

1) We still use SINT2 for compatibility;
2) We must use Connection ID 4 for the Initiate Contact Message, and for
subsequent messages, we must use the Message Connection ID field in
the host-returned VersionResponse Message.

Notes for developers of the secondary VMBus driver:
1) Must use VMBus protocol 5.0 as well;
2) Must use a different SINT number that is not in use.
3) Must use Connection ID 4 for the Initiate Contact Message, and for
subsequent messages, must use the Message Connection ID field in
the host-returned VersionResponse Message.
4) It's possible that the primary VMBus driver using protocol version 4.0
can work with a secondary VMBus driver using protocol version 5.0, but it's
recommended that both should use 5.0 for new Hyper-V features in the future.

Signed-off-by: Dexuan Cui <de...@microsoft.com>
Cc: Stephen Hemminger <sthem...@microsoft.com>
Cc: K. Y. Srinivasan <k...@microsoft.com>
Cc: Michael Kelley <mikel...@microsoft.com>
---
 drivers/hv/connection.c   | 44 ++++++++++++++++++++++++++++++++++++++++++--
 drivers/hv/hyperv_vmbus.h |  3 +++
 include/linux/hyperv.h    | 26 ++++++++++++++++++++++++--
 3 files changed, 69 insertions(+), 4 deletions(-)

diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
index 7285518..19e0468 100644
--- a/drivers/hv/connection.c
+++ b/drivers/hv/connection.c
@@ -63,6 +63,9 @@ static __u32 vmbus_get_next_version(__u32 current_version)
        case (VERSION_WIN10):
                return VERSION_WIN8_1;
 
+       case (VERSION_WIN10_V5):
+               return VERSION_WIN10;
+
        case (VERSION_WS2008):
        default:
                return VERSION_INVAL;
@@ -80,9 +83,29 @@ static int vmbus_negotiate_version(struct 
vmbus_channel_msginfo *msginfo,
 
        msg = (struct vmbus_channel_initiate_contact *)msginfo->msg;
 
+       memset(msg, 0, sizeof(*msg));
        msg->header.msgtype = CHANNELMSG_INITIATE_CONTACT;
        msg->vmbus_version_requested = version;
-       msg->interrupt_page = virt_to_phys(vmbus_connection.int_page);
+
+       /*
+        * VMBus protocol 5.0 (VERSION_WIN10_V5) requires 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, we don't use msg->interrupt_page, but we 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;
+               vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID_4;
+       } else {
+               msg->interrupt_page = virt_to_phys(vmbus_connection.int_page);
+               vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID;
+       }
+
        msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages[0]);
        msg->monitor_page2 = virt_to_phys(vmbus_connection.monitor_pages[1]);
        /*
@@ -137,6 +160,10 @@ static int vmbus_negotiate_version(struct 
vmbus_channel_msginfo *msginfo,
        /* Check if successful */
        if (msginfo->response.version_response.version_supported) {
                vmbus_connection.conn_state = CONNECTED;
+
+               if (version >= VERSION_WIN10_V5)
+                       vmbus_connection.msg_conn_id =
+                               msginfo->response.version_response.msg_conn_id;
        } else {
                return -ECONNREFUSED;
        }
@@ -354,13 +381,14 @@ void vmbus_on_event(unsigned long data)
  */
 int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep)
 {
+       struct vmbus_channel_message_header *hdr;
        union hv_connection_id conn_id;
        int ret = 0;
        int retries = 0;
        u32 usec = 1;
 
        conn_id.asu32 = 0;
-       conn_id.u.id = VMBUS_MESSAGE_CONNECTION_ID;
+       conn_id.u.id = vmbus_connection.msg_conn_id;
 
        /*
         * hv_post_message() can have transient failures because of
@@ -373,6 +401,18 @@ 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
+                        * requires 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.
+                        */
+                       hdr = (struct vmbus_channel_message_header *)buffer;
+                       if (hdr->msgtype == CHANNELMSG_INITIATE_CONTACT)
+                               return -EINVAL;
+                       /*
                         * 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 f761bef3..72eaba3 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -187,6 +187,7 @@ struct hv_input_post_message {
 
 enum {
        VMBUS_MESSAGE_CONNECTION_ID     = 1,
+       VMBUS_MESSAGE_CONNECTION_ID_4   = 4,
        VMBUS_MESSAGE_PORT_ID           = 1,
        VMBUS_EVENT_CONNECTION_ID       = 2,
        VMBUS_EVENT_PORT_ID             = 2,
@@ -302,6 +303,8 @@ struct vmbus_connection {
         */
        int connect_cpu;
 
+       u32 msg_conn_id;
+
        atomic_t offer_in_progress;
 
        enum vmbus_connect_state conn_state;
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index 192ed8f..11b5612 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -163,6 +163,7 @@ static inline u32 hv_get_bytes_to_write(const struct 
hv_ring_buffer_info *rbi)
  * 2 . 4  (Windows 8)
  * 3 . 0  (Windows 8 R2)
  * 4 . 0  (Windows 10)
+ * 5 . 0  (Newer Windows 10)
  */
 
 #define VERSION_WS2008  ((0 << 16) | (13))
@@ -170,10 +171,11 @@ static inline u32 hv_get_bytes_to_write(const struct 
hv_ring_buffer_info *rbi)
 #define VERSION_WIN8    ((2 << 16) | (4))
 #define VERSION_WIN8_1    ((3 << 16) | (0))
 #define VERSION_WIN10  ((4 << 16) | (0))
+#define VERSION_WIN10_V5 ((5 << 16) | (0))
 
 #define VERSION_INVAL -1
 
-#define VERSION_CURRENT VERSION_WIN10
+#define VERSION_CURRENT VERSION_WIN10_V5
 
 /* Make maximum size of pipe payload of 16K */
 #define MAX_PIPE_DATA_PAYLOAD          (sizeof(u8) * 16384)
@@ -570,7 +572,14 @@ struct vmbus_channel_initiate_contact {
        struct vmbus_channel_message_header header;
        u32 vmbus_version_requested;
        u32 target_vcpu; /* The VCPU the host should respond to */
-       u64 interrupt_page;
+       union {
+               u64 interrupt_page;
+               struct {
+                       u8      msg_sint;
+                       u8      padding1[3];
+                       u32     padding2;
+               };
+       };
        u64 monitor_page1;
        u64 monitor_page2;
 } __packed;
@@ -585,6 +594,19 @@ struct vmbus_channel_tl_connect_request {
 struct vmbus_channel_version_response {
        struct vmbus_channel_message_header header;
        u8 version_supported;
+
+       u8 connection_state;
+       u16 padding;
+
+       /*
+        * On new hosts that support VMBus protocol 5.0, 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.
+        *
+        * On old hosts, we should always use VMBUS_MESSAGE_CONNECTION_ID (1).
+        */
+       u32 msg_conn_id;
 } __packed;
 
 enum vmbus_channel_state {
-- 
2.7.4

Reply via email to