~ubuntu-branches/ubuntu/quantal/libvirt/quantal

« back to all changes in this revision

Viewing changes to .pc/add-armhf-cpuinfo-parser.patch/src/nodeinfo.c

  • Committer: Package Import Robot
  • Author(s): Chuck Short
  • Date: 2012-07-19 14:54:47 UTC
  • Revision ID: package-import@ubuntu.com-20120719145447-ksf59r7lsc2df56l
Tags: 0.9.13-0ubuntu4
* debian/patches/add-armhf-sysinfo-infomration.patch:
  Provides cpuinfo for armhf cpus.
* debian/patches/add-armhf-cpuinfo-parser.patch:
  Fixes compile time warning about armhf cpus.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * nodeinfo.c: Helper routines for OS specific node information
 
3
 *
 
4
 * Copyright (C) 2006-2008, 2010-2012 Red Hat, Inc.
 
5
 * Copyright (C) 2006 Daniel P. Berrange
 
6
 *
 
7
 * This library is free software; you can redistribute it and/or
 
8
 * modify it under the terms of the GNU Lesser General Public
 
9
 * License as published by the Free Software Foundation; either
 
10
 * version 2.1 of the License, or (at your option) any later version.
 
11
 *
 
12
 * This library is distributed in the hope that it will be useful,
 
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
15
 * Lesser General Public License for more details.
 
16
 *
 
17
 * You should have received a copy of the GNU Lesser General Public
 
18
 * License along with this library; if not, write to the Free Software
 
19
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 
20
 *
 
21
 * Author: Daniel P. Berrange <berrange@redhat.com>
 
22
 */
 
23
 
 
24
#include <config.h>
 
25
 
 
26
#include <stdio.h>
 
27
#include <string.h>
 
28
#include <stdlib.h>
 
29
#include <stdint.h>
 
30
#include <errno.h>
 
31
#include <dirent.h>
 
32
#include <sys/utsname.h>
 
33
#include <sched.h>
 
34
#include "conf/domain_conf.h"
 
35
 
 
36
#if HAVE_NUMACTL
 
37
# define NUMA_VERSION1_COMPATIBILITY 1
 
38
# include <numa.h>
 
39
#endif
 
40
 
 
41
#include "c-ctype.h"
 
42
#include "memory.h"
 
43
#include "nodeinfo.h"
 
44
#include "physmem.h"
 
45
#include "util.h"
 
46
#include "logging.h"
 
47
#include "virterror_internal.h"
 
48
#include "count-one-bits.h"
 
49
#include "intprops.h"
 
50
#include "virfile.h"
 
51
 
 
52
 
 
53
#define VIR_FROM_THIS VIR_FROM_NONE
 
54
 
 
55
#define nodeReportError(code, ...)                                      \
 
56
    virReportErrorHelper(VIR_FROM_NONE, code, __FILE__,                 \
 
57
                         __FUNCTION__, __LINE__, __VA_ARGS__)
 
58
 
 
59
#ifdef __linux__
 
60
# define CPUINFO_PATH "/proc/cpuinfo"
 
61
# define SYSFS_SYSTEM_PATH "/sys/devices/system"
 
62
# define PROCSTAT_PATH "/proc/stat"
 
63
# define MEMINFO_PATH "/proc/meminfo"
 
64
 
 
65
# define LINUX_NB_CPU_STATS 4
 
66
# define LINUX_NB_MEMORY_STATS_ALL 4
 
67
# define LINUX_NB_MEMORY_STATS_CELL 2
 
68
 
 
69
/* NB, this is not static as we need to call it from the testsuite */
 
70
int linuxNodeInfoCPUPopulate(FILE *cpuinfo,
 
71
                             const char *sysfs_dir,
 
72
                             virNodeInfoPtr nodeinfo);
 
73
 
 
74
static int linuxNodeGetCPUStats(FILE *procstat,
 
75
                                int cpuNum,
 
76
                                virNodeCPUStatsPtr params,
 
77
                                int *nparams);
 
78
static int linuxNodeGetMemoryStats(FILE *meminfo,
 
79
                                   int cellNum,
 
80
                                   virNodeMemoryStatsPtr params,
 
81
                                   int *nparams);
 
82
 
 
83
/* Return the positive decimal contents of the given
 
84
 * DIR/cpu%u/FILE, or -1 on error.  If MISSING_OK and the
 
85
 * file could not be found, return 1 instead of an error; this is
 
86
 * because some machines cannot hot-unplug cpu0, or because
 
87
 * hot-unplugging is disabled.  */
 
88
static int
 
89
virNodeGetCpuValue(const char *dir, unsigned int cpu, const char *file,
 
90
                   bool missing_ok)
 
91
{
 
92
    char *path;
 
93
    FILE *pathfp;
 
94
    int value = -1;
 
95
    char value_str[INT_BUFSIZE_BOUND(value)];
 
96
    char *tmp;
 
97
 
 
98
    if (virAsprintf(&path, "%s/cpu%u/%s", dir, cpu, file) < 0) {
 
99
        virReportOOMError();
 
100
        return -1;
 
101
    }
 
102
 
 
103
    pathfp = fopen(path, "r");
 
104
    if (pathfp == NULL) {
 
105
        if (missing_ok && errno == ENOENT)
 
106
            value = 1;
 
107
        else
 
108
            virReportSystemError(errno, _("cannot open %s"), path);
 
109
        goto cleanup;
 
110
    }
 
111
 
 
112
    if (fgets(value_str, sizeof(value_str), pathfp) == NULL) {
 
113
        virReportSystemError(errno, _("cannot read from %s"), path);
 
114
        goto cleanup;
 
115
    }
 
116
    if (virStrToLong_i(value_str, &tmp, 10, &value) < 0) {
 
117
        nodeReportError(VIR_ERR_INTERNAL_ERROR,
 
118
                        _("could not convert '%s' to an integer"),
 
119
                        value_str);
 
120
        goto cleanup;
 
121
    }
 
122
 
 
123
cleanup:
 
124
    VIR_FORCE_FCLOSE(pathfp);
 
125
    VIR_FREE(path);
 
126
 
 
127
    return value;
 
128
}
 
