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