VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/VMReq.cpp

Last change on this file was 106061, checked in by vboxsync, 5 days ago

Copyright year updates by scm.

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