129
 
 
130
static unsigned long
 
131
virNodeCountThreadSiblings(const char *dir, unsigned int cpu)
 
132
{
 
133
    unsigned long ret = 0;
 
134
    char *path;
 
135
    FILE *pathfp;
 
136
    char str[1024];
 
137
    int i;
 
138
 
 
139
    if (virAsprintf(&path, "%s/cpu%u/topology/thread_siblings",
 
140
                    dir, cpu) < 0) {
 
141
        virReportOOMError();
 
142
        return 0;
 
143
    }
 
144
 
 
145
    pathfp = fopen(path, "r");
 
146
    if (pathfp == NULL) {
 
147
        virReportSystemError(errno, _("cannot open %s"), path);
 
148
        VIR_FREE(path);
 
149
        return 0;
 
150
    }
 
151
 
 
152
    if (fgets(str, sizeof(str), pathfp) == NULL) {
 
153
        virReportSystemError(errno, _("cannot read from %s"), path);
 
154
        goto cleanup;
 
155
    }
 
156
 
 
157
    i = 0;
 
158
    while (str[i] != '\0') {
 
159
        if (c_isdigit(str[i]))
 
160
            ret += count_one_bits(str[i] - '0');
 
161
        else if (str[i] >= 'A' && str[i] <= 'F')
 
162
            ret += count_one_bits(str[i] - 'A' + 10);
 
163
        else if (str[i] >= 'a' && str[i] <= 'f')
 
164
            ret += count_one_bits(str[i] - 'a' + 10);
 
165
        i++;
 
166
    }
 
167
 
 
168
cleanup:
 
169
    VIR_FORCE_FCLOSE(pathfp);
 
170
    VIR_FREE(path);
 
171
 
 
172
    return ret;
 
173
}
 
174
 
 
175
static int
 
176
virNodeParseSocket(const char *dir, unsigned int cpu)
 
177
{
 
178
    int ret = virNodeGetCpuValue(dir, cpu, "topology/physical_package_id",
 
179
                                 false);
 
180
# if defined(__powerpc__) || \
 
181
    defined(__powerpc64__) || \
 
182
    defined(__s390__) || \
 
183
    defined(__s390x__)
 
184
    /* ppc and s390(x) has -1 */
 
185
    if (ret < 0)
 
186
        ret = 0;
 
187
# endif
 
188
    return ret;
 
189
}
 
190
 
 
191
static int
 
192
virNodeParseNode(const char *sysfs_dir)
 
193
{
 
194
    char *file = NULL;
 
195
    char *possible = NULL;
 
196
    char *tmp;
 
197
    int ret = -1;
 
198
 
 
199
    if (virAsprintf(&file, "%s/node/possible", sysfs_dir) < 0) {
 
200
        virReportOOMError();
 
201
        goto cleanup;
 
202
    }
 
203
    /* Assume that a missing node/possible file implies no NUMA
 
204
     * support, and hence all cpus belong to the same node.  */
 
205
    if (!virFileExists(file)) {
 
206
        ret = 1;
 
207
        goto cleanup;
 
208
    }
 
209
    if (virFileReadAll(file, 1024, &possible) < 0)
 
210
        goto cleanup;
 
211
    if (virStrToLong_i(possible, &tmp, 10, &ret) < 0 ||
 
212
        (*tmp == '-' && virStrToLong_i(tmp+1, &tmp, 10, &ret) < 0) ||
 
213
        *tmp != '\n') {
 
214
        nodeReportError(VIR_ERR_INTERNAL_ERROR,
 
215
                        _("failed to parse possible nodes '%s'"), possible);
 
216
        goto cleanup;
 
217
    }
 
218
    ret++;
 
219
 
 
220
cleanup:
 
221
    VIR_FREE(file);
 
222
    VIR_FREE(possible);
 
223
    return ret;
 
224
}
 
225
 
 
226
int linuxNodeInfoCPUPopulate(FILE *cpuinfo,
 
227
                             const char *sysfs_dir,
 
228
                             virNodeInfoPtr nodeinfo)
 
