VirtualBox

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

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

pReq may be invalid after queuing it (VMREQFLAGS_NO_WAIT), so make local copies of the fFlags and pVM members. This should fix the sporadic assertions we've been seeing.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 30.4 KB
Line 
1/* $Id: VMReq.cpp 6274 2008-01-08 12:56:33Z 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
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 /*
529 * Are we the EMT or not?
530 * Also, store pVM (and fFlags) locally since pReq may be invalid after queuing it.
531 */
532 int rc = VINF_SUCCESS;
533 PVM pVM = ((VMREQ volatile *)pReq)->pVM; /* volatile paranoia */
534 if (pVM->NativeThreadEMT != RTThreadNativeSelf())
535 {
536 unsigned fFlags = ((VMREQ volatile *)pReq)->fFlags; /* volatile paranoia */
537
538 /*
539 * Insert it.
540 */
541 pReq->enmState = VMREQSTATE_QUEUED;
542 PVMREQ pNext;
543 do
544 {
545 pNext = pVM->vm.s.pReqs;
546 pReq->pNext = pNext;
547 } while (!ASMAtomicCmpXchgPtr((void * volatile *)&pVM->vm.s.pReqs, (void *)pReq, (void *)pNext));
548
549 /*
550 * Notify EMT.
551 */
552 VM_FF_SET(pVM, VM_FF_REQUEST);
553 VMR3NotifyFF(pVM, false);
554
555 /*
556 * Wait and return.
557 */
558 if (!(fFlags & VMREQFLAGS_NO_WAIT))
559 rc = VMR3ReqWait(pReq, cMillies);
560 LogFlow(("VMR3ReqQueue: returns %Vrc\n", rc));
561 }
562 else
563 {
564 /*
565 * The requester was EMT, just execute it.
566 */
567 pReq->enmState = VMREQSTATE_QUEUED;
568 rc = vmR3ReqProcessOne(pVM, pReq);
569 LogFlow(("VMR3ReqQueue: returns %Vrc (processed)\n", rc));
570 }
571 return rc;
572}
573
574
575/**
576 * Wait for a request to be completed.
577 *
578 * @returns VBox status code.
579 * @returns VERR_TIMEOUT if cMillies was reached without the packet being completed.
580 *
581 * @param pReq The request to wait for.
582 * @param cMillies Number of milliseconds to wait.
583 * Use RT_INDEFINITE_WAIT to only wait till it's completed.
584 */
585VMR3DECL(int) VMR3ReqWait(PVMREQ pReq, unsigned cMillies)
586{
587 LogFlow(("VMR3ReqWait: pReq=%p cMillies=%d\n", pReq, cMillies));
588
589 /*
590 * Verify the supplied package.
591 */
592 if ( pReq->enmState != VMREQSTATE_QUEUED
593 && pReq->enmState != VMREQSTATE_PROCESSING
594 && pReq->enmState != VMREQSTATE_COMPLETED)
595 {
596 AssertMsgFailed(("Invalid state %d\n", pReq->enmState));
597 return VERR_VM_REQUEST_STATE;
598 }
599 if ( !pReq->pVM
600 || !pReq->EventSem)
601 {
602 AssertMsgFailed(("Invalid request package! Anyone cooking their own packages???\n"));
603 return VERR_VM_REQUEST_INVALID_PACKAGE;
604 }
605 if ( pReq->enmType < VMREQTYPE_INVALID
606 || pReq->enmType > VMREQTYPE_MAX)
607 {
608 AssertMsgFailed(("Invalid package type %d valid range %d-%d inclusivly. This was verified on alloc and queue too...\n",
609 pReq->enmType, VMREQTYPE_INVALID + 1, VMREQTYPE_MAX - 1));
610 return VERR_VM_REQUEST_INVALID_TYPE;
611 }
612
613 /*
614 * Check for deadlock condition
615 */
616 AssertMsg(!VMMR3LockIsOwner(pReq->pVM), ("Waiting for EMT to process a request, but we own the global VM lock!?!?!?!\n"));
617
618 /*
619 * Wait on the package.
620 */
621 int rc;
622 if (cMillies != RT_INDEFINITE_WAIT)
623 rc = RTSemEventWait(pReq->EventSem, cMillies);
624 else
625 {
626 do
627 {
628 rc = RTSemEventWait(pReq->EventSem, RT_INDEFINITE_WAIT);
629 Assert(rc != VERR_TIMEOUT);
630 } while ( pReq->enmState != VMREQSTATE_COMPLETED
631 && pReq->enmState != VMREQSTATE_INVALID);
632 }
633 if (VBOX_SUCCESS(rc))
634 ASMAtomicXchgSize(&pReq->fEventSemClear, true);
635 if (pReq->enmState == VMREQSTATE_COMPLETED)
636 rc = VINF_SUCCESS;
637 LogFlow(("VMR3ReqWait: returns %Vrc\n", rc));
638 Assert(rc != VERR_INTERRUPTED);
639 return rc;
640}
641
642
643/**
644 * Process pending request(s).
645 *
646 * This function is called from a forced action handler in
647 * the EMT.
648 *
649 * @returns VBox status code.
650 *
651 * @param pVM VM handle.
652 */
653VMR3DECL(int) VMR3ReqProcess(PVM pVM)
654{
655 LogFlow(("VMR3ReqProcess: (enmVMState=%d)\n", pVM->enmVMState));
656
657 /*
658 * Process loop.
659 *
660 * We do not repeat the outer loop if we've got an informationtional status code
661 * since that code needs processing by our caller.
662 */
663 int rc = VINF_SUCCESS;
664 while (rc <= VINF_SUCCESS)
665 {
666 /*
667 * Get pending requests.
668 */
669 VM_FF_CLEAR(pVM, VM_FF_REQUEST);
670 PVMREQ pReqs = (PVMREQ)ASMAtomicXchgPtr((void * volatile *)&pVM->vm.s.pReqs, NULL);
671 if (!pReqs)
672 break;
673
674 /*
675 * Reverse the list to process it in FIFO order.
676 */
677 PVMREQ pReq = pReqs;
678 if (pReq->pNext)
679 Log2(("VMR3ReqProcess: 2+ requests: %p %p %p\n", pReq, pReq->pNext, pReq->pNext->pNext));
680 pReqs = NULL;
681 while (pReq)
682 {
683 Assert(pReq->enmState == VMREQSTATE_QUEUED);
684 Assert(pReq->pVM == pVM);
685 PVMREQ pCur = pReq;
686 pReq = pReq->pNext;
687 pCur->pNext = pReqs;
688 pReqs = pCur;
689 }
690
691
692 /*
693 * Process the requests.
694 *
695 * Since this is a FF worker certain rules applies to the
696 * status codes. See the EM section in VBox/err.h and EM.cpp for details.
697 */
698 while (pReqs)
699 {
700 /* Unchain the first request and advance the list. */
701 pReq = pReqs;
702 pReqs = pReqs->pNext;
703 pReq->pNext = NULL;
704
705 /* Process the request */
706 int rc2 = vmR3ReqProcessOne(pVM, pReq);
707
708 /*
709 * The status code handling extremely important yet very fragile. Should probably
710 * look for a better way of communicating status changes to EM...
711 */
712 if ( rc2 >= VINF_EM_FIRST
713 && rc2 <= VINF_EM_LAST
714 && ( rc == VINF_SUCCESS
715 || rc2 < rc) )
716 rc = rc2;
717 }
718 }
719
720 LogFlow(("VMR3ReqProcess: returns %Vrc (enmVMState=%d)\n", rc, pVM->enmVMState));
721 return rc;
722}
723
724
725/**
726 * Process one request.
727 *
728 * @returns VBox status code.
729 *
730 * @param pVM VM handle.
731 * @param pReq Request packet to process.
732 */
733static int vmR3ReqProcessOne(PVM pVM, PVMREQ pReq)
734{
735 LogFlow(("vmR3ReqProcessOne: pReq=%p type=%d fFlags=%#x\n", pReq, pReq->enmType, pReq->fFlags));
736
737 /*
738 * Process the request.
739 */
740 Assert(pReq->enmState == VMREQSTATE_QUEUED);
741 pReq->enmState = VMREQSTATE_PROCESSING;
742 int rcRet = VINF_SUCCESS; /* the return code of this function. */
743 int rcReq = VERR_NOT_IMPLEMENTED; /* the request status. */
744 switch (pReq->enmType)
745 {
746 /*
747 * A packed down call frame.
748 */
749 case VMREQTYPE_INTERNAL:
750 {
751 uintptr_t *pauArgs = &pReq->u.Internal.aArgs[0];
752 union
753 {
754 PFNRT pfn;
755 DECLCALLBACKMEMBER(int, pfn00)(void);
756 DECLCALLBACKMEMBER(int, pfn01)(uintptr_t);
757 DECLCALLBACKMEMBER(int, pfn02)(uintptr_t, uintptr_t);
758 DECLCALLBACKMEMBER(int, pfn03)(uintptr_t, uintptr_t, uintptr_t);
759 DECLCALLBACKMEMBER(int, pfn04)(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
760 DECLCALLBACKMEMBER(int, pfn05)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
761 DECLCALLBACKMEMBER(int, pfn06)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
762 DECLCALLBACKMEMBER(int, pfn07)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
763 DECLCALLBACKMEMBER(int, pfn08)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
764 DECLCALLBACKMEMBER(int, pfn09)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
765 DECLCALLBACKMEMBER(int, pfn10)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
766 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);
767 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);
768 } u;
769 u.pfn = pReq->u.Internal.pfn;
770#ifdef RT_ARCH_AMD64
771 switch (pReq->u.Internal.cArgs)
772 {
773 case 0: rcRet = u.pfn00(); break;
774 case 1: rcRet = u.pfn01(pauArgs[0]); break;
775 case 2: rcRet = u.pfn02(pauArgs[0], pauArgs[1]); break;
776 case 3: rcRet = u.pfn03(pauArgs[0], pauArgs[1], pauArgs[2]); break;
777 case 4: rcRet = u.pfn04(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3]); break;
778 case 5: rcRet = u.pfn05(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4]); break;
779 case 6: rcRet = u.pfn06(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5]); break;
780 case 7: rcRet = u.pfn07(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6]); break;
781 case 8: rcRet = u.pfn08(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7]); break;
782 case 9: rcRet = u.pfn09(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8]); break;
783 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;
784 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;
785 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;
786 default:
787 AssertReleaseMsgFailed(("cArgs=%d\n", pReq->u.Internal.cArgs));
788 rcRet = rcReq = VERR_INTERNAL_ERROR;
789 break;
790 }
791#else /* x86: */
792 size_t cbArgs = pReq->u.Internal.cArgs * sizeof(uintptr_t);
793# ifdef __GNUC__
794 __asm__ __volatile__("movl %%esp, %%edx\n\t"
795 "subl %2, %%esp\n\t"
796 "andl $0xfffffff0, %%esp\n\t"
797 "shrl $2, %2\n\t"
798 "movl %%esp, %%edi\n\t"
799 "rep movsl\n\t"
800 "movl %%edx, %%edi\n\t"
801 "call *%%eax\n\t"
802 "mov %%edi, %%esp\n\t"
803 : "=a" (rcRet),
804 "=S" (pauArgs),
805 "=c" (cbArgs)
806 : "0" (u.pfn),
807 "1" (pauArgs),
808 "2" (cbArgs)
809 : "edi", "edx");
810# else
811 __asm
812 {
813 xor edx, edx /* just mess it up. */
814 mov eax, u.pfn
815 mov ecx, cbArgs
816 shr ecx, 2
817 mov esi, pauArgs
818 mov ebx, esp
819 sub esp, cbArgs
820 and esp, 0xfffffff0
821 mov edi, esp
822 rep movsd
823 call eax
824 mov esp, ebx
825 mov rcRet, eax
826 }
827# endif
828#endif /* x86 */
829 if ((pReq->fFlags & (VMREQFLAGS_RETURN_MASK)) == VMREQFLAGS_VOID)
830 rcRet = VINF_SUCCESS;
831 rcReq = rcRet;
832 break;
833 }
834
835 default:
836 AssertMsgFailed(("pReq->enmType=%d\n", pReq->enmType));
837 rcReq = VERR_NOT_IMPLEMENTED;
838 break;
839 }
840
841 /*
842 * Complete the request.
843 */
844 pReq->iStatus = rcReq;
845 pReq->enmState = VMREQSTATE_COMPLETED;
846 if (pReq->fFlags & VMREQFLAGS_NO_WAIT)
847 {
848 /* Free the packet, nobody is waiting. */
849 LogFlow(("vmR3ReqProcessOne: Completed request %p: rcReq=%Vrc rcRet=%Vrc - freeing it\n",
850 pReq, rcReq, rcRet));
851 VMR3ReqFree(pReq);
852 }
853 else
854 {
855 /* Notify the waiter and him free up the packet. */
856 LogFlow(("vmR3ReqProcessOne: Completed request %p: rcReq=%Vrc rcRet=%Vrc - notifying waiting thread\n",
857 pReq, rcReq, rcRet));
858 ASMAtomicXchgSize(&pReq->fEventSemClear, false);
859 int rc2 = RTSemEventSignal(pReq->EventSem);
860 if (VBOX_FAILURE(rc2))
861 {
862 AssertRC(rc2);
863 rcRet = rc2;
864 }
865 }
866 return rcRet;
867}
868
869
870
871
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