VirtualBox

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

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

Removed unused VMR3ReqCallU variant.

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