VirtualBox

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

Last change on this file since 40363 was 40128, checked in by vboxsync, 13 years ago

VBoxService: Fixed unknown command line parameter handling of sub-services.

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