VirtualBox

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

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

Return from VMR3ReqWait, if the request was killed in vmR3DestroyFinalBit.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 30.1 KB
Line 
1/* $Id: VMReq.cpp 4943 2007-09-21 07:53:12Z 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 && pReq->enmState != VMREQSTATE_INVALID);
625 }
626 if (VBOX_SUCCESS(rc))
627 ASMAtomicXchgSize(&pReq->fEventSemClear, true);
628 if (pReq->enmState == VMREQSTATE_COMPLETED)
629 rc = VINF_SUCCESS;
630 LogFlow(("VMR3ReqWait: returns %Vrc\n", rc));
631 Assert(rc != VERR_INTERRUPTED);
632 return rc;
633}
634
635
636/**
637 * Process pending request(s).
638 *
639 * This function is called from a forced action handler in
640 * the EMT.
641 *
642 * @returns VBox status code.
643 *
644 * @param pVM VM handle.
645 */
646VMR3DECL(int) VMR3ReqProcess(PVM pVM)
647{
648 LogFlow(("VMR3ReqProcess: (enmVMState=%d)\n", pVM->enmVMState));
649
650 /*
651 * Process loop.
652 *
653 * We do not repeat the outer loop if we've got an informationtional status code
654 * since that code needs processing by our caller.
655 */
656 int rc = VINF_SUCCESS;
657 while (rc <= VINF_SUCCESS)
658 {
659 /*
660 * Get pending requests.
661 */
662 VM_FF_CLEAR(pVM, VM_FF_REQUEST);
663 PVMREQ pReqs = (PVMREQ)ASMAtomicXchgPtr((void * volatile *)&pVM->vm.s.pReqs, NULL);
664 if (!pReqs)
665 break;
666
667 /*
668 * Reverse the list to process it in FIFO order.
669 */
670 PVMREQ pReq = pReqs;
671 if (pReq->pNext)
672 Log2(("VMR3ReqProcess: 2+ requests: %p %p %p\n", pReq, pReq->pNext, pReq->pNext->pNext));
673 pReqs = NULL;
674 while (pReq)
675 {
676 Assert(pReq->enmState == VMREQSTATE_QUEUED);
677 Assert(pReq->pVM == pVM);
678 PVMREQ pCur = pReq;
679 pReq = pReq->pNext;
680 pCur->pNext = pReqs;
681 pReqs = pCur;
682 }
683
684
685 /*
686 * Process the requests.
687 *
688 * Since this is a FF worker certain rules applies to the
689 * status codes. See the EM section in VBox/err.h and EM.cpp for details.
690 */
691 while (pReqs)
692 {
693 /* Unchain the first request and advance the list. */
694 pReq = pReqs;
695 pReqs = pReqs->pNext;
696 pReq->pNext = NULL;
697
698 /* Process the request */
699 int rc2 = vmR3ReqProcessOne(pVM, pReq);
700
701 /*
702 * The status code handling extremely important yet very fragile. Should probably
703 * look for a better way of communicating status changes to EM...
704 */
705 if ( rc2 >= VINF_EM_FIRST
706 && rc2 <= VINF_EM_LAST
707 && ( rc == VINF_SUCCESS
708 || rc2 < rc) )
709 rc = rc2;
710 }
711 }
712
713 LogFlow(("VMR3ReqProcess: returns %Vrc (enmVMState=%d)\n", rc, pVM->enmVMState));
714 return rc;
715}
716
717
718/**
719 * Process one request.
720 *
721 * @returns VBox status code.
722 *
723 * @param pVM VM handle.
724 * @param pReq Request packet to process.
725 */
726static int vmR3ReqProcessOne(PVM pVM, PVMREQ pReq)
727{
728 LogFlow(("vmR3ReqProcessOne: pReq=%p type=%d fFlags=%#x\n", pReq, pReq->enmType, pReq->fFlags));
729
730 /*
731 * Process the request.
732 */
733 Assert(pReq->enmState == VMREQSTATE_QUEUED);
734 pReq->enmState = VMREQSTATE_PROCESSING;
735 int rcRet = VINF_SUCCESS; /* the return code of this function. */
736 int rcReq = VERR_NOT_IMPLEMENTED; /* the request status. */
737 switch (pReq->enmType)
738 {
739 /*
740 * A packed down call frame.
741 */
742 case VMREQTYPE_INTERNAL:
743 {
744 uintptr_t *pauArgs = &pReq->u.Internal.aArgs[0];
745 union
746 {
747 PFNRT pfn;
748 DECLCALLBACKMEMBER(int, pfn00)(void);
749 DECLCALLBACKMEMBER(int, pfn01)(uintptr_t);
750 DECLCALLBACKMEMBER(int, pfn02)(uintptr_t, uintptr_t);
751 DECLCALLBACKMEMBER(int, pfn03)(uintptr_t, uintptr_t, uintptr_t);
752 DECLCALLBACKMEMBER(int, pfn04)(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
753 DECLCALLBACKMEMBER(int, pfn05)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
754 DECLCALLBACKMEMBER(int, pfn06)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
755 DECLCALLBACKMEMBER(int, pfn07)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
756 DECLCALLBACKMEMBER(int, pfn08)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
757 DECLCALLBACKMEMBER(int, pfn09)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
758 DECLCALLBACKMEMBER(int, pfn10)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
759 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);
760 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);
761 } u;
762 u.pfn = pReq->u.Internal.pfn;
763#ifdef RT_ARCH_AMD64
764 switch (pReq->u.Internal.cArgs)
765 {
766 case 0: rcRet = u.pfn00(); break;
767 case 1: rcRet = u.pfn01(pauArgs[0]); break;
768 case 2: rcRet = u.pfn02(pauArgs[0], pauArgs[1]); break;
769 case 3: rcRet = u.pfn03(pauArgs[0], pauArgs[1], pauArgs[2]); break;
770 case 4: rcRet = u.pfn04(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3]); break;
771 case 5: rcRet = u.pfn05(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4]); break;
772 case 6: rcRet = u.pfn06(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5]); break;
773 case 7: rcRet = u.pfn07(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6]); break;
774 case 8: rcRet = u.pfn08(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7]); break;
775 case 9: rcRet = u.pfn09(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8]); break;
776 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;
777 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;
778 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;
779 default:
780 AssertReleaseMsgFailed(("cArgs=%d\n", pReq->u.Internal.cArgs));
781 rcRet = rcReq = VERR_INTERNAL_ERROR;
782 break;
783 }
784#else /* x86: */
785 size_t cbArgs = pReq->u.Internal.cArgs * sizeof(uintptr_t);
786# ifdef __GNUC__
787 __asm__ __volatile__("movl %%esp, %%edx\n\t"
788 "subl %2, %%esp\n\t"
789 "andl $0xfffffff0, %%esp\n\t"
790 "shrl $2, %2\n\t"
791 "movl %%esp, %%edi\n\t"
792 "rep movsl\n\t"
793 "movl %%edx, %%edi\n\t"
794 "call *%%eax\n\t"
795 "mov %%edi, %%esp\n\t"
796 : "=a" (rcRet),
797 "=S" (pauArgs),
798 "=c" (cbArgs)
799 : "0" (u.pfn),
800 "1" (pauArgs),
801 "2" (cbArgs)
802 : "edi", "edx");
803# else
804 __asm
805 {
806 xor edx, edx /* just mess it up. */
807 mov eax, u.pfn
808 mov ecx, cbArgs
809 shr ecx, 2
810 mov esi, pauArgs
811 mov ebx, esp
812 sub esp, cbArgs
813 and esp, 0xfffffff0
814 mov edi, esp
815 rep movsd
816 call eax
817 mov esp, ebx
818 mov rcRet, eax
819 }
820# endif
821#endif /* x86 */
822 if ((pReq->fFlags & (VMREQFLAGS_RETURN_MASK)) == VMREQFLAGS_VOID)
823 rcRet = VINF_SUCCESS;
824 rcReq = rcRet;
825 break;
826 }
827
828 default:
829 AssertMsgFailed(("pReq->enmType=%d\n", pReq->enmType));
830 rcReq = VERR_NOT_IMPLEMENTED;
831 break;
832 }
833
834 /*
835 * Complete the request.
836 */
837 pReq->iStatus = rcReq;
838 pReq->enmState = VMREQSTATE_COMPLETED;
839 if (pReq->fFlags & VMREQFLAGS_NO_WAIT)
840 {
841 /* Free the packet, nobody is waiting. */
842 LogFlow(("vmR3ReqProcessOne: Completed request %p: rcReq=%Vrc rcRet=%Vrc - freeing it\n",
843 pReq, rcReq, rcRet));
844 VMR3ReqFree(pReq);
845 }
846 else
847 {
848 /* Notify the waiter and him free up the packet. */
849 LogFlow(("vmR3ReqProcessOne: Completed request %p: rcReq=%Vrc rcRet=%Vrc - notifying waiting thread\n",
850 pReq, rcReq, rcRet));
851 ASMAtomicXchgSize(&pReq->fEventSemClear, false);
852 int rc2 = RTSemEventSignal(pReq->EventSem);
853 if (VBOX_FAILURE(rc2))
854 {
855 AssertRC(rc2);
856 rcRet = rc2;
857 }
858 }
859 return rcRet;
860}
861
862
863
864
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