diff options
| author | Haiyang Zhang <haiyangz@microsoft.com> | 2011-12-15 16:45:16 -0500 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2012-01-04 19:13:05 -0500 |
| commit | f157e78de5923dfb209355f3005ce1b5d64f7998 (patch) | |
| tree | 188be24418c55525f234e027ec5c516c466d1955 /drivers/net/hyperv | |
| parent | 453263421f88b4a7e508c2e7b639c97e99c5b118 (diff) | |
net/hyperv: Add NETVSP protocol version negotiation
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>
Diffstat (limited to 'drivers/net/hyperv')
| -rw-r--r-- | drivers/net/hyperv/hyperv_net.h | 101 | ||||
| -rw-r--r-- | drivers/net/hyperv/netvsc.c | 82 |
2 files changed, 149 insertions, 34 deletions
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index ff1b5209b45f..287767055125 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h | |||
| @@ -134,8 +134,7 @@ int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter); | |||
| 134 | #define NVSP_INVALID_PROTOCOL_VERSION ((u32)0xFFFFFFFF) | 134 | #define NVSP_INVALID_PROTOCOL_VERSION ((u32)0xFFFFFFFF) |
| 135 | 135 | ||
| 136 | #define NVSP_PROTOCOL_VERSION_1 2 | 136 | #define NVSP_PROTOCOL_VERSION_1 2 |
| 137 | #define NVSP_MIN_PROTOCOL_VERSION NVSP_PROTOCOL_VERSION_1 | 137 | #define NVSP_PROTOCOL_VERSION_2 0x30002 |
| 138 | #define NVSP_MAX_PROTOCOL_VERSION NVSP_PROTOCOL_VERSION_1 | ||
| 139 | 138 | ||
| 140 | enum { | 139 | enum { |
| 141 | NVSP_MSG_TYPE_NONE = 0, | 140 | NVSP_MSG_TYPE_NONE = 0, |
| @@ -160,11 +159,36 @@ enum { | |||
| 160 | NVSP_MSG1_TYPE_SEND_RNDIS_PKT, | 159 | NVSP_MSG1_TYPE_SEND_RNDIS_PKT, |
| 161 | NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE, | 160 | NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE, |
| 162 | 161 | ||
| 163 | /* | 162 | /* Version 2 messages */ |
| 164 | * This should be set to the number of messages for the version with | 163 | NVSP_MSG2_TYPE_SEND_CHIMNEY_DELEGATED_BUF, |
| 165 | * the maximum number of messages. | 164 | NVSP_MSG2_TYPE_SEND_CHIMNEY_DELEGATED_BUF_COMP, |
| 166 | */ | 165 | NVSP_MSG2_TYPE_REVOKE_CHIMNEY_DELEGATED_BUF, |
| 167 | NVSP_NUM_MSG_PER_VERSION = 9, | 166 | |
| 167 | NVSP_MSG2_TYPE_RESUME_CHIMNEY_RX_INDICATION, | ||
| 168 | |||
| 169 | NVSP_MSG2_TYPE_TERMINATE_CHIMNEY, | ||
| 170 | NVSP_MSG2_TYPE_TERMINATE_CHIMNEY_COMP, | ||
| 171 | |||
| 172 | NVSP_MSG2_TYPE_INDICATE_CHIMNEY_EVENT, | ||
| 173 | |||
| 174 | NVSP_MSG2_TYPE_SEND_CHIMNEY_PKT, | ||
| 175 | NVSP_MSG2_TYPE_SEND_CHIMNEY_PKT_COMP, | ||
| 176 | |||
| 177 | NVSP_MSG2_TYPE_POST_CHIMNEY_RECV_REQ, | ||
| 178 | NVSP_MSG2_TYPE_POST_CHIMNEY_RECV_REQ_COMP, | ||
| 179 | |||
| 180 | NVSP_MSG2_TYPE_ALLOC_RXBUF, | ||
| 181 | NVSP_MSG2_TYPE_ALLOC_RXBUF_COMP, | ||
| 182 | |||
| 183 | NVSP_MSG2_TYPE_FREE_RXBUF, | ||
| 184 | |||
| 185 | NVSP_MSG2_TYPE_SEND_VMQ_RNDIS_PKT, | ||
| 186 | NVSP_MSG2_TYPE_SEND_VMQ_RNDIS_PKT_COMP, | ||
| 187 | |||
| 188 | NVSP_MSG2_TYPE_SEND_NDIS_CONFIG, | ||
| 189 | |||
| 190 | NVSP_MSG2_TYPE_ALLOC_CHIMNEY_HANDLE, | ||
| 191 | NVSP_MSG2_TYPE_ALLOC_CHIMNEY_HANDLE_COMP, | ||
| 168 | }; | 192 | }; |
| 169 | 193 | ||
| 170 | enum { | 194 | enum { |
| @@ -175,6 +199,7 @@ enum { | |||
| 175 | NVSP_STAT_PROTOCOL_TOO_OLD, | 199 | NVSP_STAT_PROTOCOL_TOO_OLD, |
| 176 | NVSP_STAT_INVALID_RNDIS_PKT, | 200 | NVSP_STAT_INVALID_RNDIS_PKT, |
| 177 | NVSP_STAT_BUSY, | 201 | NVSP_STAT_BUSY, |
| 202 | NVSP_STAT_PROTOCOL_UNSUPPORTED, | ||
| 178 | NVSP_STAT_MAX, | 203 | NVSP_STAT_MAX, |
| 179 | }; | 204 | }; |
| 180 | 205 | ||
| @@ -359,9 +384,69 @@ union nvsp_1_message_uber { | |||
| 359 | send_rndis_pkt_complete; | 384 | send_rndis_pkt_complete; |
| 360 | } __packed; | 385 | } __packed; |
| 361 | 386 | ||
| 387 | |||
| 388 | /* | ||
| 389 | * Network VSP protocol version 2 messages: | ||
| 390 | */ | ||
| 391 | struct nvsp_2_vsc_capability { | ||
| 392 | union { | ||
| 393 | u64 data; | ||
| 394 | struct { | ||
| 395 | u64 vmq:1; | ||
| 396 | u64 chimney:1; | ||
| 397 | u64 sriov:1; | ||
| 398 | u64 ieee8021q:1; | ||
| 399 | u64 correlation_id:1; | ||
| 400 | }; | ||
| 401 | }; | ||
| 402 | } __packed; | ||
| 403 | |||
| 404 | struct nvsp_2_send_ndis_config { | ||
| 405 | u32 mtu; | ||
| 406 | u32 reserved; | ||
| 407 | struct nvsp_2_vsc_capability capability; | ||
| 408 | } __packed; | ||
| 409 | |||
| 410 | /* Allocate receive buffer */ | ||
| 411 | struct nvsp_2_alloc_rxbuf { | ||
| 412 | /* Allocation ID to match the allocation request and response */ | ||
| 413 | u32 alloc_id; | ||
| 414 | |||
| 415 | /* Length of the VM shared memory receive buffer that needs to | ||
| 416 | * be allocated | ||
| 417 | */ | ||
| 418 | u32 len; | ||
| 419 | } __packed; | ||
| 420 | |||
| 421 | /* Allocate receive buffer complete */ | ||
| 422 | struct nvsp_2_alloc_rxbuf_comp { | ||
| 423 | /* The NDIS_STATUS code for buffer allocation */ | ||
| 424 | u32 status; | ||
| 425 | |||
| 426 | u32 alloc_id; | ||
| 427 | |||
| 428 | /* GPADL handle for the allocated receive buffer */ | ||
| 429 | u32 gpadl_handle; | ||
| 430 | |||
| 431 | /* Receive buffer ID */ | ||
| 432 | u64 recv_buf_id; | ||
| 433 | } __packed; | ||
| 434 | |||
| 435 | struct nvsp_2_free_rxbuf { | ||
| 436 | u64 recv_buf_id; | ||
| 437 | } __packed; | ||
| 438 | |||
| 439 | union nvsp_2_message_uber { | ||
| 440 | struct nvsp_2_send_ndis_config send_ndis_config; | ||
| 441 | struct nvsp_2_alloc_rxbuf alloc_rxbuf; | ||
| 442 | struct nvsp_2_alloc_rxbuf_comp alloc_rxbuf_comp; | ||
| 443 | struct nvsp_2_free_rxbuf free_rxbuf; | ||
| 444 | } __packed; | ||
| 445 | |||
| 362 | union nvsp_all_messages { | 446 | union nvsp_all_messages { |
| 363 | union nvsp_message_init_uber init_msg; | 447 | union nvsp_message_init_uber init_msg; |
| 364 | union nvsp_1_message_uber v1_msg; | 448 | union nvsp_1_message_uber v1_msg; |
| 449 | union nvsp_2_message_uber v2_msg; | ||
| 365 | } __packed; | 450 | } __packed; |
| 366 | 451 | ||
| 367 | /* ALL Messages */ | 452 | /* ALL Messages */ |
| @@ -391,6 +476,8 @@ struct nvsp_message { | |||
| 391 | struct netvsc_device { | 476 | struct netvsc_device { |
| 392 | struct hv_device *dev; | 477 | struct hv_device *dev; |
| 393 | 478 | ||
| 479 | u32 nvsp_version; | ||
| 480 | |||
| 394 | atomic_t num_outstanding_sends; | 481 | atomic_t num_outstanding_sends; |
| 395 | bool destroy; | 482 | bool destroy; |
| 396 | /* | 483 | /* |
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index bab627f261c4..46828b4dd8ab 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c | |||
| @@ -28,6 +28,7 @@ | |||
| 28 | #include <linux/io.h> | 28 | #include <linux/io.h> |
| 29 | #include <linux/slab.h> | 29 | #include <linux/slab.h> |
| 30 | #include <linux/netdevice.h> | 30 | #include <linux/netdevice.h> |
| 31 | #include <linux/if_ether.h> | ||
| 31 | 32 | ||
| 32 | #include "hyperv_net.h" | 33 | #include "hyperv_net.h" |
| 33 | 34 | ||
| @@ -260,27 +261,18 @@ exit: | |||
| 260 | } | 261 | } |
| 261 | 262 | ||
| 262 | 263 | ||
| 263 | static int netvsc_connect_vsp(struct hv_device *device) | 264 | /* Negotiate NVSP protocol version */ |
| 265 | static int negotiate_nvsp_ver(struct hv_device *device, | ||
| 266 | struct netvsc_device *net_device, | ||
| 267 | struct nvsp_message *init_packet, | ||
| 268 | u32 nvsp_ver) | ||
| 264 | { | 269 | { |
| 265 | int ret, t; | 270 | int ret, t; |
| 266 | struct netvsc_device *net_device; | ||
| 267 | struct nvsp_message *init_packet; | ||
| 268 | int ndis_version; | ||
| 269 | struct net_device *ndev; | ||
| 270 | |||
| 271 | net_device = get_outbound_net_device(device); | ||
| 272 | if (!net_device) | ||
| 273 | return -ENODEV; | ||
| 274 | ndev = net_device->ndev; | ||
| 275 | |||
| 276 | init_packet = &net_device->channel_init_pkt; | ||
| 277 | 271 | ||
| 278 | memset(init_packet, 0, sizeof(struct nvsp_message)); | 272 | memset(init_packet, 0, sizeof(struct nvsp_message)); |
| 279 | init_packet->hdr.msg_type = NVSP_MSG_TYPE_INIT; | 273 | init_packet->hdr.msg_type = NVSP_MSG_TYPE_INIT; |
| 280 | init_packet->msg.init_msg.init.min_protocol_ver = | 274 | init_packet->msg.init_msg.init.min_protocol_ver = nvsp_ver; |
| 281 | NVSP_MIN_PROTOCOL_VERSION; | 275 | init_packet->msg.init_msg.init.max_protocol_ver = nvsp_ver; |
| 282 | init_packet->msg.init_msg.init.max_protocol_ver = | ||
| 283 | NVSP_MAX_PROTOCOL_VERSION; | ||
| 284 | 276 | ||
| 285 | /* Send the init request */ | 277 | /* Send the init request */ |
| 286 | ret = vmbus_sendpacket(device->channel, init_packet, | 278 | ret = vmbus_sendpacket(device->channel, init_packet, |
| @@ -290,26 +282,62 @@ static int netvsc_connect_vsp(struct hv_device *device) | |||
| 290 | VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); | 282 | VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED); |
| 291 | 283 | ||
| 292 | if (ret != 0) | 284 | if (ret != 0) |
| 293 | goto cleanup; | 285 | return ret; |
| 294 | 286 | ||
| 295 | t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ); | 287 | t = wait_for_completion_timeout(&net_device->channel_init_wait, 5*HZ); |
| 296 | 288 | ||
| 297 | if (t == 0) { | 289 | if (t == 0) |
| 298 | ret = -ETIMEDOUT; | 290 | return -ETIMEDOUT; |
| 299 | goto cleanup; | ||
| 300 | } | ||
| 301 | 291 | ||
| 302 | if (init_packet->msg.init_msg.init_complete.status != | 292 | if (init_packet->msg.init_msg.init_complete.status != |
| 303 | NVSP_STAT_SUCCESS) { | 293 | NVSP_STAT_SUCCESS) |
| 304 | ret = -EINVAL; | 294 | return -EINVAL; |
| 305 | goto cleanup; | ||
| 306 | } | ||
| 307 | 295 | ||
| 308 | if (init_packet->msg.init_msg.init_complete. | 296 | if (nvsp_ver != NVSP_PROTOCOL_VERSION_2) |
| 309 | negotiated_protocol_ver != NVSP_PROTOCOL_VERSION_1) { | 297 | return 0; |
| 298 | |||
| 299 | /* NVSPv2 only: Send NDIS config */ | ||
| 300 | memset(init_packet, 0, sizeof(struct nvsp_message)); | ||
| 301 | init_packet->hdr.msg_type = NVSP_MSG2_TYPE_SEND_NDIS_CONFIG; | ||
| 302 | init_packet->msg.v2_msg.send_ndis_config.mtu = ETH_DATA_LEN; | ||
| 303 | |||
| 304 | ret = vmbus_sendpacket(device->channel, init_packet, | ||
| 305 | sizeof(struct nvsp_message), | ||
| 306 | (unsigned long)init_packet, | ||
| 307 | VM_PKT_DATA_INBAND, 0); | ||
| 308 | |||
| 309 | return ret; | ||
| 310 | } | ||
| 311 | |||
| 312 | static int netvsc_connect_vsp(struct hv_device *device) | ||
| 313 | { | ||
| 314 | int ret; | ||
| 315 | struct netvsc_device *net_device; | ||
| 316 | struct nvsp_message *init_packet; | ||
| 317 | int ndis_version; | ||
| 318 | struct net_device *ndev; | ||
| 319 | |||
| 320 | net_device = get_outbound_net_device(device); | ||
| 321 | if (!net_device) | ||
| 322 | return -ENODEV; | ||
| 323 | ndev = net_device->ndev; | ||
| 324 | |||
| 325 | init_packet = &net_device->channel_init_pkt; | ||
| 326 | |||
| 327 | /* Negotiate the latest NVSP protocol supported */ | ||
| 328 | if (negotiate_nvsp_ver(device, net_device, init_packet, | ||
| 329 | NVSP_PROTOCOL_VERSION_2) == 0) { | ||
| 330 | net_device->nvsp_version = NVSP_PROTOCOL_VERSION_2; | ||
| 331 | } else if (negotiate_nvsp_ver(device, net_device, init_packet, | ||
| 332 | NVSP_PROTOCOL_VERSION_1) == 0) { | ||
| 333 | net_device->nvsp_version = NVSP_PROTOCOL_VERSION_1; | ||
| 334 | } else { | ||
| 310 | ret = -EPROTO; | 335 | ret = -EPROTO; |
| 311 | goto cleanup; | 336 | goto cleanup; |
| 312 | } | 337 | } |
| 338 | |||
| 339 | pr_debug("Negotiated NVSP version:%x\n", net_device->nvsp_version); | ||
| 340 | |||
| 313 | /* Send the ndis version */ | 341 | /* Send the ndis version */ |
| 314 | memset(init_packet, 0, sizeof(struct nvsp_message)); | 342 | memset(init_packet, 0, sizeof(struct nvsp_message)); |
| 315 | 343 | ||
