hv_connection.c revision 295309
1250199Sgrehan/*-
2250199Sgrehan * Copyright (c) 2009-2012 Microsoft Corp.
3250199Sgrehan * Copyright (c) 2012 NetApp Inc.
4250199Sgrehan * Copyright (c) 2012 Citrix Inc.
5250199Sgrehan * All rights reserved.
6250199Sgrehan *
7250199Sgrehan * Redistribution and use in source and binary forms, with or without
8250199Sgrehan * modification, are permitted provided that the following conditions
9250199Sgrehan * are met:
10250199Sgrehan * 1. Redistributions of source code must retain the above copyright
11250199Sgrehan *    notice unmodified, this list of conditions, and the following
12250199Sgrehan *    disclaimer.
13250199Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
14250199Sgrehan *    notice, this list of conditions and the following disclaimer in the
15250199Sgrehan *    documentation and/or other materials provided with the distribution.
16250199Sgrehan *
17250199Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18250199Sgrehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19250199Sgrehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20250199Sgrehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21250199Sgrehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22250199Sgrehan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23250199Sgrehan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24250199Sgrehan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25250199Sgrehan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26250199Sgrehan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27250199Sgrehan */
28250199Sgrehan
29250199Sgrehan#include <sys/param.h>
30250199Sgrehan#include <sys/malloc.h>
31250199Sgrehan#include <sys/systm.h>
32250199Sgrehan#include <sys/lock.h>
33250199Sgrehan#include <sys/mutex.h>
34250199Sgrehan#include <machine/bus.h>
35250199Sgrehan#include <vm/vm.h>
36250199Sgrehan#include <vm/vm_param.h>
37250199Sgrehan#include <vm/pmap.h>
38250199Sgrehan
39250199Sgrehan#include "hv_vmbus_priv.h"
40250199Sgrehan
41250199Sgrehan/*
42250199Sgrehan * Globals
43250199Sgrehan */
44250199Sgrehanhv_vmbus_connection hv_vmbus_g_connection =
45250199Sgrehan	{ .connect_state = HV_DISCONNECTED,
46250199Sgrehan	  .next_gpadl_handle = 0xE1E10, };
47250199Sgrehan
48282212Swhuuint32_t hv_vmbus_protocal_version = HV_VMBUS_VERSION_WS2008;
49282212Swhu
50282212Swhustatic uint32_t
51282212Swhuhv_vmbus_get_next_version(uint32_t current_ver)
52282212Swhu{
53282212Swhu	switch (current_ver) {
54282212Swhu	case (HV_VMBUS_VERSION_WIN7):
55282212Swhu		return(HV_VMBUS_VERSION_WS2008);
56282212Swhu
57282212Swhu	case (HV_VMBUS_VERSION_WIN8):
58282212Swhu		return(HV_VMBUS_VERSION_WIN7);
59282212Swhu
60282212Swhu	case (HV_VMBUS_VERSION_WIN8_1):
61282212Swhu		return(HV_VMBUS_VERSION_WIN8);
62282212Swhu
63282212Swhu	case (HV_VMBUS_VERSION_WS2008):
64282212Swhu	default:
65282212Swhu		return(HV_VMBUS_VERSION_INVALID);
66282212Swhu	}
67282212Swhu}
68282212Swhu
69250199Sgrehan/**
70282212Swhu * Negotiate the highest supported hypervisor version.
71282212Swhu */
72282212Swhustatic int
73282212Swhuhv_vmbus_negotiate_version(hv_vmbus_channel_msg_info *msg_info,
74282212Swhu	uint32_t version)
75282212Swhu{
76282212Swhu	int					ret = 0;
77282212Swhu	hv_vmbus_channel_initiate_contact	*msg;
78282212Swhu
79282212Swhu	sema_init(&msg_info->wait_sema, 0, "Msg Info Sema");
80282212Swhu	msg = (hv_vmbus_channel_initiate_contact*) msg_info->msg;
81282212Swhu
82282212Swhu	msg->header.message_type = HV_CHANNEL_MESSAGE_INITIATED_CONTACT;
83282212Swhu	msg->vmbus_version_requested = version;
84282212Swhu
85282212Swhu	msg->interrupt_page = hv_get_phys_addr(
86282212Swhu		hv_vmbus_g_connection.interrupt_page);
87282212Swhu
88282212Swhu	msg->monitor_page_1 = hv_get_phys_addr(
89295309Ssephe		hv_vmbus_g_connection.monitor_page_1);
90282212Swhu
91295308Ssephe	msg->monitor_page_2 = hv_get_phys_addr(
92295309Ssephe		hv_vmbus_g_connection.monitor_page_2);
93282212Swhu
94282212Swhu	/**
95282212Swhu	 * Add to list before we send the request since we may receive the
96282212Swhu	 * response before returning from this routine
97282212Swhu	 */
98282212Swhu	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
99282212Swhu
100282212Swhu	TAILQ_INSERT_TAIL(
101282212Swhu		&hv_vmbus_g_connection.channel_msg_anchor,
102282212Swhu		msg_info,
103282212Swhu		msg_list_entry);
104282212Swhu
105282212Swhu	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
106282212Swhu
107282212Swhu	ret = hv_vmbus_post_message(
108282212Swhu		msg,
109282212Swhu		sizeof(hv_vmbus_channel_initiate_contact));
110282212Swhu
111282212Swhu	if (ret != 0) {
112282212Swhu		mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
113282212Swhu		TAILQ_REMOVE(
114282212Swhu			&hv_vmbus_g_connection.channel_msg_anchor,
115282212Swhu			msg_info,
116282212Swhu			msg_list_entry);
117282212Swhu		mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
118282212Swhu		return (ret);
119282212Swhu	}
120282212Swhu
121282212Swhu	/**
122282212Swhu	 * Wait for the connection response
123282212Swhu	 */
124282212Swhu	ret = sema_timedwait(&msg_info->wait_sema, 500); /* KYS 5 seconds */
125282212Swhu
126282212Swhu	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
127282212Swhu	TAILQ_REMOVE(
128282212Swhu		&hv_vmbus_g_connection.channel_msg_anchor,
129282212Swhu		msg_info,
130282212Swhu		msg_list_entry);
131282212Swhu	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
132282212Swhu
133282212Swhu	/**
134282212Swhu	 * Check if successful
135282212Swhu	 */
136282212Swhu	if (msg_info->response.version_response.version_supported) {
137282212Swhu		hv_vmbus_g_connection.connect_state = HV_CONNECTED;
138282212Swhu	} else {
139282212Swhu		ret = ECONNREFUSED;
140282212Swhu	}
141282212Swhu
142282212Swhu	return (ret);
143282212Swhu}
144282212Swhu
145282212Swhu/**
146250199Sgrehan * Send a connect request on the partition service connection
147250199Sgrehan */
148250199Sgrehanint
149250199Sgrehanhv_vmbus_connect(void) {
150250199Sgrehan	int					ret = 0;
151282212Swhu	uint32_t				version;
152250199Sgrehan	hv_vmbus_channel_msg_info*		msg_info = NULL;
153250199Sgrehan
154250199Sgrehan	/**
155250199Sgrehan	 * Make sure we are not connecting or connected
156250199Sgrehan	 */
157250199Sgrehan	if (hv_vmbus_g_connection.connect_state != HV_DISCONNECTED) {
158250199Sgrehan		return (-1);
159250199Sgrehan	}
160250199Sgrehan
161250199Sgrehan	/**
162250199Sgrehan	 * Initialize the vmbus connection
163250199Sgrehan	 */
164250199Sgrehan	hv_vmbus_g_connection.connect_state = HV_CONNECTING;
165250199Sgrehan
166250199Sgrehan	TAILQ_INIT(&hv_vmbus_g_connection.channel_msg_anchor);
167250199Sgrehan	mtx_init(&hv_vmbus_g_connection.channel_msg_lock, "vmbus channel msg",
168250199Sgrehan		NULL, MTX_SPIN);
169250199Sgrehan
170250199Sgrehan	TAILQ_INIT(&hv_vmbus_g_connection.channel_anchor);
171250199Sgrehan	mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel",
172282212Swhu		NULL, MTX_DEF);
173250199Sgrehan
174250199Sgrehan	/**
175250199Sgrehan	 * Setup the vmbus event connection for channel interrupt abstraction
176250199Sgrehan	 * stuff
177250199Sgrehan	 */
178295309Ssephe	hv_vmbus_g_connection.interrupt_page = malloc(
179250199Sgrehan					PAGE_SIZE, M_DEVBUF,
180295309Ssephe					M_WAITOK | M_ZERO);
181250199Sgrehan
182250199Sgrehan	hv_vmbus_g_connection.recv_interrupt_page =
183250199Sgrehan		hv_vmbus_g_connection.interrupt_page;
184250199Sgrehan
185250199Sgrehan	hv_vmbus_g_connection.send_interrupt_page =
186250199Sgrehan		((uint8_t *) hv_vmbus_g_connection.interrupt_page +
187250199Sgrehan		    (PAGE_SIZE >> 1));
188250199Sgrehan
189250199Sgrehan	/**
190250199Sgrehan	 * Set up the monitor notification facility. The 1st page for
191250199Sgrehan	 * parent->child and the 2nd page for child->parent
192250199Sgrehan	 */
193295309Ssephe	hv_vmbus_g_connection.monitor_page_1 = malloc(
194295309Ssephe		PAGE_SIZE,
195250199Sgrehan		M_DEVBUF,
196295309Ssephe		M_WAITOK | M_ZERO);
197295309Ssephe	hv_vmbus_g_connection.monitor_page_2 = malloc(
198250199Sgrehan		PAGE_SIZE,
199295309Ssephe		M_DEVBUF,
200295309Ssephe		M_WAITOK | M_ZERO);
201250199Sgrehan
202250199Sgrehan	msg_info = (hv_vmbus_channel_msg_info*)
203250199Sgrehan		malloc(sizeof(hv_vmbus_channel_msg_info) +
204250199Sgrehan			sizeof(hv_vmbus_channel_initiate_contact),
205295308Ssephe			M_DEVBUF, M_WAITOK | M_ZERO);
206250199Sgrehan
207294553Ssephe	hv_vmbus_g_connection.channels = malloc(sizeof(hv_vmbus_channel*) *
208294553Ssephe		HV_CHANNEL_MAX_COUNT,
209294553Ssephe		M_DEVBUF, M_WAITOK | M_ZERO);
210282212Swhu	/*
211282212Swhu	 * Find the highest vmbus version number we can support.
212250199Sgrehan	 */
213282212Swhu	version = HV_VMBUS_VERSION_CURRENT;
214250199Sgrehan
215282212Swhu	do {
216282212Swhu		ret = hv_vmbus_negotiate_version(msg_info, version);
217282212Swhu		if (ret == EWOULDBLOCK) {
218282212Swhu			/*
219282212Swhu			 * We timed out.
220282212Swhu			 */
221282212Swhu			goto cleanup;
222282212Swhu		}
223250199Sgrehan
224282212Swhu		if (hv_vmbus_g_connection.connect_state == HV_CONNECTED)
225282212Swhu			break;
226250199Sgrehan
227282212Swhu		version = hv_vmbus_get_next_version(version);
228282212Swhu	} while (version != HV_VMBUS_VERSION_INVALID);
229250199Sgrehan
230282212Swhu	hv_vmbus_protocal_version = version;
231282212Swhu	if (bootverbose)
232293870Ssephe		printf("VMBUS: Protocol Version: %d.%d\n",
233282212Swhu		    version >> 16, version & 0xFFFF);
234250199Sgrehan
235250199Sgrehan	sema_destroy(&msg_info->wait_sema);
236250199Sgrehan	free(msg_info, M_DEVBUF);
237250199Sgrehan
238250199Sgrehan	return (0);
239250199Sgrehan
240250199Sgrehan	/*
241250199Sgrehan	 * Cleanup after failure!
242250199Sgrehan	 */
243250199Sgrehan	cleanup:
244250199Sgrehan
245250199Sgrehan	hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
246250199Sgrehan
247250199Sgrehan	mtx_destroy(&hv_vmbus_g_connection.channel_lock);
248250199Sgrehan	mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
249250199Sgrehan
250250199Sgrehan	if (hv_vmbus_g_connection.interrupt_page != NULL) {
251250199Sgrehan		contigfree(
252250199Sgrehan			hv_vmbus_g_connection.interrupt_page,
253250199Sgrehan			PAGE_SIZE,
254250199Sgrehan			M_DEVBUF);
255250199Sgrehan		hv_vmbus_g_connection.interrupt_page = NULL;
256250199Sgrehan	}
257250199Sgrehan
258295309Ssephe	free(hv_vmbus_g_connection.monitor_page_1, M_DEVBUF);
259295309Ssephe	free(hv_vmbus_g_connection.monitor_page_2, M_DEVBUF);
260250199Sgrehan
261250199Sgrehan	if (msg_info) {
262250199Sgrehan		sema_destroy(&msg_info->wait_sema);
263250199Sgrehan		free(msg_info, M_DEVBUF);
264250199Sgrehan	}
265250199Sgrehan
266294553Ssephe	free(hv_vmbus_g_connection.channels, M_DEVBUF);
267250199Sgrehan	return (ret);
268250199Sgrehan}
269250199Sgrehan
270250199Sgrehan/**
271250199Sgrehan * Send a disconnect request on the partition service connection
272250199Sgrehan */
273250199Sgrehanint
274250199Sgrehanhv_vmbus_disconnect(void) {
275250199Sgrehan	int			 ret = 0;
276295308Ssephe	hv_vmbus_channel_unload  msg;
277250199Sgrehan
278295308Ssephe	msg.message_type = HV_CHANNEL_MESSAGE_UNLOAD;
279250199Sgrehan
280295308Ssephe	ret = hv_vmbus_post_message(&msg, sizeof(hv_vmbus_channel_unload));
281250199Sgrehan
282250199Sgrehan	contigfree(hv_vmbus_g_connection.interrupt_page, PAGE_SIZE, M_DEVBUF);
283250199Sgrehan
284250199Sgrehan	mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
285250199Sgrehan
286294553Ssephe	free(hv_vmbus_g_connection.channels, M_DEVBUF);
287250199Sgrehan	hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
288250199Sgrehan
289250199Sgrehan	return (ret);
290250199Sgrehan}
291250199Sgrehan
292250199Sgrehan/**
293250199Sgrehan * Handler for events
294250199Sgrehan */
295250199Sgrehanvoid
296294886Ssephehv_vmbus_on_events(int cpu)
297250199Sgrehan{
298282212Swhu	int bit;
299250199Sgrehan	int dword;
300282212Swhu	void *page_addr;
301282212Swhu	uint32_t* recv_interrupt_page = NULL;
302250199Sgrehan	int rel_id;
303282212Swhu	int maxdword;
304282212Swhu	hv_vmbus_synic_event_flags *event;
305250199Sgrehan	/* int maxdword = PAGE_SIZE >> 3; */
306250199Sgrehan
307282212Swhu	KASSERT(cpu <= mp_maxid, ("VMBUS: hv_vmbus_on_events: "
308282212Swhu	    "cpu out of range!"));
309250199Sgrehan
310282212Swhu	if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) ||
311282212Swhu	    (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)) {
312282212Swhu		maxdword = HV_MAX_NUM_CHANNELS_SUPPORTED >> 5;
313282212Swhu		/*
314282212Swhu		 * receive size is 1/2 page and divide that by 4 bytes
315282212Swhu		 */
316282212Swhu		recv_interrupt_page =
317282212Swhu		    hv_vmbus_g_connection.recv_interrupt_page;
318282212Swhu	} else {
319282212Swhu		/*
320282212Swhu		 * On Host with Win8 or above, the event page can be
321282212Swhu		 * checked directly to get the id of the channel
322282212Swhu		 * that has the pending interrupt.
323282212Swhu		 */
324282212Swhu		maxdword = HV_EVENT_FLAGS_DWORD_COUNT;
325282212Swhu		page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu];
326282212Swhu		event = (hv_vmbus_synic_event_flags *)
327282212Swhu		    page_addr + HV_VMBUS_MESSAGE_SINT;
328282212Swhu		recv_interrupt_page = event->flags32;
329282212Swhu	}
330282212Swhu
331250199Sgrehan	/*
332250199Sgrehan	 * Check events
333250199Sgrehan	 */
334250199Sgrehan	if (recv_interrupt_page != NULL) {
335250199Sgrehan	    for (dword = 0; dword < maxdword; dword++) {
336250199Sgrehan		if (recv_interrupt_page[dword]) {
337294553Ssephe		    for (bit = 0; bit < HV_CHANNEL_DWORD_LEN; bit++) {
338250199Sgrehan			if (synch_test_and_clear_bit(bit,
339250199Sgrehan			    (uint32_t *) &recv_interrupt_page[dword])) {
340250199Sgrehan			    rel_id = (dword << 5) + bit;
341250199Sgrehan			    if (rel_id == 0) {
342250199Sgrehan				/*
343250199Sgrehan				 * Special case -
344250199Sgrehan				 * vmbus channel protocol msg.
345250199Sgrehan				 */
346250199Sgrehan				continue;
347250199Sgrehan			    } else {
348294886Ssephe				hv_vmbus_channel * channel = hv_vmbus_g_connection.channels[rel_id];
349294886Ssephe				/* if channel is closed or closing */
350294886Ssephe				if (channel == NULL || channel->rxq == NULL)
351294886Ssephe					continue;
352250199Sgrehan
353294886Ssephe				if (channel->batched_reading)
354294886Ssephe					hv_ring_buffer_read_begin(&channel->inbound);
355294886Ssephe				taskqueue_enqueue_fast(channel->rxq, &channel->channel_task);
356250199Sgrehan			    }
357250199Sgrehan			}
358250199Sgrehan		    }
359250199Sgrehan		}
360250199Sgrehan	    }
361250199Sgrehan	}
362250199Sgrehan
363250199Sgrehan	return;
364250199Sgrehan}
365250199Sgrehan
366250199Sgrehan/**
367250199Sgrehan * Send a msg on the vmbus's message connection
368250199Sgrehan */
369250199Sgrehanint hv_vmbus_post_message(void *buffer, size_t bufferLen) {
370250199Sgrehan	int ret = 0;
371250199Sgrehan	hv_vmbus_connection_id connId;
372250199Sgrehan	unsigned retries = 0;
373250199Sgrehan
374250199Sgrehan	/* NetScaler delays from previous code were consolidated here */
375250199Sgrehan	static int delayAmount[] = {100, 100, 100, 500, 500, 5000, 5000, 5000};
376250199Sgrehan
377250199Sgrehan	/* for(each entry in delayAmount) try to post message,
378250199Sgrehan	 *  delay a little bit before retrying
379250199Sgrehan	 */
380250199Sgrehan	for (retries = 0;
381250199Sgrehan	    retries < sizeof(delayAmount)/sizeof(delayAmount[0]); retries++) {
382250199Sgrehan	    connId.as_uint32_t = 0;
383250199Sgrehan	    connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID;
384250199Sgrehan	    ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer, bufferLen);
385250199Sgrehan	    if (ret != HV_STATUS_INSUFFICIENT_BUFFERS)
386250199Sgrehan		break;
387250199Sgrehan	    /* TODO: KYS We should use a blocking wait call */
388250199Sgrehan	    DELAY(delayAmount[retries]);
389250199Sgrehan	}
390250199Sgrehan
391250199Sgrehan	KASSERT(ret == 0, ("Error VMBUS: Message Post Failed\n"));
392250199Sgrehan
393250199Sgrehan	return (ret);
394250199Sgrehan}
395250199Sgrehan
396250199Sgrehan/**
397250199Sgrehan * Send an event notification to the parent
398250199Sgrehan */
399250199Sgrehanint
400282212Swhuhv_vmbus_set_event(hv_vmbus_channel *channel) {
401250199Sgrehan	int ret = 0;
402282212Swhu	uint32_t child_rel_id = channel->offer_msg.child_rel_id;
403250199Sgrehan
404250199Sgrehan	/* Each uint32_t represents 32 channels */
405250199Sgrehan
406250199Sgrehan	synch_set_bit(child_rel_id & 31,
407250199Sgrehan		(((uint32_t *)hv_vmbus_g_connection.send_interrupt_page
408250199Sgrehan			+ (child_rel_id >> 5))));
409282212Swhu	ret = hv_vmbus_signal_event(channel->signal_event_param);
410250199Sgrehan
411250199Sgrehan	return (ret);
412250199Sgrehan}
413