VirtualBox

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

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

VMM: whitespace cleanup.

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