LCOV - code coverage report
Current view: top level - shared - cpu-set-util.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 129 201 64.2 %
Date: 2019-08-22 15:41:25 Functions: 11 15 73.3 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <errno.h>
       4             : #include <stddef.h>
       5             : #include <stdio.h>
       6             : #include <syslog.h>
       7             : 
       8             : #include "alloc-util.h"
       9             : #include "cpu-set-util.h"
      10             : #include "dirent-util.h"
      11             : #include "errno-util.h"
      12             : #include "extract-word.h"
      13             : #include "fd-util.h"
      14             : #include "log.h"
      15             : #include "macro.h"
      16             : #include "memory-util.h"
      17             : #include "missing_syscall.h"
      18             : #include "parse-util.h"
      19             : #include "stat-util.h"
      20             : #include "string-util.h"
      21             : #include "string-table.h"
      22             : #include "strv.h"
      23             : #include "util.h"
      24             : 
      25          14 : char* cpu_set_to_string(const CPUSet *a) {
      26          14 :         _cleanup_free_ char *str = NULL;
      27          14 :         size_t allocated = 0, len = 0;
      28             :         int i, r;
      29             : 
      30        9230 :         for (i = 0; (size_t) i < a->allocated * 8; i++) {
      31        9216 :                 if (!CPU_ISSET_S(i, a->allocated, a->set))
      32        8844 :                         continue;
      33             : 
      34         372 :                 if (!GREEDY_REALLOC(str, allocated, len + 1 + DECIMAL_STR_MAX(int)))
      35           0 :                         return NULL;
      36             : 
      37         372 :                 r = sprintf(str + len, len > 0 ? " %d" : "%d", i);
      38         372 :                 assert_se(r > 0);
      39         372 :                 len += r;
      40             :         }
      41             : 
      42          14 :         return TAKE_PTR(str) ?: strdup("");
      43             : }
      44             : 
      45          10 : char *cpu_set_to_range_string(const CPUSet *set) {
      46          10 :         unsigned range_start = 0, range_end;
      47          10 :         _cleanup_free_ char *str = NULL;
      48          10 :         size_t allocated = 0, len = 0;
      49          10 :         bool in_range = false;
      50             :         int r;
      51             : 
      52        8714 :         for (unsigned i = 0; i < set->allocated * 8; i++)
      53        8704 :                 if (CPU_ISSET_S(i, set->allocated, set->set)) {
      54         247 :                         if (in_range)
      55         232 :                                 range_end++;
      56             :                         else {
      57          15 :                                 range_start = range_end = i;
      58          15 :                                 in_range = true;
      59             :                         }
      60        8457 :                 } else if (in_range) {
      61          13 :                         in_range = false;
      62             : 
      63          13 :                         if (!GREEDY_REALLOC(str, allocated, len + 2 + 2 * DECIMAL_STR_MAX(unsigned)))
      64           0 :                                 return NULL;
      65             : 
      66          13 :                         if (range_end > range_start)
      67           9 :                                 r = sprintf(str + len, len > 0 ? " %d-%d" : "%d-%d", range_start, range_end);
      68             :                         else
      69           4 :                                 r = sprintf(str + len, len > 0 ? " %d" : "%d", range_start);
      70          13 :                         assert_se(r > 0);
      71          13 :                         len += r;
      72             :                 }
      73             : 
      74          10 :         if (in_range) {
      75           2 :                 if (!GREEDY_REALLOC(str, allocated, len + 2 + 2 * DECIMAL_STR_MAX(int)))
      76           0 :                         return NULL;
      77             : 
      78           2 :                 if (range_end > range_start)
      79           1 :                         r = sprintf(str + len, len > 0 ? " %d-%d" : "%d-%d", range_start, range_end);
      80             :                 else
      81           1 :                         r = sprintf(str + len, len > 0 ? " %d" : "%d", range_start);
      82           2 :                 assert_se(r > 0);
      83             :         }
      84             : 
      85          10 :         return TAKE_PTR(str) ?: strdup("");
      86             : }
      87             : 
      88         492 : int cpu_set_realloc(CPUSet *cpu_set, unsigned ncpus) {
      89             :         size_t need;
      90             : 
      91         492 :         assert(cpu_set);
      92             : 
      93         492 :         need = CPU_ALLOC_SIZE(ncpus);
      94         492 :         if (need > cpu_set->allocated) {
      95             :                 cpu_set_t *t;
      96             : 
      97          20 :                 t = realloc(cpu_set->set, need);
      98          20 :                 if (!t)
      99           0 :                         return -ENOMEM;
     100             : 
     101          20 :                 memzero((uint8_t*) t + cpu_set->allocated, need - cpu_set->allocated);
     102             : 
     103          20 :                 cpu_set->set = t;
     104          20 :                 cpu_set->allocated = need;
     105             :         }
     106             : 
     107         492 :         return 0;
     108             : }
     109             : 
     110         491 : static int cpu_set_add(CPUSet *cpu_set, unsigned cpu) {
     111             :         int r;
     112             : 
     113         491 :         if (cpu >= 8192)
     114             :                 /* As of kernel 5.1, CONFIG_NR_CPUS can be set to 8192 on PowerPC */
     115           0 :                 return -ERANGE;
     116             : 
     117         491 :         r = cpu_set_realloc(cpu_set, cpu + 1);
     118         491 :         if (r < 0)
     119           0 :                 return r;
     120             : 
     121         491 :         CPU_SET_S(cpu, cpu_set->allocated, cpu_set->set);
     122         491 :         return 0;
     123             : }
     124             : 
     125           1 : int cpu_set_add_all(CPUSet *a, const CPUSet *b) {
     126             :         int r;
     127             : 
     128             :         /* Do this backwards, so if we fail, we fail before changing anything. */
     129          65 :         for (unsigned cpu_p1 = b->allocated * 8; cpu_p1 > 0; cpu_p1--)
     130          64 :                 if (CPU_ISSET_S(cpu_p1 - 1, b->allocated, b->set)) {
     131           1 :                         r = cpu_set_add(a, cpu_p1 - 1);
     132           1 :                         if (r < 0)
     133           0 :                                 return r;
     134             :                 }
     135             : 
     136           1 :         return 0;
     137             : }
     138             : 
     139          20 : int parse_cpu_set_full(
     140             :                 const char *rvalue,
     141             :                 CPUSet *cpu_set,
     142             :                 bool warn,
     143             :                 const char *unit,
     144             :                 const char *filename,
     145             :                 unsigned line,
     146             :                 const char *lvalue) {
     147             : 
     148          20 :         _cleanup_(cpu_set_reset) CPUSet c = {};
     149          20 :         const char *p = rvalue;
     150             : 
     151          20 :         assert(p);
     152             : 
     153          60 :         for (;;) {
     154          80 :                 _cleanup_free_ char *word = NULL;
     155             :                 unsigned cpu_lower, cpu_upper;
     156             :                 int r;
     157             : 
     158          80 :                 r = extract_first_word(&p, &word, WHITESPACE ",", EXTRACT_UNQUOTE);
     159          80 :                 if (r == -ENOMEM)
     160           0 :                         return warn ? log_oom() : -ENOMEM;
     161          80 :                 if (r < 0)
     162           1 :                         return warn ? log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, rvalue) : r;
     163          79 :                 if (r == 0)
     164          17 :                         break;
     165             : 
     166          62 :                 r = parse_range(word, &cpu_lower, &cpu_upper);
     167          62 :                 if (r < 0)
     168           2 :                         return warn ? log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CPU affinity '%s'", word) : r;
     169             : 
     170          60 :                 if (cpu_lower > cpu_upper) {
     171           1 :                         if (warn)
     172           1 :                                 log_syntax(unit, LOG_WARNING, filename, line, 0, "Range '%s' is invalid, %u > %u, ignoring.",
     173             :                                            word, cpu_lower, cpu_upper);
     174             : 
     175             :                         /* Make sure something is allocated, to distinguish this from the empty case */
     176           1 :                         r = cpu_set_realloc(&c, 1);
     177           1 :                         if (r < 0)
     178           0 :                                 return r;
     179             :                 }
     180             : 
     181         446 :                 for (unsigned cpu_p1 = MIN(cpu_upper, UINT_MAX-1) + 1; cpu_p1 > cpu_lower; cpu_p1--) {
     182         386 :                         r = cpu_set_add(&c, cpu_p1 - 1);
     183         386 :                         if (r < 0)
     184           0 :                                 return warn ? log_syntax(unit, LOG_ERR, filename, line, r,
     185           0 :                                                          "Cannot add CPU %u to set: %m", cpu_p1 - 1) : r;
     186             :                 }
     187             :         }
     188             : 
     189             :         /* On success, transfer ownership to the output variable */
     190          17 :         *cpu_set = c;
     191          17 :         c = (CPUSet) {};
     192             : 
     193          17 :         return 0;
     194             : }
     195             : 
     196           4 : int parse_cpu_set_extend(
     197             :                 const char *rvalue,
     198             :                 CPUSet *old,
     199             :                 bool warn,
     200             :                 const char *unit,
     201             :                 const char *filename,
     202             :                 unsigned line,
     203             :                 const char *lvalue) {
     204             : 
     205           4 :         _cleanup_(cpu_set_reset) CPUSet cpuset = {};
     206             :         int r;
     207             : 
     208           4 :         r = parse_cpu_set_full(rvalue, &cpuset, true, unit, filename, line, lvalue);
     209           4 :         if (r < 0)
     210           0 :                 return r;
     211             : 
     212           4 :         if (!cpuset.set) {
     213             :                 /* An empty assignment resets the CPU list */
     214           1 :                 cpu_set_reset(old);
     215           1 :                 return 0;
     216             :         }
     217             : 
     218           3 :         if (!old->set) {
     219           2 :                 *old = cpuset;
     220           2 :                 cpuset = (CPUSet) {};
     221           2 :                 return 0;
     222             :         }
     223             : 
     224           1 :         return cpu_set_add_all(old, &cpuset);
     225             : }
     226             : 
     227          20 : int cpus_in_affinity_mask(void) {
     228          20 :         size_t n = 16;
     229             :         int r;
     230             : 
     231           0 :         for (;;) {
     232             :                 cpu_set_t *c;
     233             : 
     234          20 :                 c = CPU_ALLOC(n);
     235          20 :                 if (!c)
     236           0 :                         return -ENOMEM;
     237             : 
     238          20 :                 if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), c) >= 0) {
     239             :                         int k;
     240             : 
     241          20 :                         k = CPU_COUNT_S(CPU_ALLOC_SIZE(n), c);
     242          20 :                         CPU_FREE(c);
     243             : 
     244          20 :                         if (k <= 0)
     245           0 :                                 return -EINVAL;
     246             : 
     247          20 :                         return k;
     248             :                 }
     249             : 
     250           0 :                 r = -errno;
     251           0 :                 CPU_FREE(c);
     252             : 
     253           0 :                 if (r != -EINVAL)
     254           0 :                         return r;
     255           0 :                 if (n > SIZE_MAX/2)
     256           0 :                         return -ENOMEM;
     257           0 :                 n *= 2;
     258             :         }
     259             : }
     260             : 
     261           1 : int cpu_set_to_dbus(const CPUSet *set, uint8_t **ret, size_t *allocated) {
     262             :         uint8_t *out;
     263             : 
     264           1 :         assert(set);
     265           1 :         assert(ret);
     266             : 
     267           1 :         out = new0(uint8_t, set->allocated);
     268           1 :         if (!out)
     269           0 :                 return -ENOMEM;
     270             : 
     271         257 :         for (unsigned cpu = 0; cpu < set->allocated * 8; cpu++)
     272         256 :                 if (CPU_ISSET_S(cpu, set->allocated, set->set))
     273         104 :                         out[cpu / 8] |= 1u << (cpu % 8);
     274             : 
     275           1 :         *ret = out;
     276           1 :         *allocated = set->allocated;
     277           1 :         return 0;
     278             : }
     279             : 
     280           1 : int cpu_set_from_dbus(const uint8_t *bits, size_t size, CPUSet *set) {
     281           1 :         _cleanup_(cpu_set_reset) CPUSet s = {};
     282             :         int r;
     283             : 
     284           1 :         assert(bits);
     285           1 :         assert(set);
     286             : 
     287         257 :         for (unsigned cpu = size * 8; cpu > 0; cpu--)
     288         256 :                 if (bits[(cpu - 1) / 8] & (1u << ((cpu - 1) % 8))) {
     289         104 :                         r = cpu_set_add(&s, cpu - 1);
     290         104 :                         if (r < 0)
     291           0 :                                 return r;
     292             :                 }
     293             : 
     294           1 :         *set = s;
     295           1 :         s = (CPUSet) {};
     296           1 :         return 0;
     297             : }
     298             : 
     299           0 : bool numa_policy_is_valid(const NUMAPolicy *policy) {
     300           0 :         assert(policy);
     301             : 
     302           0 :         if (!mpol_is_valid(numa_policy_get_type(policy)))
     303           0 :                 return false;
     304             : 
     305           0 :         if (!policy->nodes.set &&
     306           0 :             !IN_SET(numa_policy_get_type(policy), MPOL_DEFAULT, MPOL_LOCAL, MPOL_PREFERRED))
     307           0 :                 return false;
     308             : 
     309           0 :         if (policy->nodes.set &&
     310           0 :             numa_policy_get_type(policy) == MPOL_PREFERRED &&
     311           0 :             CPU_COUNT_S(policy->nodes.allocated, policy->nodes.set) != 1)
     312           0 :                 return false;
     313             : 
     314           0 :         return true;
     315             : }
     316             : 
     317           0 : static int numa_policy_to_mempolicy(const NUMAPolicy *policy, unsigned long *ret_maxnode, unsigned long **ret_nodes) {
     318           0 :         unsigned node, bits = 0, ulong_bits;
     319           0 :         _cleanup_free_ unsigned long *out = NULL;
     320             : 
     321           0 :         assert(policy);
     322           0 :         assert(ret_maxnode);
     323           0 :         assert(ret_nodes);
     324             : 
     325           0 :         if (IN_SET(numa_policy_get_type(policy), MPOL_DEFAULT, MPOL_LOCAL) ||
     326           0 :             (numa_policy_get_type(policy) == MPOL_PREFERRED && !policy->nodes.set)) {
     327           0 :                 *ret_nodes = NULL;
     328           0 :                 *ret_maxnode = 0;
     329           0 :                 return 0;
     330             :         }
     331             : 
     332           0 :         bits = policy->nodes.allocated * 8;
     333           0 :         ulong_bits = sizeof(unsigned long) * 8;
     334             : 
     335           0 :         out = new0(unsigned long, DIV_ROUND_UP(policy->nodes.allocated, sizeof(unsigned long)));
     336           0 :         if (!out)
     337           0 :                 return -ENOMEM;
     338             : 
     339             :         /* We don't make any assumptions about internal type libc is using to store NUMA node mask.
     340             :            Hence we need to convert the node mask to the representation expected by set_mempolicy() */
     341           0 :         for (node = 0; node < bits; node++)
     342           0 :                 if (CPU_ISSET_S(node, policy->nodes.allocated, policy->nodes.set))
     343           0 :                         out[node / ulong_bits] |= 1ul << (node % ulong_bits);
     344             : 
     345           0 :         *ret_nodes = TAKE_PTR(out);
     346           0 :         *ret_maxnode = bits + 1;
     347           0 :         return 0;
     348             : }
     349             : 
     350           0 : int apply_numa_policy(const NUMAPolicy *policy) {
     351             :         int r;
     352           0 :         _cleanup_free_ unsigned long *nodes = NULL;
     353             :         unsigned long maxnode;
     354             : 
     355           0 :         assert(policy);
     356             : 
     357           0 :         if (get_mempolicy(NULL, NULL, 0, 0, 0) < 0 && errno == ENOSYS)
     358           0 :                 return -EOPNOTSUPP;
     359             : 
     360           0 :         if (!numa_policy_is_valid(policy))
     361           0 :                 return -EINVAL;
     362             : 
     363           0 :         r = numa_policy_to_mempolicy(policy, &maxnode, &nodes);
     364           0 :         if (r < 0)
     365           0 :                 return r;
     366             : 
     367           0 :         r = set_mempolicy(numa_policy_get_type(policy), nodes, maxnode);
     368           0 :         if (r < 0)
     369           0 :                 return -errno;
     370             : 
     371           0 :         return 0;
     372             : }
     373             : 
     374             : static const char* const mpol_table[] = {
     375             :         [MPOL_DEFAULT]    = "default",
     376             :         [MPOL_PREFERRED]  = "preferred",
     377             :         [MPOL_BIND]       = "bind",
     378             :         [MPOL_INTERLEAVE] = "interleave",
     379             :         [MPOL_LOCAL]      = "local",
     380             : };
     381             : 
     382           1 : DEFINE_STRING_TABLE_LOOKUP(mpol, int);

Generated by: LCOV version 1.14