aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVitaly Kuznetsov <vkuznets@redhat.com>2016-12-07 04:16:24 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-03-12 00:41:47 -0500
commit65013a93b6c335bd166c911d73c47ff4bcbdb8be (patch)
tree5e240078524e8cce6c5fc202292c877f62963e0a
parent730b1b20c6cdc63f4ce32efc5a494fc9b141b854 (diff)
Drivers: hv: vmbus: Raise retry/wait limits in vmbus_post_msg()
commit c0bb03924f1a80e7f65900e36c8e6b3dc167c5f8 upstream. DoS protection conditions were altered in WS2016 and now it's easy to get -EAGAIN returned from vmbus_post_msg() (e.g. when we try changing MTU on a netvsc device in a loop). All vmbus_post_msg() callers don't retry the operation and we usually end up with a non-functional device or crash. While host's DoS protection conditions are unknown to me my tests show that it can take up to 10 seconds before the message is sent so doing udelay() is not an option, we really need to sleep. Almost all vmbus_post_msg() callers are ready to sleep but there is one special case: vmbus_initiate_unload() which can be called from interrupt/NMI context and we can't sleep there. I'm also not sure about the lonely vmbus_send_tl_connect_request() which has no in-tree users but its external users are most likely waiting for the host to reply so sleeping there is also appropriate. Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com> Signed-off-by: K. Y. Srinivasan <kys@microsoft.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/hv/channel.c17
-rw-r--r--drivers/hv/channel_mgmt.c10
-rw-r--r--drivers/hv/connection.c17
-rw-r--r--drivers/hv/hyperv_vmbus.h2
4 files changed, 28 insertions, 18 deletions
diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c
index 5fb4c6d9209b..d5b8d9fd50bb 100644
--- a/drivers/hv/channel.c
+++ b/drivers/hv/channel.c
@@ -181,7 +181,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
181 spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); 181 spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
182 182
183 ret = vmbus_post_msg(open_msg, 183 ret = vmbus_post_msg(open_msg,
184 sizeof(struct vmbus_channel_open_channel)); 184 sizeof(struct vmbus_channel_open_channel), true);
185 185
186 if (ret != 0) { 186 if (ret != 0) {
187 err = ret; 187 err = ret;
@@ -233,7 +233,7 @@ int vmbus_send_tl_connect_request(const uuid_le *shv_guest_servie_id,
233 conn_msg.guest_endpoint_id = *shv_guest_servie_id; 233 conn_msg.guest_endpoint_id = *shv_guest_servie_id;
234 conn_msg.host_service_id = *shv_host_servie_id; 234 conn_msg.host_service_id = *shv_host_servie_id;
235 235
236 return vmbus_post_msg(&conn_msg, sizeof(conn_msg)); 236 return vmbus_post_msg(&conn_msg, sizeof(conn_msg), true);
237} 237}
238EXPORT_SYMBOL_GPL(vmbus_send_tl_connect_request); 238EXPORT_SYMBOL_GPL(vmbus_send_tl_connect_request);
239 239
@@ -419,7 +419,7 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
419 spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); 419 spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
420 420
421 ret = vmbus_post_msg(gpadlmsg, msginfo->msgsize - 421 ret = vmbus_post_msg(gpadlmsg, msginfo->msgsize -
422 sizeof(*msginfo)); 422 sizeof(*msginfo), true);
423 if (ret != 0) 423 if (ret != 0)
424 goto cleanup; 424 goto cleanup;
425 425
@@ -433,8 +433,8 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
433 gpadl_body->gpadl = next_gpadl_handle; 433 gpadl_body->gpadl = next_gpadl_handle;
434 434
435 ret = vmbus_post_msg(gpadl_body, 435 ret = vmbus_post_msg(gpadl_body,
436 submsginfo->msgsize - 436 submsginfo->msgsize - sizeof(*submsginfo),
437 sizeof(*submsginfo)); 437 true);
438 if (ret != 0) 438 if (ret != 0)
439 goto cleanup; 439 goto cleanup;
440 440
@@ -485,8 +485,8 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle)
485 list_add_tail(&info->msglistentry, 485 list_add_tail(&info->msglistentry,
486 &vmbus_connection.chn_msg_list); 486 &vmbus_connection.chn_msg_list);
487 spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); 487 spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
488 ret = vmbus_post_msg(msg, 488 ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_gpadl_teardown),
489 sizeof(struct vmbus_channel_gpadl_teardown)); 489 true);
490 490
491 if (ret) 491 if (ret)
492 goto post_msg_err; 492 goto post_msg_err;
@@ -557,7 +557,8 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
557 msg->header.msgtype = CHANNELMSG_CLOSECHANNEL; 557 msg->header.msgtype = CHANNELMSG_CLOSECHANNEL;
558 msg->child_relid = channel->offermsg.child_relid; 558 msg->child_relid = channel->offermsg.child_relid;
559 559
560 ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_close_channel)); 560 ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_close_channel),
561 true);
561 562
562 if (ret) { 563 if (ret) {
563 pr_err("Close failed: close post msg return is %d\n", ret); 564 pr_err("Close failed: close post msg return is %d\n", ret);
diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
index caf341842464..f3a8b52acb51 100644
--- a/drivers/hv/channel_mgmt.c
+++ b/drivers/hv/channel_mgmt.c
@@ -321,7 +321,8 @@ static void vmbus_release_relid(u32 relid)
321 memset(&msg, 0, sizeof(struct vmbus_channel_relid_released)); 321 memset(&msg, 0, sizeof(struct vmbus_channel_relid_released));
322 msg.child_relid = relid; 322 msg.child_relid = relid;
323 msg.header.msgtype = CHANNELMSG_RELID_RELEASED; 323 msg.header.msgtype = CHANNELMSG_RELID_RELEASED;
324 vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released)); 324 vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released),
325 true);
325} 326}
326 327
327void hv_event_tasklet_disable(struct vmbus_channel *channel) 328void hv_event_tasklet_disable(struct vmbus_channel *channel)
@@ -728,7 +729,8 @@ void vmbus_initiate_unload(bool crash)
728 init_completion(&vmbus_connection.unload_event); 729 init_completion(&vmbus_connection.unload_event);
729 memset(&hdr, 0, sizeof(struct vmbus_channel_message_header)); 730 memset(&hdr, 0, sizeof(struct vmbus_channel_message_header));
730 hdr.msgtype = CHANNELMSG_UNLOAD; 731 hdr.msgtype = CHANNELMSG_UNLOAD;
731 vmbus_post_msg(&hdr, sizeof(struct vmbus_channel_message_header)); 732 vmbus_post_msg(&hdr, sizeof(struct vmbus_channel_message_header),
733 !crash);
732 734
733 /* 735 /*
734 * vmbus_initiate_unload() is also called on crash and the crash can be 736 * vmbus_initiate_unload() is also called on crash and the crash can be
@@ -1116,8 +1118,8 @@ int vmbus_request_offers(void)
1116 msg->msgtype = CHANNELMSG_REQUESTOFFERS; 1118 msg->msgtype = CHANNELMSG_REQUESTOFFERS;
1117 1119
1118 1120
1119 ret = vmbus_post_msg(msg, 1121 ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_message_header),
1120 sizeof(struct vmbus_channel_message_header)); 1122 true);
1121 if (ret != 0) { 1123 if (ret != 0) {
1122 pr_err("Unable to request offers - %d\n", ret); 1124 pr_err("Unable to request offers - %d\n", ret);
1123 1125
diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
index 78e6368a4423..840b6db0ea4b 100644
--- a/drivers/hv/connection.c
+++ b/drivers/hv/connection.c
@@ -110,7 +110,8 @@ static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo,
110 spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); 110 spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
111 111
112 ret = vmbus_post_msg(msg, 112 ret = vmbus_post_msg(msg,
113 sizeof(struct vmbus_channel_initiate_contact)); 113 sizeof(struct vmbus_channel_initiate_contact),
114 true);
114 if (ret != 0) { 115 if (ret != 0) {
115 spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); 116 spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
116 list_del(&msginfo->msglistentry); 117 list_del(&msginfo->msglistentry);
@@ -434,7 +435,7 @@ void vmbus_on_event(unsigned long data)
434/* 435/*
435 * vmbus_post_msg - Send a msg on the vmbus's message connection 436 * vmbus_post_msg - Send a msg on the vmbus's message connection
436 */ 437 */
437int vmbus_post_msg(void *buffer, size_t buflen) 438int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep)
438{ 439{
439 union hv_connection_id conn_id; 440 union hv_connection_id conn_id;
440 int ret = 0; 441 int ret = 0;
@@ -449,7 +450,7 @@ int vmbus_post_msg(void *buffer, size_t buflen)
449 * insufficient resources. Retry the operation a couple of 450 * insufficient resources. Retry the operation a couple of
450 * times before giving up. 451 * times before giving up.
451 */ 452 */
452 while (retries < 20) { 453 while (retries < 100) {
453 ret = hv_post_message(conn_id, 1, buffer, buflen); 454 ret = hv_post_message(conn_id, 1, buffer, buflen);
454 455
455 switch (ret) { 456 switch (ret) {
@@ -472,8 +473,14 @@ int vmbus_post_msg(void *buffer, size_t buflen)
472 } 473 }
473 474
474 retries++; 475 retries++;
475 udelay(usec); 476 if (can_sleep && usec > 1000)
476 if (usec < 2048) 477 msleep(usec / 1000);
478 else if (usec < MAX_UDELAY_MS * 1000)
479 udelay(usec);
480 else
481 mdelay(usec / 1000);
482
483 if (usec < 256000)
477 usec *= 2; 484 usec *= 2;
478 } 485 }
479 return ret; 486 return ret;
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index 2b13f2a0a71e..8d7f865c1133 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -683,7 +683,7 @@ void vmbus_free_channels(void);
683int vmbus_connect(void); 683int vmbus_connect(void);
684void vmbus_disconnect(void); 684void vmbus_disconnect(void);
685 685
686int vmbus_post_msg(void *buffer, size_t buflen); 686int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep);
687 687
688void vmbus_on_event(unsigned long data); 688void vmbus_on_event(unsigned long data);
689void vmbus_on_msg_dpc(unsigned long data); 689void vmbus_on_msg_dpc(unsigned long data);