162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2009, Microsoft Corporation.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Authors:
662306a36Sopenharmony_ci *   Haiyang Zhang <haiyangz@microsoft.com>
762306a36Sopenharmony_ci *   Hank Janssen  <hjanssen@microsoft.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/sched.h>
1462306a36Sopenharmony_ci#include <linux/wait.h>
1562306a36Sopenharmony_ci#include <linux/mm.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include <linux/list.h>
1862306a36Sopenharmony_ci#include <linux/module.h>
1962306a36Sopenharmony_ci#include <linux/completion.h>
2062306a36Sopenharmony_ci#include <linux/delay.h>
2162306a36Sopenharmony_ci#include <linux/cpu.h>
2262306a36Sopenharmony_ci#include <linux/hyperv.h>
2362306a36Sopenharmony_ci#include <asm/mshyperv.h>
2462306a36Sopenharmony_ci#include <linux/sched/isolation.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "hyperv_vmbus.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic void init_vp_index(struct vmbus_channel *channel);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ciconst struct vmbus_device vmbus_devs[] = {
3162306a36Sopenharmony_ci	/* IDE */
3262306a36Sopenharmony_ci	{ .dev_type = HV_IDE,
3362306a36Sopenharmony_ci	  HV_IDE_GUID,
3462306a36Sopenharmony_ci	  .perf_device = true,
3562306a36Sopenharmony_ci	  .allowed_in_isolated = false,
3662306a36Sopenharmony_ci	},
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	/* SCSI */
3962306a36Sopenharmony_ci	{ .dev_type = HV_SCSI,
4062306a36Sopenharmony_ci	  HV_SCSI_GUID,
4162306a36Sopenharmony_ci	  .perf_device = true,
4262306a36Sopenharmony_ci	  .allowed_in_isolated = true,
4362306a36Sopenharmony_ci	},
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	/* Fibre Channel */
4662306a36Sopenharmony_ci	{ .dev_type = HV_FC,
4762306a36Sopenharmony_ci	  HV_SYNTHFC_GUID,
4862306a36Sopenharmony_ci	  .perf_device = true,
4962306a36Sopenharmony_ci	  .allowed_in_isolated = false,
5062306a36Sopenharmony_ci	},
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	/* Synthetic NIC */
5362306a36Sopenharmony_ci	{ .dev_type = HV_NIC,
5462306a36Sopenharmony_ci	  HV_NIC_GUID,
5562306a36Sopenharmony_ci	  .perf_device = true,
5662306a36Sopenharmony_ci	  .allowed_in_isolated = true,
5762306a36Sopenharmony_ci	},
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	/* Network Direct */
6062306a36Sopenharmony_ci	{ .dev_type = HV_ND,
6162306a36Sopenharmony_ci	  HV_ND_GUID,
6262306a36Sopenharmony_ci	  .perf_device = true,
6362306a36Sopenharmony_ci	  .allowed_in_isolated = false,
6462306a36Sopenharmony_ci	},
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	/* PCIE */
6762306a36Sopenharmony_ci	{ .dev_type = HV_PCIE,
6862306a36Sopenharmony_ci	  HV_PCIE_GUID,
6962306a36Sopenharmony_ci	  .perf_device = false,
7062306a36Sopenharmony_ci	  .allowed_in_isolated = true,
7162306a36Sopenharmony_ci	},
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	/* Synthetic Frame Buffer */
7462306a36Sopenharmony_ci	{ .dev_type = HV_FB,
7562306a36Sopenharmony_ci	  HV_SYNTHVID_GUID,
7662306a36Sopenharmony_ci	  .perf_device = false,
7762306a36Sopenharmony_ci	  .allowed_in_isolated = false,
7862306a36Sopenharmony_ci	},
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	/* Synthetic Keyboard */
8162306a36Sopenharmony_ci	{ .dev_type = HV_KBD,
8262306a36Sopenharmony_ci	  HV_KBD_GUID,
8362306a36Sopenharmony_ci	  .perf_device = false,
8462306a36Sopenharmony_ci	  .allowed_in_isolated = false,
8562306a36Sopenharmony_ci	},
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	/* Synthetic MOUSE */
8862306a36Sopenharmony_ci	{ .dev_type = HV_MOUSE,
8962306a36Sopenharmony_ci	  HV_MOUSE_GUID,
9062306a36Sopenharmony_ci	  .perf_device = false,
9162306a36Sopenharmony_ci	  .allowed_in_isolated = false,
9262306a36Sopenharmony_ci	},
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/* KVP */
9562306a36Sopenharmony_ci	{ .dev_type = HV_KVP,
9662306a36Sopenharmony_ci	  HV_KVP_GUID,
9762306a36Sopenharmony_ci	  .perf_device = false,
9862306a36Sopenharmony_ci	  .allowed_in_isolated = false,
9962306a36Sopenharmony_ci	},
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	/* Time Synch */
10262306a36Sopenharmony_ci	{ .dev_type = HV_TS,
10362306a36Sopenharmony_ci	  HV_TS_GUID,
10462306a36Sopenharmony_ci	  .perf_device = false,
10562306a36Sopenharmony_ci	  .allowed_in_isolated = true,
10662306a36Sopenharmony_ci	},
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	/* Heartbeat */
10962306a36Sopenharmony_ci	{ .dev_type = HV_HB,
11062306a36Sopenharmony_ci	  HV_HEART_BEAT_GUID,
11162306a36Sopenharmony_ci	  .perf_device = false,
11262306a36Sopenharmony_ci	  .allowed_in_isolated = true,
11362306a36Sopenharmony_ci	},
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	/* Shutdown */
11662306a36Sopenharmony_ci	{ .dev_type = HV_SHUTDOWN,
11762306a36Sopenharmony_ci	  HV_SHUTDOWN_GUID,
11862306a36Sopenharmony_ci	  .perf_device = false,
11962306a36Sopenharmony_ci	  .allowed_in_isolated = true,
12062306a36Sopenharmony_ci	},
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	/* File copy */
12362306a36Sopenharmony_ci	{ .dev_type = HV_FCOPY,
12462306a36Sopenharmony_ci	  HV_FCOPY_GUID,
12562306a36Sopenharmony_ci	  .perf_device = false,
12662306a36Sopenharmony_ci	  .allowed_in_isolated = false,
12762306a36Sopenharmony_ci	},
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	/* Backup */
13062306a36Sopenharmony_ci	{ .dev_type = HV_BACKUP,
13162306a36Sopenharmony_ci	  HV_VSS_GUID,
13262306a36Sopenharmony_ci	  .perf_device = false,
13362306a36Sopenharmony_ci	  .allowed_in_isolated = false,
13462306a36Sopenharmony_ci	},
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	/* Dynamic Memory */
13762306a36Sopenharmony_ci	{ .dev_type = HV_DM,
13862306a36Sopenharmony_ci	  HV_DM_GUID,
13962306a36Sopenharmony_ci	  .perf_device = false,
14062306a36Sopenharmony_ci	  .allowed_in_isolated = false,
14162306a36Sopenharmony_ci	},
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	/* Unknown GUID */
14462306a36Sopenharmony_ci	{ .dev_type = HV_UNKNOWN,
14562306a36Sopenharmony_ci	  .perf_device = false,
14662306a36Sopenharmony_ci	  .allowed_in_isolated = false,
14762306a36Sopenharmony_ci	},
14862306a36Sopenharmony_ci};
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic const struct {
15162306a36Sopenharmony_ci	guid_t guid;
15262306a36Sopenharmony_ci} vmbus_unsupported_devs[] = {
15362306a36Sopenharmony_ci	{ HV_AVMA1_GUID },
15462306a36Sopenharmony_ci	{ HV_AVMA2_GUID },
15562306a36Sopenharmony_ci	{ HV_RDV_GUID	},
15662306a36Sopenharmony_ci	{ HV_IMC_GUID	},
15762306a36Sopenharmony_ci};
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci/*
16062306a36Sopenharmony_ci * The rescinded channel may be blocked waiting for a response from the host;
16162306a36Sopenharmony_ci * take care of that.
16262306a36Sopenharmony_ci */
16362306a36Sopenharmony_cistatic void vmbus_rescind_cleanup(struct vmbus_channel *channel)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	struct vmbus_channel_msginfo *msginfo;
16662306a36Sopenharmony_ci	unsigned long flags;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
17062306a36Sopenharmony_ci	channel->rescind = true;
17162306a36Sopenharmony_ci	list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list,
17262306a36Sopenharmony_ci				msglistentry) {
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci		if (msginfo->waiting_channel == channel) {
17562306a36Sopenharmony_ci			complete(&msginfo->waitevent);
17662306a36Sopenharmony_ci			break;
17762306a36Sopenharmony_ci		}
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci	spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic bool is_unsupported_vmbus_devs(const guid_t *guid)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	int i;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vmbus_unsupported_devs); i++)
18762306a36Sopenharmony_ci		if (guid_equal(guid, &vmbus_unsupported_devs[i].guid))
18862306a36Sopenharmony_ci			return true;
18962306a36Sopenharmony_ci	return false;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic u16 hv_get_dev_type(const struct vmbus_channel *channel)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	const guid_t *guid = &channel->offermsg.offer.if_type;
19562306a36Sopenharmony_ci	u16 i;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	if (is_hvsock_channel(channel) || is_unsupported_vmbus_devs(guid))
19862306a36Sopenharmony_ci		return HV_UNKNOWN;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	for (i = HV_IDE; i < HV_UNKNOWN; i++) {
20162306a36Sopenharmony_ci		if (guid_equal(guid, &vmbus_devs[i].guid))
20262306a36Sopenharmony_ci			return i;
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci	pr_info("Unknown GUID: %pUl\n", guid);
20562306a36Sopenharmony_ci	return i;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci/**
20962306a36Sopenharmony_ci * vmbus_prep_negotiate_resp() - Create default response for Negotiate message
21062306a36Sopenharmony_ci * @icmsghdrp: Pointer to msg header structure
21162306a36Sopenharmony_ci * @buf: Raw buffer channel data
21262306a36Sopenharmony_ci * @buflen: Length of the raw buffer channel data.
21362306a36Sopenharmony_ci * @fw_version: The framework versions we can support.
21462306a36Sopenharmony_ci * @fw_vercnt: The size of @fw_version.
21562306a36Sopenharmony_ci * @srv_version: The service versions we can support.
21662306a36Sopenharmony_ci * @srv_vercnt: The size of @srv_version.
21762306a36Sopenharmony_ci * @nego_fw_version: The selected framework version.
21862306a36Sopenharmony_ci * @nego_srv_version: The selected service version.
21962306a36Sopenharmony_ci *
22062306a36Sopenharmony_ci * Note: Versions are given in decreasing order.
22162306a36Sopenharmony_ci *
22262306a36Sopenharmony_ci * Set up and fill in default negotiate response message.
22362306a36Sopenharmony_ci * Mainly used by Hyper-V drivers.
22462306a36Sopenharmony_ci */
22562306a36Sopenharmony_cibool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, u8 *buf,
22662306a36Sopenharmony_ci				u32 buflen, const int *fw_version, int fw_vercnt,
22762306a36Sopenharmony_ci				const int *srv_version, int srv_vercnt,
22862306a36Sopenharmony_ci				int *nego_fw_version, int *nego_srv_version)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	int icframe_major, icframe_minor;
23162306a36Sopenharmony_ci	int icmsg_major, icmsg_minor;
23262306a36Sopenharmony_ci	int fw_major, fw_minor;
23362306a36Sopenharmony_ci	int srv_major, srv_minor;
23462306a36Sopenharmony_ci	int i, j;
23562306a36Sopenharmony_ci	bool found_match = false;
23662306a36Sopenharmony_ci	struct icmsg_negotiate *negop;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/* Check that there's enough space for icframe_vercnt, icmsg_vercnt */
23962306a36Sopenharmony_ci	if (buflen < ICMSG_HDR + offsetof(struct icmsg_negotiate, reserved)) {
24062306a36Sopenharmony_ci		pr_err_ratelimited("Invalid icmsg negotiate\n");
24162306a36Sopenharmony_ci		return false;
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	icmsghdrp->icmsgsize = 0x10;
24562306a36Sopenharmony_ci	negop = (struct icmsg_negotiate *)&buf[ICMSG_HDR];
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	icframe_major = negop->icframe_vercnt;
24862306a36Sopenharmony_ci	icframe_minor = 0;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	icmsg_major = negop->icmsg_vercnt;
25162306a36Sopenharmony_ci	icmsg_minor = 0;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	/* Validate negop packet */
25462306a36Sopenharmony_ci	if (icframe_major > IC_VERSION_NEGOTIATION_MAX_VER_COUNT ||
25562306a36Sopenharmony_ci	    icmsg_major > IC_VERSION_NEGOTIATION_MAX_VER_COUNT ||
25662306a36Sopenharmony_ci	    ICMSG_NEGOTIATE_PKT_SIZE(icframe_major, icmsg_major) > buflen) {
25762306a36Sopenharmony_ci		pr_err_ratelimited("Invalid icmsg negotiate - icframe_major: %u, icmsg_major: %u\n",
25862306a36Sopenharmony_ci				   icframe_major, icmsg_major);
25962306a36Sopenharmony_ci		goto fw_error;
26062306a36Sopenharmony_ci	}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	/*
26362306a36Sopenharmony_ci	 * Select the framework version number we will
26462306a36Sopenharmony_ci	 * support.
26562306a36Sopenharmony_ci	 */
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	for (i = 0; i < fw_vercnt; i++) {
26862306a36Sopenharmony_ci		fw_major = (fw_version[i] >> 16);
26962306a36Sopenharmony_ci		fw_minor = (fw_version[i] & 0xFFFF);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci		for (j = 0; j < negop->icframe_vercnt; j++) {
27262306a36Sopenharmony_ci			if ((negop->icversion_data[j].major == fw_major) &&
27362306a36Sopenharmony_ci			    (negop->icversion_data[j].minor == fw_minor)) {
27462306a36Sopenharmony_ci				icframe_major = negop->icversion_data[j].major;
27562306a36Sopenharmony_ci				icframe_minor = negop->icversion_data[j].minor;
27662306a36Sopenharmony_ci				found_match = true;
27762306a36Sopenharmony_ci				break;
27862306a36Sopenharmony_ci			}
27962306a36Sopenharmony_ci		}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci		if (found_match)
28262306a36Sopenharmony_ci			break;
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	if (!found_match)
28662306a36Sopenharmony_ci		goto fw_error;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	found_match = false;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	for (i = 0; i < srv_vercnt; i++) {
29162306a36Sopenharmony_ci		srv_major = (srv_version[i] >> 16);
29262306a36Sopenharmony_ci		srv_minor = (srv_version[i] & 0xFFFF);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		for (j = negop->icframe_vercnt;
29562306a36Sopenharmony_ci			(j < negop->icframe_vercnt + negop->icmsg_vercnt);
29662306a36Sopenharmony_ci			j++) {
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci			if ((negop->icversion_data[j].major == srv_major) &&
29962306a36Sopenharmony_ci				(negop->icversion_data[j].minor == srv_minor)) {
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci				icmsg_major = negop->icversion_data[j].major;
30262306a36Sopenharmony_ci				icmsg_minor = negop->icversion_data[j].minor;
30362306a36Sopenharmony_ci				found_match = true;
30462306a36Sopenharmony_ci				break;
30562306a36Sopenharmony_ci			}
30662306a36Sopenharmony_ci		}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci		if (found_match)
30962306a36Sopenharmony_ci			break;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	/*
31362306a36Sopenharmony_ci	 * Respond with the framework and service
31462306a36Sopenharmony_ci	 * version numbers we can support.
31562306a36Sopenharmony_ci	 */
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cifw_error:
31862306a36Sopenharmony_ci	if (!found_match) {
31962306a36Sopenharmony_ci		negop->icframe_vercnt = 0;
32062306a36Sopenharmony_ci		negop->icmsg_vercnt = 0;
32162306a36Sopenharmony_ci	} else {
32262306a36Sopenharmony_ci		negop->icframe_vercnt = 1;
32362306a36Sopenharmony_ci		negop->icmsg_vercnt = 1;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	if (nego_fw_version)
32762306a36Sopenharmony_ci		*nego_fw_version = (icframe_major << 16) | icframe_minor;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	if (nego_srv_version)
33062306a36Sopenharmony_ci		*nego_srv_version = (icmsg_major << 16) | icmsg_minor;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	negop->icversion_data[0].major = icframe_major;
33362306a36Sopenharmony_ci	negop->icversion_data[0].minor = icframe_minor;
33462306a36Sopenharmony_ci	negop->icversion_data[1].major = icmsg_major;
33562306a36Sopenharmony_ci	negop->icversion_data[1].minor = icmsg_minor;
33662306a36Sopenharmony_ci	return found_match;
33762306a36Sopenharmony_ci}
33862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci/*
34162306a36Sopenharmony_ci * alloc_channel - Allocate and initialize a vmbus channel object
34262306a36Sopenharmony_ci */
34362306a36Sopenharmony_cistatic struct vmbus_channel *alloc_channel(void)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	struct vmbus_channel *channel;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	channel = kzalloc(sizeof(*channel), GFP_ATOMIC);
34862306a36Sopenharmony_ci	if (!channel)
34962306a36Sopenharmony_ci		return NULL;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	spin_lock_init(&channel->sched_lock);
35262306a36Sopenharmony_ci	init_completion(&channel->rescind_event);
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	INIT_LIST_HEAD(&channel->sc_list);
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	tasklet_init(&channel->callback_event,
35762306a36Sopenharmony_ci		     vmbus_on_event, (unsigned long)channel);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	hv_ringbuffer_pre_init(channel);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	return channel;
36262306a36Sopenharmony_ci}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci/*
36562306a36Sopenharmony_ci * free_channel - Release the resources used by the vmbus channel object
36662306a36Sopenharmony_ci */
36762306a36Sopenharmony_cistatic void free_channel(struct vmbus_channel *channel)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	tasklet_kill(&channel->callback_event);
37062306a36Sopenharmony_ci	vmbus_remove_channel_attr_group(channel);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	kobject_put(&channel->kobj);
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_civoid vmbus_channel_map_relid(struct vmbus_channel *channel)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	if (WARN_ON(channel->offermsg.child_relid >= MAX_CHANNEL_RELIDS))
37862306a36Sopenharmony_ci		return;
37962306a36Sopenharmony_ci	/*
38062306a36Sopenharmony_ci	 * The mapping of the channel's relid is visible from the CPUs that
38162306a36Sopenharmony_ci	 * execute vmbus_chan_sched() by the time that vmbus_chan_sched() will
38262306a36Sopenharmony_ci	 * execute:
38362306a36Sopenharmony_ci	 *
38462306a36Sopenharmony_ci	 *  (a) In the "normal (i.e., not resuming from hibernation)" path,
38562306a36Sopenharmony_ci	 *      the full barrier in virt_store_mb() guarantees that the store
38662306a36Sopenharmony_ci	 *      is propagated to all CPUs before the add_channel_work work
38762306a36Sopenharmony_ci	 *      is queued.  In turn, add_channel_work is queued before the
38862306a36Sopenharmony_ci	 *      channel's ring buffer is allocated/initialized and the
38962306a36Sopenharmony_ci	 *      OPENCHANNEL message for the channel is sent in vmbus_open().
39062306a36Sopenharmony_ci	 *      Hyper-V won't start sending the interrupts for the channel
39162306a36Sopenharmony_ci	 *      before the OPENCHANNEL message is acked.  The memory barrier
39262306a36Sopenharmony_ci	 *      in vmbus_chan_sched() -> sync_test_and_clear_bit() ensures
39362306a36Sopenharmony_ci	 *      that vmbus_chan_sched() must find the channel's relid in
39462306a36Sopenharmony_ci	 *      recv_int_page before retrieving the channel pointer from the
39562306a36Sopenharmony_ci	 *      array of channels.
39662306a36Sopenharmony_ci	 *
39762306a36Sopenharmony_ci	 *  (b) In the "resuming from hibernation" path, the virt_store_mb()
39862306a36Sopenharmony_ci	 *      guarantees that the store is propagated to all CPUs before
39962306a36Sopenharmony_ci	 *      the VMBus connection is marked as ready for the resume event
40062306a36Sopenharmony_ci	 *      (cf. check_ready_for_resume_event()).  The interrupt handler
40162306a36Sopenharmony_ci	 *      of the VMBus driver and vmbus_chan_sched() can not run before
40262306a36Sopenharmony_ci	 *      vmbus_bus_resume() has completed execution (cf. resume_noirq).
40362306a36Sopenharmony_ci	 */
40462306a36Sopenharmony_ci	virt_store_mb(
40562306a36Sopenharmony_ci		vmbus_connection.channels[channel->offermsg.child_relid],
40662306a36Sopenharmony_ci		channel);
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_civoid vmbus_channel_unmap_relid(struct vmbus_channel *channel)
41062306a36Sopenharmony_ci{
41162306a36Sopenharmony_ci	if (WARN_ON(channel->offermsg.child_relid >= MAX_CHANNEL_RELIDS))
41262306a36Sopenharmony_ci		return;
41362306a36Sopenharmony_ci	WRITE_ONCE(
41462306a36Sopenharmony_ci		vmbus_connection.channels[channel->offermsg.child_relid],
41562306a36Sopenharmony_ci		NULL);
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic void vmbus_release_relid(u32 relid)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	struct vmbus_channel_relid_released msg;
42162306a36Sopenharmony_ci	int ret;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	memset(&msg, 0, sizeof(struct vmbus_channel_relid_released));
42462306a36Sopenharmony_ci	msg.child_relid = relid;
42562306a36Sopenharmony_ci	msg.header.msgtype = CHANNELMSG_RELID_RELEASED;
42662306a36Sopenharmony_ci	ret = vmbus_post_msg(&msg, sizeof(struct vmbus_channel_relid_released),
42762306a36Sopenharmony_ci			     true);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	trace_vmbus_release_relid(&msg, ret);
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_civoid hv_process_channel_removal(struct vmbus_channel *channel)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	lockdep_assert_held(&vmbus_connection.channel_mutex);
43562306a36Sopenharmony_ci	BUG_ON(!channel->rescind);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	/*
43862306a36Sopenharmony_ci	 * hv_process_channel_removal() could find INVALID_RELID only for
43962306a36Sopenharmony_ci	 * hv_sock channels.  See the inline comments in vmbus_onoffer().
44062306a36Sopenharmony_ci	 */
44162306a36Sopenharmony_ci	WARN_ON(channel->offermsg.child_relid == INVALID_RELID &&
44262306a36Sopenharmony_ci		!is_hvsock_channel(channel));
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	/*
44562306a36Sopenharmony_ci	 * Upon suspend, an in-use hv_sock channel is removed from the array of
44662306a36Sopenharmony_ci	 * channels and the relid is invalidated.  After hibernation, when the
44762306a36Sopenharmony_ci	 * user-space application destroys the channel, it's unnecessary and
44862306a36Sopenharmony_ci	 * unsafe to remove the channel from the array of channels.  See also
44962306a36Sopenharmony_ci	 * the inline comments before the call of vmbus_release_relid() below.
45062306a36Sopenharmony_ci	 */
45162306a36Sopenharmony_ci	if (channel->offermsg.child_relid != INVALID_RELID)
45262306a36Sopenharmony_ci		vmbus_channel_unmap_relid(channel);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	if (channel->primary_channel == NULL)
45562306a36Sopenharmony_ci		list_del(&channel->listentry);
45662306a36Sopenharmony_ci	else
45762306a36Sopenharmony_ci		list_del(&channel->sc_list);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	/*
46062306a36Sopenharmony_ci	 * If this is a "perf" channel, updates the hv_numa_map[] masks so that
46162306a36Sopenharmony_ci	 * init_vp_index() can (re-)use the CPU.
46262306a36Sopenharmony_ci	 */
46362306a36Sopenharmony_ci	if (hv_is_perf_channel(channel))
46462306a36Sopenharmony_ci		hv_clear_allocated_cpu(channel->target_cpu);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	/*
46762306a36Sopenharmony_ci	 * Upon suspend, an in-use hv_sock channel is marked as "rescinded" and
46862306a36Sopenharmony_ci	 * the relid is invalidated; after hibernation, when the user-space app
46962306a36Sopenharmony_ci	 * destroys the channel, the relid is INVALID_RELID, and in this case
47062306a36Sopenharmony_ci	 * it's unnecessary and unsafe to release the old relid, since the same
47162306a36Sopenharmony_ci	 * relid can refer to a completely different channel now.
47262306a36Sopenharmony_ci	 */
47362306a36Sopenharmony_ci	if (channel->offermsg.child_relid != INVALID_RELID)
47462306a36Sopenharmony_ci		vmbus_release_relid(channel->offermsg.child_relid);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	free_channel(channel);
47762306a36Sopenharmony_ci}
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_civoid vmbus_free_channels(void)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	struct vmbus_channel *channel, *tmp;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	list_for_each_entry_safe(channel, tmp, &vmbus_connection.chn_list,
48462306a36Sopenharmony_ci		listentry) {
48562306a36Sopenharmony_ci		/* hv_process_channel_removal() needs this */
48662306a36Sopenharmony_ci		channel->rescind = true;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci		vmbus_device_unregister(channel->device_obj);
48962306a36Sopenharmony_ci	}
49062306a36Sopenharmony_ci}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci/* Note: the function can run concurrently for primary/sub channels. */
49362306a36Sopenharmony_cistatic void vmbus_add_channel_work(struct work_struct *work)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	struct vmbus_channel *newchannel =
49662306a36Sopenharmony_ci		container_of(work, struct vmbus_channel, add_channel_work);
49762306a36Sopenharmony_ci	struct vmbus_channel *primary_channel = newchannel->primary_channel;
49862306a36Sopenharmony_ci	int ret;
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci	/*
50162306a36Sopenharmony_ci	 * This state is used to indicate a successful open
50262306a36Sopenharmony_ci	 * so that when we do close the channel normally, we
50362306a36Sopenharmony_ci	 * can cleanup properly.
50462306a36Sopenharmony_ci	 */
50562306a36Sopenharmony_ci	newchannel->state = CHANNEL_OPEN_STATE;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	if (primary_channel != NULL) {
50862306a36Sopenharmony_ci		/* newchannel is a sub-channel. */
50962306a36Sopenharmony_ci		struct hv_device *dev = primary_channel->device_obj;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci		if (vmbus_add_channel_kobj(dev, newchannel))
51262306a36Sopenharmony_ci			goto err_deq_chan;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci		if (primary_channel->sc_creation_callback != NULL)
51562306a36Sopenharmony_ci			primary_channel->sc_creation_callback(newchannel);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		newchannel->probe_done = true;
51862306a36Sopenharmony_ci		return;
51962306a36Sopenharmony_ci	}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	/*
52262306a36Sopenharmony_ci	 * Start the process of binding the primary channel to the driver
52362306a36Sopenharmony_ci	 */
52462306a36Sopenharmony_ci	newchannel->device_obj = vmbus_device_create(
52562306a36Sopenharmony_ci		&newchannel->offermsg.offer.if_type,
52662306a36Sopenharmony_ci		&newchannel->offermsg.offer.if_instance,
52762306a36Sopenharmony_ci		newchannel);
52862306a36Sopenharmony_ci	if (!newchannel->device_obj)
52962306a36Sopenharmony_ci		goto err_deq_chan;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	newchannel->device_obj->device_id = newchannel->device_id;
53262306a36Sopenharmony_ci	/*
53362306a36Sopenharmony_ci	 * Add the new device to the bus. This will kick off device-driver
53462306a36Sopenharmony_ci	 * binding which eventually invokes the device driver's AddDevice()
53562306a36Sopenharmony_ci	 * method.
53662306a36Sopenharmony_ci	 *
53762306a36Sopenharmony_ci	 * If vmbus_device_register() fails, the 'device_obj' is freed in
53862306a36Sopenharmony_ci	 * vmbus_device_release() as called by device_unregister() in the
53962306a36Sopenharmony_ci	 * error path of vmbus_device_register(). In the outside error
54062306a36Sopenharmony_ci	 * path, there's no need to free it.
54162306a36Sopenharmony_ci	 */
54262306a36Sopenharmony_ci	ret = vmbus_device_register(newchannel->device_obj);
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	if (ret != 0) {
54562306a36Sopenharmony_ci		pr_err("unable to add child device object (relid %d)\n",
54662306a36Sopenharmony_ci			newchannel->offermsg.child_relid);
54762306a36Sopenharmony_ci		goto err_deq_chan;
54862306a36Sopenharmony_ci	}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	newchannel->probe_done = true;
55162306a36Sopenharmony_ci	return;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_cierr_deq_chan:
55462306a36Sopenharmony_ci	mutex_lock(&vmbus_connection.channel_mutex);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	/*
55762306a36Sopenharmony_ci	 * We need to set the flag, otherwise
55862306a36Sopenharmony_ci	 * vmbus_onoffer_rescind() can be blocked.
55962306a36Sopenharmony_ci	 */
56062306a36Sopenharmony_ci	newchannel->probe_done = true;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	if (primary_channel == NULL)
56362306a36Sopenharmony_ci		list_del(&newchannel->listentry);
56462306a36Sopenharmony_ci	else
56562306a36Sopenharmony_ci		list_del(&newchannel->sc_list);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	/* vmbus_process_offer() has mapped the channel. */
56862306a36Sopenharmony_ci	vmbus_channel_unmap_relid(newchannel);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	mutex_unlock(&vmbus_connection.channel_mutex);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	vmbus_release_relid(newchannel->offermsg.child_relid);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	free_channel(newchannel);
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci/*
57862306a36Sopenharmony_ci * vmbus_process_offer - Process the offer by creating a channel/device
57962306a36Sopenharmony_ci * associated with this offer
58062306a36Sopenharmony_ci */
58162306a36Sopenharmony_cistatic void vmbus_process_offer(struct vmbus_channel *newchannel)
58262306a36Sopenharmony_ci{
58362306a36Sopenharmony_ci	struct vmbus_channel *channel;
58462306a36Sopenharmony_ci	struct workqueue_struct *wq;
58562306a36Sopenharmony_ci	bool fnew = true;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	/*
58862306a36Sopenharmony_ci	 * Synchronize vmbus_process_offer() and CPU hotplugging:
58962306a36Sopenharmony_ci	 *
59062306a36Sopenharmony_ci	 * CPU1				CPU2
59162306a36Sopenharmony_ci	 *
59262306a36Sopenharmony_ci	 * [vmbus_process_offer()]	[Hot removal of the CPU]
59362306a36Sopenharmony_ci	 *
59462306a36Sopenharmony_ci	 * CPU_READ_LOCK		CPUS_WRITE_LOCK
59562306a36Sopenharmony_ci	 * LOAD cpu_online_mask		SEARCH chn_list
59662306a36Sopenharmony_ci	 * STORE target_cpu		LOAD target_cpu
59762306a36Sopenharmony_ci	 * INSERT chn_list		STORE cpu_online_mask
59862306a36Sopenharmony_ci	 * CPUS_READ_UNLOCK		CPUS_WRITE_UNLOCK
59962306a36Sopenharmony_ci	 *
60062306a36Sopenharmony_ci	 * Forbids: CPU1's LOAD from *not* seing CPU2's STORE &&
60162306a36Sopenharmony_ci	 *              CPU2's SEARCH from *not* seeing CPU1's INSERT
60262306a36Sopenharmony_ci	 *
60362306a36Sopenharmony_ci	 * Forbids: CPU2's SEARCH from seeing CPU1's INSERT &&
60462306a36Sopenharmony_ci	 *              CPU2's LOAD from *not* seing CPU1's STORE
60562306a36Sopenharmony_ci	 */
60662306a36Sopenharmony_ci	cpus_read_lock();
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	/*
60962306a36Sopenharmony_ci	 * Serializes the modifications of the chn_list list as well as
61062306a36Sopenharmony_ci	 * the accesses to next_numa_node_id in init_vp_index().
61162306a36Sopenharmony_ci	 */
61262306a36Sopenharmony_ci	mutex_lock(&vmbus_connection.channel_mutex);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
61562306a36Sopenharmony_ci		if (guid_equal(&channel->offermsg.offer.if_type,
61662306a36Sopenharmony_ci			       &newchannel->offermsg.offer.if_type) &&
61762306a36Sopenharmony_ci		    guid_equal(&channel->offermsg.offer.if_instance,
61862306a36Sopenharmony_ci			       &newchannel->offermsg.offer.if_instance)) {
61962306a36Sopenharmony_ci			fnew = false;
62062306a36Sopenharmony_ci			newchannel->primary_channel = channel;
62162306a36Sopenharmony_ci			break;
62262306a36Sopenharmony_ci		}
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	init_vp_index(newchannel);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	/* Remember the channels that should be cleaned up upon suspend. */
62862306a36Sopenharmony_ci	if (is_hvsock_channel(newchannel) || is_sub_channel(newchannel))
62962306a36Sopenharmony_ci		atomic_inc(&vmbus_connection.nr_chan_close_on_suspend);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	/*
63262306a36Sopenharmony_ci	 * Now that we have acquired the channel_mutex,
63362306a36Sopenharmony_ci	 * we can release the potentially racing rescind thread.
63462306a36Sopenharmony_ci	 */
63562306a36Sopenharmony_ci	atomic_dec(&vmbus_connection.offer_in_progress);
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	if (fnew) {
63862306a36Sopenharmony_ci		list_add_tail(&newchannel->listentry,
63962306a36Sopenharmony_ci			      &vmbus_connection.chn_list);
64062306a36Sopenharmony_ci	} else {
64162306a36Sopenharmony_ci		/*
64262306a36Sopenharmony_ci		 * Check to see if this is a valid sub-channel.
64362306a36Sopenharmony_ci		 */
64462306a36Sopenharmony_ci		if (newchannel->offermsg.offer.sub_channel_index == 0) {
64562306a36Sopenharmony_ci			mutex_unlock(&vmbus_connection.channel_mutex);
64662306a36Sopenharmony_ci			cpus_read_unlock();
64762306a36Sopenharmony_ci			/*
64862306a36Sopenharmony_ci			 * Don't call free_channel(), because newchannel->kobj
64962306a36Sopenharmony_ci			 * is not initialized yet.
65062306a36Sopenharmony_ci			 */
65162306a36Sopenharmony_ci			kfree(newchannel);
65262306a36Sopenharmony_ci			WARN_ON_ONCE(1);
65362306a36Sopenharmony_ci			return;
65462306a36Sopenharmony_ci		}
65562306a36Sopenharmony_ci		/*
65662306a36Sopenharmony_ci		 * Process the sub-channel.
65762306a36Sopenharmony_ci		 */
65862306a36Sopenharmony_ci		list_add_tail(&newchannel->sc_list, &channel->sc_list);
65962306a36Sopenharmony_ci	}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	vmbus_channel_map_relid(newchannel);
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	mutex_unlock(&vmbus_connection.channel_mutex);
66462306a36Sopenharmony_ci	cpus_read_unlock();
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	/*
66762306a36Sopenharmony_ci	 * vmbus_process_offer() mustn't call channel->sc_creation_callback()
66862306a36Sopenharmony_ci	 * directly for sub-channels, because sc_creation_callback() ->
66962306a36Sopenharmony_ci	 * vmbus_open() may never get the host's response to the
67062306a36Sopenharmony_ci	 * OPEN_CHANNEL message (the host may rescind a channel at any time,
67162306a36Sopenharmony_ci	 * e.g. in the case of hot removing a NIC), and vmbus_onoffer_rescind()
67262306a36Sopenharmony_ci	 * may not wake up the vmbus_open() as it's blocked due to a non-zero
67362306a36Sopenharmony_ci	 * vmbus_connection.offer_in_progress, and finally we have a deadlock.
67462306a36Sopenharmony_ci	 *
67562306a36Sopenharmony_ci	 * The above is also true for primary channels, if the related device
67662306a36Sopenharmony_ci	 * drivers use sync probing mode by default.
67762306a36Sopenharmony_ci	 *
67862306a36Sopenharmony_ci	 * And, usually the handling of primary channels and sub-channels can
67962306a36Sopenharmony_ci	 * depend on each other, so we should offload them to different
68062306a36Sopenharmony_ci	 * workqueues to avoid possible deadlock, e.g. in sync-probing mode,
68162306a36Sopenharmony_ci	 * NIC1's netvsc_subchan_work() can race with NIC2's netvsc_probe() ->
68262306a36Sopenharmony_ci	 * rtnl_lock(), and causes deadlock: the former gets the rtnl_lock
68362306a36Sopenharmony_ci	 * and waits for all the sub-channels to appear, but the latter
68462306a36Sopenharmony_ci	 * can't get the rtnl_lock and this blocks the handling of
68562306a36Sopenharmony_ci	 * sub-channels.
68662306a36Sopenharmony_ci	 */
68762306a36Sopenharmony_ci	INIT_WORK(&newchannel->add_channel_work, vmbus_add_channel_work);
68862306a36Sopenharmony_ci	wq = fnew ? vmbus_connection.handle_primary_chan_wq :
68962306a36Sopenharmony_ci		    vmbus_connection.handle_sub_chan_wq;
69062306a36Sopenharmony_ci	queue_work(wq, &newchannel->add_channel_work);
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci/*
69462306a36Sopenharmony_ci * Check if CPUs used by other channels of the same device.
69562306a36Sopenharmony_ci * It should only be called by init_vp_index().
69662306a36Sopenharmony_ci */
69762306a36Sopenharmony_cistatic bool hv_cpuself_used(u32 cpu, struct vmbus_channel *chn)
69862306a36Sopenharmony_ci{
69962306a36Sopenharmony_ci	struct vmbus_channel *primary = chn->primary_channel;
70062306a36Sopenharmony_ci	struct vmbus_channel *sc;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	lockdep_assert_held(&vmbus_connection.channel_mutex);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	if (!primary)
70562306a36Sopenharmony_ci		return false;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	if (primary->target_cpu == cpu)
70862306a36Sopenharmony_ci		return true;
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	list_for_each_entry(sc, &primary->sc_list, sc_list)
71162306a36Sopenharmony_ci		if (sc != chn && sc->target_cpu == cpu)
71262306a36Sopenharmony_ci			return true;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	return false;
71562306a36Sopenharmony_ci}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci/*
71862306a36Sopenharmony_ci * We use this state to statically distribute the channel interrupt load.
71962306a36Sopenharmony_ci */
72062306a36Sopenharmony_cistatic int next_numa_node_id;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci/*
72362306a36Sopenharmony_ci * We can statically distribute the incoming channel interrupt load
72462306a36Sopenharmony_ci * by binding a channel to VCPU.
72562306a36Sopenharmony_ci *
72662306a36Sopenharmony_ci * For non-performance critical channels we assign the VMBUS_CONNECT_CPU.
72762306a36Sopenharmony_ci * Performance critical channels will be distributed evenly among all
72862306a36Sopenharmony_ci * the available NUMA nodes.  Once the node is assigned, we will assign
72962306a36Sopenharmony_ci * the CPU based on a simple round robin scheme.
73062306a36Sopenharmony_ci */
73162306a36Sopenharmony_cistatic void init_vp_index(struct vmbus_channel *channel)
73262306a36Sopenharmony_ci{
73362306a36Sopenharmony_ci	bool perf_chn = hv_is_perf_channel(channel);
73462306a36Sopenharmony_ci	u32 i, ncpu = num_online_cpus();
73562306a36Sopenharmony_ci	cpumask_var_t available_mask;
73662306a36Sopenharmony_ci	struct cpumask *allocated_mask;
73762306a36Sopenharmony_ci	const struct cpumask *hk_mask = housekeeping_cpumask(HK_TYPE_MANAGED_IRQ);
73862306a36Sopenharmony_ci	u32 target_cpu;
73962306a36Sopenharmony_ci	int numa_node;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	if (!perf_chn ||
74262306a36Sopenharmony_ci	    !alloc_cpumask_var(&available_mask, GFP_KERNEL) ||
74362306a36Sopenharmony_ci	    cpumask_empty(hk_mask)) {
74462306a36Sopenharmony_ci		/*
74562306a36Sopenharmony_ci		 * If the channel is not a performance critical
74662306a36Sopenharmony_ci		 * channel, bind it to VMBUS_CONNECT_CPU.
74762306a36Sopenharmony_ci		 * In case alloc_cpumask_var() fails, bind it to
74862306a36Sopenharmony_ci		 * VMBUS_CONNECT_CPU.
74962306a36Sopenharmony_ci		 * If all the cpus are isolated, bind it to
75062306a36Sopenharmony_ci		 * VMBUS_CONNECT_CPU.
75162306a36Sopenharmony_ci		 */
75262306a36Sopenharmony_ci		channel->target_cpu = VMBUS_CONNECT_CPU;
75362306a36Sopenharmony_ci		if (perf_chn)
75462306a36Sopenharmony_ci			hv_set_allocated_cpu(VMBUS_CONNECT_CPU);
75562306a36Sopenharmony_ci		return;
75662306a36Sopenharmony_ci	}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	for (i = 1; i <= ncpu + 1; i++) {
75962306a36Sopenharmony_ci		while (true) {
76062306a36Sopenharmony_ci			numa_node = next_numa_node_id++;
76162306a36Sopenharmony_ci			if (numa_node == nr_node_ids) {
76262306a36Sopenharmony_ci				next_numa_node_id = 0;
76362306a36Sopenharmony_ci				continue;
76462306a36Sopenharmony_ci			}
76562306a36Sopenharmony_ci			if (cpumask_empty(cpumask_of_node(numa_node)))
76662306a36Sopenharmony_ci				continue;
76762306a36Sopenharmony_ci			break;
76862306a36Sopenharmony_ci		}
76962306a36Sopenharmony_ci		allocated_mask = &hv_context.hv_numa_map[numa_node];
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ciretry:
77262306a36Sopenharmony_ci		cpumask_xor(available_mask, allocated_mask, cpumask_of_node(numa_node));
77362306a36Sopenharmony_ci		cpumask_and(available_mask, available_mask, hk_mask);
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci		if (cpumask_empty(available_mask)) {
77662306a36Sopenharmony_ci			/*
77762306a36Sopenharmony_ci			 * We have cycled through all the CPUs in the node;
77862306a36Sopenharmony_ci			 * reset the allocated map.
77962306a36Sopenharmony_ci			 */
78062306a36Sopenharmony_ci			cpumask_clear(allocated_mask);
78162306a36Sopenharmony_ci			goto retry;
78262306a36Sopenharmony_ci		}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci		target_cpu = cpumask_first(available_mask);
78562306a36Sopenharmony_ci		cpumask_set_cpu(target_cpu, allocated_mask);
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci		if (channel->offermsg.offer.sub_channel_index >= ncpu ||
78862306a36Sopenharmony_ci		    i > ncpu || !hv_cpuself_used(target_cpu, channel))
78962306a36Sopenharmony_ci			break;
79062306a36Sopenharmony_ci	}
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	channel->target_cpu = target_cpu;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	free_cpumask_var(available_mask);
79562306a36Sopenharmony_ci}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci#define UNLOAD_DELAY_UNIT_MS	10		/* 10 milliseconds */
79862306a36Sopenharmony_ci#define UNLOAD_WAIT_MS		(100*1000)	/* 100 seconds */
79962306a36Sopenharmony_ci#define UNLOAD_WAIT_LOOPS	(UNLOAD_WAIT_MS/UNLOAD_DELAY_UNIT_MS)
80062306a36Sopenharmony_ci#define UNLOAD_MSG_MS		(5*1000)	/* Every 5 seconds */
80162306a36Sopenharmony_ci#define UNLOAD_MSG_LOOPS	(UNLOAD_MSG_MS/UNLOAD_DELAY_UNIT_MS)
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_cistatic void vmbus_wait_for_unload(void)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	int cpu;
80662306a36Sopenharmony_ci	void *page_addr;
80762306a36Sopenharmony_ci	struct hv_message *msg;
80862306a36Sopenharmony_ci	struct vmbus_channel_message_header *hdr;
80962306a36Sopenharmony_ci	u32 message_type, i;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	/*
81262306a36Sopenharmony_ci	 * CHANNELMSG_UNLOAD_RESPONSE is always delivered to the CPU which was
81362306a36Sopenharmony_ci	 * used for initial contact or to CPU0 depending on host version. When
81462306a36Sopenharmony_ci	 * we're crashing on a different CPU let's hope that IRQ handler on
81562306a36Sopenharmony_ci	 * the cpu which receives CHANNELMSG_UNLOAD_RESPONSE is still
81662306a36Sopenharmony_ci	 * functional and vmbus_unload_response() will complete
81762306a36Sopenharmony_ci	 * vmbus_connection.unload_event. If not, the last thing we can do is
81862306a36Sopenharmony_ci	 * read message pages for all CPUs directly.
81962306a36Sopenharmony_ci	 *
82062306a36Sopenharmony_ci	 * Wait up to 100 seconds since an Azure host must writeback any dirty
82162306a36Sopenharmony_ci	 * data in its disk cache before the VMbus UNLOAD request will
82262306a36Sopenharmony_ci	 * complete. This flushing has been empirically observed to take up
82362306a36Sopenharmony_ci	 * to 50 seconds in cases with a lot of dirty data, so allow additional
82462306a36Sopenharmony_ci	 * leeway and for inaccuracies in mdelay(). But eventually time out so
82562306a36Sopenharmony_ci	 * that the panic path can't get hung forever in case the response
82662306a36Sopenharmony_ci	 * message isn't seen.
82762306a36Sopenharmony_ci	 */
82862306a36Sopenharmony_ci	for (i = 1; i <= UNLOAD_WAIT_LOOPS; i++) {
82962306a36Sopenharmony_ci		if (completion_done(&vmbus_connection.unload_event))
83062306a36Sopenharmony_ci			goto completed;
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci		for_each_present_cpu(cpu) {
83362306a36Sopenharmony_ci			struct hv_per_cpu_context *hv_cpu
83462306a36Sopenharmony_ci				= per_cpu_ptr(hv_context.cpu_context, cpu);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci			/*
83762306a36Sopenharmony_ci			 * In a CoCo VM the synic_message_page is not allocated
83862306a36Sopenharmony_ci			 * in hv_synic_alloc(). Instead it is set/cleared in
83962306a36Sopenharmony_ci			 * hv_synic_enable_regs() and hv_synic_disable_regs()
84062306a36Sopenharmony_ci			 * such that it is set only when the CPU is online. If
84162306a36Sopenharmony_ci			 * not all present CPUs are online, the message page
84262306a36Sopenharmony_ci			 * might be NULL, so skip such CPUs.
84362306a36Sopenharmony_ci			 */
84462306a36Sopenharmony_ci			page_addr = hv_cpu->synic_message_page;
84562306a36Sopenharmony_ci			if (!page_addr)
84662306a36Sopenharmony_ci				continue;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci			msg = (struct hv_message *)page_addr
84962306a36Sopenharmony_ci				+ VMBUS_MESSAGE_SINT;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci			message_type = READ_ONCE(msg->header.message_type);
85262306a36Sopenharmony_ci			if (message_type == HVMSG_NONE)
85362306a36Sopenharmony_ci				continue;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci			hdr = (struct vmbus_channel_message_header *)
85662306a36Sopenharmony_ci				msg->u.payload;
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci			if (hdr->msgtype == CHANNELMSG_UNLOAD_RESPONSE)
85962306a36Sopenharmony_ci				complete(&vmbus_connection.unload_event);
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci			vmbus_signal_eom(msg, message_type);
86262306a36Sopenharmony_ci		}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci		/*
86562306a36Sopenharmony_ci		 * Give a notice periodically so someone watching the
86662306a36Sopenharmony_ci		 * serial output won't think it is completely hung.
86762306a36Sopenharmony_ci		 */
86862306a36Sopenharmony_ci		if (!(i % UNLOAD_MSG_LOOPS))
86962306a36Sopenharmony_ci			pr_notice("Waiting for VMBus UNLOAD to complete\n");
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci		mdelay(UNLOAD_DELAY_UNIT_MS);
87262306a36Sopenharmony_ci	}
87362306a36Sopenharmony_ci	pr_err("Continuing even though VMBus UNLOAD did not complete\n");
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_cicompleted:
87662306a36Sopenharmony_ci	/*
87762306a36Sopenharmony_ci	 * We're crashing and already got the UNLOAD_RESPONSE, cleanup all
87862306a36Sopenharmony_ci	 * maybe-pending messages on all CPUs to be able to receive new
87962306a36Sopenharmony_ci	 * messages after we reconnect.
88062306a36Sopenharmony_ci	 */
88162306a36Sopenharmony_ci	for_each_present_cpu(cpu) {
88262306a36Sopenharmony_ci		struct hv_per_cpu_context *hv_cpu
88362306a36Sopenharmony_ci			= per_cpu_ptr(hv_context.cpu_context, cpu);
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci		page_addr = hv_cpu->synic_message_page;
88662306a36Sopenharmony_ci		if (!page_addr)
88762306a36Sopenharmony_ci			continue;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci		msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT;
89062306a36Sopenharmony_ci		msg->header.message_type = HVMSG_NONE;
89162306a36Sopenharmony_ci	}
89262306a36Sopenharmony_ci}
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci/*
89562306a36Sopenharmony_ci * vmbus_unload_response - Handler for the unload response.
89662306a36Sopenharmony_ci */
89762306a36Sopenharmony_cistatic void vmbus_unload_response(struct vmbus_channel_message_header *hdr)
89862306a36Sopenharmony_ci{
89962306a36Sopenharmony_ci	/*
90062306a36Sopenharmony_ci	 * This is a global event; just wakeup the waiting thread.
90162306a36Sopenharmony_ci	 * Once we successfully unload, we can cleanup the monitor state.
90262306a36Sopenharmony_ci	 *
90362306a36Sopenharmony_ci	 * NB.  A malicious or compromised Hyper-V could send a spurious
90462306a36Sopenharmony_ci	 * message of type CHANNELMSG_UNLOAD_RESPONSE, and trigger a call
90562306a36Sopenharmony_ci	 * of the complete() below.  Make sure that unload_event has been
90662306a36Sopenharmony_ci	 * initialized by the time this complete() is executed.
90762306a36Sopenharmony_ci	 */
90862306a36Sopenharmony_ci	complete(&vmbus_connection.unload_event);
90962306a36Sopenharmony_ci}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_civoid vmbus_initiate_unload(bool crash)
91262306a36Sopenharmony_ci{
91362306a36Sopenharmony_ci	struct vmbus_channel_message_header hdr;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	if (xchg(&vmbus_connection.conn_state, DISCONNECTED) == DISCONNECTED)
91662306a36Sopenharmony_ci		return;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	/* Pre-Win2012R2 hosts don't support reconnect */
91962306a36Sopenharmony_ci	if (vmbus_proto_version < VERSION_WIN8_1)
92062306a36Sopenharmony_ci		return;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci	reinit_completion(&vmbus_connection.unload_event);
92362306a36Sopenharmony_ci	memset(&hdr, 0, sizeof(struct vmbus_channel_message_header));
92462306a36Sopenharmony_ci	hdr.msgtype = CHANNELMSG_UNLOAD;
92562306a36Sopenharmony_ci	vmbus_post_msg(&hdr, sizeof(struct vmbus_channel_message_header),
92662306a36Sopenharmony_ci		       !crash);
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci	/*
92962306a36Sopenharmony_ci	 * vmbus_initiate_unload() is also called on crash and the crash can be
93062306a36Sopenharmony_ci	 * happening in an interrupt context, where scheduling is impossible.
93162306a36Sopenharmony_ci	 */
93262306a36Sopenharmony_ci	if (!crash)
93362306a36Sopenharmony_ci		wait_for_completion(&vmbus_connection.unload_event);
93462306a36Sopenharmony_ci	else
93562306a36Sopenharmony_ci		vmbus_wait_for_unload();
93662306a36Sopenharmony_ci}
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_cistatic void check_ready_for_resume_event(void)
93962306a36Sopenharmony_ci{
94062306a36Sopenharmony_ci	/*
94162306a36Sopenharmony_ci	 * If all the old primary channels have been fixed up, then it's safe
94262306a36Sopenharmony_ci	 * to resume.
94362306a36Sopenharmony_ci	 */
94462306a36Sopenharmony_ci	if (atomic_dec_and_test(&vmbus_connection.nr_chan_fixup_on_resume))
94562306a36Sopenharmony_ci		complete(&vmbus_connection.ready_for_resume_event);
94662306a36Sopenharmony_ci}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_cistatic void vmbus_setup_channel_state(struct vmbus_channel *channel,
94962306a36Sopenharmony_ci				      struct vmbus_channel_offer_channel *offer)
95062306a36Sopenharmony_ci{
95162306a36Sopenharmony_ci	/*
95262306a36Sopenharmony_ci	 * Setup state for signalling the host.
95362306a36Sopenharmony_ci	 */
95462306a36Sopenharmony_ci	channel->sig_event = VMBUS_EVENT_CONNECTION_ID;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	channel->is_dedicated_interrupt =
95762306a36Sopenharmony_ci			(offer->is_dedicated_interrupt != 0);
95862306a36Sopenharmony_ci	channel->sig_event = offer->connection_id;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	memcpy(&channel->offermsg, offer,
96162306a36Sopenharmony_ci	       sizeof(struct vmbus_channel_offer_channel));
96262306a36Sopenharmony_ci	channel->monitor_grp = (u8)offer->monitorid / 32;
96362306a36Sopenharmony_ci	channel->monitor_bit = (u8)offer->monitorid % 32;
96462306a36Sopenharmony_ci	channel->device_id = hv_get_dev_type(channel);
96562306a36Sopenharmony_ci}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci/*
96862306a36Sopenharmony_ci * find_primary_channel_by_offer - Get the channel object given the new offer.
96962306a36Sopenharmony_ci * This is only used in the resume path of hibernation.
97062306a36Sopenharmony_ci */
97162306a36Sopenharmony_cistatic struct vmbus_channel *
97262306a36Sopenharmony_cifind_primary_channel_by_offer(const struct vmbus_channel_offer_channel *offer)
97362306a36Sopenharmony_ci{
97462306a36Sopenharmony_ci	struct vmbus_channel *channel = NULL, *iter;
97562306a36Sopenharmony_ci	const guid_t *inst1, *inst2;
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	/* Ignore sub-channel offers. */
97862306a36Sopenharmony_ci	if (offer->offer.sub_channel_index != 0)
97962306a36Sopenharmony_ci		return NULL;
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci	mutex_lock(&vmbus_connection.channel_mutex);
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	list_for_each_entry(iter, &vmbus_connection.chn_list, listentry) {
98462306a36Sopenharmony_ci		inst1 = &iter->offermsg.offer.if_instance;
98562306a36Sopenharmony_ci		inst2 = &offer->offer.if_instance;
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci		if (guid_equal(inst1, inst2)) {
98862306a36Sopenharmony_ci			channel = iter;
98962306a36Sopenharmony_ci			break;
99062306a36Sopenharmony_ci		}
99162306a36Sopenharmony_ci	}
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	mutex_unlock(&vmbus_connection.channel_mutex);
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	return channel;
99662306a36Sopenharmony_ci}
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_cistatic bool vmbus_is_valid_offer(const struct vmbus_channel_offer_channel *offer)
99962306a36Sopenharmony_ci{
100062306a36Sopenharmony_ci	const guid_t *guid = &offer->offer.if_type;
100162306a36Sopenharmony_ci	u16 i;
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	if (!hv_is_isolation_supported())
100462306a36Sopenharmony_ci		return true;
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	if (is_hvsock_offer(offer))
100762306a36Sopenharmony_ci		return true;
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vmbus_devs); i++) {
101062306a36Sopenharmony_ci		if (guid_equal(guid, &vmbus_devs[i].guid))
101162306a36Sopenharmony_ci			return vmbus_devs[i].allowed_in_isolated;
101262306a36Sopenharmony_ci	}
101362306a36Sopenharmony_ci	return false;
101462306a36Sopenharmony_ci}
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci/*
101762306a36Sopenharmony_ci * vmbus_onoffer - Handler for channel offers from vmbus in parent partition.
101862306a36Sopenharmony_ci *
101962306a36Sopenharmony_ci */
102062306a36Sopenharmony_cistatic void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
102162306a36Sopenharmony_ci{
102262306a36Sopenharmony_ci	struct vmbus_channel_offer_channel *offer;
102362306a36Sopenharmony_ci	struct vmbus_channel *oldchannel, *newchannel;
102462306a36Sopenharmony_ci	size_t offer_sz;
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	offer = (struct vmbus_channel_offer_channel *)hdr;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	trace_vmbus_onoffer(offer);
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	if (!vmbus_is_valid_offer(offer)) {
103162306a36Sopenharmony_ci		pr_err_ratelimited("Invalid offer %d from the host supporting isolation\n",
103262306a36Sopenharmony_ci				   offer->child_relid);
103362306a36Sopenharmony_ci		atomic_dec(&vmbus_connection.offer_in_progress);
103462306a36Sopenharmony_ci		return;
103562306a36Sopenharmony_ci	}
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	oldchannel = find_primary_channel_by_offer(offer);
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	if (oldchannel != NULL) {
104062306a36Sopenharmony_ci		/*
104162306a36Sopenharmony_ci		 * We're resuming from hibernation: all the sub-channel and
104262306a36Sopenharmony_ci		 * hv_sock channels we had before the hibernation should have
104362306a36Sopenharmony_ci		 * been cleaned up, and now we must be seeing a re-offered
104462306a36Sopenharmony_ci		 * primary channel that we had before the hibernation.
104562306a36Sopenharmony_ci		 */
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci		/*
104862306a36Sopenharmony_ci		 * { Initially: channel relid = INVALID_RELID,
104962306a36Sopenharmony_ci		 *		channels[valid_relid] = NULL }
105062306a36Sopenharmony_ci		 *
105162306a36Sopenharmony_ci		 * CPU1					CPU2
105262306a36Sopenharmony_ci		 *
105362306a36Sopenharmony_ci		 * [vmbus_onoffer()]			[vmbus_device_release()]
105462306a36Sopenharmony_ci		 *
105562306a36Sopenharmony_ci		 * LOCK channel_mutex			LOCK channel_mutex
105662306a36Sopenharmony_ci		 * STORE channel relid = valid_relid	LOAD r1 = channel relid
105762306a36Sopenharmony_ci		 * MAP_RELID channel			if (r1 != INVALID_RELID)
105862306a36Sopenharmony_ci		 * UNLOCK channel_mutex			  UNMAP_RELID channel
105962306a36Sopenharmony_ci		 *					UNLOCK channel_mutex
106062306a36Sopenharmony_ci		 *
106162306a36Sopenharmony_ci		 * Forbids: r1 == valid_relid &&
106262306a36Sopenharmony_ci		 *              channels[valid_relid] == channel
106362306a36Sopenharmony_ci		 *
106462306a36Sopenharmony_ci		 * Note.  r1 can be INVALID_RELID only for an hv_sock channel.
106562306a36Sopenharmony_ci		 * None of the hv_sock channels which were present before the
106662306a36Sopenharmony_ci		 * suspend are re-offered upon the resume.  See the WARN_ON()
106762306a36Sopenharmony_ci		 * in hv_process_channel_removal().
106862306a36Sopenharmony_ci		 */
106962306a36Sopenharmony_ci		mutex_lock(&vmbus_connection.channel_mutex);
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci		atomic_dec(&vmbus_connection.offer_in_progress);
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci		WARN_ON(oldchannel->offermsg.child_relid != INVALID_RELID);
107462306a36Sopenharmony_ci		/* Fix up the relid. */
107562306a36Sopenharmony_ci		oldchannel->offermsg.child_relid = offer->child_relid;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci		offer_sz = sizeof(*offer);
107862306a36Sopenharmony_ci		if (memcmp(offer, &oldchannel->offermsg, offer_sz) != 0) {
107962306a36Sopenharmony_ci			/*
108062306a36Sopenharmony_ci			 * This is not an error, since the host can also change
108162306a36Sopenharmony_ci			 * the other field(s) of the offer, e.g. on WS RS5
108262306a36Sopenharmony_ci			 * (Build 17763), the offer->connection_id of the
108362306a36Sopenharmony_ci			 * Mellanox VF vmbus device can change when the host
108462306a36Sopenharmony_ci			 * reoffers the device upon resume.
108562306a36Sopenharmony_ci			 */
108662306a36Sopenharmony_ci			pr_debug("vmbus offer changed: relid=%d\n",
108762306a36Sopenharmony_ci				 offer->child_relid);
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci			print_hex_dump_debug("Old vmbus offer: ",
109062306a36Sopenharmony_ci					     DUMP_PREFIX_OFFSET, 16, 4,
109162306a36Sopenharmony_ci					     &oldchannel->offermsg, offer_sz,
109262306a36Sopenharmony_ci					     false);
109362306a36Sopenharmony_ci			print_hex_dump_debug("New vmbus offer: ",
109462306a36Sopenharmony_ci					     DUMP_PREFIX_OFFSET, 16, 4,
109562306a36Sopenharmony_ci					     offer, offer_sz, false);
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci			/* Fix up the old channel. */
109862306a36Sopenharmony_ci			vmbus_setup_channel_state(oldchannel, offer);
109962306a36Sopenharmony_ci		}
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci		/* Add the channel back to the array of channels. */
110262306a36Sopenharmony_ci		vmbus_channel_map_relid(oldchannel);
110362306a36Sopenharmony_ci		check_ready_for_resume_event();
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci		mutex_unlock(&vmbus_connection.channel_mutex);
110662306a36Sopenharmony_ci		return;
110762306a36Sopenharmony_ci	}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	/* Allocate the channel object and save this offer. */
111062306a36Sopenharmony_ci	newchannel = alloc_channel();
111162306a36Sopenharmony_ci	if (!newchannel) {
111262306a36Sopenharmony_ci		vmbus_release_relid(offer->child_relid);
111362306a36Sopenharmony_ci		atomic_dec(&vmbus_connection.offer_in_progress);
111462306a36Sopenharmony_ci		pr_err("Unable to allocate channel object\n");
111562306a36Sopenharmony_ci		return;
111662306a36Sopenharmony_ci	}
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	vmbus_setup_channel_state(newchannel, offer);
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	vmbus_process_offer(newchannel);
112162306a36Sopenharmony_ci}
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_cistatic void check_ready_for_suspend_event(void)
112462306a36Sopenharmony_ci{
112562306a36Sopenharmony_ci	/*
112662306a36Sopenharmony_ci	 * If all the sub-channels or hv_sock channels have been cleaned up,
112762306a36Sopenharmony_ci	 * then it's safe to suspend.
112862306a36Sopenharmony_ci	 */
112962306a36Sopenharmony_ci	if (atomic_dec_and_test(&vmbus_connection.nr_chan_close_on_suspend))
113062306a36Sopenharmony_ci		complete(&vmbus_connection.ready_for_suspend_event);
113162306a36Sopenharmony_ci}
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci/*
113462306a36Sopenharmony_ci * vmbus_onoffer_rescind - Rescind offer handler.
113562306a36Sopenharmony_ci *
113662306a36Sopenharmony_ci * We queue a work item to process this offer synchronously
113762306a36Sopenharmony_ci */
113862306a36Sopenharmony_cistatic void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
113962306a36Sopenharmony_ci{
114062306a36Sopenharmony_ci	struct vmbus_channel_rescind_offer *rescind;
114162306a36Sopenharmony_ci	struct vmbus_channel *channel;
114262306a36Sopenharmony_ci	struct device *dev;
114362306a36Sopenharmony_ci	bool clean_up_chan_for_suspend;
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	rescind = (struct vmbus_channel_rescind_offer *)hdr;
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	trace_vmbus_onoffer_rescind(rescind);
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	/*
115062306a36Sopenharmony_ci	 * The offer msg and the corresponding rescind msg
115162306a36Sopenharmony_ci	 * from the host are guranteed to be ordered -
115262306a36Sopenharmony_ci	 * offer comes in first and then the rescind.
115362306a36Sopenharmony_ci	 * Since we process these events in work elements,
115462306a36Sopenharmony_ci	 * and with preemption, we may end up processing
115562306a36Sopenharmony_ci	 * the events out of order.  We rely on the synchronization
115662306a36Sopenharmony_ci	 * provided by offer_in_progress and by channel_mutex for
115762306a36Sopenharmony_ci	 * ordering these events:
115862306a36Sopenharmony_ci	 *
115962306a36Sopenharmony_ci	 * { Initially: offer_in_progress = 1 }
116062306a36Sopenharmony_ci	 *
116162306a36Sopenharmony_ci	 * CPU1				CPU2
116262306a36Sopenharmony_ci	 *
116362306a36Sopenharmony_ci	 * [vmbus_onoffer()]		[vmbus_onoffer_rescind()]
116462306a36Sopenharmony_ci	 *
116562306a36Sopenharmony_ci	 * LOCK channel_mutex		WAIT_ON offer_in_progress == 0
116662306a36Sopenharmony_ci	 * DECREMENT offer_in_progress	LOCK channel_mutex
116762306a36Sopenharmony_ci	 * STORE channels[]		LOAD channels[]
116862306a36Sopenharmony_ci	 * UNLOCK channel_mutex		UNLOCK channel_mutex
116962306a36Sopenharmony_ci	 *
117062306a36Sopenharmony_ci	 * Forbids: CPU2's LOAD from *not* seeing CPU1's STORE
117162306a36Sopenharmony_ci	 */
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	while (atomic_read(&vmbus_connection.offer_in_progress) != 0) {
117462306a36Sopenharmony_ci		/*
117562306a36Sopenharmony_ci		 * We wait here until any channel offer is currently
117662306a36Sopenharmony_ci		 * being processed.
117762306a36Sopenharmony_ci		 */
117862306a36Sopenharmony_ci		msleep(1);
117962306a36Sopenharmony_ci	}
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	mutex_lock(&vmbus_connection.channel_mutex);
118262306a36Sopenharmony_ci	channel = relid2channel(rescind->child_relid);
118362306a36Sopenharmony_ci	if (channel != NULL) {
118462306a36Sopenharmony_ci		/*
118562306a36Sopenharmony_ci		 * Guarantee that no other instance of vmbus_onoffer_rescind()
118662306a36Sopenharmony_ci		 * has got a reference to the channel object.  Synchronize on
118762306a36Sopenharmony_ci		 * &vmbus_connection.channel_mutex.
118862306a36Sopenharmony_ci		 */
118962306a36Sopenharmony_ci		if (channel->rescind_ref) {
119062306a36Sopenharmony_ci			mutex_unlock(&vmbus_connection.channel_mutex);
119162306a36Sopenharmony_ci			return;
119262306a36Sopenharmony_ci		}
119362306a36Sopenharmony_ci		channel->rescind_ref = true;
119462306a36Sopenharmony_ci	}
119562306a36Sopenharmony_ci	mutex_unlock(&vmbus_connection.channel_mutex);
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	if (channel == NULL) {
119862306a36Sopenharmony_ci		/*
119962306a36Sopenharmony_ci		 * We failed in processing the offer message;
120062306a36Sopenharmony_ci		 * we would have cleaned up the relid in that
120162306a36Sopenharmony_ci		 * failure path.
120262306a36Sopenharmony_ci		 */
120362306a36Sopenharmony_ci		return;
120462306a36Sopenharmony_ci	}
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	clean_up_chan_for_suspend = is_hvsock_channel(channel) ||
120762306a36Sopenharmony_ci				    is_sub_channel(channel);
120862306a36Sopenharmony_ci	/*
120962306a36Sopenharmony_ci	 * Before setting channel->rescind in vmbus_rescind_cleanup(), we
121062306a36Sopenharmony_ci	 * should make sure the channel callback is not running any more.
121162306a36Sopenharmony_ci	 */
121262306a36Sopenharmony_ci	vmbus_reset_channel_cb(channel);
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	/*
121562306a36Sopenharmony_ci	 * Now wait for offer handling to complete.
121662306a36Sopenharmony_ci	 */
121762306a36Sopenharmony_ci	vmbus_rescind_cleanup(channel);
121862306a36Sopenharmony_ci	while (READ_ONCE(channel->probe_done) == false) {
121962306a36Sopenharmony_ci		/*
122062306a36Sopenharmony_ci		 * We wait here until any channel offer is currently
122162306a36Sopenharmony_ci		 * being processed.
122262306a36Sopenharmony_ci		 */
122362306a36Sopenharmony_ci		msleep(1);
122462306a36Sopenharmony_ci	}
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_ci	/*
122762306a36Sopenharmony_ci	 * At this point, the rescind handling can proceed safely.
122862306a36Sopenharmony_ci	 */
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	if (channel->device_obj) {
123162306a36Sopenharmony_ci		if (channel->chn_rescind_callback) {
123262306a36Sopenharmony_ci			channel->chn_rescind_callback(channel);
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci			if (clean_up_chan_for_suspend)
123562306a36Sopenharmony_ci				check_ready_for_suspend_event();
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci			return;
123862306a36Sopenharmony_ci		}
123962306a36Sopenharmony_ci		/*
124062306a36Sopenharmony_ci		 * We will have to unregister this device from the
124162306a36Sopenharmony_ci		 * driver core.
124262306a36Sopenharmony_ci		 */
124362306a36Sopenharmony_ci		dev = get_device(&channel->device_obj->device);
124462306a36Sopenharmony_ci		if (dev) {
124562306a36Sopenharmony_ci			vmbus_device_unregister(channel->device_obj);
124662306a36Sopenharmony_ci			put_device(dev);
124762306a36Sopenharmony_ci		}
124862306a36Sopenharmony_ci	} else if (channel->primary_channel != NULL) {
124962306a36Sopenharmony_ci		/*
125062306a36Sopenharmony_ci		 * Sub-channel is being rescinded. Following is the channel
125162306a36Sopenharmony_ci		 * close sequence when initiated from the driveri (refer to
125262306a36Sopenharmony_ci		 * vmbus_close() for details):
125362306a36Sopenharmony_ci		 * 1. Close all sub-channels first
125462306a36Sopenharmony_ci		 * 2. Then close the primary channel.
125562306a36Sopenharmony_ci		 */
125662306a36Sopenharmony_ci		mutex_lock(&vmbus_connection.channel_mutex);
125762306a36Sopenharmony_ci		if (channel->state == CHANNEL_OPEN_STATE) {
125862306a36Sopenharmony_ci			/*
125962306a36Sopenharmony_ci			 * The channel is currently not open;
126062306a36Sopenharmony_ci			 * it is safe for us to cleanup the channel.
126162306a36Sopenharmony_ci			 */
126262306a36Sopenharmony_ci			hv_process_channel_removal(channel);
126362306a36Sopenharmony_ci		} else {
126462306a36Sopenharmony_ci			complete(&channel->rescind_event);
126562306a36Sopenharmony_ci		}
126662306a36Sopenharmony_ci		mutex_unlock(&vmbus_connection.channel_mutex);
126762306a36Sopenharmony_ci	}
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	/* The "channel" may have been freed. Do not access it any longer. */
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	if (clean_up_chan_for_suspend)
127262306a36Sopenharmony_ci		check_ready_for_suspend_event();
127362306a36Sopenharmony_ci}
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_civoid vmbus_hvsock_device_unregister(struct vmbus_channel *channel)
127662306a36Sopenharmony_ci{
127762306a36Sopenharmony_ci	BUG_ON(!is_hvsock_channel(channel));
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	/* We always get a rescind msg when a connection is closed. */
128062306a36Sopenharmony_ci	while (!READ_ONCE(channel->probe_done) || !READ_ONCE(channel->rescind))
128162306a36Sopenharmony_ci		msleep(1);
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	vmbus_device_unregister(channel->device_obj);
128462306a36Sopenharmony_ci}
128562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_hvsock_device_unregister);
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci/*
128962306a36Sopenharmony_ci * vmbus_onoffers_delivered -
129062306a36Sopenharmony_ci * This is invoked when all offers have been delivered.
129162306a36Sopenharmony_ci *
129262306a36Sopenharmony_ci * Nothing to do here.
129362306a36Sopenharmony_ci */
129462306a36Sopenharmony_cistatic void vmbus_onoffers_delivered(
129562306a36Sopenharmony_ci			struct vmbus_channel_message_header *hdr)
129662306a36Sopenharmony_ci{
129762306a36Sopenharmony_ci}
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci/*
130062306a36Sopenharmony_ci * vmbus_onopen_result - Open result handler.
130162306a36Sopenharmony_ci *
130262306a36Sopenharmony_ci * This is invoked when we received a response to our channel open request.
130362306a36Sopenharmony_ci * Find the matching request, copy the response and signal the requesting
130462306a36Sopenharmony_ci * thread.
130562306a36Sopenharmony_ci */
130662306a36Sopenharmony_cistatic void vmbus_onopen_result(struct vmbus_channel_message_header *hdr)
130762306a36Sopenharmony_ci{
130862306a36Sopenharmony_ci	struct vmbus_channel_open_result *result;
130962306a36Sopenharmony_ci	struct vmbus_channel_msginfo *msginfo;
131062306a36Sopenharmony_ci	struct vmbus_channel_message_header *requestheader;
131162306a36Sopenharmony_ci	struct vmbus_channel_open_channel *openmsg;
131262306a36Sopenharmony_ci	unsigned long flags;
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	result = (struct vmbus_channel_open_result *)hdr;
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	trace_vmbus_onopen_result(result);
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	/*
131962306a36Sopenharmony_ci	 * Find the open msg, copy the result and signal/unblock the wait event
132062306a36Sopenharmony_ci	 */
132162306a36Sopenharmony_ci	spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list,
132462306a36Sopenharmony_ci				msglistentry) {
132562306a36Sopenharmony_ci		requestheader =
132662306a36Sopenharmony_ci			(struct vmbus_channel_message_header *)msginfo->msg;
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci		if (requestheader->msgtype == CHANNELMSG_OPENCHANNEL) {
132962306a36Sopenharmony_ci			openmsg =
133062306a36Sopenharmony_ci			(struct vmbus_channel_open_channel *)msginfo->msg;
133162306a36Sopenharmony_ci			if (openmsg->child_relid == result->child_relid &&
133262306a36Sopenharmony_ci			    openmsg->openid == result->openid) {
133362306a36Sopenharmony_ci				memcpy(&msginfo->response.open_result,
133462306a36Sopenharmony_ci				       result,
133562306a36Sopenharmony_ci				       sizeof(
133662306a36Sopenharmony_ci					struct vmbus_channel_open_result));
133762306a36Sopenharmony_ci				complete(&msginfo->waitevent);
133862306a36Sopenharmony_ci				break;
133962306a36Sopenharmony_ci			}
134062306a36Sopenharmony_ci		}
134162306a36Sopenharmony_ci	}
134262306a36Sopenharmony_ci	spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
134362306a36Sopenharmony_ci}
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci/*
134662306a36Sopenharmony_ci * vmbus_ongpadl_created - GPADL created handler.
134762306a36Sopenharmony_ci *
134862306a36Sopenharmony_ci * This is invoked when we received a response to our gpadl create request.
134962306a36Sopenharmony_ci * Find the matching request, copy the response and signal the requesting
135062306a36Sopenharmony_ci * thread.
135162306a36Sopenharmony_ci */
135262306a36Sopenharmony_cistatic void vmbus_ongpadl_created(struct vmbus_channel_message_header *hdr)
135362306a36Sopenharmony_ci{
135462306a36Sopenharmony_ci	struct vmbus_channel_gpadl_created *gpadlcreated;
135562306a36Sopenharmony_ci	struct vmbus_channel_msginfo *msginfo;
135662306a36Sopenharmony_ci	struct vmbus_channel_message_header *requestheader;
135762306a36Sopenharmony_ci	struct vmbus_channel_gpadl_header *gpadlheader;
135862306a36Sopenharmony_ci	unsigned long flags;
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	gpadlcreated = (struct vmbus_channel_gpadl_created *)hdr;
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	trace_vmbus_ongpadl_created(gpadlcreated);
136362306a36Sopenharmony_ci
136462306a36Sopenharmony_ci	/*
136562306a36Sopenharmony_ci	 * Find the establish msg, copy the result and signal/unblock the wait
136662306a36Sopenharmony_ci	 * event
136762306a36Sopenharmony_ci	 */
136862306a36Sopenharmony_ci	spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list,
137162306a36Sopenharmony_ci				msglistentry) {
137262306a36Sopenharmony_ci		requestheader =
137362306a36Sopenharmony_ci			(struct vmbus_channel_message_header *)msginfo->msg;
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci		if (requestheader->msgtype == CHANNELMSG_GPADL_HEADER) {
137662306a36Sopenharmony_ci			gpadlheader =
137762306a36Sopenharmony_ci			(struct vmbus_channel_gpadl_header *)requestheader;
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci			if ((gpadlcreated->child_relid ==
138062306a36Sopenharmony_ci			     gpadlheader->child_relid) &&
138162306a36Sopenharmony_ci			    (gpadlcreated->gpadl == gpadlheader->gpadl)) {
138262306a36Sopenharmony_ci				memcpy(&msginfo->response.gpadl_created,
138362306a36Sopenharmony_ci				       gpadlcreated,
138462306a36Sopenharmony_ci				       sizeof(
138562306a36Sopenharmony_ci					struct vmbus_channel_gpadl_created));
138662306a36Sopenharmony_ci				complete(&msginfo->waitevent);
138762306a36Sopenharmony_ci				break;
138862306a36Sopenharmony_ci			}
138962306a36Sopenharmony_ci		}
139062306a36Sopenharmony_ci	}
139162306a36Sopenharmony_ci	spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
139262306a36Sopenharmony_ci}
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci/*
139562306a36Sopenharmony_ci * vmbus_onmodifychannel_response - Modify Channel response handler.
139662306a36Sopenharmony_ci *
139762306a36Sopenharmony_ci * This is invoked when we received a response to our channel modify request.
139862306a36Sopenharmony_ci * Find the matching request, copy the response and signal the requesting thread.
139962306a36Sopenharmony_ci */
140062306a36Sopenharmony_cistatic void vmbus_onmodifychannel_response(struct vmbus_channel_message_header *hdr)
140162306a36Sopenharmony_ci{
140262306a36Sopenharmony_ci	struct vmbus_channel_modifychannel_response *response;
140362306a36Sopenharmony_ci	struct vmbus_channel_msginfo *msginfo;
140462306a36Sopenharmony_ci	unsigned long flags;
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci	response = (struct vmbus_channel_modifychannel_response *)hdr;
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci	trace_vmbus_onmodifychannel_response(response);
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	/*
141162306a36Sopenharmony_ci	 * Find the modify msg, copy the response and signal/unblock the wait event.
141262306a36Sopenharmony_ci	 */
141362306a36Sopenharmony_ci	spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list, msglistentry) {
141662306a36Sopenharmony_ci		struct vmbus_channel_message_header *responseheader =
141762306a36Sopenharmony_ci				(struct vmbus_channel_message_header *)msginfo->msg;
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci		if (responseheader->msgtype == CHANNELMSG_MODIFYCHANNEL) {
142062306a36Sopenharmony_ci			struct vmbus_channel_modifychannel *modifymsg;
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci			modifymsg = (struct vmbus_channel_modifychannel *)msginfo->msg;
142362306a36Sopenharmony_ci			if (modifymsg->child_relid == response->child_relid) {
142462306a36Sopenharmony_ci				memcpy(&msginfo->response.modify_response, response,
142562306a36Sopenharmony_ci				       sizeof(*response));
142662306a36Sopenharmony_ci				complete(&msginfo->waitevent);
142762306a36Sopenharmony_ci				break;
142862306a36Sopenharmony_ci			}
142962306a36Sopenharmony_ci		}
143062306a36Sopenharmony_ci	}
143162306a36Sopenharmony_ci	spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
143262306a36Sopenharmony_ci}
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci/*
143562306a36Sopenharmony_ci * vmbus_ongpadl_torndown - GPADL torndown handler.
143662306a36Sopenharmony_ci *
143762306a36Sopenharmony_ci * This is invoked when we received a response to our gpadl teardown request.
143862306a36Sopenharmony_ci * Find the matching request, copy the response and signal the requesting
143962306a36Sopenharmony_ci * thread.
144062306a36Sopenharmony_ci */
144162306a36Sopenharmony_cistatic void vmbus_ongpadl_torndown(
144262306a36Sopenharmony_ci			struct vmbus_channel_message_header *hdr)
144362306a36Sopenharmony_ci{
144462306a36Sopenharmony_ci	struct vmbus_channel_gpadl_torndown *gpadl_torndown;
144562306a36Sopenharmony_ci	struct vmbus_channel_msginfo *msginfo;
144662306a36Sopenharmony_ci	struct vmbus_channel_message_header *requestheader;
144762306a36Sopenharmony_ci	struct vmbus_channel_gpadl_teardown *gpadl_teardown;
144862306a36Sopenharmony_ci	unsigned long flags;
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	gpadl_torndown = (struct vmbus_channel_gpadl_torndown *)hdr;
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	trace_vmbus_ongpadl_torndown(gpadl_torndown);
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	/*
145562306a36Sopenharmony_ci	 * Find the open msg, copy the result and signal/unblock the wait event
145662306a36Sopenharmony_ci	 */
145762306a36Sopenharmony_ci	spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list,
146062306a36Sopenharmony_ci				msglistentry) {
146162306a36Sopenharmony_ci		requestheader =
146262306a36Sopenharmony_ci			(struct vmbus_channel_message_header *)msginfo->msg;
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci		if (requestheader->msgtype == CHANNELMSG_GPADL_TEARDOWN) {
146562306a36Sopenharmony_ci			gpadl_teardown =
146662306a36Sopenharmony_ci			(struct vmbus_channel_gpadl_teardown *)requestheader;
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci			if (gpadl_torndown->gpadl == gpadl_teardown->gpadl) {
146962306a36Sopenharmony_ci				memcpy(&msginfo->response.gpadl_torndown,
147062306a36Sopenharmony_ci				       gpadl_torndown,
147162306a36Sopenharmony_ci				       sizeof(
147262306a36Sopenharmony_ci					struct vmbus_channel_gpadl_torndown));
147362306a36Sopenharmony_ci				complete(&msginfo->waitevent);
147462306a36Sopenharmony_ci				break;
147562306a36Sopenharmony_ci			}
147662306a36Sopenharmony_ci		}
147762306a36Sopenharmony_ci	}
147862306a36Sopenharmony_ci	spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
147962306a36Sopenharmony_ci}
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci/*
148262306a36Sopenharmony_ci * vmbus_onversion_response - Version response handler
148362306a36Sopenharmony_ci *
148462306a36Sopenharmony_ci * This is invoked when we received a response to our initiate contact request.
148562306a36Sopenharmony_ci * Find the matching request, copy the response and signal the requesting
148662306a36Sopenharmony_ci * thread.
148762306a36Sopenharmony_ci */
148862306a36Sopenharmony_cistatic void vmbus_onversion_response(
148962306a36Sopenharmony_ci		struct vmbus_channel_message_header *hdr)
149062306a36Sopenharmony_ci{
149162306a36Sopenharmony_ci	struct vmbus_channel_msginfo *msginfo;
149262306a36Sopenharmony_ci	struct vmbus_channel_message_header *requestheader;
149362306a36Sopenharmony_ci	struct vmbus_channel_version_response *version_response;
149462306a36Sopenharmony_ci	unsigned long flags;
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	version_response = (struct vmbus_channel_version_response *)hdr;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	trace_vmbus_onversion_response(version_response);
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags);
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci	list_for_each_entry(msginfo, &vmbus_connection.chn_msg_list,
150362306a36Sopenharmony_ci				msglistentry) {
150462306a36Sopenharmony_ci		requestheader =
150562306a36Sopenharmony_ci			(struct vmbus_channel_message_header *)msginfo->msg;
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci		if (requestheader->msgtype ==
150862306a36Sopenharmony_ci		    CHANNELMSG_INITIATE_CONTACT) {
150962306a36Sopenharmony_ci			memcpy(&msginfo->response.version_response,
151062306a36Sopenharmony_ci			      version_response,
151162306a36Sopenharmony_ci			      sizeof(struct vmbus_channel_version_response));
151262306a36Sopenharmony_ci			complete(&msginfo->waitevent);
151362306a36Sopenharmony_ci		}
151462306a36Sopenharmony_ci	}
151562306a36Sopenharmony_ci	spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags);
151662306a36Sopenharmony_ci}
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci/* Channel message dispatch table */
151962306a36Sopenharmony_ciconst struct vmbus_channel_message_table_entry
152062306a36Sopenharmony_cichannel_message_table[CHANNELMSG_COUNT] = {
152162306a36Sopenharmony_ci	{ CHANNELMSG_INVALID,			0, NULL, 0},
152262306a36Sopenharmony_ci	{ CHANNELMSG_OFFERCHANNEL,		0, vmbus_onoffer,
152362306a36Sopenharmony_ci		sizeof(struct vmbus_channel_offer_channel)},
152462306a36Sopenharmony_ci	{ CHANNELMSG_RESCIND_CHANNELOFFER,	0, vmbus_onoffer_rescind,
152562306a36Sopenharmony_ci		sizeof(struct vmbus_channel_rescind_offer) },
152662306a36Sopenharmony_ci	{ CHANNELMSG_REQUESTOFFERS,		0, NULL, 0},
152762306a36Sopenharmony_ci	{ CHANNELMSG_ALLOFFERS_DELIVERED,	1, vmbus_onoffers_delivered, 0},
152862306a36Sopenharmony_ci	{ CHANNELMSG_OPENCHANNEL,		0, NULL, 0},
152962306a36Sopenharmony_ci	{ CHANNELMSG_OPENCHANNEL_RESULT,	1, vmbus_onopen_result,
153062306a36Sopenharmony_ci		sizeof(struct vmbus_channel_open_result)},
153162306a36Sopenharmony_ci	{ CHANNELMSG_CLOSECHANNEL,		0, NULL, 0},
153262306a36Sopenharmony_ci	{ CHANNELMSG_GPADL_HEADER,		0, NULL, 0},
153362306a36Sopenharmony_ci	{ CHANNELMSG_GPADL_BODY,		0, NULL, 0},
153462306a36Sopenharmony_ci	{ CHANNELMSG_GPADL_CREATED,		1, vmbus_ongpadl_created,
153562306a36Sopenharmony_ci		sizeof(struct vmbus_channel_gpadl_created)},
153662306a36Sopenharmony_ci	{ CHANNELMSG_GPADL_TEARDOWN,		0, NULL, 0},
153762306a36Sopenharmony_ci	{ CHANNELMSG_GPADL_TORNDOWN,		1, vmbus_ongpadl_torndown,
153862306a36Sopenharmony_ci		sizeof(struct vmbus_channel_gpadl_torndown) },
153962306a36Sopenharmony_ci	{ CHANNELMSG_RELID_RELEASED,		0, NULL, 0},
154062306a36Sopenharmony_ci	{ CHANNELMSG_INITIATE_CONTACT,		0, NULL, 0},
154162306a36Sopenharmony_ci	{ CHANNELMSG_VERSION_RESPONSE,		1, vmbus_onversion_response,
154262306a36Sopenharmony_ci		sizeof(struct vmbus_channel_version_response)},
154362306a36Sopenharmony_ci	{ CHANNELMSG_UNLOAD,			0, NULL, 0},
154462306a36Sopenharmony_ci	{ CHANNELMSG_UNLOAD_RESPONSE,		1, vmbus_unload_response, 0},
154562306a36Sopenharmony_ci	{ CHANNELMSG_18,			0, NULL, 0},
154662306a36Sopenharmony_ci	{ CHANNELMSG_19,			0, NULL, 0},
154762306a36Sopenharmony_ci	{ CHANNELMSG_20,			0, NULL, 0},
154862306a36Sopenharmony_ci	{ CHANNELMSG_TL_CONNECT_REQUEST,	0, NULL, 0},
154962306a36Sopenharmony_ci	{ CHANNELMSG_MODIFYCHANNEL,		0, NULL, 0},
155062306a36Sopenharmony_ci	{ CHANNELMSG_TL_CONNECT_RESULT,		0, NULL, 0},
155162306a36Sopenharmony_ci	{ CHANNELMSG_MODIFYCHANNEL_RESPONSE,	1, vmbus_onmodifychannel_response,
155262306a36Sopenharmony_ci		sizeof(struct vmbus_channel_modifychannel_response)},
155362306a36Sopenharmony_ci};
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci/*
155662306a36Sopenharmony_ci * vmbus_onmessage - Handler for channel protocol messages.
155762306a36Sopenharmony_ci *
155862306a36Sopenharmony_ci * This is invoked in the vmbus worker thread context.
155962306a36Sopenharmony_ci */
156062306a36Sopenharmony_civoid vmbus_onmessage(struct vmbus_channel_message_header *hdr)
156162306a36Sopenharmony_ci{
156262306a36Sopenharmony_ci	trace_vmbus_on_message(hdr);
156362306a36Sopenharmony_ci
156462306a36Sopenharmony_ci	/*
156562306a36Sopenharmony_ci	 * vmbus_on_msg_dpc() makes sure the hdr->msgtype here can not go
156662306a36Sopenharmony_ci	 * out of bound and the message_handler pointer can not be NULL.
156762306a36Sopenharmony_ci	 */
156862306a36Sopenharmony_ci	channel_message_table[hdr->msgtype].message_handler(hdr);
156962306a36Sopenharmony_ci}
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci/*
157262306a36Sopenharmony_ci * vmbus_request_offers - Send a request to get all our pending offers.
157362306a36Sopenharmony_ci */
157462306a36Sopenharmony_ciint vmbus_request_offers(void)
157562306a36Sopenharmony_ci{
157662306a36Sopenharmony_ci	struct vmbus_channel_message_header *msg;
157762306a36Sopenharmony_ci	struct vmbus_channel_msginfo *msginfo;
157862306a36Sopenharmony_ci	int ret;
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci	msginfo = kzalloc(sizeof(*msginfo) +
158162306a36Sopenharmony_ci			  sizeof(struct vmbus_channel_message_header),
158262306a36Sopenharmony_ci			  GFP_KERNEL);
158362306a36Sopenharmony_ci	if (!msginfo)
158462306a36Sopenharmony_ci		return -ENOMEM;
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci	msg = (struct vmbus_channel_message_header *)msginfo->msg;
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci	msg->msgtype = CHANNELMSG_REQUESTOFFERS;
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci	ret = vmbus_post_msg(msg, sizeof(struct vmbus_channel_message_header),
159162306a36Sopenharmony_ci			     true);
159262306a36Sopenharmony_ci
159362306a36Sopenharmony_ci	trace_vmbus_request_offers(ret);
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci	if (ret != 0) {
159662306a36Sopenharmony_ci		pr_err("Unable to request offers - %d\n", ret);
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci		goto cleanup;
159962306a36Sopenharmony_ci	}
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_cicleanup:
160262306a36Sopenharmony_ci	kfree(msginfo);
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci	return ret;
160562306a36Sopenharmony_ci}
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_civoid vmbus_set_sc_create_callback(struct vmbus_channel *primary_channel,
160862306a36Sopenharmony_ci				void (*sc_cr_cb)(struct vmbus_channel *new_sc))
160962306a36Sopenharmony_ci{
161062306a36Sopenharmony_ci	primary_channel->sc_creation_callback = sc_cr_cb;
161162306a36Sopenharmony_ci}
161262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_set_sc_create_callback);
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_civoid vmbus_set_chn_rescind_callback(struct vmbus_channel *channel,
161562306a36Sopenharmony_ci		void (*chn_rescind_cb)(struct vmbus_channel *))
161662306a36Sopenharmony_ci{
161762306a36Sopenharmony_ci	channel->chn_rescind_callback = chn_rescind_cb;
161862306a36Sopenharmony_ci}
161962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vmbus_set_chn_rescind_callback);
1620