VirtualBox

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

Last change on this file since 19400 was 19400, checked in by vboxsync, 15 years ago

VM: VMR3Notify*FF refactorying (for poking); converting fNotifiedREM to a flag and dropping the pVM/pVCpu edition of the API.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 39.8 KB
Line 
1/* $Id: VMReq.cpp 19400 2009-05-05 21:49:16Z 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)), 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, 0);
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, 0);
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
791 /*
792 * Wait on the package.
793 */
794 int rc;
795 if (cMillies != RT_INDEFINITE_WAIT)
796 rc = RTSemEventWait(pReq->EventSem, cMillies);
797 else
798 {
799 do
800 {
801 rc = RTSemEventWait(pReq->EventSem, RT_INDEFINITE_WAIT);
802 Assert(rc != VERR_TIMEOUT);
803 } while ( pReq->enmState != VMREQSTATE_COMPLETED
804 && pReq->enmState != VMREQSTATE_INVALID);
805 }
806 if (RT_SUCCESS(rc))
807 ASMAtomicXchgSize(&pReq->fEventSemClear, true);
808 if (pReq->enmState == VMREQSTATE_COMPLETED)
809 rc = VINF_SUCCESS;
810 LogFlow(("VMR3ReqWait: returns %Rrc\n", rc));
811 Assert(rc != VERR_INTERRUPTED);
812 return rc;
813}
814
815
816/**
817 * Process pending request(s).
818 *
819 * This function is called from a forced action handler in the EMT
820 * or from one of the EMT loops.
821 *
822 * @returns VBox status code.
823 *
824 * @param pUVM Pointer to the user mode VM structure.
825 * @param idDstCpu Pass VMCPUID_ANY to process the common request queue
826 * and the CPU ID for a CPU specific one. In the latter
827 * case the calling thread must be the EMT of that CPU.
828 */
829VMMR3DECL(int) VMR3ReqProcessU(PUVM pUVM, VMCPUID idDstCpu)
830{
831 LogFlow(("VMR3ReqProcessU: (enmVMState=%d) idDstCpu=%d\n", pUVM->pVM ? pUVM->pVM->enmVMState : VMSTATE_CREATING, idDstCpu));
832
833 /*
834 * Process loop.
835 *
836 * We do not repeat the outer loop if we've got an informational status code
837 * since that code needs processing by our caller.
838 */
839 int rc = VINF_SUCCESS;
840 while (rc <= VINF_SUCCESS)
841 {
842 /*
843 * Get pending requests.
844 */
845 void * volatile *ppReqs;
846 if (idDstCpu == VMCPUID_ANY)
847 {
848 ppReqs = (void * volatile *)&pUVM->vm.s.pReqs;
849 if (RT_LIKELY(pUVM->pVM))
850 VM_FF_CLEAR(pUVM->pVM, VM_FF_REQUEST);
851 }
852 else
853 {
854 Assert(idDstCpu < pUVM->cCpus);
855 Assert(pUVM->aCpus[idDstCpu].vm.s.NativeThreadEMT == RTThreadNativeSelf());
856 ppReqs = (void * volatile *)&pUVM->aCpus[idDstCpu].vm.s.pReqs;
857 if (RT_LIKELY(pUVM->pVM))
858 VMCPU_FF_CLEAR(&pUVM->pVM->aCpus[idDstCpu], VMCPU_FF_REQUEST);
859 }
860 PVMREQ pReqs = (PVMREQ)ASMAtomicXchgPtr(ppReqs, NULL);
861 if (!pReqs)
862 break;
863
864 /*
865 * Reverse the list to process it in FIFO order.
866 */
867 PVMREQ pReq = pReqs;
868 if (pReq->pNext)
869 Log2(("VMR3ReqProcess: 2+ requests: %p %p %p\n", pReq, pReq->pNext, pReq->pNext->pNext));
870 pReqs = NULL;
871 while (pReq)
872 {
873 Assert(pReq->enmState == VMREQSTATE_QUEUED);
874 Assert(pReq->pUVM == pUVM);
875 PVMREQ pCur = pReq;
876 pReq = pReq->pNext;
877 pCur->pNext = pReqs;
878 pReqs = pCur;
879 }
880
881
882 /*
883 * Process the requests.
884 *
885 * Since this is a FF worker certain rules applies to the
886 * status codes. See the EM section in VBox/err.h and EM.cpp for details.
887 */
888 while (pReqs)
889 {
890 /* Unchain the first request and advance the list. */
891 pReq = pReqs;
892 pReqs = pReqs->pNext;
893 pReq->pNext = NULL;
894
895 /* Process the request */
896 int rc2 = vmR3ReqProcessOneU(pUVM, pReq);
897
898 /*
899 * The status code handling extremely important yet very fragile. Should probably
900 * look for a better way of communicating status changes to EM...
901 */
902 if ( rc2 >= VINF_EM_FIRST
903 && rc2 <= VINF_EM_LAST
904 && ( rc == VINF_SUCCESS
905 || rc2 < rc) )
906 rc = rc2;
907 }
908 }
909
910 LogFlow(("VMR3ReqProcess: returns %Rrc (enmVMState=%d)\n", rc, pUVM->pVM ? pUVM->pVM->enmVMState : VMSTATE_CREATING));
911 return rc;
912}
913
914
915/**
916 * Process one request.
917 *
918 * @returns VBox status code.
919 *
920 * @param pVM VM handle.
921 * @param pReq Request packet to process.
922 */
923static int vmR3ReqProcessOneU(PUVM pUVM, PVMREQ pReq)
924{
925 LogFlow(("vmR3ReqProcessOne: pReq=%p type=%d fFlags=%#x\n", pReq, pReq->enmType, pReq->fFlags));
926
927 /*
928 * Process the request.
929 */
930 Assert(pReq->enmState == VMREQSTATE_QUEUED);
931 pReq->enmState = VMREQSTATE_PROCESSING;
932 int rcRet = VINF_SUCCESS; /* the return code of this function. */
933 int rcReq = VERR_NOT_IMPLEMENTED; /* the request status. */
934 switch (pReq->enmType)
935 {
936 /*
937 * A packed down call frame.
938 */
939 case VMREQTYPE_INTERNAL:
940 {
941 uintptr_t *pauArgs = &pReq->u.Internal.aArgs[0];
942 union
943 {
944 PFNRT pfn;
945 DECLCALLBACKMEMBER(int, pfn00)(void);
946 DECLCALLBACKMEMBER(int, pfn01)(uintptr_t);
947 DECLCALLBACKMEMBER(int, pfn02)(uintptr_t, uintptr_t);
948 DECLCALLBACKMEMBER(int, pfn03)(uintptr_t, uintptr_t, uintptr_t);
949 DECLCALLBACKMEMBER(int, pfn04)(uintptr_t, uintptr_t, uintptr_t, uintptr_t);
950 DECLCALLBACKMEMBER(int, pfn05)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
951 DECLCALLBACKMEMBER(int, pfn06)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
952 DECLCALLBACKMEMBER(int, pfn07)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
953 DECLCALLBACKMEMBER(int, pfn08)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
954 DECLCALLBACKMEMBER(int, pfn09)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
955 DECLCALLBACKMEMBER(int, pfn10)(uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t);
956 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);
957 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);
958 } u;
959 u.pfn = pReq->u.Internal.pfn;
960#ifdef RT_ARCH_AMD64
961 switch (pReq->u.Internal.cArgs)
962 {
963 case 0: rcRet = u.pfn00(); break;
964 case 1: rcRet = u.pfn01(pauArgs[0]); break;
965 case 2: rcRet = u.pfn02(pauArgs[0], pauArgs[1]); break;
966 case 3: rcRet = u.pfn03(pauArgs[0], pauArgs[1], pauArgs[2]); break;
967 case 4: rcRet = u.pfn04(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3]); break;
968 case 5: rcRet = u.pfn05(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4]); break;
969 case 6: rcRet = u.pfn06(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5]); break;
970 case 7: rcRet = u.pfn07(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6]); break;
971 case 8: rcRet = u.pfn08(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7]); break;
972 case 9: rcRet = u.pfn09(pauArgs[0], pauArgs[1], pauArgs[2], pauArgs[3], pauArgs[4], pauArgs[5], pauArgs[6], pauArgs[7], pauArgs[8]); break;
973 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;
974 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;
975 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;
976 default:
977 AssertReleaseMsgFailed(("cArgs=%d\n", pReq->u.Internal.cArgs));
978 rcRet = rcReq = VERR_INTERNAL_ERROR;
979 break;
980 }
981#else /* x86: */
982 size_t cbArgs = pReq->u.Internal.cArgs * sizeof(uintptr_t);
983# ifdef __GNUC__
984 __asm__ __volatile__("movl %%esp, %%edx\n\t"
985 "subl %2, %%esp\n\t"
986 "andl $0xfffffff0, %%esp\n\t"
987 "shrl $2, %2\n\t"
988 "movl %%esp, %%edi\n\t"
989 "rep movsl\n\t"
990 "movl %%edx, %%edi\n\t"
991 "call *%%eax\n\t"
992 "mov %%edi, %%esp\n\t"
993 : "=a" (rcRet),
994 "=S" (pauArgs),
995 "=c" (cbArgs)
996 : "0" (u.pfn),
997 "1" (pauArgs),
998 "2" (cbArgs)
999 : "edi", "edx");
1000# else
1001 __asm
1002 {
1003 xor edx, edx /* just mess it up. */
1004 mov eax, u.pfn
1005 mov ecx, cbArgs
1006 shr ecx, 2
1007 mov esi, pauArgs
1008 mov ebx, esp
1009 sub esp, cbArgs
1010 and esp, 0xfffffff0
1011 mov edi, esp
1012 rep movsd
1013 call eax
1014 mov esp, ebx
1015 mov rcRet, eax
1016 }
1017# endif
1018#endif /* x86 */
1019 if ((pReq->fFlags & (VMREQFLAGS_RETURN_MASK)) == VMREQFLAGS_VOID)
1020 rcRet = VINF_SUCCESS;
1021 rcReq = rcRet;
1022 break;
1023 }
1024
1025 default:
1026 AssertMsgFailed(("pReq->enmType=%d\n", pReq->enmType));
1027 rcReq = VERR_NOT_IMPLEMENTED;
1028 break;
1029 }
1030
1031 /*
1032 * Complete the request.
1033 */
1034 pReq->iStatus = rcReq;
1035 pReq->enmState = VMREQSTATE_COMPLETED;
1036 if (pReq->fFlags & VMREQFLAGS_NO_WAIT)
1037 {
1038 /* Free the packet, nobody is waiting. */
1039 LogFlow(("vmR3ReqProcessOne: Completed request %p: rcReq=%Rrc rcRet=%Rrc - freeing it\n",
1040 pReq, rcReq, rcRet));
1041 VMR3ReqFree(pReq);
1042 }
1043 else
1044 {
1045 /* Notify the waiter and him free up the packet. */
1046 LogFlow(("vmR3ReqProcessOne: Completed request %p: rcReq=%Rrc rcRet=%Rrc - notifying waiting thread\n",
1047 pReq, rcReq, rcRet));
1048 ASMAtomicXchgSize(&pReq->fEventSemClear, false);
1049 int rc2 = RTSemEventSignal(pReq->EventSem);
1050 if (RT_FAILURE(rc2))
1051 {
1052 AssertRC(rc2);
1053 rcRet = rc2;
1054 }
1055 }
1056 return rcRet;
1057}
1058
1059
1060
1061
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