VBoxServiceCpuHotPlug.cpp revision e64031e20c39650a7bc902a3e1aba613b9415dee
/* $Id$ */
/** @file
* VBoxService - Guest Additions CPU Hot Plugging Service.
*/
/*
* Copyright (C) 2010 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <VBox/VBoxGuestLib.h>
#include "VBoxServiceInternal.h"
#ifdef RT_OS_LINUX
# include <errno.h> /* For the sysfs API */
#endif
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
#ifdef RT_OS_LINUX
/** @name Paths to access the CPU device
* @{
*/
# define SYSFS_ACPI_CPU_PATH "/sys/devices/LNXSYSTM:00/device:00"
# define SYSFS_CPU_PATH "/sys/devices/system/cpu"
/** @} */
#endif
#ifdef RT_OS_LINUX
/**
* Returns the path of the ACPI CPU device with the given core and package ID.
*
* @returns VBox status code.
* @param ppszPath Where to store the path.
* @param idCpuCore The core ID of the CPU.
* @param idCpuPackage The package ID of the CPU.
*/
static int VBoxServiceCpuHotPlugGetACPIDevicePath(char **ppszPath, uint32_t idCpuCore, uint32_t idCpuPackage)
{
if (RT_SUCCESS(rc))
{
/* Search every ACPI0004 container device for LNXCPU devices. */
bool fFound = false;
&& !fFound) /* Assumption that szName has always enough space */
{
{
char *pszAcpiContainerPath = NULL;
rc = RTStrAPrintf(&pszAcpiContainerPath, "%s/%s", SYSFS_ACPI_CPU_PATH, DirFolderAcpiContainer.szName);
if (RT_FAILURE(rc))
break;
if (RT_SUCCESS(rc))
{
while (RT_SUCCESS(RTDirRead(pDirAcpiContainer, &DirFolderContent, NULL))) /* Assumption that szName has always enough space */
{
{
/* Get the sysdev */
&& idPackage == idCpuPackage)
{
/* Return the path */
fFound = true;
break;
}
}
}
}
}
}
}
return rc;
}
#endif /* RT_OS_LINUX */
/** @copydoc VBOXSERVICE::pfnPreInit */
static DECLCALLBACK(int) VBoxServiceCpuHotPlugPreInit(void)
{
return VINF_SUCCESS;
}
/** @copydoc VBOXSERVICE::pfnOption */
static DECLCALLBACK(int) VBoxServiceCpuHotPlugOption(const char **ppszShort, int argc, char **argv, int *pi)
{
return VINF_SUCCESS;
}
/** @copydoc VBOXSERVICE::pfnInit */
static DECLCALLBACK(int) VBoxServiceCpuHotPlugInit(void)
{
return VINF_SUCCESS;
}
/**
* Handles VMMDevCpuEventType_Plug.
*
* @param idCpuCore The CPU core ID.
* @param idCpuPackage The CPU package ID.
*/
{
#ifdef RT_OS_LINUX
/*
* The topology directory (containing the physical and core id properties)
* is not available until the CPU is online. So we just iterate over all directories
* and enable every CPU which is not online already.
* Because the directory might not be available immediately we try a few times.
*
* @todo: Maybe use udev to monitor hot-add events from the kernel
*/
bool fCpuOnline = false;
unsigned cTries = 5;
do
{
if (RT_SUCCESS(rc))
{
while (RT_SUCCESS(RTDirRead(pDirDevices, &DirFolderContent, NULL))) /* Assumption that szName has always enough space */
{
/** @todo r-bird: This code is bringing all CPUs online; the idCpuCore and
* idCpuPackage parameters are unused!
* aeichner: These files are not available at this point unfortunately. (see comment above)
* bird: Yes, but isn't that easily dealt with by doing:
* if (matching_topology() || !have_topology_directory())
* bring_cpu_online()
* That could save you the cpu0 and cpuidle checks to.
*/
/*
* Check if this is a CPU object.
* cpu0 is excluded because it is not possible to change the state
* of the first CPU on Linux (it doesn't even have an online file)
* and cpuidle is no CPU device. Prevents error messages later.
*/
{
/* Get the sysdev */
if (RT_SUCCESS(rc))
{
/* Write a 1 to online the CPU */
if (RT_SUCCESS(rc))
{
fCpuOnline = true;
break;
}
/* Error means CPU not present or online already */
}
else
VBoxServiceError("CpuHotPlug: Failed to open \"%s/%s/online\" rc=%Rrc\n",
}
}
}
else
/* Sleep a bit */
if (!fCpuOnline)
RTThreadSleep(10);
} while ( !fCpuOnline
&& cTries-- > 0);
#else
# error "Port me"
#endif
}
/**
* Handles VMMDevCpuEventType_Unplug.
*
* @param idCpuCore The CPU core ID.
* @param idCpuPackage The CPU package ID.
*/
{
#ifdef RT_OS_LINUX
char *pszCpuDevicePath = NULL;
if (RT_SUCCESS(rc))
{
"%s/eject", pszCpuDevicePath);
if (RT_SUCCESS(rc))
{
/* Write a 1 to eject the CPU */
if (RT_SUCCESS(rc))
else
}
else
}
else
#else
# error "Port me"
#endif
}
/** @copydoc VBOXSERVICE::pfnWorker */
{
/*
* Tell the control thread that it can continue spawning services.
*/
/*
* Enable the CPU hotplug notifier.
*/
int rc = VbglR3CpuHotPlugInit();
if (RT_FAILURE(rc))
return rc;
/*
* The Work Loop.
*/
for (;;)
{
/* Wait for CPU hot plugging event. */
if (RT_SUCCESS(rc))
{
switch (enmEventType)
{
case VMMDevCpuEventType_Plug:
break;
break;
default:
{
if (s_iErrors++ < 10)
VBoxServiceError("CpuHotPlug: Unknown event: idCpuCore=%u idCpuPackage=%u enmEventType=%d\n",
break;
}
}
}
{
break;
}
if (*pfShutdown)
break;
}
return rc;
}
/** @copydoc VBOXSERVICE::pfnStop */
static DECLCALLBACK(void) VBoxServiceCpuHotPlugStop(void)
{
return;
}
/** @copydoc VBOXSERVICE::pfnTerm */
static DECLCALLBACK(void) VBoxServiceCpuHotPlugTerm(void)
{
return;
}
/**
* The 'timesync' service description.
*/
{
/* pszName. */
"cpuhotplug",
/* pszDescription. */
"CPU hot plugging monitor",
/* pszUsage. */
NULL,
/* pszOptions. */
NULL,
/* methods */
};