VirtualBox

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

Last change on this file since 28800 was 28800, checked in by vboxsync, 15 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.6 KB
Line 
1/* $Id: VBoxServiceCpuHotPlug.cpp 28800 2010-04-27 08:22:32Z 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/string.h>
26#include <iprt/thread.h>
27#include <VBox/VBoxGuestLib.h>
28#include "VBoxServiceInternal.h"
29
30#ifdef RT_OS_LINUX
31# include <iprt/linux/sysfs.h>
32# include <errno.h> /* For the sysfs API */
33#endif
34
35
36/*******************************************************************************
37* Defined Constants And Macros *
38*******************************************************************************/
39#ifdef RT_OS_LINUX
40/** @name Paths to access the CPU device
41 * @{
42 */
43# define SYSFS_ACPI_CPU_PATH "/sys/devices/LNXSYSTM:00/device:00"
44# define SYSFS_CPU_PATH "/sys/devices/system/cpu"
45/** @} */
46#endif
47
48
49#ifdef RT_OS_LINUX
50/**
51 * Returns the path of the ACPI CPU device with the given core and package ID.
52 *
53 * @returns VBox status code.
54 * @param ppszPath Where to store the path.
55 * @param idCpuCore The core ID of the CPU.
56 * @param idCpuPackage The package ID of the CPU.
57 */
58static int VBoxServiceCpuHotPlugGetACPIDevicePath(char **ppszPath, uint32_t idCpuCore, uint32_t idCpuPackage)
59{
60 AssertPtr(ppszPath);
61
62 PRTDIR pDirDevices = NULL;
63 int rc = RTDirOpen(&pDirDevices, SYSFS_ACPI_CPU_PATH); /* could use RTDirOpenFiltered */
64 if (RT_SUCCESS(rc))
65 {
66 /* Search every ACPI0004 container device for LNXCPU devices. */
67 RTDIRENTRY DirFolderAcpiContainer;
68 bool fFound = false;
69
70 while ( RT_SUCCESS(RTDirRead(pDirDevices, &DirFolderAcpiContainer, NULL))
71 && !fFound) /* Assumption that szName has always enough space */
72 {
73 if (!strncmp(DirFolderAcpiContainer.szName, "ACPI0004", 8))
74 {
75 char *pszAcpiContainerPath = NULL;
76 PRTDIR pDirAcpiContainer = NULL;
77
78 rc = RTStrAPrintf(&pszAcpiContainerPath, "%s/%s", SYSFS_ACPI_CPU_PATH, DirFolderAcpiContainer.szName);
79 if (RT_FAILURE(rc))
80 break;
81
82 rc = RTDirOpen(&pDirAcpiContainer, pszAcpiContainerPath);
83 if (RT_SUCCESS(rc))
84 {
85 RTDIRENTRY DirFolderContent;
86 while (RT_SUCCESS(RTDirRead(pDirAcpiContainer, &DirFolderContent, NULL))) /* Assumption that szName has always enough space */
87 {
88 if ( !strncmp(DirFolderContent.szName, "LNXCPU", 6)
89 || !strncmp(DirFolderContent.szName, "ACPI_CPU", 8))
90 {
91 /* Get the sysdev */
92 uint32_t idCore = RTLinuxSysFsReadIntFile(10, "%s/%s/sysdev/topology/core_id",
93 pszAcpiContainerPath, DirFolderContent.szName);
94 uint32_t idPackage = RTLinuxSysFsReadIntFile(10, "%s/%s/sysdev/topology/physical_package_id",
95 pszAcpiContainerPath, DirFolderContent.szName);
96 if ( idCore == idCpuCore
97 && idPackage == idCpuPackage)
98 {
99 /* Return the path */
100 rc = RTStrAPrintf(ppszPath, "%s/%s", pszAcpiContainerPath, DirFolderContent.szName);
101 fFound = true;
102 break;
103 }
104 }
105 }
106
107 RTDirClose(pDirAcpiContainer);
108 }
109
110 RTStrFree(pszAcpiContainerPath);
111 }
112 }
113
114 RTDirClose(pDirDevices);
115 }
116
117 return rc;
118}
119#endif /* RT_OS_LINUX */
120
121
122/** @copydoc VBOXSERVICE::pfnPreInit */
123static DECLCALLBACK(int) VBoxServiceCpuHotPlugPreInit(void)
124{
125 return VINF_SUCCESS;
126}
127
128
129/** @copydoc VBOXSERVICE::pfnOption */
130static DECLCALLBACK(int) VBoxServiceCpuHotPlugOption(const char **ppszShort, int argc, char **argv, int *pi)
131{
132 NOREF(ppszShort);
133 NOREF(argc);
134 NOREF(argv);
135 NOREF(pi);
136 return VINF_SUCCESS;
137}
138
139
140/** @copydoc VBOXSERVICE::pfnInit */
141static DECLCALLBACK(int) VBoxServiceCpuHotPlugInit(void)
142{
143 return VINF_SUCCESS;
144}
145
146
147/**
148 * Handles VMMDevCpuEventType_Plug.
149 *
150 * @param idCpuCore The CPU core ID.
151 * @param idCpuPackage The CPU package ID.
152 */
153static void VBoxServiceCpuHotPlugHandlePlugEvent(uint32_t idCpuCore, uint32_t idCpuPackage)
154{
155#ifdef RT_OS_LINUX
156 /*
157 * The topology directory (containing the physical and core id properties)
158 * is not available until the CPU is online. So we just iterate over all directories
159 * and enable every CPU which is not online already.
160 * Because the directory might not be available immediately we try a few times.
161 *
162 * @todo: Maybe use udev to monitor hot-add events from the kernel
163 */
164 bool fCpuOnline = false;
165 unsigned cTries = 5;
166
167 do
168 {
169 PRTDIR pDirDevices = NULL;
170 int rc = RTDirOpen(&pDirDevices, SYSFS_CPU_PATH);
171 if (RT_SUCCESS(rc))
172 {
173 RTDIRENTRY DirFolderContent;
174 while (RT_SUCCESS(RTDirRead(pDirDevices, &DirFolderContent, NULL))) /* Assumption that szName has always enough space */
175 {
176 /** @todo r-bird: This code is bringing all CPUs online; the idCpuCore and
177 * idCpuPackage parameters are unused!
178 * aeichner: These files are not available at this point unfortunately. (see comment above)
179 * bird: Yes, but isn't that easily dealt with by doing:
180 * if (matching_topology() || !have_topology_directory())
181 * bring_cpu_online()
182 * That could save you the cpu0 and cpuidle checks to.
183 */
184 /*
185 * Check if this is a CPU object.
186 * cpu0 is excluded because it is not possible to change the state
187 * of the first CPU on Linux (it doesn't even have an online file)
188 * and cpuidle is no CPU device. Prevents error messages later.
189 */
190 if( !strncmp(DirFolderContent.szName, "cpu", 3)
191 && strncmp(DirFolderContent.szName, "cpu0", 4)
192 && strncmp(DirFolderContent.szName, "cpuidle", 7))
193 {
194 /* Get the sysdev */
195 RTFILE hFileCpuOnline = NIL_RTFILE;
196
197 rc = RTFileOpenF(&hFileCpuOnline, RTFILE_O_WRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
198 "%s/%s/online", SYSFS_CPU_PATH, DirFolderContent.szName);
199 if (RT_SUCCESS(rc))
200 {
201 /* Write a 1 to online the CPU */
202 rc = RTFileWrite(hFileCpuOnline, "1", 1, NULL);
203 RTFileClose(hFileCpuOnline);
204 if (RT_SUCCESS(rc))
205 {
206 VBoxServiceVerbose(1, "CpuHotPlug: CPU %u/%u was brought online\n", idCpuPackage, idCpuCore);
207 fCpuOnline = true;
208 break;
209 }
210 /* Error means CPU not present or online already */
211 }
212 else
213 VBoxServiceError("CpuHotPlug: Failed to open \"%s/%s/online\" rc=%Rrc\n",
214 SYSFS_CPU_PATH, DirFolderContent.szName, rc);
215 }
216 }
217 }
218 else
219 VBoxServiceError("CpuHotPlug: Failed to open path %s rc=%Rrc\n", SYSFS_CPU_PATH, rc);
220
221 /* Sleep a bit */
222 if (!fCpuOnline)
223 RTThreadSleep(10);
224
225 } while ( !fCpuOnline
226 && cTries-- > 0);
227#else
228# error "Port me"
229#endif
230}
231
232
233/**
234 * Handles VMMDevCpuEventType_Unplug.
235 *
236 * @param idCpuCore The CPU core ID.
237 * @param idCpuPackage The CPU package ID.
238 */
239static void VBoxServiceCpuHotPlugHandleUnplugEvent(uint32_t idCpuCore, uint32_t idCpuPackage)
240{
241#ifdef RT_OS_LINUX
242 char *pszCpuDevicePath = NULL;
243 int rc = VBoxServiceCpuHotPlugGetACPIDevicePath(&pszCpuDevicePath, idCpuCore, idCpuPackage);
244 if (RT_SUCCESS(rc))
245 {
246 RTFILE hFileCpuEject;
247 rc = RTFileOpenF(&hFileCpuEject, RTFILE_O_WRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
248 "%s/eject", pszCpuDevicePath);
249 if (RT_SUCCESS(rc))
250 {
251 /* Write a 1 to eject the CPU */
252 rc = RTFileWrite(hFileCpuEject, "1", 1, NULL);
253 if (RT_SUCCESS(rc))
254 VBoxServiceVerbose(1, "CpuHotPlug: CPU %u/%u was ejected\n", idCpuPackage, idCpuCore);
255 else
256 VBoxServiceError("CpuHotPlug: Failed to eject CPU %u/%u rc=%Rrc\n", idCpuPackage, idCpuCore, rc);
257
258 RTFileClose(hFileCpuEject);
259 }
260 else
261 VBoxServiceError("CpuHotPlug: Failed to open \"%s/eject\" rc=%Rrc\n", pszCpuDevicePath, rc);
262 RTStrFree(pszCpuDevicePath);
263 }
264 else
265 VBoxServiceError("CpuHotPlug: Failed to get CPU device path rc=%Rrc\n", rc);
266#else
267# error "Port me"
268#endif
269}
270
271
272/** @copydoc VBOXSERVICE::pfnWorker */
273DECLCALLBACK(int) VBoxServiceCpuHotPlugWorker(bool volatile *pfShutdown)
274{
275 /*
276 * Tell the control thread that it can continue spawning services.
277 */
278 RTThreadUserSignal(RTThreadSelf());
279
280 /*
281 * Enable the CPU hotplug notifier.
282 */
283 int rc = VbglR3CpuHotPlugInit();
284 if (RT_FAILURE(rc))
285 return rc;
286
287 /*
288 * The Work Loop.
289 */
290 for (;;)
291 {
292 /* Wait for CPU hot plugging event. */
293 uint32_t idCpuCore;
294 uint32_t idCpuPackage;
295 VMMDevCpuEventType enmEventType;
296 rc = VbglR3CpuHotPlugWaitForEvent(&enmEventType, &idCpuCore, &idCpuPackage);
297 if (RT_SUCCESS(rc))
298 {
299 VBoxServiceVerbose(3, "CpuHotPlug: Event happened idCpuCore=%u idCpuPackage=%u enmEventType=%d\n",
300 idCpuCore, idCpuPackage, enmEventType);
301 switch (enmEventType)
302 {
303 case VMMDevCpuEventType_Plug:
304 VBoxServiceCpuHotPlugHandlePlugEvent(idCpuCore, idCpuPackage);
305 break;
306
307 case VMMDevCpuEventType_Unplug:
308 VBoxServiceCpuHotPlugHandleUnplugEvent(idCpuCore, idCpuPackage);
309 break;
310
311 default:
312 {
313 static uint32_t s_iErrors = 0;
314 if (s_iErrors++ < 10)
315 VBoxServiceError("CpuHotPlug: Unknown event: idCpuCore=%u idCpuPackage=%u enmEventType=%d\n",
316 idCpuCore, idCpuPackage, enmEventType);
317 break;
318 }
319 }
320 }
321 else if (rc != VERR_INTERRUPTED && rc != VERR_TRY_AGAIN)
322 {
323 VBoxServiceError("CpuHotPlug: VbglR3CpuHotPlugWaitForEvent returned %Rrc\n", rc);
324 break;
325 }
326
327 if (*pfShutdown)
328 break;
329 }
330
331 VbglR3CpuHotPlugTerm();
332 return rc;
333}
334
335
336/** @copydoc VBOXSERVICE::pfnStop */
337static DECLCALLBACK(void) VBoxServiceCpuHotPlugStop(void)
338{
339 VbglR3InterruptEventWaits();
340 return;
341}
342
343
344/** @copydoc VBOXSERVICE::pfnTerm */
345static DECLCALLBACK(void) VBoxServiceCpuHotPlugTerm(void)
346{
347 return;
348}
349
350
351/**
352 * The 'timesync' service description.
353 */
354VBOXSERVICE g_CpuHotPlug =
355{
356 /* pszName. */
357 "cpuhotplug",
358 /* pszDescription. */
359 "CPU hot plugging monitor",
360 /* pszUsage. */
361 NULL,
362 /* pszOptions. */
363 NULL,
364 /* methods */
365 VBoxServiceCpuHotPlugPreInit,
366 VBoxServiceCpuHotPlugOption,
367 VBoxServiceCpuHotPlugInit,
368 VBoxServiceCpuHotPlugWorker,
369 VBoxServiceCpuHotPlugStop,
370 VBoxServiceCpuHotPlugTerm
371};
372
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