summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDexuan Cui <decui@microsoft.com>2019-09-05 19:01:22 -0400
committerSasha Levin <sashal@kernel.org>2019-09-06 14:52:44 -0400
commitd8bd2d442bb2688b428ac7164e5dc6d95d4fa65b (patch)
tree5779d9e76257a90c457b183963a61117a05c033a
parentb307b38962eb0f22d1aa6dcf53cb7d3c2ed5eec7 (diff)
Drivers: hv: vmbus: Resume after fixing up old primary channels
When the host re-offers the primary channels upon resume, the host only guarantees the Instance GUID doesn't change, so vmbus_bus_suspend() should invalidate channel->offermsg.child_relid and figure out the number of primary channels that need to be fixed up upon resume. Upon resume, vmbus_onoffer() finds the old channel structs, and maps the new offers to the old channels, and fixes up the old structs, and finally the resume callbacks of the VSC drivers will re-open the channels. Signed-off-by: Dexuan Cui <decui@microsoft.com> Reviewed-by: Michael Kelley <mikelley@microsoft.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
-rw-r--r--drivers/hv/channel_mgmt.c85
-rw-r--r--drivers/hv/connection.c2
-rw-r--r--drivers/hv/hyperv_vmbus.h14
-rw-r--r--drivers/hv/vmbus_drv.c17
-rw-r--r--include/linux/hyperv.h3
5 files changed, 101 insertions, 20 deletions
diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c
index 5518d031f62a..8eb167540b4f 100644
--- a/drivers/hv/channel_mgmt.c
+++ b/drivers/hv/channel_mgmt.c
@@ -407,7 +407,15 @@ void hv_process_channel_removal(struct vmbus_channel *channel)
407 cpumask_clear_cpu(channel->target_cpu, 407 cpumask_clear_cpu(channel->target_cpu,
408 &primary_channel->alloced_cpus_in_node); 408 &primary_channel->alloced_cpus_in_node);
409 409
410 vmbus_release_relid(channel->offermsg.child_relid); 410 /*
411 * Upon suspend, an in-use hv_sock channel is marked as "rescinded" and
412 * the relid is invalidated; after hibernation, when the user-space app
413 * destroys the channel, the relid is INVALID_RELID, and in this case
414 * it's unnecessary and unsafe to release the old relid, since the same
415 * relid can refer to a completely different channel now.
416 */
417 if (channel->offermsg.child_relid != INVALID_RELID)
418 vmbus_release_relid(channel->offermsg.child_relid);
411 419
412 free_channel(channel); 420 free_channel(channel);
413} 421}
@@ -851,6 +859,36 @@ void vmbus_initiate_unload(bool crash)
851 vmbus_wait_for_unload(); 859 vmbus_wait_for_unload();
852} 860}
853 861
862static void check_ready_for_resume_event(void)
863{
864 /*
865 * If all the old primary channels have been fixed up, then it's safe
866 * to resume.
867 */
868 if (atomic_dec_and_test(&vmbus_connection.nr_chan_fixup_on_resume))
869 complete(&vmbus_connection.ready_for_resume_event);
870}
871
872static void vmbus_setup_channel_state(struct vmbus_channel *channel,
873 struct vmbus_channel_offer_channel *offer)
874{
875 /*
876 * Setup state for signalling the host.
877 */
878 channel->sig_event = VMBUS_EVENT_CONNECTION_ID;
879
880 if (vmbus_proto_version != VERSION_WS2008) {
881 channel->is_dedicated_interrupt =
882 (offer->is_dedicated_interrupt != 0);
883 channel->sig_event = offer->connection_id;
884 }
885
886 memcpy(&channel->offermsg, offer,
887 sizeof(struct vmbus_channel_offer_channel));
888 channel->monitor_grp = (u8)offer->monitorid / 32;
889 channel->monitor_bit = (u8)offer->monitorid % 32;
890}
891
854/* 892/*
855 * find_primary_channel_by_offer - Get the channel object given the new offer. 893 * find_primary_channel_by_offer - Get the channel object given the new offer.
856 * This is only used in the resume path of hibernation. 894 * This is only used in the resume path of hibernation.
@@ -902,14 +940,29 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
902 atomic_dec(&vmbus_connection.offer_in_progress); 940 atomic_dec(&vmbus_connection.offer_in_progress);
903 941
904 /* 942 /*
905 * We're resuming from hibernation: we expect the host to send 943 * We're resuming from hibernation: all the sub-channel and
906 * exactly the same offers that we had before the hibernation. 944 * hv_sock channels we had before the hibernation should have
945 * been cleaned up, and now we must be seeing a re-offered
946 * primary channel that we had before the hibernation.
907 */ 947 */
948
949 WARN_ON(oldchannel->offermsg.child_relid != INVALID_RELID);
950 /* Fix up the relid. */
951 oldchannel->offermsg.child_relid = offer->child_relid;
952
908 offer_sz = sizeof(*offer); 953 offer_sz = sizeof(*offer);
909 if (memcmp(offer, &oldchannel->offermsg, offer_sz) == 0) 954 if (memcmp(offer, &oldchannel->offermsg, offer_sz) == 0) {
955 check_ready_for_resume_event();
910 return; 956 return;
957 }
911 958
912 pr_debug("Mismatched offer from the host (relid=%d)\n", 959 /*
960 * This is not an error, since the host can also change the
961 * other field(s) of the offer, e.g. on WS RS5 (Build 17763),
962 * the offer->connection_id of the Mellanox VF vmbus device
963 * can change when the host reoffers the device upon resume.
964 */
965 pr_debug("vmbus offer changed: relid=%d\n",
913 offer->child_relid); 966 offer->child_relid);
914 967
915 print_hex_dump_debug("Old vmbus offer: ", DUMP_PREFIX_OFFSET, 968 print_hex_dump_debug("Old vmbus offer: ", DUMP_PREFIX_OFFSET,
@@ -917,6 +970,12 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
917 false); 970 false);
918 print_hex_dump_debug("New vmbus offer: ", DUMP_PREFIX_OFFSET, 971 print_hex_dump_debug("New vmbus offer: ", DUMP_PREFIX_OFFSET,
919 16, 4, offer, offer_sz, false); 972 16, 4, offer, offer_sz, false);
973
974 /* Fix up the old channel. */
975 vmbus_setup_channel_state(oldchannel, offer);
976
977 check_ready_for_resume_event();
978
920 return; 979 return;
921 } 980 }
922 981
@@ -929,21 +988,7 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
929 return; 988 return;
930 } 989 }
931 990
932 /* 991 vmbus_setup_channel_state(newchannel, offer);
933 * Setup state for signalling the host.
934 */
935 newchannel->sig_event = VMBUS_EVENT_CONNECTION_ID;
936
937 if (vmbus_proto_version != VERSION_WS2008) {
938 newchannel->is_dedicated_interrupt =
939 (offer->is_dedicated_interrupt != 0);
940 newchannel->sig_event = offer->connection_id;
941 }
942
943 memcpy(&newchannel->offermsg, offer,
944 sizeof(struct vmbus_channel_offer_channel));
945 newchannel->monitor_grp = (u8)offer->monitorid / 32;
946 newchannel->monitor_bit = (u8)offer->monitorid % 32;
947 992
948 vmbus_process_offer(newchannel); 993 vmbus_process_offer(newchannel);
949} 994}
diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
index 99851ea682eb..6e4c015783ff 100644
--- a/drivers/hv/connection.c
+++ b/drivers/hv/connection.c
@@ -29,6 +29,8 @@ struct vmbus_connection vmbus_connection = {
29 29
30 .ready_for_suspend_event= COMPLETION_INITIALIZER( 30 .ready_for_suspend_event= COMPLETION_INITIALIZER(
31 vmbus_connection.ready_for_suspend_event), 31 vmbus_connection.ready_for_suspend_event),
32 .ready_for_resume_event = COMPLETION_INITIALIZER(
33 vmbus_connection.ready_for_resume_event),
32}; 34};
33EXPORT_SYMBOL_GPL(vmbus_connection); 35EXPORT_SYMBOL_GPL(vmbus_connection);
34 36
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index 974b747ca1fc..f7a5f5615f34 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -272,6 +272,20 @@ struct vmbus_connection {
272 * drop to zero. 272 * drop to zero.
273 */ 273 */
274 struct completion ready_for_suspend_event; 274 struct completion ready_for_suspend_event;
275
276 /*
277 * The number of primary channels that should be "fixed up"
278 * upon resume: these channels are re-offered upon resume, and some
279 * fields of the channel offers (i.e. child_relid and connection_id)
280 * can change, so the old offermsg must be fixed up, before the resume
281 * callbacks of the VSC drivers start to further touch the channels.
282 */
283 atomic_t nr_chan_fixup_on_resume;
284 /*
285 * vmbus_bus_resume() waits for "nr_chan_fixup_on_resume" to
286 * drop to zero.
287 */
288 struct completion ready_for_resume_event;
275}; 289};
276 290
277 291
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index 32ec951d334f..391f0b225c9a 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -2164,9 +2164,17 @@ static int vmbus_bus_suspend(struct device *dev)
2164 if (atomic_read(&vmbus_connection.nr_chan_close_on_suspend) > 0) 2164 if (atomic_read(&vmbus_connection.nr_chan_close_on_suspend) > 0)
2165 wait_for_completion(&vmbus_connection.ready_for_suspend_event); 2165 wait_for_completion(&vmbus_connection.ready_for_suspend_event);
2166 2166
2167 WARN_ON(atomic_read(&vmbus_connection.nr_chan_fixup_on_resume) != 0);
2168
2167 mutex_lock(&vmbus_connection.channel_mutex); 2169 mutex_lock(&vmbus_connection.channel_mutex);
2168 2170
2169 list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { 2171 list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
2172 /*
2173 * Invalidate the field. Upon resume, vmbus_onoffer() will fix
2174 * up the field, and the other fields (if necessary).
2175 */
2176 channel->offermsg.child_relid = INVALID_RELID;
2177
2170 if (is_hvsock_channel(channel)) { 2178 if (is_hvsock_channel(channel)) {
2171 if (!channel->rescind) { 2179 if (!channel->rescind) {
2172 pr_err("hv_sock channel not rescinded!\n"); 2180 pr_err("hv_sock channel not rescinded!\n");
@@ -2181,6 +2189,8 @@ static int vmbus_bus_suspend(struct device *dev)
2181 WARN_ON_ONCE(1); 2189 WARN_ON_ONCE(1);
2182 } 2190 }
2183 spin_unlock_irqrestore(&channel->lock, flags); 2191 spin_unlock_irqrestore(&channel->lock, flags);
2192
2193 atomic_inc(&vmbus_connection.nr_chan_fixup_on_resume);
2184 } 2194 }
2185 2195
2186 mutex_unlock(&vmbus_connection.channel_mutex); 2196 mutex_unlock(&vmbus_connection.channel_mutex);
@@ -2189,6 +2199,9 @@ static int vmbus_bus_suspend(struct device *dev)
2189 2199
2190 vmbus_connection.conn_state = DISCONNECTED; 2200 vmbus_connection.conn_state = DISCONNECTED;
2191 2201
2202 /* Reset the event for the next resume. */
2203 reinit_completion(&vmbus_connection.ready_for_resume_event);
2204
2192 return 0; 2205 return 0;
2193} 2206}
2194 2207
@@ -2223,8 +2236,12 @@ static int vmbus_bus_resume(struct device *dev)
2223 if (ret != 0) 2236 if (ret != 0)
2224 return ret; 2237 return ret;
2225 2238
2239 WARN_ON(atomic_read(&vmbus_connection.nr_chan_fixup_on_resume) == 0);
2240
2226 vmbus_request_offers(); 2241 vmbus_request_offers();
2227 2242
2243 wait_for_completion(&vmbus_connection.ready_for_resume_event);
2244
2228 /* Reset the event for the next suspend. */ 2245 /* Reset the event for the next suspend. */
2229 reinit_completion(&vmbus_connection.ready_for_suspend_event); 2246 reinit_completion(&vmbus_connection.ready_for_suspend_event);
2230 2247
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index 8a60e7766037..a3aa9e9ef6f2 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -426,6 +426,9 @@ enum vmbus_channel_message_type {
426 CHANNELMSG_COUNT 426 CHANNELMSG_COUNT
427}; 427};
428 428
429/* Hyper-V supports about 2048 channels, and the RELIDs start with 1. */
430#define INVALID_RELID U32_MAX
431
429struct vmbus_channel_message_header { 432struct vmbus_channel_message_header {
430 enum vmbus_channel_message_type msgtype; 433 enum vmbus_channel_message_type msgtype;
431 u32 padding; 434 u32 padding;