VirtualBox

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

Last change on this file since 2776 was 23, checked in by vboxsync, 18 years ago

string.h & stdio.h + header cleanups.

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