2016-04-13 17:53:02 +00:00
|
|
|
/*
|
|
|
|
* virhostcpu.c: helper APIs for host CPU info
|
|
|
|
*
|
|
|
|
* Copyright (C) 2006-2016 Red Hat, Inc.
|
|
|
|
* Copyright (C) 2006 Daniel P. Berrange
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library. If not, see
|
|
|
|
* <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
* Author: Daniel P. Berrange <berrange@redhat.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <sys/utsname.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#if HAVE_LINUX_KVM_H
|
|
|
|
# include <linux/kvm.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(__FreeBSD__) || defined(__APPLE__)
|
|
|
|
# include <sys/time.h>
|
|
|
|
# include <sys/types.h>
|
|
|
|
# include <sys/sysctl.h>
|
|
|
|
# include <sys/resource.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "c-ctype.h"
|
|
|
|
#include "viralloc.h"
|
|
|
|
#include "virhostcpupriv.h"
|
|
|
|
#include "physmem.h"
|
|
|
|
#include "virerror.h"
|
|
|
|
#include "count-one-bits.h"
|
|
|
|
#include "intprops.h"
|
|
|
|
#include "virarch.h"
|
|
|
|
#include "virfile.h"
|
|
|
|
#include "virtypedparam.h"
|
|
|
|
#include "virstring.h"
|
|
|
|
#include "virnuma.h"
|
|
|
|
#include "virlog.h"
|
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_NONE
|
|
|
|
|
|
|
|
VIR_LOG_INIT("util.hostcpu");
|
|
|
|
|
2016-06-24 15:03:13 +00:00
|
|
|
#define KVM_DEVICE "/dev/kvm"
|
|
|
|
|
2016-04-13 17:53:02 +00:00
|
|
|
|
|
|
|
#if defined(__FreeBSD__) || defined(__APPLE__)
|
|
|
|
static int
|
|
|
|
virHostCPUGetCountAppleFreeBSD(void)
|
|
|
|
{
|
|
|
|
int ncpu_mib[2] = { CTL_HW, HW_NCPU };
|
|
|
|
unsigned long ncpu;
|
|
|
|
size_t ncpu_len = sizeof(ncpu);
|
|
|
|
|
|
|
|
if (sysctl(ncpu_mib, 2, &ncpu, &ncpu_len, NULL, 0) == -1) {
|
|
|
|
virReportSystemError(errno, "%s", _("Cannot obtain CPU count"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ncpu;
|
|
|
|
}
|
|
|
|
#endif /* defined(__FreeBSD__) || defined(__APPLE__) */
|
|
|
|
|
|
|
|
#ifdef __FreeBSD__
|
|
|
|
# define BSD_CPU_STATS_ALL 4
|
|
|
|
# define BSD_MEMORY_STATS_ALL 4
|
|
|
|
|
|
|
|
# define TICK_TO_NSEC (1000ull * 1000ull * 1000ull / (stathz ? stathz : hz))
|
|
|
|
|
|
|
|
static int
|
2016-06-12 07:53:23 +00:00
|
|
|
virHostCPUGetStatsFreeBSD(int cpuNum,
|
2016-04-13 17:53:02 +00:00
|
|
|
virNodeCPUStatsPtr params,
|
|
|
|
int *nparams)
|
|
|
|
{
|
|
|
|
const char *sysctl_name;
|
|
|
|
long *cpu_times;
|
|
|
|
struct clockinfo clkinfo;
|
|
|
|
size_t i, j, cpu_times_size, clkinfo_size;
|
|
|
|
int cpu_times_num, offset, hz, stathz, ret = -1;
|
|
|
|
struct field_cpu_map {
|
|
|
|
const char *field;
|
|
|
|
int idx[CPUSTATES];
|
|
|
|
} cpu_map[] = {
|
|
|
|
{VIR_NODE_CPU_STATS_KERNEL, {CP_SYS}},
|
|
|
|
{VIR_NODE_CPU_STATS_USER, {CP_USER, CP_NICE}},
|
|
|
|
{VIR_NODE_CPU_STATS_IDLE, {CP_IDLE}},
|
|
|
|
{VIR_NODE_CPU_STATS_INTR, {CP_INTR}},
|
|
|
|
{NULL, {0}}
|
|
|
|
};
|
|
|
|
|
|
|
|
if ((*nparams) == 0) {
|
|
|
|
*nparams = BSD_CPU_STATS_ALL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((*nparams) != BSD_CPU_STATS_ALL) {
|
|
|
|
virReportInvalidArg(*nparams,
|
|
|
|
_("nparams in %s must be equal to %d"),
|
|
|
|
__FUNCTION__, BSD_CPU_STATS_ALL);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
clkinfo_size = sizeof(clkinfo);
|
|
|
|
if (sysctlbyname("kern.clockrate", &clkinfo, &clkinfo_size, NULL, 0) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("sysctl failed for '%s'"),
|
|
|
|
"kern.clockrate");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
stathz = clkinfo.stathz;
|
|
|
|
hz = clkinfo.hz;
|
|
|
|
|
|
|
|
if (cpuNum == VIR_NODE_CPU_STATS_ALL_CPUS) {
|
|
|
|
sysctl_name = "kern.cp_time";
|
|
|
|
cpu_times_num = 1;
|
|
|
|
offset = 0;
|
|
|
|
} else {
|
|
|
|
sysctl_name = "kern.cp_times";
|
|
|
|
cpu_times_num = virHostCPUGetCountAppleFreeBSD();
|
|
|
|
|
|
|
|
if (cpuNum >= cpu_times_num) {
|
|
|
|
virReportInvalidArg(cpuNum,
|
|
|
|
_("Invalid cpuNum in %s"),
|
|
|
|
__FUNCTION__);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
offset = cpu_times_num * CPUSTATES;
|
|
|
|
}
|
|
|
|
|
|
|
|
cpu_times_size = sizeof(long) * cpu_times_num * CPUSTATES;
|
|
|
|
|
|
|
|
if (VIR_ALLOC_N(cpu_times, cpu_times_num * CPUSTATES) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (sysctlbyname(sysctl_name, cpu_times, &cpu_times_size, NULL, 0) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("sysctl failed for '%s'"),
|
|
|
|
sysctl_name);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; cpu_map[i].field != NULL; i++) {
|
|
|
|
virNodeCPUStatsPtr param = ¶ms[i];
|
|
|
|
|
|
|
|
if (virStrcpyStatic(param->field, cpu_map[i].field) == NULL) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Field '%s' too long for destination"),
|
|
|
|
cpu_map[i].field);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
param->value = 0;
|
|
|
|
for (j = 0; j < ARRAY_CARDINALITY(cpu_map[i].idx); j++)
|
|
|
|
param->value += cpu_times[offset + cpu_map[i].idx[j]] * TICK_TO_NSEC;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(cpu_times);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* __FreeBSD__ */
|
|
|
|
|
|
|
|
#ifdef __linux__
|
|
|
|
# define SYSFS_SYSTEM_PATH "/sys/devices/system"
|
|
|
|
# define CPUINFO_PATH "/proc/cpuinfo"
|
|
|
|
# define PROCSTAT_PATH "/proc/stat"
|
|
|
|
# define SYSFS_THREAD_SIBLINGS_LIST_LENGTH_MAX 8192
|
|
|
|
|
|
|
|
# define LINUX_NB_CPU_STATS 4
|
|
|
|
|
|
|
|
static const char *sysfs_system_path = SYSFS_SYSTEM_PATH;
|
|
|
|
|
|
|
|
void virHostCPUSetSysFSSystemPathLinux(const char *path)
|
|
|
|
{
|
|
|
|
if (path)
|
|
|
|
sysfs_system_path = path;
|
|
|
|
else
|
|
|
|
sysfs_system_path = SYSFS_SYSTEM_PATH;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return the positive decimal contents of the given
|
|
|
|
* DIR/cpu%u/FILE, or -1 on error. If DEFAULT_VALUE is non-negative
|
|
|
|
* and the file could not be found, return that instead of an error;
|
|
|
|
* this is useful for machines that cannot hot-unplug cpu0, or where
|
|
|
|
* hot-unplugging is disabled, or where the kernel is too old
|
|
|
|
* to support NUMA cells, etc. */
|
|
|
|
static int
|
|
|
|
virHostCPUGetValue(const char *dir, unsigned int cpu, const char *file,
|
|
|
|
int default_value)
|
|
|
|
{
|
|
|
|
char *path;
|
|
|
|
FILE *pathfp;
|
|
|
|
int value = -1;
|
|
|
|
char value_str[INT_BUFSIZE_BOUND(value)];
|
|
|
|
char *tmp;
|
|
|
|
|
|
|
|
if (virAsprintf(&path, "%s/cpu%u/%s", dir, cpu, file) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
pathfp = fopen(path, "r");
|
|
|
|
if (pathfp == NULL) {
|
|
|
|
if (default_value >= 0 && errno == ENOENT)
|
|
|
|
value = default_value;
|
|
|
|
else
|
|
|
|
virReportSystemError(errno, _("cannot open %s"), path);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fgets(value_str, sizeof(value_str), pathfp) == NULL) {
|
|
|
|
virReportSystemError(errno, _("cannot read from %s"), path);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (virStrToLong_i(value_str, &tmp, 10, &value) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("could not convert '%s' to an integer"),
|
|
|
|
value_str);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FORCE_FCLOSE(pathfp);
|
|
|
|
VIR_FREE(path);
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned long
|
|
|
|
virHostCPUCountThreadSiblings(const char *dir, unsigned int cpu)
|
|
|
|
{
|
|
|
|
unsigned long ret = 0;
|
|
|
|
char *path;
|
|
|
|
char *str = NULL;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (virAsprintf(&path, "%s/cpu%u/topology/thread_siblings",
|
|
|
|
dir, cpu) < 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!virFileExists(path)) {
|
|
|
|
/* If file doesn't exist, then pretend our only
|
|
|
|
* sibling is ourself */
|
|
|
|
ret = 1;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virFileReadAll(path, SYSFS_THREAD_SIBLINGS_LIST_LENGTH_MAX, &str) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
for (i = 0; str[i] != '\0'; i++) {
|
|
|
|
if (c_isxdigit(str[i]))
|
|
|
|
ret += count_one_bits(virHexToBin(str[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(str);
|
|
|
|
VIR_FREE(path);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
virHostCPUParseSocket(const char *dir,
|
|
|
|
virArch arch,
|
|
|
|
unsigned int cpu)
|
|
|
|
{
|
|
|
|
int ret = virHostCPUGetValue(dir, cpu, "topology/physical_package_id", 0);
|
|
|
|
|
|
|
|
if (ARCH_IS_ARM(arch) || ARCH_IS_PPC(arch) || ARCH_IS_S390(arch)) {
|
|
|
|
/* arm, ppc and s390(x) has -1 */
|
|
|
|
if (ret < 0)
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* parses a node entry, returning number of processors in the node and
|
|
|
|
* filling arguments */
|
|
|
|
static int
|
|
|
|
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3)
|
|
|
|
ATTRIBUTE_NONNULL(4) ATTRIBUTE_NONNULL(6)
|
|
|
|
ATTRIBUTE_NONNULL(7) ATTRIBUTE_NONNULL(8)
|
|
|
|
ATTRIBUTE_NONNULL(9)
|
|
|
|
virHostCPUParseNode(const char *node,
|
|
|
|
virArch arch,
|
|
|
|
virBitmapPtr present_cpus_map,
|
|
|
|
virBitmapPtr online_cpus_map,
|
|
|
|
int threads_per_subcore,
|
|
|
|
int *sockets,
|
|
|
|
int *cores,
|
|
|
|
int *threads,
|
|
|
|
int *offline)
|
|
|
|
{
|
|
|
|
/* Biggest value we can expect to be used as either socket id
|
|
|
|
* or core id. Bitmaps will need to be sized accordingly */
|
|
|
|
const int ID_MAX = 4095;
|
|
|
|
int ret = -1;
|
|
|
|
int processors = 0;
|
|
|
|
DIR *cpudir = NULL;
|
|
|
|
struct dirent *cpudirent = NULL;
|
|
|
|
virBitmapPtr node_cpus_map = NULL;
|
|
|
|
virBitmapPtr sockets_map = NULL;
|
|
|
|
virBitmapPtr *cores_maps = NULL;
|
|
|
|
int npresent_cpus = virBitmapSize(present_cpus_map);
|
|
|
|
int sock_max = 0;
|
|
|
|
int sock;
|
|
|
|
int core;
|
|
|
|
size_t i;
|
|
|
|
int siblings;
|
|
|
|
unsigned int cpu;
|
|
|
|
int direrr;
|
|
|
|
|
|
|
|
*threads = 0;
|
|
|
|
*cores = 0;
|
|
|
|
*sockets = 0;
|
|
|
|
|
2016-06-21 14:34:08 +00:00
|
|
|
if (virDirOpen(&cpudir, node) < 0)
|
2016-04-13 17:53:02 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* Keep track of the CPUs that belong to the current node */
|
|
|
|
if (!(node_cpus_map = virBitmapNew(npresent_cpus)))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* enumerate sockets in the node */
|
|
|
|
if (!(sockets_map = virBitmapNew(ID_MAX + 1)))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
while ((direrr = virDirRead(cpudir, &cpudirent, node)) > 0) {
|
|
|
|
if (sscanf(cpudirent->d_name, "cpu%u", &cpu) != 1)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!virBitmapIsBitSet(present_cpus_map, cpu))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Mark this CPU as part of the current node */
|
|
|
|
if (virBitmapSetBit(node_cpus_map, cpu) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!virBitmapIsBitSet(online_cpus_map, cpu))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Parse socket */
|
|
|
|
if ((sock = virHostCPUParseSocket(node, arch, cpu)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (sock > ID_MAX) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Socket %d can't be handled (max socket is %d)"),
|
|
|
|
sock, ID_MAX);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virBitmapSetBit(sockets_map, sock) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (sock > sock_max)
|
|
|
|
sock_max = sock;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (direrr < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
sock_max++;
|
|
|
|
|
|
|
|
/* allocate cores maps for each socket */
|
|
|
|
if (VIR_ALLOC_N(cores_maps, sock_max) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
for (i = 0; i < sock_max; i++)
|
|
|
|
if (!(cores_maps[i] = virBitmapNew(ID_MAX + 1)))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* Iterate over all CPUs in the node, in ascending order */
|
|
|
|
for (cpu = 0; cpu < npresent_cpus; cpu++) {
|
|
|
|
|
|
|
|
/* Skip CPUs that are not part of the current node */
|
|
|
|
if (!virBitmapIsBitSet(node_cpus_map, cpu))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!virBitmapIsBitSet(online_cpus_map, cpu)) {
|
|
|
|
if (threads_per_subcore > 0 &&
|
|
|
|
cpu % threads_per_subcore != 0 &&
|
|
|
|
virBitmapIsBitSet(online_cpus_map,
|
|
|
|
cpu - (cpu % threads_per_subcore))) {
|
|
|
|
/* Secondary offline threads are counted as online when
|
|
|
|
* subcores are in use and the corresponding primary
|
|
|
|
* thread is online */
|
|
|
|
processors++;
|
|
|
|
} else {
|
|
|
|
/* But they are counted as offline otherwise */
|
|
|
|
(*offline)++;
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
processors++;
|
|
|
|
|
|
|
|
/* Parse socket */
|
|
|
|
if ((sock = virHostCPUParseSocket(node, arch, cpu)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (!virBitmapIsBitSet(sockets_map, sock)) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("CPU socket topology has changed"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse core */
|
|
|
|
if (ARCH_IS_S390(arch)) {
|
|
|
|
/* logical cpu is equivalent to a core on s390 */
|
|
|
|
core = cpu;
|
|
|
|
} else {
|
|
|
|
if ((core = virHostCPUGetValue(node, cpu,
|
|
|
|
"topology/core_id", 0)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (core > ID_MAX) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Core %d can't be handled (max core is %d)"),
|
|
|
|
core, ID_MAX);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virBitmapSetBit(cores_maps[sock], core) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!(siblings = virHostCPUCountThreadSiblings(node, cpu)))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (siblings > *threads)
|
|
|
|
*threads = siblings;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* finalize the returned data */
|
|
|
|
*sockets = virBitmapCountBits(sockets_map);
|
|
|
|
|
|
|
|
for (i = 0; i < sock_max; i++) {
|
|
|
|
if (!virBitmapIsBitSet(sockets_map, i))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
core = virBitmapCountBits(cores_maps[i]);
|
|
|
|
if (core > *cores)
|
|
|
|
*cores = core;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (threads_per_subcore > 0) {
|
|
|
|
/* The thread count ignores offline threads, which means that only
|
|
|
|
* only primary threads have been considered so far. If subcores
|
|
|
|
* are in use, we need to also account for secondary threads */
|
|
|
|
*threads *= threads_per_subcore;
|
|
|
|
}
|
|
|
|
ret = processors;
|
|
|
|
|
|
|
|
cleanup:
|
2016-06-21 10:40:29 +00:00
|
|
|
VIR_DIR_CLOSE(cpudir);
|
2016-04-13 17:53:02 +00:00
|
|
|
if (cores_maps)
|
|
|
|
for (i = 0; i < sock_max; i++)
|
|
|
|
virBitmapFree(cores_maps[i]);
|
|
|
|
VIR_FREE(cores_maps);
|
|
|
|
virBitmapFree(sockets_map);
|
|
|
|
virBitmapFree(node_cpus_map);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check whether the host subcore configuration is valid.
|
|
|
|
*
|
|
|
|
* A valid configuration is one where no secondary thread is online;
|
|
|
|
* the primary thread in a subcore is always the first one */
|
|
|
|
static bool
|
|
|
|
virHostCPUHasValidSubcoreConfiguration(int threads_per_subcore)
|
|
|
|
{
|
|
|
|
virBitmapPtr online_cpus = NULL;
|
|
|
|
int cpu = -1;
|
|
|
|
bool ret = false;
|
|
|
|
|
|
|
|
/* No point in checking if subcores are not in use */
|
|
|
|
if (threads_per_subcore <= 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!(online_cpus = virHostCPUGetOnlineBitmap()))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
while ((cpu = virBitmapNextSetBit(online_cpus, cpu)) >= 0) {
|
|
|
|
|
|
|
|
/* A single online secondary thread is enough to
|
|
|
|
* make the configuration invalid */
|
|
|
|
if (cpu % threads_per_subcore != 0)
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = true;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
virBitmapFree(online_cpus);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
virHostCPUGetInfoPopulateLinux(FILE *cpuinfo,
|
|
|
|
virArch arch,
|
|
|
|
unsigned int *cpus,
|
|
|
|
unsigned int *mhz,
|
|
|
|
unsigned int *nodes,
|
|
|
|
unsigned int *sockets,
|
|
|
|
unsigned int *cores,
|
|
|
|
unsigned int *threads)
|
|
|
|
{
|
|
|
|
virBitmapPtr present_cpus_map = NULL;
|
|
|
|
virBitmapPtr online_cpus_map = NULL;
|
|
|
|
char line[1024];
|
|
|
|
DIR *nodedir = NULL;
|
|
|
|
struct dirent *nodedirent = NULL;
|
|
|
|
int nodecpus, nodecores, nodesockets, nodethreads, offline = 0;
|
|
|
|
int threads_per_subcore = 0;
|
|
|
|
unsigned int node;
|
|
|
|
int ret = -1;
|
|
|
|
char *sysfs_nodedir = NULL;
|
|
|
|
char *sysfs_cpudir = NULL;
|
|
|
|
int direrr;
|
|
|
|
|
|
|
|
*mhz = 0;
|
|
|
|
*cpus = *nodes = *sockets = *cores = *threads = 0;
|
|
|
|
|
|
|
|
/* Start with parsing CPU clock speed from /proc/cpuinfo */
|
|
|
|
while (fgets(line, sizeof(line), cpuinfo) != NULL) {
|
|
|
|
if (ARCH_IS_X86(arch)) {
|
|
|
|
char *buf = line;
|
|
|
|
if (STRPREFIX(buf, "cpu MHz")) {
|
|
|
|
char *p;
|
|
|
|
unsigned int ui;
|
|
|
|
|
|
|
|
buf += 7;
|
|
|
|
while (*buf && c_isspace(*buf))
|
|
|
|
buf++;
|
|
|
|
|
|
|
|
if (*buf != ':' || !buf[1]) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("parsing cpu MHz from cpuinfo"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virStrToLong_ui(buf+1, &p, 10, &ui) == 0 &&
|
|
|
|
/* Accept trailing fractional part. */
|
|
|
|
(*p == '\0' || *p == '.' || c_isspace(*p)))
|
|
|
|
*mhz = ui;
|
|
|
|
}
|
|
|
|
} else if (ARCH_IS_PPC(arch)) {
|
|
|
|
char *buf = line;
|
|
|
|
if (STRPREFIX(buf, "clock")) {
|
|
|
|
char *p;
|
|
|
|
unsigned int ui;
|
|
|
|
|
|
|
|
buf += 5;
|
|
|
|
while (*buf && c_isspace(*buf))
|
|
|
|
buf++;
|
|
|
|
|
|
|
|
if (*buf != ':' || !buf[1]) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("parsing cpu MHz from cpuinfo"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virStrToLong_ui(buf+1, &p, 10, &ui) == 0 &&
|
|
|
|
/* Accept trailing fractional part. */
|
|
|
|
(*p == '\0' || *p == '.' || c_isspace(*p)))
|
|
|
|
*mhz = ui;
|
|
|
|
/* No other interesting infos are available in /proc/cpuinfo.
|
|
|
|
* However, there is a line identifying processor's version,
|
|
|
|
* identification and machine, but we don't want it to be caught
|
|
|
|
* and parsed in next iteration, because it is not in expected
|
|
|
|
* format and thus lead to error. */
|
|
|
|
}
|
|
|
|
} else if (ARCH_IS_ARM(arch)) {
|
|
|
|
char *buf = line;
|
|
|
|
if (STRPREFIX(buf, "BogoMIPS")) {
|
|
|
|
char *p;
|
|
|
|
unsigned int ui;
|
|
|
|
|
|
|
|
buf += 8;
|
|
|
|
while (*buf && c_isspace(*buf))
|
|
|
|
buf++;
|
|
|
|
|
|
|
|
if (*buf != ':' || !buf[1]) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("parsing cpu MHz from cpuinfo"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virStrToLong_ui(buf+1, &p, 10, &ui) == 0
|
|
|
|
/* Accept trailing fractional part. */
|
|
|
|
&& (*p == '\0' || *p == '.' || c_isspace(*p)))
|
|
|
|
*mhz = ui;
|
|
|
|
}
|
|
|
|
} else if (ARCH_IS_S390(arch)) {
|
|
|
|
/* s390x has no realistic value for CPU speed,
|
|
|
|
* assign a value of zero to signify this */
|
|
|
|
*mhz = 0;
|
|
|
|
} else {
|
|
|
|
VIR_WARN("Parser for /proc/cpuinfo needs to be adapted for your architecture");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get information about what CPUs are present in the host and what
|
|
|
|
* CPUs are online, so that we don't have to so for each node */
|
|
|
|
present_cpus_map = virHostCPUGetPresentBitmap();
|
|
|
|
if (!present_cpus_map)
|
|
|
|
goto cleanup;
|
|
|
|
online_cpus_map = virHostCPUGetOnlineBitmap();
|
|
|
|
if (!online_cpus_map)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* OK, we've parsed clock speed out of /proc/cpuinfo. Get the
|
|
|
|
* core, node, socket, thread and topology information from /sys
|
|
|
|
*/
|
|
|
|
if (virAsprintf(&sysfs_nodedir, "%s/node", sysfs_system_path) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
2016-06-21 14:52:37 +00:00
|
|
|
if (virDirOpenQuiet(&nodedir, sysfs_nodedir) < 0) {
|
2016-04-13 17:53:02 +00:00
|
|
|
/* the host isn't probably running a NUMA architecture */
|
|
|
|
goto fallback;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* PPC-KVM needs the secondary threads of a core to be offline on the
|
|
|
|
* host. The kvm scheduler brings the secondary threads online in the
|
|
|
|
* guest context. Moreover, P8 processor has split-core capability
|
|
|
|
* where, there can be 1,2 or 4 subcores per core. The primaries of the
|
|
|
|
* subcores alone will be online on the host for a subcore in the
|
|
|
|
* host. Even though the actual threads per core for P8 processor is 8,
|
|
|
|
* depending on the subcores_per_core = 1, 2 or 4, the threads per
|
2016-11-15 14:00:08 +00:00
|
|
|
* subcore will vary accordingly to 8, 4 and 2 respectively.
|
2016-04-13 17:53:02 +00:00
|
|
|
* So, On host threads_per_core what is arrived at from sysfs in the
|
|
|
|
* current logic is actually the subcores_per_core. Threads per subcore
|
|
|
|
* can only be obtained from the kvm device. For example, on P8 wih 1
|
|
|
|
* core having 8 threads, sub_cores_percore=4, the threads 0,2,4 & 6
|
|
|
|
* will be online. The sysfs reflects this and in the current logic
|
|
|
|
* variable 'threads' will be 4 which is nothing but subcores_per_core.
|
|
|
|
* If the user tampers the cpu online/offline states using chcpu or other
|
|
|
|
* means, then it is an unsupported configuration for kvm.
|
|
|
|
* The code below tries to keep in mind
|
|
|
|
* - when the libvirtd is run inside a KVM guest or Phyp based guest.
|
|
|
|
* - Or on the kvm host where user manually tampers the cpu states to
|
|
|
|
* offline/online randomly.
|
|
|
|
* On hosts other than POWER this will be 0, in which case a simpler
|
|
|
|
* thread-counting logic will be used */
|
|
|
|
if ((threads_per_subcore = virHostCPUGetThreadsPerSubcore(arch)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* If the subcore configuration is not valid, just pretend subcores
|
|
|
|
* are not in use and count threads one by one */
|
|
|
|
if (!virHostCPUHasValidSubcoreConfiguration(threads_per_subcore))
|
|
|
|
threads_per_subcore = 0;
|
|
|
|
|
|
|
|
while ((direrr = virDirRead(nodedir, &nodedirent, sysfs_nodedir)) > 0) {
|
|
|
|
if (sscanf(nodedirent->d_name, "node%u", &node) != 1)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
(*nodes)++;
|
|
|
|
|
|
|
|
if (virAsprintf(&sysfs_cpudir, "%s/node/%s",
|
|
|
|
sysfs_system_path, nodedirent->d_name) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if ((nodecpus = virHostCPUParseNode(sysfs_cpudir, arch,
|
|
|
|
present_cpus_map,
|
|
|
|
online_cpus_map,
|
|
|
|
threads_per_subcore,
|
|
|
|
&nodesockets, &nodecores,
|
|
|
|
&nodethreads, &offline)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
VIR_FREE(sysfs_cpudir);
|
|
|
|
|
|
|
|
*cpus += nodecpus;
|
|
|
|
|
|
|
|
if (nodesockets > *sockets)
|
|
|
|
*sockets = nodesockets;
|
|
|
|
|
|
|
|
if (nodecores > *cores)
|
|
|
|
*cores = nodecores;
|
|
|
|
|
|
|
|
if (nodethreads > *threads)
|
|
|
|
*threads = nodethreads;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (direrr < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (*cpus && *nodes)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
fallback:
|
|
|
|
VIR_FREE(sysfs_cpudir);
|
|
|
|
|
|
|
|
if (virAsprintf(&sysfs_cpudir, "%s/cpu", sysfs_system_path) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if ((nodecpus = virHostCPUParseNode(sysfs_cpudir, arch,
|
|
|
|
present_cpus_map,
|
|
|
|
online_cpus_map,
|
|
|
|
threads_per_subcore,
|
|
|
|
&nodesockets, &nodecores,
|
|
|
|
&nodethreads, &offline)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
*nodes = 1;
|
|
|
|
*cpus = nodecpus;
|
|
|
|
*sockets = nodesockets;
|
|
|
|
*cores = nodecores;
|
|
|
|
*threads = nodethreads;
|
|
|
|
|
|
|
|
done:
|
|
|
|
/* There should always be at least one cpu, socket, node, and thread. */
|
|
|
|
if (*cpus == 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("no CPUs found"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*sockets == 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("no sockets found"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*threads == 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("no threads found"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now check if the topology makes sense. There are machines that don't
|
|
|
|
* expose their real number of nodes or for example the AMD Bulldozer
|
|
|
|
* architecture that exposes their Clustered integer core modules as both
|
|
|
|
* threads and cores. This approach throws off our detection. Unfortunately
|
|
|
|
* the nodeinfo structure isn't designed to carry the full topology so
|
|
|
|
* we're going to lie about the detected topology to notify the user
|
|
|
|
* to check the host capabilities for the actual topology. */
|
|
|
|
if ((*nodes *
|
|
|
|
*sockets *
|
|
|
|
*cores *
|
|
|
|
*threads) != (*cpus + offline)) {
|
|
|
|
*nodes = 1;
|
|
|
|
*sockets = 1;
|
|
|
|
*cores = *cpus + offline;
|
|
|
|
*threads = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
2016-06-21 10:40:29 +00:00
|
|
|
VIR_DIR_CLOSE(nodedir);
|
2016-04-13 17:53:02 +00:00
|
|
|
virBitmapFree(present_cpus_map);
|
|
|
|
virBitmapFree(online_cpus_map);
|
|
|
|
VIR_FREE(sysfs_nodedir);
|
|
|
|
VIR_FREE(sysfs_cpudir);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
# define TICK_TO_NSEC (1000ull * 1000ull * 1000ull / sysconf(_SC_CLK_TCK))
|
|
|
|
|
|
|
|
int
|
|
|
|
virHostCPUGetStatsLinux(FILE *procstat,
|
|
|
|
int cpuNum,
|
|
|
|
virNodeCPUStatsPtr params,
|
|
|
|
int *nparams)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
char line[1024];
|
|
|
|
unsigned long long usr, ni, sys, idle, iowait;
|
|
|
|
unsigned long long irq, softirq, steal, guest, guest_nice;
|
|
|
|
char cpu_header[4 + INT_BUFSIZE_BOUND(cpuNum)];
|
|
|
|
|
|
|
|
if ((*nparams) == 0) {
|
|
|
|
/* Current number of cpu stats supported by linux */
|
|
|
|
*nparams = LINUX_NB_CPU_STATS;
|
|
|
|
ret = 0;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((*nparams) != LINUX_NB_CPU_STATS) {
|
|
|
|
virReportInvalidArg(*nparams,
|
|
|
|
_("nparams in %s must be equal to %d"),
|
|
|
|
__FUNCTION__, LINUX_NB_CPU_STATS);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cpuNum == VIR_NODE_CPU_STATS_ALL_CPUS) {
|
|
|
|
strcpy(cpu_header, "cpu ");
|
|
|
|
} else {
|
|
|
|
snprintf(cpu_header, sizeof(cpu_header), "cpu%d ", cpuNum);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (fgets(line, sizeof(line), procstat) != NULL) {
|
|
|
|
char *buf = line;
|
|
|
|
|
|
|
|
if (STRPREFIX(buf, cpu_header)) { /* aka logical CPU time */
|
|
|
|
if (sscanf(buf,
|
|
|
|
"%*s %llu %llu %llu %llu %llu" // user ~ iowait
|
|
|
|
"%llu %llu %llu %llu %llu", // irq ~ guest_nice
|
|
|
|
&usr, &ni, &sys, &idle, &iowait,
|
|
|
|
&irq, &softirq, &steal, &guest, &guest_nice) < 4) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virHostCPUStatsAssign(¶ms[0], VIR_NODE_CPU_STATS_KERNEL,
|
|
|
|
(sys + irq + softirq) * TICK_TO_NSEC) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virHostCPUStatsAssign(¶ms[1], VIR_NODE_CPU_STATS_USER,
|
|
|
|
(usr + ni) * TICK_TO_NSEC) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virHostCPUStatsAssign(¶ms[2], VIR_NODE_CPU_STATS_IDLE,
|
|
|
|
idle * TICK_TO_NSEC) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virHostCPUStatsAssign(¶ms[3], VIR_NODE_CPU_STATS_IOWAIT,
|
|
|
|
iowait * TICK_TO_NSEC) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virReportInvalidArg(cpuNum,
|
|
|
|
_("Invalid cpuNum in %s"),
|
|
|
|
__FUNCTION__);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
virHostCPUGetGlobalPathLinux(const char *file)
|
|
|
|
{
|
|
|
|
char *path = NULL;
|
|
|
|
|
|
|
|
if (virAsprintf(&path, "%s/cpu/%s", sysfs_system_path, file) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
virHostCPUGetPresentPathLinux(void)
|
|
|
|
{
|
|
|
|
return virHostCPUGetGlobalPathLinux("present");
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *
|
|
|
|
virHostCPUGetOnlinePathLinux(void)
|
|
|
|
{
|
|
|
|
return virHostCPUGetGlobalPathLinux("online");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Determine the number of CPUs (maximum CPU id + 1) from a file containing
|
|
|
|
* a list of CPU ids, like the Linux sysfs cpu/present file */
|
|
|
|
static int
|
|
|
|
virHostCPUParseCountLinux(const char *path)
|
|
|
|
{
|
|
|
|
char *str = NULL;
|
|
|
|
char *tmp;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (virFileReadAll(path, 5 * VIR_HOST_CPU_MASK_LEN, &str) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
tmp = str;
|
|
|
|
do {
|
|
|
|
if (virStrToLong_i(tmp, &tmp, 10, &ret) < 0 ||
|
|
|
|
!strchr(",-\n", *tmp)) {
|
|
|
|
virReportError(VIR_ERR_NO_SUPPORT,
|
|
|
|
_("failed to parse %s"), path);
|
|
|
|
ret = -1;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
} while (*tmp++ != '\n');
|
|
|
|
ret++;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(str);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Linux maintains cpu bit map under cpu/online. For example, if
|
|
|
|
* cpuid=5's flag is not set and max cpu is 7, the map file shows
|
|
|
|
* 0-4,6-7. This function parses it and returns cpumap.
|
|
|
|
*/
|
|
|
|
static virBitmapPtr
|
|
|
|
virHostCPUParseMapLinux(int max_cpuid, const char *path)
|
|
|
|
{
|
|
|
|
virBitmapPtr map = NULL;
|
|
|
|
char *str = NULL;
|
|
|
|
|
|
|
|
if (virFileReadAll(path, 5 * VIR_HOST_CPU_MASK_LEN, &str) < 0)
|
|
|
|
goto error;
|
|
|
|
|
2016-06-17 12:56:45 +00:00
|
|
|
if (virBitmapParse(str, &map, max_cpuid) < 0)
|
2016-04-13 17:53:02 +00:00
|
|
|
goto error;
|
|
|
|
|
|
|
|
VIR_FREE(str);
|
|
|
|
return map;
|
|
|
|
|
|
|
|
error:
|
|
|
|
VIR_FREE(str);
|
|
|
|
virBitmapFree(map);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2016-09-16 06:33:47 +00:00
|
|
|
int
|
|
|
|
virHostCPUStatsAssign(virNodeCPUStatsPtr param,
|
|
|
|
const char *name,
|
|
|
|
unsigned long long value)
|
|
|
|
{
|
|
|
|
if (virStrcpyStatic(param->field, name) == NULL) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
"%s", _("kernel cpu time field is too long"
|
|
|
|
" for the destination"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
param->value = value;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-13 17:53:02 +00:00
|
|
|
int
|
2016-06-12 07:53:23 +00:00
|
|
|
virHostCPUGetInfo(virArch hostarch ATTRIBUTE_UNUSED,
|
2016-06-14 14:46:06 +00:00
|
|
|
unsigned int *cpus ATTRIBUTE_UNUSED,
|
|
|
|
unsigned int *mhz ATTRIBUTE_UNUSED,
|
|
|
|
unsigned int *nodes ATTRIBUTE_UNUSED,
|
|
|
|
unsigned int *sockets ATTRIBUTE_UNUSED,
|
|
|
|
unsigned int *cores ATTRIBUTE_UNUSED,
|
|
|
|
unsigned int *threads ATTRIBUTE_UNUSED)
|
2016-04-13 17:53:02 +00:00
|
|
|
{
|
|
|
|
#ifdef __linux__
|
|
|
|
int ret = -1;
|
|
|
|
FILE *cpuinfo = fopen(CPUINFO_PATH, "r");
|
|
|
|
|
|
|
|
if (!cpuinfo) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("cannot open %s"), CPUINFO_PATH);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = virHostCPUGetInfoPopulateLinux(cpuinfo, hostarch,
|
|
|
|
cpus, mhz, nodes,
|
|
|
|
sockets, cores, threads);
|
|
|
|
if (ret < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FORCE_FCLOSE(cpuinfo);
|
|
|
|
return ret;
|
|
|
|
#elif defined(__FreeBSD__) || defined(__APPLE__)
|
|
|
|
unsigned long cpu_freq;
|
|
|
|
size_t cpu_freq_len = sizeof(cpu_freq);
|
|
|
|
|
|
|
|
*cpus = virHostCPUGetCountAppleFreeBSD();
|
|
|
|
if (*cpus == -1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
*nodes = 1;
|
|
|
|
*sockets = 1;
|
|
|
|
*cores = *cpus;
|
|
|
|
*threads = 1;
|
|
|
|
|
|
|
|
# ifdef __FreeBSD__
|
2016-08-31 05:14:41 +00:00
|
|
|
/* dev.cpu.%d.freq reports current active CPU frequency. It is provided by
|
|
|
|
* the cpufreq(4) framework. However, it might be disabled or no driver
|
|
|
|
* available. In this case fallback to "hw.clockrate" which reports boot time
|
|
|
|
* CPU frequency. */
|
|
|
|
|
2016-04-13 17:53:02 +00:00
|
|
|
if (sysctlbyname("dev.cpu.0.freq", &cpu_freq, &cpu_freq_len, NULL, 0) < 0) {
|
2016-08-31 05:14:41 +00:00
|
|
|
if (sysctlbyname("hw.clockrate", &cpu_freq, &cpu_freq_len, NULL, 0) < 0) {
|
|
|
|
virReportSystemError(errno, "%s", _("cannot obtain CPU freq"));
|
|
|
|
return -1;
|
|
|
|
}
|
2016-04-13 17:53:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
*mhz = cpu_freq;
|
|
|
|
# else
|
|
|
|
if (sysctlbyname("hw.cpufrequency", &cpu_freq, &cpu_freq_len, NULL, 0) < 0) {
|
|
|
|
virReportSystemError(errno, "%s", _("cannot obtain CPU freq"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
*mhz = cpu_freq / 1000000;
|
|
|
|
# endif
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
#else
|
|
|
|
/* XXX Solaris will need an impl later if they port QEMU driver */
|
|
|
|
virReportError(VIR_ERR_NO_SUPPORT, "%s",
|
|
|
|
_("node info not implemented on this platform"));
|
|
|
|
return -1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virHostCPUGetStats(int cpuNum ATTRIBUTE_UNUSED,
|
|
|
|
virNodeCPUStatsPtr params ATTRIBUTE_UNUSED,
|
|
|
|
int *nparams ATTRIBUTE_UNUSED,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
|
|
|
|
#ifdef __linux__
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
FILE *procstat = fopen(PROCSTAT_PATH, "r");
|
|
|
|
if (!procstat) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("cannot open %s"), PROCSTAT_PATH);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
ret = virHostCPUGetStatsLinux(procstat, cpuNum, params, nparams);
|
|
|
|
VIR_FORCE_FCLOSE(procstat);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#elif defined(__FreeBSD__)
|
|
|
|
return virHostCPUGetStatsFreeBSD(cpuNum, params, nparams);
|
|
|
|
#else
|
|
|
|
virReportError(VIR_ERR_NO_SUPPORT, "%s",
|
|
|
|
_("node CPU stats not implemented on this platform"));
|
|
|
|
return -1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virHostCPUGetCount(void)
|
|
|
|
{
|
|
|
|
#if defined(__linux__)
|
|
|
|
/* To support older kernels that lack cpu/present, such as 2.6.18
|
|
|
|
* in RHEL5, we fall back to count cpu/cpuNN entries; this assumes
|
|
|
|
* that such kernels also lack hotplug, and therefore cpu/cpuNN
|
|
|
|
* will be consecutive.
|
|
|
|
*/
|
|
|
|
char *present_path = NULL;
|
|
|
|
char *cpupath = NULL;
|
|
|
|
int ncpu = -1;
|
|
|
|
|
|
|
|
if (!(present_path = virHostCPUGetPresentPathLinux()))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (virFileExists(present_path)) {
|
|
|
|
ncpu = virHostCPUParseCountLinux(present_path);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virAsprintf(&cpupath, "%s/cpu/cpu0", sysfs_system_path) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (virFileExists(cpupath)) {
|
|
|
|
ncpu = 0;
|
|
|
|
do {
|
|
|
|
ncpu++;
|
|
|
|
VIR_FREE(cpupath);
|
|
|
|
if (virAsprintf(&cpupath, "%s/cpu/cpu%d",
|
|
|
|
sysfs_system_path, ncpu) < 0) {
|
|
|
|
ncpu = -1;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
} while (virFileExists(cpupath));
|
|
|
|
} else {
|
|
|
|
/* no cpu/cpu0: we give up */
|
|
|
|
virReportError(VIR_ERR_NO_SUPPORT, "%s",
|
|
|
|
_("host cpu counting not supported on this node"));
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(present_path);
|
|
|
|
VIR_FREE(cpupath);
|
|
|
|
return ncpu;
|
|
|
|
#elif defined(__FreeBSD__) || defined(__APPLE__)
|
|
|
|
return virHostCPUGetCountAppleFreeBSD();
|
|
|
|
#else
|
|
|
|
virReportError(VIR_ERR_NO_SUPPORT, "%s",
|
|
|
|
_("host cpu counting not implemented on this platform"));
|
|
|
|
return -1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-11-25 13:57:04 +00:00
|
|
|
bool
|
|
|
|
virHostCPUHasBitmap(void)
|
|
|
|
{
|
|
|
|
#ifdef __linux__
|
|
|
|
return true;
|
|
|
|
#else
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-04-13 17:53:02 +00:00
|
|
|
virBitmapPtr
|
|
|
|
virHostCPUGetPresentBitmap(void)
|
|
|
|
{
|
|
|
|
#ifdef __linux__
|
|
|
|
virBitmapPtr present_cpus = NULL;
|
|
|
|
char *present_path = NULL;
|
|
|
|
int npresent_cpus;
|
|
|
|
|
|
|
|
if ((npresent_cpus = virHostCPUGetCount()) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (!(present_path = virHostCPUGetPresentPathLinux()))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* If the cpu/present file is available, parse it and exit */
|
|
|
|
if (virFileExists(present_path)) {
|
|
|
|
present_cpus = virHostCPUParseMapLinux(npresent_cpus, present_path);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the file is not available, we can assume that the kernel is
|
|
|
|
* too old to support non-consecutive CPU ids and just mark all
|
|
|
|
* possible CPUs as present */
|
|
|
|
if (!(present_cpus = virBitmapNew(npresent_cpus)))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
virBitmapSetAll(present_cpus);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(present_path);
|
|
|
|
|
|
|
|
return present_cpus;
|
|
|
|
#endif
|
|
|
|
virReportError(VIR_ERR_NO_SUPPORT, "%s",
|
|
|
|
_("node present CPU map not implemented on this platform"));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
virBitmapPtr
|
|
|
|
virHostCPUGetOnlineBitmap(void)
|
|
|
|
{
|
|
|
|
#ifdef __linux__
|
|
|
|
char *online_path = NULL;
|
|
|
|
char *cpudir = NULL;
|
|
|
|
virBitmapPtr cpumap;
|
|
|
|
int present;
|
|
|
|
|
|
|
|
present = virHostCPUGetCount();
|
|
|
|
if (present < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!(online_path = virHostCPUGetOnlinePathLinux()))
|
|
|
|
return NULL;
|
|
|
|
if (virFileExists(online_path)) {
|
|
|
|
cpumap = virHostCPUParseMapLinux(present, online_path);
|
|
|
|
} else {
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
cpumap = virBitmapNew(present);
|
|
|
|
if (!cpumap)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (virAsprintf(&cpudir, "%s/cpu", sysfs_system_path) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
for (i = 0; i < present; i++) {
|
|
|
|
int online = virHostCPUGetValue(cpudir, i, "online", 1);
|
|
|
|
if (online < 0) {
|
|
|
|
virBitmapFree(cpumap);
|
|
|
|
cpumap = NULL;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (online)
|
|
|
|
ignore_value(virBitmapSetBit(cpumap, i));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(online_path);
|
|
|
|
VIR_FREE(cpudir);
|
|
|
|
return cpumap;
|
|
|
|
#else
|
|
|
|
virReportError(VIR_ERR_NO_SUPPORT, "%s",
|
|
|
|
_("node online CPU map not implemented on this platform"));
|
|
|
|
return NULL;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
virHostCPUGetMap(unsigned char **cpumap,
|
|
|
|
unsigned int *online,
|
|
|
|
unsigned int flags)
|
|
|
|
{
|
|
|
|
virBitmapPtr cpus = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
int dummy;
|
|
|
|
|
|
|
|
virCheckFlags(0, -1);
|
|
|
|
|
|
|
|
if (!cpumap && !online)
|
|
|
|
return virHostCPUGetCount();
|
|
|
|
|
|
|
|
if (!(cpus = virHostCPUGetOnlineBitmap()))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (cpumap && virBitmapToData(cpus, cpumap, &dummy) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (online)
|
|
|
|
*online = virBitmapCountBits(cpus);
|
|
|
|
|
|
|
|
ret = virBitmapSize(cpus);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (ret < 0 && cpumap)
|
|
|
|
VIR_FREE(*cpumap);
|
|
|
|
virBitmapFree(cpus);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#if HAVE_LINUX_KVM_H && defined(KVM_CAP_PPC_SMT)
|
|
|
|
|
|
|
|
/* Get the number of threads per subcore.
|
|
|
|
*
|
|
|
|
* This will be 2, 4 or 8 on POWER hosts, depending on the current
|
|
|
|
* micro-threading configuration, and 0 everywhere else.
|
|
|
|
*
|
|
|
|
* Returns the number of threads per subcore if subcores are in use, zero
|
|
|
|
* if subcores are not in use, and a negative value on error */
|
|
|
|
int
|
|
|
|
virHostCPUGetThreadsPerSubcore(virArch arch)
|
|
|
|
{
|
|
|
|
int threads_per_subcore = 0;
|
|
|
|
int kvmfd;
|
|
|
|
|
|
|
|
if (ARCH_IS_PPC64(arch)) {
|
|
|
|
|
|
|
|
/* It's okay if /dev/kvm doesn't exist, because
|
|
|
|
* a. we might be running in a guest
|
|
|
|
* b. the kvm module might not be installed or enabled
|
|
|
|
* In either case, falling back to the subcore-unaware thread
|
|
|
|
* counting logic is the right thing to do */
|
2016-06-24 17:34:38 +00:00
|
|
|
if (!virFileExists(KVM_DEVICE))
|
2016-04-13 17:53:02 +00:00
|
|
|
goto out;
|
|
|
|
|
2016-06-24 17:34:38 +00:00
|
|
|
if ((kvmfd = open(KVM_DEVICE, O_RDONLY)) < 0) {
|
2016-04-13 17:53:02 +00:00
|
|
|
/* This can happen when running as a regular user if
|
|
|
|
* permissions are tight enough, in which case erroring out
|
|
|
|
* is better than silently falling back and reporting
|
|
|
|
* different nodeinfo depending on the user */
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Failed to open '%s'"),
|
2016-06-24 17:34:38 +00:00
|
|
|
KVM_DEVICE);
|
2016-04-13 17:53:02 +00:00
|
|
|
threads_per_subcore = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* For Phyp and KVM based guests the ioctl for KVM_CAP_PPC_SMT
|
|
|
|
* returns zero and both primary and secondary threads will be
|
|
|
|
* online */
|
|
|
|
threads_per_subcore = ioctl(kvmfd,
|
|
|
|
KVM_CHECK_EXTENSION,
|
|
|
|
KVM_CAP_PPC_SMT);
|
|
|
|
|
|
|
|
VIR_FORCE_CLOSE(kvmfd);
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
return threads_per_subcore;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
/* Fallback for nodeGetThreadsPerSubcore() used when KVM headers
|
|
|
|
* are not available on the system */
|
|
|
|
int
|
|
|
|
virHostCPUGetThreadsPerSubcore(virArch arch ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* HAVE_LINUX_KVM_H && defined(KVM_CAP_PPC_SMT) */
|
2016-06-24 15:03:13 +00:00
|
|
|
|
2016-06-27 11:47:53 +00:00
|
|
|
#if HAVE_LINUX_KVM_H
|
2016-06-24 15:03:13 +00:00
|
|
|
int
|
|
|
|
virHostCPUGetKVMMaxVCPUs(void)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if ((fd = open(KVM_DEVICE, O_RDONLY)) < 0) {
|
|
|
|
virReportSystemError(errno, _("Unable to open %s"), KVM_DEVICE);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-06-27 11:47:53 +00:00
|
|
|
# ifdef KVM_CAP_MAX_VCPUS
|
2016-06-24 15:03:13 +00:00
|
|
|
/* at first try KVM_CAP_MAX_VCPUS to determine the maximum count */
|
|
|
|
if ((ret = ioctl(fd, KVM_CHECK_EXTENSION, KVM_CAP_MAX_VCPUS)) > 0)
|
|
|
|
goto cleanup;
|
2016-06-27 11:47:53 +00:00
|
|
|
# endif /* KVM_CAP_MAX_VCPUS */
|
2016-06-24 15:03:13 +00:00
|
|
|
|
|
|
|
/* as a fallback get KVM_CAP_NR_VCPUS (the recommended maximum number of
|
|
|
|
* vcpus). Note that on most machines this is set to 160. */
|
|
|
|
if ((ret = ioctl(fd, KVM_CHECK_EXTENSION, KVM_CAP_NR_VCPUS)) > 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* if KVM_CAP_NR_VCPUS doesn't exist either, kernel documentation states
|
|
|
|
* that 4 should be used as the maximum number of cpus */
|
|
|
|
ret = 4;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FORCE_CLOSE(fd);
|
|
|
|
return ret;
|
|
|
|
}
|
2016-06-27 11:47:53 +00:00
|
|
|
#else
|
|
|
|
int
|
|
|
|
virHostCPUGetKVMMaxVCPUs(void)
|
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS, "%s",
|
|
|
|
_("KVM is not supported on this platform"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
#endif /* HAVE_LINUX_KVM_H */
|