VirtualBox

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

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

VBoxService,VBoxGuestLib: CPU hot plugging cleanup + review.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.4 KB
Line 
1/* $Id: VBoxServiceCpuHotPlug.cpp 26061 2010-01-27 10:18:30Z 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 RTDIRENTRY DirFolderContent;
71 while (RT_SUCCESS(RTDirRead(pDirDevices, &DirFolderContent, NULL))) /* Assumption that szName has always enough space */
72 {
73 if (!strncmp(DirFolderContent.szName, "LNXCPU", 6))
74 {
75 /* Get the sysdev */
76 uint32_t idCore = RTLinuxSysFsReadIntFile(10, "%s/%s/sysdev/topology/core_id",
77 SYSFS_ACPI_CPU_PATH, DirFolderContent.szName);
78 uint32_t idPackage = RTLinuxSysFsReadIntFile(10, "%s/%s/sysdev/topology/physical_package_id",
79 SYSFS_ACPI_CPU_PATH, DirFolderContent.szName);
80 if ( idCore == idCpuCore
81 && idPackage == idCpuPackage)
82 {
83 /* Return the path */
84 rc = RTStrAPrintf(ppszPath, "%s/%s", SYSFS_ACPI_CPU_PATH, DirFolderContent.szName);
85 break;
86 }
87 }
88 }
89
90 RTDirClose(pDirDevices);
91 }
92
93 return rc;
94}
95#endif /* RT_OS_LINUX */
96
97
98/** @copydoc VBOXSERVICE::pfnPreInit */
99static DECLCALLBACK(int) VBoxServiceCpuHotPlugPreInit(void)
100{
101 return VINF_SUCCESS;
102}
103
104
105/** @copydoc VBOXSERVICE::pfnOption */
106static DECLCALLBACK(int) VBoxServiceCpuHotPlugOption(const char **ppszShort, int argc, char **argv, int *pi)
107{
108 return VINF_SUCCESS;
109}
110
111
112/** @copydoc VBOXSERVICE::pfnInit */
113static DECLCALLBACK(int) VBoxServiceCpuHotPlugInit(void)
114{
115 return VINF_SUCCESS;
116}
117
118
119/**
120 * Handles VMMDevCpuEventType_Plug.
121 *
122 * @param idCpuCore The CPU core ID.
123 * @param idCpuPackage The CPU package ID.
124 */
125static void VBoxServiceCpuHotPlugHandlePlugEvent(uint32_t idCpuCore, uint32_t idCpuPackage)
126{
127#ifdef RT_OS_LINUX
128 /*
129 * The topology directory is not available until the CPU is online. So we just iterate over all directories
130 * and enable every CPU which is not online already.
131 */
132 /** @todo Maybe use udev to monitor events from the kernel */
133 PRTDIR pDirDevices = NULL;
134 int rc = RTDirOpen(&pDirDevices, SYSFS_CPU_PATH); /*could use RTDirOpenFiltered*/
135 if (RT_SUCCESS(rc))
136 {
137 RTDIRENTRY DirFolderContent;
138 while (RT_SUCCESS(RTDirRead(pDirDevices, &DirFolderContent, NULL))) /* Assumption that szName has always enough space */
139 {
140/** @todo r-bird: This code is bringing all CPUs online; the idCpuCore and
141 * idCpuPackage parameters are unused! */
142 /*
143 * Check if this is a CPU object.
144 * cpu0 is excluded because it is not possible to change the state
145 * of the first CPU on Linux (it doesn't even have an online file)
146 * and cpuidle is no CPU device. Prevents error messages later.
147 */
148 if( !strncmp(DirFolderContent.szName, "cpu", 3)
149 && strncmp(DirFolderContent.szName, "cpu0", 4)
150 && strncmp(DirFolderContent.szName, "cpuidle", 7))
151 {
152 /* Get the sysdev */
153 RTFILE hFileCpuOnline = NIL_RTFILE;
154 rc = RTFileOpenF(&hFileCpuOnline, RTFILE_O_WRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
155 "%s/%s/online", SYSFS_CPU_PATH, DirFolderContent.szName);
156 if (RT_SUCCESS(rc))
157 {
158 /* Write a 1 to online the CPU */
159 rc = RTFileWrite(hFileCpuOnline, "1", 1, NULL);
160 RTFileClose(hFileCpuOnline);
161 if (RT_SUCCESS(rc))
162 {
163 VBoxServiceVerbose(1, "CpuHotPlug: CPU %u/%u was brought online\n", idCpuPackage, idCpuCore);
164 break;
165 }
166 /* Error means CPU not present or online already */
167 }
168 else
169 VBoxServiceError("CpuHotPlug: Failed to open \"%s/%s/online\" rc=%Rrc\n",
170 SYSFS_CPU_PATH, DirFolderContent.szName, rc);
171 }
172 }
173 }
174 else
175 VBoxServiceError("CpuHotPlug: Failed to open path %s rc=%Rrc\n", SYSFS_CPU_PATH, rc);
176#else
177# error "Port me"
178#endif
179}
180
181
182/**
183 * Handles VMMDevCpuEventType_Unplug.
184 *
185 * @param idCpuCore The CPU core ID.
186 * @param idCpuPackage The CPU package ID.
187 */
188static void VBoxServiceCpuHotPlugHandleUnplugEvent(uint32_t idCpuCore, uint32_t idCpuPackage)
189{
190#ifdef RT_OS_LINUX
191 char *pszCpuDevicePath = NULL;
192 int rc = VBoxServiceCpuHotPlugGetACPIDevicePath(&pszCpuDevicePath, idCpuCore, idCpuPackage);
193 if (RT_SUCCESS(rc))
194 {
195 RTFILE hFileCpuEject;
196 rc = RTFileOpenF(&hFileCpuEject, RTFILE_O_WRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
197 "%s/eject", pszCpuDevicePath);
198 if (RT_SUCCESS(rc))
199 {
200 /* Write a 1 to eject the CPU */
201 rc = RTFileWrite(hFileCpuEject, "1", 1, NULL);
202 if (RT_SUCCESS(rc))
203 VBoxServiceVerbose(1, "CpuHotPlug: CPU %u/%u was ejected\n", idCpuPackage, idCpuCore);
204 else
205 VBoxServiceError("CpuHotPlug: Failed to eject CPU %u/%u rc=%Rrc\n", idCpuPackage, idCpuCore, rc);
206
207 RTFileClose(hFileCpuEject);
208 }
209 else
210 VBoxServiceError("CpuHotPlug: Failed to open \"%s/eject\" rc=%Rrc\n", pszCpuDevicePath, rc);
211 RTStrFree(pszCpuDevicePath);
212 }
213 else
214 VBoxServiceError("CpuHotPlug: Failed to get CPU device path rc=%Rrc\n", rc);
215#else
216# error "Port me"
217#endif
218}
219
220
221/** @copydoc VBOXSERVICE::pfnWorker */
222DECLCALLBACK(int) VBoxServiceCpuHotPlugWorker(bool volatile *pfShutdown)
223{
224 /*
225 * Tell the control thread that it can continue spawning services.
226 */
227 RTThreadUserSignal(RTThreadSelf());
228
229 /*
230 * Enable the CPU hotplug notifier.
231 */
232 int rc = VbglR3CpuHotPlugInit();
233 if (RT_FAILURE(rc))
234 return rc;
235
236 /*
237 * The Work Loop.
238 */
239 for (;;)
240 {
241 /* Wait for CPU hot plugging event. */
242 uint32_t idCpuCore;
243 uint32_t idCpuPackage;
244 VMMDevCpuEventType enmEventType;
245 rc = VbglR3CpuHotPlugWaitForEvent(&enmEventType, &idCpuCore, &idCpuPackage);
246 if (RT_SUCCESS(rc))
247 {
248 VBoxServiceVerbose(3, "CpuHotPlug: Event happened idCpuCore=%u idCpuPackage=%u enmEventType=%d\n",
249 idCpuCore, idCpuPackage, enmEventType);
250 switch (enmEventType)
251 {
252 case VMMDevCpuEventType_Plug:
253 VBoxServiceCpuHotPlugHandlePlugEvent(idCpuCore, idCpuPackage);
254 break;
255
256 case VMMDevCpuEventType_Unplug:
257 VBoxServiceCpuHotPlugHandleUnplugEvent(idCpuCore, idCpuPackage);
258 break;
259
260 default:
261 {
262 static uint32_t s_iErrors = 0;
263 if (s_iErrors++ < 10)
264 VBoxServiceError("CpuHotPlug: Unknown event: idCpuCore=%u idCpuPackage=%u enmEventType=%d\n",
265 idCpuCore, idCpuPackage, enmEventType);
266 break;
267 }
268 }
269 }
270 else if (rc != VERR_INTERRUPTED && rc != VERR_TRY_AGAIN)
271 {
272 VBoxServiceError("CpuHotPlug: VbglR3CpuHotPlugWaitForEvent returned %Rrc\n", rc);
273 break;
274 }
275
276 if (*pfShutdown)
277 break;
278 }
279
280 VbglR3CpuHotPlugTerm();
281 return rc;
282}
283
284
285/** @copydoc VBOXSERVICE::pfnStop */
286static DECLCALLBACK(void) VBoxServiceCpuHotPlugStop(void)
287{
288 VbglR3InterruptEventWaits();
289 return;
290}
291
292
293/** @copydoc VBOXSERVICE::pfnTerm */
294static DECLCALLBACK(void) VBoxServiceCpuHotPlugTerm(void)
295{
296 return;
297}
298
299
300/**
301 * The 'timesync' service description.
302 */
303VBOXSERVICE g_CpuHotPlug =
304{
305 /* pszName. */
306 "cpuhotplug",
307 /* pszDescription. */
308 "CPU hot plugging monitor",
309 /* pszUsage. */
310 NULL,
311 /* pszOptions. */
312 NULL,
313 /* methods */
314 VBoxServiceCpuHotPlugPreInit,
315 VBoxServiceCpuHotPlugOption,
316 VBoxServiceCpuHotPlugInit,
317 VBoxServiceCpuHotPlugWorker,
318 VBoxServiceCpuHotPlugStop,
319 VBoxServiceCpuHotPlugTerm
320};
321
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