Skip to content

Commit 54a6626

Browse files
kattisrinivasangregkh
authored andcommitted
Drivers: hv: vmbus: Fix rescind handling
Fix the rescind handling. This patch addresses the following rescind scenario that is currently not handled correctly: If a rescind were to be received while the offer is still being peocessed, we will be blocked indefinitely since the rescind message is handled on the same work element as the offer message. Fix this issue. I would like to thank Dexuan Cui <[email protected]> and Long Li <[email protected]> for working with me on this patch. Signed-off-by: K. Y. Srinivasan <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 1e052a1 commit 54a6626

File tree

5 files changed

+99
-21
lines changed

5 files changed

+99
-21
lines changed

drivers/hv/channel.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -630,9 +630,13 @@ void vmbus_close(struct vmbus_channel *channel)
630630
*/
631631
list_for_each_safe(cur, tmp, &channel->sc_list) {
632632
cur_channel = list_entry(cur, struct vmbus_channel, sc_list);
633-
if (cur_channel->state != CHANNEL_OPENED_STATE)
634-
continue;
635633
vmbus_close_internal(cur_channel);
634+
if (cur_channel->rescind) {
635+
mutex_lock(&vmbus_connection.channel_mutex);
636+
hv_process_channel_removal(cur_channel,
637+
cur_channel->offermsg.child_relid);
638+
mutex_unlock(&vmbus_connection.channel_mutex);
639+
}
636640
}
637641
/*
638642
* Now close the primary.

drivers/hv/channel_mgmt.c

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -428,15 +428,13 @@ void vmbus_free_channels(void)
428428
{
429429
struct vmbus_channel *channel, *tmp;
430430

431-
mutex_lock(&vmbus_connection.channel_mutex);
432431
list_for_each_entry_safe(channel, tmp, &vmbus_connection.chn_list,
433432
listentry) {
434433
/* hv_process_channel_removal() needs this */
435434
channel->rescind = true;
436435

437436
vmbus_device_unregister(channel->device_obj);
438437
}
439-
mutex_unlock(&vmbus_connection.channel_mutex);
440438
}
441439

442440
/*
@@ -483,8 +481,10 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
483481
list_add_tail(&newchannel->sc_list, &channel->sc_list);
484482
channel->num_sc++;
485483
spin_unlock_irqrestore(&channel->lock, flags);
486-
} else
484+
} else {
485+
atomic_dec(&vmbus_connection.offer_in_progress);
487486
goto err_free_chan;
487+
}
488488
}
489489

490490
dev_type = hv_get_dev_type(newchannel);
@@ -511,6 +511,7 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
511511
if (!fnew) {
512512
if (channel->sc_creation_callback != NULL)
513513
channel->sc_creation_callback(newchannel);
514+
atomic_dec(&vmbus_connection.offer_in_progress);
514515
return;
515516
}
516517

@@ -532,16 +533,16 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
532533
* binding which eventually invokes the device driver's AddDevice()
533534
* method.
534535
*/
535-
mutex_lock(&vmbus_connection.channel_mutex);
536536
ret = vmbus_device_register(newchannel->device_obj);
537-
mutex_unlock(&vmbus_connection.channel_mutex);
538537

539538
if (ret != 0) {
540539
pr_err("unable to add child device object (relid %d)\n",
541540
newchannel->offermsg.child_relid);
542541
kfree(newchannel->device_obj);
543542
goto err_deq_chan;
544543
}
544+
545+
atomic_dec(&vmbus_connection.offer_in_progress);
545546
return;
546547

547548
err_deq_chan:
@@ -797,6 +798,7 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
797798
newchannel = alloc_channel();
798799
if (!newchannel) {
799800
vmbus_release_relid(offer->child_relid);
801+
atomic_dec(&vmbus_connection.offer_in_progress);
800802
pr_err("Unable to allocate channel object\n");
801803
return;
802804
}
@@ -843,16 +845,38 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
843845

844846
rescind = (struct vmbus_channel_rescind_offer *)hdr;
845847

848+
/*
849+
* The offer msg and the corresponding rescind msg
850+
* from the host are guranteed to be ordered -
851+
* offer comes in first and then the rescind.
852+
* Since we process these events in work elements,
853+
* and with preemption, we may end up processing
854+
* the events out of order. Given that we handle these
855+
* work elements on the same CPU, this is possible only
856+
* in the case of preemption. In any case wait here
857+
* until the offer processing has moved beyond the
858+
* point where the channel is discoverable.
859+
*/
860+
861+
while (atomic_read(&vmbus_connection.offer_in_progress) != 0) {
862+
/*
863+
* We wait here until any channel offer is currently
864+
* being processed.
865+
*/
866+
msleep(1);
867+
}
868+
846869
mutex_lock(&vmbus_connection.channel_mutex);
847870
channel = relid2channel(rescind->child_relid);
871+
mutex_unlock(&vmbus_connection.channel_mutex);
848872

