VirtualBox

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

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

Guest Additions: memory ballooning for guests without support for RTR0MemObjAllocPhysNC()

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette