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        ssize_t numread;
  20        int fd;
  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
  38unsigned int cpupower_write_sysfs(const char *path, char *buf, size_t buflen)
  39{
  40        ssize_t numwritten;
  41        int fd;
  42
  43        fd = open(path, O_WRONLY);
  44        if (fd == -1)
  45                return 0;
  46
  47        numwritten = write(fd, buf, buflen - 1);
  48        if (numwritten < 1) {
  49                perror(path);
  50                close(fd);
  51                return -1;
  52        }
  53
  54        close(fd);
  55
  56        return (unsigned int) numwritten;
  57}
  58
  59/*
  60 * Detect whether a CPU is online
  61 *
  62 * Returns:
  63 *     1 -> if CPU is online
  64 *     0 -> if CPU is offline
  65 *     negative errno values in error case
  66 */
  67int cpupower_is_cpu_online(unsigned int cpu)
  68{
  69        char path[SYSFS_PATH_MAX];
  70        int fd;
  71        ssize_t numread;
  72        unsigned long long value;
  73        char linebuf[MAX_LINE_LEN];
  74        char *endp;
  75        struct stat statbuf;
  76
  77        snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u", cpu);
  78
  79        if (stat(path, &statbuf) != 0)
  80                return 0;
  81
  82        /*
  83         * kernel without CONFIG_HOTPLUG_CPU
  84         * -> cpuX directory exists, but not cpuX/online file
  85         */
  86        snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/online", cpu);
  87        if (stat(path, &statbuf) != 0)
  88                return 1;
  89
  90        fd = open(path, O_RDONLY);
  91        if (fd == -1)
  92                return -errno;
  93
  94        numread = read(fd, linebuf, MAX_LINE_LEN - 1);
  95        if (numread < 1) {
  96                close(fd);
  97                return -EIO;
  98        }
  99        linebuf[numread] = '\0';
 100        close(fd);
 101
 102        value = strtoull(linebuf, &endp, 0);
 103        if (value > 1)
 104                return -EINVAL;
 105
 106        return value;
 107}
 108
 109/* returns -1 on failure, 0 on success */
 110static int sysfs_topology_read_file(unsigned int cpu, const char *fname, int *result)
 111{
 112        char linebuf[MAX_LINE_LEN];
 113        char *endp;
 114        char path[SYSFS_PATH_MAX];
 115
 116        snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/topology/%s",
 117                         cpu, fname);
 118        if (cpupower_read_sysfs(path, linebuf, MAX_LINE_LEN) == 0)
 119                return -1;
 120        *result = strtol(linebuf, &endp, 0);
 121        if (endp == linebuf || errno == ERANGE)
 122                return -1;
 123        return 0;
 124}
 125
 126static int __compare(const void *t1, const void *t2)
 127{
 128        struct cpuid_core_info *top1 = (struct cpuid_core_info *)t1;
 129        struct cpuid_core_info *top2 = (struct cpuid_core_info *)t2;
 130        if (top1->pkg < top2->pkg)
 131                return -1;
 132        else if (top1->pkg > top2->pkg)
 133                return 1;
 134        else if (top1->core < top2->core)
 135                return -1;
 136        else if (top1->core > top2->core)
 137                return 1;
 138        else if (top1->cpu < top2->cpu)
 139                return -1;
 140        else if (top1->cpu > top2->cpu)
 141                return 1;
 142        else
 143                return 0;
 144}
 145
 146/*
 147 * Returns amount of cpus, negative on error, cpu_top must be
 148 * passed to cpu_topology_release to free resources
 149 *
 150 * Array is sorted after ->pkg, ->core, then ->cpu
 151 */
 152int get_cpu_topology(struct cpupower_topology *cpu_top)
 153{
 154        int cpu, last_pkg, cpus = sysconf(_SC_NPROCESSORS_CONF);
 155
 156        cpu_top->core_info = malloc(sizeof(struct cpuid_core_info) * cpus);
 157        if (cpu_top->core_info == NULL)
 158                return -ENOMEM;
 159        cpu_top->pkgs = cpu_top->cores = 0;
 160        for (cpu = 0; cpu < cpus; cpu++) {
 161                cpu_top->core_info[cpu].cpu = cpu;
 162                cpu_top->core_info[cpu].is_online = cpupower_is_cpu_online(cpu);
 163                if(sysfs_topology_read_file(
 164                        cpu,
 165                        "physical_package_id",
 166                        &(cpu_top->core_info[cpu].pkg)) < 0) {
 167                        cpu_top->core_info[cpu].pkg = -1;
 168                        cpu_top->core_info[cpu].core = -1;
 169                        continue;
 170                }
 171                if(sysfs_topology_read_file(
 172                        cpu,
 173                        "core_id",
 174                        &(cpu_top->core_info[cpu].core)) < 0) {
 175                        cpu_top->core_info[cpu].pkg = -1;
 176                        cpu_top->core_info[cpu].core = -1;
 177                        continue;
 178                }
 179        }
 180
 181        qsort(cpu_top->core_info, cpus, sizeof(struct cpuid_core_info),
 182              __compare);
 183
 184        /* Count the number of distinct pkgs values. This works
 185           because the primary sort of the core_info struct was just
 186           done by pkg value. */
 187        last_pkg = cpu_top->core_info[0].pkg;
 188        for(cpu = 1; cpu < cpus; cpu++) {
 189                if (cpu_top->core_info[cpu].pkg != last_pkg &&
 190                                cpu_top->core_info[cpu].pkg != -1) {
 191
 192                        last_pkg = cpu_top->core_info[cpu].pkg;
 193                        cpu_top->pkgs++;
 194                }
 195        }
 196        if (!(cpu_top->core_info[0].pkg == -1))
 197                cpu_top->pkgs++;
 198
 199        /* Intel's cores count is not consecutively numbered, there may
 200         * be a core_id of 3, but none of 2. Assume there always is 0
 201         * Get amount of cores by counting duplicates in a package
 202        for (cpu = 0; cpu_top->core_info[cpu].pkg = 0 && cpu < cpus; cpu++) {
 203                if (cpu_top->core_info[cpu].core == 0)
 204        cpu_top->cores++;
 205        */
 206        return cpus;
 207}
 208
 209void cpu_topology_release(struct cpupower_topology cpu_top)
 210{
 211        free(cpu_top.core_info);
 212}
 213