VirtualBox

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

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

Pass the VMCPU id to the ring 0 callbacks.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 39.0 KB
Line 
1/* $Id: VMReq.cpp 13858 2008-11-05 13:45:41Z 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 (RT_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 ( RT_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 %Rrc *ppReq=%p\n", rc, pReq));
300 }
301 else
302 LogFlow(("VMR3ReqCallV: returns %Rrc\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=%Rrc from RTSemEventWait(%#x).\n", rc, pReq->EventSem));
452 RTSemEventDestroy(pReq->EventSem);
453 rc = RTSemEventCreate(&pReq->EventSem);
454 AssertRC(rc);
455 if (RT_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 (RT_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->aCpus[i].vm.s.pReqs;
641 pReq->pNext = pNext;
642 } while (!ASMAtomicCmpXchgPtr((void * volatile *)&pUVM->aCpus[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 %Rrc\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 %Rrc (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->aCpus[idTarget].vm.s.pReqs;
686 pReq->pNext = pNext;
687 } while (!ASMAtomicCmpXchgPtr((void * volatile *)&pUVM->aCpus[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 %Rrc\n", rc));
704 }
705 else if ( pReq->enmDest == VMREQDEST_ANY
706 && !pUVMCPU /* only EMT threads have a valid pointer stored in the TLS slot. */)
707 {
708 unsigned fFlags = ((VMREQ volatile *)pReq)->fFlags; /* volatile paranoia */
709
710 /*
711 * Insert it.
712 */
713 pReq->enmState = VMREQSTATE_QUEUED;
714 PVMREQ pNext;
715 do
716 {
717 pNext = pUVM->vm.s.pReqs;
718 pReq->pNext = pNext;
719 } while (!ASMAtomicCmpXchgPtr((void * volatile *)&pUVM->vm.s.pReqs, (void *)pReq, (void *)pNext));
720
721 /*
722 * Notify EMT.
723 */
724 if (pUVM->pVM)
725 VM_FF_SET(pUVM->pVM, VM_FF_REQUEST);
726 VMR3NotifyFFU(pUVM, false);
727
728 /*
729 * Wait and return.
730 */
731 if (!(fFlags & VMREQFLAGS_NO_WAIT))
732 rc = VMR3ReqWait(pReq, cMillies);
733 LogFlow(("VMR3ReqQueue: returns %Rrc\n", rc));
734 }
735 else
736 {
737 Assert(pUVMCPU);
738
739 /*
740 * The requester was EMT, just execute it.
741 */
742 pReq->enmState = VMREQSTATE_QUEUED;
743 rc = vmR3ReqProcessOneU(pUVM, pReq);
744 LogFlow(("VMR3ReqQueue: returns %Rrc (processed)\n", rc));
745 }
746 return rc;
747}
748
749
750/**
751 * Wait for a request to be completed.
752 *
753 * @returns VBox status code.
754 * @returns VERR_TIMEOUT if cMillies was reached without the packet being completed.
755 *
756 * @param pReq The request to wait for.
757 * @param cMillies Number of milliseconds to wait.
758 * Use RT_INDEFINITE_WAIT to only wait till it's completed.
759 */
760VMMR3DECL(int) VMR3ReqWait(PVMREQ pReq, unsigned cMillies)
761{
762 LogFlow(("VMR3ReqWait: pReq=%p cMillies=%d\n", pReq, cMillies));
763
764 /*
765 * Verify the supplied package.
766 */
767 AssertMsgReturn( pReq->enmState == VMREQSTATE_QUEUED
768 || pReq->enmState == VMREQSTATE_PROCESSING
769 || pReq->enmState == VMREQSTATE_COMPLETED,
770 ("Invalid state %d\n", pReq->enmState),
771 VERR_VM_REQUEST_STATE);
772 AssertMsgReturn( VALID_PTR(pReq->pUVM)
773 && pReq->EventSem != NIL_RTSEMEVENT,
774 ("Invalid request package! Anyone cooking their own packages???\n"),
775 VERR_VM_REQUEST_INVALID_PACKAGE);
776 AssertMsgReturn( pReq->enmType > VMREQTYPE_INVALID
777 && pReq->enmType < VMREQTYPE_MAX,
778 ("Invalid package type %d valid range %d-%d inclusivly. This was verified on alloc too...\n",
779 pReq->enmType, VMREQTYPE_INVALID + 1, VMREQTYPE_MAX - 1),
780 VERR_VM_REQUEST_INVALID_TYPE);
781
782 /*
783 * Check for deadlock condition
784 */
785 PUVM pUVM = pReq->pUVM;
786 NOREF(pUVM);
787 AssertMsg(!pUVM->pVM || !VMMR3LockIsOwner(pUVM->pVM),
788 ("Waiting for EMT to process a request, but we own the global VM lock!?!?!?!\n"));
789
790 /*
791 * Wait on the package.
792 */
793 int rc;
794 if (cMillies != RT_INDEFINITE_WAIT)
795 rc = RTSemEventWait(pReq->EventSem, cMillies);
796 else
797 {
798 do
799 {
800 rc = RTSemEventWait(pReq->EventSem, RT_INDEFINITE_WAIT);
801 Assert(rc != VERR_TIMEOUT);
802 } while ( pReq->enmState != VMREQSTATE_COMPLETED
803 && pReq->enmState != VMREQSTATE_INVALID);
804 }
805 if (RT_SUCCESS(rc))
806 ASMAtomicXchgSize(&pReq->fEventSemClear, true);
807 if (pReq->enmState == VMREQSTATE_COMPLETED)
808 rc = VINF_SUCCESS;
809 LogFlow(("VMR3ReqWait: returns %Rrc\n", rc));
810 Assert(rc != VERR_INTERRUPTED);
811 return rc;
812}
813
814
815/**
816 * Process pending request(s).
817 *
818 * This function is called from a forced action handler in the EMT
819 * or from one of the EMT loops.
820 *
821 * @returns VBox status code.
822 *
823 * @param pUVM Pointer to the user mode VM structure.
824 * @param enmDest Destination of the request packet (global or per VCPU).
825 */
826VMMR3DECL(int) VMR3ReqProcessU(PUVM pUVM, VMREQDEST enmDest)
827{
828 LogFlow(("VMR3ReqProcessU: (enmVMState=%d) enmDest=%d\n", pUVM->pVM ? pUVM->pVM->enmVMState : VMSTATE_CREATING, enmDest));
829
830 /*
831 * Process loop.
832 *
833 * We do not repeat the outer loop if we've got an informationtional status code
834 * since that code needs processing by our caller.
835 */
836 int rc = VINF_SUCCESS;
837 while (rc <= VINF_SUCCESS)
838 {
839 /*
840 * Get pending requests.
841 */
842 void *volatile *ppReqs;
843 if (enmDest == VMREQDEST_ANY)
844 {
845 ppReqs = (void * volatile *)&pUVM->vm.s.pReqs;
846 if (RT_LIKELY(pUVM->pVM))
847 VM_FF_CLEAR(pUVM->pVM, VM_FF_REQUEST);
848 }
849 else
850 {
851 ppReqs = (void * volatile *)&pUVM->aCpus[enmDest].vm.s.pReqs;
852 if (RT_LIKELY(pUVM->pVM))
853 VMCPU_FF_CLEAR(pUVM->pVM, enmDest, VM_FF_REQUEST);
854 }
855
856 PVMREQ pReqs = (PVMREQ)ASMAtomicXchgPtr(ppReqs, NULL);
857 if (!pReqs)
858 break;
859
860 /*
861 * Reverse the list to process it in FIFO order.
862 */
863 PVMREQ pReq = pReqs;
864 if (pReq->pNext)
865 Log2(("VMR3ReqProcess: 2+ requests: %p %p %p\n", pReq, pReq->pNext, pReq->pNext->pNext));
866 pReqs = NULL;
867 while (pReq)
868 {
869 Assert(pReq->enmState == VMREQSTATE_QUEUED);
870 Assert(pReq->pUVM == pUVM);
871 PVMREQ pCur = pReq;
872 pReq = pReq->pNext;
873 pCur->pNext = pReqs;
874 pReqs = pCur;
875 }
876
877
878 /*
879 * Process the requests.
880 *
881 * Since this is a FF worker certain rules applies to the
882 * status codes. See the EM section in VBox/err.h and EM.cpp for details.
883 */
884 while (pReqs)
885 {
886 /* Unchain the first request and advance the list. */
887 pReq = pReqs;
888 pReqs = pReqs->pNext;
889 pReq->pNext = NULL;
890
891 /* Process the request */
892 int rc2 = vmR3ReqProcessOneU(pUVM, pReq);
893
894 /*
895 * The status code handling extremely important yet very fragile. Should probably
896 * look for a better way of communicating status changes to EM...
897 */
898 if ( rc2 >= VINF_EM_FIRST
899 && rc2 <= VINF_EM_LAST
900 && ( rc == VINF_SUCCESS
901 || rc2 < rc) )
902 rc = rc2;
903 }
904 }
905
906 LogFlow(("VMR3ReqProcess: returns %Rrc (enmVMState=%d)\n", rc, pUVM->pVM ? pUVM->pVM->enmVMState : VMSTATE_CREATING));
907 return rc;
908}
909
910
911/**
912 * Process one request.
913 *
914 * @returns VBox status code.
915 *
916 * @param pVM VM handle.
917 * @param pReq Request packet to process.
918 */
919static int vmR3ReqProcessOneU(PUVM pUVM, PVMREQ pReq)
920{
921 LogFlow(("vmR3ReqProcessOne: pReq=%p type=%d fFlags=%#x\n", pReq, pReq->enmType, pReq->fFlags));
922
923 /*
924 * Process the request.
925 */
926 Assert(pReq->enmState == VMREQSTATE_QUEUED);
927 pReq->enmState = VMREQSTATE_PROCESSING;
928 int rcRet = VINF_SUCCESS; /* the return code of this function. */
929 int rcReq = VERR_NOT_IMPLEMENTED; /* the request status. */
930 switch (pReq->enmType)
931 {
932 /*
933 * A packed down call frame.
934 */
935 case VMREQTYPE_INTERNAL:
936 {
937 uintptr_t *pauArgs = &pReq->u.Internal.aArgs[0];
938 union
939 {
940 PFNRT pfn;
941 DECLCALLBACKMEMBER(int, pfn00)(void);
942 DECLCALLBACKMEMBER(int, pfn01)(uintptr_t);
943 DECLCALLBACKMEMBER(int, pfn02)(uintptr_t, uintptr_t);
944 DECLCALLBACKMEMBER(int, pfn03)(uintptr_t, uintptr_t, uintptr_t);
945 DECLCALLBACKMEMBER(int, pfn04)(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
946 DECLCALLBACKMEMBER(int, pfn05)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
947 DECLCALLBACKMEMBER(int, pfn06)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
948 DECLCALLBACKMEMBER(int, pfn07)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
949 DECLCALLBACKMEMBER(int, pfn08)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
950 DECLCALLBACKMEMBER(int, pfn09)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
951 DECLCALLBACKMEMBER(int, pfn10)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
952 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);
953 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);
954 } u;
955 u.pfn = pReq->u.Internal.pfn;
956#ifdef RT_ARCH_AMD64
957 switch (pReq->u.Internal.cArgs)
958 {
959 case 0: rcRet = u.pfn00(); break;
960 case 1: rcRet = u.pfn01(pauArgs[0]); break;
961 case 2: rcRet = u.pfn02(pauArgs[0], pauArgs[1]); break;
962 case 3: rcRet = u.pfn03(pauArgs[0], pauArgs[1], pauArgs[2]); break;
963 case 4: rcRet = u.pfn04(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3]); break;
964 case 5: rcRet = u.pfn05(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4]); break;
965 case 6: rcRet = u.pfn06(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5]); break;
966 case 7: rcRet = u.pfn07(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6]); break;
967 case 8: rcRet = u.pfn08(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7]); break;
968 case 9: rcRet = u.pfn09(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8]); break;
969 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;
970 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;
971 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;
972 default:
973 AssertReleaseMsgFailed(("cArgs=%d\n", pReq->u.Internal.cArgs));
974 rcRet = rcReq = VERR_INTERNAL_ERROR;
975 break;
976 }
977#else /* x86: */
978 size_t cbArgs = pReq->u.Internal.cArgs * sizeof(uintptr_t);
979# ifdef __GNUC__
980 __asm__ __volatile__("movl %%esp, %%edx\n\t"
981 "subl %2, %%esp\n\t"
982 "andl $0xfffffff0, %%esp\n\t"
983 "shrl $2, %2\n\t"
984 "movl %%esp, %%edi\n\t"
985 "rep movsl\n\t"
986 "movl %%edx, %%edi\n\t"
987 "call *%%eax\n\t"
988 "mov %%edi, %%esp\n\t"
989 : "=a" (rcRet),
990 "=S" (pauArgs),
991 "=c" (cbArgs)
992 : "0" (u.pfn),
993 "1" (pauArgs),
994 "2" (cbArgs)
995 : "edi", "edx");
996# else
997 __asm
998 {
999 xor edx, edx /* just mess it up. */
1000 mov eax, u.pfn
1001 mov ecx, cbArgs
1002 shr ecx, 2
1003 mov esi, pauArgs
1004 mov ebx, esp
1005 sub esp, cbArgs
1006 and esp, 0xfffffff0
1007 mov edi, esp
1008 rep movsd
1009 call eax
1010 mov esp, ebx
1011 mov rcRet, eax
1012 }
1013# endif
1014#endif /* x86 */
1015 if ((pReq->fFlags & (VMREQFLAGS_RETURN_MASK)) == VMREQFLAGS_VOID)
1016 rcRet = VINF_SUCCESS;
1017 rcReq = rcRet;
1018 break;
1019 }
1020
1021 default:
1022 AssertMsgFailed(("pReq->enmType=%d\n", pReq->enmType));
1023 rcReq = VERR_NOT_IMPLEMENTED;
1024 break;
1025 }
1026
1027 /*
1028 * Complete the request.
1029 */
1030 pReq->iStatus = rcReq;
1031 pReq->enmState = VMREQSTATE_COMPLETED;
1032 if (pReq->fFlags & VMREQFLAGS_NO_WAIT)
1033 {
1034 /* Free the packet, nobody is waiting. */
1035 LogFlow(("vmR3ReqProcessOne: Completed request %p: rcReq=%Rrc rcRet=%Rrc - freeing it\n",
1036 pReq, rcReq, rcRet));
1037 VMR3ReqFree(pReq);
1038 }
1039 else
1040 {
1041 /* Notify the waiter and him free up the packet. */
1042 LogFlow(("vmR3ReqProcessOne: Completed request %p: rcReq=%Rrc rcRet=%Rrc - notifying waiting thread\n",
1043 pReq, rcReq, rcRet));
1044 ASMAtomicXchgSize(&pReq->fEventSemClear, false);
1045 int rc2 = RTSemEventSignal(pReq->EventSem);
1046 if (RT_FAILURE(rc2))
1047 {
1048 AssertRC(rc2);
1049 rcRet = rc2;
1050 }
1051 }
1052 return rcRet;
1053}
1054
1055
1056
1057
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use