int vmbus_connect()

in connection.c [160:345]


int vmbus_connect(void)
{
	struct vmbus_channel_msginfo *msginfo = NULL;
	int i, ret = 0;
	__u32 version;

	/* Initialize the vmbus connection */
	vmbus_connection.conn_state = CONNECTING;
	vmbus_connection.work_queue = create_workqueue("hv_vmbus_con");
	if (!vmbus_connection.work_queue) {
		ret = -ENOMEM;
		goto cleanup;
	}

	vmbus_connection.handle_primary_chan_wq =
		create_workqueue("hv_pri_chan");
	if (!vmbus_connection.handle_primary_chan_wq) {
		ret = -ENOMEM;
		goto cleanup;
	}

	vmbus_connection.handle_sub_chan_wq =
		create_workqueue("hv_sub_chan");
	if (!vmbus_connection.handle_sub_chan_wq) {
		ret = -ENOMEM;
		goto cleanup;
	}

	INIT_LIST_HEAD(&vmbus_connection.chn_msg_list);
	spin_lock_init(&vmbus_connection.channelmsg_lock);

	INIT_LIST_HEAD(&vmbus_connection.chn_list);
	mutex_init(&vmbus_connection.channel_mutex);

	/*
	 * Setup the vmbus event connection for channel interrupt
	 * abstraction stuff
	 */
	vmbus_connection.int_page =
	(void *)hv_alloc_hyperv_zeroed_page();
	if (vmbus_connection.int_page == NULL) {
		ret = -ENOMEM;
		goto cleanup;
	}

	vmbus_connection.recv_int_page = vmbus_connection.int_page;
	vmbus_connection.send_int_page =
		(void *)((unsigned long)vmbus_connection.int_page +
			(HV_HYP_PAGE_SIZE >> 1));

	/*
	 * Setup the monitor notification facility. The 1st page for
	 * parent->child and the 2nd page for child->parent
	 */
	vmbus_connection.monitor_pages[0] = (void *)hv_alloc_hyperv_zeroed_page();
	vmbus_connection.monitor_pages[1] = (void *)hv_alloc_hyperv_zeroed_page();
	if ((vmbus_connection.monitor_pages[0] == NULL) ||
	    (vmbus_connection.monitor_pages[1] == NULL)) {
		ret = -ENOMEM;
		goto cleanup;
	}

	vmbus_connection.monitor_pages_original[0]
		= vmbus_connection.monitor_pages[0];
	vmbus_connection.monitor_pages_original[1]
		= vmbus_connection.monitor_pages[1];
	vmbus_connection.monitor_pages_pa[0]
		= virt_to_phys(vmbus_connection.monitor_pages[0]);
	vmbus_connection.monitor_pages_pa[1]
		= virt_to_phys(vmbus_connection.monitor_pages[1]);

	if (hv_is_isolation_supported()) {
		ret = set_memory_decrypted((unsigned long)
					   vmbus_connection.monitor_pages[0],
					   1);
		ret |= set_memory_decrypted((unsigned long)
					    vmbus_connection.monitor_pages[1],
					    1);
		if (ret)
			goto cleanup;

		/*
		 * Isolation VM with AMD SNP needs to access monitor page via
		 * address space above shared gpa boundary.
		 */
		if (hv_isolation_type_snp()) {
			vmbus_connection.monitor_pages_pa[0] +=
				ms_hyperv.shared_gpa_boundary;
			vmbus_connection.monitor_pages_pa[1] +=
				ms_hyperv.shared_gpa_boundary;

			vmbus_connection.monitor_pages[0]
				= memremap(vmbus_connection.monitor_pages_pa[0],
					   HV_HYP_PAGE_SIZE,
					   MEMREMAP_WB);
			if (!vmbus_connection.monitor_pages[0]) {
				ret = -ENOMEM;
				goto cleanup;
			}

			vmbus_connection.monitor_pages[1]
				= memremap(vmbus_connection.monitor_pages_pa[1],
					   HV_HYP_PAGE_SIZE,
					   MEMREMAP_WB);
			if (!vmbus_connection.monitor_pages[1]) {
				ret = -ENOMEM;
				goto cleanup;
			}
		}

		/*
		 * Set memory host visibility hvcall smears memory
		 * and so zero monitor pages here.
		 */
		memset(vmbus_connection.monitor_pages[0], 0x00,
		       HV_HYP_PAGE_SIZE);
		memset(vmbus_connection.monitor_pages[1], 0x00,
		       HV_HYP_PAGE_SIZE);

	}

	msginfo = kzalloc(sizeof(*msginfo) +
			  sizeof(struct vmbus_channel_initiate_contact),
			  GFP_KERNEL);
	if (msginfo == NULL) {
		ret = -ENOMEM;
		goto cleanup;
	}

	/*
	 * Negotiate a compatible VMBUS version number with the
	 * host. We start with the highest number we can support
	 * and work our way down until we negotiate a compatible
	 * version.
	 */

	for (i = 0; ; i++) {
		if (i == ARRAY_SIZE(vmbus_versions)) {
			ret = -EDOM;
			goto cleanup;
		}

		version = vmbus_versions[i];
		if (version > max_version)
			continue;

		ret = vmbus_negotiate_version(msginfo, version);
		if (ret == -ETIMEDOUT)
			goto cleanup;

		if (vmbus_connection.conn_state == CONNECTED)
			break;
	}

	if (hv_is_isolation_supported() && version < VERSION_WIN10_V5_2) {
		pr_err("Invalid VMBus version %d.%d (expected >= %d.%d) from the host supporting isolation\n",
		       version >> 16, version & 0xFFFF, VERSION_WIN10_V5_2 >> 16, VERSION_WIN10_V5_2 & 0xFFFF);
		ret = -EINVAL;
		goto cleanup;
	}

	vmbus_proto_version = version;
	pr_info("Vmbus version:%d.%d\n",
		version >> 16, version & 0xFFFF);

	vmbus_connection.channels = kcalloc(MAX_CHANNEL_RELIDS,
					    sizeof(struct vmbus_channel *),
					    GFP_KERNEL);
	if (vmbus_connection.channels == NULL) {
		ret = -ENOMEM;
		goto cleanup;
	}

	kfree(msginfo);
	return 0;

cleanup:
	pr_err("Unable to connect to host\n");

	vmbus_connection.conn_state = DISCONNECTED;
	vmbus_disconnect();

	kfree(msginfo);

	return ret;
}