nodeinfo.c 12.6 KB
Newer Older
1 2 3
/*
 * nodeinfo.c: Helper routines for OS specific node information
 *
4
 * Copyright (C) 2006, 2007, 2008, 2010 Red Hat, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 * 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 *
 * Author: Daniel P. Berrange <berrange@redhat.com>
 */

24
#include <config.h>
J
Jim Meyering 已提交
25

26 27 28
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
29
#include <stdint.h>
30
#include <errno.h>
31
#include <dirent.h>
32 33 34 35 36

#if HAVE_NUMACTL
# define NUMA_VERSION1_COMPATIBILITY 1
# include <numa.h>
#endif
37

38
#ifdef HAVE_SYS_UTSNAME_H
39
# include <sys/utsname.h>
40 41
#endif

42 43
#include "c-ctype.h"
#include "memory.h"
44
#include "nodeinfo.h"
45
#include "physmem.h"
46
#include "util.h"
47
#include "logging.h"
48
#include "virterror_internal.h"
49
#include "count-one-bits.h"
50

51 52 53

#define VIR_FROM_THIS VIR_FROM_NONE

54 55
#define nodeReportError(code, ...)                                      \
    virReportErrorHelper(NULL, VIR_FROM_NONE, code, __FILE__,           \
56
                         __FUNCTION__, __LINE__, __VA_ARGS__)
57

58
#ifdef __linux__
59 60
# define CPUINFO_PATH "/proc/cpuinfo"
# define CPU_SYS_PATH "/sys/devices/system/cpu"
61

62
/* NB, this is not static as we need to call it from the testsuite */
63
int linuxNodeInfoCPUPopulate(FILE *cpuinfo,
64
                             virNodeInfoPtr nodeinfo);
65

C
Chris Lalancette 已提交
66
static unsigned long count_thread_siblings(unsigned int cpu)
67 68
{
    unsigned long ret = 0;
C
Chris Lalancette 已提交
69 70
    char *path;
    FILE *pathfp;
71 72 73
    char str[1024];
    int i;

C
Chris Lalancette 已提交
74
    if (virAsprintf(&path, CPU_SYS_PATH "/cpu%u/topology/thread_siblings",
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
                    cpu) < 0) {
        virReportOOMError();
        return 0;
    }

    pathfp = fopen(path, "r");
    if (pathfp == NULL) {
        virReportSystemError(errno, _("cannot open %s"), path);
        VIR_FREE(path);
        return 0;
    }

    if (fgets(str, sizeof(str), pathfp) == NULL) {
        virReportSystemError(errno, _("cannot read from %s"), path);
        goto cleanup;
    }

    i = 0;
    while (str[i] != '\0') {
C
Chris Lalancette 已提交
94
        if (c_isdigit(str[i]))
95
            ret += count_one_bits(str[i] - '0');
C
Chris Lalancette 已提交
96 97 98 99
        else if (str[i] >= 'A' && str[i] <= 'F')
            ret += count_one_bits(str[i] - 'A' + 10);
        else if (str[i] >= 'a' && str[i] <= 'f')
            ret += count_one_bits(str[i] - 'a' + 10);
100 101 102 103 104 105 106 107 108 109
        i++;
    }

cleanup:
    fclose(pathfp);
    VIR_FREE(path);

    return ret;
}

C
Chris Lalancette 已提交
110
static int parse_socket(unsigned int cpu)
111
{
C
Chris Lalancette 已提交
112
    char *path;
113 114 115
    FILE *pathfp;
    char socket_str[1024];
    char *tmp;
C
Chris Lalancette 已提交
116
    int socket = -1;
117

C
Chris Lalancette 已提交
118 119
    if (virAsprintf(&path, CPU_SYS_PATH "/cpu%u/topology/physical_package_id",
                    cpu) < 0) {
120 121 122 123 124 125 126
        virReportOOMError();
        return -1;
    }

    pathfp = fopen(path, "r");
    if (pathfp == NULL) {
        virReportSystemError(errno, _("cannot open %s"), path);
C
Chris Lalancette 已提交
127 128
        VIR_FREE(path);
        return -1;
129 130 131 132 133 134 135
    }

    if (fgets(socket_str, sizeof(socket_str), pathfp) == NULL) {
        virReportSystemError(errno, _("cannot read from %s"), path);
        goto cleanup;
    }
    if (virStrToLong_i(socket_str, &tmp, 10, &socket) < 0) {
136
        nodeReportError(VIR_ERR_INTERNAL_ERROR,
137 138 139 140 141 142 143 144 145 146 147 148
                        _("could not convert '%s' to an integer"),
                        socket_str);
        goto cleanup;
    }

cleanup:
    fclose(pathfp);
    VIR_FREE(path);

    return socket;
}

