linux/tools/power/cpupower/lib/cpupower.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 *  (C) 2004-2009  Dominik Brodowski <linux@dominikbrodowski.de>
   4 */
   5
   6#include <sys/types.h>
   7#include <sys/stat.h>
   8#include <fcntl.h>
   9#include <unistd.h>
  10#include <stdio.h>
  11#include <errno.h>
  12#include <stdlib.h>
  13
  14#include "cpupower.h"
  15#include "cpupower_intern.h"
  16
  17unsigned int cpupower_read_sysfs(const char *path, char *buf, size_t buflen)
  18{
  19        int fd;
  20        ssize_t numread;
  21
  22        fd = open(path, O_RDONLY);
  23        if (fd == -1)
  24                return 0;
  25
  26        numread = read(fd, buf, buflen - 1);
  27        if (numread < 1) {
  28                close(fd);
  29                return 0;
  30        }
  31
  32        buf[numread] = '\0';
  33        close(fd);
  34
  35        return (unsigned int) numread;
  36}
  37
  38/*
  39 * Detect whether a CPU is online
  40 *
  41 * Returns:
  42 *     1 -> if CPU is online
  43 *     0 -> if CPU is offline
  44 *     negative errno values in error case
  45 */
  46int cpupower_is_cpu_online(unsigned int cpu)
  47{
  48        char path[SYSFS_PATH_MAX];
  49        int fd;
  50        ssize_t numread;
  51        unsigned long long value;
  52        char linebuf[MAX_LINE_LEN];
  53        char *endp;
  54        struct stat statbuf;
  55
  56        snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u", cpu);
  57
  58        if (stat(path, &statbuf) != 0)
  59                return 0;
  60
  61        /*
  62         * kernel without CONFIG_HOTPLUG_CPU
  63         * -> cpuX directory exists, but not cpuX/online file
  64         */
  65        snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/online", cpu);
  66        if (stat(path, &statbuf) != 0)
  67                return 1;
  68
  69        fd = open(path, O_RDONLY);
  70        if (fd == -1)
  71                return -errno;
  72
  73        numread = read(fd, linebuf, MAX_LINE_LEN - 1);
  74        if (numread < 1) {
  75                close(fd);
  76                return -EIO;
  77        }
  78        linebuf[numread] = '\0';
  79        close(fd);
  80
  81        value = strtoull(linebuf, &endp, 0);
  82        if (value > 1)
  83                return -EINVAL;
  84
  85        return value;
  86}
  87
  88/* returns -1 on failure, 0 on success */
  89static int sysfs_topology_read_file(unsigned int cpu, const char *fname, int *result)
  90{
  91        char linebuf[MAX_LINE_LEN];
  92        char *endp;
  93        char path[SYSFS_PATH_MAX];
  94
  95        snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/topology/%s",
  96                         cpu, fname);
  97        if (cpupower_read_sysfs(path, linebuf, MAX_LINE_LEN) == 0)
  98                return -1;
  99        *result = strtol(linebuf, &endp, 0);
 100        if (endp == linebuf || errno == ERANGE)
 101                return -1;
 102        return 0;
 103}
 104
 105static int __compare(const void *t1, const void *t2)
 106{
 107        struct cpuid_core_info *top1 = (struct cpuid_core_info *)t1;
 108        struct cpuid_core_info *top2 = (struct cpuid_core_info *)t2;
 109        if (top1->pkg < top2->pkg)
 110                return -1;
 111        else if (top1->pkg > top2->pkg)
 112                return 1;
 113        else if (top1->core < top2->core)
 114                return -1;
 115        else if (top1->core > top2->core)
 116                return 1;
 117        else if (top1->cpu < top2->cpu)
 118                return -1;
 119        else if (top1->cpu > top2->cpu)
 120                return 1;
 121        else
 122                return 0;
 123}
 124
 125/*
 126 * Returns amount of cpus, negative on error, cpu_top must be
 127 * passed to cpu_topology_release to free resources
 128 *
 129 * Array is sorted after ->pkg, ->core, then ->cpu
 130 */
 131int get_cpu_topology(struct cpupower_topology *cpu_top)
 132{
 133        int cpu, last_pkg, cpus = sysconf(_SC_NPROCESSORS_CONF);
 134
 135        cpu_top->core_info = malloc(sizeof(struct cpuid_core_info) * cpus);
 136        if (cpu_top->core_info == NULL)
 137                return -ENOMEM;
 138        cpu_top->pkgs = cpu_top->cores = 0;
 139        for (cpu = 0; cpu < cpus; cpu++) {
 140                cpu_top->core_info[cpu].cpu = cpu;
 141                cpu_top->core_info[cpu].is_online = cpupower_is_cpu_online(cpu);
 142                if(sysfs_topology_read_file(
 143                        cpu,
 144                        "physical_package_id",
 145                        &(cpu_top->core_info[cpu].pkg)) < 0) {
 146                        cpu_top->core_info[cpu].pkg = -1;
 147                        cpu_top->core_info[cpu].core = -1;
 148                        continue;
 149                }
 150                if(sysfs_topology_read_file(
 151                        cpu,
 152                        "core_id",
 153                        &(cpu_top->core_info[cpu].core)) < 0) {
 154                        cpu_top->core_info[cpu].pkg = -1;
 155                        cpu_top->core_info[cpu].core = -1;
 156                        continue;
 157                }
 158        }
 159
 160        qsort(cpu_top->core_info, cpus, sizeof(struct cpuid_core_info),
 161              __compare);
 162
 163        /* Count the number of distinct pkgs values. This works
 164           because the primary sort of the core_info struct was just
 165           done by pkg value. */
 166        last_pkg = cpu_top->core_info[0].pkg;
 167        for(cpu = 1; cpu < cpus; cpu++) {
 168                if (cpu_top->core_info[cpu].pkg != last_pkg &&
 169                                cpu_top->core_info[cpu].pkg != -1) {
 170
 171                        last_pkg = cpu_top->core_info[cpu].pkg;
 172                        cpu_top->pkgs++;
 173                }
 174        }
 175        if (!(cpu_top->core_info[0].pkg == -1))
 176                cpu_top->pkgs++;
 177
 178        /* Intel's cores count is not consecutively numbered, there may
 179         * be a core_id of 3, but none of 2. Assume there always is 0
 180         * Get amount of cores by counting duplicates in a package
 181        for (cpu = 0; cpu_top->core_info[cpu].pkg = 0 && cpu < cpus; cpu++) {
 182                if (cpu_top->core_info[cpu].core == 0)
 183        cpu_top->cores++;
 184        */
 185        return cpus;
 186}
 187
 188void cpu_topology_release(struct cpupower_topology cpu_top)
 189{
 190        free(cpu_top.core_info);
 191}
 192