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
29283280Swhu#include <sys/cdefs.h>
30283280Swhu__FBSDID("$FreeBSD: releng/10.3/sys/dev/hyperv/vmbus/hv_channel_mgmt.c 303984 2016-08-12 04:01:16Z glebius $");
31283280Swhu
32250199Sgrehan#include <sys/param.h>
33250199Sgrehan#include <sys/mbuf.h>
34250199Sgrehan
35250199Sgrehan#include "hv_vmbus_priv.h"
36250199Sgrehan
37250199Sgrehan/*
38250199Sgrehan * Internal functions
39250199Sgrehan */
40250199Sgrehan
41250199Sgrehanstatic void vmbus_channel_on_offer(hv_vmbus_channel_msg_header* hdr);
42250199Sgrehanstatic void vmbus_channel_on_open_result(hv_vmbus_channel_msg_header* hdr);
43250199Sgrehanstatic void vmbus_channel_on_offer_rescind(hv_vmbus_channel_msg_header* hdr);
44250199Sgrehanstatic void vmbus_channel_on_gpadl_created(hv_vmbus_channel_msg_header* hdr);
45250199Sgrehanstatic void vmbus_channel_on_gpadl_torndown(hv_vmbus_channel_msg_header* hdr);
46250199Sgrehanstatic void vmbus_channel_on_offers_delivered(hv_vmbus_channel_msg_header* hdr);
47250199Sgrehanstatic void vmbus_channel_on_version_response(hv_vmbus_channel_msg_header* hdr);
48250199Sgrehan
49250199Sgrehan/**
50250199Sgrehan * Channel message dispatch table
51250199Sgrehan */
52250199Sgrehanhv_vmbus_channel_msg_table_entry
53250199Sgrehan    g_channel_message_table[HV_CHANNEL_MESSAGE_COUNT] = {
54293820Sdelphij	{ HV_CHANNEL_MESSAGE_INVALID,
55293820Sdelphij		0, NULL },
56293820Sdelphij	{ HV_CHANNEL_MESSAGE_OFFER_CHANNEL,
57293820Sdelphij		0, vmbus_channel_on_offer },
58250199Sgrehan	{ HV_CHANNEL_MESSAGE_RESCIND_CHANNEL_OFFER,
59293820Sdelphij		0, vmbus_channel_on_offer_rescind },
60293820Sdelphij	{ HV_CHANNEL_MESSAGE_REQUEST_OFFERS,
61293820Sdelphij		0, NULL },
62250199Sgrehan	{ HV_CHANNEL_MESSAGE_ALL_OFFERS_DELIVERED,
63293820Sdelphij		1, vmbus_channel_on_offers_delivered },
64293820Sdelphij	{ HV_CHANNEL_MESSAGE_OPEN_CHANNEL,
65293820Sdelphij		0, NULL },
66250199Sgrehan	{ HV_CHANNEL_MESSAGE_OPEN_CHANNEL_RESULT,
67293820Sdelphij		1, vmbus_channel_on_open_result },
68293820Sdelphij	{ HV_CHANNEL_MESSAGE_CLOSE_CHANNEL,
69293820Sdelphij		0, NULL },
70293820Sdelphij	{ HV_CHANNEL_MESSAGEL_GPADL_HEADER,
71293820Sdelphij		0, NULL },
72293820Sdelphij	{ HV_CHANNEL_MESSAGE_GPADL_BODY,
73293820Sdelphij		0, NULL },
74250199Sgrehan	{ HV_CHANNEL_MESSAGE_GPADL_CREATED,
75293820Sdelphij		1, vmbus_channel_on_gpadl_created },
76293820Sdelphij	{ HV_CHANNEL_MESSAGE_GPADL_TEARDOWN,
77293820Sdelphij		0, NULL },
78250199Sgrehan	{ HV_CHANNEL_MESSAGE_GPADL_TORNDOWN,
79293820Sdelphij		1, vmbus_channel_on_gpadl_torndown },
80293820Sdelphij	{ HV_CHANNEL_MESSAGE_REL_ID_RELEASED,
81293820Sdelphij		0, NULL },
82293820Sdelphij	{ HV_CHANNEL_MESSAGE_INITIATED_CONTACT,
83293820Sdelphij		0, NULL },
84250199Sgrehan	{ HV_CHANNEL_MESSAGE_VERSION_RESPONSE,
85293820Sdelphij		1, vmbus_channel_on_version_response },
86293820Sdelphij	{ HV_CHANNEL_MESSAGE_UNLOAD,
87293820Sdelphij		0, NULL }
88250199Sgrehan};
89250199Sgrehan
90250199Sgrehan
91250199Sgrehan/**
92250199Sgrehan * Implementation of the work abstraction.
93250199Sgrehan */
94250199Sgrehanstatic void
95250199Sgrehanwork_item_callback(void *work, int pending)
96250199Sgrehan{
97250199Sgrehan	struct hv_work_item *w = (struct hv_work_item *)work;
98250199Sgrehan
99250199Sgrehan	/*
100250199Sgrehan	 * Serialize work execution.
101250199Sgrehan	 */
102250199Sgrehan	if (w->wq->work_sema != NULL) {
103250199Sgrehan		sema_wait(w->wq->work_sema);
104250199Sgrehan	}
105250199Sgrehan
106250199Sgrehan	w->callback(w->context);
107250199Sgrehan
108250199Sgrehan	if (w->wq->work_sema != NULL) {
109250199Sgrehan		sema_post(w->wq->work_sema);
110250199Sgrehan	}
111250199Sgrehan
112250199Sgrehan	free(w, M_DEVBUF);
113250199Sgrehan}
114250199Sgrehan
115250199Sgrehanstruct hv_work_queue*
116250199Sgrehanhv_work_queue_create(char* name)
117250199Sgrehan{
118250199Sgrehan	static unsigned int	qid = 0;
119250199Sgrehan	char			qname[64];
120250199Sgrehan	int			pri;
121250199Sgrehan	struct hv_work_queue*	wq;
122250199Sgrehan
123250199Sgrehan	wq = malloc(sizeof(struct hv_work_queue), M_DEVBUF, M_NOWAIT | M_ZERO);
124250199Sgrehan	KASSERT(wq != NULL, ("Error VMBUS: Failed to allocate work_queue\n"));
125250199Sgrehan	if (wq == NULL)
126250199Sgrehan	    return (NULL);
127250199Sgrehan
128250199Sgrehan	/*
129250199Sgrehan	 * We use work abstraction to handle messages
130250199Sgrehan	 * coming from the host and these are typically offers.
131250199Sgrehan	 * Some FreeBsd drivers appear to have a concurrency issue
132250199Sgrehan	 * where probe/attach needs to be serialized. We ensure that
133250199Sgrehan	 * by having only one thread process work elements in a
134250199Sgrehan	 * specific queue by serializing work execution.
135250199Sgrehan	 *
136250199Sgrehan	 */
137250199Sgrehan	if (strcmp(name, "vmbusQ") == 0) {
138250199Sgrehan	    pri = PI_DISK;
139250199Sgrehan	} else { /* control */
140250199Sgrehan	    pri = PI_NET;
141250199Sgrehan	    /*
142250199Sgrehan	     * Initialize semaphore for this queue by pointing
143250199Sgrehan	     * to the globale semaphore used for synchronizing all
144250199Sgrehan	     * control messages.
145250199Sgrehan	     */
146250199Sgrehan	    wq->work_sema = &hv_vmbus_g_connection.control_sema;
147250199Sgrehan	}
148250199Sgrehan
149250199Sgrehan	sprintf(qname, "hv_%s_%u", name, qid);
150250199Sgrehan
151250199Sgrehan	/*
152250199Sgrehan	 * Fixme:  FreeBSD 8.2 has a different prototype for
153250199Sgrehan	 * taskqueue_create(), and for certain other taskqueue functions.
154250199Sgrehan	 * We need to research the implications of these changes.
155250199Sgrehan	 * Fixme:  Not sure when the changes were introduced.
156250199Sgrehan	 */
157250199Sgrehan	wq->queue = taskqueue_create(qname, M_NOWAIT, taskqueue_thread_enqueue,
158250199Sgrehan	    &wq->queue
159250199Sgrehan	    #if __FreeBSD_version < 800000
160250199Sgrehan	    , &wq->proc
161250199Sgrehan	    #endif
162250199Sgrehan	    );
163250199Sgrehan
164250199Sgrehan	if (wq->queue == NULL) {
165250199Sgrehan	    free(wq, M_DEVBUF);
166250199Sgrehan	    return (NULL);
167250199Sgrehan	}
168250199Sgrehan
169250199Sgrehan	if (taskqueue_start_threads(&wq->queue, 1, pri, "%s taskq", qname)) {
170250199Sgrehan	    taskqueue_free(wq->queue);
171250199Sgrehan	    free(wq, M_DEVBUF);
172250199Sgrehan	    return (NULL);
173250199Sgrehan	}
174250199Sgrehan
175250199Sgrehan	qid++;
176250199Sgrehan
177250199Sgrehan	return (wq);
178250199Sgrehan}
179250199Sgrehan
180250199Sgrehanvoid
181250199Sgrehanhv_work_queue_close(struct hv_work_queue *wq)
182250199Sgrehan{
183250199Sgrehan	/*
184250199Sgrehan	 * KYS: Need to drain the taskqueue
185250199Sgrehan	 * before we close the hv_work_queue.
186250199Sgrehan	 */
187250199Sgrehan	/*KYS: taskqueue_drain(wq->tq, ); */
188250199Sgrehan	taskqueue_free(wq->queue);
189250199Sgrehan	free(wq, M_DEVBUF);
190250199Sgrehan}
191250199Sgrehan
192250199Sgrehan/**
193250199Sgrehan * @brief Create work item
194250199Sgrehan */
195250199Sgrehanint
196250199Sgrehanhv_queue_work_item(
197250199Sgrehan	struct hv_work_queue *wq,
198250199Sgrehan	void (*callback)(void *), void *context)
199250199Sgrehan{
200250199Sgrehan	struct hv_work_item *w = malloc(sizeof(struct hv_work_item),
201250199Sgrehan					M_DEVBUF, M_NOWAIT | M_ZERO);
202250199Sgrehan	KASSERT(w != NULL, ("Error VMBUS: Failed to allocate WorkItem\n"));
203250199Sgrehan	if (w == NULL)
204250199Sgrehan	    return (ENOMEM);
205250199Sgrehan
206250199Sgrehan	w->callback = callback;
207250199Sgrehan	w->context = context;
208250199Sgrehan	w->wq = wq;
209250199Sgrehan
210250199Sgrehan	TASK_INIT(&w->work, 0, work_item_callback, w);
211250199Sgrehan
212250199Sgrehan	return (taskqueue_enqueue(wq->queue, &w->work));
213250199Sgrehan}
214250199Sgrehan
215250199Sgrehan
216250199Sgrehan/**
217250199Sgrehan * @brief Allocate and initialize a vmbus channel object
218250199Sgrehan */
219250199Sgrehanhv_vmbus_channel*
220250199Sgrehanhv_vmbus_allocate_channel(void)
221250199Sgrehan{
222250199Sgrehan	hv_vmbus_channel* channel;
223250199Sgrehan
224250199Sgrehan	channel = (hv_vmbus_channel*) malloc(
225250199Sgrehan					sizeof(hv_vmbus_channel),
226250199Sgrehan					M_DEVBUF,
227250199Sgrehan					M_NOWAIT | M_ZERO);
228250199Sgrehan	KASSERT(channel != NULL, ("Error VMBUS: Failed to allocate channel!"));
229250199Sgrehan	if (channel == NULL)
230250199Sgrehan	    return (NULL);
231250199Sgrehan
232250199Sgrehan	mtx_init(&channel->inbound_lock, "channel inbound", NULL, MTX_DEF);
233283280Swhu	mtx_init(&channel->sc_lock, "vmbus multi channel", NULL, MTX_DEF);
234250199Sgrehan
235283280Swhu	TAILQ_INIT(&channel->sc_list_anchor);
236283280Swhu
237250199Sgrehan	return (channel);
238250199Sgrehan}
239250199Sgrehan
240250199Sgrehan/**
241250199Sgrehan * @brief Release the vmbus channel object itself
242250199Sgrehan */
243250199Sgrehanstatic inline void
244250199SgrehanReleaseVmbusChannel(void *context)
245250199Sgrehan{
246250199Sgrehan	hv_vmbus_channel* channel = (hv_vmbus_channel*) context;
247250199Sgrehan	free(channel, M_DEVBUF);
248250199Sgrehan}
249250199Sgrehan
250250199Sgrehan/**
251250199Sgrehan * @brief Release the resources used by the vmbus channel object
252250199Sgrehan */
253250199Sgrehanvoid
254250199Sgrehanhv_vmbus_free_vmbus_channel(hv_vmbus_channel* channel)
255250199Sgrehan{
256283280Swhu	mtx_destroy(&channel->sc_lock);
257250199Sgrehan	mtx_destroy(&channel->inbound_lock);
258250199Sgrehan	/*
259250199Sgrehan	 * We have to release the channel's workqueue/thread in
260250199Sgrehan	 *  the vmbus's workqueue/thread context
261250199Sgrehan	 * ie we can't destroy ourselves
262250199Sgrehan	 */
263250199Sgrehan	hv_queue_work_item(hv_vmbus_g_connection.work_queue,
264250199Sgrehan	    ReleaseVmbusChannel, (void *) channel);
265250199Sgrehan}
266250199Sgrehan
267250199Sgrehan/**
268250199Sgrehan * @brief Process the offer by creating a channel/device
269250199Sgrehan * associated with this offer
270250199Sgrehan */
271250199Sgrehanstatic void
272293820Sdelphijvmbus_channel_process_offer(hv_vmbus_channel *new_channel)
273250199Sgrehan{
274250199Sgrehan	boolean_t		f_new;
275250199Sgrehan	hv_vmbus_channel*	channel;
276283280Swhu	int			ret;
277295948Ssephe	uint32_t                relid;
278250199Sgrehan
279250199Sgrehan	f_new = TRUE;
280250199Sgrehan	channel = NULL;
281295948Ssephe	relid = new_channel->offer_msg.child_rel_id;
282250199Sgrehan	/*
283250199Sgrehan	 * Make sure this is a new offer
284250199Sgrehan	 */
285283280Swhu	mtx_lock(&hv_vmbus_g_connection.channel_lock);
286295948Ssephe	hv_vmbus_g_connection.channels[relid] = new_channel;
287250199Sgrehan
288250199Sgrehan	TAILQ_FOREACH(channel, &hv_vmbus_g_connection.channel_anchor,
289250199Sgrehan	    list_entry)
290250199Sgrehan	{
291283280Swhu		if (memcmp(&channel->offer_msg.offer.interface_type,
292283280Swhu		    &new_channel->offer_msg.offer.interface_type,
293283280Swhu		    sizeof(hv_guid)) == 0 &&
294283280Swhu		    memcmp(&channel->offer_msg.offer.interface_instance,
295250199Sgrehan		    &new_channel->offer_msg.offer.interface_instance,
296283280Swhu		    sizeof(hv_guid)) == 0) {
297283280Swhu			f_new = FALSE;
298283280Swhu			break;
299283280Swhu		}
300250199Sgrehan	}
301250199Sgrehan
302250199Sgrehan	if (f_new) {
303283280Swhu		/* Insert at tail */
304283280Swhu		TAILQ_INSERT_TAIL(
305283280Swhu		    &hv_vmbus_g_connection.channel_anchor,
306283280Swhu		    new_channel,
307283280Swhu		    list_entry);
308250199Sgrehan	}
309283280Swhu	mtx_unlock(&hv_vmbus_g_connection.channel_lock);
310250199Sgrehan
311283280Swhu	/*XXX add new channel to percpu_list */
312283280Swhu
313250199Sgrehan	if (!f_new) {
314283280Swhu		/*
315283280Swhu		 * Check if this is a sub channel.
316283280Swhu		 */
317283280Swhu		if (new_channel->offer_msg.offer.sub_channel_index != 0) {
318283280Swhu			/*
319283280Swhu			 * It is a sub channel offer, process it.
320283280Swhu			 */
321283280Swhu			new_channel->primary_channel = channel;
322283280Swhu			mtx_lock(&channel->sc_lock);
323283280Swhu			TAILQ_INSERT_TAIL(
324283280Swhu			    &channel->sc_list_anchor,
325283280Swhu			    new_channel,
326283280Swhu			    sc_list_entry);
327283280Swhu			mtx_unlock(&channel->sc_lock);
328283280Swhu
329283280Swhu			/* Insert new channel into channel_anchor. */
330295948Ssephe			printf("VMBUS get multi-channel offer, rel=%u,sub=%u\n",
331295948Ssephe			    new_channel->offer_msg.child_rel_id,
332295948Ssephe			    new_channel->offer_msg.offer.sub_channel_index);
333283280Swhu			mtx_lock(&hv_vmbus_g_connection.channel_lock);
334283280Swhu			TAILQ_INSERT_TAIL(&hv_vmbus_g_connection.channel_anchor,
335283280Swhu			    new_channel, list_entry);
336283280Swhu			mtx_unlock(&hv_vmbus_g_connection.channel_lock);
337283280Swhu
338283280Swhu			if(bootverbose)
339295948Ssephe				printf("VMBUS: new multi-channel offer <%p>, "
340295948Ssephe				    "its primary channel is <%p>.\n",
341295948Ssephe				    new_channel, new_channel->primary_channel);
342283280Swhu
343283280Swhu			/*XXX add it to percpu_list */
344283280Swhu
345283280Swhu			new_channel->state = HV_CHANNEL_OPEN_STATE;
346283280Swhu			if (channel->sc_creation_callback != NULL) {
347283280Swhu				channel->sc_creation_callback(new_channel);
348283280Swhu			}
349283280Swhu			return;
350283280Swhu		}
351283280Swhu
352250199Sgrehan	    hv_vmbus_free_vmbus_channel(new_channel);
353250199Sgrehan	    return;
354250199Sgrehan	}
355250199Sgrehan
356283280Swhu	new_channel->state = HV_CHANNEL_OPEN_STATE;
357283280Swhu
358250199Sgrehan	/*
359250199Sgrehan	 * Start the process of binding this offer to the driver
360250199Sgrehan	 * (We need to set the device field before calling
361250199Sgrehan	 * hv_vmbus_child_device_add())
362250199Sgrehan	 */
363250199Sgrehan	new_channel->device = hv_vmbus_child_device_create(
364250199Sgrehan	    new_channel->offer_msg.offer.interface_type,
365250199Sgrehan	    new_channel->offer_msg.offer.interface_instance, new_channel);
366250199Sgrehan
367250199Sgrehan	/*
368250199Sgrehan	 * Add the new device to the bus. This will kick off device-driver
369250199Sgrehan	 * binding which eventually invokes the device driver's AddDevice()
370250199Sgrehan	 * method.
371250199Sgrehan	 */
372250199Sgrehan	ret = hv_vmbus_child_device_register(new_channel->device);
373250199Sgrehan	if (ret != 0) {
374283280Swhu		mtx_lock(&hv_vmbus_g_connection.channel_lock);
375283280Swhu		TAILQ_REMOVE(
376283280Swhu		    &hv_vmbus_g_connection.channel_anchor,
377283280Swhu		    new_channel,
378283280Swhu		    list_entry);
379283280Swhu		mtx_unlock(&hv_vmbus_g_connection.channel_lock);
380283280Swhu		hv_vmbus_free_vmbus_channel(new_channel);
381283280Swhu	}
382283280Swhu}
383250199Sgrehan
384283280Swhu/**
385283280Swhu * Array of device guids that are performance critical. We try to distribute
386283280Swhu * the interrupt load for these devices across all online cpus.
387283280Swhu */
388283280Swhustatic const hv_guid high_perf_devices[] = {
389283280Swhu	{HV_NIC_GUID, },
390283280Swhu	{HV_IDE_GUID, },
391283280Swhu	{HV_SCSI_GUID, },
392283280Swhu};
393283280Swhu
394283280Swhuenum {
395283280Swhu	PERF_CHN_NIC = 0,
396283280Swhu	PERF_CHN_IDE,
397283280Swhu	PERF_CHN_SCSI,
398283280Swhu	MAX_PERF_CHN,
399283280Swhu};
400283280Swhu
401283280Swhu/*
402283280Swhu * We use this static number to distribute the channel interrupt load.
403283280Swhu */
404283280Swhustatic uint32_t next_vcpu;
405283280Swhu
406283280Swhu/**
407283280Swhu * Starting with Win8, we can statically distribute the incoming
408283280Swhu * channel interrupt load by binding a channel to VCPU. We
409283280Swhu * implement here a simple round robin scheme for distributing
410283280Swhu * the interrupt load.
411283280Swhu * We will bind channels that are not performance critical to cpu 0 and
412283280Swhu * performance critical channels (IDE, SCSI and Network) will be uniformly
413283280Swhu * distributed across all available CPUs.
414283280Swhu */
415283280Swhustatic void
416283280Swhuvmbus_channel_select_cpu(hv_vmbus_channel *channel, hv_guid *guid)
417283280Swhu{
418283280Swhu	uint32_t current_cpu;
419283280Swhu	int i;
420283280Swhu	boolean_t is_perf_channel = FALSE;
421283280Swhu
422283280Swhu	for (i = PERF_CHN_NIC; i < MAX_PERF_CHN; i++) {
423283280Swhu		if (memcmp(guid->data, high_perf_devices[i].data,
424283280Swhu		    sizeof(hv_guid)) == 0) {
425283280Swhu			is_perf_channel = TRUE;
426283280Swhu			break;
427283280Swhu		}
428250199Sgrehan	}
429283280Swhu
430283280Swhu	if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) ||
431283280Swhu	    (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7) ||
432283280Swhu	    (!is_perf_channel)) {
433283280Swhu		/* Host's view of guest cpu */
434283280Swhu		channel->target_vcpu = 0;
435283280Swhu		/* Guest's own view of cpu */
436283280Swhu		channel->target_cpu = 0;
437283280Swhu		return;
438283280Swhu	}
439283280Swhu	/* mp_ncpus should have the number cpus currently online */
440283280Swhu	current_cpu = (++next_vcpu % mp_ncpus);
441283280Swhu	channel->target_cpu = current_cpu;
442283280Swhu	channel->target_vcpu =
443283280Swhu	    hv_vmbus_g_context.hv_vcpu_index[current_cpu];
444283280Swhu	if (bootverbose)
445283280Swhu		printf("VMBUS: Total online cpus %d, assign perf channel %d "
446283280Swhu		    "to vcpu %d, cpu %d\n", mp_ncpus, i, channel->target_vcpu,
447283280Swhu		    current_cpu);
448250199Sgrehan}
449250199Sgrehan
450250199Sgrehan/**
451250199Sgrehan * @brief Handler for channel offers from Hyper-V/Azure
452250199Sgrehan *
453250199Sgrehan * Handler for channel offers from vmbus in parent partition. We ignore
454250199Sgrehan * all offers except network and storage offers. For each network and storage
455250199Sgrehan * offers, we create a channel object and queue a work item to the channel
456250199Sgrehan * object to process the offer synchronously
457250199Sgrehan */
458250199Sgrehanstatic void
459250199Sgrehanvmbus_channel_on_offer(hv_vmbus_channel_msg_header* hdr)
460250199Sgrehan{
461250199Sgrehan	hv_vmbus_channel_offer_channel* offer;
462250199Sgrehan	hv_vmbus_channel* new_channel;
463250199Sgrehan
464250199Sgrehan	offer = (hv_vmbus_channel_offer_channel*) hdr;
465250199Sgrehan
466250199Sgrehan	hv_guid *guidType;
467250199Sgrehan	hv_guid *guidInstance;
468250199Sgrehan
469250199Sgrehan	guidType = &offer->offer.interface_type;
470250199Sgrehan	guidInstance = &offer->offer.interface_instance;
471250199Sgrehan
472250199Sgrehan	/* Allocate the channel object and save this offer */
473250199Sgrehan	new_channel = hv_vmbus_allocate_channel();
474250199Sgrehan	if (new_channel == NULL)
475250199Sgrehan	    return;
476250199Sgrehan
477283280Swhu	/*
478283280Swhu	 * By default we setup state to enable batched
479283280Swhu	 * reading. A specific service can choose to
480283280Swhu	 * disable this prior to opening the channel.
481283280Swhu	 */
482283280Swhu	new_channel->batched_reading = TRUE;
483283280Swhu
484283280Swhu	new_channel->signal_event_param =
485283280Swhu	    (hv_vmbus_input_signal_event *)
486283280Swhu	    (HV_ALIGN_UP((unsigned long)
487283280Swhu		&new_channel->signal_event_buffer,
488283280Swhu		HV_HYPERCALL_PARAM_ALIGN));
489283280Swhu
490283280Swhu 	new_channel->signal_event_param->connection_id.as_uint32_t = 0;
491283280Swhu	new_channel->signal_event_param->connection_id.u.id =
492283280Swhu	    HV_VMBUS_EVENT_CONNECTION_ID;
493283280Swhu	new_channel->signal_event_param->flag_number = 0;
494283280Swhu	new_channel->signal_event_param->rsvd_z = 0;
495283280Swhu
496283280Swhu	if (hv_vmbus_protocal_version != HV_VMBUS_VERSION_WS2008) {
497283280Swhu		new_channel->is_dedicated_interrupt =
498283280Swhu		    (offer->is_dedicated_interrupt != 0);
499283280Swhu		new_channel->signal_event_param->connection_id.u.id =
500283280Swhu		    offer->connection_id;
501283280Swhu	}
502283280Swhu
503283280Swhu	/*
504283280Swhu	 * Bind the channel to a chosen cpu.
505283280Swhu	 */
506283280Swhu	vmbus_channel_select_cpu(new_channel,
507283280Swhu	    &offer->offer.interface_type);
508283280Swhu
509250199Sgrehan	memcpy(&new_channel->offer_msg, offer,
510250199Sgrehan	    sizeof(hv_vmbus_channel_offer_channel));
511250199Sgrehan	new_channel->monitor_group = (uint8_t) offer->monitor_id / 32;
512250199Sgrehan	new_channel->monitor_bit = (uint8_t) offer->monitor_id % 32;
513250199Sgrehan
514293820Sdelphij	vmbus_channel_process_offer(new_channel);
515250199Sgrehan}
516250199Sgrehan
517250199Sgrehan/**
518250199Sgrehan * @brief Rescind offer handler.
519250199Sgrehan *
520250199Sgrehan * We queue a work item to process this offer
521250199Sgrehan * synchronously
522250199Sgrehan */
523250199Sgrehanstatic void
524250199Sgrehanvmbus_channel_on_offer_rescind(hv_vmbus_channel_msg_header* hdr)
525250199Sgrehan{
526250199Sgrehan	hv_vmbus_channel_rescind_offer*	rescind;
527250199Sgrehan	hv_vmbus_channel*		channel;
528250199Sgrehan
529250199Sgrehan	rescind = (hv_vmbus_channel_rescind_offer*) hdr;
530250199Sgrehan
531295948Ssephe	channel = hv_vmbus_g_connection.channels[rescind->child_rel_id];
532250199Sgrehan	if (channel == NULL)
533250199Sgrehan	    return;
534250199Sgrehan
535293820Sdelphij	hv_vmbus_child_device_unregister(channel->device);
536295948Ssephe	mtx_lock(&hv_vmbus_g_connection.channel_lock);
537295948Ssephe	hv_vmbus_g_connection.channels[rescind->child_rel_id] = NULL;
538295948Ssephe	mtx_unlock(&hv_vmbus_g_connection.channel_lock);
539250199Sgrehan}
540250199Sgrehan
541250199Sgrehan/**
542250199Sgrehan *
543250199Sgrehan * @brief Invoked when all offers have been delivered.
544250199Sgrehan */
545250199Sgrehanstatic void
546250199Sgrehanvmbus_channel_on_offers_delivered(hv_vmbus_channel_msg_header* hdr)
547250199Sgrehan{
548250199Sgrehan}
549250199Sgrehan
550250199Sgrehan/**
551250199Sgrehan * @brief Open result handler.
552250199Sgrehan *
553250199Sgrehan * This is invoked when we received a response
554250199Sgrehan * to our channel open request. Find the matching request, copy the
555250199Sgrehan * response and signal the requesting thread.
556250199Sgrehan */
557250199Sgrehanstatic void
558250199Sgrehanvmbus_channel_on_open_result(hv_vmbus_channel_msg_header* hdr)
559250199Sgrehan{
560250199Sgrehan	hv_vmbus_channel_open_result*	result;
561250199Sgrehan	hv_vmbus_channel_msg_info*	msg_info;
562250199Sgrehan	hv_vmbus_channel_msg_header*	requestHeader;
563250199Sgrehan	hv_vmbus_channel_open_channel*	openMsg;
564250199Sgrehan
565250199Sgrehan	result = (hv_vmbus_channel_open_result*) hdr;
566250199Sgrehan
567250199Sgrehan	/*
568250199Sgrehan	 * Find the open msg, copy the result and signal/unblock the wait event
569250199Sgrehan	 */
570303984Sglebius	mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
571250199Sgrehan
572250199Sgrehan	TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
573250199Sgrehan	    msg_list_entry) {
574250199Sgrehan	    requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg;
575250199Sgrehan
576250199Sgrehan	    if (requestHeader->message_type ==
577250199Sgrehan		    HV_CHANNEL_MESSAGE_OPEN_CHANNEL) {
578250199Sgrehan		openMsg = (hv_vmbus_channel_open_channel*) msg_info->msg;
579250199Sgrehan		if (openMsg->child_rel_id == result->child_rel_id
580250199Sgrehan		    && openMsg->open_id == result->open_id) {
581250199Sgrehan		    memcpy(&msg_info->response.open_result, result,
582250199Sgrehan			sizeof(hv_vmbus_channel_open_result));
583250199Sgrehan		    sema_post(&msg_info->wait_sema);
584250199Sgrehan		    break;
585250199Sgrehan		}
586250199Sgrehan	    }
587250199Sgrehan	}
588303984Sglebius	mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
589250199Sgrehan
590250199Sgrehan}
591250199Sgrehan
592250199Sgrehan/**
593250199Sgrehan * @brief GPADL created handler.
594250199Sgrehan *
595250199Sgrehan * This is invoked when we received a response
596250199Sgrehan * to our gpadl create request. Find the matching request, copy the
597250199Sgrehan * response and signal the requesting thread.
598250199Sgrehan */
599250199Sgrehanstatic void
600250199Sgrehanvmbus_channel_on_gpadl_created(hv_vmbus_channel_msg_header* hdr)
601250199Sgrehan{
602250199Sgrehan	hv_vmbus_channel_gpadl_created*		gpadl_created;
603250199Sgrehan	hv_vmbus_channel_msg_info*		msg_info;
604250199Sgrehan	hv_vmbus_channel_msg_header*		request_header;
605250199Sgrehan	hv_vmbus_channel_gpadl_header*		gpadl_header;
606250199Sgrehan
607250199Sgrehan	gpadl_created = (hv_vmbus_channel_gpadl_created*) hdr;
608250199Sgrehan
609250199Sgrehan	/* Find the establish msg, copy the result and signal/unblock
610250199Sgrehan	 * the wait event
611250199Sgrehan	 */
612303984Sglebius	mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
613250199Sgrehan	TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
614250199Sgrehan		msg_list_entry) {
615250199Sgrehan	    request_header = (hv_vmbus_channel_msg_header*) msg_info->msg;
616250199Sgrehan	    if (request_header->message_type ==
617250199Sgrehan		    HV_CHANNEL_MESSAGEL_GPADL_HEADER) {
618250199Sgrehan		gpadl_header =
619250199Sgrehan		    (hv_vmbus_channel_gpadl_header*) request_header;
620250199Sgrehan
621250199Sgrehan		if ((gpadl_created->child_rel_id == gpadl_header->child_rel_id)
622250199Sgrehan		    && (gpadl_created->gpadl == gpadl_header->gpadl)) {
623250199Sgrehan		    memcpy(&msg_info->response.gpadl_created,
624250199Sgrehan			gpadl_created,
625250199Sgrehan			sizeof(hv_vmbus_channel_gpadl_created));
626250199Sgrehan		    sema_post(&msg_info->wait_sema);
627250199Sgrehan		    break;
628250199Sgrehan		}
629250199Sgrehan	    }
630250199Sgrehan	}
631303984Sglebius	mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
632250199Sgrehan}
633250199Sgrehan
634250199Sgrehan/**
635250199Sgrehan * @brief GPADL torndown handler.
636250199Sgrehan *
637250199Sgrehan * This is invoked when we received a respons
638250199Sgrehan * to our gpadl teardown request. Find the matching request, copy the
639250199Sgrehan * response and signal the requesting thread
640250199Sgrehan */
641250199Sgrehanstatic void
642250199Sgrehanvmbus_channel_on_gpadl_torndown(hv_vmbus_channel_msg_header* hdr)
643250199Sgrehan{
644250199Sgrehan	hv_vmbus_channel_gpadl_torndown*	gpadl_torndown;
645250199Sgrehan	hv_vmbus_channel_msg_info*		msg_info;
646250199Sgrehan	hv_vmbus_channel_msg_header*		requestHeader;
647250199Sgrehan	hv_vmbus_channel_gpadl_teardown*	gpadlTeardown;
648250199Sgrehan
649250199Sgrehan	gpadl_torndown = (hv_vmbus_channel_gpadl_torndown*)hdr;
650250199Sgrehan
651250199Sgrehan	/*
652250199Sgrehan	 * Find the open msg, copy the result and signal/unblock the
653250199Sgrehan	 * wait event.
654250199Sgrehan	 */
655250199Sgrehan
656303984Sglebius	mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
657250199Sgrehan
658250199Sgrehan	TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
659250199Sgrehan		msg_list_entry) {
660250199Sgrehan	    requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg;
661250199Sgrehan
662250199Sgrehan	    if (requestHeader->message_type
663250199Sgrehan		    == HV_CHANNEL_MESSAGE_GPADL_TEARDOWN) {
664250199Sgrehan		gpadlTeardown =
665250199Sgrehan		    (hv_vmbus_channel_gpadl_teardown*) requestHeader;
666250199Sgrehan
667250199Sgrehan		if (gpadl_torndown->gpadl == gpadlTeardown->gpadl) {
668250199Sgrehan		    memcpy(&msg_info->response.gpadl_torndown,
669250199Sgrehan			gpadl_torndown,
670250199Sgrehan			sizeof(hv_vmbus_channel_gpadl_torndown));
671250199Sgrehan		    sema_post(&msg_info->wait_sema);
672250199Sgrehan		    break;
673250199Sgrehan		}
674250199Sgrehan	    }
675250199Sgrehan	}
676303984Sglebius    mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
677250199Sgrehan}
678250199Sgrehan
679250199Sgrehan/**
680250199Sgrehan * @brief Version response handler.
681250199Sgrehan *
682250199Sgrehan * This is invoked when we received a response
683250199Sgrehan * to our initiate contact request. Find the matching request, copy th
684250199Sgrehan * response and signal the requesting thread.
685250199Sgrehan */
686250199Sgrehanstatic void
687250199Sgrehanvmbus_channel_on_version_response(hv_vmbus_channel_msg_header* hdr)
688250199Sgrehan{
689250199Sgrehan	hv_vmbus_channel_msg_info*		msg_info;
690250199Sgrehan	hv_vmbus_channel_msg_header*		requestHeader;
691250199Sgrehan	hv_vmbus_channel_initiate_contact*	initiate;
692250199Sgrehan	hv_vmbus_channel_version_response*	versionResponse;
693250199Sgrehan
694250199Sgrehan	versionResponse = (hv_vmbus_channel_version_response*)hdr;
695250199Sgrehan
696303984Sglebius	mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
697250199Sgrehan	TAILQ_FOREACH(msg_info, &hv_vmbus_g_connection.channel_msg_anchor,
698250199Sgrehan	    msg_list_entry) {
699250199Sgrehan	    requestHeader = (hv_vmbus_channel_msg_header*) msg_info->msg;
700250199Sgrehan	    if (requestHeader->message_type
701250199Sgrehan		== HV_CHANNEL_MESSAGE_INITIATED_CONTACT) {
702250199Sgrehan		initiate =
703250199Sgrehan		    (hv_vmbus_channel_initiate_contact*) requestHeader;
704250199Sgrehan		memcpy(&msg_info->response.version_response,
705250199Sgrehan		    versionResponse,
706250199Sgrehan		    sizeof(hv_vmbus_channel_version_response));
707250199Sgrehan		sema_post(&msg_info->wait_sema);
708250199Sgrehan	    }
709250199Sgrehan	}
710303984Sglebius    mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
711250199Sgrehan
712250199Sgrehan}
713250199Sgrehan
714250199Sgrehan/**
715250199Sgrehan * @brief Handler for channel protocol messages.
716250199Sgrehan *
717250199Sgrehan * This is invoked in the vmbus worker thread context.
718250199Sgrehan */
719250199Sgrehanvoid
720250199Sgrehanhv_vmbus_on_channel_message(void *context)
721250199Sgrehan{
722250199Sgrehan	hv_vmbus_message*		msg;
723250199Sgrehan	hv_vmbus_channel_msg_header*	hdr;
724250199Sgrehan	int				size;
725250199Sgrehan
726250199Sgrehan	msg = (hv_vmbus_message*) context;
727250199Sgrehan	hdr = (hv_vmbus_channel_msg_header*) msg->u.payload;
728250199Sgrehan	size = msg->header.payload_size;
729250199Sgrehan
730250199Sgrehan	if (hdr->message_type >= HV_CHANNEL_MESSAGE_COUNT) {
731250199Sgrehan	    free(msg, M_DEVBUF);
732250199Sgrehan	    return;
733250199Sgrehan	}
734250199Sgrehan
735250199Sgrehan	if (g_channel_message_table[hdr->message_type].messageHandler) {
736250199Sgrehan	    g_channel_message_table[hdr->message_type].messageHandler(hdr);
737250199Sgrehan	}
738250199Sgrehan
739250199Sgrehan	/* Free the msg that was allocated in VmbusOnMsgDPC() */
740250199Sgrehan	free(msg, M_DEVBUF);
741250199Sgrehan}
742250199Sgrehan
743250199Sgrehan/**
744250199Sgrehan *  @brief Send a request to get all our pending offers.
745250199Sgrehan */
746250199Sgrehanint
747250199Sgrehanhv_vmbus_request_channel_offers(void)
748250199Sgrehan{
749250199Sgrehan	int				ret;
750250199Sgrehan	hv_vmbus_channel_msg_header*	msg;
751250199Sgrehan	hv_vmbus_channel_msg_info*	msg_info;
752250199Sgrehan
753250199Sgrehan	msg_info = (hv_vmbus_channel_msg_info *)
754250199Sgrehan	    malloc(sizeof(hv_vmbus_channel_msg_info)
755250199Sgrehan		    + sizeof(hv_vmbus_channel_msg_header), M_DEVBUF, M_NOWAIT);
756250199Sgrehan
757250199Sgrehan	if (msg_info == NULL) {
758250199Sgrehan	    if(bootverbose)
759250199Sgrehan		printf("Error VMBUS: malloc failed for Request Offers\n");
760250199Sgrehan	    return (ENOMEM);
761250199Sgrehan	}
762250199Sgrehan
763250199Sgrehan	msg = (hv_vmbus_channel_msg_header*) msg_info->msg;
764250199Sgrehan	msg->message_type = HV_CHANNEL_MESSAGE_REQUEST_OFFERS;
765250199Sgrehan
766250199Sgrehan	ret = hv_vmbus_post_message(msg, sizeof(hv_vmbus_channel_msg_header));
767250199Sgrehan
768250199Sgrehan	if (msg_info)
769250199Sgrehan	    free(msg_info, M_DEVBUF);
770250199Sgrehan
771250199Sgrehan	return (ret);
772250199Sgrehan}
773250199Sgrehan
774250199Sgrehan/**
775250199Sgrehan * @brief Release channels that are unattached/unconnected (i.e., no drivers associated)
776250199Sgrehan */
777250199Sgrehanvoid
778250199Sgrehanhv_vmbus_release_unattached_channels(void)
779250199Sgrehan{
780250199Sgrehan	hv_vmbus_channel *channel;
781250199Sgrehan
782283280Swhu	mtx_lock(&hv_vmbus_g_connection.channel_lock);
783250199Sgrehan
784250199Sgrehan	while (!TAILQ_EMPTY(&hv_vmbus_g_connection.channel_anchor)) {
785250199Sgrehan	    channel = TAILQ_FIRST(&hv_vmbus_g_connection.channel_anchor);
786250199Sgrehan	    TAILQ_REMOVE(&hv_vmbus_g_connection.channel_anchor,
787250199Sgrehan			    channel, list_entry);
788250199Sgrehan
789250199Sgrehan	    hv_vmbus_child_device_unregister(channel->device);
790250199Sgrehan	    hv_vmbus_free_vmbus_channel(channel);
791250199Sgrehan	}
792295948Ssephe	bzero(hv_vmbus_g_connection.channels,
793295948Ssephe		sizeof(hv_vmbus_channel*) * HV_CHANNEL_MAX_COUNT);
794283280Swhu	mtx_unlock(&hv_vmbus_g_connection.channel_lock);
795250199Sgrehan}
796283280Swhu
797283280Swhu/**
798283280Swhu * @brief Select the best outgoing channel
799283280Swhu *
800283280Swhu * The channel whose vcpu binding is closest to the currect vcpu will
801283280Swhu * be selected.
802283280Swhu * If no multi-channel, always select primary channel
803283280Swhu *
804283280Swhu * @param primary - primary channel
805283280Swhu */
806283280Swhustruct hv_vmbus_channel *
807283280Swhuvmbus_select_outgoing_channel(struct hv_vmbus_channel *primary)
808283280Swhu{
809283280Swhu	hv_vmbus_channel *new_channel = NULL;
810283280Swhu	hv_vmbus_channel *outgoing_channel = primary;
811283280Swhu	int old_cpu_distance = 0;
812283280Swhu	int new_cpu_distance = 0;
813283280Swhu	int cur_vcpu = 0;
814283280Swhu	int smp_pro_id = PCPU_GET(cpuid);
815283280Swhu
816283280Swhu	if (TAILQ_EMPTY(&primary->sc_list_anchor)) {
817283280Swhu		return outgoing_channel;
818283280Swhu	}
819283280Swhu
820283280Swhu	if (smp_pro_id >= MAXCPU) {
821283280Swhu		return outgoing_channel;
822283280Swhu	}
823283280Swhu
824283280Swhu	cur_vcpu = hv_vmbus_g_context.hv_vcpu_index[smp_pro_id];
825283280Swhu
826283280Swhu	TAILQ_FOREACH(new_channel, &primary->sc_list_anchor, sc_list_entry) {
827283280Swhu		if (new_channel->state != HV_CHANNEL_OPENED_STATE){
828283280Swhu			continue;
829283280Swhu		}
830283280Swhu
831283280Swhu		if (new_channel->target_vcpu == cur_vcpu){
832283280Swhu			return new_channel;
833283280Swhu		}
834283280Swhu
835283280Swhu		old_cpu_distance = ((outgoing_channel->target_vcpu > cur_vcpu) ?
836283280Swhu		    (outgoing_channel->target_vcpu - cur_vcpu) :
837283280Swhu		    (cur_vcpu - outgoing_channel->target_vcpu));
838283280Swhu
839283280Swhu		new_cpu_distance = ((new_channel->target_vcpu > cur_vcpu) ?
840283280Swhu		    (new_channel->target_vcpu - cur_vcpu) :
841283280Swhu		    (cur_vcpu - new_channel->target_vcpu));
842283280Swhu
843283280Swhu		if (old_cpu_distance < new_cpu_distance) {
844283280Swhu			continue;
845283280Swhu		}
846283280Swhu
847283280Swhu		outgoing_channel = new_channel;
848283280Swhu	}
849283280Swhu
850283280Swhu	return(outgoing_channel);
851283280Swhu}
852