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
48250199Sgrehan/**
49250199Sgrehan * Send a connect request on the partition service connection
50250199Sgrehan */
51250199Sgrehanint
52250199Sgrehanhv_vmbus_connect(void) {
53250199Sgrehan	int					ret = 0;
54250199Sgrehan	hv_vmbus_channel_msg_info*		msg_info = NULL;
55250199Sgrehan	hv_vmbus_channel_initiate_contact*	msg;
56250199Sgrehan
57250199Sgrehan	/**
58250199Sgrehan	 * Make sure we are not connecting or connected
59250199Sgrehan	 */
60250199Sgrehan	if (hv_vmbus_g_connection.connect_state != HV_DISCONNECTED) {
61250199Sgrehan		return (-1);
62250199Sgrehan	}
63250199Sgrehan
64250199Sgrehan	/**
65250199Sgrehan	 * Initialize the vmbus connection
66250199Sgrehan	 */
67250199Sgrehan	hv_vmbus_g_connection.connect_state = HV_CONNECTING;
68250199Sgrehan	hv_vmbus_g_connection.work_queue = hv_work_queue_create("vmbusQ");
69250199Sgrehan	sema_init(&hv_vmbus_g_connection.control_sema, 1, "control_sema");
70250199Sgrehan
71250199Sgrehan	TAILQ_INIT(&hv_vmbus_g_connection.channel_msg_anchor);
72250199Sgrehan	mtx_init(&hv_vmbus_g_connection.channel_msg_lock, "vmbus channel msg",
73250199Sgrehan		NULL, MTX_SPIN);
74250199Sgrehan
75250199Sgrehan	TAILQ_INIT(&hv_vmbus_g_connection.channel_anchor);
76250199Sgrehan	mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel",
77250199Sgrehan		NULL, MTX_SPIN);
78250199Sgrehan
79250199Sgrehan	/**
80250199Sgrehan	 * Setup the vmbus event connection for channel interrupt abstraction
81250199Sgrehan	 * stuff
82250199Sgrehan	 */
83250199Sgrehan	hv_vmbus_g_connection.interrupt_page = contigmalloc(
84250199Sgrehan					PAGE_SIZE, M_DEVBUF,
85250199Sgrehan					M_NOWAIT | M_ZERO, 0UL,
86250199Sgrehan					BUS_SPACE_MAXADDR,
87250199Sgrehan					PAGE_SIZE, 0);
88250199Sgrehan	KASSERT(hv_vmbus_g_connection.interrupt_page != NULL,
89250199Sgrehan	    ("Error VMBUS: malloc failed to allocate Channel"
90250199Sgrehan		" Request Event message!"));
91250199Sgrehan	if (hv_vmbus_g_connection.interrupt_page == NULL) {
92250199Sgrehan	    ret = ENOMEM;
93250199Sgrehan	    goto cleanup;
94250199Sgrehan	}
95250199Sgrehan
96250199Sgrehan	hv_vmbus_g_connection.recv_interrupt_page =
97250199Sgrehan		hv_vmbus_g_connection.interrupt_page;
98250199Sgrehan
99250199Sgrehan	hv_vmbus_g_connection.send_interrupt_page =
100250199Sgrehan		((uint8_t *) hv_vmbus_g_connection.interrupt_page +
101250199Sgrehan		    (PAGE_SIZE >> 1));
102250199Sgrehan
103250199Sgrehan	/**
104250199Sgrehan	 * Set up the monitor notification facility. The 1st page for
105250199Sgrehan	 * parent->child and the 2nd page for child->parent
106250199Sgrehan	 */
107250199Sgrehan	hv_vmbus_g_connection.monitor_pages = contigmalloc(
108250199Sgrehan		2 * PAGE_SIZE,
109250199Sgrehan		M_DEVBUF,
110250199Sgrehan		M_NOWAIT | M_ZERO,
111250199Sgrehan		0UL,
112250199Sgrehan		BUS_SPACE_MAXADDR,
113250199Sgrehan		PAGE_SIZE,
114250199Sgrehan		0);
115250199Sgrehan	KASSERT(hv_vmbus_g_connection.monitor_pages != NULL,
116250199Sgrehan	    ("Error VMBUS: malloc failed to allocate Monitor Pages!"));
117250199Sgrehan	if (hv_vmbus_g_connection.monitor_pages == NULL) {
118250199Sgrehan	    ret = ENOMEM;
119250199Sgrehan	    goto cleanup;
120250199Sgrehan	}
121250199Sgrehan
122250199Sgrehan	msg_info = (hv_vmbus_channel_msg_info*)
123250199Sgrehan		malloc(sizeof(hv_vmbus_channel_msg_info) +
124250199Sgrehan			sizeof(hv_vmbus_channel_initiate_contact),
125250199Sgrehan			M_DEVBUF, M_NOWAIT | M_ZERO);
126250199Sgrehan	KASSERT(msg_info != NULL,
127250199Sgrehan	    ("Error VMBUS: malloc failed for Initiate Contact message!"));
128250199Sgrehan	if (msg_info == NULL) {
129250199Sgrehan	    ret = ENOMEM;
130250199Sgrehan	    goto cleanup;
131250199Sgrehan	}
132250199Sgrehan
133250199Sgrehan	sema_init(&msg_info->wait_sema, 0, "Msg Info Sema");
134250199Sgrehan	msg = (hv_vmbus_channel_initiate_contact*) msg_info->msg;
135250199Sgrehan
136250199Sgrehan	msg->header.message_type = HV_CHANNEL_MESSAGE_INITIATED_CONTACT;
137250199Sgrehan	msg->vmbus_version_requested = HV_VMBUS_REVISION_NUMBER;
138250199Sgrehan
139250199Sgrehan	msg->interrupt_page = hv_get_phys_addr(
140250199Sgrehan		hv_vmbus_g_connection.interrupt_page);
141250199Sgrehan
142250199Sgrehan	msg->monitor_page_1 = hv_get_phys_addr(
143250199Sgrehan		hv_vmbus_g_connection.monitor_pages);
144250199Sgrehan
145250199Sgrehan	msg->monitor_page_2 =
146250199Sgrehan		hv_get_phys_addr(
147250199Sgrehan			((uint8_t *) hv_vmbus_g_connection.monitor_pages
148250199Sgrehan			+ PAGE_SIZE));
149250199Sgrehan
150250199Sgrehan	/**
151250199Sgrehan	 * Add to list before we send the request since we may receive the
152250199Sgrehan	 * response before returning from this routine
153250199Sgrehan	 */
154250199Sgrehan	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
155250199Sgrehan
156250199Sgrehan	TAILQ_INSERT_TAIL(
157250199Sgrehan		&hv_vmbus_g_connection.channel_msg_anchor,
158250199Sgrehan		msg_info,
159250199Sgrehan		msg_list_entry);
160250199Sgrehan
161250199Sgrehan	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
162250199Sgrehan
163250199Sgrehan	ret = hv_vmbus_post_message(
164250199Sgrehan		msg,
165250199Sgrehan		sizeof(hv_vmbus_channel_initiate_contact));
166250199Sgrehan
167250199Sgrehan	if (ret != 0) {
168250199Sgrehan		mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
169250199Sgrehan		TAILQ_REMOVE(
170250199Sgrehan			&hv_vmbus_g_connection.channel_msg_anchor,
171250199Sgrehan			msg_info,
172250199Sgrehan			msg_list_entry);
173250199Sgrehan		mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
174250199Sgrehan		goto cleanup;
175250199Sgrehan	}
176250199Sgrehan
177250199Sgrehan	/**
178250199Sgrehan	 * Wait for the connection response
179250199Sgrehan	 */
180250199Sgrehan	ret = sema_timedwait(&msg_info->wait_sema, 500); /* KYS 5 seconds */
181250199Sgrehan
182250199Sgrehan	mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
183250199Sgrehan	TAILQ_REMOVE(
184250199Sgrehan		&hv_vmbus_g_connection.channel_msg_anchor,
185250199Sgrehan		msg_info,
186250199Sgrehan		msg_list_entry);
187250199Sgrehan	mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
188250199Sgrehan
189250199Sgrehan	/**
190250199Sgrehan	 * Check if successful
191250199Sgrehan	 */
192250199Sgrehan	if (msg_info->response.version_response.version_supported) {
193250199Sgrehan		hv_vmbus_g_connection.connect_state = HV_CONNECTED;
194250199Sgrehan	} else {
195250199Sgrehan		ret = ECONNREFUSED;
196250199Sgrehan		goto cleanup;
197250199Sgrehan	}
198250199Sgrehan
199250199Sgrehan	sema_destroy(&msg_info->wait_sema);
200250199Sgrehan	free(msg_info, M_DEVBUF);
201250199Sgrehan
202250199Sgrehan	return (0);
203250199Sgrehan
204250199Sgrehan	/*
205250199Sgrehan	 * Cleanup after failure!
206250199Sgrehan	 */
207250199Sgrehan	cleanup:
208250199Sgrehan
209250199Sgrehan	hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
210250199Sgrehan
211250199Sgrehan	hv_work_queue_close(hv_vmbus_g_connection.work_queue);
212250199Sgrehan	sema_destroy(&hv_vmbus_g_connection.control_sema);
213250199Sgrehan	mtx_destroy(&hv_vmbus_g_connection.channel_lock);
214250199Sgrehan	mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
215250199Sgrehan
216250199Sgrehan	if (hv_vmbus_g_connection.interrupt_page != NULL) {
217250199Sgrehan		contigfree(
218250199Sgrehan			hv_vmbus_g_connection.interrupt_page,
219250199Sgrehan			PAGE_SIZE,
220250199Sgrehan			M_DEVBUF);
221250199Sgrehan		hv_vmbus_g_connection.interrupt_page = NULL;
222250199Sgrehan	}
223250199Sgrehan
224250199Sgrehan	if (hv_vmbus_g_connection.monitor_pages != NULL) {
225250199Sgrehan		contigfree(
226250199Sgrehan			hv_vmbus_g_connection.monitor_pages,
227250199Sgrehan			2 * PAGE_SIZE,
228250199Sgrehan			M_DEVBUF);
229250199Sgrehan		hv_vmbus_g_connection.monitor_pages = NULL;
230250199Sgrehan	}
231250199Sgrehan
232250199Sgrehan	if (msg_info) {
233250199Sgrehan		sema_destroy(&msg_info->wait_sema);
234250199Sgrehan		free(msg_info, M_DEVBUF);
235250199Sgrehan	}
236250199Sgrehan
237250199Sgrehan	return (ret);
238250199Sgrehan}
239250199Sgrehan
240250199Sgrehan/**
241250199Sgrehan * Send a disconnect request on the partition service connection
242250199Sgrehan */
243250199Sgrehanint
244250199Sgrehanhv_vmbus_disconnect(void) {
245250199Sgrehan	int			 ret = 0;
246250199Sgrehan	hv_vmbus_channel_unload* msg;
247250199Sgrehan
248250199Sgrehan	msg = malloc(sizeof(hv_vmbus_channel_unload),
249250199Sgrehan	    M_DEVBUF, M_NOWAIT | M_ZERO);
250250199Sgrehan	KASSERT(msg != NULL,
251250199Sgrehan	    ("Error VMBUS: malloc failed to allocate Channel Unload Msg!"));
252250199Sgrehan	if (msg == NULL)
253250199Sgrehan	    return (ENOMEM);
254250199Sgrehan
255250199Sgrehan	msg->message_type = HV_CHANNEL_MESSAGE_UNLOAD;
256250199Sgrehan
257250199Sgrehan	ret = hv_vmbus_post_message(msg, sizeof(hv_vmbus_channel_unload));
258250199Sgrehan
259250199Sgrehan
260250199Sgrehan	contigfree(hv_vmbus_g_connection.interrupt_page, PAGE_SIZE, M_DEVBUF);
261250199Sgrehan
262250199Sgrehan	mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
263250199Sgrehan
264250199Sgrehan	hv_work_queue_close(hv_vmbus_g_connection.work_queue);
265250199Sgrehan	sema_destroy(&hv_vmbus_g_connection.control_sema);
266250199Sgrehan
267250199Sgrehan	hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
268250199Sgrehan
269250199Sgrehan	free(msg, M_DEVBUF);
270250199Sgrehan
271250199Sgrehan	return (ret);
272250199Sgrehan}
273250199Sgrehan
274250199Sgrehan/**
275250199Sgrehan * Get the channel object given its child relative id (ie channel id)
276250199Sgrehan */
277250199Sgrehanhv_vmbus_channel*
278250199Sgrehanhv_vmbus_get_channel_from_rel_id(uint32_t rel_id) {
279250199Sgrehan
280250199Sgrehan	hv_vmbus_channel* channel;
281250199Sgrehan	hv_vmbus_channel* foundChannel = NULL;
282250199Sgrehan
283250199Sgrehan	/*
284250199Sgrehan	 * TODO:
285250199Sgrehan	 * Consider optimization where relids are stored in a fixed size array
286250199Sgrehan	 *  and channels are accessed without the need to take this lock or search
287250199Sgrehan	 *  the list.
288250199Sgrehan	 */
289250199Sgrehan	mtx_lock_spin(&hv_vmbus_g_connection.channel_lock);
290250199Sgrehan	TAILQ_FOREACH(channel,
291250199Sgrehan		&hv_vmbus_g_connection.channel_anchor, list_entry) {
292250199Sgrehan
293250199Sgrehan	    if (channel->offer_msg.child_rel_id == rel_id) {
294250199Sgrehan		foundChannel = channel;
295250199Sgrehan		break;
296250199Sgrehan	    }
297250199Sgrehan	}
298250199Sgrehan	mtx_unlock_spin(&hv_vmbus_g_connection.channel_lock);
299250199Sgrehan
300250199Sgrehan	return (foundChannel);
301250199Sgrehan}
302250199Sgrehan
303250199Sgrehan/**
304250199Sgrehan * Process a channel event notification
305250199Sgrehan */
306250199Sgrehanstatic void
307250199SgrehanVmbusProcessChannelEvent(uint32_t relid)
308250199Sgrehan{
309250199Sgrehan	hv_vmbus_channel* channel;
310250199Sgrehan
311250199Sgrehan	/**
312250199Sgrehan	 * Find the channel based on this relid and invokes
313250199Sgrehan	 * the channel callback to process the event
314250199Sgrehan	 */
315250199Sgrehan
316250199Sgrehan	channel = hv_vmbus_get_channel_from_rel_id(relid);
317250199Sgrehan
318250199Sgrehan	if (channel == NULL) {
319250199Sgrehan		return;
320250199Sgrehan	}
321250199Sgrehan	/**
322250199Sgrehan	 * To deal with the race condition where we might
323250199Sgrehan	 * receive a packet while the relevant driver is
324250199Sgrehan	 * being unloaded, dispatch the callback while
325250199Sgrehan	 * holding the channel lock. The unloading driver
326250199Sgrehan	 * will acquire the same channel lock to set the
327250199Sgrehan	 * callback to NULL. This closes the window.
328250199Sgrehan	 */
329250199Sgrehan
330250199Sgrehan	mtx_lock(&channel->inbound_lock);
331250199Sgrehan	if (channel->on_channel_callback != NULL) {
332250199Sgrehan		channel->on_channel_callback(channel->channel_callback_context);
333250199Sgrehan	}
334250199Sgrehan	mtx_unlock(&channel->inbound_lock);
335250199Sgrehan}
336250199Sgrehan
337250199Sgrehan/**
338250199Sgrehan * Handler for events
339250199Sgrehan */
340250199Sgrehanvoid
341250199Sgrehanhv_vmbus_on_events(void *arg)
342250199Sgrehan{
343250199Sgrehan	int dword;
344250199Sgrehan	int bit;
345250199Sgrehan	int rel_id;
346250199Sgrehan	int maxdword = HV_MAX_NUM_CHANNELS_SUPPORTED >> 5;
347250199Sgrehan	/* int maxdword = PAGE_SIZE >> 3; */
348250199Sgrehan
349250199Sgrehan	/*
350250199Sgrehan	 * receive size is 1/2 page and divide that by 4 bytes
351250199Sgrehan	 */
352250199Sgrehan
353250199Sgrehan	uint32_t* recv_interrupt_page =
354250199Sgrehan	    hv_vmbus_g_connection.recv_interrupt_page;
355250199Sgrehan
356250199Sgrehan	/*
357250199Sgrehan	 * Check events
358250199Sgrehan	 */
359250199Sgrehan	if (recv_interrupt_page != NULL) {
360250199Sgrehan	    for (dword = 0; dword < maxdword; dword++) {
361250199Sgrehan		if (recv_interrupt_page[dword]) {
362250199Sgrehan		    for (bit = 0; bit < 32; bit++) {
363250199Sgrehan			if (synch_test_and_clear_bit(bit,
364250199Sgrehan			    (uint32_t *) &recv_interrupt_page[dword])) {
365250199Sgrehan			    rel_id = (dword << 5) + bit;
366250199Sgrehan			    if (rel_id == 0) {
367250199Sgrehan				/*
368250199Sgrehan				 * Special case -
369250199Sgrehan				 * vmbus channel protocol msg.
370250199Sgrehan				 */
371250199Sgrehan				continue;
372250199Sgrehan			    } else {
373250199Sgrehan				VmbusProcessChannelEvent(rel_id);
374250199Sgrehan
375250199Sgrehan			    }
376250199Sgrehan			}
377250199Sgrehan		    }
378250199Sgrehan		}
379250199Sgrehan	    }
380250199Sgrehan	}
381250199Sgrehan
382250199Sgrehan	return;
383250199Sgrehan}
384250199Sgrehan
385250199Sgrehan/**
386250199Sgrehan * Send a msg on the vmbus's message connection
387250199Sgrehan */
388250199Sgrehanint hv_vmbus_post_message(void *buffer, size_t bufferLen) {
389250199Sgrehan	int ret = 0;
390250199Sgrehan	hv_vmbus_connection_id connId;
391250199Sgrehan	unsigned retries = 0;
392250199Sgrehan
393250199Sgrehan	/* NetScaler delays from previous code were consolidated here */
394250199Sgrehan	static int delayAmount[] = {100, 100, 100, 500, 500, 5000, 5000, 5000};
395250199Sgrehan
396250199Sgrehan	/* for(each entry in delayAmount) try to post message,
397250199Sgrehan	 *  delay a little bit before retrying
398250199Sgrehan	 */
399250199Sgrehan	for (retries = 0;
400250199Sgrehan	    retries < sizeof(delayAmount)/sizeof(delayAmount[0]); retries++) {
401250199Sgrehan	    connId.as_uint32_t = 0;
402250199Sgrehan	    connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID;
403250199Sgrehan	    ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer, bufferLen);
404250199Sgrehan	    if (ret != HV_STATUS_INSUFFICIENT_BUFFERS)
405250199Sgrehan		break;
406250199Sgrehan	    /* TODO: KYS We should use a blocking wait call */
407250199Sgrehan	    DELAY(delayAmount[retries]);
408250199Sgrehan	}
409250199Sgrehan
410250199Sgrehan	KASSERT(ret == 0, ("Error VMBUS: Message Post Failed\n"));
411250199Sgrehan
412250199Sgrehan	return (ret);
413250199Sgrehan}
414250199Sgrehan
415250199Sgrehan/**
416250199Sgrehan * Send an event notification to the parent
417250199Sgrehan */
418250199Sgrehanint
419250199Sgrehanhv_vmbus_set_event(uint32_t child_rel_id) {
420250199Sgrehan	int ret = 0;
421250199Sgrehan
422250199Sgrehan	/* Each uint32_t represents 32 channels */
423250199Sgrehan
424250199Sgrehan	synch_set_bit(child_rel_id & 31,
425250199Sgrehan		(((uint32_t *)hv_vmbus_g_connection.send_interrupt_page
426250199Sgrehan			+ (child_rel_id >> 5))));
427250199Sgrehan	ret = hv_vmbus_signal_event();
428250199Sgrehan
429250199Sgrehan	return (ret);
430250199Sgrehan}
431250199Sgrehan
432