summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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;