VirtualBox

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

Last change on this file since 93115 was 93115, checked in by vboxsync, 2 years ago

scm --update-copyright-year

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

© 2023 Oracle
ContactPrivacy policyTerms of Use