229
{
 
230
    char line[1024];
 
231
    DIR *cpudir = NULL;
 
232
    struct dirent *cpudirent = NULL;
 
233
    unsigned int cpu;
 
234
    unsigned long core, sock, cur_threads;
 
235
    cpu_set_t core_mask;
 
236
    cpu_set_t socket_mask;
 
237
    int online;
 
238
    int ret = -1;
 
239
    char *sysfs_cpudir = NULL;
 
240
    unsigned int cpu_cores = 0;
 
241
 
 
242
    nodeinfo->cpus = 0;
 
243
    nodeinfo->mhz = 0;
 
244
    nodeinfo->cores = 0;
 
245
 
 
246
    /* Start with parsing /proc/cpuinfo; although it tends to have
 
247
     * fewer details.  Hyperthreads are ignored at this stage.  */
 
248
    while (fgets(line, sizeof(line), cpuinfo) != NULL) {
 
249
# if defined(__x86_64__) || \
 
250
    defined(__amd64__)  || \
 
251
    defined(__i386__)
 
252
        char *buf = line;
 
253
        if (STRPREFIX(buf, "cpu MHz")) {
 
254
            char *p;
 
255
            unsigned int ui;
 
256
 
 
257
            buf += 9;
 
258
            while (*buf && c_isspace(*buf))
 
259
                buf++;
 
260
 
 
261
            if (*buf != ':' || !buf[1]) {
 
262
                nodeReportError(VIR_ERR_INTERNAL_ERROR,
 
263
                                "%s", _("parsing cpu MHz from cpuinfo"));
 
264
                goto cleanup;
 
265
            }
 
266
 
 
267
            if (virStrToLong_ui(buf+1, &p, 10, &ui) == 0
 
268
                /* Accept trailing fractional part.  */
 
269
                && (*p == '\0' || *p == '.' || c_isspace(*p)))
 
270
                nodeinfo->mhz = ui;
 
271
        }
 
272
 
 
273
        if (STRPREFIX(buf, "cpu cores")) {
 
274
            char *p;
 
275
            unsigned int ui;
 
276
 
 
277
            buf += 9;
 
278
            while (*buf && c_isspace(*buf))
 
279
                buf++;
 
280
 
 
281
            if (*buf != ':' || !buf[1]) {
 
282
                nodeReportError(VIR_ERR_INTERNAL_ERROR,
 
283
                                "%s", _("parsing cpu cores from cpuinfo"));
 
284
                return -1;
 
285
            }
 
286
 
 
287
            if (virStrToLong_ui(buf+1, &p, 10, &ui) == 0
 
288
                && (*p == '\0' || c_isspace(*p)))
 
289
                cpu_cores = ui;
 
290
         }
 
291
# elif defined(__powerpc__) || \
 
292
      defined(__powerpc64__)
 
293
        char *buf = line;
 
294
        if (STRPREFIX(buf, "clock")) {
 
295
            char *p;
 
296
            unsigned int ui;
 
297
 
 
298
            buf += 5;
 
299
            while (*buf && c_isspace(*buf))
 
300
                buf++;
 
301
 
 
302
            if (*buf != ':' || !buf[1]) {
 
303
                nodeReportError(VIR_ERR_INTERNAL_ERROR,
 
304
                                "%s", _("parsing cpu MHz from cpuinfo"));
 
305
                goto cleanup;
 
306
            }
 
307
 
 
308
            if (virStrToLong_ui(buf+1, &p, 10, &ui) == 0
 
309
                /* Accept trailing fractional part.  */
 
310
                && (*p == '\0' || *p == '.' || c_isspace(*p)))
 
311
                nodeinfo->mhz = ui;
 
312
            /* No other interesting infos are available in /proc/cpuinfo.
 
313
             * However, there is a line identifying processor's version,
 
314
             * identification and machine, but we don't want it to be caught
 
315
             * and parsed in next iteration, because it is not in expected
 
316
             * format and thus lead to error. */
 
317
        }
 
318
# elif defined(__s390__) || \
 
319
      defined(__s390x__)
 
320
        /* s390x has no realistic value for CPU speed,
 
321
         * assign a value of zero to signify this */
 
322
        nodeinfo->mhz = 0;
 
323
# else
 
324
#  warning Parser for /proc/cpuinfo needs to be adapted for your architecture
 
325
# endif
 
326
    }
 
327
 
 
328
    /* OK, we've parsed clock speed out of /proc/cpuinfo. Get the
 
329
     * core, node, socket, thread and topology information from /sys
 
330
     */
 
331
    if (virAsprintf(&sysfs_cpudir, "%s/cpu", sysfs_dir) < 0) {
 
332
        virReportOOMError();
 
333
        goto cleanup;
 
334
    }
 
335
    cpudir = opendir(sysfs_cpudir);
 
336
    if (cpudir == NULL) {
 
337
        virReportSystemError(errno, _("cannot opendir %s"), sysfs_cpudir);
 
338
        goto cleanup;
 
339
    }
 
340
 
 
341
    CPU_ZERO(&core_mask);
 
342
    CPU_ZERO(&socket_mask);
 
343
 
 
344
    while ((cpudirent = readdir(cpudir))) {
 
345
        if (sscanf(cpudirent->d_name, "cpu%u", &cpu) != 1)
 
346
            continue;
 
347
 
 
348
        online = virNodeGetCpuValue(sysfs_cpudir, cpu, "online", true);
 
349
        if (online < 0) {
 
350
            closedir(cpudir);
 
351
            goto cleanup;
 
352
        }
 
353
        if (!online)
 
354
            continue;
 
355
        nodeinfo->cpus++;
 
356
 
 
357
        /* Parse core */
 
358
# if defined(__s390__) || \
 
359
    defined(__s390x__)
 
360
    /* logical cpu is equivalent to a core on s390 */
 
361
        core = cpu;
 
362
# else
 
363
        core = virNodeGetCpuValue(sysfs_cpudir, cpu, "topology/core_id", false);
 
364
# endif
 
365
        if (!CPU_ISSET(core, &core_mask)) {
 
366
            CPU_SET(core, &core_mask);
 
367
            nodeinfo->cores++;
 
368
        }
 
369
 
 
370
        /* Parse socket */
 
371
        sock = virNodeParseSocket(sysfs_cpudir, cpu);
 
372
        if (!CPU_ISSET(sock, &socket_mask)) {
 
373
            CPU_SET(sock, &socket_mask);
 
374
            nodeinfo->sockets++;
 
375
        }
 
376
 
 
377
        cur_threads = virNodeCountThreadSiblings(sysfs_cpudir, cpu);
 
378
        if (cur_threads == 0) {
 
379
            closedir(cpudir);
 
380
            goto cleanup;
 
381
        }
 
382
        if (cur_threads > nodeinfo->threads)
 
383
            nodeinfo->threads = cur_threads;
 
384
    }
 
385
    if (errno) {
 
386
        virReportSystemError(errno,
 
387
                             _("problem reading %s"), sysfs_cpudir);
 
388
        closedir(cpudir);
 
389
        goto cleanup;
 
390
    }
 
391
    if (closedir(cpudir) < 0) {
 
392
        virReportSystemError(errno,
 
393
                             _("problem closing %s"), sysfs_cpudir);
 
394
        goto cleanup;
 
395
    }
 
396
 
 
397
    if ((nodeinfo->nodes = virNodeParseNode(sysfs_dir)) <= 0)
 
398
        goto cleanup;
 
399
 
 
400
    /* There should always be at least one cpu, socket, node, and thread. */
 
401
    if (nodeinfo->cpus == 0) {
 
402
        nodeReportError(VIR_ERR_INTERNAL_ERROR,
 
403
                        "%s", _("no CPUs found"));
 
404
        goto cleanup;
 
405
    }
 
406
    if (nodeinfo->sockets == 0) {
 
407
        nodeReportError(VIR_ERR_INTERNAL_ERROR,
 
408
                        "%s", _("no sockets found"));
 
409
        goto cleanup;
 
410
    }
 
411
    if (nodeinfo->threads == 0) {
 
412
        nodeReportError(VIR_ERR_INTERNAL_ERROR,
 
413
                        "%s", _("no threads found"));
 
414
        goto cleanup;
 
415
    }
 
416
 
 
417
    /* Platform like AMD Magny Cours has two NUMA nodes each package, and
 
418
     * the two nodes share the same core ID set, it results in the cores
 
419
     * number calculated from sysfs is not the actual cores number. Use
 
420
     * "cpu cores" in /proc/cpuinfo as the cores number instead in this case.
 
421
     * More details about the problem:
 
422
     * https://www.redhat.com/archives/libvir-list/2012-May/msg00607.html
 
423
     */
 
424
    if (cpu_cores && (cpu_cores > nodeinfo->cores))
 
425
        nodeinfo->cores = cpu_cores;
 
426
 
 
427
    /* nodeinfo->sockets is supposed to be a number of sockets per NUMA node,
 
428
     * however if NUMA nodes are not composed of whole sockets, we just lie
 
429
     * about the number of NUMA nodes and force apps to check capabilities XML
 
430
     * for the actual NUMA topology.
 
431
     */
 
432
    if (nodeinfo->sockets % nodeinfo->nodes == 0)
 
433
        nodeinfo->sockets /= nodeinfo->nodes;
 
434
    else
 
435
        nodeinfo->nodes = 1;
 
436
 
 
437
    ret = 0;
 
438
 
 
439
cleanup:
 
440
    VIR_FREE(sysfs_cpudir);
 
441
    return ret;
 
442
}
 
