]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/dev/hyperv/vmbus/hv_connection.c
MFC 295307,295308,295309,295606
[FreeBSD/stable/10.git] / sys / dev / hyperv / vmbus / hv_connection.c
1 /*-
2  * Copyright (c) 2009-2012 Microsoft Corp.
3  * Copyright (c) 2012 NetApp Inc.
4  * Copyright (c) 2012 Citrix Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice unmodified, this list of conditions, and the following
12  *    disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/kernel.h>
34 #include <sys/malloc.h>
35 #include <sys/systm.h>
36 #include <sys/lock.h>
37 #include <sys/mutex.h>
38 #include <machine/bus.h>
39 #include <vm/vm.h>
40 #include <vm/vm_param.h>
41 #include <vm/pmap.h>
42
43 #include "hv_vmbus_priv.h"
44
45 /*
46  * Globals
47  */
48 hv_vmbus_connection hv_vmbus_g_connection =
49         { .connect_state = HV_DISCONNECTED,
50           .next_gpadl_handle = 0xE1E10, };
51
52 uint32_t hv_vmbus_protocal_version = HV_VMBUS_VERSION_WS2008;
53
54 static uint32_t
55 hv_vmbus_get_next_version(uint32_t current_ver)
56 {
57         switch (current_ver) {
58         case (HV_VMBUS_VERSION_WIN7):
59                 return(HV_VMBUS_VERSION_WS2008);
60
61         case (HV_VMBUS_VERSION_WIN8):
62                 return(HV_VMBUS_VERSION_WIN7);
63
64         case (HV_VMBUS_VERSION_WIN8_1):
65                 return(HV_VMBUS_VERSION_WIN8);
66
67         case (HV_VMBUS_VERSION_WS2008):
68         default:
69                 return(HV_VMBUS_VERSION_INVALID);
70         }
71 }
72
73 /**
74  * Negotiate the highest supported hypervisor version.
75  */
76 static int
77 hv_vmbus_negotiate_version(hv_vmbus_channel_msg_info *msg_info,
78         uint32_t version)
79 {
80         int                                     ret = 0;
81         hv_vmbus_channel_initiate_contact       *msg;
82
83         sema_init(&msg_info->wait_sema, 0, "Msg Info Sema");
84         msg = (hv_vmbus_channel_initiate_contact*) msg_info->msg;
85
86         msg->header.message_type = HV_CHANNEL_MESSAGE_INITIATED_CONTACT;
87         msg->vmbus_version_requested = version;
88
89         msg->interrupt_page = hv_get_phys_addr(
90                 hv_vmbus_g_connection.interrupt_page);
91
92         msg->monitor_page_1 = hv_get_phys_addr(
93                 hv_vmbus_g_connection.monitor_page_1);
94
95         msg->monitor_page_2 = hv_get_phys_addr(
96                 hv_vmbus_g_connection.monitor_page_2);
97
98         /**
99          * Add to list before we send the request since we may receive the
100          * response before returning from this routine
101          */
102         mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
103
104         TAILQ_INSERT_TAIL(
105                 &hv_vmbus_g_connection.channel_msg_anchor,
106                 msg_info,
107                 msg_list_entry);
108
109         mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
110
111         ret = hv_vmbus_post_message(
112                 msg,
113                 sizeof(hv_vmbus_channel_initiate_contact));
114
115         if (ret != 0) {
116                 mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
117                 TAILQ_REMOVE(
118                         &hv_vmbus_g_connection.channel_msg_anchor,
119                         msg_info,
120                         msg_list_entry);
121                 mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
122                 return (ret);
123         }
124
125         /**
126          * Wait for the connection response
127          */
128         ret = sema_timedwait(&msg_info->wait_sema, 5 * hz); /* KYS 5 seconds */
129
130         mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock);
131         TAILQ_REMOVE(
132                 &hv_vmbus_g_connection.channel_msg_anchor,
133                 msg_info,
134                 msg_list_entry);
135         mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock);
136
137         /**
138          * Check if successful
139          */
140         if (msg_info->response.version_response.version_supported) {
141                 hv_vmbus_g_connection.connect_state = HV_CONNECTED;
142         } else {
143                 ret = ECONNREFUSED;
144         }
145
146         return (ret);
147 }
148
149 /**
150  * Send a connect request on the partition service connection
151  */
152 int
153 hv_vmbus_connect(void) {
154         int                                     ret = 0;
155         uint32_t                                version;
156         hv_vmbus_channel_msg_info*              msg_info = NULL;
157
158         /**
159          * Make sure we are not connecting or connected
160          */
161         if (hv_vmbus_g_connection.connect_state != HV_DISCONNECTED) {
162                 return (-1);
163         }
164
165         /**
166          * Initialize the vmbus connection
167          */
168         hv_vmbus_g_connection.connect_state = HV_CONNECTING;
169
170         TAILQ_INIT(&hv_vmbus_g_connection.channel_msg_anchor);
171         mtx_init(&hv_vmbus_g_connection.channel_msg_lock, "vmbus channel msg",
172                 NULL, MTX_SPIN);
173
174         TAILQ_INIT(&hv_vmbus_g_connection.channel_anchor);
175         mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel",
176                 NULL, MTX_DEF);
177
178         /**
179          * Setup the vmbus event connection for channel interrupt abstraction
180          * stuff
181          */
182         hv_vmbus_g_connection.interrupt_page = malloc(
183                                         PAGE_SIZE, M_DEVBUF,
184                                         M_WAITOK | M_ZERO);
185
186         hv_vmbus_g_connection.recv_interrupt_page =
187                 hv_vmbus_g_connection.interrupt_page;
188
189         hv_vmbus_g_connection.send_interrupt_page =
190                 ((uint8_t *) hv_vmbus_g_connection.interrupt_page +
191                     (PAGE_SIZE >> 1));
192
193         /**
194          * Set up the monitor notification facility. The 1st page for
195          * parent->child and the 2nd page for child->parent
196          */
197         hv_vmbus_g_connection.monitor_page_1 = malloc(
198                 PAGE_SIZE,
199                 M_DEVBUF,
200                 M_WAITOK | M_ZERO);
201         hv_vmbus_g_connection.monitor_page_2 = malloc(
202                 PAGE_SIZE,
203                 M_DEVBUF,
204                 M_WAITOK | M_ZERO);
205
206         msg_info = (hv_vmbus_channel_msg_info*)
207                 malloc(sizeof(hv_vmbus_channel_msg_info) +
208                         sizeof(hv_vmbus_channel_initiate_contact),
209                         M_DEVBUF, M_WAITOK | M_ZERO);
210
211         hv_vmbus_g_connection.channels = malloc(sizeof(hv_vmbus_channel*) *
212                 HV_CHANNEL_MAX_COUNT,
213                 M_DEVBUF, M_WAITOK | M_ZERO);
214         /*
215          * Find the highest vmbus version number we can support.
216          */
217         version = HV_VMBUS_VERSION_CURRENT;
218
219         do {
220                 ret = hv_vmbus_negotiate_version(msg_info, version);
221                 if (ret == EWOULDBLOCK) {
222                         /*
223                          * We timed out.
224                          */
225                         goto cleanup;
226                 }
227
228                 if (hv_vmbus_g_connection.connect_state == HV_CONNECTED)
229                         break;
230
231                 version = hv_vmbus_get_next_version(version);
232         } while (version != HV_VMBUS_VERSION_INVALID);
233
234         hv_vmbus_protocal_version = version;
235         if (bootverbose)
236                 printf("VMBUS: Protocol Version: %d.%d\n",
237                     version >> 16, version & 0xFFFF);
238
239         sema_destroy(&msg_info->wait_sema);
240         free(msg_info, M_DEVBUF);
241
242         return (0);
243
244         /*
245          * Cleanup after failure!
246          */
247         cleanup:
248
249         hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
250
251         mtx_destroy(&hv_vmbus_g_connection.channel_lock);
252         mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
253
254         if (hv_vmbus_g_connection.interrupt_page != NULL) {
255                 contigfree(
256                         hv_vmbus_g_connection.interrupt_page,
257                         PAGE_SIZE,
258                         M_DEVBUF);
259                 hv_vmbus_g_connection.interrupt_page = NULL;
260         }
261
262         free(hv_vmbus_g_connection.monitor_page_1, M_DEVBUF);
263         free(hv_vmbus_g_connection.monitor_page_2, M_DEVBUF);
264
265         if (msg_info) {
266                 sema_destroy(&msg_info->wait_sema);
267                 free(msg_info, M_DEVBUF);
268         }
269
270         free(hv_vmbus_g_connection.channels, M_DEVBUF);
271         return (ret);
272 }
273
274 /**
275  * Send a disconnect request on the partition service connection
276  */
277 int
278 hv_vmbus_disconnect(void) {
279         int                      ret = 0;
280         hv_vmbus_channel_unload  msg;
281
282         msg.message_type = HV_CHANNEL_MESSAGE_UNLOAD;
283
284         ret = hv_vmbus_post_message(&msg, sizeof(hv_vmbus_channel_unload));
285
286         contigfree(hv_vmbus_g_connection.interrupt_page, PAGE_SIZE, M_DEVBUF);
287
288         mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
289
290         free(hv_vmbus_g_connection.channels, M_DEVBUF);
291         hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
292
293         return (ret);
294 }
295
296 /**
297  * Handler for events
298  */
299 void
300 hv_vmbus_on_events(int cpu)
301 {
302         int bit;
303         int dword;
304         void *page_addr;
305         uint32_t* recv_interrupt_page = NULL;
306         int rel_id;
307         int maxdword;
308         hv_vmbus_synic_event_flags *event;
309         /* int maxdword = PAGE_SIZE >> 3; */
310
311         KASSERT(cpu <= mp_maxid, ("VMBUS: hv_vmbus_on_events: "
312             "cpu out of range!"));
313
314         if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) ||
315             (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)) {
316                 maxdword = HV_MAX_NUM_CHANNELS_SUPPORTED >> 5;
317                 /*
318                  * receive size is 1/2 page and divide that by 4 bytes
319                  */
320                 recv_interrupt_page =
321                     hv_vmbus_g_connection.recv_interrupt_page;
322         } else {
323                 /*
324                  * On Host with Win8 or above, the event page can be
325                  * checked directly to get the id of the channel
326                  * that has the pending interrupt.
327                  */
328                 maxdword = HV_EVENT_FLAGS_DWORD_COUNT;
329                 page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu];
330                 event = (hv_vmbus_synic_event_flags *)
331                     page_addr + HV_VMBUS_MESSAGE_SINT;
332                 recv_interrupt_page = event->flags32;
333         }
334
335         /*
336          * Check events
337          */
338         if (recv_interrupt_page != NULL) {
339             for (dword = 0; dword < maxdword; dword++) {
340                 if (recv_interrupt_page[dword]) {
341                     for (bit = 0; bit < HV_CHANNEL_DWORD_LEN; bit++) {
342                         if (synch_test_and_clear_bit(bit,
343                             (uint32_t *) &recv_interrupt_page[dword])) {
344                             rel_id = (dword << 5) + bit;
345                             if (rel_id == 0) {
346                                 /*
347                                  * Special case -
348                                  * vmbus channel protocol msg.
349                                  */
350                                 continue;
351                             } else {
352                                 hv_vmbus_channel * channel = hv_vmbus_g_connection.channels[rel_id];
353                                 /* if channel is closed or closing */
354                                 if (channel == NULL || channel->rxq == NULL)
355                                         continue;
356
357                                 if (channel->batched_reading)
358                                         hv_ring_buffer_read_begin(&channel->inbound);
359                                 taskqueue_enqueue_fast(channel->rxq, &channel->channel_task);
360                             }
361                         }
362                     }
363                 }
364             }
365         }
366
367         return;
368 }
369
370 /**
371  * Send a msg on the vmbus's message connection
372  */
373 int hv_vmbus_post_message(void *buffer, size_t bufferLen) {
374         int ret = 0;
375         hv_vmbus_connection_id connId;
376         unsigned retries = 0;
377
378         /* NetScaler delays from previous code were consolidated here */
379         static int delayAmount[] = {100, 100, 100, 500, 500, 5000, 5000, 5000};
380
381         /* for(each entry in delayAmount) try to post message,
382          *  delay a little bit before retrying
383          */
384         for (retries = 0;
385             retries < sizeof(delayAmount)/sizeof(delayAmount[0]); retries++) {
386             connId.as_uint32_t = 0;
387             connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID;
388             ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer, bufferLen);
389             if (ret != HV_STATUS_INSUFFICIENT_BUFFERS)
390                 break;
391             /* TODO: KYS We should use a blocking wait call */
392             DELAY(delayAmount[retries]);
393         }
394
395         KASSERT(ret == 0, ("Error VMBUS: Message Post Failed\n"));
396
397         return (ret);
398 }
399
400 /**
401  * Send an event notification to the parent
402  */
403 int
404 hv_vmbus_set_event(hv_vmbus_channel *channel) {
405         int ret = 0;
406         uint32_t child_rel_id = channel->offer_msg.child_rel_id;
407
408         /* Each uint32_t represents 32 channels */
409
410         synch_set_bit(child_rel_id & 31,
411                 (((uint32_t *)hv_vmbus_g_connection.send_interrupt_page
412                         + (child_rel_id >> 5))));
413         ret = hv_vmbus_signal_event(channel->signal_event_param);
414
415         return (ret);
416 }