hyperv/vmbus: use a better retry method in hv_vmbus_post_message()

Most often, hv_vmbus_post_message() doesn't fail.  However, it fails
intermittently when GPADLs of large shared memory is to be established
with the host, e.g. on the hn(4) attach path: a GPADL of 15MB sendbuf
is created, for which lots of messages will be flooded to the host.
The host side tries to throttle the message rate by returning
HV_STATUS_INSUFFICIENT_BUFFERS.

Before this commit, we do several retries for failed messages, but the
delay between each retry is pretty/too low, which will cause sporadic
message posting failure.  We now use large delay (>=1ms) between each
retry to fix the message posting failure.

Submitted by:	Dexuan Cui <decui microsoft com>
Reviewed by:	sephe
MFC after:	1 week
Sponsored by:	Microsoft OSTC
Differential Revision:	https://reviews.freebsd.org/D5715
This commit is contained in:
Sepherosa Ziehau 2016-03-24 00:40:41 +00:00
parent 7a2c1d8c60
commit 25a40f12b0
2 changed files with 23 additions and 18 deletions

View File

@ -364,31 +364,35 @@ hv_vmbus_on_events(int cpu)
/**
* Send a msg on the vmbus's message connection
*/
int hv_vmbus_post_message(void *buffer, size_t bufferLen) {
int ret = 0;
int hv_vmbus_post_message(void *buffer, size_t bufferLen)
{
hv_vmbus_connection_id connId;
unsigned retries = 0;
sbintime_t time = SBT_1MS;
int retries;
int ret;
/* NetScaler delays from previous code were consolidated here */
static int delayAmount[] = {100, 100, 100, 500, 500, 5000, 5000, 5000};
connId.as_uint32_t = 0;
connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID;
/* for(each entry in delayAmount) try to post message,
* delay a little bit before retrying
/*
* We retry to cope with transient failures caused by host side's
* insufficient resources. 20 times should suffice in practice.
*/
for (retries = 0;
retries < sizeof(delayAmount)/sizeof(delayAmount[0]); retries++) {
connId.as_uint32_t = 0;
connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID;
ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer, bufferLen);
if (ret != HV_STATUS_INSUFFICIENT_BUFFERS)
break;
/* TODO: KYS We should use a blocking wait call */
DELAY(delayAmount[retries]);
for (retries = 0; retries < 20; retries++) {
ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer,
bufferLen);
if (ret == HV_STATUS_SUCCESS)
return (0);
pause_sbt("pstmsg", time, 0, C_HARDCLOCK);
if (time < SBT_1S * 2)
time *= 2;
}
KASSERT(ret == 0, ("Error VMBUS: Message Post Failed\n"));
KASSERT(ret == HV_STATUS_SUCCESS,
("Error VMBUS: Message Post Failed, ret=%d\n", ret));
return (ret);
return (EAGAIN);
}
/**

View File

@ -70,6 +70,7 @@ typedef uint16_t hv_vmbus_status;
* You did not supply enough message buffers to send a message.
*/
#define HV_STATUS_SUCCESS ((uint16_t)0)
#define HV_STATUS_INSUFFICIENT_BUFFERS ((uint16_t)0x0013)
typedef void (*hv_vmbus_channel_callback)(void *context);