443
 
 
444
# define TICK_TO_NSEC (1000ull * 1000ull * 1000ull / sysconf(_SC_CLK_TCK))
 
445
 
 
446
int linuxNodeGetCPUStats(FILE *procstat,
 
447
                         int cpuNum,
 
448
                         virNodeCPUStatsPtr params,
 
449
                         int *nparams)
 
450
{
 
451
    int ret = -1;
 
452
    char line[1024];
 
453
    unsigned long long usr, ni, sys, idle, iowait;
 
454
    unsigned long long irq, softirq, steal, guest, guest_nice;
 
455
    char cpu_header[3 + INT_BUFSIZE_BOUND(cpuNum)];
 
456
 
 
457
    if ((*nparams) == 0) {
 
458
        /* Current number of cpu stats supported by linux */
 
459
        *nparams = LINUX_NB_CPU_STATS;
 
460
        ret = 0;
 
461
        goto cleanup;
 
462
    }
 
463
 
 
464
    if ((*nparams) != LINUX_NB_CPU_STATS) {
 
465
        virReportInvalidArg(*nparams,
 
466
                            _("nparams in %s must be equal to %d"),
 
467
                            __FUNCTION__, LINUX_NB_CPU_STATS);
 
468
        goto cleanup;
 
469
    }
 
470
 
 
471
    if (cpuNum == VIR_NODE_CPU_STATS_ALL_CPUS) {
 
472
        strcpy(cpu_header, "cpu");
 
473
    } else {
 
474
        snprintf(cpu_header, sizeof(cpu_header), "cpu%d", cpuNum);
 
475
    }
 
476
 
 
477
    while (fgets(line, sizeof(line), procstat) != NULL) {
 
478
        char *buf = line;
 
479
 
 
480
        if (STRPREFIX(buf, cpu_header)) { /* aka logical CPU time */
 
481
            int i;
 
482
 
 
483
            if (sscanf(buf,
 
484
                       "%*s %llu %llu %llu %llu %llu" // user ~ iowait
 
485
                       "%llu %llu %llu %llu %llu",    // irq  ~ guest_nice
 
486
                       &usr, &ni, &sys, &idle, &iowait,
 
487
                       &irq, &softirq, &steal, &guest, &guest_nice) < 4) {
 
488
                continue;
 
489
            }
 
490
 
 
491
            for (i = 0; i < *nparams; i++) {
 
492
                virNodeCPUStatsPtr param = &params[i];
 
493
 
 
494
                switch (i) {
 
495
                case 0: /* fill kernel cpu time here */
 
496
                    if (virStrcpyStatic(param->field, VIR_NODE_CPU_STATS_KERNEL) == NULL) {
 
497
                        nodeReportError(VIR_ERR_INTERNAL_ERROR,
 
498
                                        "%s", _("Field kernel cpu time too long for destination"));
 
499
                        goto cleanup;
 
500
                    }
 
501
                    param->value = (sys + irq + softirq) * TICK_TO_NSEC;
 
502
                    break;
 
503
 
 
504
                case 1: /* fill user cpu time here */
 
505
                    if (virStrcpyStatic(param->field, VIR_NODE_CPU_STATS_USER) == NULL) {
 
506
                        nodeReportError(VIR_ERR_INTERNAL_ERROR,
 
507
                                        "%s", _("Field kernel cpu time too long for destination"));
 
508
                        goto cleanup;
 
509
                    }
 
510
                    param->value = (usr + ni) * TICK_TO_NSEC;
 
511
                    break;
 
512
 
 
513
                case 2: /* fill idle cpu time here */
 
514
                    if (virStrcpyStatic(param->field, VIR_NODE_CPU_STATS_IDLE) == NULL) {
 
515
                        nodeReportError(VIR_ERR_INTERNAL_ERROR,
 
516
                                        "%s", _("Field kernel cpu time too long for destination"));
 
517
                        goto cleanup;
 
518
                    }
 
519
                    param->value = idle * TICK_TO_NSEC;
 
520
                    break;
 
521
 
 
522
                case 3: /* fill iowait cpu time here */
 
523
                    if (virStrcpyStatic(param->field, VIR_NODE_CPU_STATS_IOWAIT) == NULL) {
 
524
                        nodeReportError(VIR_ERR_INTERNAL_ERROR,
 
525
                                        "%s", _("Field kernel cpu time too long for destination"));
 
526
                        goto cleanup;
 
527
                    }
 
528
                    param->value = iowait * TICK_TO_NSEC;
 
529
                    break;
 
530
 
 
531
                default:
 
532
                    break;
 
533
                    /* should not hit here */
 
534
                }
 
535
            }
 
536
            ret = 0;
 
537
            goto cleanup;
 
538
        }
 
539
    }
 
540
 
 
541
    virReportInvalidArg(cpuNum,
 
542
                        _("Invalid cpuNum in %s"),
 
543
                        __FUNCTION__);
 
544
 
 
545
cleanup:
 
546
    return ret;
 
547
}
 