849873
if (channel == NULL) {
850874
/*
851-
* This is very impossible, because in
852-
* vmbus_process_offer(), we have already invoked
853-
* vmbus_release_relid() on error.
875+
* We failed in processing the offer message;
876+
* we would have cleaned up the relid in that
877+
* failure path.
854878
*/
855-
goto out;
879+
return;
856880
}
857881

858882
spin_lock_irqsave(&channel->lock, flags);
@@ -864,7 +888,7 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
864888
if (channel->device_obj) {
865889
if (channel->chn_rescind_callback) {
866890
channel->chn_rescind_callback(channel);
867-
goto out;
891+
return;
868892
}
869893
/*
870894
* We will have to unregister this device from the
@@ -875,13 +899,26 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
875899
vmbus_device_unregister(channel->device_obj);
876900
put_device(dev);
877901
}
878-
} else {
879-
hv_process_channel_removal(channel,
880-
channel->offermsg.child_relid);
881902
}
882-
883-
out:
884-
mutex_unlock(&vmbus_connection.channel_mutex);
903+
if (channel->primary_channel != NULL) {
904+
/*
905+
* Sub-channel is being rescinded. Following is the channel
906+
* close sequence when initiated from the driveri (refer to
907+
* vmbus_close() for details):
908+
* 1. Close all sub-channels first
909+
* 2. Then close the primary channel.
910+
*/
911+
if (channel->state == CHANNEL_OPEN_STATE) {
912+
/*
913+
* The channel is currently not open;
914+
* it is safe for us to cleanup the channel.
915+
*/
916+
mutex_lock(&vmbus_connection.channel_mutex);
917+
hv_process_channel_removal(channel,
918+
channel->offermsg.child_relid);
919+
mutex_unlock(&vmbus_connection.channel_mutex);
920+
}
921+
}
885922
}
886923

887924
void vmbus_hvsock_device_unregister(struct vmbus_channel *channel)

drivers/hv/connection.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,13 @@ static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo,
9393
* all the CPUs. This is needed for kexec to work correctly where
9494
* the CPU attempting to connect may not be CPU 0.
9595
*/
96-
if (version >= VERSION_WIN8_1)
96+
if (version >= VERSION_WIN8_1) {
9797
msg->target_vcpu = hv_context.vp_index[smp_processor_id()];
98-
else
98+
vmbus_connection.connect_cpu = smp_processor_id();
99+
} else {
99100
msg->target_vcpu = 0;
101+
vmbus_connection.connect_cpu = 0;
102+
}
100103

101104
/*
102105
* Add to list before we send the request since we may

drivers/hv/hyperv_vmbus.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,13 @@ enum vmbus_connect_state {
303303
#define MAX_SIZE_CHANNEL_MESSAGE HV_MESSAGE_PAYLOAD_BYTE_COUNT
304304

305305
struct vmbus_connection {
306+
/*
307+
* CPU on which the initial host contact was made.
308+
*/
309+
int connect_cpu;
310+
311+
atomic_t offer_in_progress;
312+
306313
enum vmbus_connect_state conn_state;
307314

308315
atomic_t next_gpadl_handle;

drivers/hv/vmbus_drv.c

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -798,8 +798,10 @@ static void vmbus_device_release(struct device *device)
798798
struct hv_device *hv_dev = device_to_hv_device(device);
799799
struct vmbus_channel *channel = hv_dev->channel;
800800

801+
mutex_lock(&vmbus_connection.channel_mutex);
801802
hv_process_channel_removal(channel,
802803
channel->offermsg.child_relid);
804+
mutex_unlock(&vmbus_connection.channel_mutex);
803805
kfree(hv_dev);
804806

805807
}
@@ -877,7 +879,32 @@ void vmbus_on_msg_dpc(unsigned long data)
877879
INIT_WORK(&ctx->work, vmbus_onmessage_work);
878880
memcpy(&ctx->msg, msg, sizeof(*msg));
879881

880-
queue_work(vmbus_connection.work_queue, &ctx->work);
882+
/*
883+
* The host can generate a rescind message while we
884+
* may still be handling the original offer. We deal with
885+
* this condition by ensuring the processing is done on the
886+
* same CPU.
887+
*/
888+
switch (hdr->msgtype) {
889+
case CHANNELMSG_RESCIND_CHANNELOFFER:
890+
/*
891+
* If we are handling the rescind message;
892+
* schedule the work on the global work queue.
893+
*/
894+
schedule_work_on(vmbus_connection.connect_cpu,
895+
&ctx->work);
896+
break;
897+
898+
case CHANNELMSG_OFFERCHANNEL:
899+
atomic_inc(&vmbus_connection.offer_in_progress);
900+
queue_work_on(vmbus_connection.connect_cpu,
901+
vmbus_connection.work_queue,
902+
&ctx->work);
903+
break;
904+
905+
default:
906+
queue_work(vmbus_connection.work_queue, &ctx->work);
907+
}
881908
} else
882909
entry->message_handler(hdr);
883910

0 commit comments

Comments
 (0)