149
int linuxNodeInfoCPUPopulate(FILE *cpuinfo,
150 151
                             virNodeInfoPtr nodeinfo)
{
152
    char line[1024];
153 154
    DIR *cpudir = NULL;
    struct dirent *cpudirent = NULL;
C
Chris Lalancette 已提交
155
    unsigned int cpu;
156 157 158
    unsigned long cur_threads;
    int socket;
    unsigned long long socket_mask = 0;
159 160 161

    nodeinfo->cpus = 0;
    nodeinfo->mhz = 0;
162
    nodeinfo->cores = 1;
163 164

    nodeinfo->nodes = 1;
J
Jiri Denemark 已提交
165
# if HAVE_NUMACTL
166
    if (numa_available() >= 0)
167
        nodeinfo->nodes = numa_max_node() + 1;
J
Jiri Denemark 已提交
168
# endif
169 170

    /* NB: It is impossible to fill our nodes, since cpuinfo
C
Chris Lalancette 已提交
171
     * has no knowledge of NUMA nodes */
172

173
    /* NOTE: hyperthreads are ignored here; they are parsed out of /sys */
174 175
    while (fgets(line, sizeof(line), cpuinfo) != NULL) {
        char *buf = line;
176
        if (STRPREFIX(buf, "processor")) { /* aka a single logical CPU */
177
            buf += 9;
178
            while (*buf && c_isspace(*buf))
179 180
                buf++;
            if (*buf != ':') {
181
                nodeReportError(VIR_ERR_INTERNAL_ERROR,
182
                                "%s", _("parsing cpuinfo processor"));
183 184 185
                return -1;
            }
            nodeinfo->cpus++;
186
        } else if (STRPREFIX(buf, "cpu MHz")) {
187 188
            char *p;
            unsigned int ui;
189
            buf += 9;
190
            while (*buf && c_isspace(*buf))
191 192
                buf++;
            if (*buf != ':' || !buf[1]) {
193
                nodeReportError(VIR_ERR_INTERNAL_ERROR,
194
                                "%s", _("parsing cpuinfo cpu MHz"));
195 196
                return -1;
            }
197
            if (virStrToLong_ui(buf+1, &p, 10, &ui) == 0
198
                /* Accept trailing fractional part.  */
199
                && (*p == '\0' || *p == '.' || c_isspace(*p)))
200
                nodeinfo->mhz = ui;
201
        } else if (STRPREFIX(buf, "cpu cores")) { /* aka cores */
202
            char *p;
203 204
            unsigned int id;
            buf += 9;
205
            while (*buf && c_isspace(*buf))
206 207
                buf++;
            if (*buf != ':' || !buf[1]) {
208
                nodeReportError(VIR_ERR_INTERNAL_ERROR,
209
                                "parsing cpuinfo cpu cores %c", *buf);
210 211
                return -1;
            }
212
            if (virStrToLong_ui(buf+1, &p, 10, &id) == 0
213
                && (*p == '\0' || c_isspace(*p))
214
                && id > nodeinfo->cores)
215 216 217 218 219
                nodeinfo->cores = id;
        }
    }

    if (!nodeinfo->cpus) {
220
        nodeReportError(VIR_ERR_INTERNAL_ERROR,
221
                        "%s", _("no cpus found"));
222 223 224
        return -1;
    }

225 226
    /* OK, we've parsed what we can out of /proc/cpuinfo.  Get the socket
     * and thread information from /sys
227
     */
228 229 230 231 232 233
    cpudir = opendir(CPU_SYS_PATH);
    if (cpudir == NULL) {
        virReportSystemError(errno, _("cannot opendir %s"), CPU_SYS_PATH);
        return -1;
    }
    while ((cpudirent = readdir(cpudir))) {
C
Chris Lalancette 已提交
234
        if (sscanf(cpudirent->d_name, "cpu%u", &cpu) != 1)
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
            continue;

        socket = parse_socket(cpu);
        if (socket < 0) {
            closedir(cpudir);
            return -1;
        }
        if (!(socket_mask & (1 << socket))) {
            socket_mask |= (1 << socket);
            nodeinfo->sockets++;
        }

        cur_threads = count_thread_siblings(cpu);
        if (cur_threads == 0) {
            closedir(cpudir);
            return -1;
        }
        if (cur_threads > nodeinfo->threads)
            nodeinfo->threads = cur_threads;
    }

    closedir(cpudir);
257

C
Chris Lalancette 已提交
258 259
    /* there should always be at least one socket and one thread */
    if (nodeinfo->sockets == 0) {
260
        nodeReportError(VIR_ERR_INTERNAL_ERROR,
C
Chris Lalancette 已提交
261 262 263 264
                        "%s", _("no sockets found"));
        return -1;
    }
    if (nodeinfo->threads == 0) {
265
        nodeReportError(VIR_ERR_INTERNAL_ERROR,
C
Chris Lalancette 已提交
266 267 268 269
                        "%s", _("no threads found"));
        return -1;
    }

270 271 272 273 274
    return 0;
}

