VirtualBox

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

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

VMReq.cpp: Made VMR3ReqProcessU re-entrant.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use