Skip to content

Commit

Permalink
Drivers: hv: vmbus: Fix a rescind handling bug
Browse files Browse the repository at this point in the history
The host can rescind a channel that has been offered to the
guest and once the channel is rescinded, the host does not
respond to any requests on that channel. Deal with the case where
the guest may be blocked waiting for a response from the host.

Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
K. Y. Srinivasan authored and Greg Kroah-Hartman committed Jan 10, 2017
1 parent 523b940 commit ccb61f8
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 0 deletions.
18 changes: 18 additions & 0 deletions drivers/hv/channel.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
}

init_completion(&open_info->waitevent);
open_info->waiting_channel = newchannel;

open_msg = (struct vmbus_channel_open_channel *)open_info->msg;
open_msg->header.msgtype = CHANNELMSG_OPENCHANNEL;
Expand Down Expand Up @@ -194,6 +195,11 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
list_del(&open_info->msglistentry);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);

if (newchannel->rescind) {
err = -ENODEV;
goto error_free_gpadl;
}

if (open_info->response.open_result.status) {
err = -EAGAIN;
goto error_free_gpadl;
Expand Down Expand Up @@ -405,6 +411,7 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
return ret;

init_completion(&msginfo->waitevent);
msginfo->waiting_channel = channel;

gpadlmsg = (struct vmbus_channel_gpadl_header *)msginfo->msg;
gpadlmsg->header.msgtype = CHANNELMSG_GPADL_HEADER;
Expand Down Expand Up @@ -441,6 +448,11 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,
}
wait_for_completion(&msginfo->waitevent);

if (channel->rescind) {
ret = -ENODEV;
goto cleanup;
}

/* At this point, we received the gpadl created msg */
*gpadl_handle = gpadlmsg->gpadl;

Expand Down Expand Up @@ -474,6 +486,7 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle)
return -ENOMEM;

init_completion(&info->waitevent);
info->waiting_channel = channel;

msg = (struct vmbus_channel_gpadl_teardown *)info->msg;

Expand All @@ -493,6 +506,11 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle)

wait_for_completion(&info->waitevent);

if (channel->rescind) {
ret = -ENODEV;
goto post_msg_err;
}

post_msg_err:
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
list_del(&info->msglistentry);
Expand Down
25 changes: 25 additions & 0 deletions drivers/hv/channel_mgmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,29 @@ static const struct {
{ HV_RDV_GUID },
};

/*
* The rescinded channel may be blocked waiting for a response from the host;
* take care of that.
*/
static void vmbus_rescind_cleanup(struct vmbus_channel *channel)
{
struct vmbus_channel_msginfo *msginfo;
unsigned long flags;


spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);

list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list,
msglistentry) {

if (msginfo->waiting_channel == channel) {
complete(&msginfo->waitevent);
break;
}
}
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
}

static bool is_unsupported_vmbus_devs(const uuid_le *guid)
{
int i;
Expand Down Expand Up @@ -825,6 +848,8 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
channel->rescind = true;
spin_unlock_irqrestore(&channel->lock, flags);

vmbus_rescind_cleanup(channel);

if (channel->device_obj) {
if (channel->chn_rescind_callback) {
channel->chn_rescind_callback(channel);
Expand Down
1 change: 1 addition & 0 deletions include/linux/hyperv.h
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,7 @@ struct vmbus_channel_msginfo {

/* Synchronize the request/response if needed */
struct completion waitevent;
struct vmbus_channel *waiting_channel;
union {
struct vmbus_channel_version_supported version_supported;
struct vmbus_channel_open_result open_result;
Expand Down

0 comments on commit ccb61f8

Please sign in to comment.