#endif

275
int nodeGetInfo(virConnectPtr conn ATTRIBUTE_UNUSED, virNodeInfoPtr nodeinfo) {
276 277
    memset(nodeinfo, 0, sizeof(*nodeinfo));

278
#ifdef HAVE_UNAME
279
    {
280 281
    struct utsname info;

282 283
    uname(&info);

C
Chris Lalancette 已提交
284 285
    if (virStrcpyStatic(nodeinfo->model, info.machine) == NULL)
        return -1;
286
    }
287 288
#endif /* !HAVE_UNAME */

289
#ifdef __linux__
290 291 292
    {
    int ret;
    FILE *cpuinfo = fopen(CPUINFO_PATH, "r");
293
    if (!cpuinfo) {
294
        virReportSystemError(errno,
295
                             _("cannot open %s"), CPUINFO_PATH);
296 297
        return -1;
    }
298
    ret = linuxNodeInfoCPUPopulate(cpuinfo, nodeinfo);
299 300 301 302
    fclose(cpuinfo);
    if (ret < 0)
        return -1;

303 304
    /* Convert to KB. */
    nodeinfo->memory = physmem_total () / 1024;
305 306

    return ret;
307
    }
308 309
#else
    /* XXX Solaris will need an impl later if they port QEMU driver */
310
    nodeReportError(VIR_ERR_NO_SUPPORT, "%s",
311
                    _("node info not implemented on this platform"));
312 313 314
    return -1;
#endif
}
315 316 317 318 319 320 321 322 323 324 325 326 327

#if HAVE_NUMACTL
# if LIBNUMA_API_VERSION <= 1
#  define NUMA_MAX_N_CPUS 4096
# else
#  define NUMA_MAX_N_CPUS (numa_all_cpus_ptr->size)
# endif

# define n_bits(var) (8 * sizeof(var))
# define MASK_CPU_ISSET(mask, cpu) \
  (((mask)[((cpu) / n_bits(*(mask)))] >> ((cpu) % n_bits(*(mask)))) & 1)

