VirtualBox

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

Last change on this file since 51564 was 51564, checked in by vboxsync, 11 years ago

Additions/common/VBoxService: revert r94117.

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