548
 
 
549
int linuxNodeGetMemoryStats(FILE *meminfo,
 
550
                            int cellNum,
 
551
                            virNodeMemoryStatsPtr params,
 
552
                            int *nparams)
 
553
{
 
554
    int ret = -1;
 
555
    int i = 0, j = 0, k = 0;
 
556
    int found = 0;
 
557
    int nr_param;
 
558
    char line[1024];
 
559
    char meminfo_hdr[VIR_NODE_MEMORY_STATS_FIELD_LENGTH];
 
560
    unsigned long val;
 
561
    struct field_conv {
 
562
        const char *meminfo_hdr;  // meminfo header
 
563
        const char *field;        // MemoryStats field name
 
564
    } field_conv[] = {
 
565
        {"MemTotal:", VIR_NODE_MEMORY_STATS_TOTAL},
 
566
        {"MemFree:",  VIR_NODE_MEMORY_STATS_FREE},
 
567
        {"Buffers:",  VIR_NODE_MEMORY_STATS_BUFFERS},
 
568
        {"Cached:",   VIR_NODE_MEMORY_STATS_CACHED},
 
569
        {NULL,        NULL}
 
570
    };
 
571
 
 
572
    if (cellNum == VIR_NODE_MEMORY_STATS_ALL_CELLS) {
 
573
        nr_param = LINUX_NB_MEMORY_STATS_ALL;
 
574
    } else {
 
575
        nr_param = LINUX_NB_MEMORY_STATS_CELL;
 
576
    }
 
577
 
 
578
    if ((*nparams) == 0) {
 
579
        /* Current number of memory stats supported by linux */
 
580
        *nparams = nr_param;
 
581
        ret = 0;
 
582
        goto cleanup;
 
583
    }
 
584
 
 
585
    if ((*nparams) != nr_param) {
 
586
        virReportInvalidArg(nparams,
 
587
                            _("nparams in %s must be %d"),
 
588
                            __FUNCTION__, nr_param);
 
589
        goto cleanup;
 
590
    }
 
591
 
 
592
    while (fgets(line, sizeof(line), meminfo) != NULL) {
 
593
        char *buf = line;
 
594
 
 
595
        if (STRPREFIX(buf, "Node ")) {
 
596
            /*
 
597
             * /sys/devices/system/node/nodeX/meminfo format is below.
 
598
             * So, skip prefix "Node XX ".
 
599
             *
 
600
             * Node 0 MemTotal:        8386980 kB
 
601
             * Node 0 MemFree:         5300920 kB
 
602
             *         :
 
603
             */
 
604
            char *p;
 
605
 
 
606
            p = buf;
 
607
            for (i = 0; i < 2; i++) {
 
608
                p = strchr(p, ' ');
 
609
                if (p == NULL) {
 
610
                    nodeReportError(VIR_ERR_INTERNAL_ERROR,
 
611
                                    "%s", _("no prefix found"));
 
612
                    goto cleanup;
 
613
                }
 
614
                p++;
 
615
            }
 
616
            buf = p;
 
617
        }
 
618
 
 
619
        if (sscanf(buf, "%s %lu kB", meminfo_hdr, &val) < 2)
 
620
            continue;
 
621
 
 
622
        for (j = 0; field_conv[j].meminfo_hdr != NULL; j++) {
 
623
            struct field_conv *convp = &field_conv[j];
 
624
 
 
625
            if (STREQ(meminfo_hdr, convp->meminfo_hdr)) {
 
626
                virNodeMemoryStatsPtr param = &params[k++];
 
627
 
 
628
                if (virStrcpyStatic(param->field, convp->field) == NULL) {
 
629
                    nodeReportError(VIR_ERR_INTERNAL_ERROR,
 
630
                                    "%s", _("Field kernel memory too long for destination"));
 
631
                    goto cleanup;
 
632
                }
 
633
                param->value = val;
 
634
                found++;
 
635
                break;
 
636
            }
 
637
        }
 
638
        if (found >= nr_param)
 
639
            break;
 
640
    }
 
641
 
 
642
    if (found == 0) {
 
643
        nodeReportError(VIR_ERR_INTERNAL_ERROR,
 
644
                        "%s", _("no available memory line found"));
 
645
        goto cleanup;
 
646
    }
 
647
 
 
648
    ret = 0;
 
649
 
 
650
cleanup:
 
651
    return ret;
 
652
}
 
653
 
 
654
/*
 
655
 * Linux maintains cpu bit map. For example, if cpuid=5's flag is not set
 
656
 * and max cpu is 7. The map file shows 0-4,6-7. This function parses
 
657
 * it and returns cpumap.
 
658
 */
 
659
static char *
 
660
linuxParseCPUmap(int *max_cpuid, const char *path)
 
661
{
 
662
    char *map = NULL;
 
663
    char *str = NULL;
 
664
    int max_id = 0, i;
 
665
 
 
666
    if (virFileReadAll(path, 5 * VIR_DOMAIN_CPUMASK_LEN, &str) < 0) {
 
667
        virReportOOMError();
 
668
        goto error;
 
669
    }
 
670
 
 
671
    if (VIR_ALLOC_N(map, VIR_DOMAIN_CPUMASK_LEN) < 0) {
 
672
        virReportOOMError();
 
673
        goto error;
 
674
    }
 
675
    if (virDomainCpuSetParse(str, 0, map,
 
676
                             VIR_DOMAIN_CPUMASK_LEN) < 0) {
 
677
        goto error;
 
678
    }
 
679
 
 
680
    for (i = 0; i < VIR_DOMAIN_CPUMASK_LEN; i++) {
 
681
        if (map[i]) {
 
682
            max_id = i;
 
683
        }
 
684
    }
 
685
    *max_cpuid = max_id;
 
686
 
 
687
    VIR_FREE(str);
 
688
    return map;
 
689
 
 
690
error:
 
691
    VIR_FREE(str);
 
692
    VIR_FREE(map);
 
693
    return NULL;
 
694
}
 
695
#endif
 
696
 
 
697
int nodeGetInfo(virConnectPtr conn ATTRIBUTE_UNUSED, virNodeInfoPtr nodeinfo) {
 
698
    struct utsname info;
 
699
 
 
700
    memset(nodeinfo, 0, sizeof(*nodeinfo));
 
701
    uname(&info);
 
702
 
 
703
    if (virStrcpyStatic(nodeinfo->model, info.machine) == NULL)
 
704
        return -1;
 
705
 
 
706
#ifdef __linux__
 
707
    {
 
708
    int ret = -1;
 
709
    FILE *cpuinfo = fopen(CPUINFO_PATH, "r");
 
710
    if (!cpuinfo) {
 
711
        virReportSystemError(errno,
 
712
                             _("cannot open %s"), CPUINFO_PATH);
 
713
        return -1;
 
714
    }
 
715
 
 
716
    ret = linuxNodeInfoCPUPopulate(cpuinfo, SYSFS_SYSTEM_PATH, nodeinfo);
 
717
    if (ret < 0)
 
718
        goto cleanup;
 
719
 
 
720
    /* Convert to KB. */
 
721
    nodeinfo->memory = physmem_total () / 1024;
 
722
 
 
723
cleanup:
 
724
    VIR_FORCE_FCLOSE(cpuinfo);
 
725
    return ret;
 
726
    }
 
727
#else
 
728
    /* XXX Solaris will need an impl later if they port QEMU driver */
 
729
    nodeReportError(VIR_ERR_NO_SUPPORT, "%s",
 
730
                    _("node info not implemented on this platform"));
 
731
    return -1;
 
732
#endif
 
733
}
 
734
 
 
735
int nodeGetCPUStats(virConnectPtr conn ATTRIBUTE_UNUSED,
 
736
                    int cpuNum ATTRIBUTE_UNUSED,
 
737
                    virNodeCPUStatsPtr params ATTRIBUTE_UNUSED,
 
738
                    int *nparams ATTRIBUTE_UNUSED,
 
739
                    unsigned int flags)
 
740
{
 
741
    virCheckFlags(0, -1);
 
742
 
 
743
#ifdef __linux__
 
744
    {
 
745
        int ret;
 
746
        FILE *procstat = fopen(PROCSTAT_PATH, "r");
 
747
        if (!procstat) {
 
748
            virReportSystemError(errno,
 
749
                                 _("cannot open %s"), PROCSTAT_PATH);
 
750
            return -1;
 
751
        }
 
752
        ret = linuxNodeGetCPUStats(procstat, cpuNum, params, nparams);
 
753
        VIR_FORCE_FCLOSE(procstat);
 
754
 
 
755
        return ret;
 
756
    }
 
757
#else
 
758
    nodeReportError(VIR_ERR_NO_SUPPORT, "%s",
 
759
                    _("node CPU stats not implemented on this platform"));
 
760
    return -1;
 
761
#endif
 
762
}
 