int
328
nodeCapsInitNUMA(virCapsPtr caps)
329 330
{
    int n;
331
    unsigned long *mask = NULL;
332 333 334 335 336 337 338 339 340 341 342 343 344 345
    int *cpus = NULL;
    int ret = -1;
    int max_n_cpus = NUMA_MAX_N_CPUS;

    if (numa_available() < 0)
        return 0;

    int mask_n_bytes = max_n_cpus / 8;
    if (VIR_ALLOC_N(mask, mask_n_bytes / sizeof *mask) < 0)
        goto cleanup;

    for (n = 0 ; n <= numa_max_node() ; n++) {
        int i;
        int ncpus;
346 347 348 349 350
        if (numa_node_to_cpus(n, mask, mask_n_bytes) < 0) {
            VIR_WARN("NUMA topology for cell %d of %d not available, ignoring",
                     n, numa_max_node());
            continue;
        }
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378

        for (ncpus = 0, i = 0 ; i < max_n_cpus ; i++)
            if (MASK_CPU_ISSET(mask, i))
                ncpus++;

        if (VIR_ALLOC_N(cpus, ncpus) < 0)
            goto cleanup;

        for (ncpus = 0, i = 0 ; i < max_n_cpus ; i++)
            if (MASK_CPU_ISSET(mask, i))
                cpus[ncpus++] = i;

        if (virCapabilitiesAddHostNUMACell(caps,
                                           n,
                                           ncpus,
                                           cpus) < 0)
            goto cleanup;

        VIR_FREE(cpus);
    }

    ret = 0;

cleanup:
    VIR_FREE(cpus);
    VIR_FREE(mask);
    return ret;
}
379 380 381


int
382
nodeGetCellsFreeMemory(virConnectPtr conn ATTRIBUTE_UNUSED,
383 384 385 386 387 388 389 390 391
                       unsigned long long *freeMems,
                       int startCell,
                       int maxCells)
{
    int n, lastCell, numCells;
    int ret = -1;
    int maxCell;

    if (numa_available() < 0) {
392
        nodeReportError(VIR_ERR_NO_SUPPORT,
393 394 395 396 397
                        "%s", _("NUMA not supported on this host"));
        goto cleanup;
    }
    maxCell = numa_max_node();
    if (startCell > maxCell) {
398
        nodeReportError(VIR_ERR_INTERNAL_ERROR,
399 400 401 402 403 404 405 406 407 408 409
                        _("start cell %d out of range (0-%d)"),
                        startCell, maxCell);
        goto cleanup;
    }
    lastCell = startCell + maxCells - 1;
    if (lastCell > maxCell)
        lastCell = maxCell;

    for (numCells = 0, n = startCell ; n <= lastCell ; n++) {
        long long mem;
        if (numa_node_size64(n, &mem) < 0) {
410
            nodeReportError(VIR_ERR_INTERNAL_ERROR,
411 412 413 414 415 416 417 418 419 420 421 422
                            "%s", _("Failed to query NUMA free memory"));
            goto cleanup;
        }
        freeMems[numCells++] = mem;
    }
    ret = numCells;

cleanup:
    return ret;
}

unsigned long long
423
nodeGetFreeMemory(virConnectPtr conn ATTRIBUTE_UNUSED)
424 425 426 427 428
{
    unsigned long long freeMem = 0;
    int n;

    if (numa_available() < 0) {
429
        nodeReportError(VIR_ERR_NO_SUPPORT,
430 431 432 433 434 435 436
                        "%s", _("NUMA not supported on this host"));
        goto cleanup;
    }

    for (n = 0 ; n <= numa_max_node() ; n++) {
        long long mem;
        if (numa_node_size64(n, &mem) < 0) {
437
            nodeReportError(VIR_ERR_INTERNAL_ERROR,
438 439 440 441 442 443 444 445 446 447
                            "%s", _("Failed to query NUMA free memory"));
            goto cleanup;
        }
        freeMem += mem;
    }

cleanup:
    return freeMem;
}

448
#else
449 450 451 452 453 454 455 456 457
int nodeCapsInitNUMA(virCapsPtr caps ATTRIBUTE_UNUSED) {
    return 0;
}

int nodeGetCellsFreeMemory(virConnectPtr conn,
                              unsigned long long *freeMems ATTRIBUTE_UNUSED,
                              int startCell ATTRIBUTE_UNUSED,
                              int maxCells ATTRIBUTE_UNUSED)
{
458
    nodeReportError(VIR_ERR_NO_SUPPORT, "%s",
459 460 461 462 463 464
                    _("NUMA memory information not available on this platform"));
    return -1;
}

unsigned long long nodeGetFreeMemory(virConnectPtr conn)
{
465
    nodeReportError(VIR_ERR_NO_SUPPORT, "%s",
466 467 468
                    _("NUMA memory information not available on this platform"));
    return 0;
}
469
#endif