VirtualBox

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

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

VMReq,*: Replaced VMREQDEST with VMCPUID because it's a pain to have to cast CPU IDs all the time.

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