VirtualBox

source: vbox/trunk/src/VBox/VMM/VMReq.cpp@ 6796

Last change on this file since 6796 was 6796, checked in by vboxsync, 17 years ago

Fixed init problems wrt. VM ownership by implementing the UVM structure (U = user mode) and moving problematic ring-3 stuff over there (emt+reqs, r3heap, stam, loader[VMMR0.r0]). Big change, but it works fine here... :-)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 35.9 KB
Line 
1/* $Id: VMReq.cpp 6796 2008-02-04 18:19:58Z vboxsync $ */
2/** @file
3 * VM - Virtual Machine
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#define LOG_GROUP LOG_GROUP_VM
23#include <VBox/mm.h>
24#include <VBox/vmm.h>
25#include "VMInternal.h"
26#include <VBox/vm.h>
27#include <VBox/uvm.h>
28
29#include <VBox/err.h>
30#include <VBox/param.h>
31#include <VBox/log.h>
32#include <iprt/assert.h>
33#include <iprt/asm.h>
34#include <iprt/string.h>
35#include <iprt/time.h>
36#include <iprt/semaphore.h>
37#include <iprt/thread.h>
38
39
40/*******************************************************************************
41* Internal Functions *
42*******************************************************************************/
43static int vmR3ReqProcessOneU(PUVM pUVM, PVMREQ pReq);
44
45
46/**
47 * Allocate and queue a call request.
48 *
49 * If it's desired to poll on the completion of the request set cMillies
50 * to 0 and use VMR3ReqWait() to check for completation. In the other case
51 * use RT_INDEFINITE_WAIT.
52 * The returned request packet must be freed using VMR3ReqFree().
53 *
54 * @returns VBox status code.
55 * Will not return VERR_INTERRUPTED.
56 * @returns VERR_TIMEOUT if cMillies was reached without the packet being completed.
57 *
58 * @param pUVM Pointer to the user mode VM structure.
59 * @param ppReq Where to store the pointer to the request.
60 * This will be NULL or a valid request pointer not matter what happends.
61 * @param cMillies Number of milliseconds to wait for the request to
62 * be completed. Use RT_INDEFINITE_WAIT to only
63 * wait till it's completed.
64 * @param pfnFunction Pointer to the function to call.
65 * @param cArgs Number of arguments following in the ellipsis.
66 * Not possible to pass 64-bit arguments!
67 * @param ... Function arguments.
68 */
69VMR3DECL(int) VMR3ReqCallU(PUVM pUVM, PVMREQ *ppReq, unsigned cMillies, PFNRT pfnFunction, unsigned cArgs, ...)
70{
71 va_list va;
72 va_start(va, cArgs);
73 int rc = VMR3ReqCallVU(pUVM, ppReq, cMillies, VMREQFLAGS_VBOX_STATUS, pfnFunction, cArgs, va);
74 va_end(va);
75 return rc;
76}
77
78
79/**
80 * Allocate and queue a call request.
81 *
82 * If it's desired to poll on the completion of the request set cMillies
83 * to 0 and use VMR3ReqWait() to check for completation. In the other case
84 * use RT_INDEFINITE_WAIT.
85 * The returned request packet must be freed using VMR3ReqFree().
86 *
87 * @returns VBox status code.
88 * Will not return VERR_INTERRUPTED.
89 * @returns VERR_TIMEOUT if cMillies was reached without the packet being completed.
90 *
91 * @param pVM The VM handle.
92 * @param ppReq Where to store the pointer to the request.
93 * This will be NULL or a valid request pointer not matter what happends.
94 * @param cMillies Number of milliseconds to wait for the request to
95 * be completed. Use RT_INDEFINITE_WAIT to only
96 * wait till it's completed.
97 * @param pfnFunction Pointer to the function to call.
98 * @param cArgs Number of arguments following in the ellipsis.
99 * Not possible to pass 64-bit arguments!
100 * @param ... Function arguments.
101 */
102VMR3DECL(int) VMR3ReqCall(PVM pVM, PVMREQ *ppReq, unsigned cMillies, PFNRT pfnFunction, unsigned cArgs, ...)
103{
104 va_list va;
105 va_start(va, cArgs);
106 int rc = VMR3ReqCallVU(pVM->pUVM, ppReq, cMillies, VMREQFLAGS_VBOX_STATUS, pfnFunction, cArgs, va);
107 va_end(va);
108 return rc;
109}
110
111
112/**
113 * Allocate and queue a call request to a void function.
114 *
115 * If it's desired to poll on the completion of the request set cMillies
116 * to 0 and use VMR3ReqWait() to check for completation. In the other case
117 * use RT_INDEFINITE_WAIT.
118 * The returned request packet must be freed using VMR3ReqFree().
119 *
120 * @returns VBox status code.
121 * Will not return VERR_INTERRUPTED.
122 * @returns VERR_TIMEOUT if cMillies was reached without the packet being completed.
123 *
124 * @param pUVM Pointer to the user mode VM structure.
125 * @param ppReq Where to store the pointer to the request.
126 * This will be NULL or a valid request pointer not matter what happends.
127 * @param cMillies Number of milliseconds to wait for the request to
128 * be completed. Use RT_INDEFINITE_WAIT to only
129 * wait till it's completed.
130 * @param pfnFunction Pointer to the function to call.
131 * @param cArgs Number of arguments following in the ellipsis.
132 * Not possible to pass 64-bit arguments!
133 * @param ... Function arguments.
134 */
135VMR3DECL(int) VMR3ReqCallVoidU(PUVM pUVM, PVMREQ *ppReq, unsigned cMillies, PFNRT pfnFunction, unsigned cArgs, ...)
136{
137 va_list va;
138 va_start(va, cArgs);
139 int rc = VMR3ReqCallVU(pUVM, ppReq, cMillies, VMREQFLAGS_VOID, pfnFunction, cArgs, va);
140 va_end(va);
141 return rc;
142}
143
144
145/**
146 * Allocate and queue a call request to a void function.
147 *
148 * If it's desired to poll on the completion of the request set cMillies
149 * to 0 and use VMR3ReqWait() to check for completation. In the other case
150 * use RT_INDEFINITE_WAIT.
151 * The returned request packet must be freed using VMR3ReqFree().
152 *
153 * @returns VBox status code.
154 * Will not return VERR_INTERRUPTED.
155 * @returns VERR_TIMEOUT if cMillies was reached without the packet being completed.
156 *
157 * @param pVM The VM handle.
158 * @param ppReq Where to store the pointer to the request.
159 * This will be NULL or a valid request pointer not matter what happends.
160 * @param cMillies Number of milliseconds to wait for the request to
161 * be completed. Use RT_INDEFINITE_WAIT to only
162 * wait till it's completed.
163 * @param pfnFunction Pointer to the function to call.
164 * @param cArgs Number of arguments following in the ellipsis.
165 * Not possible to pass 64-bit arguments!
166 * @param ... Function arguments.
167 */
168VMR3DECL(int) VMR3ReqCallVoid(PVM pVM, PVMREQ *ppReq, unsigned cMillies, PFNRT pfnFunction, unsigned cArgs, ...)
169{
170 va_list va;
171 va_start(va, cArgs);
172 int rc = VMR3ReqCallVU(pVM->pUVM, ppReq, cMillies, VMREQFLAGS_VOID, pfnFunction, cArgs, va);
173 va_end(va);
174 return rc;
175}
176
177
178/**
179 * Allocate and queue a call request to a void function.
180 *
181 * If it's desired to poll on the completion of the request set cMillies
182 * to 0 and use VMR3ReqWait() to check for completation. In the other case
183 * use RT_INDEFINITE_WAIT.
184 * The returned request packet must be freed using VMR3ReqFree().
185 *
186 * @returns VBox status code.
187 * Will not return VERR_INTERRUPTED.
188 * @returns VERR_TIMEOUT if cMillies was reached without the packet being completed.
189 *
190 * @param pVM The VM handle.
191 * @param ppReq Where to store the pointer to the request.
192 * This will be NULL or a valid request pointer not matter what happends, unless fFlags
193 * contains VMREQFLAGS_NO_WAIT when it will be optional and always NULL.
194 * @param cMillies Number of milliseconds to wait for the request to
195 * be completed. Use RT_INDEFINITE_WAIT to only
196 * wait till it's completed.
197 * @param fFlags A combination of the VMREQFLAGS values.
198 * @param pfnFunction Pointer to the function to call.
199 * @param cArgs Number of arguments following in the ellipsis.
200 * Not possible to pass 64-bit arguments!
201 * @param ... Function arguments.
202 */
203VMR3DECL(int) VMR3ReqCallEx(PVM pVM, PVMREQ *ppReq, unsigned cMillies, unsigned fFlags, PFNRT pfnFunction, unsigned cArgs, ...)
204{
205 va_list va;
206 va_start(va, cArgs);
207 int rc = VMR3ReqCallVU(pVM->pUVM, ppReq, cMillies, fFlags, pfnFunction, cArgs, va);
208 va_end(va);
209 return rc;
210}
211
212
213/**
214 * Allocate and queue a call request to a void function.
215 *
216 * If it's desired to poll on the completion of the request set cMillies
217 * to 0 and use VMR3ReqWait() to check for completation. In the other case
218 * use RT_INDEFINITE_WAIT.
219 * The returned request packet must be freed using VMR3ReqFree().
220 *
221 * @returns VBox status code.
222 * Will not return VERR_INTERRUPTED.
223 * @returns VERR_TIMEOUT if cMillies was reached without the packet being completed.
224 *
225 * @param pUVM Pointer to the user mode VM structure.
226 * @param ppReq Where to store the pointer to the request.
227 * This will be NULL or a valid request pointer not matter what happends, unless fFlags
228 * contains VMREQFLAGS_NO_WAIT when it will be optional and always NULL.
229 * @param cMillies Number of milliseconds to wait for the request to
230 * be completed. Use RT_INDEFINITE_WAIT to only
231 * wait till it's completed.
232 * @param fFlags A combination of the VMREQFLAGS values.
233 * @param pfnFunction Pointer to the function to call.
234 * @param cArgs Number of arguments following in the ellipsis.
235 * Not possible to pass 64-bit arguments!
236 * @param ... Function arguments.
237 */
238VMR3DECL(int) VMR3ReqCallU(PUVM pUVM, PVMREQ *ppReq, unsigned cMillies, unsigned fFlags, PFNRT pfnFunction, unsigned cArgs, ...)
239{
240 va_list va;
241 va_start(va, cArgs);
242 int rc = VMR3ReqCallVU(pUVM, ppReq, cMillies, fFlags, pfnFunction, cArgs, va);
243 va_end(va);
244 return rc;
245}
246
247
248/**
249 * Allocate and queue a call request.
250 *
251 * If it's desired to poll on the completion of the request set cMillies
252 * to 0 and use VMR3ReqWait() to check for completation. In the other case
253 * use RT_INDEFINITE_WAIT.
254 * The returned request packet must be freed using VMR3ReqFree().
255 *
256 * @returns VBox status code.
257 * Will not return VERR_INTERRUPTED.
258 * @returns VERR_TIMEOUT if cMillies was reached without the packet being completed.
259 *
260 * @param pUVM Pointer to the user mode VM structure.
261 * @param ppReq Where to store the pointer to the request.
262 * This will be NULL or a valid request pointer not matter what happends, unless fFlags
263 * contains VMREQFLAGS_NO_WAIT when it will be optional and always NULL.
264 * @param cMillies Number of milliseconds to wait for the request to
265 * be completed. Use RT_INDEFINITE_WAIT to only
266 * wait till it's completed.
267 * @param pfnFunction Pointer to the function to call.
268 * @param fFlags A combination of the VMREQFLAGS values.
269 * @param cArgs Number of arguments following in the ellipsis.
270 * Stuff which differs in size from uintptr_t is gonna make trouble, so don't try!
271 * @param Args Argument vector.
272 */
273VMR3DECL(int) VMR3ReqCallVU(PUVM pUVM, PVMREQ *ppReq, unsigned cMillies, unsigned fFlags, PFNRT pfnFunction, unsigned cArgs, va_list Args)
274{
275 LogFlow(("VMR3ReqCallV: cMillies=%d fFlags=%#x pfnFunction=%p cArgs=%d\n", cMillies, fFlags, pfnFunction, cArgs));
276
277 /*
278 * Validate input.
279 */
280 AssertPtrReturn(pfnFunction, VERR_INVALID_POINTER);
281 AssertPtrReturn(pUVM, VERR_INVALID_POINTER);
282 AssertReturn(!(fFlags & ~(VMREQFLAGS_RETURN_MASK | VMREQFLAGS_NO_WAIT)), VERR_INVALID_PARAMETER);
283 if (!(fFlags & VMREQFLAGS_NO_WAIT) || ppReq)
284 {
285 AssertPtrReturn(ppReq, VERR_INVALID_POINTER);
286 *ppReq = NULL;
287 }
288 PVMREQ pReq = NULL;
289 AssertMsgReturn(cArgs * sizeof(uintptr_t) <= sizeof(pReq->u.Internal.aArgs),
290 ("cArg=%d\n", cArgs),
291 VERR_TOO_MUCH_DATA);
292
293 /*
294 * Allocate request
295 */
296 int rc = VMR3ReqAllocU(pUVM, &pReq, VMREQTYPE_INTERNAL);
297 if (VBOX_FAILURE(rc))
298 return rc;
299
300 /*
301 * Initialize the request data.
302 */
303 pReq->fFlags = fFlags;
304 pReq->u.Internal.pfn = pfnFunction;
305 pReq->u.Internal.cArgs = cArgs;
306 for (unsigned iArg = 0; iArg < cArgs; iArg++)
307 pReq->u.Internal.aArgs[iArg] = va_arg(Args, uintptr_t);
308
309 /*
310 * Queue the request and return.
311 */
312 rc = VMR3ReqQueue(pReq, cMillies);
313 if ( VBOX_FAILURE(rc)
314 && rc != VERR_TIMEOUT)
315 {
316 VMR3ReqFree(pReq);
317 pReq = NULL;
318 }
319 if (!(fFlags & VMREQFLAGS_NO_WAIT))
320 {
321 *ppReq = pReq;
322 LogFlow(("VMR3ReqCallV: returns %Vrc *ppReq=%p\n", rc, pReq));
323 }
324 else
325 LogFlow(("VMR3ReqCallV: returns %Vrc\n", rc));
326 Assert(rc != VERR_INTERRUPTED);
327 return rc;
328}
329
330
331/**
332 * Joins the list pList with whatever is linked up at *pHead.
333 */
334static void vmr3ReqJoinFreeSub(volatile PVMREQ *ppHead, PVMREQ pList)
335{
336 for (unsigned cIterations = 0;; cIterations++)
337 {
338 PVMREQ pHead = (PVMREQ)ASMAtomicXchgPtr((void * volatile *)ppHead, pList);
339 if (!pHead)
340 return;
341 PVMREQ pTail = pHead;
342 while (pTail->pNext)
343 pTail = pTail->pNext;
344 pTail->pNext = pList;
345 if (ASMAtomicCmpXchgPtr((void * volatile *)ppHead, (void *)pHead, pList))
346 return;
347 pTail->pNext = NULL;
348 if (ASMAtomicCmpXchgPtr((void * volatile *)ppHead, (void *)pHead, NULL))
349 return;
350 pList = pHead;
351 Assert(cIterations != 32);
352 Assert(cIterations != 64);
353 }
354}
355
356
357/**
358 * Joins the list pList with whatever is linked up at *pHead.
359 */
360static void vmr3ReqJoinFree(PVMINTUSERPERVM pVMInt, PVMREQ pList)
361{
362 /*
363 * Split the list if it's too long.
364 */
365 unsigned cReqs = 1;
366 PVMREQ pTail = pList;
367 while (pTail->pNext)
368 {
369 if (cReqs++ > 25)
370 {
371 const uint32_t i = pVMInt->iReqFree;
372 vmr3ReqJoinFreeSub(&pVMInt->apReqFree[(i + 2) % RT_ELEMENTS(pVMInt->apReqFree)], pTail->pNext);
373
374 pTail->pNext = NULL;
375 vmr3ReqJoinFreeSub(&pVMInt->apReqFree[(i + 2 + (i == pVMInt->iReqFree)) % RT_ELEMENTS(pVMInt->apReqFree)], pTail->pNext);
376 return;
377 }
378 pTail = pTail->pNext;
379 }
380 vmr3ReqJoinFreeSub(&pVMInt->apReqFree[(pVMInt->iReqFree + 2) % RT_ELEMENTS(pVMInt->apReqFree)], pList);
381}
382
383
384/**
385 * Allocates a request packet.
386 *
387 * The caller allocates a request packet, fills in the request data
388 * union and queues the request.
389 *
390 * @returns VBox status code.
391 *
392 * @param pVM VM handle.
393 * @param ppReq Where to store the pointer to the allocated packet.
394 * @param enmType Package type.
395 */
396VMR3DECL(int) VMR3ReqAlloc(PVM pVM, PVMREQ *ppReq, VMREQTYPE enmType)
397{
398 return VMR3ReqAllocU(pVM->pUVM, ppReq, enmType);
399}
400
401
402/**
403 * Allocates a request packet.
404 *
405 * The caller allocates a request packet, fills in the request data
406 * union and queues the request.
407 *
408 * @returns VBox status code.
409 *
410 * @param pUVM Pointer to the user mode VM structure.
411 * @param ppReq Where to store the pointer to the allocated packet.
412 * @param enmType Package type.
413 */
414VMR3DECL(int) VMR3ReqAllocU(PUVM pUVM, PVMREQ *ppReq, VMREQTYPE enmType)
415{
416 /*
417 * Validate input.
418 */
419 AssertMsgReturn(enmType > VMREQTYPE_INVALID && enmType < VMREQTYPE_MAX,
420 ("Invalid package type %d valid range %d-%d inclusivly.\n",
421 enmType, VMREQTYPE_INVALID + 1, VMREQTYPE_MAX - 1),
422 VERR_VM_REQUEST_INVALID_TYPE);
423 AssertPtrReturn(ppReq, VERR_INVALID_POINTER);
424
425 /*
426 * Try get a recycled packet.
427 * While this could all be solved with a single list with a lock, it's a sport
428 * of mine to avoid locks.
429 */
430 int cTries = RT_ELEMENTS(pUVM->vm.s.apReqFree) * 2;
431 while (--cTries >= 0)
432 {
433 PVMREQ volatile *ppHead = &pUVM->vm.s.apReqFree[ASMAtomicIncU32(&pUVM->vm.s.iReqFree) % RT_ELEMENTS(pUVM->vm.s.apReqFree)];
434#if 0 /* sad, but this won't work safely because the reading of pReq->pNext. */
435 PVMREQ pNext = NULL;
436 PVMREQ pReq = *ppHead;
437 if ( pReq
438 && !ASMAtomicCmpXchgPtr((void * volatile *)ppHead, (pNext = pReq->pNext), pReq)
439 && (pReq = *ppHead)
440 && !ASMAtomicCmpXchgPtr((void * volatile *)ppHead, (pNext = pReq->pNext), pReq))
441 pReq = NULL;
442 if (pReq)
443 {
444 Assert(pReq->pNext == pNext); NOREF(pReq);
445#else
446 PVMREQ pReq = (PVMREQ)ASMAtomicXchgPtr((void * volatile *)ppHead, NULL);
447 if (pReq)
448 {
449 PVMREQ pNext = pReq->pNext;
450 if ( pNext
451 && !ASMAtomicCmpXchgPtr((void * volatile *)ppHead, pNext, NULL))
452 {
453 STAM_COUNTER_INC(&pUVM->vm.s.StatReqAllocRaces);
454 vmr3ReqJoinFree(&pUVM->vm.s, pReq->pNext);
455 }
456#endif
457 ASMAtomicDecU32(&pUVM->vm.s.cReqFree);
458
459 /*
460 * Make sure the event sem is not signaled.
461 */
462 if (!pReq->fEventSemClear)
463 {
464 int rc = RTSemEventWait(pReq->EventSem, 0);
465 if (rc != VINF_SUCCESS && rc != VERR_TIMEOUT)
466 {
467 /*
468 * This shall not happen, but if it does we'll just destroy
469 * the semaphore and create a new one.
470 */
471 AssertMsgFailed(("rc=%Vrc from RTSemEventWait(%#x).\n", rc, pReq->EventSem));
472 RTSemEventDestroy(pReq->EventSem);
473 rc = RTSemEventCreate(&pReq->EventSem);
474 AssertRC(rc);
475 if (VBOX_FAILURE(rc))
476 return rc;
477 }
478 pReq->fEventSemClear = true;
479 }
480 else
481 Assert(RTSemEventWait(pReq->EventSem, 0) == VERR_TIMEOUT);
482
483 /*
484 * Initialize the packet and return it.
485 */
486 Assert(pReq->enmType == VMREQTYPE_INVALID);
487 Assert(pReq->enmState == VMREQSTATE_FREE);
488 Assert(pReq->pUVM == pUVM);
489 ASMAtomicXchgSize(&pReq->pNext, NULL);
490 pReq->enmState = VMREQSTATE_ALLOCATED;
491 pReq->iStatus = VERR_VM_REQUEST_STATUS_STILL_PENDING;
492 pReq->fFlags = VMREQFLAGS_VBOX_STATUS;
493 pReq->enmType = enmType;
494
495 *ppReq = pReq;
496 STAM_COUNTER_INC(&pUVM->vm.s.StatReqAllocRecycled);
497 LogFlow(("VMR3ReqAlloc: returns VINF_SUCCESS *ppReq=%p recycled\n", pReq));
498 return VINF_SUCCESS;
499 }
500 }
501
502 /*
503 * Ok allocate one.
504 */
505 PVMREQ pReq = (PVMREQ)MMR3HeapAllocU(pUVM, MM_TAG_VM_REQ, sizeof(*pReq));
506 if (!pReq)
507 return VERR_NO_MEMORY;
508
509 /*
510 * Create the semaphore.
511 */
512 int rc = RTSemEventCreate(&pReq->EventSem);
513 AssertRC(rc);
514 if (VBOX_FAILURE(rc))
515 {
516 MMR3HeapFree(pReq);
517 return rc;
518 }
519
520 /*
521 * Initialize the packet and return it.
522 */
523 pReq->pNext = NULL;
524 pReq->pUVM = pUVM;
525 pReq->enmState = VMREQSTATE_ALLOCATED;
526 pReq->iStatus = VERR_VM_REQUEST_STATUS_STILL_PENDING;
527 pReq->fEventSemClear = true;
528 pReq->fFlags = VMREQFLAGS_VBOX_STATUS;
529 pReq->enmType = enmType;
530
531 *ppReq = pReq;
532 STAM_COUNTER_INC(&pUVM->vm.s.StatReqAllocNew);
533 LogFlow(("VMR3ReqAlloc: returns VINF_SUCCESS *ppReq=%p new\n", pReq));
534 return VINF_SUCCESS;
535}
536
537
538/**
539 * Free a request packet.
540 *
541 * @returns VBox status code.
542 *
543 * @param pReq Package to free.
544 * @remark The request packet must be in allocated or completed state!
545 */
546VMR3DECL(int) VMR3ReqFree(PVMREQ pReq)
547{
548 /*
549 * Ignore NULL (all free functions should do this imho).
550 */
551 if (!pReq)
552 return VINF_SUCCESS;
553
554 /*
555 * Check packet state.
556 */
557 switch (pReq->enmState)
558 {
559 case VMREQSTATE_ALLOCATED:
560 case VMREQSTATE_COMPLETED:
561 break;
562 default:
563 AssertMsgFailed(("Invalid state %d!\n", pReq->enmState));
564 return VERR_VM_REQUEST_STATE;
565 }
566
567 /*
568 * Make it a free packet and put it into one of the free packet lists.
569 */
570 pReq->enmState = VMREQSTATE_FREE;
571 pReq->iStatus = VERR_VM_REQUEST_STATUS_FREED;
572 pReq->enmType = VMREQTYPE_INVALID;
573
574 PUVM pUVM = pReq->pUVM;
575 STAM_COUNTER_INC(&pUVM->vm.s.StatReqFree);
576
577 if (pUVM->vm.s.cReqFree < 128)
578 {
579 ASMAtomicIncU32(&pUVM->vm.s.cReqFree);
580 PVMREQ volatile *ppHead = &pUVM->vm.s.apReqFree[ASMAtomicIncU32(&pUVM->vm.s.iReqFree) % RT_ELEMENTS(pUVM->vm.s.apReqFree)];
581 PVMREQ pNext;
582 do
583 {
584 pNext = *ppHead;
585 ASMAtomicXchgPtr((void * volatile *)&pReq->pNext, pNext);
586 } while (!ASMAtomicCmpXchgPtr((void * volatile *)ppHead, (void *)pReq, (void *)pNext));
587 }
588 else
589 {
590 STAM_COUNTER_INC(&pReq->pUVM->vm.s.StatReqFreeOverflow);
591 RTSemEventDestroy(pReq->EventSem);
592 MMR3HeapFree(pReq);
593 }
594 return VINF_SUCCESS;
595}
596
597
598/**
599 * Queue a request.
600 *
601 * The quest must be allocated using VMR3ReqAlloc() and contain
602 * all the required data.
603 * If it's desired to poll on the completion of the request set cMillies
604 * to 0 and use VMR3ReqWait() to check for completation. In the other case
605 * use RT_INDEFINITE_WAIT.
606 *
607 * @returns VBox status code.
608 * Will not return VERR_INTERRUPTED.
609 * @returns VERR_TIMEOUT if cMillies was reached without the packet being completed.
610 *
611 * @param pReq The request to queue.
612 * @param cMillies Number of milliseconds to wait for the request to
613 * be completed. Use RT_INDEFINITE_WAIT to only
614 * wait till it's completed.
615 */
616VMR3DECL(int) VMR3ReqQueue(PVMREQ pReq, unsigned cMillies)
617{
618 LogFlow(("VMR3ReqQueue: pReq=%p cMillies=%d\n", pReq, cMillies));
619 /*
620 * Verify the supplied package.
621 */
622 AssertMsgReturn(pReq->enmState == VMREQSTATE_ALLOCATED, ("%d\n", pReq->enmState), VERR_VM_REQUEST_STATE);
623 AssertMsgReturn( VALID_PTR(pReq->pUVM)
624 && !pReq->pNext
625 && pReq->EventSem != NIL_RTSEMEVENT,
626 ("Invalid request package! Anyone cooking their own packages???\n"),
627 VERR_VM_REQUEST_INVALID_PACKAGE);
628 AssertMsgReturn( pReq->enmType > VMREQTYPE_INVALID
629 && pReq->enmType < VMREQTYPE_MAX,
630 ("Invalid package type %d valid range %d-%d inclusivly. This was verified on alloc too...\n",
631 pReq->enmType, VMREQTYPE_INVALID + 1, VMREQTYPE_MAX - 1),
632 VERR_VM_REQUEST_INVALID_TYPE);
633
634 /*
635 * Are we the EMT or not?
636 * Also, store pVM (and fFlags) locally since pReq may be invalid after queuing it.
637 */
638 int rc = VINF_SUCCESS;
639 PUVM pUVM = ((VMREQ volatile *)pReq)->pUVM; /* volatile paranoia */
640 if (pUVM->vm.s.NativeThreadEMT != RTThreadNativeSelf())
641 {
642 unsigned fFlags = ((VMREQ volatile *)pReq)->fFlags; /* volatile paranoia */
643
644 /*
645 * Insert it.
646 */
647 pReq->enmState = VMREQSTATE_QUEUED;
648 PVMREQ pNext;
649 do
650 {
651 pNext = pUVM->vm.s.pReqs;
652 pReq->pNext = pNext;
653 } while (!ASMAtomicCmpXchgPtr((void * volatile *)&pUVM->vm.s.pReqs, (void *)pReq, (void *)pNext));
654
655 /*
656 * Notify EMT.
657 */
658 if (pUVM->pVM)
659 VM_FF_SET(pUVM->pVM, VM_FF_REQUEST);
660 VMR3NotifyFFU(pUVM, false);
661
662 /*
663 * Wait and return.
664 */
665 if (!(fFlags & VMREQFLAGS_NO_WAIT))
666 rc = VMR3ReqWait(pReq, cMillies);
667 LogFlow(("VMR3ReqQueue: returns %Vrc\n", rc));
668 }
669 else
670 {
671 /*
672 * The requester was EMT, just execute it.
673 */
674 pReq->enmState = VMREQSTATE_QUEUED;
675 rc = vmR3ReqProcessOneU(pUVM, pReq);
676 LogFlow(("VMR3ReqQueue: returns %Vrc (processed)\n", rc));
677 }
678 return rc;
679}
680
681
682/**
683 * Wait for a request to be completed.
684 *
685 * @returns VBox status code.
686 * @returns VERR_TIMEOUT if cMillies was reached without the packet being completed.
687 *
688 * @param pReq The request to wait for.
689 * @param cMillies Number of milliseconds to wait.
690 * Use RT_INDEFINITE_WAIT to only wait till it's completed.
691 */
692VMR3DECL(int) VMR3ReqWait(PVMREQ pReq, unsigned cMillies)
693{
694 LogFlow(("VMR3ReqWait: pReq=%p cMillies=%d\n", pReq, cMillies));
695
696 /*
697 * Verify the supplied package.
698 */
699 AssertMsgReturn( pReq->enmState == VMREQSTATE_QUEUED
700 || pReq->enmState == VMREQSTATE_PROCESSING
701 || pReq->enmState == VMREQSTATE_COMPLETED,
702 ("Invalid state %d\n", pReq->enmState),
703 VERR_VM_REQUEST_STATE);
704 AssertMsgReturn( VALID_PTR(pReq->pUVM)
705 && pReq->EventSem != NIL_RTSEMEVENT,
706 ("Invalid request package! Anyone cooking their own packages???\n"),
707 VERR_VM_REQUEST_INVALID_PACKAGE);
708 AssertMsgReturn( pReq->enmType > VMREQTYPE_INVALID
709 && pReq->enmType < VMREQTYPE_MAX,
710 ("Invalid package type %d valid range %d-%d inclusivly. This was verified on alloc too...\n",
711 pReq->enmType, VMREQTYPE_INVALID + 1, VMREQTYPE_MAX - 1),
712 VERR_VM_REQUEST_INVALID_TYPE);
713
714 /*
715 * Check for deadlock condition
716 */
717 PUVM pUVM = pReq->pUVM;
718 AssertMsg(!pUVM->pVM || !VMMR3LockIsOwner(pUVM->pVM),
719 ("Waiting for EMT to process a request, but we own the global VM lock!?!?!?!\n"));
720
721 /*
722 * Wait on the package.
723 */
724 int rc;
725 if (cMillies != RT_INDEFINITE_WAIT)
726 rc = RTSemEventWait(pReq->EventSem, cMillies);
727 else
728 {
729 do
730 {
731 rc = RTSemEventWait(pReq->EventSem, RT_INDEFINITE_WAIT);
732 Assert(rc != VERR_TIMEOUT);
733 } while ( pReq->enmState != VMREQSTATE_COMPLETED
734 && pReq->enmState != VMREQSTATE_INVALID);
735 }
736 if (VBOX_SUCCESS(rc))
737 ASMAtomicXchgSize(&pReq->fEventSemClear, true);
738 if (pReq->enmState == VMREQSTATE_COMPLETED)
739 rc = VINF_SUCCESS;
740 LogFlow(("VMR3ReqWait: returns %Vrc\n", rc));
741 Assert(rc != VERR_INTERRUPTED);
742 return rc;
743}
744
745
746/**
747 * Process pending request(s).
748 *
749 * This function is called from a forced action handler in the EMT
750 * or from one of the EMT loops.
751 *
752 * @returns VBox status code.
753 *
754 * @param pUVM Pointer to the user mode VM structure.
755 */
756VMR3DECL(int) VMR3ReqProcessU(PUVM pUVM)
757{
758 LogFlow(("VMR3ReqProcessU: (enmVMState=%d)\n", pUVM->pVM ? pUVM->pVM->enmVMState : VMSTATE_CREATING));
759
760 /*
761 * Process loop.
762 *
763 * We do not repeat the outer loop if we've got an informationtional status code
764 * since that code needs processing by our caller.
765 */
766 int rc = VINF_SUCCESS;
767 while (rc <= VINF_SUCCESS)
768 {
769 /*
770 * Get pending requests.
771 */
772 if (RT_LIKELY(pUVM->pVM))
773 VM_FF_CLEAR(pUVM->pVM, VM_FF_REQUEST);
774 PVMREQ pReqs = (PVMREQ)ASMAtomicXchgPtr((void * volatile *)&pUVM->vm.s.pReqs, NULL);
775 if (!pReqs)
776 break;
777
778 /*
779 * Reverse the list to process it in FIFO order.
780 */
781 PVMREQ pReq = pReqs;
782 if (pReq->pNext)
783 Log2(("VMR3ReqProcess: 2+ requests: %p %p %p\n", pReq, pReq->pNext, pReq->pNext->pNext));
784 pReqs = NULL;
785 while (pReq)
786 {
787 Assert(pReq->enmState == VMREQSTATE_QUEUED);
788 Assert(pReq->pUVM == pUVM);
789 PVMREQ pCur = pReq;
790 pReq = pReq->pNext;
791 pCur->pNext = pReqs;
792 pReqs = pCur;
793 }
794
795
796 /*
797 * Process the requests.
798 *
799 * Since this is a FF worker certain rules applies to the
800 * status codes. See the EM section in VBox/err.h and EM.cpp for details.
801 */
802 while (pReqs)
803 {
804 /* Unchain the first request and advance the list. */
805 pReq = pReqs;
806 pReqs = pReqs->pNext;
807 pReq->pNext = NULL;
808
809 /* Process the request */
810 int rc2 = vmR3ReqProcessOneU(pUVM, pReq);
811
812 /*
813 * The status code handling extremely important yet very fragile. Should probably
814 * look for a better way of communicating status changes to EM...
815 */
816 if ( rc2 >= VINF_EM_FIRST
817 && rc2 <= VINF_EM_LAST
818 && ( rc == VINF_SUCCESS
819 || rc2 < rc) )
820 rc = rc2;
821 }
822 }
823
824 LogFlow(("VMR3ReqProcess: returns %Vrc (enmVMState=%d)\n", rc, pUVM->pVM ? pUVM->pVM->enmVMState : VMSTATE_CREATING));
825 return rc;
826}
827
828
829/**
830 * Process one request.
831 *
832 * @returns VBox status code.
833 *
834 * @param pVM VM handle.
835 * @param pReq Request packet to process.
836 */
837static int vmR3ReqProcessOneU(PUVM pUVM, PVMREQ pReq)
838{
839 LogFlow(("vmR3ReqProcessOne: pReq=%p type=%d fFlags=%#x\n", pReq, pReq->enmType, pReq->fFlags));
840
841 /*
842 * Process the request.
843 */
844 Assert(pReq->enmState == VMREQSTATE_QUEUED);
845 pReq->enmState = VMREQSTATE_PROCESSING;
846 int rcRet = VINF_SUCCESS; /* the return code of this function. */
847 int rcReq = VERR_NOT_IMPLEMENTED; /* the request status. */
848 switch (pReq->enmType)
849 {
850 /*
851 * A packed down call frame.
852 */
853 case VMREQTYPE_INTERNAL:
854 {
855 uintptr_t *pauArgs = &pReq->u.Internal.aArgs[0];
856 union
857 {
858 PFNRT pfn;
859 DECLCALLBACKMEMBER(int, pfn00)(void);
860 DECLCALLBACKMEMBER(int, pfn01)(uintptr_t);
861 DECLCALLBACKMEMBER(int, pfn02)(uintptr_t, uintptr_t);
862 DECLCALLBACKMEMBER(int, pfn03)(uintptr_t, uintptr_t, uintptr_t);
863 DECLCALLBACKMEMBER(int, pfn04)(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
864 DECLCALLBACKMEMBER(int, pfn05)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
865 DECLCALLBACKMEMBER(int, pfn06)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
866 DECLCALLBACKMEMBER(int, pfn07)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
867 DECLCALLBACKMEMBER(int, pfn08)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
868 DECLCALLBACKMEMBER(int, pfn09)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
869 DECLCALLBACKMEMBER(int, pfn10)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
870 DECLCALLBACKMEMBER(int, pfn11)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
871 DECLCALLBACKMEMBER(int, pfn12)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
872 } u;
873 u.pfn = pReq->u.Internal.pfn;
874#ifdef RT_ARCH_AMD64
875 switch (pReq->u.Internal.cArgs)
876 {
877 case 0: rcRet = u.pfn00(); break;
878 case 1: rcRet = u.pfn01(pauArgs[0]); break;
879 case 2: rcRet = u.pfn02(pauArgs[0], pauArgs[1]); break;
880 case 3: rcRet = u.pfn03(pauArgs[0], pauArgs[1], pauArgs[2]); break;
881 case 4: rcRet = u.pfn04(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3]); break;
882 case 5: rcRet = u.pfn05(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4]); break;
883 case 6: rcRet = u.pfn06(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5]); break;
884 case 7: rcRet = u.pfn07(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6]); break;
885 case 8: rcRet = u.pfn08(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7]); break;
886 case 9: rcRet = u.pfn09(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8]); break;
887 case 10: rcRet = u.pfn10(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8], pauArgs[9]); break;
888 case 11: rcRet = u.pfn11(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8], pauArgs[9], pauArgs[10]); break;
889 case 12: rcRet = u.pfn12(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8], pauArgs[9], pauArgs[10], pauArgs[11]); break;
890 default:
891 AssertReleaseMsgFailed(("cArgs=%d\n", pReq->u.Internal.cArgs));
892 rcRet = rcReq = VERR_INTERNAL_ERROR;
893 break;
894 }
895#else /* x86: */
896 size_t cbArgs = pReq->u.Internal.cArgs * sizeof(uintptr_t);
897# ifdef __GNUC__
898 __asm__ __volatile__("movl %%esp, %%edx\n\t"
899 "subl %2, %%esp\n\t"
900 "andl $0xfffffff0, %%esp\n\t"
901 "shrl $2, %2\n\t"
902 "movl %%esp, %%edi\n\t"
903 "rep movsl\n\t"
904 "movl %%edx, %%edi\n\t"
905 "call *%%eax\n\t"
906 "mov %%edi, %%esp\n\t"
907 : "=a" (rcRet),
908 "=S" (pauArgs),
909 "=c" (cbArgs)
910 : "0" (u.pfn),
911 "1" (pauArgs),
912 "2" (cbArgs)
913 : "edi", "edx");
914# else
915 __asm
916 {
917 xor edx, edx /* just mess it up. */
918 mov eax, u.pfn
919 mov ecx, cbArgs
920 shr ecx, 2
921 mov esi, pauArgs
922 mov ebx, esp
923 sub esp, cbArgs
924 and esp, 0xfffffff0
925 mov edi, esp
926 rep movsd
927 call eax
928 mov esp, ebx
929 mov rcRet, eax
930 }
931# endif
932#endif /* x86 */
933 if ((pReq->fFlags & (VMREQFLAGS_RETURN_MASK)) == VMREQFLAGS_VOID)
934 rcRet = VINF_SUCCESS;
935 rcReq = rcRet;
936 break;
937 }
938
939 default:
940 AssertMsgFailed(("pReq->enmType=%d\n", pReq->enmType));
941 rcReq = VERR_NOT_IMPLEMENTED;
942 break;
943 }
944
945 /*
946 * Complete the request.
947 */
948 pReq->iStatus = rcReq;
949 pReq->enmState = VMREQSTATE_COMPLETED;
950 if (pReq->fFlags & VMREQFLAGS_NO_WAIT)
951 {
952 /* Free the packet, nobody is waiting. */
953 LogFlow(("vmR3ReqProcessOne: Completed request %p: rcReq=%Vrc rcRet=%Vrc - freeing it\n",
954 pReq, rcReq, rcRet));
955 VMR3ReqFree(pReq);
956 }
957 else
958 {
959 /* Notify the waiter and him free up the packet. */
960 LogFlow(("vmR3ReqProcessOne: Completed request %p: rcReq=%Vrc rcRet=%Vrc - notifying waiting thread\n",
961 pReq, rcReq, rcRet));
962 ASMAtomicXchgSize(&pReq->fEventSemClear, false);
963 int rc2 = RTSemEventSignal(pReq->EventSem);
964 if (VBOX_FAILURE(rc2))
965 {
966 AssertRC(rc2);
967 rcRet = rc2;
968 }
969 }
970 return rcRet;
971}
972
973
974
975
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette