Skip to content

Commit 192b2d7

Browse files
kattisrinivasangregkh
authored andcommitted
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 <[email protected]> Reviewed-by: Dexuan Cui <[email protected]> Cc: [email protected] # (4.13 and above) Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 688cb67 commit 192b2d7

File tree

4 files changed

+23
-25
lines changed

4 files changed

+23
-25
lines changed

drivers/hv/channel.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,7 @@ void vmbus_close(struct vmbus_channel *channel)
640640
*/
641641
return;
642642
}
643+
mutex_lock(&vmbus_connection.channel_mutex);
643644
/*
644645
* Close all the sub-channels first and then close the
645646
* primary channel.
@@ -648,16 +649,15 @@ void vmbus_close(struct vmbus_channel *channel)
648649
cur_channel = list_entry(cur, struct vmbus_channel, sc_list);
649650
vmbus_close_internal(cur_channel);
650651
if (cur_channel->rescind) {
651-
mutex_lock(&vmbus_connection.channel_mutex);
652-
hv_process_channel_removal(cur_channel,
652+
hv_process_channel_removal(
653653
cur_channel->offermsg.child_relid);
654-
mutex_unlock(&vmbus_connection.channel_mutex);
655654
}
656655
}
657656
/*
658657
* Now close the primary.
659658
*/
660659
vmbus_close_internal(channel);
660+
mutex_unlock(&vmbus_connection.channel_mutex);
661661
}
662662
EXPORT_SYMBOL_GPL(vmbus_close);
663663

drivers/hv/channel_mgmt.c

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ static void vmbus_rescind_cleanup(struct vmbus_channel *channel)
159159

160160

161161
spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
162-
162+
channel->rescind = true;
163163
list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list,
164164
msglistentry) {
165165

@@ -381,14 +381,21 @@ static void vmbus_release_relid(u32 relid)
381381
true);
382382
}
383383

384-
void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid)
384+
void hv_process_channel_removal(u32 relid)
385385
{
386386
unsigned long flags;
387-
struct vmbus_channel *primary_channel;
387+
struct vmbus_channel *primary_channel, *channel;
388388

389-
BUG_ON(!channel->rescind);
390389
BUG_ON(!mutex_is_locked(&vmbus_connection.channel_mutex));
391390

391+
/*
392+
* Make sure channel is valid as we may have raced.
393+
*/
394+
channel = relid2channel(relid);
395+
if (!channel)
396+
return;
397+
398+
BUG_ON(!channel->rescind);
392399
if (channel->target_cpu != get_cpu()) {
393400
put_cpu();
394401
smp_call_function_single(channel->target_cpu,
@@ -515,6 +522,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
515522
if (!fnew) {
516523
if (channel->sc_creation_callback != NULL)
517524
channel->sc_creation_callback(newchannel);
525+
newchannel->probe_done = true;
518526
return;
519527
}
520528

@@ -834,7 +842,6 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
834842
{
835843
struct vmbus_channel_rescind_offer *rescind;
836844
struct vmbus_channel *channel;
837-
unsigned long flags;
838845
struct device *dev;
839846

840847
rescind = (struct vmbus_channel_rescind_offer *)hdr;
@@ -873,16 +880,6 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
873880
return;
874881
}
875882

876-
spin_lock_irqsave(&channel->lock, flags);
877-
channel->rescind = true;
878-
spin_unlock_irqrestore(&channel->lock, flags);
879-
880-
/*
881-
* Now that we have posted the rescind state, perform
882-
* rescind related cleanup.
883-
*/
884-
vmbus_rescind_cleanup(channel);
885-
886883
/*
887884
* Now wait for offer handling to complete.
888885
*/
@@ -901,6 +898,7 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
901898
if (channel->device_obj) {
902899
if (channel->chn_rescind_callback) {
903900
channel->chn_rescind_callback(channel);
901+
vmbus_rescind_cleanup(channel);
904902
return;
905903
}
906904
/*
@@ -909,6 +907,7 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
909907
*/
910908
dev = get_device(&channel->device_obj->device);
911909
if (dev) {
910+
vmbus_rescind_cleanup(channel);
912911
vmbus_device_unregister(channel->device_obj);
913912
put_device(dev);
914913
}
@@ -921,16 +920,16 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
921920
* 1. Close all sub-channels first
922921
* 2. Then close the primary channel.
923922
*/
923+
mutex_lock(&vmbus_connection.channel_mutex);
924+
vmbus_rescind_cleanup(channel);
924925
if (channel->state == CHANNEL_OPEN_STATE) {
925926
/*
926927
* The channel is currently not open;
927928
* it is safe for us to cleanup the channel.
928929
*/
929-
mutex_lock(&vmbus_connection.channel_mutex);
930-
hv_process_channel_removal(channel,
931-
channel->offermsg.child_relid);
932-
mutex_unlock(&vmbus_connection.channel_mutex);
930+
hv_process_channel_removal(rescind->child_relid);
933931
}
932+
mutex_unlock(&vmbus_connection.channel_mutex);
934933
}
935934
}
936935

drivers/hv/vmbus_drv.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -768,8 +768,7 @@ static void vmbus_device_release(struct device *device)
768768
struct vmbus_channel *channel = hv_dev->channel;
769769

770770
mutex_lock(&vmbus_connection.channel_mutex);
771-
hv_process_channel_removal(channel,
772-
channel->offermsg.child_relid);
771+
hv_process_channel_removal(channel->offermsg.child_relid);
773772
mutex_unlock(&vmbus_connection.channel_mutex);
774773
kfree(hv_dev);
775774

include/linux/hyperv.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1403,7 +1403,7 @@ extern bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, u8 *buf,
14031403
const int *srv_version, int srv_vercnt,
14041404
int *nego_fw_version, int *nego_srv_version);
14051405

1406-
void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid);
1406+
void hv_process_channel_removal(u32 relid);
14071407

14081408
void vmbus_setevent(struct vmbus_channel *channel);
14091409
/*

0 commit comments

Comments
 (0)