]> CyberLeo.Net >> Repos - FreeBSD/releng/10.3.git/blob - sys/dev/hyperv/vmbus/hv_connection.c
Release 6 errata notices for 10.3-RELEASE, all related to Microsoft Hyper-V.
[FreeBSD/releng/10.3.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_pages);
94
95         msg->monitor_page_2 =
96                 hv_get_phys_addr(
97                         ((uint8_t *) hv_vmbus_g_connection.monitor_pages
98                         + PAGE_SIZE));
99
100         /**
101          * Add to list before we send the request since we may receive the
102          * response before returning from this routine
103          */
104         mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
105
106         TAILQ_INSERT_TAIL(
107                 &hv_vmbus_g_connection.channel_msg_anchor,
108                 msg_info,
109                 msg_list_entry);
110
111         mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
112
113         ret = hv_vmbus_post_message(
114                 msg,
115                 sizeof(hv_vmbus_channel_initiate_contact));
116
117         if (ret != 0) {
118                 mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
119                 TAILQ_REMOVE(
120                         &hv_vmbus_g_connection.channel_msg_anchor,
121                         msg_info,
122                         msg_list_entry);
123                 mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
124                 return (ret);
125         }
126
127         /**
128          * Wait for the connection response
129          */
130         ret = sema_timedwait(&msg_info->wait_sema, 5 * hz); /* KYS 5 seconds */
131
132         mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
133         TAILQ_REMOVE(
134                 &hv_vmbus_g_connection.channel_msg_anchor,
135                 msg_info,
136                 msg_list_entry);
137         mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
138
139         /**
140          * Check if successful
141          */
142         if (msg_info->response.version_response.version_supported) {
143                 hv_vmbus_g_connection.connect_state = HV_CONNECTED;
144         } else {
145                 ret = ECONNREFUSED;
146         }
147
148         return (ret);
149 }
150
151 /**
152  * Send a connect request on the partition service connection
153  */
154 int
155 hv_vmbus_connect(void) {
156         int                                     ret = 0;
157         uint32_t                                version;
158         hv_vmbus_channel_msg_info*              msg_info = NULL;
159
160         /**
161          * Make sure we are not connecting or connected
162          */
163         if (hv_vmbus_g_connection.connect_state != HV_DISCONNECTED) {
164                 return (-1);
165         }
166
167         /**
168          * Initialize the vmbus connection
169          */
170         hv_vmbus_g_connection.connect_state = HV_CONNECTING;
171         hv_vmbus_g_connection.work_queue = hv_work_queue_create("vmbusQ");
172         sema_init(&hv_vmbus_g_connection.control_sema, 1, "control_sema");
173
174         TAILQ_INIT(&hv_vmbus_g_connection.channel_msg_anchor);
175         mtx_init(&hv_vmbus_g_connection.channel_msg_lock, "vmbus channel msg",
176                 NULL, MTX_DEF);
177
178         TAILQ_INIT(&hv_vmbus_g_connection.channel_anchor);
179         mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel",
180                 NULL, MTX_DEF);
181
182         /**
183          * Setup the vmbus event connection for channel interrupt abstraction
184          * stuff
185          */
186         hv_vmbus_g_connection.interrupt_page = contigmalloc(
187                                         PAGE_SIZE, M_DEVBUF,
188                                         M_NOWAIT | M_ZERO, 0UL,
189                                         BUS_SPACE_MAXADDR,
190                                         PAGE_SIZE, 0);
191         KASSERT(hv_vmbus_g_connection.interrupt_page != NULL,
192             ("Error VMBUS: malloc failed to allocate Channel"
193                 " Request Event message!"));
194         if (hv_vmbus_g_connection.interrupt_page == NULL) {
195             ret = ENOMEM;
196             goto cleanup;
197         }
198
199         hv_vmbus_g_connection.recv_interrupt_page =
200                 hv_vmbus_g_connection.interrupt_page;
201
202         hv_vmbus_g_connection.send_interrupt_page =
203                 ((uint8_t *) hv_vmbus_g_connection.interrupt_page +
204                     (PAGE_SIZE >> 1));
205
206         /**
207          * Set up the monitor notification facility. The 1st page for
208          * parent->child and the 2nd page for child->parent
209          */
210         hv_vmbus_g_connection.monitor_pages = contigmalloc(
211                 2 * PAGE_SIZE,
212                 M_DEVBUF,
213                 M_NOWAIT | M_ZERO,
214                 0UL,
215                 BUS_SPACE_MAXADDR,
216                 PAGE_SIZE,
217                 0);
218         KASSERT(hv_vmbus_g_connection.monitor_pages != NULL,
219             ("Error VMBUS: malloc failed to allocate Monitor Pages!"));
220         if (hv_vmbus_g_connection.monitor_pages == NULL) {
221             ret = ENOMEM;
222             goto cleanup;
223         }
224
225         msg_info = (hv_vmbus_channel_msg_info*)
226                 malloc(sizeof(hv_vmbus_channel_msg_info) +
227                         sizeof(hv_vmbus_channel_initiate_contact),
228                         M_DEVBUF, M_NOWAIT | M_ZERO);
229         KASSERT(msg_info != NULL,
230             ("Error VMBUS: malloc failed for Initiate Contact message!"));
231         if (msg_info == NULL) {
232             ret = ENOMEM;
233             goto cleanup;
234         }
235
236         hv_vmbus_g_connection.channels = malloc(sizeof(hv_vmbus_channel*) *
237                 HV_CHANNEL_MAX_COUNT,
238                 M_DEVBUF, M_WAITOK | M_ZERO);
239         /*
240          * Find the highest vmbus version number we can support.
241          */
242         version = HV_VMBUS_VERSION_CURRENT;
243
244         do {
245                 ret = hv_vmbus_negotiate_version(msg_info, version);
246                 if (ret == EWOULDBLOCK) {
247                         /*
248                          * We timed out.
249                          */
250                         goto cleanup;
251                 }
252
253                 if (hv_vmbus_g_connection.connect_state == HV_CONNECTED)
254                         break;
255
256                 version = hv_vmbus_get_next_version(version);
257         } while (version != HV_VMBUS_VERSION_INVALID);
258
259         hv_vmbus_protocal_version = version;
260         if (bootverbose)
261                 printf("VMBUS: Protocol Version: %d.%d\n",
262                     version >> 16, version & 0xFFFF);
263
264         sema_destroy(&msg_info->wait_sema);
265         free(msg_info, M_DEVBUF);
266
267         return (0);
268
269         /*
270          * Cleanup after failure!
271          */
272         cleanup:
273
274         hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
275
276         hv_work_queue_close(hv_vmbus_g_connection.work_queue);
277         sema_destroy(&hv_vmbus_g_connection.control_sema);
278         mtx_destroy(&hv_vmbus_g_connection.channel_lock);
279         mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
280
281         if (hv_vmbus_g_connection.interrupt_page != NULL) {
282                 contigfree(
283                         hv_vmbus_g_connection.interrupt_page,
284                         PAGE_SIZE,
285                         M_DEVBUF);
286                 hv_vmbus_g_connection.interrupt_page = NULL;
287         }
288
289         if (hv_vmbus_g_connection.monitor_pages != NULL) {
290                 contigfree(
291                         hv_vmbus_g_connection.monitor_pages,
292                         2 * PAGE_SIZE,
293                         M_DEVBUF);
294                 hv_vmbus_g_connection.monitor_pages = NULL;
295         }
296
297         if (msg_info) {
298                 sema_destroy(&msg_info->wait_sema);
299                 free(msg_info, M_DEVBUF);
300         }
301
302         free(hv_vmbus_g_connection.channels, M_DEVBUF);
303         return (ret);
304 }
305
306 /**
307  * Send a disconnect request on the partition service connection
308  */
309 int
310 hv_vmbus_disconnect(void) {
311         int                      ret = 0;
312         hv_vmbus_channel_unload* msg;
313
314         msg = malloc(sizeof(hv_vmbus_channel_unload),
315             M_DEVBUF, M_NOWAIT | M_ZERO);
316         KASSERT(msg != NULL,
317             ("Error VMBUS: malloc failed to allocate Channel Unload Msg!"));
318         if (msg == NULL)
319             return (ENOMEM);
320
321         msg->message_type = HV_CHANNEL_MESSAGE_UNLOAD;
322
323         ret = hv_vmbus_post_message(msg, sizeof(hv_vmbus_channel_unload));
324
325
326         contigfree(hv_vmbus_g_connection.interrupt_page, PAGE_SIZE, M_DEVBUF);
327
328         mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
329
330         hv_work_queue_close(hv_vmbus_g_connection.work_queue);
331         sema_destroy(&hv_vmbus_g_connection.control_sema);
332
333         free(hv_vmbus_g_connection.channels, M_DEVBUF);
334         hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
335
336         free(msg, M_DEVBUF);
337
338         return (ret);
339 }
340
341 /**
342  * Process a channel event notification
343  */
344 static void
345 VmbusProcessChannelEvent(uint32_t relid) 
346 {
347         void* arg;
348         uint32_t bytes_to_read;
349         hv_vmbus_channel* channel;
350         boolean_t is_batched_reading;
351
352         /**
353          * Find the channel based on this relid and invokes
354          * the channel callback to process the event
355          */
356
357         channel = hv_vmbus_g_connection.channels[relid];
358
359         if (channel == NULL) {
360                 return;
361         }
362         /**
363          * To deal with the race condition where we might
364          * receive a packet while the relevant driver is 
365          * being unloaded, dispatch the callback while 
366          * holding the channel lock. The unloading driver
367          * will acquire the same channel lock to set the
368          * callback to NULL. This closes the window.
369          */
370
371         /*
372          * Disable the lock due to newly added WITNESS check in r277723.
373          * Will seek other way to avoid race condition.
374          * -- whu
375          */
376         // mtx_lock(&channel->inbound_lock);
377         if (channel->on_channel_callback != NULL) {
378                 arg = channel->channel_callback_context;
379                 is_batched_reading = channel->batched_reading;
380                 /*
381                  * Optimize host to guest signaling by ensuring:
382                  * 1. While reading the channel, we disable interrupts from
383                  *    host.
384                  * 2. Ensure that we process all posted messages from the host
385                  *    before returning from this callback.
386                  * 3. Once we return, enable signaling from the host. Once this
387                  *    state is set we check to see if additional packets are
388                  *    available to read. In this case we repeat the process.
389                  */
390                 do {
391                         if (is_batched_reading)
392                                 hv_ring_buffer_read_begin(&channel->inbound);
393
394                         channel->on_channel_callback(arg);
395
396                         if (is_batched_reading)
397                                 bytes_to_read =
398                                     hv_ring_buffer_read_end(&channel->inbound);
399                         else
400                                 bytes_to_read = 0;
401                 } while (is_batched_reading && (bytes_to_read != 0));
402         }
403         // mtx_unlock(&channel->inbound_lock);
404 }
405
406 /**
407  * Handler for events
408  */
409 void
410 hv_vmbus_on_events(void *arg) 
411 {
412         int bit;
413         int cpu;
414         int dword;
415         void *page_addr;
416         uint32_t* recv_interrupt_page = NULL;
417         int rel_id;
418         int maxdword;
419         hv_vmbus_synic_event_flags *event;
420         /* int maxdword = PAGE_SIZE >> 3; */
421
422         cpu = (int)(long)arg;
423         KASSERT(cpu <= mp_maxid, ("VMBUS: hv_vmbus_on_events: "
424             "cpu out of range!"));
425
426         if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) ||
427             (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)) {
428                 maxdword = HV_MAX_NUM_CHANNELS_SUPPORTED >> 5;
429                 /*
430                  * receive size is 1/2 page and divide that by 4 bytes
431                  */
432                 recv_interrupt_page =
433                     hv_vmbus_g_connection.recv_interrupt_page;
434         } else {
435                 /*
436                  * On Host with Win8 or above, the event page can be
437                  * checked directly to get the id of the channel
438                  * that has the pending interrupt.
439                  */
440                 maxdword = HV_EVENT_FLAGS_DWORD_COUNT;
441                 page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu];
442                 event = (hv_vmbus_synic_event_flags *)
443                     page_addr + HV_VMBUS_MESSAGE_SINT;
444                 recv_interrupt_page = event->flags32;
445         }
446
447         /*
448          * Check events
449          */
450         if (recv_interrupt_page != NULL) {
451             for (dword = 0; dword < maxdword; dword++) {
452                 if (recv_interrupt_page[dword]) {
453                     for (bit = 0; bit < HV_CHANNEL_DWORD_LEN; bit++) {
454                         if (synch_test_and_clear_bit(bit,
455                             (uint32_t *) &recv_interrupt_page[dword])) {
456                             rel_id = (dword << 5) + bit;
457                             if (rel_id == 0) {
458                                 /*
459                                  * Special case -
460                                  * vmbus channel protocol msg.
461                                  */
462                                 continue;
463                             } else {
464                                 VmbusProcessChannelEvent(rel_id);
465
466                             }
467                         }
468                     }
469                 }
470             }
471         }
472
473         return;
474 }
475
476 /**
477  * Send a msg on the vmbus's message connection
478  */
479 int hv_vmbus_post_message(void *buffer, size_t bufferLen)
480 {
481         hv_vmbus_connection_id connId;
482         sbintime_t time = SBT_1MS;
483         int retries;
484         int ret;
485
486         connId.as_uint32_t = 0;
487         connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID;
488
489         /*
490          * We retry to cope with transient failures caused by host side's
491          * insufficient resources. 20 times should suffice in practice.
492          */
493         for (retries = 0; retries < 20; retries++) {
494                 ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer,
495                                                     bufferLen);
496                 if (ret == HV_STATUS_SUCCESS)
497                         return (0);
498
499                 pause_sbt("pstmsg", time, 0, C_HARDCLOCK);
500                 if (time < SBT_1S * 2)
501                         time *= 2;
502         }
503
504         KASSERT(ret == HV_STATUS_SUCCESS,
505                 ("Error VMBUS: Message Post Failed, ret=%d\n", ret));
506
507         return (EAGAIN);
508 }
509
510 /**
511  * Send an event notification to the parent
512  */
513 int
514 hv_vmbus_set_event(hv_vmbus_channel *channel) {
515         int ret = 0;
516         uint32_t child_rel_id = channel->offer_msg.child_rel_id;
517
518         /* Each uint32_t represents 32 channels */
519
520         synch_set_bit(child_rel_id & 31,
521                 (((uint32_t *)hv_vmbus_g_connection.send_interrupt_page
522                         + (child_rel_id >> 5))));
523         ret = hv_vmbus_signal_event(channel->signal_event_param);
524
525         return (ret);
526 }