VirtualBox

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

Last change on this file since 13782 was 13782, checked in by vboxsync, 16 years ago

More SMP groundwork.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 39.0 KB
Line 
1/* $Id: VMReq.cpp 13782 2008-11-04 12:16:30Z 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.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 * 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 AssertMsgReturn(enmDest == VMREQDEST_ANY || enmDest == VMREQDEST_BROADCAST || (unsigned)enmDest < pUVM->pVM->cCPUs, ("Invalid destination %d (max=%d)\n", enmDest, pUVM->pVM->cCPUs), VERR_INVALID_PARAMETER);
404
405 /*
406 * Try get a recycled packet.
407 * While this could all be solved with a single list with a lock, it's a sport
408 * of mine to avoid locks.
409 */
410 int cTries = RT_ELEMENTS(pUVM->vm.s.apReqFree) * 2;
411 while (--cTries >= 0)
412 {
413 PVMREQ volatile *ppHead = &pUVM->vm.s.apReqFree[ASMAtomicIncU32(&pUVM->vm.s.iReqFree) % RT_ELEMENTS(pUVM->vm.s.apReqFree)];
414#if 0 /* sad, but this won't work safely because the reading of pReq->pNext. */
415 PVMREQ pNext = NULL;
416 PVMREQ pReq = *ppHead;
417 if ( pReq
418 && !ASMAtomicCmpXchgPtr((void * volatile *)ppHead, (pNext = pReq->pNext), pReq)
419 && (pReq = *ppHead)
420 && !ASMAtomicCmpXchgPtr((void * volatile *)ppHead, (pNext = pReq->pNext), pReq))
421 pReq = NULL;
422 if (pReq)
423 {
424 Assert(pReq->pNext == pNext); NOREF(pReq);
425#else
426 PVMREQ pReq = (PVMREQ)ASMAtomicXchgPtr((void * volatile *)ppHead, NULL);
427 if (pReq)
428 {
429 PVMREQ pNext = pReq->pNext;
430 if ( pNext
431 && !ASMAtomicCmpXchgPtr((void * volatile *)ppHead, pNext, NULL))
432 {
433 STAM_COUNTER_INC(&pUVM->vm.s.StatReqAllocRaces);
434 vmr3ReqJoinFree(&pUVM->vm.s, pReq->pNext);
435 }
436#endif
437 ASMAtomicDecU32(&pUVM->vm.s.cReqFree);
438
439 /*
440 * Make sure the event sem is not signaled.
441 */
442 if (!pReq->fEventSemClear)
443 {
444 int rc = RTSemEventWait(pReq->EventSem, 0);
445 if (rc != VINF_SUCCESS && rc != VERR_TIMEOUT)
446 {
447 /*
448 * This shall not happen, but if it does we'll just destroy
449 * the semaphore and create a new one.
450 */
451 AssertMsgFailed(("rc=%Vrc from RTSemEventWait(%#x).\n", rc, pReq->EventSem));
452 RTSemEventDestroy(pReq->EventSem);
453 rc = RTSemEventCreate(&pReq->EventSem);
454 AssertRC(rc);
455 if (VBOX_FAILURE(rc))
456 return rc;
457 }
458 pReq->fEventSemClear = true;
459 }
460 else
461 Assert(RTSemEventWait(pReq->EventSem, 0) == VERR_TIMEOUT);
462
463 /*
464 * Initialize the packet and return it.
465 */
466 Assert(pReq->enmType == VMREQTYPE_INVALID);
467 Assert(pReq->enmState == VMREQSTATE_FREE);
468 Assert(pReq->pUVM == pUVM);
469 ASMAtomicXchgSize(&pReq->pNext, NULL);
470 pReq->enmState = VMREQSTATE_ALLOCATED;
471 pReq->iStatus = VERR_VM_REQUEST_STATUS_STILL_PENDING;
472 pReq->fFlags = VMREQFLAGS_VBOX_STATUS;
473 pReq->enmType = enmType;
474 pReq->enmDest = enmDest;
475
476 *ppReq = pReq;
477 STAM_COUNTER_INC(&pUVM->vm.s.StatReqAllocRecycled);
478 LogFlow(("VMR3ReqAlloc: returns VINF_SUCCESS *ppReq=%p recycled\n", pReq));
479 return VINF_SUCCESS;
480 }
481 }
482
483 /*
484 * Ok allocate one.
485 */
486 PVMREQ pReq = (PVMREQ)MMR3HeapAllocU(pUVM, MM_TAG_VM_REQ, sizeof(*pReq));
487 if (!pReq)
488 return VERR_NO_MEMORY;
489
490 /*
491 * Create the semaphore.
492 */
493 int rc = RTSemEventCreate(&pReq->EventSem);
494 AssertRC(rc);
495 if (VBOX_FAILURE(rc))
496 {
497 MMR3HeapFree(pReq);
498 return rc;
499 }
500
501 /*
502 * Initialize the packet and return it.
503 */
504 pReq->pNext = NULL;
505 pReq->pUVM = pUVM;
506 pReq->enmState = VMREQSTATE_ALLOCATED;
507 pReq->iStatus = VERR_VM_REQUEST_STATUS_STILL_PENDING;
508 pReq->fEventSemClear = true;
509 pReq->fFlags = VMREQFLAGS_VBOX_STATUS;
510 pReq->enmType = enmType;
511 pReq->enmDest = enmDest;
512
513 *ppReq = pReq;
514 STAM_COUNTER_INC(&pUVM->vm.s.StatReqAllocNew);
515 LogFlow(("VMR3ReqAlloc: returns VINF_SUCCESS *ppReq=%p new\n", pReq));
516 return VINF_SUCCESS;
517}
518
519
520/**
521 * Free a request packet.
522 *
523 * @returns VBox status code.
524 *
525 * @param pReq Package to free.
526 * @remark The request packet must be in allocated or completed state!
527 */
528VMMR3DECL(int) VMR3ReqFree(PVMREQ pReq)
529{
530 /*
531 * Ignore NULL (all free functions should do this imho).
532 */
533 if (!pReq)
534 return VINF_SUCCESS;
535
536 /*
537 * Check packet state.
538 */
539 switch (pReq->enmState)
540 {
541 case VMREQSTATE_ALLOCATED:
542 case VMREQSTATE_COMPLETED:
543 break;
544 default:
545 AssertMsgFailed(("Invalid state %d!\n", pReq->enmState));
546 return VERR_VM_REQUEST_STATE;
547 }
548
549 /*
550 * Make it a free packet and put it into one of the free packet lists.
551 */
552 pReq->enmState = VMREQSTATE_FREE;
553 pReq->iStatus = VERR_VM_REQUEST_STATUS_FREED;
554 pReq->enmType = VMREQTYPE_INVALID;
555
556 PUVM pUVM = pReq->pUVM;
557 STAM_COUNTER_INC(&pUVM->vm.s.StatReqFree);
558
559 if (pUVM->vm.s.cReqFree < 128)
560 {
561 ASMAtomicIncU32(&pUVM->vm.s.cReqFree);
562 PVMREQ volatile *ppHead = &pUVM->vm.s.apReqFree[ASMAtomicIncU32(&pUVM->vm.s.iReqFree) % RT_ELEMENTS(pUVM->vm.s.apReqFree)];
563 PVMREQ pNext;
564 do
565 {
566 pNext = *ppHead;
567 ASMAtomicXchgPtr((void * volatile *)&pReq->pNext, pNext);
568 } while (!ASMAtomicCmpXchgPtr((void * volatile *)ppHead, (void *)pReq, (void *)pNext));
569 }
570 else
571 {
572 STAM_COUNTER_INC(&pReq->pUVM->vm.s.StatReqFreeOverflow);
573 RTSemEventDestroy(pReq->EventSem);
574 MMR3HeapFree(pReq);
575 }
576 return VINF_SUCCESS;
577}
578
579
580/**
581 * Queue a request.
582 *
583 * The quest must be allocated using VMR3ReqAlloc() and contain
584 * all the required data.
585 * If it's desired to poll on the completion of the request set cMillies
586 * to 0 and use VMR3ReqWait() to check for completation. In the other case
587 * use RT_INDEFINITE_WAIT.
588 *
589 * @returns VBox status code.
590 * Will not return VERR_INTERRUPTED.
591 * @returns VERR_TIMEOUT if cMillies was reached without the packet being completed.
592 *
593 * @param pReq The request to queue.
594 * @param cMillies Number of milliseconds to wait for the request to
595 * be completed. Use RT_INDEFINITE_WAIT to only
596 * wait till it's completed.
597 */
598VMMR3DECL(int) VMR3ReqQueue(PVMREQ pReq, unsigned cMillies)
599{
600 LogFlow(("VMR3ReqQueue: pReq=%p cMillies=%d\n", pReq, cMillies));
601 /*
602 * Verify the supplied package.
603 */
604 AssertMsgReturn(pReq->enmState == VMREQSTATE_ALLOCATED, ("%d\n", pReq->enmState), VERR_VM_REQUEST_STATE);
605 AssertMsgReturn( VALID_PTR(pReq->pUVM)
606 && !pReq->pNext
607 && pReq->EventSem != NIL_RTSEMEVENT,
608 ("Invalid request package! Anyone cooking their own packages???\n"),
609 VERR_VM_REQUEST_INVALID_PACKAGE);
610 AssertMsgReturn( pReq->enmType > VMREQTYPE_INVALID
611 && pReq->enmType < VMREQTYPE_MAX,
612 ("Invalid package type %d valid range %d-%d inclusivly. This was verified on alloc too...\n",
613 pReq->enmType, VMREQTYPE_INVALID + 1, VMREQTYPE_MAX - 1),
614 VERR_VM_REQUEST_INVALID_TYPE);
615
616 /*
617 * Are we the EMT or not?
618 * Also, store pVM (and fFlags) locally since pReq may be invalid after queuing it.
619 */
620 int rc = VINF_SUCCESS;
621 PUVM pUVM = ((VMREQ volatile *)pReq)->pUVM; /* volatile paranoia */
622 PUVMCPU pUVMCPU = (PUVMCPU)RTTlsGet(pUVM->vm.s.idxTLS);
623
624 if (pReq->enmDest == VMREQDEST_BROADCAST)
625 {
626 unsigned fFlags = ((VMREQ volatile *)pReq)->fFlags; /* volatile paranoia */
627
628 for (unsigned i=0;i<pUVM->pVM->cCPUs;i++)
629 {
630 if ( !pUVMCPU
631 || pUVMCPU->idCPU != i)
632 {
633 /*
634 * Insert it.
635 */
636 pReq->enmState = VMREQSTATE_QUEUED;
637 PVMREQ pNext;
638 do
639 {
640 pNext = pUVM->aCpu[i].vm.s.pReqs;
641 pReq->pNext = pNext;
642 } while (!ASMAtomicCmpXchgPtr((void * volatile *)&pUVM->aCpu[i].vm.s.pReqs, (void *)pReq, (void *)pNext));
643
644 /*
645 * Notify EMT.
646 */
647 if (pUVM->pVM)
648 VMCPU_FF_SET(pUVM->pVM, VM_FF_REQUEST, i);
649 /* @todo: VMR3NotifyFFU*/
650 AssertFailed();
651 VMR3NotifyFFU(pUVM, false);
652
653 /*
654 * Wait and return.
655 */
656 if (!(fFlags & VMREQFLAGS_NO_WAIT))
657 rc = VMR3ReqWait(pReq, cMillies);
658 LogFlow(("VMR3ReqQueue: returns %Vrc\n", rc));
659 }
660 else
661 {
662 /*
663 * The requester was EMT, just execute it.
664 */
665 pReq->enmState = VMREQSTATE_QUEUED;
666 rc = vmR3ReqProcessOneU(pUVM, pReq);
667 LogFlow(("VMR3ReqQueue: returns %Vrc (processed)\n", rc));
668 }
669 } /* for each VMCPU */
670 }
671 else
672 if ( pReq->enmDest != VMREQDEST_ANY /* for a specific VMCPU? */
673 && pUVMCPU->idCPU != (unsigned)pReq->enmDest)
674 {
675 RTCPUID idTarget = (RTCPUID)pReq->enmDest;
676 unsigned fFlags = ((VMREQ volatile *)pReq)->fFlags; /* volatile paranoia */
677
678 /*
679 * Insert it.
680 */
681 pReq->enmState = VMREQSTATE_QUEUED;
682 PVMREQ pNext;
683 do
684 {
685 pNext = pUVM->aCpu[idTarget].vm.s.pReqs;
686 pReq->pNext = pNext;
687 } while (!ASMAtomicCmpXchgPtr((void * volatile *)&pUVM->aCpu[idTarget].vm.s.pReqs, (void *)pReq, (void *)pNext));
688
689 /*
690 * Notify EMT.
691 */
692 if (pUVM->pVM)
693 VMCPU_FF_SET(pUVM->pVM, VM_FF_REQUEST, idTarget);
694 /* @todo: VMR3NotifyFFU*/
695 AssertFailed();
696 VMR3NotifyFFU(pUVM, false);
697
698 /*
699 * Wait and return.
700 */
701 if (!(fFlags & VMREQFLAGS_NO_WAIT))
702 rc = VMR3ReqWait(pReq, cMillies);
703 LogFlow(("VMR3ReqQueue: returns %Vrc\n", rc));
704 }
705 else
706 if ( pReq->enmDest == VMREQDEST_ANY
707 && !pUVMCPU /* only EMT threads have a valid pointer stored in the TLS slot. */)
708 {
709 unsigned fFlags = ((VMREQ volatile *)pReq)->fFlags; /* volatile paranoia */
710
711 /*
712 * Insert it.
713 */
714 pReq->enmState = VMREQSTATE_QUEUED;
715 PVMREQ pNext;
716 do
717 {
718 pNext = pUVM->vm.s.pReqs;
719 pReq->pNext = pNext;
720 } while (!ASMAtomicCmpXchgPtr((void * volatile *)&pUVM->vm.s.pReqs, (void *)pReq, (void *)pNext));
721
722 /*
723 * Notify EMT.
724 */
725 if (pUVM->pVM)
726 VM_FF_SET(pUVM->pVM, VM_FF_REQUEST);
727 VMR3NotifyFFU(pUVM, false);
728
729 /*
730 * Wait and return.
731 */
732 if (!(fFlags & VMREQFLAGS_NO_WAIT))
733 rc = VMR3ReqWait(pReq, cMillies);
734 LogFlow(("VMR3ReqQueue: returns %Vrc\n", rc));
735 }
736 else
737 {
738 Assert(pUVMCPU);
739
740 /*
741 * The requester was EMT, just execute it.
742 */
743 pReq->enmState = VMREQSTATE_QUEUED;
744 rc = vmR3ReqProcessOneU(pUVM, pReq);
745 LogFlow(("VMR3ReqQueue: returns %Vrc (processed)\n", rc));
746 }
747 return rc;
748}
749
750
751/**
752 * Wait for a request to be completed.
753 *
754 * @returns VBox status code.
755 * @returns VERR_TIMEOUT if cMillies was reached without the packet being completed.
756 *
757 * @param pReq The request to wait for.
758 * @param cMillies Number of milliseconds to wait.
759 * Use RT_INDEFINITE_WAIT to only wait till it's completed.
760 */
761VMMR3DECL(int) VMR3ReqWait(PVMREQ pReq, unsigned cMillies)
762{
763 LogFlow(("VMR3ReqWait: pReq=%p cMillies=%d\n", pReq, cMillies));
764
765 /*
766 * Verify the supplied package.
767 */
768 AssertMsgReturn( pReq->enmState == VMREQSTATE_QUEUED
769 || pReq->enmState == VMREQSTATE_PROCESSING
770 || pReq->enmState == VMREQSTATE_COMPLETED,
771 ("Invalid state %d\n", pReq->enmState),
772 VERR_VM_REQUEST_STATE);
773 AssertMsgReturn( VALID_PTR(pReq->pUVM)
774 && pReq->EventSem != NIL_RTSEMEVENT,
775 ("Invalid request package! Anyone cooking their own packages???\n"),
776 VERR_VM_REQUEST_INVALID_PACKAGE);
777 AssertMsgReturn( pReq->enmType > VMREQTYPE_INVALID
778 && pReq->enmType < VMREQTYPE_MAX,
779 ("Invalid package type %d valid range %d-%d inclusivly. This was verified on alloc too...\n",
780 pReq->enmType, VMREQTYPE_INVALID + 1, VMREQTYPE_MAX - 1),
781 VERR_VM_REQUEST_INVALID_TYPE);
782
783 /*
784 * Check for deadlock condition
785 */
786 PUVM pUVM = pReq->pUVM;
787 NOREF(pUVM);
788 AssertMsg(!pUVM->pVM || !VMMR3LockIsOwner(pUVM->pVM),
789 ("Waiting for EMT to process a request, but we own the global VM lock!?!?!?!\n"));
790
791 /*
792 * Wait on the package.
793 */
794 int rc;
795 if (cMillies != RT_INDEFINITE_WAIT)
796 rc = RTSemEventWait(pReq->EventSem, cMillies);
797 else
798 {
799 do
800 {
801 rc = RTSemEventWait(pReq->EventSem, RT_INDEFINITE_WAIT);
802 Assert(rc != VERR_TIMEOUT);
803 } while ( pReq->enmState != VMREQSTATE_COMPLETED
804 && pReq->enmState != VMREQSTATE_INVALID);
805 }
806 if (VBOX_SUCCESS(rc))
807 ASMAtomicXchgSize(&pReq->fEventSemClear, true);
808 if (pReq->enmState == VMREQSTATE_COMPLETED)
809 rc = VINF_SUCCESS;
810 LogFlow(("VMR3ReqWait: returns %Vrc\n", rc));
811 Assert(rc != VERR_INTERRUPTED);
812 return rc;
813}
814
815
816/**
817 * Process pending request(s).
818 *
819 * This function is called from a forced action handler in the EMT
820 * or from one of the EMT loops.
821 *
822 * @returns VBox status code.
823 *
824 * @param pUVM Pointer to the user mode VM structure.
825 * @param enmDest Destination of the request packet (global or per VCPU).
826 */
827VMMR3DECL(int) VMR3ReqProcessU(PUVM pUVM, VMREQDEST enmDest)
828{
829 LogFlow(("VMR3ReqProcessU: (enmVMState=%d) enmDest=%d\n", pUVM->pVM ? pUVM->pVM->enmVMState : VMSTATE_CREATING, enmDest));
830
831 /*
832 * Process loop.
833 *
834 * We do not repeat the outer loop if we've got an informationtional status code
835 * since that code needs processing by our caller.
836 */
837 int rc = VINF_SUCCESS;
838 while (rc <= VINF_SUCCESS)
839 {
840 void *volatile *ppReqs;
841
842 /*
843 * Get pending requests.
844 */
845 if (enmDest == VMREQDEST_ANY)
846 {
847 ppReqs = (void * volatile *)&pUVM->vm.s.pReqs;
848 if (RT_LIKELY(pUVM->pVM))
849 VM_FF_CLEAR(pUVM->pVM, VM_FF_REQUEST);
850 }
851 else
852 {
853 ppReqs = (void * volatile *)&pUVM->aCpu[enmDest].vm.s.pReqs;
854 if (RT_LIKELY(pUVM->pVM))
855 VMCPU_FF_CLEAR(pUVM->pVM, enmDest, VM_FF_REQUEST);
856 }
857
858 PVMREQ pReqs = (PVMREQ)ASMAtomicXchgPtr(ppReqs, NULL);
859 if (!pReqs)
860 break;
861
862 /*
863 * Reverse the list to process it in FIFO order.
864 */
865 PVMREQ pReq = pReqs;
866 if (pReq->pNext)
867 Log2(("VMR3ReqProcess: 2+ requests: %p %p %p\n", pReq, pReq->pNext, pReq->pNext->pNext));
868 pReqs = NULL;
869 while (pReq)
870 {
871 Assert(pReq->enmState == VMREQSTATE_QUEUED);
872 Assert(pReq->pUVM == pUVM);
873 PVMREQ pCur = pReq;
874 pReq = pReq->pNext;
875 pCur->pNext = pReqs;
876 pReqs = pCur;
877 }
878
879
880 /*
881 * Process the requests.
882 *
883 * Since this is a FF worker certain rules applies to the
884 * status codes. See the EM section in VBox/err.h and EM.cpp for details.
885 */
886 while (pReqs)
887 {
888 /* Unchain the first request and advance the list. */
889 pReq = pReqs;
890 pReqs = pReqs->pNext;
891 pReq->pNext = NULL;
892
893 /* Process the request */
894 int rc2 = vmR3ReqProcessOneU(pUVM, pReq);
895
896 /*
897 * The status code handling extremely important yet very fragile. Should probably
898 * look for a better way of communicating status changes to EM...
899 */
900 if ( rc2 >= VINF_EM_FIRST
901 && rc2 <= VINF_EM_LAST
902 && ( rc == VINF_SUCCESS
903 || rc2 < rc) )
904 rc = rc2;
905 }
906 }
907
908 LogFlow(("VMR3ReqProcess: returns %Vrc (enmVMState=%d)\n", rc, pUVM->pVM ? pUVM->pVM->enmVMState : VMSTATE_CREATING));
909 return rc;
910}
911
912
913/**
914 * Process one request.
915 *
916 * @returns VBox status code.
917 *
918 * @param pVM VM handle.
919 * @param pReq Request packet to process.
920 */
921static int vmR3ReqProcessOneU(PUVM pUVM, PVMREQ pReq)
922{
923 LogFlow(("vmR3ReqProcessOne: pReq=%p type=%d fFlags=%#x\n", pReq, pReq->enmType, pReq->fFlags));
924
925 /*
926 * Process the request.
927 */
928 Assert(pReq->enmState == VMREQSTATE_QUEUED);
929 pReq->enmState = VMREQSTATE_PROCESSING;
930 int rcRet = VINF_SUCCESS; /* the return code of this function. */
931 int rcReq = VERR_NOT_IMPLEMENTED; /* the request status. */
932 switch (pReq->enmType)
933 {
934 /*
935 * A packed down call frame.
936 */
937 case VMREQTYPE_INTERNAL:
938 {
939 uintptr_t *pauArgs = &pReq->u.Internal.aArgs[0];
940 union
941 {
942 PFNRT pfn;
943 DECLCALLBACKMEMBER(int, pfn00)(void);
944 DECLCALLBACKMEMBER(int, pfn01)(uintptr_t);
945 DECLCALLBACKMEMBER(int, pfn02)(uintptr_t, uintptr_t);
946 DECLCALLBACKMEMBER(int, pfn03)(uintptr_t, uintptr_t, uintptr_t);
947 DECLCALLBACKMEMBER(int, pfn04)(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
948 DECLCALLBACKMEMBER(int, pfn05)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
949 DECLCALLBACKMEMBER(int, pfn06)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
950 DECLCALLBACKMEMBER(int, pfn07)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
951 DECLCALLBACKMEMBER(int, pfn08)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
952 DECLCALLBACKMEMBER(int, pfn09)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
953 DECLCALLBACKMEMBER(int, pfn10)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
954 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);
955 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);
956 } u;
957 u.pfn = pReq->u.Internal.pfn;
958#ifdef RT_ARCH_AMD64
959 switch (pReq->u.Internal.cArgs)
960 {
961 case 0: rcRet = u.pfn00(); break;
962 case 1: rcRet = u.pfn01(pauArgs[0]); break;
963 case 2: rcRet = u.pfn02(pauArgs[0], pauArgs[1]); break;
964 case 3: rcRet = u.pfn03(pauArgs[0], pauArgs[1], pauArgs[2]); break;
965 case 4: rcRet = u.pfn04(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3]); break;
966 case 5: rcRet = u.pfn05(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4]); break;
967 case 6: rcRet = u.pfn06(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5]); break;
968 case 7: rcRet = u.pfn07(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6]); break;
969 case 8: rcRet = u.pfn08(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7]); break;
970 case 9: rcRet = u.pfn09(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8]); break;
971 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;
972 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;
973 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;
974 default:
975 AssertReleaseMsgFailed(("cArgs=%d\n", pReq->u.Internal.cArgs));
976 rcRet = rcReq = VERR_INTERNAL_ERROR;
977 break;
978 }
979#else /* x86: */
980 size_t cbArgs = pReq->u.Internal.cArgs * sizeof(uintptr_t);
981# ifdef __GNUC__
982 __asm__ __volatile__("movl %%esp, %%edx\n\t"
983 "subl %2, %%esp\n\t"
984 "andl $0xfffffff0, %%esp\n\t"
985 "shrl $2, %2\n\t"
986 "movl %%esp, %%edi\n\t"
987 "rep movsl\n\t"
988 "movl %%edx, %%edi\n\t"
989 "call *%%eax\n\t"
990 "mov %%edi, %%esp\n\t"
991 : "=a" (rcRet),
992 "=S" (pauArgs),
993 "=c" (cbArgs)
994 : "0" (u.pfn),
995 "1" (pauArgs),
996 "2" (cbArgs)
997 : "edi", "edx");
998# else
999 __asm
1000 {
1001 xor edx, edx /* just mess it up. */
1002 mov eax, u.pfn
1003 mov ecx, cbArgs
1004 shr ecx, 2
1005 mov esi, pauArgs
1006 mov ebx, esp
1007 sub esp, cbArgs
1008 and esp, 0xfffffff0
1009 mov edi, esp
1010 rep movsd
1011 call eax
1012 mov esp, ebx
1013 mov rcRet, eax
1014 }
1015# endif
1016#endif /* x86 */
1017 if ((pReq->fFlags & (VMREQFLAGS_RETURN_MASK)) == VMREQFLAGS_VOID)
1018 rcRet = VINF_SUCCESS;
1019 rcReq = rcRet;
1020 break;
1021 }
1022
1023 default:
1024 AssertMsgFailed(("pReq->enmType=%d\n", pReq->enmType));
1025 rcReq = VERR_NOT_IMPLEMENTED;
1026 break;
1027 }
1028
1029 /*
1030 * Complete the request.
1031 */
1032 pReq->iStatus = rcReq;
1033 pReq->enmState = VMREQSTATE_COMPLETED;
1034 if (pReq->fFlags & VMREQFLAGS_NO_WAIT)
1035 {
1036 /* Free the packet, nobody is waiting. */
1037 LogFlow(("vmR3ReqProcessOne: Completed request %p: rcReq=%Vrc rcRet=%Vrc - freeing it\n",
1038 pReq, rcReq, rcRet));
1039 VMR3ReqFree(pReq);
1040 }
1041 else
1042 {
1043 /* Notify the waiter and him free up the packet. */
1044 LogFlow(("vmR3ReqProcessOne: Completed request %p: rcReq=%Vrc rcRet=%Vrc - notifying waiting thread\n",
1045 pReq, rcReq, rcRet));
1046 ASMAtomicXchgSize(&pReq->fEventSemClear, false);
1047 int rc2 = RTSemEventSignal(pReq->EventSem);
1048 if (VBOX_FAILURE(rc2))
1049 {
1050 AssertRC(rc2);
1051 rcRet = rc2;
1052 }
1053 }
1054 return rcRet;
1055}
1056
1057
1058
1059
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