VirtualBox

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

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

Check for ACPI_CPU because older kernels used that to identify CPU objects

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