VirtualBox

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

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

Biggest check-in ever. New source code headers for all (C) innotek files.

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