763
 
 
764
int nodeGetMemoryStats(virConnectPtr conn ATTRIBUTE_UNUSED,
 
765
                       int cellNum ATTRIBUTE_UNUSED,
 
766
                       virNodeMemoryStatsPtr params ATTRIBUTE_UNUSED,
 
767
                       int *nparams ATTRIBUTE_UNUSED,
 
768
                       unsigned int flags)
 
769
{
 
770
    virCheckFlags(0, -1);
 
771
 
 
772
#ifdef __linux__
 
773
    {
 
774
        int ret;
 
775
        char *meminfo_path = NULL;
 
776
        FILE *meminfo;
 
777
 
 
778
        if (cellNum == VIR_NODE_MEMORY_STATS_ALL_CELLS) {
 
779
            meminfo_path = strdup(MEMINFO_PATH);
 
780
            if (!meminfo_path) {
 
781
                virReportOOMError();
 
782
                return -1;
 
783
            }
 
784
        } else {
 
785
# if HAVE_NUMACTL
 
786
            if (numa_available() < 0) {
 
787
# endif
 
788
                nodeReportError(VIR_ERR_INTERNAL_ERROR,
 
789
                                "%s", _("NUMA not supported on this host"));
 
790
                return -1;
 
791
# if HAVE_NUMACTL
 
792
            }
 
793
# endif
 
794
 
 
795
# if HAVE_NUMACTL
 
796
            if (cellNum > numa_max_node()) {
 
797
                virReportInvalidArg(cellNum,
 
798
                                    _("cellNum in %s must be less than or equal to %d"),
 
799
                                    __FUNCTION__, numa_max_node());
 
800
                return -1;
 
801
            }
 
802
# endif
 
803
 
 
804
            if (virAsprintf(&meminfo_path, "%s/node/node%d/meminfo",
 
805
                            SYSFS_SYSTEM_PATH, cellNum) < 0) {
 
806
                virReportOOMError();
 
807
                return -1;
 
808
            }
 
809
        }
 
810
        meminfo = fopen(meminfo_path, "r");
 
811
 
 
812
        if (!meminfo) {
 
813
            virReportSystemError(errno,
 
814
                                 _("cannot open %s"), meminfo_path);
 
815
            VIR_FREE(meminfo_path);
 
816
            return -1;
 
817
        }
 
818
        ret = linuxNodeGetMemoryStats(meminfo, cellNum, params, nparams);
 
819
        VIR_FORCE_FCLOSE(meminfo);
 
820
        VIR_FREE(meminfo_path);
 
821
 
 
822
        return ret;
 
823
    }
 
824
#else
 
825
    nodeReportError(VIR_ERR_NO_SUPPORT, "%s",
 
826
                    _("node memory stats not implemented on this platform"));
 
827
    return -1;
 
828
#endif
 
829
}
 
830
 
 
831
char *
 
832
nodeGetCPUmap(virConnectPtr conn ATTRIBUTE_UNUSED,
 
833
              int *max_id ATTRIBUTE_UNUSED,
 
834
              const char *mapname ATTRIBUTE_UNUSED)
 
835
{
 
836
#ifdef __linux__
 
837
    char *path;
 
838
    char *cpumap;
 
839
 
 
840
    if (virAsprintf(&path, SYSFS_SYSTEM_PATH "/cpu/%s", mapname) < 0) {
 
841
        virReportOOMError();
 
842
        return NULL;
 
843
    }
 
844
 
 
845
    cpumap = linuxParseCPUmap(max_id, path);
 
846
    VIR_FREE(path);
 
847
    return cpumap;
 
848
#else
 
849
    nodeReportError(VIR_ERR_NO_SUPPORT, "%s",
 
850
                    _("node cpumap not implemented on this platform"));
 
851
    return NULL;
 
852
#endif
 
853
}
 
854
 
 
855
#if HAVE_NUMACTL
 
856
# if LIBNUMA_API_VERSION <= 1
 
857
#  define NUMA_MAX_N_CPUS 4096
 
858
# else
 
859
#  define NUMA_MAX_N_CPUS (numa_all_cpus_ptr->size)
 
860
# endif
 
861
 
 
862
# define n_bits(var) (8 * sizeof(var))
 
863
# define MASK_CPU_ISSET(mask, cpu) \
 
864
  (((mask)[((cpu) / n_bits(*(mask)))] >> ((cpu) % n_bits(*(mask)))) & 1)
 
865
 
 
866
int
 
867
nodeCapsInitNUMA(virCapsPtr caps)
 
868
{
 
869
    int n;
 
870
    unsigned long *mask = NULL;
 
871
    unsigned long *allonesmask = NULL;
 
872
    int *cpus = NULL;
 
873
    int ret = -1;
 
874
    int max_n_cpus = NUMA_MAX_N_CPUS;
 
875
 
 
876
    if (numa_available() < 0)
 
877
        return 0;
 
878
 
 
879
    int mask_n_bytes = max_n_cpus / 8;
 
880
    if (VIR_ALLOC_N(mask, mask_n_bytes / sizeof(*mask)) < 0)
 
881
        goto cleanup;
 
882
    if (VIR_ALLOC_N(allonesmask, mask_n_bytes / sizeof(*mask)) < 0)
 
883
        goto cleanup;
 
884
    memset(allonesmask, 0xff, mask_n_bytes);
 
885
 
 
886
    for (n = 0 ; n <= numa_max_node() ; n++) {
 
887
        int i;
 
888
        int ncpus;
 
889
        /* The first time this returns -1, ENOENT if node doesn't exist... */
 
890
        if (numa_node_to_cpus(n, mask, mask_n_bytes) < 0) {
 
891
            VIR_WARN("NUMA topology for cell %d of %d not available, ignoring",
 
892
                     n, numa_max_node()+1);
 
893
            continue;
 
894
        }
 
895
        /* second, third... times it returns an all-1's mask */
 
896
        if (memcmp(mask, allonesmask, mask_n_bytes) == 0) {
 
897
            VIR_DEBUG("NUMA topology for cell %d of %d is all ones, ignoring",
 
898
                      n, numa_max_node()+1);
 
899
            continue;
 
900
        }
 
901
 
 
902
        for (ncpus = 0, i = 0 ; i < max_n_cpus ; i++)
 
903
            if (MASK_CPU_ISSET(mask, i))
 
904
                ncpus++;
 
905
 
 
906
        if (VIR_ALLOC_N(cpus, ncpus) < 0)
 
907
            goto cleanup;
 
908
 
 
909
        for (ncpus = 0, i = 0 ; i < max_n_cpus ; i++)
 
910
            if (MASK_CPU_ISSET(mask, i))
 
911
                cpus[ncpus++] = i;
 
912
 
 
913
        if (virCapabilitiesAddHostNUMACell(caps,
 
914
                                           n,
 
915
                                           ncpus,
 
916
                                           cpus) < 0)
 
917
            goto cleanup;
 
918
 
 
919
        VIR_FREE(cpus);
 
920
    }
 
921
 
 
922
    ret = 0;
 
923
 
 
924
cleanup:
 
925
    VIR_FREE(cpus);
 
926
    VIR_FREE(mask);
 
927
    VIR_FREE(allonesmask);
 
928
    return ret;
 
929
}
 
930
 
 
931
 
 
932
int
 
933
nodeGetCellsFreeMemory(virConnectPtr conn ATTRIBUTE_UNUSED,
 
934
                       unsigned long long *freeMems,
 
935
                       int startCell,
 
936
                       int maxCells)
 
937
{
 
938
    int n, lastCell, numCells;
 
939
    int ret = -1;
 
940
    int maxCell;
 
941
 
 
942
    if (numa_available() < 0) {
 
943
        nodeReportError(VIR_ERR_INTERNAL_ERROR,
 
944
                        "%s", _("NUMA not supported on this host"));
 
945
        goto cleanup;
 
946
    }
 
947
    maxCell = numa_max_node();
 
948
    if (startCell > maxCell) {
 
949
        nodeReportError(VIR_ERR_INTERNAL_ERROR,
 
950
                        _("start cell %d out of range (0-%d)"),
 
951
                        startCell, maxCell);
 
952
        goto cleanup;
 
953
    }
 
954
    lastCell = startCell + maxCells - 1;
 
955
    if (lastCell > maxCell)
 
956
        lastCell = maxCell;
 
957
 
 
958
    for (numCells = 0, n = startCell ; n <= lastCell ; n++) {
 
959
        long long mem;
 
960
        if (numa_node_size64(n, &mem) < 0) {
 
961
            nodeReportError(VIR_ERR_INTERNAL_ERROR,
 
962
                           _("Failed to query NUMA free memory for node: %d"),
 
963
                           n);
 
964
            goto cleanup;
 
965
        }
 
966
        freeMems[numCells++] = mem;
 
967
    }
 
968
    ret = numCells;
 
969
 
 
970
cleanup:
 
971
    return ret;
 
972
}
 
973
 
 
974
unsigned long long
 
975
nodeGetFreeMemory(virConnectPtr conn ATTRIBUTE_UNUSED)
 
976
{
 
977
    unsigned long long freeMem = 0;
 
978
    int n;
 
979
 
 
980
    if (numa_available() < 0) {
 
981
        nodeReportError(VIR_ERR_INTERNAL_ERROR,
 
982
                        "%s", _("NUMA not supported on this host"));
 
983
        goto cleanup;
 
984
    }
 
985
 
 
986
    for (n = 0 ; n <= numa_max_node() ; n++) {
 
987
        long long mem;
 
988
        if (numa_node_size64(n, &mem) < 0) {
 
989
            nodeReportError(VIR_ERR_INTERNAL_ERROR,
 
990
                            "%s", _("Failed to query NUMA free memory"));
 
991
            goto cleanup;
 
992
        }
 
993
        freeMem += mem;
 
994
    }
 
995
 
 
996
cleanup:
 
997
    return freeMem;
 
998
}
 
999
 
 
1000
#else
 
1001
int nodeCapsInitNUMA(virCapsPtr caps ATTRIBUTE_UNUSED) {
 
1002
    return 0;
 
1003
}
 
1004
 
 
1005
int nodeGetCellsFreeMemory(virConnectPtr conn ATTRIBUTE_UNUSED,
 
1006
                              unsigned long long *freeMems ATTRIBUTE_UNUSED,
 
1007
                              int startCell ATTRIBUTE_UNUSED,
 
1008
                              int maxCells ATTRIBUTE_UNUSED)
 
1009
{
 
1010
    nodeReportError(VIR_ERR_NO_SUPPORT, "%s",
 
1011
                    _("NUMA memory information not available on this platform"));
 
1012
    return -1;
 
1013
}
 
1014
 
 
1015
unsigned long long nodeGetFreeMemory(virConnectPtr conn ATTRIBUTE_UNUSED)
 
1016
{
 
1017
    nodeReportError(VIR_ERR_NO_SUPPORT, "%s",
 
1018
                    _("NUMA memory information not available on this platform"));
 
1019
    return 0;
 
1020
}
 
1021
#endif