net/hyperv: Add NETVSP protocol version negotiation
authorHaiyang Zhang <haiyangz@microsoft.com>
Thu, 15 Dec 2011 21:45:16 +0000 (13:45 -0800)
committerGreg Kroah-Hartman <gregkh@suse.de>
Thu, 5 Jan 2012 00:13:05 +0000 (16:13 -0800)
Automatically negotiate the highest protocol version mutually recognized by
both host and guest.

Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/net/hyperv/hyperv_net.h
drivers/net/hyperv/netvsc.c

index ff1b5209b45fda5406538dca98071be52e9a2e0f..2877670551253b7914d2ef399d4fc79c04413bc1 100644 (file)
@@ -134,8 +134,7 @@ int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter);
 #define NVSP_INVALID_PROTOCOL_VERSION  ((u32)0xFFFFFFFF)
 
 #define NVSP_PROTOCOL_VERSION_1                2
-#define NVSP_MIN_PROTOCOL_VERSION      NVSP_PROTOCOL_VERSION_1
-#define NVSP_MAX_PROTOCOL_VERSION      NVSP_PROTOCOL_VERSION_1
+#define NVSP_PROTOCOL_VERSION_2                0x30002
 
 enum {
        NVSP_MSG_TYPE_NONE = 0,
@@ -160,11 +159,36 @@ enum {
        NVSP_MSG1_TYPE_SEND_RNDIS_PKT,
        NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE,
 
-       /*
-        * This should be set to the number of messages for the version with
-        * the maximum number of messages.
-        */
-       NVSP_NUM_MSG_PER_VERSION                = 9,
+       /* Version 2 messages */
+       NVSP_MSG2_TYPE_SEND_CHIMNEY_DELEGATED_BUF,
+       NVSP_MSG2_TYPE_SEND_CHIMNEY_DELEGATED_BUF_COMP,
+       NVSP_MSG2_TYPE_REVOKE_CHIMNEY_DELEGATED_BUF,
+
+       NVSP_MSG2_TYPE_RESUME_CHIMNEY_RX_INDICATION,
+
+       NVSP_MSG2_TYPE_TERMINATE_CHIMNEY,
+       NVSP_MSG2_TYPE_TERMINATE_CHIMNEY_COMP,
+
+       NVSP_MSG2_TYPE_INDICATE_CHIMNEY_EVENT,
+
+       NVSP_MSG2_TYPE_SEND_CHIMNEY_PKT,
+       NVSP_MSG2_TYPE_SEND_CHIMNEY_PKT_COMP,
+
+       NVSP_MSG2_TYPE_POST_CHIMNEY_RECV_REQ,
+       NVSP_MSG2_TYPE_POST_CHIMNEY_RECV_REQ_COMP,
+
+       NVSP_MSG2_TYPE_ALLOC_RXBUF,
+       NVSP_MSG2_TYPE_ALLOC_RXBUF_COMP,
+
+       NVSP_MSG2_TYPE_FREE_RXBUF,
+
+       NVSP_MSG2_TYPE_SEND_VMQ_RNDIS_PKT,
+       NVSP_MSG2_TYPE_SEND_VMQ_RNDIS_PKT_COMP,
+
+       NVSP_MSG2_TYPE_SEND_NDIS_CONFIG,
+
+       NVSP_MSG2_TYPE_ALLOC_CHIMNEY_HANDLE,
+       NVSP_MSG2_TYPE_ALLOC_CHIMNEY_HANDLE_COMP,
 };
 
 enum {
@@ -175,6 +199,7 @@ enum {
        NVSP_STAT_PROTOCOL_TOO_OLD,
        NVSP_STAT_INVALID_RNDIS_PKT,
        NVSP_STAT_BUSY,
+       NVSP_STAT_PROTOCOL_UNSUPPORTED,
        NVSP_STAT_MAX,
 };
 
@@ -359,9 +384,69 @@ union nvsp_1_message_uber {
                                                send_rndis_pkt_complete;
 } __packed;
 
+
+/*
+ * Network VSP protocol version 2 messages:
+ */
+struct nvsp_2_vsc_capability {
+       union {
+               u64 data;
+               struct {
+                       u64 vmq:1;
+                       u64 chimney:1;
+                       u64 sriov:1;
+                       u64 ieee8021q:1;
+                       u64 correlation_id:1;
+               };
+       };
+} __packed;
+
+struct nvsp_2_send_ndis_config {
+       u32 mtu;
+       u32 reserved;
+       struct nvsp_2_vsc_capability capability;
+} __packed;
+
+/* Allocate receive buffer */
+struct nvsp_2_alloc_rxbuf {
+       /* Allocation ID to match the allocation request and response */
+       u32 alloc_id;
+
+       /* Length of the VM shared memory receive buffer that needs to
+        * be allocated
+        */
+       u32 len;
+} __packed;
+
+/* Allocate receive buffer complete */
+struct nvsp_2_alloc_rxbuf_comp {
+       /* The NDIS_STATUS code for buffer allocation */
+       u32 status;
+
+       u32 alloc_id;
+
+       /* GPADL handle for the allocated receive buffer */
+       u32 gpadl_handle;
+
+       /* Receive buffer ID */
+       u64 recv_buf_id;
+} __packed;
+
+struct nvsp_2_free_rxbuf {
+       u64 recv_buf_id;
+} __packed;
+
+union nvsp_2_message_uber {
+       struct nvsp_2_send_ndis_config send_ndis_config;
+       struct nvsp_2_alloc_rxbuf alloc_rxbuf;
+       struct nvsp_2_alloc_rxbuf_comp alloc_rxbuf_comp;
+       struct nvsp_2_free_rxbuf free_rxbuf;
+} __packed;
+
 union nvsp_all_messages {
        union nvsp_message_init_uber init_msg;
        union nvsp_1_message_uber v1_msg;
+       union nvsp_2_message_uber v2_msg;
 } __packed;
 
 /* ALL Messages */
@@ -391,6 +476,8 @@ struct nvsp_message {
 struct netvsc_device {
        struct hv_device *dev;
 
+       u32 nvsp_version;
+
        atomic_t num_outstanding_sends;
        bool destroy;
        /*
index bab627f261c424af43c301cd8884704ccb454e84..46828b4dd8ab7a85e0afe405fd38c2ea07b01767 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/io.h>
 #include <linux/slab.h>
 #include <linux/netdevice.h>
+#include <linux/if_ether.h>
 
 #include "hyperv_net.h"
 
@@ -260,27 +261,18 @@ exit:
 }
 
 
-static int netvsc_connect_vsp(struct hv_device *device)
+/* Negotiate NVSP protocol version */
+static int negotiate_nvsp_ver(struct hv_device *device,
+                             struct netvsc_device *net_device,
+                             struct nvsp_message *init_packet,
+                             u32 nvsp_ver)
 {
        int ret, t;
-       struct netvsc_device *net_device;
-       struct nvsp_message *init_packet;
-       int ndis_version;
-       struct net_device *ndev;
-
-       net_device = get_outbound_net_device(device);
-       if (!net_device)
-               return -ENODEV;
-       ndev = net_device->ndev;
-
-       init_packet = &net_device->channel_init_pkt;
 
        memset(init_packet, 0, sizeof(struct nvsp_message));
        init_packet->hdr.msg_type = NVSP_MSG_TYPE_INIT;
-       init_packet->msg.init_msg.init.min_protocol_ver =
-               NVSP_MIN_PROTOCOL_VERSION;
-       init_packet->msg.init_msg.init.max_protocol_ver =
-               NVSP_MAX_PROTOCOL_VERSION;
+       init_packet->msg.init_msg.init.min_protocol_ver = nvsp_ver;
+       init_packet->msg.init_msg.init.max_protocol_ver = nvsp_ver;
 
        /* Send the init request */
        ret = vmbus_sendpacket(device->channel, init_packet,
@@ -290,26 +282,62 @@ static int netvsc_connect_vsp(struct hv_device *device)
                               VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
 
        if (ret != 0)
-               goto cleanup;
+               return ret;
 
        t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ);
 
-       if (t == 0) {
-               ret = -ETIMEDOUT;
-               goto cleanup;
-       }
+       if (t == 0)
+               return -ETIMEDOUT;
 
        if (init_packet->msg.init_msg.init_complete.status !=
-           NVSP_STAT_SUCCESS) {
-               ret = -EINVAL;
-               goto cleanup;
-       }
+           NVSP_STAT_SUCCESS)
+               return -EINVAL;
 
-       if (init_packet->msg.init_msg.init_complete.
-           negotiated_protocol_ver != NVSP_PROTOCOL_VERSION_1) {
+       if (nvsp_ver != NVSP_PROTOCOL_VERSION_2)
+               return 0;
+
+       /* NVSPv2 only: Send NDIS config */
+       memset(init_packet, 0, sizeof(struct nvsp_message));
+       init_packet->hdr.msg_type = NVSP_MSG2_TYPE_SEND_NDIS_CONFIG;
+       init_packet->msg.v2_msg.send_ndis_config.mtu = ETH_DATA_LEN;
+
+       ret = vmbus_sendpacket(device->channel, init_packet,
+                               sizeof(struct nvsp_message),
+                               (unsigned long)init_packet,
+                               VM_PKT_DATA_INBAND, 0);
+
+       return ret;
+}
+
+static int netvsc_connect_vsp(struct hv_device *device)
+{
+       int ret;
+       struct netvsc_device *net_device;
+       struct nvsp_message *init_packet;
+       int ndis_version;
+       struct net_device *ndev;
+
+       net_device = get_outbound_net_device(device);
+       if (!net_device)
+               return -ENODEV;
+       ndev = net_device->ndev;
+
+       init_packet = &net_device->channel_init_pkt;
+
+       /* Negotiate the latest NVSP protocol supported */
+       if (negotiate_nvsp_ver(device, net_device, init_packet,
+                              NVSP_PROTOCOL_VERSION_2) == 0) {
+               net_device->nvsp_version = NVSP_PROTOCOL_VERSION_2;
+       } else if (negotiate_nvsp_ver(device, net_device, init_packet,
+                                   NVSP_PROTOCOL_VERSION_1) == 0) {
+               net_device->nvsp_version = NVSP_PROTOCOL_VERSION_1;
+       } else {
                ret = -EPROTO;
                goto cleanup;
        }
+
+       pr_debug("Negotiated NVSP version:%x\n", net_device->nvsp_version);
+
        /* Send the ndis version */
        memset(init_packet, 0, sizeof(struct nvsp_message));