| @@ -407,7 +407,15 @@ void hv_process_channel_removal(struct vmbus_channel *channel) · 406 unmodified lines |
| 407 | 407 | cpumask_clear_cpu(channel->target_cpu, |
| 408 | 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 | 420 | free_channel(channel); |
| 413 | 421 | } |
| @@ -851,6 +859,36 @@ void vmbus_initiate_unload(bool crash) · 437 unmodified lines |
| 851 | 859 | vmbus_wait_for_unload(); |
| 852 | 860 | } |
| 853 | 861 | |
| 862 | +static 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 | + |
| 872 | +static 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 | 893 | * find_primary_channel_by_offer - Get the channel object given the new offer. |
| 856 | 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) · 45 unmodified lines |
| 902 | 940 | atomic_dec(&vmbus_connection.offer_in_progress); |
| 903 | 941 | |
| 904 | 942 | /* |
| 905 | | − * We're resuming from hibernation: we expect the host to send |
| 906 | | − * exactly the same offers that we had before the hibernation. |
| 943 | + * We're resuming from hibernation: all the sub-channel and |
| 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 | 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 | 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 | 966 | offer->child_relid); |
| 914 | 967 | |
| 915 | 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) · 1 unmodified line |
| 917 | 970 | false); |
| 918 | 971 | print_hex_dump_debug("New vmbus offer: ", DUMP_PREFIX_OFFSET, |
| 919 | 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 | 979 | return; |
| 921 | 980 | } |
| 922 | 981 | |
| @@ -929,21 +988,7 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr) · 6 unmodified lines |
| 929 | 988 | return; |
| 930 | 989 | } |
| 931 | 990 | |
| 932 | | − /* |
| 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; |
| 991 | + vmbus_setup_channel_state(newchannel, offer); |
| 947 | 992 | |
| 948 | 993 | vmbus_process_offer(newchannel); |
| 949 | 994 | } |