Skip to content

Commit

Permalink
Drivers: hv: vmbus: Fix rescind handling issues
Browse files Browse the repository at this point in the history
This patch handles the following issues that were observed when we are
handling racing channel offer message and rescind message for the same
offer:

1. Since the host does not respond to messages on a rescinded channel,
in the current code, we could be indefinitely blocked on the vmbus_open() call.

2. When a rescinded channel is being closed, if there is a pending interrupt on the
channel, we could end up freeing the channel that the interrupt handler would run on.

Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Reviewed-by: Dexuan Cui <decui@microsoft.com>
Tested-by: Dexuan Cui <decui@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
K. Y. Srinivasan authored and Greg Kroah-Hartman committed Aug 16, 2017
1 parent 3f2baa8 commit 6f3d791
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 3 deletions.
14 changes: 14 additions & 0 deletions drivers/hv/channel.c
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,11 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size,
&vmbus_connection.chn_msg_list);
spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);

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

ret = vmbus_post_msg(open_msg,
sizeof(struct vmbus_channel_open_channel), true);

Expand Down Expand Up @@ -421,6 +426,11 @@ int vmbus_establish_gpadl(struct vmbus_channel *channel, void *kbuffer,

spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);

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

ret = vmbus_post_msg(gpadlmsg, msginfo->msgsize -
sizeof(*msginfo), true);
if (ret != 0)
Expand Down Expand Up @@ -494,6 +504,10 @@ 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);

if (channel->rescind)
goto post_msg_err;

ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_gpadl_teardown),
true);

Expand Down
29 changes: 26 additions & 3 deletions drivers/hv/channel_mgmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,12 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
/* Make sure this is a new offer */
mutex_lock(&vmbus_connection.channel_mutex);

/*
* Now that we have acquired the channel_mutex,
* we can release the potentially racing rescind thread.
*/
atomic_dec(&vmbus_connection.offer_in_progress);

list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
if (!uuid_le_cmp(channel->offermsg.offer.if_type,
newchannel->offermsg.offer.if_type) &&
Expand Down Expand Up @@ -481,7 +487,6 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
channel->num_sc++;
spin_unlock_irqrestore(&channel->lock, flags);
} else {
atomic_dec(&vmbus_connection.offer_in_progress);
goto err_free_chan;
}
}
Expand Down Expand Up @@ -510,7 +515,6 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
if (!fnew) {
if (channel->sc_creation_callback != NULL)
channel->sc_creation_callback(newchannel);
atomic_dec(&vmbus_connection.offer_in_progress);
return;
}

Expand Down Expand Up @@ -541,7 +545,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
goto err_deq_chan;
}

atomic_dec(&vmbus_connection.offer_in_progress);
newchannel->probe_done = true;
return;

err_deq_chan:
Expand Down Expand Up @@ -882,8 +886,27 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
channel->rescind = true;
spin_unlock_irqrestore(&channel->lock, flags);

/*
* Now that we have posted the rescind state, perform
* rescind related cleanup.
*/
vmbus_rescind_cleanup(channel);

/*
* Now wait for offer handling to complete.
*/
while (READ_ONCE(channel->probe_done) == false) {
/*
* We wait here until any channel offer is currently
* being processed.
*/
msleep(1);
}

/*
* At this point, the rescind handling can proceed safely.
*/

if (channel->device_obj) {
if (channel->chn_rescind_callback) {
channel->chn_rescind_callback(channel);
Expand Down
3 changes: 3 additions & 0 deletions drivers/hv/vmbus_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,9 @@ static void vmbus_chan_sched(struct hv_per_cpu_context *hv_cpu)
if (channel->offermsg.child_relid != relid)
continue;

if (channel->rescind)
continue;

switch (channel->callback_mode) {
case HV_CALL_ISR:
vmbus_channel_isr(channel);
Expand Down
2 changes: 2 additions & 0 deletions include/linux/hyperv.h
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,8 @@ struct vmbus_channel {
*/
enum hv_numa_policy affinity_policy;

bool probe_done;

};

static inline bool is_hvsock_channel(const struct vmbus_channel *c)
Expand Down

0 comments on commit 6f3d791

Please sign in to comment.