VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxServiceCpuHotPlug.cpp

Last change on this file was 98103, checked in by vboxsync, 15 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 23.6 KB
Line 
1/* $Id: VBoxServiceCpuHotPlug.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * VBoxService - Guest Additions CPU Hot-Plugging Service.
4 */
5
6/*
7 * Copyright (C) 2010-2023 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/** @page pg_vgsvc_cpuhotplug VBoxService - CPU Hot-Plugging
29 *
30 * The CPU Hot-Plugging subservice helps execute and coordinate CPU hot-plugging
31 * between the guest OS and the VMM.
32 *
33 * CPU Hot-Plugging is useful for reallocating CPU resources from one VM to
34 * other VMs or/and the host. It talks to the VMM via VMMDev, new hot-plugging
35 * events being signalled with an interrupt (no polling).
36 *
37 * Currently only supported for linux guests.
38 */
39
40
41/*********************************************************************************************************************************
42* Header Files *
43*********************************************************************************************************************************/
44#include <iprt/assert.h>
45#include <iprt/dir.h>
46#include <iprt/file.h>
47#include <iprt/mem.h>
48#include <iprt/path.h>
49#include <iprt/string.h>
50#include <iprt/thread.h>
51#include <VBox/VBoxGuestLib.h>
52#include "VBoxServiceInternal.h"
53
54#ifdef RT_OS_LINUX
55# include <iprt/linux/sysfs.h>
56# include <errno.h> /* For the sysfs API */
57#endif
58
59
60/*********************************************************************************************************************************
61* Defined Constants And Macros *
62*********************************************************************************************************************************/
63#ifdef RT_OS_LINUX
64
65/** @name Paths to access the CPU device
66 * @{
67 */
68# define SYSFS_ACPI_CPU_PATH "/sys/devices"
69# define SYSFS_CPU_PATH "/sys/devices/system/cpu"
70/** @} */
71
72/** Path component for the ACPI CPU path. */
73typedef struct SYSFSCPUPATHCOMP
74{
75 /** Flag whether the name is suffixed with a number */
76 bool fNumberedSuffix;
77 /** Name of the component */
78 const char *pcszName;
79} SYSFSCPUPATHCOMP, *PSYSFSCPUPATHCOMP;
80/** Pointer to a const component. */
81typedef const SYSFSCPUPATHCOMP *PCSYSFSCPUPATHCOMP;
82
83/**
84 * Structure which defines how the entries are assembled.
85 */
86typedef struct SYSFSCPUPATH
87{
88 /** Id when probing for the correct path. */
89 uint32_t uId;
90 /** Array holding the possible components. */
91 PCSYSFSCPUPATHCOMP aComponentsPossible;
92 /** Number of entries in the array, excluding the terminator. */
93 unsigned cComponents;
94 /** Directory handle */
95 RTDIR hDir;
96 /** Current directory to try. */
97 char *pszPath;
98} SYSFSCPUPATH, *PSYSFSCPUPATH;
99
100/** Content of uId if the path wasn't probed yet. */
101# define ACPI_CPU_PATH_NOT_PROBED UINT32_MAX
102#endif /* RT_OS_LINUX*/
103
104
105/*********************************************************************************************************************************
106* Global Variables *
107*********************************************************************************************************************************/
108#ifdef RT_OS_LINUX
109/** Possible combinations of all path components for level 1. */
110static const SYSFSCPUPATHCOMP g_aAcpiCpuPathLvl1[] =
111{
112 /** LNXSYSTEM:<id> */
113 { true, "LNXSYSTM:*" }
114};
115
116/** Possible combinations of all path components for level 2. */
117static const SYSFSCPUPATHCOMP g_aAcpiCpuPathLvl2[] =
118{
119 /** device:<id> */
120 { true, "device:*" },
121 /** LNXSYBUS:<id> */
122 { true, "LNXSYBUS:*" }
123};
124
125/** Possible combinations of all path components for level 3 */
126static const SYSFSCPUPATHCOMP g_aAcpiCpuPathLvl3[] =
127{
128 /** ACPI0004:<id> */
129 { true, "ACPI0004:*" }
130};
131
132/** Possible combinations of all path components for level 4 */
133static const SYSFSCPUPATHCOMP g_aAcpiCpuPathLvl4[] =
134{
135 /** LNXCPU:<id> */
136 { true, "LNXCPU:*" },
137 /** ACPI_CPU:<id> */
138 { true, "ACPI_CPU:*" }
139};
140
141/** All possible combinations. */
142static SYSFSCPUPATH g_aAcpiCpuPath[] =
143{
144 /** Level 1 */
145 { ACPI_CPU_PATH_NOT_PROBED, g_aAcpiCpuPathLvl1, RT_ELEMENTS(g_aAcpiCpuPathLvl1), NULL, NULL },
146 /** Level 2 */
147 { ACPI_CPU_PATH_NOT_PROBED, g_aAcpiCpuPathLvl2, RT_ELEMENTS(g_aAcpiCpuPathLvl2), NULL, NULL },
148 /** Level 3 */
149 { ACPI_CPU_PATH_NOT_PROBED, g_aAcpiCpuPathLvl3, RT_ELEMENTS(g_aAcpiCpuPathLvl3), NULL, NULL },
150 /** Level 4 */
151 { ACPI_CPU_PATH_NOT_PROBED, g_aAcpiCpuPathLvl4, RT_ELEMENTS(g_aAcpiCpuPathLvl4), NULL, NULL },
152};
153
154/**
155 * Possible directories to get to the topology directory for reading core and package id.
156 *
157 * @remark: This is not part of the path above because the eject file is not in one of the directories
158 * below and would make the hot unplug code fail.
159 */
160static const char *g_apszTopologyPath[] =
161{
162 "sysdev",
163 "physical_node"
164};
165
166#endif /* RT_OS_LINUX*/
167
168
169#ifdef RT_OS_LINUX
170
171/**
172 * Probes for the correct path to the ACPI CPU object in sysfs for the
173 * various different kernel versions and distro's.
174 *
175 * @returns VBox status code.
176 */
177static int vgsvcCpuHotPlugProbePath(void)
178{
179 int rc = VINF_SUCCESS;
180
181 /* Probe for the correct path if we didn't already. */
182 if (RT_UNLIKELY(g_aAcpiCpuPath[0].uId == ACPI_CPU_PATH_NOT_PROBED))
183 {
184 char *pszPath = NULL; /** < Current path, increasing while we dig deeper. */
185
186 pszPath = RTStrDup(SYSFS_ACPI_CPU_PATH);
187 if (!pszPath)
188 return VERR_NO_MEMORY;
189
190 /*
191 * Simple algorithm to find the path.
192 * Performance is not a real problem because it is
193 * only executed once.
194 */
195 for (unsigned iLvlCurr = 0; iLvlCurr < RT_ELEMENTS(g_aAcpiCpuPath); iLvlCurr++)
196 {
197 PSYSFSCPUPATH pAcpiCpuPathLvl = &g_aAcpiCpuPath[iLvlCurr];
198
199 for (unsigned iCompCurr = 0; iCompCurr < pAcpiCpuPathLvl->cComponents; iCompCurr++)
200 {
201 PCSYSFSCPUPATHCOMP pPathComponent = &pAcpiCpuPathLvl->aComponentsPossible[iCompCurr];
202
203 /* Open the directory */
204 RTDIR hDirCurr = NIL_RTDIR;
205 char *pszPathTmp = RTPathJoinA(pszPath, pPathComponent->pcszName);
206 if (pszPathTmp)
207 {
208 rc = RTDirOpenFiltered(&hDirCurr, pszPathTmp, RTDIRFILTER_WINNT, 0 /*fFlags*/);
209 RTStrFree(pszPathTmp);
210 }
211 else
212 rc = VERR_NO_STR_MEMORY;
213 if (RT_FAILURE(rc))
214 break;
215
216 /* Search if the current directory contains one of the possible parts. */
217 size_t cchName = strlen(pPathComponent->pcszName);
218 RTDIRENTRY DirFolderContent;
219 bool fFound = false;
220
221 /* Get rid of the * filter which is in the path component. */
222 if (pPathComponent->fNumberedSuffix)
223 cchName--;
224
225 while (RT_SUCCESS(RTDirRead(hDirCurr, &DirFolderContent, NULL))) /* Assumption that szName has always enough space */
226 {
227 if ( DirFolderContent.cbName >= cchName
228 && !strncmp(DirFolderContent.szName, pPathComponent->pcszName, cchName))
229 {
230 /* Found, use the complete name to dig deeper. */
231 fFound = true;
232 pAcpiCpuPathLvl->uId = iCompCurr;
233 char *pszPathLvl = RTPathJoinA(pszPath, DirFolderContent.szName);
234 if (pszPathLvl)
235 {
236 RTStrFree(pszPath);
237 pszPath = pszPathLvl;
238 }
239 else
240 rc = VERR_NO_STR_MEMORY;
241 break;
242 }
243 }
244 RTDirClose(hDirCurr);
245
246 if (fFound)
247 break;
248 } /* For every possible component. */
249
250 /* No matching component for this part, no need to continue */
251 if (RT_FAILURE(rc))
252 break;
253 } /* For every level */
254
255 VGSvcVerbose(1, "Final path after probing %s rc=%Rrc\n", pszPath, rc);
256 RTStrFree(pszPath);
257 }
258
259 return rc;
260}
261
262
263/**
264 * Returns the path of the ACPI CPU device with the given core and package ID.
265 *
266 * @returns VBox status code.
267 * @param ppszPath Where to store the path.
268 * @param idCpuCore The core ID of the CPU.
269 * @param idCpuPackage The package ID of the CPU.
270 */
271static int vgsvcCpuHotPlugGetACPIDevicePath(char **ppszPath, uint32_t idCpuCore, uint32_t idCpuPackage)
272{
273 int rc = VINF_SUCCESS;
274
275 AssertPtrReturn(ppszPath, VERR_INVALID_PARAMETER);
276
277 rc = vgsvcCpuHotPlugProbePath();
278 if (RT_SUCCESS(rc))
279 {
280 /* Build the path from all components. */
281 bool fFound = false;
282 unsigned iLvlCurr = 0;
283 char *pszPath = NULL;
284 char *pszPathDir = NULL;
285 PSYSFSCPUPATH pAcpiCpuPathLvl = &g_aAcpiCpuPath[iLvlCurr];
286
287 /* Init everything. */
288 Assert(pAcpiCpuPathLvl->uId != ACPI_CPU_PATH_NOT_PROBED);
289 pszPath = RTPathJoinA(SYSFS_ACPI_CPU_PATH, pAcpiCpuPathLvl->aComponentsPossible[pAcpiCpuPathLvl->uId].pcszName);
290 if (!pszPath)
291 return VERR_NO_STR_MEMORY;
292
293 pAcpiCpuPathLvl->pszPath = RTStrDup(SYSFS_ACPI_CPU_PATH);
294 if (!pAcpiCpuPathLvl->pszPath)
295 {
296 RTStrFree(pszPath);
297 return VERR_NO_STR_MEMORY;
298 }
299
300 /* Open the directory */
301 rc = RTDirOpenFiltered(&pAcpiCpuPathLvl->hDir, pszPath, RTDIRFILTER_WINNT, 0 /*fFlags*/);
302 if (RT_SUCCESS(rc))
303 {
304 RTStrFree(pszPath);
305 pszPath = NULL;
306
307 /* Search for CPU */
308 while (!fFound)
309 {
310 /* Get the next directory. */
311 RTDIRENTRY DirFolderContent;
312 rc = RTDirRead(pAcpiCpuPathLvl->hDir, &DirFolderContent, NULL);
313 if (RT_SUCCESS(rc))
314 {
315 /* Create the new path. */
316 char *pszPathCurr = RTPathJoinA(pAcpiCpuPathLvl->pszPath, DirFolderContent.szName);
317 if (!pszPathCurr)
318 {
319 rc = VERR_NO_STR_MEMORY;
320 break;
321 }
322
323 /* If this is the last level check for the given core and package id. */
324 if (iLvlCurr == RT_ELEMENTS(g_aAcpiCpuPath) - 1)
325 {
326 /* Get the sysdev */
327 uint32_t idCore = 0;
328 uint32_t idPackage = 0;
329
330 for (unsigned i = 0; i < RT_ELEMENTS(g_apszTopologyPath); i++)
331 {
332 int64_t i64Core = 0;
333 int64_t i64Package = 0;
334
335 int rc2 = RTLinuxSysFsReadIntFile(10, &i64Core, "%s/%s/topology/core_id",
336 pszPathCurr, g_apszTopologyPath[i]);
337 if (RT_SUCCESS(rc2))
338 rc2 = RTLinuxSysFsReadIntFile(10, &i64Package, "%s/%s/topology/physical_package_id",
339 pszPathCurr, g_apszTopologyPath[i]);
340
341 if (RT_SUCCESS(rc2))
342 {
343 idCore = (uint32_t)i64Core;
344 idPackage = (uint32_t)i64Package;
345 break;
346 }
347 }
348
349 if ( idCore == idCpuCore
350 && idPackage == idCpuPackage)
351 {
352 /* Return the path */
353 pszPath = pszPathCurr;
354 fFound = true;
355 VGSvcVerbose(3, "CPU found\n");
356 break;
357 }
358 else
359 {
360 /* Get the next directory. */
361 RTStrFree(pszPathCurr);
362 pszPathCurr = NULL;
363 VGSvcVerbose(3, "CPU doesn't match, next directory\n");
364 }
365 }
366 else
367 {
368 /* Go deeper */
369 iLvlCurr++;
370
371 VGSvcVerbose(3, "Going deeper (iLvlCurr=%u)\n", iLvlCurr);
372
373 pAcpiCpuPathLvl = &g_aAcpiCpuPath[iLvlCurr];
374
375 Assert(pAcpiCpuPathLvl->hDir == NIL_RTDIR);
376 Assert(!pAcpiCpuPathLvl->pszPath);
377 pAcpiCpuPathLvl->pszPath = pszPathCurr;
378 PCSYSFSCPUPATHCOMP pPathComponent = &pAcpiCpuPathLvl->aComponentsPossible[pAcpiCpuPathLvl->uId];
379
380 Assert(pAcpiCpuPathLvl->uId != ACPI_CPU_PATH_NOT_PROBED);
381
382 pszPathDir = RTPathJoinA(pszPathCurr, pPathComponent->pcszName);
383 if (!pszPathDir)
384 {
385 rc = VERR_NO_STR_MEMORY;
386 break;
387 }
388
389 VGSvcVerbose(3, "New path %s\n", pszPathDir);
390
391 /* Open the directory */
392 rc = RTDirOpenFiltered(&pAcpiCpuPathLvl->hDir, pszPathDir, RTDIRFILTER_WINNT, 0 /*fFlags*/);
393 RTStrFree(pszPathDir);
394 pszPathDir = NULL;
395 if (RT_FAILURE(rc))
396 break;
397 }
398 }
399 else
400 {
401 RTDirClose(pAcpiCpuPathLvl->hDir);
402 RTStrFree(pAcpiCpuPathLvl->pszPath);
403 pAcpiCpuPathLvl->hDir = NIL_RTDIR;
404 pAcpiCpuPathLvl->pszPath = NULL;
405
406 /*
407 * If we reached the end we didn't find the matching path
408 * meaning the CPU is already offline.
409 */
410 if (!iLvlCurr)
411 {
412 rc = VERR_NOT_FOUND;
413 break;
414 }
415
416 iLvlCurr--;
417 pAcpiCpuPathLvl = &g_aAcpiCpuPath[iLvlCurr];
418 VGSvcVerbose(3, "Directory not found, going back (iLvlCurr=%u)\n", iLvlCurr);
419 }
420 } /* while not found */
421 } /* Successful init */
422
423 /* Cleanup */
424 for (unsigned i = 0; i < RT_ELEMENTS(g_aAcpiCpuPath); i++)
425 {
426 if (g_aAcpiCpuPath[i].hDir)
427 RTDirClose(g_aAcpiCpuPath[i].hDir);
428 if (g_aAcpiCpuPath[i].pszPath)
429 RTStrFree(g_aAcpiCpuPath[i].pszPath);
430 g_aAcpiCpuPath[i].hDir = NIL_RTDIR;
431 g_aAcpiCpuPath[i].pszPath = NULL;
432 }
433 if (pszPathDir)
434 RTStrFree(pszPathDir);
435 if (RT_FAILURE(rc) && pszPath)
436 RTStrFree(pszPath);
437
438 if (RT_SUCCESS(rc))
439 *ppszPath = pszPath;
440 }
441
442 return rc;
443}
444
445#endif /* RT_OS_LINUX */
446
447/**
448 * Handles VMMDevCpuEventType_Plug.
449 *
450 * @param idCpuCore The CPU core ID.
451 * @param idCpuPackage The CPU package ID.
452 */
453static void vgsvcCpuHotPlugHandlePlugEvent(uint32_t idCpuCore, uint32_t idCpuPackage)
454{
455#ifdef RT_OS_LINUX
456 /*
457 * The topology directory (containing the physical and core id properties)
458 * is not available until the CPU is online. So we just iterate over all directories
459 * and enable the first CPU which is not online already.
460 * Because the directory might not be available immediately we try a few times.
461 *
462 */
463 /** @todo Maybe use udev to monitor hot-add events from the kernel */
464 bool fCpuOnline = false;
465 unsigned cTries = 5;
466
467 do
468 {
469 RTDIR hDirDevices = NULL;
470 int rc = RTDirOpen(&hDirDevices, SYSFS_CPU_PATH);
471 if (RT_SUCCESS(rc))
472 {
473 RTDIRENTRY DirFolderContent;
474 while (RT_SUCCESS(RTDirRead(hDirDevices, &DirFolderContent, NULL))) /* Assumption that szName has always enough space */
475 {
476 /* Check if this is a CPU object which can be brought online. */
477 if (RTLinuxSysFsExists("%s/%s/online", SYSFS_CPU_PATH, DirFolderContent.szName))
478 {
479 /* Check the status of the CPU by reading the online flag. */
480 int64_t i64Status = 0;
481 rc = RTLinuxSysFsReadIntFile(10 /*uBase*/, &i64Status, "%s/%s/online", SYSFS_CPU_PATH, DirFolderContent.szName);
482 if ( RT_SUCCESS(rc)
483 && i64Status == 0)
484 {
485 /* CPU is offline, turn it on. */
486 rc = RTLinuxSysFsWriteU8File(10 /*uBase*/, 1, "%s/%s/online", SYSFS_CPU_PATH, DirFolderContent.szName);
487 if (RT_SUCCESS(rc))
488 {
489 VGSvcVerbose(1, "CpuHotPlug: CPU %u/%u was brought online\n", idCpuPackage, idCpuCore);
490 fCpuOnline = true;
491 break;
492 }
493 }
494 else if (RT_FAILURE(rc))
495 VGSvcError("CpuHotPlug: Failed to open '%s/%s/online' rc=%Rrc\n",
496 SYSFS_CPU_PATH, DirFolderContent.szName, rc);
497 else
498 {
499 /*
500 * Check whether the topology matches what we got (which means someone raced us and brought the CPU
501 * online already).
502 */
503 int64_t i64Core = 0;
504 int64_t i64Package = 0;
505
506 int rc2 = RTLinuxSysFsReadIntFile(10, &i64Core, "%s/%s/topology/core_id",
507 SYSFS_CPU_PATH, DirFolderContent.szName);
508 if (RT_SUCCESS(rc2))
509 rc2 = RTLinuxSysFsReadIntFile(10, &i64Package, "%s/%s/topology/physical_package_id",
510 SYSFS_CPU_PATH, DirFolderContent.szName);
511 if ( RT_SUCCESS(rc2)
512 && idCpuPackage == i64Package
513 && idCpuCore == i64Core)
514 {
515 VGSvcVerbose(1, "CpuHotPlug: '%s' is already online\n", DirFolderContent.szName);
516 fCpuOnline = true;
517 break;
518 }
519 }
520 }
521 }
522 RTDirClose(hDirDevices);
523 }
524 else
525 VGSvcError("CpuHotPlug: Failed to open path %s rc=%Rrc\n", SYSFS_CPU_PATH, rc);
526
527 /* Sleep a bit */
528 if (!fCpuOnline)
529 RTThreadSleep(100);
530
531 } while ( !fCpuOnline
532 && cTries-- > 0);
533#else
534# error "Port me"
535#endif
536}
537
538
539/**
540 * Handles VMMDevCpuEventType_Unplug.
541 *
542 * @param idCpuCore The CPU core ID.
543 * @param idCpuPackage The CPU package ID.
544 */
545static void vgsvcCpuHotPlugHandleUnplugEvent(uint32_t idCpuCore, uint32_t idCpuPackage)
546{
547#ifdef RT_OS_LINUX
548 char *pszCpuDevicePath = NULL;
549 int rc = vgsvcCpuHotPlugGetACPIDevicePath(&pszCpuDevicePath, idCpuCore, idCpuPackage);
550 if (RT_SUCCESS(rc))
551 {
552 RTFILE hFileCpuEject;
553 rc = RTFileOpenF(&hFileCpuEject, RTFILE_O_WRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE, "%s/eject", pszCpuDevicePath);
554 if (RT_SUCCESS(rc))
555 {
556 /* Write a 1 to eject the CPU */
557 rc = RTFileWrite(hFileCpuEject, "1", 1, NULL);
558 if (RT_SUCCESS(rc))
559 VGSvcVerbose(1, "CpuHotPlug: CPU %u/%u was ejected\n", idCpuPackage, idCpuCore);
560 else
561 VGSvcError("CpuHotPlug: Failed to eject CPU %u/%u rc=%Rrc\n", idCpuPackage, idCpuCore, rc);
562
563 RTFileClose(hFileCpuEject);
564 }
565 else
566 VGSvcError("CpuHotPlug: Failed to open '%s/eject' rc=%Rrc\n", pszCpuDevicePath, rc);
567 RTStrFree(pszCpuDevicePath);
568 }
569 else if (rc == VERR_NOT_FOUND)
570 VGSvcVerbose(1, "CpuHotPlug: CPU %u/%u was aleady ejected by someone else!\n", idCpuPackage, idCpuCore);
571 else
572 VGSvcError("CpuHotPlug: Failed to get CPU device path rc=%Rrc\n", rc);
573#else
574# error "Port me"
575#endif
576}
577
578
579/** @interface_method_impl{VBOXSERVICE,pfnWorker} */
580static DECLCALLBACK(int) vgsvcCpuHotPlugWorker(bool volatile *pfShutdown)
581{
582 /*
583 * Tell the control thread that it can continue spawning services.
584 */
585 RTThreadUserSignal(RTThreadSelf());
586
587 /*
588 * Enable the CPU hotplug notifier.
589 */
590 int rc = VbglR3CpuHotPlugInit();
591 if (RT_FAILURE(rc))
592 return rc;
593
594 /*
595 * The Work Loop.
596 */
597 for (;;)
598 {
599 /* Wait for CPU hot-plugging event. */
600 uint32_t idCpuCore;
601 uint32_t idCpuPackage;
602 VMMDevCpuEventType enmEventType;
603 rc = VbglR3CpuHotPlugWaitForEvent(&enmEventType, &idCpuCore, &idCpuPackage);
604 if (RT_SUCCESS(rc))
605 {
606 VGSvcVerbose(3, "CpuHotPlug: Event happened idCpuCore=%u idCpuPackage=%u enmEventType=%d\n",
607 idCpuCore, idCpuPackage, enmEventType);
608 switch (enmEventType)
609 {
610 case VMMDevCpuEventType_Plug:
611 vgsvcCpuHotPlugHandlePlugEvent(idCpuCore, idCpuPackage);
612 break;
613
614 case VMMDevCpuEventType_Unplug:
615 vgsvcCpuHotPlugHandleUnplugEvent(idCpuCore, idCpuPackage);
616 break;
617
618 default:
619 {
620 static uint32_t s_iErrors = 0;
621 if (s_iErrors++ < 10)
622 VGSvcError("CpuHotPlug: Unknown event: idCpuCore=%u idCpuPackage=%u enmEventType=%d\n",
623 idCpuCore, idCpuPackage, enmEventType);
624 break;
625 }
626 }
627 }
628 else if (rc != VERR_INTERRUPTED && rc != VERR_TRY_AGAIN)
629 {
630 VGSvcError("CpuHotPlug: VbglR3CpuHotPlugWaitForEvent returned %Rrc\n", rc);
631 break;
632 }
633
634 if (*pfShutdown)
635 break;
636 }
637
638 VbglR3CpuHotPlugTerm();
639 return rc;
640}
641
642
643/** @interface_method_impl{VBOXSERVICE,pfnStop} */
644static DECLCALLBACK(void) vgsvcCpuHotPlugStop(void)
645{
646 VbglR3InterruptEventWaits();
647 return;
648}
649
650
651/**
652 * The 'CpuHotPlug' service description.
653 */
654VBOXSERVICE g_CpuHotPlug =
655{
656 /* pszName. */
657 "cpuhotplug",
658 /* pszDescription. */
659 "CPU hot-plugging monitor",
660 /* pszUsage. */
661 NULL,
662 /* pszOptions. */
663 NULL,
664 /* methods */
665 VGSvcDefaultPreInit,
666 VGSvcDefaultOption,
667 VGSvcDefaultInit,
668 vgsvcCpuHotPlugWorker,
669 vgsvcCpuHotPlugStop,
670 VGSvcDefaultTerm
671};
672
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use