VirtualBox

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

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

Started with VM request API changes.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette