18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2009, Microsoft Corporation. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Authors: 68c2ecf20Sopenharmony_ci * Haiyang Zhang <haiyangz@microsoft.com> 78c2ecf20Sopenharmony_ci * Hank Janssen <hjanssen@microsoft.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/sched.h> 148c2ecf20Sopenharmony_ci#include <linux/wait.h> 158c2ecf20Sopenharmony_ci#include <linux/mm.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/list.h> 188c2ecf20Sopenharmony_ci#include <linux/module.h> 198c2ecf20Sopenharmony_ci#include <linux/completion.h> 208c2ecf20Sopenharmony_ci#include <linux/delay.h> 218c2ecf20Sopenharmony_ci#include <linux/cpu.h> 228c2ecf20Sopenharmony_ci#include <linux/hyperv.h> 238c2ecf20Sopenharmony_ci#include <asm/mshyperv.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "hyperv_vmbus.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic void init_vp_index(struct vmbus_channel *channel); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ciconst struct vmbus_device vmbus_devs[] = { 308c2ecf20Sopenharmony_ci /* IDE */ 318c2ecf20Sopenharmony_ci { .dev_type = HV_IDE, 328c2ecf20Sopenharmony_ci HV_IDE_GUID, 338c2ecf20Sopenharmony_ci .perf_device = true, 348c2ecf20Sopenharmony_ci }, 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci /* SCSI */ 378c2ecf20Sopenharmony_ci { .dev_type = HV_SCSI, 388c2ecf20Sopenharmony_ci HV_SCSI_GUID, 398c2ecf20Sopenharmony_ci .perf_device = true, 408c2ecf20Sopenharmony_ci }, 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci /* Fibre Channel */ 438c2ecf20Sopenharmony_ci { .dev_type = HV_FC, 448c2ecf20Sopenharmony_ci HV_SYNTHFC_GUID, 458c2ecf20Sopenharmony_ci .perf_device = true, 468c2ecf20Sopenharmony_ci }, 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci /* Synthetic NIC */ 498c2ecf20Sopenharmony_ci { .dev_type = HV_NIC, 508c2ecf20Sopenharmony_ci HV_NIC_GUID, 518c2ecf20Sopenharmony_ci .perf_device = true, 528c2ecf20Sopenharmony_ci }, 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* Network Direct */ 558c2ecf20Sopenharmony_ci { .dev_type = HV_ND, 568c2ecf20Sopenharmony_ci HV_ND_GUID, 578c2ecf20Sopenharmony_ci .perf_device = true, 588c2ecf20Sopenharmony_ci }, 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci /* PCIE */ 618c2ecf20Sopenharmony_ci { .dev_type = HV_PCIE, 628c2ecf20Sopenharmony_ci HV_PCIE_GUID, 638c2ecf20Sopenharmony_ci .perf_device = false, 648c2ecf20Sopenharmony_ci }, 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* Synthetic Frame Buffer */ 678c2ecf20Sopenharmony_ci { .dev_type = HV_FB, 688c2ecf20Sopenharmony_ci HV_SYNTHVID_GUID, 698c2ecf20Sopenharmony_ci .perf_device = false, 708c2ecf20Sopenharmony_ci }, 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* Synthetic Keyboard */ 738c2ecf20Sopenharmony_ci { .dev_type = HV_KBD, 748c2ecf20Sopenharmony_ci HV_KBD_GUID, 758c2ecf20Sopenharmony_ci .perf_device = false, 768c2ecf20Sopenharmony_ci }, 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* Synthetic MOUSE */ 798c2ecf20Sopenharmony_ci { .dev_type = HV_MOUSE, 808c2ecf20Sopenharmony_ci HV_MOUSE_GUID, 818c2ecf20Sopenharmony_ci .perf_device = false, 828c2ecf20Sopenharmony_ci }, 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* KVP */ 858c2ecf20Sopenharmony_ci { .dev_type = HV_KVP, 868c2ecf20Sopenharmony_ci HV_KVP_GUID, 878c2ecf20Sopenharmony_ci .perf_device = false, 888c2ecf20Sopenharmony_ci }, 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* Time Synch */ 918c2ecf20Sopenharmony_ci { .dev_type = HV_TS, 928c2ecf20Sopenharmony_ci HV_TS_GUID, 938c2ecf20Sopenharmony_ci .perf_device = false, 948c2ecf20Sopenharmony_ci }, 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* Heartbeat */ 978c2ecf20Sopenharmony_ci { .dev_type = HV_HB, 988c2ecf20Sopenharmony_ci HV_HEART_BEAT_GUID, 998c2ecf20Sopenharmony_ci .perf_device = false, 1008c2ecf20Sopenharmony_ci }, 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* Shutdown */ 1038c2ecf20Sopenharmony_ci { .dev_type = HV_SHUTDOWN, 1048c2ecf20Sopenharmony_ci HV_SHUTDOWN_GUID, 1058c2ecf20Sopenharmony_ci .perf_device = false, 1068c2ecf20Sopenharmony_ci }, 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* File copy */ 1098c2ecf20Sopenharmony_ci { .dev_type = HV_FCOPY, 1108c2ecf20Sopenharmony_ci HV_FCOPY_GUID, 1118c2ecf20Sopenharmony_ci .perf_device = false, 1128c2ecf20Sopenharmony_ci }, 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* Backup */ 1158c2ecf20Sopenharmony_ci { .dev_type = HV_BACKUP, 1168c2ecf20Sopenharmony_ci HV_VSS_GUID, 1178c2ecf20Sopenharmony_ci .perf_device = false, 1188c2ecf20Sopenharmony_ci }, 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* Dynamic Memory */ 1218c2ecf20Sopenharmony_ci { .dev_type = HV_DM, 1228c2ecf20Sopenharmony_ci HV_DM_GUID, 1238c2ecf20Sopenharmony_ci .perf_device = false, 1248c2ecf20Sopenharmony_ci }, 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* Unknown GUID */ 1278c2ecf20Sopenharmony_ci { .dev_type = HV_UNKNOWN, 1288c2ecf20Sopenharmony_ci .perf_device = false, 1298c2ecf20Sopenharmony_ci }, 1308c2ecf20Sopenharmony_ci}; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic const struct { 1338c2ecf20Sopenharmony_ci guid_t guid; 1348c2ecf20Sopenharmony_ci} vmbus_unsupported_devs[] = { 1358c2ecf20Sopenharmony_ci { HV_AVMA1_GUID }, 1368c2ecf20Sopenharmony_ci { HV_AVMA2_GUID }, 1378c2ecf20Sopenharmony_ci { HV_RDV_GUID }, 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci/* 1418c2ecf20Sopenharmony_ci * The rescinded channel may be blocked waiting for a response from the host; 1428c2ecf20Sopenharmony_ci * take care of that. 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_cistatic void vmbus_rescind_cleanup(struct vmbus_channel *channel) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci struct vmbus_channel_msginfo *msginfo; 1478c2ecf20Sopenharmony_ci unsigned long flags; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); 1518c2ecf20Sopenharmony_ci channel->rescind = true; 1528c2ecf20Sopenharmony_ci list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, 1538c2ecf20Sopenharmony_ci msglistentry) { 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (msginfo->waiting_channel == channel) { 1568c2ecf20Sopenharmony_ci complete(&msginfo->waitevent); 1578c2ecf20Sopenharmony_ci break; 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic bool is_unsupported_vmbus_devs(const guid_t *guid) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci int i; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vmbus_unsupported_devs); i++) 1688c2ecf20Sopenharmony_ci if (guid_equal(guid, &vmbus_unsupported_devs[i].guid)) 1698c2ecf20Sopenharmony_ci return true; 1708c2ecf20Sopenharmony_ci return false; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic u16 hv_get_dev_type(const struct vmbus_channel *channel) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci const guid_t *guid = &channel->offermsg.offer.if_type; 1768c2ecf20Sopenharmony_ci u16 i; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (is_hvsock_channel(channel) || is_unsupported_vmbus_devs(guid)) 1798c2ecf20Sopenharmony_ci return HV_UNKNOWN; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci for (i = HV_IDE; i < HV_UNKNOWN; i++) { 1828c2ecf20Sopenharmony_ci if (guid_equal(guid, &vmbus_devs[i].guid)) 1838c2ecf20Sopenharmony_ci return i; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci pr_info("Unknown GUID: %pUl\n", guid); 1868c2ecf20Sopenharmony_ci return i; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/** 1908c2ecf20Sopenharmony_ci * vmbus_prep_negotiate_resp() - Create default response for Negotiate message 1918c2ecf20Sopenharmony_ci * @icmsghdrp: Pointer to msg header structure 1928c2ecf20Sopenharmony_ci * @buf: Raw buffer channel data 1938c2ecf20Sopenharmony_ci * @fw_version: The framework versions we can support. 1948c2ecf20Sopenharmony_ci * @fw_vercnt: The size of @fw_version. 1958c2ecf20Sopenharmony_ci * @srv_version: The service versions we can support. 1968c2ecf20Sopenharmony_ci * @srv_vercnt: The size of @srv_version. 1978c2ecf20Sopenharmony_ci * @nego_fw_version: The selected framework version. 1988c2ecf20Sopenharmony_ci * @nego_srv_version: The selected service version. 1998c2ecf20Sopenharmony_ci * 2008c2ecf20Sopenharmony_ci * Note: Versions are given in decreasing order. 2018c2ecf20Sopenharmony_ci * 2028c2ecf20Sopenharmony_ci * Set up and fill in default negotiate response message. 2038c2ecf20Sopenharmony_ci * Mainly used by Hyper-V drivers. 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_cibool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, 2068c2ecf20Sopenharmony_ci u8 *buf, const int *fw_version, int fw_vercnt, 2078c2ecf20Sopenharmony_ci const int *srv_version, int srv_vercnt, 2088c2ecf20Sopenharmony_ci int *nego_fw_version, int *nego_srv_version) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci int icframe_major, icframe_minor; 2118c2ecf20Sopenharmony_ci int icmsg_major, icmsg_minor; 2128c2ecf20Sopenharmony_ci int fw_major, fw_minor; 2138c2ecf20Sopenharmony_ci int srv_major, srv_minor; 2148c2ecf20Sopenharmony_ci int i, j; 2158c2ecf20Sopenharmony_ci bool found_match = false; 2168c2ecf20Sopenharmony_ci struct icmsg_negotiate *negop; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci icmsghdrp->icmsgsize = 0x10; 2198c2ecf20Sopenharmony_ci negop = (struct icmsg_negotiate *)&buf[ 2208c2ecf20Sopenharmony_ci sizeof(struct vmbuspipe_hdr) + 2218c2ecf20Sopenharmony_ci sizeof(struct icmsg_hdr)]; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci icframe_major = negop->icframe_vercnt; 2248c2ecf20Sopenharmony_ci icframe_minor = 0; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci icmsg_major = negop->icmsg_vercnt; 2278c2ecf20Sopenharmony_ci icmsg_minor = 0; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* 2308c2ecf20Sopenharmony_ci * Select the framework version number we will 2318c2ecf20Sopenharmony_ci * support. 2328c2ecf20Sopenharmony_ci */ 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci for (i = 0; i < fw_vercnt; i++) { 2358c2ecf20Sopenharmony_ci fw_major = (fw_version[i] >> 16); 2368c2ecf20Sopenharmony_ci fw_minor = (fw_version[i] & 0xFFFF); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci for (j = 0; j < negop->icframe_vercnt; j++) { 2398c2ecf20Sopenharmony_ci if ((negop->icversion_data[j].major == fw_major) && 2408c2ecf20Sopenharmony_ci (negop->icversion_data[j].minor == fw_minor)) { 2418c2ecf20Sopenharmony_ci icframe_major = negop->icversion_data[j].major; 2428c2ecf20Sopenharmony_ci icframe_minor = negop->icversion_data[j].minor; 2438c2ecf20Sopenharmony_ci found_match = true; 2448c2ecf20Sopenharmony_ci break; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (found_match) 2498c2ecf20Sopenharmony_ci break; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (!found_match) 2538c2ecf20Sopenharmony_ci goto fw_error; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci found_match = false; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci for (i = 0; i < srv_vercnt; i++) { 2588c2ecf20Sopenharmony_ci srv_major = (srv_version[i] >> 16); 2598c2ecf20Sopenharmony_ci srv_minor = (srv_version[i] & 0xFFFF); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci for (j = negop->icframe_vercnt; 2628c2ecf20Sopenharmony_ci (j < negop->icframe_vercnt + negop->icmsg_vercnt); 2638c2ecf20Sopenharmony_ci j++) { 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if ((negop->icversion_data[j].major == srv_major) && 2668c2ecf20Sopenharmony_ci (negop->icversion_data[j].minor == srv_minor)) { 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci icmsg_major = negop->icversion_data[j].major; 2698c2ecf20Sopenharmony_ci icmsg_minor = negop->icversion_data[j].minor; 2708c2ecf20Sopenharmony_ci found_match = true; 2718c2ecf20Sopenharmony_ci break; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (found_match) 2768c2ecf20Sopenharmony_ci break; 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* 2808c2ecf20Sopenharmony_ci * Respond with the framework and service 2818c2ecf20Sopenharmony_ci * version numbers we can support. 2828c2ecf20Sopenharmony_ci */ 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cifw_error: 2858c2ecf20Sopenharmony_ci if (!found_match) { 2868c2ecf20Sopenharmony_ci negop->icframe_vercnt = 0; 2878c2ecf20Sopenharmony_ci negop->icmsg_vercnt = 0; 2888c2ecf20Sopenharmony_ci } else { 2898c2ecf20Sopenharmony_ci negop->icframe_vercnt = 1; 2908c2ecf20Sopenharmony_ci negop->icmsg_vercnt = 1; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (nego_fw_version) 2948c2ecf20Sopenharmony_ci *nego_fw_version = (icframe_major << 16) | icframe_minor; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (nego_srv_version) 2978c2ecf20Sopenharmony_ci *nego_srv_version = (icmsg_major << 16) | icmsg_minor; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci negop->icversion_data[0].major = icframe_major; 3008c2ecf20Sopenharmony_ci negop->icversion_data[0].minor = icframe_minor; 3018c2ecf20Sopenharmony_ci negop->icversion_data[1].major = icmsg_major; 3028c2ecf20Sopenharmony_ci negop->icversion_data[1].minor = icmsg_minor; 3038c2ecf20Sopenharmony_ci return found_match; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci/* 3098c2ecf20Sopenharmony_ci * alloc_channel - Allocate and initialize a vmbus channel object 3108c2ecf20Sopenharmony_ci */ 3118c2ecf20Sopenharmony_cistatic struct vmbus_channel *alloc_channel(void) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci struct vmbus_channel *channel; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci channel = kzalloc(sizeof(*channel), GFP_ATOMIC); 3168c2ecf20Sopenharmony_ci if (!channel) 3178c2ecf20Sopenharmony_ci return NULL; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci spin_lock_init(&channel->sched_lock); 3208c2ecf20Sopenharmony_ci init_completion(&channel->rescind_event); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&channel->sc_list); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci tasklet_init(&channel->callback_event, 3258c2ecf20Sopenharmony_ci vmbus_on_event, (unsigned long)channel); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci hv_ringbuffer_pre_init(channel); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci return channel; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci/* 3338c2ecf20Sopenharmony_ci * free_channel - Release the resources used by the vmbus channel object 3348c2ecf20Sopenharmony_ci */ 3358c2ecf20Sopenharmony_cistatic void free_channel(struct vmbus_channel *channel) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci tasklet_kill(&channel->callback_event); 3388c2ecf20Sopenharmony_ci vmbus_remove_channel_attr_group(channel); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci kobject_put(&channel->kobj); 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_civoid vmbus_channel_map_relid(struct vmbus_channel *channel) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci if (WARN_ON(channel->offermsg.child_relid >= MAX_CHANNEL_RELIDS)) 3468c2ecf20Sopenharmony_ci return; 3478c2ecf20Sopenharmony_ci /* 3488c2ecf20Sopenharmony_ci * The mapping of the channel's relid is visible from the CPUs that 3498c2ecf20Sopenharmony_ci * execute vmbus_chan_sched() by the time that vmbus_chan_sched() will 3508c2ecf20Sopenharmony_ci * execute: 3518c2ecf20Sopenharmony_ci * 3528c2ecf20Sopenharmony_ci * (a) In the "normal (i.e., not resuming from hibernation)" path, 3538c2ecf20Sopenharmony_ci * the full barrier in virt_store_mb() guarantees that the store 3548c2ecf20Sopenharmony_ci * is propagated to all CPUs before the add_channel_work work 3558c2ecf20Sopenharmony_ci * is queued. In turn, add_channel_work is queued before the 3568c2ecf20Sopenharmony_ci * channel's ring buffer is allocated/initialized and the 3578c2ecf20Sopenharmony_ci * OPENCHANNEL message for the channel is sent in vmbus_open(). 3588c2ecf20Sopenharmony_ci * Hyper-V won't start sending the interrupts for the channel 3598c2ecf20Sopenharmony_ci * before the OPENCHANNEL message is acked. The memory barrier 3608c2ecf20Sopenharmony_ci * in vmbus_chan_sched() -> sync_test_and_clear_bit() ensures 3618c2ecf20Sopenharmony_ci * that vmbus_chan_sched() must find the channel's relid in 3628c2ecf20Sopenharmony_ci * recv_int_page before retrieving the channel pointer from the 3638c2ecf20Sopenharmony_ci * array of channels. 3648c2ecf20Sopenharmony_ci * 3658c2ecf20Sopenharmony_ci * (b) In the "resuming from hibernation" path, the virt_store_mb() 3668c2ecf20Sopenharmony_ci * guarantees that the store is propagated to all CPUs before 3678c2ecf20Sopenharmony_ci * the VMBus connection is marked as ready for the resume event 3688c2ecf20Sopenharmony_ci * (cf. check_ready_for_resume_event()). The interrupt handler 3698c2ecf20Sopenharmony_ci * of the VMBus driver and vmbus_chan_sched() can not run before 3708c2ecf20Sopenharmony_ci * vmbus_bus_resume() has completed execution (cf. resume_noirq). 3718c2ecf20Sopenharmony_ci */ 3728c2ecf20Sopenharmony_ci virt_store_mb( 3738c2ecf20Sopenharmony_ci vmbus_connection.channels[channel->offermsg.child_relid], 3748c2ecf20Sopenharmony_ci channel); 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_civoid vmbus_channel_unmap_relid(struct vmbus_channel *channel) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci if (WARN_ON(channel->offermsg.child_relid >= MAX_CHANNEL_RELIDS)) 3808c2ecf20Sopenharmony_ci return; 3818c2ecf20Sopenharmony_ci WRITE_ONCE( 3828c2ecf20Sopenharmony_ci vmbus_connection.channels[channel->offermsg.child_relid], 3838c2ecf20Sopenharmony_ci NULL); 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic void vmbus_release_relid(u32 relid) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci struct vmbus_channel_relid_released msg; 3898c2ecf20Sopenharmony_ci int ret; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci memset(&msg, 0, sizeof(struct vmbus_channel_relid_released)); 3928c2ecf20Sopenharmony_ci msg.child_relid = relid; 3938c2ecf20Sopenharmony_ci msg.header.msgtype = CHANNELMSG_RELID_RELEASED; 3948c2ecf20Sopenharmony_ci ret = vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released), 3958c2ecf20Sopenharmony_ci true); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci trace_vmbus_release_relid(&msg, ret); 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_civoid hv_process_channel_removal(struct vmbus_channel *channel) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci lockdep_assert_held(&vmbus_connection.channel_mutex); 4038c2ecf20Sopenharmony_ci BUG_ON(!channel->rescind); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci /* 4068c2ecf20Sopenharmony_ci * hv_process_channel_removal() could find INVALID_RELID only for 4078c2ecf20Sopenharmony_ci * hv_sock channels. See the inline comments in vmbus_onoffer(). 4088c2ecf20Sopenharmony_ci */ 4098c2ecf20Sopenharmony_ci WARN_ON(channel->offermsg.child_relid == INVALID_RELID && 4108c2ecf20Sopenharmony_ci !is_hvsock_channel(channel)); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci /* 4138c2ecf20Sopenharmony_ci * Upon suspend, an in-use hv_sock channel is removed from the array of 4148c2ecf20Sopenharmony_ci * channels and the relid is invalidated. After hibernation, when the 4158c2ecf20Sopenharmony_ci * user-space appplication destroys the channel, it's unnecessary and 4168c2ecf20Sopenharmony_ci * unsafe to remove the channel from the array of channels. See also 4178c2ecf20Sopenharmony_ci * the inline comments before the call of vmbus_release_relid() below. 4188c2ecf20Sopenharmony_ci */ 4198c2ecf20Sopenharmony_ci if (channel->offermsg.child_relid != INVALID_RELID) 4208c2ecf20Sopenharmony_ci vmbus_channel_unmap_relid(channel); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci if (channel->primary_channel == NULL) 4238c2ecf20Sopenharmony_ci list_del(&channel->listentry); 4248c2ecf20Sopenharmony_ci else 4258c2ecf20Sopenharmony_ci list_del(&channel->sc_list); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* 4288c2ecf20Sopenharmony_ci * If this is a "perf" channel, updates the hv_numa_map[] masks so that 4298c2ecf20Sopenharmony_ci * init_vp_index() can (re-)use the CPU. 4308c2ecf20Sopenharmony_ci */ 4318c2ecf20Sopenharmony_ci if (hv_is_perf_channel(channel)) 4328c2ecf20Sopenharmony_ci hv_clear_alloced_cpu(channel->target_cpu); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* 4358c2ecf20Sopenharmony_ci * Upon suspend, an in-use hv_sock channel is marked as "rescinded" and 4368c2ecf20Sopenharmony_ci * the relid is invalidated; after hibernation, when the user-space app 4378c2ecf20Sopenharmony_ci * destroys the channel, the relid is INVALID_RELID, and in this case 4388c2ecf20Sopenharmony_ci * it's unnecessary and unsafe to release the old relid, since the same 4398c2ecf20Sopenharmony_ci * relid can refer to a completely different channel now. 4408c2ecf20Sopenharmony_ci */ 4418c2ecf20Sopenharmony_ci if (channel->offermsg.child_relid != INVALID_RELID) 4428c2ecf20Sopenharmony_ci vmbus_release_relid(channel->offermsg.child_relid); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci free_channel(channel); 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_civoid vmbus_free_channels(void) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci struct vmbus_channel *channel, *tmp; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci list_for_each_entry_safe(channel, tmp, &vmbus_connection.chn_list, 4528c2ecf20Sopenharmony_ci listentry) { 4538c2ecf20Sopenharmony_ci /* hv_process_channel_removal() needs this */ 4548c2ecf20Sopenharmony_ci channel->rescind = true; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci vmbus_device_unregister(channel->device_obj); 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci/* Note: the function can run concurrently for primary/sub channels. */ 4618c2ecf20Sopenharmony_cistatic void vmbus_add_channel_work(struct work_struct *work) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci struct vmbus_channel *newchannel = 4648c2ecf20Sopenharmony_ci container_of(work, struct vmbus_channel, add_channel_work); 4658c2ecf20Sopenharmony_ci struct vmbus_channel *primary_channel = newchannel->primary_channel; 4668c2ecf20Sopenharmony_ci int ret; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* 4698c2ecf20Sopenharmony_ci * This state is used to indicate a successful open 4708c2ecf20Sopenharmony_ci * so that when we do close the channel normally, we 4718c2ecf20Sopenharmony_ci * can cleanup properly. 4728c2ecf20Sopenharmony_ci */ 4738c2ecf20Sopenharmony_ci newchannel->state = CHANNEL_OPEN_STATE; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (primary_channel != NULL) { 4768c2ecf20Sopenharmony_ci /* newchannel is a sub-channel. */ 4778c2ecf20Sopenharmony_ci struct hv_device *dev = primary_channel->device_obj; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (vmbus_add_channel_kobj(dev, newchannel)) 4808c2ecf20Sopenharmony_ci goto err_deq_chan; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci if (primary_channel->sc_creation_callback != NULL) 4838c2ecf20Sopenharmony_ci primary_channel->sc_creation_callback(newchannel); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci newchannel->probe_done = true; 4868c2ecf20Sopenharmony_ci return; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* 4908c2ecf20Sopenharmony_ci * Start the process of binding the primary channel to the driver 4918c2ecf20Sopenharmony_ci */ 4928c2ecf20Sopenharmony_ci newchannel->device_obj = vmbus_device_create( 4938c2ecf20Sopenharmony_ci &newchannel->offermsg.offer.if_type, 4948c2ecf20Sopenharmony_ci &newchannel->offermsg.offer.if_instance, 4958c2ecf20Sopenharmony_ci newchannel); 4968c2ecf20Sopenharmony_ci if (!newchannel->device_obj) 4978c2ecf20Sopenharmony_ci goto err_deq_chan; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci newchannel->device_obj->device_id = newchannel->device_id; 5008c2ecf20Sopenharmony_ci /* 5018c2ecf20Sopenharmony_ci * Add the new device to the bus. This will kick off device-driver 5028c2ecf20Sopenharmony_ci * binding which eventually invokes the device driver's AddDevice() 5038c2ecf20Sopenharmony_ci * method. 5048c2ecf20Sopenharmony_ci * 5058c2ecf20Sopenharmony_ci * If vmbus_device_register() fails, the 'device_obj' is freed in 5068c2ecf20Sopenharmony_ci * vmbus_device_release() as called by device_unregister() in the 5078c2ecf20Sopenharmony_ci * error path of vmbus_device_register(). In the outside error 5088c2ecf20Sopenharmony_ci * path, there's no need to free it. 5098c2ecf20Sopenharmony_ci */ 5108c2ecf20Sopenharmony_ci ret = vmbus_device_register(newchannel->device_obj); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci if (ret != 0) { 5138c2ecf20Sopenharmony_ci pr_err("unable to add child device object (relid %d)\n", 5148c2ecf20Sopenharmony_ci newchannel->offermsg.child_relid); 5158c2ecf20Sopenharmony_ci goto err_deq_chan; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci newchannel->probe_done = true; 5198c2ecf20Sopenharmony_ci return; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cierr_deq_chan: 5228c2ecf20Sopenharmony_ci mutex_lock(&vmbus_connection.channel_mutex); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci /* 5258c2ecf20Sopenharmony_ci * We need to set the flag, otherwise 5268c2ecf20Sopenharmony_ci * vmbus_onoffer_rescind() can be blocked. 5278c2ecf20Sopenharmony_ci */ 5288c2ecf20Sopenharmony_ci newchannel->probe_done = true; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (primary_channel == NULL) 5318c2ecf20Sopenharmony_ci list_del(&newchannel->listentry); 5328c2ecf20Sopenharmony_ci else 5338c2ecf20Sopenharmony_ci list_del(&newchannel->sc_list); 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci /* vmbus_process_offer() has mapped the channel. */ 5368c2ecf20Sopenharmony_ci vmbus_channel_unmap_relid(newchannel); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci mutex_unlock(&vmbus_connection.channel_mutex); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci vmbus_release_relid(newchannel->offermsg.child_relid); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci free_channel(newchannel); 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci/* 5468c2ecf20Sopenharmony_ci * vmbus_process_offer - Process the offer by creating a channel/device 5478c2ecf20Sopenharmony_ci * associated with this offer 5488c2ecf20Sopenharmony_ci */ 5498c2ecf20Sopenharmony_cistatic void vmbus_process_offer(struct vmbus_channel *newchannel) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci struct vmbus_channel *channel; 5528c2ecf20Sopenharmony_ci struct workqueue_struct *wq; 5538c2ecf20Sopenharmony_ci bool fnew = true; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* 5568c2ecf20Sopenharmony_ci * Synchronize vmbus_process_offer() and CPU hotplugging: 5578c2ecf20Sopenharmony_ci * 5588c2ecf20Sopenharmony_ci * CPU1 CPU2 5598c2ecf20Sopenharmony_ci * 5608c2ecf20Sopenharmony_ci * [vmbus_process_offer()] [Hot removal of the CPU] 5618c2ecf20Sopenharmony_ci * 5628c2ecf20Sopenharmony_ci * CPU_READ_LOCK CPUS_WRITE_LOCK 5638c2ecf20Sopenharmony_ci * LOAD cpu_online_mask SEARCH chn_list 5648c2ecf20Sopenharmony_ci * STORE target_cpu LOAD target_cpu 5658c2ecf20Sopenharmony_ci * INSERT chn_list STORE cpu_online_mask 5668c2ecf20Sopenharmony_ci * CPUS_READ_UNLOCK CPUS_WRITE_UNLOCK 5678c2ecf20Sopenharmony_ci * 5688c2ecf20Sopenharmony_ci * Forbids: CPU1's LOAD from *not* seing CPU2's STORE && 5698c2ecf20Sopenharmony_ci * CPU2's SEARCH from *not* seeing CPU1's INSERT 5708c2ecf20Sopenharmony_ci * 5718c2ecf20Sopenharmony_ci * Forbids: CPU2's SEARCH from seeing CPU1's INSERT && 5728c2ecf20Sopenharmony_ci * CPU2's LOAD from *not* seing CPU1's STORE 5738c2ecf20Sopenharmony_ci */ 5748c2ecf20Sopenharmony_ci cpus_read_lock(); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci /* 5778c2ecf20Sopenharmony_ci * Serializes the modifications of the chn_list list as well as 5788c2ecf20Sopenharmony_ci * the accesses to next_numa_node_id in init_vp_index(). 5798c2ecf20Sopenharmony_ci */ 5808c2ecf20Sopenharmony_ci mutex_lock(&vmbus_connection.channel_mutex); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci init_vp_index(newchannel); 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* Remember the channels that should be cleaned up upon suspend. */ 5858c2ecf20Sopenharmony_ci if (is_hvsock_channel(newchannel) || is_sub_channel(newchannel)) 5868c2ecf20Sopenharmony_ci atomic_inc(&vmbus_connection.nr_chan_close_on_suspend); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci /* 5898c2ecf20Sopenharmony_ci * Now that we have acquired the channel_mutex, 5908c2ecf20Sopenharmony_ci * we can release the potentially racing rescind thread. 5918c2ecf20Sopenharmony_ci */ 5928c2ecf20Sopenharmony_ci atomic_dec(&vmbus_connection.offer_in_progress); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { 5958c2ecf20Sopenharmony_ci if (guid_equal(&channel->offermsg.offer.if_type, 5968c2ecf20Sopenharmony_ci &newchannel->offermsg.offer.if_type) && 5978c2ecf20Sopenharmony_ci guid_equal(&channel->offermsg.offer.if_instance, 5988c2ecf20Sopenharmony_ci &newchannel->offermsg.offer.if_instance)) { 5998c2ecf20Sopenharmony_ci fnew = false; 6008c2ecf20Sopenharmony_ci break; 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci if (fnew) { 6058c2ecf20Sopenharmony_ci list_add_tail(&newchannel->listentry, 6068c2ecf20Sopenharmony_ci &vmbus_connection.chn_list); 6078c2ecf20Sopenharmony_ci } else { 6088c2ecf20Sopenharmony_ci /* 6098c2ecf20Sopenharmony_ci * Check to see if this is a valid sub-channel. 6108c2ecf20Sopenharmony_ci */ 6118c2ecf20Sopenharmony_ci if (newchannel->offermsg.offer.sub_channel_index == 0) { 6128c2ecf20Sopenharmony_ci mutex_unlock(&vmbus_connection.channel_mutex); 6138c2ecf20Sopenharmony_ci cpus_read_unlock(); 6148c2ecf20Sopenharmony_ci /* 6158c2ecf20Sopenharmony_ci * Don't call free_channel(), because newchannel->kobj 6168c2ecf20Sopenharmony_ci * is not initialized yet. 6178c2ecf20Sopenharmony_ci */ 6188c2ecf20Sopenharmony_ci kfree(newchannel); 6198c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 6208c2ecf20Sopenharmony_ci return; 6218c2ecf20Sopenharmony_ci } 6228c2ecf20Sopenharmony_ci /* 6238c2ecf20Sopenharmony_ci * Process the sub-channel. 6248c2ecf20Sopenharmony_ci */ 6258c2ecf20Sopenharmony_ci newchannel->primary_channel = channel; 6268c2ecf20Sopenharmony_ci list_add_tail(&newchannel->sc_list, &channel->sc_list); 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci vmbus_channel_map_relid(newchannel); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci mutex_unlock(&vmbus_connection.channel_mutex); 6328c2ecf20Sopenharmony_ci cpus_read_unlock(); 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci /* 6358c2ecf20Sopenharmony_ci * vmbus_process_offer() mustn't call channel->sc_creation_callback() 6368c2ecf20Sopenharmony_ci * directly for sub-channels, because sc_creation_callback() -> 6378c2ecf20Sopenharmony_ci * vmbus_open() may never get the host's response to the 6388c2ecf20Sopenharmony_ci * OPEN_CHANNEL message (the host may rescind a channel at any time, 6398c2ecf20Sopenharmony_ci * e.g. in the case of hot removing a NIC), and vmbus_onoffer_rescind() 6408c2ecf20Sopenharmony_ci * may not wake up the vmbus_open() as it's blocked due to a non-zero 6418c2ecf20Sopenharmony_ci * vmbus_connection.offer_in_progress, and finally we have a deadlock. 6428c2ecf20Sopenharmony_ci * 6438c2ecf20Sopenharmony_ci * The above is also true for primary channels, if the related device 6448c2ecf20Sopenharmony_ci * drivers use sync probing mode by default. 6458c2ecf20Sopenharmony_ci * 6468c2ecf20Sopenharmony_ci * And, usually the handling of primary channels and sub-channels can 6478c2ecf20Sopenharmony_ci * depend on each other, so we should offload them to different 6488c2ecf20Sopenharmony_ci * workqueues to avoid possible deadlock, e.g. in sync-probing mode, 6498c2ecf20Sopenharmony_ci * NIC1's netvsc_subchan_work() can race with NIC2's netvsc_probe() -> 6508c2ecf20Sopenharmony_ci * rtnl_lock(), and causes deadlock: the former gets the rtnl_lock 6518c2ecf20Sopenharmony_ci * and waits for all the sub-channels to appear, but the latter 6528c2ecf20Sopenharmony_ci * can't get the rtnl_lock and this blocks the handling of 6538c2ecf20Sopenharmony_ci * sub-channels. 6548c2ecf20Sopenharmony_ci */ 6558c2ecf20Sopenharmony_ci INIT_WORK(&newchannel->add_channel_work, vmbus_add_channel_work); 6568c2ecf20Sopenharmony_ci wq = fnew ? vmbus_connection.handle_primary_chan_wq : 6578c2ecf20Sopenharmony_ci vmbus_connection.handle_sub_chan_wq; 6588c2ecf20Sopenharmony_ci queue_work(wq, &newchannel->add_channel_work); 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci/* 6628c2ecf20Sopenharmony_ci * We use this state to statically distribute the channel interrupt load. 6638c2ecf20Sopenharmony_ci */ 6648c2ecf20Sopenharmony_cistatic int next_numa_node_id; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci/* 6678c2ecf20Sopenharmony_ci * Starting with Win8, we can statically distribute the incoming 6688c2ecf20Sopenharmony_ci * channel interrupt load by binding a channel to VCPU. 6698c2ecf20Sopenharmony_ci * 6708c2ecf20Sopenharmony_ci * For pre-win8 hosts or non-performance critical channels we assign the 6718c2ecf20Sopenharmony_ci * VMBUS_CONNECT_CPU. 6728c2ecf20Sopenharmony_ci * 6738c2ecf20Sopenharmony_ci * Starting with win8, performance critical channels will be distributed 6748c2ecf20Sopenharmony_ci * evenly among all the available NUMA nodes. Once the node is assigned, 6758c2ecf20Sopenharmony_ci * we will assign the CPU based on a simple round robin scheme. 6768c2ecf20Sopenharmony_ci */ 6778c2ecf20Sopenharmony_cistatic void init_vp_index(struct vmbus_channel *channel) 6788c2ecf20Sopenharmony_ci{ 6798c2ecf20Sopenharmony_ci bool perf_chn = hv_is_perf_channel(channel); 6808c2ecf20Sopenharmony_ci cpumask_var_t available_mask; 6818c2ecf20Sopenharmony_ci struct cpumask *alloced_mask; 6828c2ecf20Sopenharmony_ci u32 target_cpu; 6838c2ecf20Sopenharmony_ci int numa_node; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if ((vmbus_proto_version == VERSION_WS2008) || 6868c2ecf20Sopenharmony_ci (vmbus_proto_version == VERSION_WIN7) || (!perf_chn) || 6878c2ecf20Sopenharmony_ci !alloc_cpumask_var(&available_mask, GFP_KERNEL)) { 6888c2ecf20Sopenharmony_ci /* 6898c2ecf20Sopenharmony_ci * Prior to win8, all channel interrupts are 6908c2ecf20Sopenharmony_ci * delivered on VMBUS_CONNECT_CPU. 6918c2ecf20Sopenharmony_ci * Also if the channel is not a performance critical 6928c2ecf20Sopenharmony_ci * channel, bind it to VMBUS_CONNECT_CPU. 6938c2ecf20Sopenharmony_ci * In case alloc_cpumask_var() fails, bind it to 6948c2ecf20Sopenharmony_ci * VMBUS_CONNECT_CPU. 6958c2ecf20Sopenharmony_ci */ 6968c2ecf20Sopenharmony_ci channel->target_cpu = VMBUS_CONNECT_CPU; 6978c2ecf20Sopenharmony_ci if (perf_chn) 6988c2ecf20Sopenharmony_ci hv_set_alloced_cpu(VMBUS_CONNECT_CPU); 6998c2ecf20Sopenharmony_ci return; 7008c2ecf20Sopenharmony_ci } 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci while (true) { 7038c2ecf20Sopenharmony_ci numa_node = next_numa_node_id++; 7048c2ecf20Sopenharmony_ci if (numa_node == nr_node_ids) { 7058c2ecf20Sopenharmony_ci next_numa_node_id = 0; 7068c2ecf20Sopenharmony_ci continue; 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci if (cpumask_empty(cpumask_of_node(numa_node))) 7098c2ecf20Sopenharmony_ci continue; 7108c2ecf20Sopenharmony_ci break; 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci alloced_mask = &hv_context.hv_numa_map[numa_node]; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci if (cpumask_weight(alloced_mask) == 7158c2ecf20Sopenharmony_ci cpumask_weight(cpumask_of_node(numa_node))) { 7168c2ecf20Sopenharmony_ci /* 7178c2ecf20Sopenharmony_ci * We have cycled through all the CPUs in the node; 7188c2ecf20Sopenharmony_ci * reset the alloced map. 7198c2ecf20Sopenharmony_ci */ 7208c2ecf20Sopenharmony_ci cpumask_clear(alloced_mask); 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci cpumask_xor(available_mask, alloced_mask, cpumask_of_node(numa_node)); 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci target_cpu = cpumask_first(available_mask); 7268c2ecf20Sopenharmony_ci cpumask_set_cpu(target_cpu, alloced_mask); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci channel->target_cpu = target_cpu; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci free_cpumask_var(available_mask); 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci#define UNLOAD_DELAY_UNIT_MS 10 /* 10 milliseconds */ 7348c2ecf20Sopenharmony_ci#define UNLOAD_WAIT_MS (100*1000) /* 100 seconds */ 7358c2ecf20Sopenharmony_ci#define UNLOAD_WAIT_LOOPS (UNLOAD_WAIT_MS/UNLOAD_DELAY_UNIT_MS) 7368c2ecf20Sopenharmony_ci#define UNLOAD_MSG_MS (5*1000) /* Every 5 seconds */ 7378c2ecf20Sopenharmony_ci#define UNLOAD_MSG_LOOPS (UNLOAD_MSG_MS/UNLOAD_DELAY_UNIT_MS) 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_cistatic void vmbus_wait_for_unload(void) 7408c2ecf20Sopenharmony_ci{ 7418c2ecf20Sopenharmony_ci int cpu; 7428c2ecf20Sopenharmony_ci void *page_addr; 7438c2ecf20Sopenharmony_ci struct hv_message *msg; 7448c2ecf20Sopenharmony_ci struct vmbus_channel_message_header *hdr; 7458c2ecf20Sopenharmony_ci u32 message_type, i; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci /* 7488c2ecf20Sopenharmony_ci * CHANNELMSG_UNLOAD_RESPONSE is always delivered to the CPU which was 7498c2ecf20Sopenharmony_ci * used for initial contact or to CPU0 depending on host version. When 7508c2ecf20Sopenharmony_ci * we're crashing on a different CPU let's hope that IRQ handler on 7518c2ecf20Sopenharmony_ci * the cpu which receives CHANNELMSG_UNLOAD_RESPONSE is still 7528c2ecf20Sopenharmony_ci * functional and vmbus_unload_response() will complete 7538c2ecf20Sopenharmony_ci * vmbus_connection.unload_event. If not, the last thing we can do is 7548c2ecf20Sopenharmony_ci * read message pages for all CPUs directly. 7558c2ecf20Sopenharmony_ci * 7568c2ecf20Sopenharmony_ci * Wait up to 100 seconds since an Azure host must writeback any dirty 7578c2ecf20Sopenharmony_ci * data in its disk cache before the VMbus UNLOAD request will 7588c2ecf20Sopenharmony_ci * complete. This flushing has been empirically observed to take up 7598c2ecf20Sopenharmony_ci * to 50 seconds in cases with a lot of dirty data, so allow additional 7608c2ecf20Sopenharmony_ci * leeway and for inaccuracies in mdelay(). But eventually time out so 7618c2ecf20Sopenharmony_ci * that the panic path can't get hung forever in case the response 7628c2ecf20Sopenharmony_ci * message isn't seen. 7638c2ecf20Sopenharmony_ci */ 7648c2ecf20Sopenharmony_ci for (i = 1; i <= UNLOAD_WAIT_LOOPS; i++) { 7658c2ecf20Sopenharmony_ci if (completion_done(&vmbus_connection.unload_event)) 7668c2ecf20Sopenharmony_ci goto completed; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci for_each_present_cpu(cpu) { 7698c2ecf20Sopenharmony_ci struct hv_per_cpu_context *hv_cpu 7708c2ecf20Sopenharmony_ci = per_cpu_ptr(hv_context.cpu_context, cpu); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci /* 7738c2ecf20Sopenharmony_ci * In a CoCo VM the synic_message_page is not allocated 7748c2ecf20Sopenharmony_ci * in hv_synic_alloc(). Instead it is set/cleared in 7758c2ecf20Sopenharmony_ci * hv_synic_enable_regs() and hv_synic_disable_regs() 7768c2ecf20Sopenharmony_ci * such that it is set only when the CPU is online. If 7778c2ecf20Sopenharmony_ci * not all present CPUs are online, the message page 7788c2ecf20Sopenharmony_ci * might be NULL, so skip such CPUs. 7798c2ecf20Sopenharmony_ci */ 7808c2ecf20Sopenharmony_ci page_addr = hv_cpu->synic_message_page; 7818c2ecf20Sopenharmony_ci if (!page_addr) 7828c2ecf20Sopenharmony_ci continue; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci msg = (struct hv_message *)page_addr 7858c2ecf20Sopenharmony_ci + VMBUS_MESSAGE_SINT; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci message_type = READ_ONCE(msg->header.message_type); 7888c2ecf20Sopenharmony_ci if (message_type == HVMSG_NONE) 7898c2ecf20Sopenharmony_ci continue; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci hdr = (struct vmbus_channel_message_header *) 7928c2ecf20Sopenharmony_ci msg->u.payload; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci if (hdr->msgtype == CHANNELMSG_UNLOAD_RESPONSE) 7958c2ecf20Sopenharmony_ci complete(&vmbus_connection.unload_event); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci vmbus_signal_eom(msg, message_type); 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci /* 8018c2ecf20Sopenharmony_ci * Give a notice periodically so someone watching the 8028c2ecf20Sopenharmony_ci * serial output won't think it is completely hung. 8038c2ecf20Sopenharmony_ci */ 8048c2ecf20Sopenharmony_ci if (!(i % UNLOAD_MSG_LOOPS)) 8058c2ecf20Sopenharmony_ci pr_notice("Waiting for VMBus UNLOAD to complete\n"); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci mdelay(UNLOAD_DELAY_UNIT_MS); 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci pr_err("Continuing even though VMBus UNLOAD did not complete\n"); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_cicompleted: 8128c2ecf20Sopenharmony_ci /* 8138c2ecf20Sopenharmony_ci * We're crashing and already got the UNLOAD_RESPONSE, cleanup all 8148c2ecf20Sopenharmony_ci * maybe-pending messages on all CPUs to be able to receive new 8158c2ecf20Sopenharmony_ci * messages after we reconnect. 8168c2ecf20Sopenharmony_ci */ 8178c2ecf20Sopenharmony_ci for_each_present_cpu(cpu) { 8188c2ecf20Sopenharmony_ci struct hv_per_cpu_context *hv_cpu 8198c2ecf20Sopenharmony_ci = per_cpu_ptr(hv_context.cpu_context, cpu); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci page_addr = hv_cpu->synic_message_page; 8228c2ecf20Sopenharmony_ci if (!page_addr) 8238c2ecf20Sopenharmony_ci continue; 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT; 8268c2ecf20Sopenharmony_ci msg->header.message_type = HVMSG_NONE; 8278c2ecf20Sopenharmony_ci } 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci/* 8318c2ecf20Sopenharmony_ci * vmbus_unload_response - Handler for the unload response. 8328c2ecf20Sopenharmony_ci */ 8338c2ecf20Sopenharmony_cistatic void vmbus_unload_response(struct vmbus_channel_message_header *hdr) 8348c2ecf20Sopenharmony_ci{ 8358c2ecf20Sopenharmony_ci /* 8368c2ecf20Sopenharmony_ci * This is a global event; just wakeup the waiting thread. 8378c2ecf20Sopenharmony_ci * Once we successfully unload, we can cleanup the monitor state. 8388c2ecf20Sopenharmony_ci */ 8398c2ecf20Sopenharmony_ci complete(&vmbus_connection.unload_event); 8408c2ecf20Sopenharmony_ci} 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_civoid vmbus_initiate_unload(bool crash) 8438c2ecf20Sopenharmony_ci{ 8448c2ecf20Sopenharmony_ci struct vmbus_channel_message_header hdr; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci if (xchg(&vmbus_connection.conn_state, DISCONNECTED) == DISCONNECTED) 8478c2ecf20Sopenharmony_ci return; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci /* Pre-Win2012R2 hosts don't support reconnect */ 8508c2ecf20Sopenharmony_ci if (vmbus_proto_version < VERSION_WIN8_1) 8518c2ecf20Sopenharmony_ci return; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci init_completion(&vmbus_connection.unload_event); 8548c2ecf20Sopenharmony_ci memset(&hdr, 0, sizeof(struct vmbus_channel_message_header)); 8558c2ecf20Sopenharmony_ci hdr.msgtype = CHANNELMSG_UNLOAD; 8568c2ecf20Sopenharmony_ci vmbus_post_msg(&hdr, sizeof(struct vmbus_channel_message_header), 8578c2ecf20Sopenharmony_ci !crash); 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci /* 8608c2ecf20Sopenharmony_ci * vmbus_initiate_unload() is also called on crash and the crash can be 8618c2ecf20Sopenharmony_ci * happening in an interrupt context, where scheduling is impossible. 8628c2ecf20Sopenharmony_ci */ 8638c2ecf20Sopenharmony_ci if (!crash) 8648c2ecf20Sopenharmony_ci wait_for_completion(&vmbus_connection.unload_event); 8658c2ecf20Sopenharmony_ci else 8668c2ecf20Sopenharmony_ci vmbus_wait_for_unload(); 8678c2ecf20Sopenharmony_ci} 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_cistatic void check_ready_for_resume_event(void) 8708c2ecf20Sopenharmony_ci{ 8718c2ecf20Sopenharmony_ci /* 8728c2ecf20Sopenharmony_ci * If all the old primary channels have been fixed up, then it's safe 8738c2ecf20Sopenharmony_ci * to resume. 8748c2ecf20Sopenharmony_ci */ 8758c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&vmbus_connection.nr_chan_fixup_on_resume)) 8768c2ecf20Sopenharmony_ci complete(&vmbus_connection.ready_for_resume_event); 8778c2ecf20Sopenharmony_ci} 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_cistatic void vmbus_setup_channel_state(struct vmbus_channel *channel, 8808c2ecf20Sopenharmony_ci struct vmbus_channel_offer_channel *offer) 8818c2ecf20Sopenharmony_ci{ 8828c2ecf20Sopenharmony_ci /* 8838c2ecf20Sopenharmony_ci * Setup state for signalling the host. 8848c2ecf20Sopenharmony_ci */ 8858c2ecf20Sopenharmony_ci channel->sig_event = VMBUS_EVENT_CONNECTION_ID; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci if (vmbus_proto_version != VERSION_WS2008) { 8888c2ecf20Sopenharmony_ci channel->is_dedicated_interrupt = 8898c2ecf20Sopenharmony_ci (offer->is_dedicated_interrupt != 0); 8908c2ecf20Sopenharmony_ci channel->sig_event = offer->connection_id; 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci memcpy(&channel->offermsg, offer, 8948c2ecf20Sopenharmony_ci sizeof(struct vmbus_channel_offer_channel)); 8958c2ecf20Sopenharmony_ci channel->monitor_grp = (u8)offer->monitorid / 32; 8968c2ecf20Sopenharmony_ci channel->monitor_bit = (u8)offer->monitorid % 32; 8978c2ecf20Sopenharmony_ci channel->device_id = hv_get_dev_type(channel); 8988c2ecf20Sopenharmony_ci} 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci/* 9018c2ecf20Sopenharmony_ci * find_primary_channel_by_offer - Get the channel object given the new offer. 9028c2ecf20Sopenharmony_ci * This is only used in the resume path of hibernation. 9038c2ecf20Sopenharmony_ci */ 9048c2ecf20Sopenharmony_cistatic struct vmbus_channel * 9058c2ecf20Sopenharmony_cifind_primary_channel_by_offer(const struct vmbus_channel_offer_channel *offer) 9068c2ecf20Sopenharmony_ci{ 9078c2ecf20Sopenharmony_ci struct vmbus_channel *channel = NULL, *iter; 9088c2ecf20Sopenharmony_ci const guid_t *inst1, *inst2; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci /* Ignore sub-channel offers. */ 9118c2ecf20Sopenharmony_ci if (offer->offer.sub_channel_index != 0) 9128c2ecf20Sopenharmony_ci return NULL; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci mutex_lock(&vmbus_connection.channel_mutex); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci list_for_each_entry(iter, &vmbus_connection.chn_list, listentry) { 9178c2ecf20Sopenharmony_ci inst1 = &iter->offermsg.offer.if_instance; 9188c2ecf20Sopenharmony_ci inst2 = &offer->offer.if_instance; 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci if (guid_equal(inst1, inst2)) { 9218c2ecf20Sopenharmony_ci channel = iter; 9228c2ecf20Sopenharmony_ci break; 9238c2ecf20Sopenharmony_ci } 9248c2ecf20Sopenharmony_ci } 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci mutex_unlock(&vmbus_connection.channel_mutex); 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci return channel; 9298c2ecf20Sopenharmony_ci} 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci/* 9328c2ecf20Sopenharmony_ci * vmbus_onoffer - Handler for channel offers from vmbus in parent partition. 9338c2ecf20Sopenharmony_ci * 9348c2ecf20Sopenharmony_ci */ 9358c2ecf20Sopenharmony_cistatic void vmbus_onoffer(struct vmbus_channel_message_header *hdr) 9368c2ecf20Sopenharmony_ci{ 9378c2ecf20Sopenharmony_ci struct vmbus_channel_offer_channel *offer; 9388c2ecf20Sopenharmony_ci struct vmbus_channel *oldchannel, *newchannel; 9398c2ecf20Sopenharmony_ci size_t offer_sz; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci offer = (struct vmbus_channel_offer_channel *)hdr; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci trace_vmbus_onoffer(offer); 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci oldchannel = find_primary_channel_by_offer(offer); 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci if (oldchannel != NULL) { 9488c2ecf20Sopenharmony_ci /* 9498c2ecf20Sopenharmony_ci * We're resuming from hibernation: all the sub-channel and 9508c2ecf20Sopenharmony_ci * hv_sock channels we had before the hibernation should have 9518c2ecf20Sopenharmony_ci * been cleaned up, and now we must be seeing a re-offered 9528c2ecf20Sopenharmony_ci * primary channel that we had before the hibernation. 9538c2ecf20Sopenharmony_ci */ 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ci /* 9568c2ecf20Sopenharmony_ci * { Initially: channel relid = INVALID_RELID, 9578c2ecf20Sopenharmony_ci * channels[valid_relid] = NULL } 9588c2ecf20Sopenharmony_ci * 9598c2ecf20Sopenharmony_ci * CPU1 CPU2 9608c2ecf20Sopenharmony_ci * 9618c2ecf20Sopenharmony_ci * [vmbus_onoffer()] [vmbus_device_release()] 9628c2ecf20Sopenharmony_ci * 9638c2ecf20Sopenharmony_ci * LOCK channel_mutex LOCK channel_mutex 9648c2ecf20Sopenharmony_ci * STORE channel relid = valid_relid LOAD r1 = channel relid 9658c2ecf20Sopenharmony_ci * MAP_RELID channel if (r1 != INVALID_RELID) 9668c2ecf20Sopenharmony_ci * UNLOCK channel_mutex UNMAP_RELID channel 9678c2ecf20Sopenharmony_ci * UNLOCK channel_mutex 9688c2ecf20Sopenharmony_ci * 9698c2ecf20Sopenharmony_ci * Forbids: r1 == valid_relid && 9708c2ecf20Sopenharmony_ci * channels[valid_relid] == channel 9718c2ecf20Sopenharmony_ci * 9728c2ecf20Sopenharmony_ci * Note. r1 can be INVALID_RELID only for an hv_sock channel. 9738c2ecf20Sopenharmony_ci * None of the hv_sock channels which were present before the 9748c2ecf20Sopenharmony_ci * suspend are re-offered upon the resume. See the WARN_ON() 9758c2ecf20Sopenharmony_ci * in hv_process_channel_removal(). 9768c2ecf20Sopenharmony_ci */ 9778c2ecf20Sopenharmony_ci mutex_lock(&vmbus_connection.channel_mutex); 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci atomic_dec(&vmbus_connection.offer_in_progress); 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci WARN_ON(oldchannel->offermsg.child_relid != INVALID_RELID); 9828c2ecf20Sopenharmony_ci /* Fix up the relid. */ 9838c2ecf20Sopenharmony_ci oldchannel->offermsg.child_relid = offer->child_relid; 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci offer_sz = sizeof(*offer); 9868c2ecf20Sopenharmony_ci if (memcmp(offer, &oldchannel->offermsg, offer_sz) != 0) { 9878c2ecf20Sopenharmony_ci /* 9888c2ecf20Sopenharmony_ci * This is not an error, since the host can also change 9898c2ecf20Sopenharmony_ci * the other field(s) of the offer, e.g. on WS RS5 9908c2ecf20Sopenharmony_ci * (Build 17763), the offer->connection_id of the 9918c2ecf20Sopenharmony_ci * Mellanox VF vmbus device can change when the host 9928c2ecf20Sopenharmony_ci * reoffers the device upon resume. 9938c2ecf20Sopenharmony_ci */ 9948c2ecf20Sopenharmony_ci pr_debug("vmbus offer changed: relid=%d\n", 9958c2ecf20Sopenharmony_ci offer->child_relid); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci print_hex_dump_debug("Old vmbus offer: ", 9988c2ecf20Sopenharmony_ci DUMP_PREFIX_OFFSET, 16, 4, 9998c2ecf20Sopenharmony_ci &oldchannel->offermsg, offer_sz, 10008c2ecf20Sopenharmony_ci false); 10018c2ecf20Sopenharmony_ci print_hex_dump_debug("New vmbus offer: ", 10028c2ecf20Sopenharmony_ci DUMP_PREFIX_OFFSET, 16, 4, 10038c2ecf20Sopenharmony_ci offer, offer_sz, false); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci /* Fix up the old channel. */ 10068c2ecf20Sopenharmony_ci vmbus_setup_channel_state(oldchannel, offer); 10078c2ecf20Sopenharmony_ci } 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci /* Add the channel back to the array of channels. */ 10108c2ecf20Sopenharmony_ci vmbus_channel_map_relid(oldchannel); 10118c2ecf20Sopenharmony_ci check_ready_for_resume_event(); 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci mutex_unlock(&vmbus_connection.channel_mutex); 10148c2ecf20Sopenharmony_ci return; 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci /* Allocate the channel object and save this offer. */ 10188c2ecf20Sopenharmony_ci newchannel = alloc_channel(); 10198c2ecf20Sopenharmony_ci if (!newchannel) { 10208c2ecf20Sopenharmony_ci vmbus_release_relid(offer->child_relid); 10218c2ecf20Sopenharmony_ci atomic_dec(&vmbus_connection.offer_in_progress); 10228c2ecf20Sopenharmony_ci pr_err("Unable to allocate channel object\n"); 10238c2ecf20Sopenharmony_ci return; 10248c2ecf20Sopenharmony_ci } 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci vmbus_setup_channel_state(newchannel, offer); 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci vmbus_process_offer(newchannel); 10298c2ecf20Sopenharmony_ci} 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_cistatic void check_ready_for_suspend_event(void) 10328c2ecf20Sopenharmony_ci{ 10338c2ecf20Sopenharmony_ci /* 10348c2ecf20Sopenharmony_ci * If all the sub-channels or hv_sock channels have been cleaned up, 10358c2ecf20Sopenharmony_ci * then it's safe to suspend. 10368c2ecf20Sopenharmony_ci */ 10378c2ecf20Sopenharmony_ci if (atomic_dec_and_test(&vmbus_connection.nr_chan_close_on_suspend)) 10388c2ecf20Sopenharmony_ci complete(&vmbus_connection.ready_for_suspend_event); 10398c2ecf20Sopenharmony_ci} 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci/* 10428c2ecf20Sopenharmony_ci * vmbus_onoffer_rescind - Rescind offer handler. 10438c2ecf20Sopenharmony_ci * 10448c2ecf20Sopenharmony_ci * We queue a work item to process this offer synchronously 10458c2ecf20Sopenharmony_ci */ 10468c2ecf20Sopenharmony_cistatic void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) 10478c2ecf20Sopenharmony_ci{ 10488c2ecf20Sopenharmony_ci struct vmbus_channel_rescind_offer *rescind; 10498c2ecf20Sopenharmony_ci struct vmbus_channel *channel; 10508c2ecf20Sopenharmony_ci struct device *dev; 10518c2ecf20Sopenharmony_ci bool clean_up_chan_for_suspend; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci rescind = (struct vmbus_channel_rescind_offer *)hdr; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci trace_vmbus_onoffer_rescind(rescind); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci /* 10588c2ecf20Sopenharmony_ci * The offer msg and the corresponding rescind msg 10598c2ecf20Sopenharmony_ci * from the host are guranteed to be ordered - 10608c2ecf20Sopenharmony_ci * offer comes in first and then the rescind. 10618c2ecf20Sopenharmony_ci * Since we process these events in work elements, 10628c2ecf20Sopenharmony_ci * and with preemption, we may end up processing 10638c2ecf20Sopenharmony_ci * the events out of order. We rely on the synchronization 10648c2ecf20Sopenharmony_ci * provided by offer_in_progress and by channel_mutex for 10658c2ecf20Sopenharmony_ci * ordering these events: 10668c2ecf20Sopenharmony_ci * 10678c2ecf20Sopenharmony_ci * { Initially: offer_in_progress = 1 } 10688c2ecf20Sopenharmony_ci * 10698c2ecf20Sopenharmony_ci * CPU1 CPU2 10708c2ecf20Sopenharmony_ci * 10718c2ecf20Sopenharmony_ci * [vmbus_onoffer()] [vmbus_onoffer_rescind()] 10728c2ecf20Sopenharmony_ci * 10738c2ecf20Sopenharmony_ci * LOCK channel_mutex WAIT_ON offer_in_progress == 0 10748c2ecf20Sopenharmony_ci * DECREMENT offer_in_progress LOCK channel_mutex 10758c2ecf20Sopenharmony_ci * STORE channels[] LOAD channels[] 10768c2ecf20Sopenharmony_ci * UNLOCK channel_mutex UNLOCK channel_mutex 10778c2ecf20Sopenharmony_ci * 10788c2ecf20Sopenharmony_ci * Forbids: CPU2's LOAD from *not* seeing CPU1's STORE 10798c2ecf20Sopenharmony_ci */ 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci while (atomic_read(&vmbus_connection.offer_in_progress) != 0) { 10828c2ecf20Sopenharmony_ci /* 10838c2ecf20Sopenharmony_ci * We wait here until any channel offer is currently 10848c2ecf20Sopenharmony_ci * being processed. 10858c2ecf20Sopenharmony_ci */ 10868c2ecf20Sopenharmony_ci msleep(1); 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci mutex_lock(&vmbus_connection.channel_mutex); 10908c2ecf20Sopenharmony_ci channel = relid2channel(rescind->child_relid); 10918c2ecf20Sopenharmony_ci mutex_unlock(&vmbus_connection.channel_mutex); 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci if (channel == NULL) { 10948c2ecf20Sopenharmony_ci /* 10958c2ecf20Sopenharmony_ci * We failed in processing the offer message; 10968c2ecf20Sopenharmony_ci * we would have cleaned up the relid in that 10978c2ecf20Sopenharmony_ci * failure path. 10988c2ecf20Sopenharmony_ci */ 10998c2ecf20Sopenharmony_ci return; 11008c2ecf20Sopenharmony_ci } 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci clean_up_chan_for_suspend = is_hvsock_channel(channel) || 11038c2ecf20Sopenharmony_ci is_sub_channel(channel); 11048c2ecf20Sopenharmony_ci /* 11058c2ecf20Sopenharmony_ci * Before setting channel->rescind in vmbus_rescind_cleanup(), we 11068c2ecf20Sopenharmony_ci * should make sure the channel callback is not running any more. 11078c2ecf20Sopenharmony_ci */ 11088c2ecf20Sopenharmony_ci vmbus_reset_channel_cb(channel); 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci /* 11118c2ecf20Sopenharmony_ci * Now wait for offer handling to complete. 11128c2ecf20Sopenharmony_ci */ 11138c2ecf20Sopenharmony_ci vmbus_rescind_cleanup(channel); 11148c2ecf20Sopenharmony_ci while (READ_ONCE(channel->probe_done) == false) { 11158c2ecf20Sopenharmony_ci /* 11168c2ecf20Sopenharmony_ci * We wait here until any channel offer is currently 11178c2ecf20Sopenharmony_ci * being processed. 11188c2ecf20Sopenharmony_ci */ 11198c2ecf20Sopenharmony_ci msleep(1); 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci /* 11238c2ecf20Sopenharmony_ci * At this point, the rescind handling can proceed safely. 11248c2ecf20Sopenharmony_ci */ 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci if (channel->device_obj) { 11278c2ecf20Sopenharmony_ci if (channel->chn_rescind_callback) { 11288c2ecf20Sopenharmony_ci channel->chn_rescind_callback(channel); 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci if (clean_up_chan_for_suspend) 11318c2ecf20Sopenharmony_ci check_ready_for_suspend_event(); 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci return; 11348c2ecf20Sopenharmony_ci } 11358c2ecf20Sopenharmony_ci /* 11368c2ecf20Sopenharmony_ci * We will have to unregister this device from the 11378c2ecf20Sopenharmony_ci * driver core. 11388c2ecf20Sopenharmony_ci */ 11398c2ecf20Sopenharmony_ci dev = get_device(&channel->device_obj->device); 11408c2ecf20Sopenharmony_ci if (dev) { 11418c2ecf20Sopenharmony_ci vmbus_device_unregister(channel->device_obj); 11428c2ecf20Sopenharmony_ci put_device(dev); 11438c2ecf20Sopenharmony_ci } 11448c2ecf20Sopenharmony_ci } else if (channel->primary_channel != NULL) { 11458c2ecf20Sopenharmony_ci /* 11468c2ecf20Sopenharmony_ci * Sub-channel is being rescinded. Following is the channel 11478c2ecf20Sopenharmony_ci * close sequence when initiated from the driveri (refer to 11488c2ecf20Sopenharmony_ci * vmbus_close() for details): 11498c2ecf20Sopenharmony_ci * 1. Close all sub-channels first 11508c2ecf20Sopenharmony_ci * 2. Then close the primary channel. 11518c2ecf20Sopenharmony_ci */ 11528c2ecf20Sopenharmony_ci mutex_lock(&vmbus_connection.channel_mutex); 11538c2ecf20Sopenharmony_ci if (channel->state == CHANNEL_OPEN_STATE) { 11548c2ecf20Sopenharmony_ci /* 11558c2ecf20Sopenharmony_ci * The channel is currently not open; 11568c2ecf20Sopenharmony_ci * it is safe for us to cleanup the channel. 11578c2ecf20Sopenharmony_ci */ 11588c2ecf20Sopenharmony_ci hv_process_channel_removal(channel); 11598c2ecf20Sopenharmony_ci } else { 11608c2ecf20Sopenharmony_ci complete(&channel->rescind_event); 11618c2ecf20Sopenharmony_ci } 11628c2ecf20Sopenharmony_ci mutex_unlock(&vmbus_connection.channel_mutex); 11638c2ecf20Sopenharmony_ci } 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci /* The "channel" may have been freed. Do not access it any longer. */ 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci if (clean_up_chan_for_suspend) 11688c2ecf20Sopenharmony_ci check_ready_for_suspend_event(); 11698c2ecf20Sopenharmony_ci} 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_civoid vmbus_hvsock_device_unregister(struct vmbus_channel *channel) 11728c2ecf20Sopenharmony_ci{ 11738c2ecf20Sopenharmony_ci BUG_ON(!is_hvsock_channel(channel)); 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci /* We always get a rescind msg when a connection is closed. */ 11768c2ecf20Sopenharmony_ci while (!READ_ONCE(channel->probe_done) || !READ_ONCE(channel->rescind)) 11778c2ecf20Sopenharmony_ci msleep(1); 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci vmbus_device_unregister(channel->device_obj); 11808c2ecf20Sopenharmony_ci} 11818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_hvsock_device_unregister); 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci/* 11858c2ecf20Sopenharmony_ci * vmbus_onoffers_delivered - 11868c2ecf20Sopenharmony_ci * This is invoked when all offers have been delivered. 11878c2ecf20Sopenharmony_ci * 11888c2ecf20Sopenharmony_ci * Nothing to do here. 11898c2ecf20Sopenharmony_ci */ 11908c2ecf20Sopenharmony_cistatic void vmbus_onoffers_delivered( 11918c2ecf20Sopenharmony_ci struct vmbus_channel_message_header *hdr) 11928c2ecf20Sopenharmony_ci{ 11938c2ecf20Sopenharmony_ci} 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci/* 11968c2ecf20Sopenharmony_ci * vmbus_onopen_result - Open result handler. 11978c2ecf20Sopenharmony_ci * 11988c2ecf20Sopenharmony_ci * This is invoked when we received a response to our channel open request. 11998c2ecf20Sopenharmony_ci * Find the matching request, copy the response and signal the requesting 12008c2ecf20Sopenharmony_ci * thread. 12018c2ecf20Sopenharmony_ci */ 12028c2ecf20Sopenharmony_cistatic void vmbus_onopen_result(struct vmbus_channel_message_header *hdr) 12038c2ecf20Sopenharmony_ci{ 12048c2ecf20Sopenharmony_ci struct vmbus_channel_open_result *result; 12058c2ecf20Sopenharmony_ci struct vmbus_channel_msginfo *msginfo; 12068c2ecf20Sopenharmony_ci struct vmbus_channel_message_header *requestheader; 12078c2ecf20Sopenharmony_ci struct vmbus_channel_open_channel *openmsg; 12088c2ecf20Sopenharmony_ci unsigned long flags; 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci result = (struct vmbus_channel_open_result *)hdr; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci trace_vmbus_onopen_result(result); 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci /* 12158c2ecf20Sopenharmony_ci * Find the open msg, copy the result and signal/unblock the wait event 12168c2ecf20Sopenharmony_ci */ 12178c2ecf20Sopenharmony_ci spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, 12208c2ecf20Sopenharmony_ci msglistentry) { 12218c2ecf20Sopenharmony_ci requestheader = 12228c2ecf20Sopenharmony_ci (struct vmbus_channel_message_header *)msginfo->msg; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci if (requestheader->msgtype == CHANNELMSG_OPENCHANNEL) { 12258c2ecf20Sopenharmony_ci openmsg = 12268c2ecf20Sopenharmony_ci (struct vmbus_channel_open_channel *)msginfo->msg; 12278c2ecf20Sopenharmony_ci if (openmsg->child_relid == result->child_relid && 12288c2ecf20Sopenharmony_ci openmsg->openid == result->openid) { 12298c2ecf20Sopenharmony_ci memcpy(&msginfo->response.open_result, 12308c2ecf20Sopenharmony_ci result, 12318c2ecf20Sopenharmony_ci sizeof( 12328c2ecf20Sopenharmony_ci struct vmbus_channel_open_result)); 12338c2ecf20Sopenharmony_ci complete(&msginfo->waitevent); 12348c2ecf20Sopenharmony_ci break; 12358c2ecf20Sopenharmony_ci } 12368c2ecf20Sopenharmony_ci } 12378c2ecf20Sopenharmony_ci } 12388c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); 12398c2ecf20Sopenharmony_ci} 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci/* 12428c2ecf20Sopenharmony_ci * vmbus_ongpadl_created - GPADL created handler. 12438c2ecf20Sopenharmony_ci * 12448c2ecf20Sopenharmony_ci * This is invoked when we received a response to our gpadl create request. 12458c2ecf20Sopenharmony_ci * Find the matching request, copy the response and signal the requesting 12468c2ecf20Sopenharmony_ci * thread. 12478c2ecf20Sopenharmony_ci */ 12488c2ecf20Sopenharmony_cistatic void vmbus_ongpadl_created(struct vmbus_channel_message_header *hdr) 12498c2ecf20Sopenharmony_ci{ 12508c2ecf20Sopenharmony_ci struct vmbus_channel_gpadl_created *gpadlcreated; 12518c2ecf20Sopenharmony_ci struct vmbus_channel_msginfo *msginfo; 12528c2ecf20Sopenharmony_ci struct vmbus_channel_message_header *requestheader; 12538c2ecf20Sopenharmony_ci struct vmbus_channel_gpadl_header *gpadlheader; 12548c2ecf20Sopenharmony_ci unsigned long flags; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci gpadlcreated = (struct vmbus_channel_gpadl_created *)hdr; 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci trace_vmbus_ongpadl_created(gpadlcreated); 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci /* 12618c2ecf20Sopenharmony_ci * Find the establish msg, copy the result and signal/unblock the wait 12628c2ecf20Sopenharmony_ci * event 12638c2ecf20Sopenharmony_ci */ 12648c2ecf20Sopenharmony_ci spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, 12678c2ecf20Sopenharmony_ci msglistentry) { 12688c2ecf20Sopenharmony_ci requestheader = 12698c2ecf20Sopenharmony_ci (struct vmbus_channel_message_header *)msginfo->msg; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci if (requestheader->msgtype == CHANNELMSG_GPADL_HEADER) { 12728c2ecf20Sopenharmony_ci gpadlheader = 12738c2ecf20Sopenharmony_ci (struct vmbus_channel_gpadl_header *)requestheader; 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci if ((gpadlcreated->child_relid == 12768c2ecf20Sopenharmony_ci gpadlheader->child_relid) && 12778c2ecf20Sopenharmony_ci (gpadlcreated->gpadl == gpadlheader->gpadl)) { 12788c2ecf20Sopenharmony_ci memcpy(&msginfo->response.gpadl_created, 12798c2ecf20Sopenharmony_ci gpadlcreated, 12808c2ecf20Sopenharmony_ci sizeof( 12818c2ecf20Sopenharmony_ci struct vmbus_channel_gpadl_created)); 12828c2ecf20Sopenharmony_ci complete(&msginfo->waitevent); 12838c2ecf20Sopenharmony_ci break; 12848c2ecf20Sopenharmony_ci } 12858c2ecf20Sopenharmony_ci } 12868c2ecf20Sopenharmony_ci } 12878c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); 12888c2ecf20Sopenharmony_ci} 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci/* 12918c2ecf20Sopenharmony_ci * vmbus_ongpadl_torndown - GPADL torndown handler. 12928c2ecf20Sopenharmony_ci * 12938c2ecf20Sopenharmony_ci * This is invoked when we received a response to our gpadl teardown request. 12948c2ecf20Sopenharmony_ci * Find the matching request, copy the response and signal the requesting 12958c2ecf20Sopenharmony_ci * thread. 12968c2ecf20Sopenharmony_ci */ 12978c2ecf20Sopenharmony_cistatic void vmbus_ongpadl_torndown( 12988c2ecf20Sopenharmony_ci struct vmbus_channel_message_header *hdr) 12998c2ecf20Sopenharmony_ci{ 13008c2ecf20Sopenharmony_ci struct vmbus_channel_gpadl_torndown *gpadl_torndown; 13018c2ecf20Sopenharmony_ci struct vmbus_channel_msginfo *msginfo; 13028c2ecf20Sopenharmony_ci struct vmbus_channel_message_header *requestheader; 13038c2ecf20Sopenharmony_ci struct vmbus_channel_gpadl_teardown *gpadl_teardown; 13048c2ecf20Sopenharmony_ci unsigned long flags; 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci gpadl_torndown = (struct vmbus_channel_gpadl_torndown *)hdr; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci trace_vmbus_ongpadl_torndown(gpadl_torndown); 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci /* 13118c2ecf20Sopenharmony_ci * Find the open msg, copy the result and signal/unblock the wait event 13128c2ecf20Sopenharmony_ci */ 13138c2ecf20Sopenharmony_ci spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, 13168c2ecf20Sopenharmony_ci msglistentry) { 13178c2ecf20Sopenharmony_ci requestheader = 13188c2ecf20Sopenharmony_ci (struct vmbus_channel_message_header *)msginfo->msg; 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci if (requestheader->msgtype == CHANNELMSG_GPADL_TEARDOWN) { 13218c2ecf20Sopenharmony_ci gpadl_teardown = 13228c2ecf20Sopenharmony_ci (struct vmbus_channel_gpadl_teardown *)requestheader; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci if (gpadl_torndown->gpadl == gpadl_teardown->gpadl) { 13258c2ecf20Sopenharmony_ci memcpy(&msginfo->response.gpadl_torndown, 13268c2ecf20Sopenharmony_ci gpadl_torndown, 13278c2ecf20Sopenharmony_ci sizeof( 13288c2ecf20Sopenharmony_ci struct vmbus_channel_gpadl_torndown)); 13298c2ecf20Sopenharmony_ci complete(&msginfo->waitevent); 13308c2ecf20Sopenharmony_ci break; 13318c2ecf20Sopenharmony_ci } 13328c2ecf20Sopenharmony_ci } 13338c2ecf20Sopenharmony_ci } 13348c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); 13358c2ecf20Sopenharmony_ci} 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_ci/* 13388c2ecf20Sopenharmony_ci * vmbus_onversion_response - Version response handler 13398c2ecf20Sopenharmony_ci * 13408c2ecf20Sopenharmony_ci * This is invoked when we received a response to our initiate contact request. 13418c2ecf20Sopenharmony_ci * Find the matching request, copy the response and signal the requesting 13428c2ecf20Sopenharmony_ci * thread. 13438c2ecf20Sopenharmony_ci */ 13448c2ecf20Sopenharmony_cistatic void vmbus_onversion_response( 13458c2ecf20Sopenharmony_ci struct vmbus_channel_message_header *hdr) 13468c2ecf20Sopenharmony_ci{ 13478c2ecf20Sopenharmony_ci struct vmbus_channel_msginfo *msginfo; 13488c2ecf20Sopenharmony_ci struct vmbus_channel_message_header *requestheader; 13498c2ecf20Sopenharmony_ci struct vmbus_channel_version_response *version_response; 13508c2ecf20Sopenharmony_ci unsigned long flags; 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci version_response = (struct vmbus_channel_version_response *)hdr; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci trace_vmbus_onversion_response(version_response); 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, 13598c2ecf20Sopenharmony_ci msglistentry) { 13608c2ecf20Sopenharmony_ci requestheader = 13618c2ecf20Sopenharmony_ci (struct vmbus_channel_message_header *)msginfo->msg; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci if (requestheader->msgtype == 13648c2ecf20Sopenharmony_ci CHANNELMSG_INITIATE_CONTACT) { 13658c2ecf20Sopenharmony_ci memcpy(&msginfo->response.version_response, 13668c2ecf20Sopenharmony_ci version_response, 13678c2ecf20Sopenharmony_ci sizeof(struct vmbus_channel_version_response)); 13688c2ecf20Sopenharmony_ci complete(&msginfo->waitevent); 13698c2ecf20Sopenharmony_ci } 13708c2ecf20Sopenharmony_ci } 13718c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); 13728c2ecf20Sopenharmony_ci} 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci/* Channel message dispatch table */ 13758c2ecf20Sopenharmony_ciconst struct vmbus_channel_message_table_entry 13768c2ecf20Sopenharmony_cichannel_message_table[CHANNELMSG_COUNT] = { 13778c2ecf20Sopenharmony_ci { CHANNELMSG_INVALID, 0, NULL, 0}, 13788c2ecf20Sopenharmony_ci { CHANNELMSG_OFFERCHANNEL, 0, vmbus_onoffer, 13798c2ecf20Sopenharmony_ci sizeof(struct vmbus_channel_offer_channel)}, 13808c2ecf20Sopenharmony_ci { CHANNELMSG_RESCIND_CHANNELOFFER, 0, vmbus_onoffer_rescind, 13818c2ecf20Sopenharmony_ci sizeof(struct vmbus_channel_rescind_offer) }, 13828c2ecf20Sopenharmony_ci { CHANNELMSG_REQUESTOFFERS, 0, NULL, 0}, 13838c2ecf20Sopenharmony_ci { CHANNELMSG_ALLOFFERS_DELIVERED, 1, vmbus_onoffers_delivered, 0}, 13848c2ecf20Sopenharmony_ci { CHANNELMSG_OPENCHANNEL, 0, NULL, 0}, 13858c2ecf20Sopenharmony_ci { CHANNELMSG_OPENCHANNEL_RESULT, 1, vmbus_onopen_result, 13868c2ecf20Sopenharmony_ci sizeof(struct vmbus_channel_open_result)}, 13878c2ecf20Sopenharmony_ci { CHANNELMSG_CLOSECHANNEL, 0, NULL, 0}, 13888c2ecf20Sopenharmony_ci { CHANNELMSG_GPADL_HEADER, 0, NULL, 0}, 13898c2ecf20Sopenharmony_ci { CHANNELMSG_GPADL_BODY, 0, NULL, 0}, 13908c2ecf20Sopenharmony_ci { CHANNELMSG_GPADL_CREATED, 1, vmbus_ongpadl_created, 13918c2ecf20Sopenharmony_ci sizeof(struct vmbus_channel_gpadl_created)}, 13928c2ecf20Sopenharmony_ci { CHANNELMSG_GPADL_TEARDOWN, 0, NULL, 0}, 13938c2ecf20Sopenharmony_ci { CHANNELMSG_GPADL_TORNDOWN, 1, vmbus_ongpadl_torndown, 13948c2ecf20Sopenharmony_ci sizeof(struct vmbus_channel_gpadl_torndown) }, 13958c2ecf20Sopenharmony_ci { CHANNELMSG_RELID_RELEASED, 0, NULL, 0}, 13968c2ecf20Sopenharmony_ci { CHANNELMSG_INITIATE_CONTACT, 0, NULL, 0}, 13978c2ecf20Sopenharmony_ci { CHANNELMSG_VERSION_RESPONSE, 1, vmbus_onversion_response, 13988c2ecf20Sopenharmony_ci sizeof(struct vmbus_channel_version_response)}, 13998c2ecf20Sopenharmony_ci { CHANNELMSG_UNLOAD, 0, NULL, 0}, 14008c2ecf20Sopenharmony_ci { CHANNELMSG_UNLOAD_RESPONSE, 1, vmbus_unload_response, 0}, 14018c2ecf20Sopenharmony_ci { CHANNELMSG_18, 0, NULL, 0}, 14028c2ecf20Sopenharmony_ci { CHANNELMSG_19, 0, NULL, 0}, 14038c2ecf20Sopenharmony_ci { CHANNELMSG_20, 0, NULL, 0}, 14048c2ecf20Sopenharmony_ci { CHANNELMSG_TL_CONNECT_REQUEST, 0, NULL, 0}, 14058c2ecf20Sopenharmony_ci { CHANNELMSG_MODIFYCHANNEL, 0, NULL, 0}, 14068c2ecf20Sopenharmony_ci { CHANNELMSG_TL_CONNECT_RESULT, 0, NULL, 0}, 14078c2ecf20Sopenharmony_ci}; 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci/* 14108c2ecf20Sopenharmony_ci * vmbus_onmessage - Handler for channel protocol messages. 14118c2ecf20Sopenharmony_ci * 14128c2ecf20Sopenharmony_ci * This is invoked in the vmbus worker thread context. 14138c2ecf20Sopenharmony_ci */ 14148c2ecf20Sopenharmony_civoid vmbus_onmessage(struct vmbus_channel_message_header *hdr) 14158c2ecf20Sopenharmony_ci{ 14168c2ecf20Sopenharmony_ci trace_vmbus_on_message(hdr); 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci /* 14198c2ecf20Sopenharmony_ci * vmbus_on_msg_dpc() makes sure the hdr->msgtype here can not go 14208c2ecf20Sopenharmony_ci * out of bound and the message_handler pointer can not be NULL. 14218c2ecf20Sopenharmony_ci */ 14228c2ecf20Sopenharmony_ci channel_message_table[hdr->msgtype].message_handler(hdr); 14238c2ecf20Sopenharmony_ci} 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci/* 14268c2ecf20Sopenharmony_ci * vmbus_request_offers - Send a request to get all our pending offers. 14278c2ecf20Sopenharmony_ci */ 14288c2ecf20Sopenharmony_ciint vmbus_request_offers(void) 14298c2ecf20Sopenharmony_ci{ 14308c2ecf20Sopenharmony_ci struct vmbus_channel_message_header *msg; 14318c2ecf20Sopenharmony_ci struct vmbus_channel_msginfo *msginfo; 14328c2ecf20Sopenharmony_ci int ret; 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci msginfo = kmalloc(sizeof(*msginfo) + 14358c2ecf20Sopenharmony_ci sizeof(struct vmbus_channel_message_header), 14368c2ecf20Sopenharmony_ci GFP_KERNEL); 14378c2ecf20Sopenharmony_ci if (!msginfo) 14388c2ecf20Sopenharmony_ci return -ENOMEM; 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_ci msg = (struct vmbus_channel_message_header *)msginfo->msg; 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci msg->msgtype = CHANNELMSG_REQUESTOFFERS; 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_message_header), 14458c2ecf20Sopenharmony_ci true); 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci trace_vmbus_request_offers(ret); 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci if (ret != 0) { 14508c2ecf20Sopenharmony_ci pr_err("Unable to request offers - %d\n", ret); 14518c2ecf20Sopenharmony_ci 14528c2ecf20Sopenharmony_ci goto cleanup; 14538c2ecf20Sopenharmony_ci } 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_cicleanup: 14568c2ecf20Sopenharmony_ci kfree(msginfo); 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci return ret; 14598c2ecf20Sopenharmony_ci} 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_cistatic void invoke_sc_cb(struct vmbus_channel *primary_channel) 14628c2ecf20Sopenharmony_ci{ 14638c2ecf20Sopenharmony_ci struct list_head *cur, *tmp; 14648c2ecf20Sopenharmony_ci struct vmbus_channel *cur_channel; 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci if (primary_channel->sc_creation_callback == NULL) 14678c2ecf20Sopenharmony_ci return; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci list_for_each_safe(cur, tmp, &primary_channel->sc_list) { 14708c2ecf20Sopenharmony_ci cur_channel = list_entry(cur, struct vmbus_channel, sc_list); 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci primary_channel->sc_creation_callback(cur_channel); 14738c2ecf20Sopenharmony_ci } 14748c2ecf20Sopenharmony_ci} 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_civoid vmbus_set_sc_create_callback(struct vmbus_channel *primary_channel, 14778c2ecf20Sopenharmony_ci void (*sc_cr_cb)(struct vmbus_channel *new_sc)) 14788c2ecf20Sopenharmony_ci{ 14798c2ecf20Sopenharmony_ci primary_channel->sc_creation_callback = sc_cr_cb; 14808c2ecf20Sopenharmony_ci} 14818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_set_sc_create_callback); 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_cibool vmbus_are_subchannels_present(struct vmbus_channel *primary) 14848c2ecf20Sopenharmony_ci{ 14858c2ecf20Sopenharmony_ci bool ret; 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci ret = !list_empty(&primary->sc_list); 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci if (ret) { 14908c2ecf20Sopenharmony_ci /* 14918c2ecf20Sopenharmony_ci * Invoke the callback on sub-channel creation. 14928c2ecf20Sopenharmony_ci * This will present a uniform interface to the 14938c2ecf20Sopenharmony_ci * clients. 14948c2ecf20Sopenharmony_ci */ 14958c2ecf20Sopenharmony_ci invoke_sc_cb(primary); 14968c2ecf20Sopenharmony_ci } 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci return ret; 14998c2ecf20Sopenharmony_ci} 15008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_are_subchannels_present); 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_civoid vmbus_set_chn_rescind_callback(struct vmbus_channel *channel, 15038c2ecf20Sopenharmony_ci void (*chn_rescind_cb)(struct vmbus_channel *)) 15048c2ecf20Sopenharmony_ci{ 15058c2ecf20Sopenharmony_ci channel->chn_rescind_callback = chn_rescind_cb; 15068c2ecf20Sopenharmony_ci} 15078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_set_chn_rescind_callback); 1508