RE: [PATCH] Drivers: hv: vmbus: Raise retry/wait limits in vmbus_post_msg()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




> -----Original Message-----
> From: Vitaly Kuznetsov [mailto:vkuznets@xxxxxxxxxx]
> Sent: Wednesday, October 26, 2016 7:12 AM
> To: devel@xxxxxxxxxxxxxxxxxxxxxx
> Cc: linux-kernel@xxxxxxxxxxxxxxx; KY Srinivasan <kys@xxxxxxxxxxxxx>;
> Haiyang Zhang <haiyangz@xxxxxxxxxxxxx>
> Subject: [PATCH] Drivers: hv: vmbus: Raise retry/wait limits in
> vmbus_post_msg()
> 
> 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 46 attempts to send a message after changing udelay()
> to
> mdelay() and caping msec at '256', this means we can wait up to 10
> seconds
> before the message is sent so we need to use msleep() instead. 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@xxxxxxxxxx>
> ---
>  drivers/hv/channel.c      | 17 +++++++++--------
>  drivers/hv/channel_mgmt.c | 10 ++++++----
>  drivers/hv/connection.c   | 19 ++++++++++++-------
>  drivers/hv/hyperv_vmbus.h |  2 +-
>  4 files changed, 28 insertions(+), 20 deletions(-)
> 
> diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c
> index 16f91c8..28ca66e 100644
> --- a/drivers/hv/channel.c
> +++ b/drivers/hv/channel.c
> @@ -180,7 +180,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32
> send_ringbuffer_size,
>  	spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
> 
>  	ret = vmbus_post_msg(open_msg,
> -			       sizeof(struct vmbus_channel_open_channel));
> +			     sizeof(struct vmbus_channel_open_channel), true);
> 
>  	if (ret != 0) {
>  		err = ret;
> @@ -232,7 +232,7 @@ int vmbus_send_tl_connect_request(const uuid_le
> *shv_guest_servie_id,
>  	conn_msg.guest_endpoint_id = *shv_guest_servie_id;
>  	conn_msg.host_service_id = *shv_host_servie_id;
> 
> -	return vmbus_post_msg(&conn_msg, sizeof(conn_msg));
> +	return vmbus_post_msg(&conn_msg, sizeof(conn_msg), true);
>  }
>  EXPORT_SYMBOL_GPL(vmbus_send_tl_connect_request);
> 
> @@ -418,7 +418,7 @@ int vmbus_establish_gpadl(struct vmbus_channel
> *channel, void *kbuffer,
>  	spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
> 
>  	ret = vmbus_post_msg(gpadlmsg, msginfo->msgsize -
> -			       sizeof(*msginfo));
> +			     sizeof(*msginfo), true);
>  	if (ret != 0)
>  		goto cleanup;
> 
> @@ -432,8 +432,8 @@ int vmbus_establish_gpadl(struct vmbus_channel
> *channel, void *kbuffer,
>  		gpadl_body->gpadl = next_gpadl_handle;
> 
>  		ret = vmbus_post_msg(gpadl_body,
> -				     submsginfo->msgsize -
> -				     sizeof(*submsginfo));
> +				     submsginfo->msgsize - sizeof(*submsginfo),
> +				     true);
>  		if (ret != 0)
>  			goto cleanup;
> 
> @@ -484,8 +484,8 @@ int vmbus_teardown_gpadl(struct vmbus_channel
> *channel, u32 gpadl_handle)
>  	list_add_tail(&info->msglistentry,
>  		      &vmbus_connection.chn_msg_list);
>  	spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
> -	ret = vmbus_post_msg(msg,
> -			       sizeof(struct vmbus_channel_gpadl_teardown));
> +	ret = vmbus_post_msg(msg, sizeof(struct
> vmbus_channel_gpadl_teardown),
> +			     true);
> 
>  	if (ret)
>  		goto post_msg_err;
> @@ -556,7 +556,8 @@ static int vmbus_close_internal(struct vmbus_channel
> *channel)
>  	msg->header.msgtype = CHANNELMSG_CLOSECHANNEL;
>  	msg->child_relid = channel->offermsg.child_relid;
> 
> -	ret = vmbus_post_msg(msg, sizeof(struct
> vmbus_channel_close_channel));
> +	ret = vmbus_post_msg(msg, sizeof(struct
> vmbus_channel_close_channel),
> +			     true);
> 
>  	if (ret) {
>  		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 96a85cd..160d92e 100644
> --- a/drivers/hv/channel_mgmt.c
> +++ b/drivers/hv/channel_mgmt.c
> @@ -321,7 +321,8 @@ static void vmbus_release_relid(u32 relid)
>  	memset(&msg, 0, sizeof(struct vmbus_channel_relid_released));
>  	msg.child_relid = relid;
>  	msg.header.msgtype = CHANNELMSG_RELID_RELEASED;
> -	vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released));
> +	vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released),
> +		       true);
>  }
> 
>  void hv_event_tasklet_disable(struct vmbus_channel *channel)
> @@ -728,7 +729,8 @@ void vmbus_initiate_unload(bool crash)
>  	init_completion(&vmbus_connection.unload_event);
>  	memset(&hdr, 0, sizeof(struct vmbus_channel_message_header));
>  	hdr.msgtype = CHANNELMSG_UNLOAD;
> -	vmbus_post_msg(&hdr, sizeof(struct vmbus_channel_message_header));
> +	vmbus_post_msg(&hdr, sizeof(struct vmbus_channel_message_header),
> +		       !crash);
> 
>  	/*
>  	 * vmbus_initiate_unload() is also called on crash and the crash
> can be
> @@ -1116,8 +1118,8 @@ int vmbus_request_offers(void)
>  	msg->msgtype = CHANNELMSG_REQUESTOFFERS;
> 
> 
> -	ret = vmbus_post_msg(msg,
> -			       sizeof(struct vmbus_channel_message_header));
> +	ret = vmbus_post_msg(msg, sizeof(struct
> vmbus_channel_message_header),
> +			     true);
>  	if (ret != 0) {
>  		pr_err("Unable to request offers - %d\n", ret);
> 
> diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
> index 78e6368..da1feb4 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,
>  	spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
> 
>  	ret = vmbus_post_msg(msg,
> -			       sizeof(struct vmbus_channel_initiate_contact));
> +			     sizeof(struct vmbus_channel_initiate_contact),
> +			     true);
>  	if (ret != 0) {
>  		spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
>  		list_del(&msginfo->msglistentry);
> @@ -434,12 +435,12 @@ void vmbus_on_event(unsigned long data)
>  /*
>   * vmbus_post_msg - Send a msg on the vmbus's message connection
>   */
> -int vmbus_post_msg(void *buffer, size_t buflen)
> +int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep)
>  {
>  	union hv_connection_id conn_id;
>  	int ret = 0;
>  	int retries = 0;
> -	u32 usec = 1;
> +	u32 msec = 1;
> 
>  	conn_id.asu32 = 0;
>  	conn_id.u.id = VMBUS_MESSAGE_CONNECTION_ID;
> @@ -449,7 +450,7 @@ int vmbus_post_msg(void *buffer, size_t buflen)
>  	 * insufficient resources. Retry the operation a couple of
>  	 * times before giving up.
>  	 */
> -	while (retries < 20) {
> +	while (retries < 100) {
>  		ret = hv_post_message(conn_id, 1, buffer, buflen);
> 
>  		switch (ret) {
> @@ -472,9 +473,13 @@ int vmbus_post_msg(void *buffer, size_t buflen)
>  		}
> 
>  		retries++;
> -		udelay(usec);
> -		if (usec < 2048)
> -			usec *= 2;
> +		if (can_sleep)
> +			msleep(msec);
> +		else
> +			mdelay(msec);
> +
> +		if (msec < 256)
> +			msec *= 2;

The change looks fine.
I knew, in case of initializing large trunk of receive/send buffers, vmbus_establish_gpadl() is called many times, and retrying of 1ms or more is needed often. So, using milliseconds delay/sleep is good.

Thanks,

Reviewed-by: Haiyang Zhang <haiyangz@xxxxxxxxxxxxx>


_______________________________________________
devel mailing list
devel@xxxxxxxxxxxxxxxxxxxxxx
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel



[Index of Archives]     [Linux Driver Backports]     [DMA Engine]     [Linux GPIO]     [Linux SPI]     [Video for Linux]     [Linux USB Devel]     [Linux Coverity]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux