virNodeParseNode(const char *node, int *sockets, int *cores, int *threads, int *offline) { int ret = -1; int processors = 0; DIR *cpudir = NULL; struct dirent *cpudirent = NULL; int sock_max = 0; cpu_set_t sock_map; int sock; cpu_set_t *core_maps = NULL; int core; size_t i; int siblings; unsigned int cpu; int online; *threads = 0; *cores = 0; *sockets = 0; if (!(cpudir = opendir(node))) { virReportSystemError(errno, _("cannot opendir %s"), node); goto cleanup; } /* enumerate sockets in the node */ CPU_ZERO(&sock_map); errno = 0; while ((cpudirent = readdir(cpudir))) { if (sscanf(cpudirent->d_name, "cpu%u", &cpu) != 1) continue; if ((online = virNodeGetCpuValue(node, cpu, "online", 1)) < 0) goto cleanup; if (!online) continue; /* Parse socket */ if ((sock = virNodeParseSocket(node, cpu)) < 0) goto cleanup; CPU_SET(sock, &sock_map); if (sock > sock_max) sock_max = sock; errno = 0; } if (errno) { virReportSystemError(errno, _("problem reading %s"), node); goto cleanup; } sock_max++; /* allocate cpu maps for each socket */ if (VIR_ALLOC_N(core_maps, sock_max) < 0) goto cleanup; for (i = 0; i < sock_max; i++) CPU_ZERO(&core_maps[i]); /* iterate over all CPU's in the node */ rewinddir(cpudir); errno = 0; while ((cpudirent = readdir(cpudir))) { if (sscanf(cpudirent->d_name, "cpu%u", &cpu) != 1) continue; if ((online = virNodeGetCpuValue(node, cpu, "online", 1)) < 0) goto cleanup; if (!online) { (*offline)++; continue; } processors++; /* Parse socket */ if ((sock = virNodeParseSocket(node, cpu)) < 0) goto cleanup; if (!CPU_ISSET(sock, &sock_map)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("CPU socket topology has changed")); goto cleanup; } /* Parse core */ # if defined(__s390__) || \ defined(__s390x__) /* logical cpu is equivalent to a core on s390 */ core = cpu; # else core = virNodeGetCpuValue(node, cpu, "topology/core_id", 0); # endif CPU_SET(core, &core_maps[sock]); if (!(siblings = virNodeCountThreadSiblings(node, cpu))) goto cleanup; if (siblings > *threads) *threads = siblings; errno = 0; } if (errno) { virReportSystemError(errno, _("problem reading %s"), node); goto cleanup; } /* finalize the returned data */ *sockets = CPU_COUNT(&sock_map); for (i = 0; i < sock_max; i++) { if (!CPU_ISSET(i, &sock_map)) continue; core = CPU_COUNT(&core_maps[i]); if (core > *cores) *cores = core; } ret = processors; cleanup: /* don't shadow a more serious error */ if (cpudir && closedir(cpudir) < 0 && ret >= 0) { virReportSystemError(errno, _("problem closing %s"), node); ret = -1; } VIR_FREE(core_maps); return ret; }
int linuxNodeInfoCPUPopulate(FILE *cpuinfo, const char *sysfs_dir, virNodeInfoPtr nodeinfo) { char line[1024]; DIR *cpudir = NULL; struct dirent *cpudirent = NULL; unsigned int cpu; unsigned long core, sock, cur_threads; cpu_set_t core_mask; cpu_set_t socket_mask; int online; int ret = -1; char *sysfs_cpudir = NULL; unsigned int cpu_cores = 0; nodeinfo->cpus = 0; nodeinfo->mhz = 0; nodeinfo->cores = 0; /* Start with parsing /proc/cpuinfo; although it tends to have * fewer details. Hyperthreads are ignored at this stage. */ while (fgets(line, sizeof(line), cpuinfo) != NULL) { # if defined(__x86_64__) || \ defined(__amd64__) || \ defined(__i386__) char *buf = line; if (STRPREFIX(buf, "cpu MHz")) { char *p; unsigned int ui; buf += 9; while (*buf && c_isspace(*buf)) buf++; if (*buf != ':' || !buf[1]) { nodeReportError(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))) nodeinfo->mhz = ui; } if (STRPREFIX(buf, "cpu cores")) { char *p; unsigned int ui; buf += 9; while (*buf && c_isspace(*buf)) buf++; if (*buf != ':' || !buf[1]) { nodeReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("parsing cpu cores from cpuinfo")); return -1; } if (virStrToLong_ui(buf+1, &p, 10, &ui) == 0 && (*p == '\0' || c_isspace(*p))) cpu_cores = ui; } # elif defined(__powerpc__) || \ defined(__powerpc64__) char *buf = line; if (STRPREFIX(buf, "clock")) { char *p; unsigned int ui; buf += 5; while (*buf && c_isspace(*buf)) buf++; if (*buf != ':' || !buf[1]) { nodeReportError(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))) nodeinfo->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 # warning Parser for /proc/cpuinfo needs to be adapted for your architecture # endif } /* OK, we've parsed clock speed out of /proc/cpuinfo. Get the * core, node, socket, thread and topology information from /sys */ if (virAsprintf(&sysfs_cpudir, "%s/cpu", sysfs_dir) < 0) { virReportOOMError(); goto cleanup; } cpudir = opendir(sysfs_cpudir); if (cpudir == NULL) { virReportSystemError(errno, _("cannot opendir %s"), sysfs_cpudir); goto cleanup; } CPU_ZERO(&core_mask); CPU_ZERO(&socket_mask); while ((cpudirent = readdir(cpudir))) { if (sscanf(cpudirent->d_name, "cpu%u", &cpu) != 1) continue; online = virNodeGetCpuValue(sysfs_cpudir, cpu, "online", true); if (online < 0) { closedir(cpudir); goto cleanup; } if (!online) continue; nodeinfo->cpus++; /* Parse core */ core = virNodeGetCpuValue(sysfs_cpudir, cpu, "topology/core_id", false); if (!CPU_ISSET(core, &core_mask)) { CPU_SET(core, &core_mask); nodeinfo->cores++; } /* Parse socket */ sock = virNodeParseSocket(sysfs_cpudir, cpu); if (!CPU_ISSET(sock, &socket_mask)) { CPU_SET(sock, &socket_mask); nodeinfo->sockets++; } cur_threads = virNodeCountThreadSiblings(sysfs_cpudir, cpu); if (cur_threads == 0) { closedir(cpudir); goto cleanup; } if (cur_threads > nodeinfo->threads) nodeinfo->threads = cur_threads; } if (errno) { virReportSystemError(errno, _("problem reading %s"), sysfs_cpudir); closedir(cpudir); goto cleanup; } if (closedir(cpudir) < 0) { virReportSystemError(errno, _("problem closing %s"), sysfs_cpudir); goto cleanup; } if ((nodeinfo->nodes = virNodeParseNode(sysfs_dir)) <= 0) goto cleanup; /* There should always be at least one cpu, socket, node, and thread. */ if (nodeinfo->cpus == 0) { nodeReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("no CPUs found")); goto cleanup; } if (nodeinfo->sockets == 0) { nodeReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("no sockets found")); goto cleanup; } if (nodeinfo->threads == 0) { nodeReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("no threads found")); goto cleanup; } /* Platform like AMD Magny Cours has two NUMA nodes each package, and * the two nodes share the same core ID set, it results in the cores * number calculated from sysfs is not the actual cores number. Use * "cpu cores" in /proc/cpuinfo as the cores number instead in this case. * More details about the problem: * https://www.redhat.com/archives/libvir-list/2012-May/msg00607.html */ if (cpu_cores && (cpu_cores > nodeinfo->cores)) nodeinfo->cores = cpu_cores; /* nodeinfo->sockets is supposed to be a number of sockets per NUMA node, * however if NUMA nodes are not composed of whole sockets, we just lie * about the number of NUMA nodes and force apps to check capabilities XML * for the actual NUMA topology. */ if (nodeinfo->sockets % nodeinfo->nodes == 0) nodeinfo->sockets /= nodeinfo->nodes; else nodeinfo->nodes = 1; ret = 0; cleanup: VIR_FREE(sysfs_cpudir); return ret; }