Drivers: hv: vmbus: Fix bugs in rescind handling
This patch addresses the following bugs in the current rescind handling code: 1. Fixes a race condition where we may be invoking hv_process_channel_removal() on an already freed channel. 2. Prevents indefinite wait when rescinding sub-channels by correctly setting the probe_complete state. I would like to thank Dexuan for patiently reviewing earlier versions of this patch and identifying many of the issues fixed here. Greg, please apply this to 4.14-final. Fixes: '54a66265d675 ("Drivers: hv: vmbus: Fix rescind handling")' Signed-off-by: K. Y. Srinivasan <kys@microsoft.com> Reviewed-by: Dexuan Cui <decui@microsoft.com> Cc: stable@vger.kernel.org # (4.13 and above) Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
688cb67839
commit
192b2d7872
|
@ -640,6 +640,7 @@ void vmbus_close(struct vmbus_channel *channel)
|
||||||
*/
|
*/
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
mutex_lock(&vmbus_connection.channel_mutex);
|
||||||
/*
|
/*
|
||||||
* Close all the sub-channels first and then close the
|
* Close all the sub-channels first and then close the
|
||||||
* primary channel.
|
* primary channel.
|
||||||
|
@ -648,16 +649,15 @@ void vmbus_close(struct vmbus_channel *channel)
|
||||||
cur_channel = list_entry(cur, struct vmbus_channel, sc_list);
|
cur_channel = list_entry(cur, struct vmbus_channel, sc_list);
|
||||||
vmbus_close_internal(cur_channel);
|
vmbus_close_internal(cur_channel);
|
||||||
if (cur_channel->rescind) {
|
if (cur_channel->rescind) {
|
||||||
mutex_lock(&vmbus_connection.channel_mutex);
|
hv_process_channel_removal(
|
||||||
hv_process_channel_removal(cur_channel,
|
|
||||||
cur_channel->offermsg.child_relid);
|
cur_channel->offermsg.child_relid);
|
||||||
mutex_unlock(&vmbus_connection.channel_mutex);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Now close the primary.
|
* Now close the primary.
|
||||||
*/
|
*/
|
||||||
vmbus_close_internal(channel);
|
vmbus_close_internal(channel);
|
||||||
|
mutex_unlock(&vmbus_connection.channel_mutex);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(vmbus_close);
|
EXPORT_SYMBOL_GPL(vmbus_close);
|
||||||
|
|
||||||
|
|
|
@ -159,7 +159,7 @@ static void vmbus_rescind_cleanup(struct vmbus_channel *channel)
|
||||||
|
|
||||||
|
|
||||||
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
|
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
|
||||||
|
channel->rescind = true;
|
||||||
list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list,
|
list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list,
|
||||||
msglistentry) {
|
msglistentry) {
|
||||||
|
|
||||||
|
@ -381,14 +381,21 @@ static void vmbus_release_relid(u32 relid)
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid)
|
void hv_process_channel_removal(u32 relid)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
struct vmbus_channel *primary_channel;
|
struct vmbus_channel *primary_channel, *channel;
|
||||||
|
|
||||||
BUG_ON(!channel->rescind);
|
|
||||||
BUG_ON(!mutex_is_locked(&vmbus_connection.channel_mutex));
|
BUG_ON(!mutex_is_locked(&vmbus_connection.channel_mutex));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure channel is valid as we may have raced.
|
||||||
|
*/
|
||||||
|
channel = relid2channel(relid);
|
||||||
|
if (!channel)
|
||||||
|
return;
|
||||||
|
|
||||||
|
BUG_ON(!channel->rescind);
|
||||||
if (channel->target_cpu != get_cpu()) {
|
if (channel->target_cpu != get_cpu()) {
|
||||||
put_cpu();
|
put_cpu();
|
||||||
smp_call_function_single(channel->target_cpu,
|
smp_call_function_single(channel->target_cpu,
|
||||||
|
@ -515,6 +522,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
|
||||||
if (!fnew) {
|
if (!fnew) {
|
||||||
if (channel->sc_creation_callback != NULL)
|
if (channel->sc_creation_callback != NULL)
|
||||||
channel->sc_creation_callback(newchannel);
|
channel->sc_creation_callback(newchannel);
|
||||||
|
newchannel->probe_done = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -834,7 +842,6 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
|
||||||
{
|
{
|
||||||
struct vmbus_channel_rescind_offer *rescind;
|
struct vmbus_channel_rescind_offer *rescind;
|
||||||
struct vmbus_channel *channel;
|
struct vmbus_channel *channel;
|
||||||
unsigned long flags;
|
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
|
|
||||||
rescind = (struct vmbus_channel_rescind_offer *)hdr;
|
rescind = (struct vmbus_channel_rescind_offer *)hdr;
|
||||||
|
@ -873,16 +880,6 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&channel->lock, flags);
|
|
||||||
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.
|
* Now wait for offer handling to complete.
|
||||||
*/
|
*/
|
||||||
|
@ -901,6 +898,7 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
|
||||||
if (channel->device_obj) {
|
if (channel->device_obj) {
|
||||||
if (channel->chn_rescind_callback) {
|
if (channel->chn_rescind_callback) {
|
||||||
channel->chn_rescind_callback(channel);
|
channel->chn_rescind_callback(channel);
|
||||||
|
vmbus_rescind_cleanup(channel);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
@ -909,6 +907,7 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
|
||||||
*/
|
*/
|
||||||
dev = get_device(&channel->device_obj->device);
|
dev = get_device(&channel->device_obj->device);
|
||||||
if (dev) {
|
if (dev) {
|
||||||
|
vmbus_rescind_cleanup(channel);
|
||||||
vmbus_device_unregister(channel->device_obj);
|
vmbus_device_unregister(channel->device_obj);
|
||||||
put_device(dev);
|
put_device(dev);
|
||||||
}
|
}
|
||||||
|
@ -921,16 +920,16 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
|
||||||
* 1. Close all sub-channels first
|
* 1. Close all sub-channels first
|
||||||
* 2. Then close the primary channel.
|
* 2. Then close the primary channel.
|
||||||
*/
|
*/
|
||||||
|
mutex_lock(&vmbus_connection.channel_mutex);
|
||||||
|
vmbus_rescind_cleanup(channel);
|
||||||
if (channel->state == CHANNEL_OPEN_STATE) {
|
if (channel->state == CHANNEL_OPEN_STATE) {
|
||||||
/*
|
/*
|
||||||
* The channel is currently not open;
|
* The channel is currently not open;
|
||||||
* it is safe for us to cleanup the channel.
|
* it is safe for us to cleanup the channel.
|
||||||
*/
|
*/
|
||||||
mutex_lock(&vmbus_connection.channel_mutex);
|
hv_process_channel_removal(rescind->child_relid);
|
||||||
hv_process_channel_removal(channel,
|
|
||||||
channel->offermsg.child_relid);
|
|
||||||
mutex_unlock(&vmbus_connection.channel_mutex);
|
|
||||||
}
|
}
|
||||||
|
mutex_unlock(&vmbus_connection.channel_mutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -768,8 +768,7 @@ static void vmbus_device_release(struct device *device)
|
||||||
struct vmbus_channel *channel = hv_dev->channel;
|
struct vmbus_channel *channel = hv_dev->channel;
|
||||||
|
|
||||||
mutex_lock(&vmbus_connection.channel_mutex);
|
mutex_lock(&vmbus_connection.channel_mutex);
|
||||||
hv_process_channel_removal(channel,
|
hv_process_channel_removal(channel->offermsg.child_relid);
|
||||||
channel->offermsg.child_relid);
|
|
||||||
mutex_unlock(&vmbus_connection.channel_mutex);
|
mutex_unlock(&vmbus_connection.channel_mutex);
|
||||||
kfree(hv_dev);
|
kfree(hv_dev);
|
||||||
|
|
||||||
|
|
|
@ -1403,7 +1403,7 @@ extern bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, u8 *buf,
|
||||||
const int *srv_version, int srv_vercnt,
|
const int *srv_version, int srv_vercnt,
|
||||||
int *nego_fw_version, int *nego_srv_version);
|
int *nego_fw_version, int *nego_srv_version);
|
||||||
|
|
||||||
void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid);
|
void hv_process_channel_removal(u32 relid);
|
||||||
|
|
||||||
void vmbus_setevent(struct vmbus_channel *channel);
|
void vmbus_setevent(struct vmbus_channel *channel);
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in New Issue