VirtualBox

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

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

No @note in body.

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