blob: 0b4b24921713600b2d03c59d5598eb434c736754 [file] [log] [blame]
Pau Espin Pedroleb6882f2020-07-28 11:57:51 +02001/*! \file cpu_sched_vty.c
2 * Implementation to CPU / Threading / Scheduler properties from VTY configuration.
3 */
4/* (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
5 *
6 * Author: Pau Espin Pedrol <pespin@sysmocom.de>
7 *
8 * All Rights Reserved
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 *
23 * SPDX-License-Identifier: GPLv2+
24 */
25
26#define _GNU_SOURCE
27
28#include <string.h>
29#include <stdlib.h>
30#include <errno.h>
31#include <limits.h>
32#include <unistd.h>
33#include <sched.h>
34#include <ctype.h>
35#include <sys/types.h>
36#include <sys/stat.h>
37#include <fcntl.h>
38#include <dirent.h>
39#include <pthread.h>
40#include <inttypes.h>
41
42#include <osmocom/vty/vty.h>
43#include <osmocom/vty/command.h>
44#include <osmocom/vty/tdef_vty.h>
45#include <osmocom/core/tdef.h>
46#include <osmocom/core/fsm.h>
47#include <osmocom/core/linuxlist.h>
48
49/*! \addtogroup Tdef_VTY
50 *
51 * CPU Scheduling related VTY API.
52 *
53 * @{
54 * \file cpu_sched_vty.c
55 */
56
57enum sched_vty_thread_id {
58 SCHED_VTY_THREAD_SELF,
59 SCHED_VTY_THREAD_ALL,
60 SCHED_VTY_THREAD_ID,
61 SCHED_VTY_THREAD_NAME,
62 SCHED_VTY_THREAD_UNKNOWN,
63};
64
65struct cpu_affinity_it {
66 struct llist_head entry;
67 enum sched_vty_thread_id tid_type;
68 char bufname[64];
69 cpu_set_t *cpuset;
70 size_t cpuset_size;
71 bool delay;
72};
73
74struct sched_vty_opts {
75 void *tall_ctx;
76 int sched_rr_prio;
77 struct llist_head cpu_affinity_li;
78 pthread_mutex_t cpu_affinity_li_mutex;
79};
80
81static struct sched_vty_opts *sched_vty_opts;
82
83static struct cmd_node sched_node = {
84 L_CPU_SCHED_NODE,
Pau Espin Pedrol62be97e2020-08-18 12:29:57 +020085 "%s(config-cpu-sched)# ",
Pau Espin Pedroleb6882f2020-07-28 11:57:51 +020086 1,
87};
88
89/* returns number of configured CPUs in the system, or negative otherwise */
90static int get_num_cpus() {
91 static unsigned int num_cpus = 0;
92 long ln;
93
94 if (num_cpus)
95 return num_cpus;
96
97 /* This is expensive (goes across /sys, so let's do it only once. It is
98 * guaranteed it won't change during process span anyway). */
99 ln = sysconf(_SC_NPROCESSORS_CONF);
100 if (ln < 0) {
101 LOGP(DLGLOBAL, LOGL_ERROR, "sysconf(_SC_NPROCESSORS_CONF) failed: %s\n",
102 strerror(errno));
103 return -1;
104 }
105 num_cpus = (unsigned int) ln;
106 return num_cpus;
107}
108
109/* Parses string with CPU hex Affinity Mask, with right-most bit being CPU0, and
110 * fills a cpuset of size cpuset_size.
111 */
112static int parse_cpu_hex_mask(const char *str, cpu_set_t *cpuset, size_t cpuset_size)
113{
114 int len = strlen(str);
115 const char *ptr = str + len - 1;
116 int cpu = 0;
117
118 /* skip optional '0x' prefix format */
119 if (len >= 2 && str[0] == '0' && str[1] == 'x')
120 str += 2;
121 CPU_ZERO_S(cpuset_size, cpuset);
122
123 while (ptr >= str) {
124 char c = *ptr;
125 uint8_t val;
126
127 if (c >= '0' && c <= '9') {
128 val = c - '0';
129 } else {
130 c = (char)tolower((int)c);
131 if (c >= 'a' && c <= 'f')
132 val = c + (10 - 'a');
133 else
134 return -1;
135 }
136 if (val & 0x01)
137 CPU_SET_S(cpu, cpuset_size, cpuset);
138 if (val & 0x02)
139 CPU_SET_S(cpu + 1, cpuset_size, cpuset);
140 if (val & 0x04)
141 CPU_SET_S(cpu + 2, cpuset_size, cpuset);
142 if (val & 0x08)
143 CPU_SET_S(cpu + 3, cpuset_size, cpuset);
144 ptr--;
145 cpu += 4;
146 }
147
148 return 0;
149}
150
151/* Generates a hexstring in str from cpuset of size cpuset_size */
152static int generate_cpu_hex_mask(char *str, size_t str_buf_size,
153 cpu_set_t *cpuset, size_t cpuset_size)
154{
155 char *ptr = str;
156 int cpu;
157 bool first_nonzero_found = false;
158
159 /* 2 char per byte, + '0x' prefix + '\0' */
160 if (cpuset_size * 2 + 2 + 1 > str_buf_size)
161 return -1;
162
163 *ptr++ = '0';
164 *ptr++ = 'x';
165
166 for (cpu = cpuset_size*8 - 4; cpu >= 0; cpu -= 4) {
167 uint8_t val = 0;
168
169 if (CPU_ISSET_S(cpu, cpuset_size, cpuset))
170 val |= 0x01;
171 if (CPU_ISSET_S(cpu + 1, cpuset_size, cpuset))
172 val |= 0x02;
173 if (CPU_ISSET_S(cpu + 2, cpuset_size, cpuset))
174 val |= 0x04;
175 if (CPU_ISSET_S(cpu + 3, cpuset_size, cpuset))
176 val |= 0x08;
177
Vadim Yanitskiye0ed1472020-09-11 20:13:19 +0700178 if (val < 10)
Pau Espin Pedroleb6882f2020-07-28 11:57:51 +0200179 *ptr = '0' + val;
180 else
181 *ptr = ('a' - 10) + val;
182 if (val)
183 first_nonzero_found = true;
184 if (first_nonzero_found)
185 ptr++;
186
187 }
188 if (!first_nonzero_found)
189 *ptr++ = '0';
190 *ptr = '\0';
191 return 0;
192}
193
194/* Checks whther a thread identified by tid exists and belongs to the running process */
195static bool proc_tid_exists(pid_t tid)
196{
197 DIR *proc_dir;
198 struct dirent *entry;
199 char dirname[100];
200 int tid_it;
201 bool found = false;
202
203 snprintf(dirname, sizeof(dirname), "/proc/%ld/task", (long int)getpid());
204 proc_dir = opendir(dirname);
205 if (!proc_dir)
206 return false; /*FIXME; print error */
207
208 while ((entry = readdir(proc_dir))) {
209 if (entry->d_name[0] == '.')
210 continue;
211 tid_it = atoi(entry->d_name);
212 if (tid_it == tid) {
213 found = true;
214 break;
215 }
216 }
217
218 closedir(proc_dir);
219 return found;
220}
221
222/* Checks whther a thread identified by name exists and belongs to the running
223 * process, and returns its disocevered TID in res_pid.
224 */
225static bool proc_name_exists(const char *name, pid_t *res_pid)
226{
227 DIR *proc_dir;
228 struct dirent *entry;
229 char path[100];
230 char buf[17]; /* 15 + \n + \0 */
231 int tid_it;
232 int fd;
233 pid_t mypid = getpid();
234 bool found = false;
235 int rc;
236
237 *res_pid = 0;
238
239 snprintf(path, sizeof(path), "/proc/%ld/task", (long int)mypid);
240 proc_dir = opendir(path);
241 if (!proc_dir)
242 return false;
243
244 while ((entry = readdir(proc_dir)))
245 {
246 if (entry->d_name[0] == '.')
247 continue;
248
249 tid_it = atoi(entry->d_name);
250 snprintf(path, sizeof(path), "/proc/%ld/task/%ld/comm", (long int)mypid, (long int) tid_it);
251 if ((fd = open(path, O_RDONLY)) == -1)
252 continue;
253 rc = read(fd, buf, sizeof(buf) - 1);
254 if (rc >= 0) {
255 /* Last may char contain a '\n', get rid of it */
256 if (rc > 0 && buf[rc - 1] == '\n')
257 buf[rc - 1] = '\0';
258 else
259 buf[rc] = '\0';
260 if (strcmp(name, buf) == 0) {
261 *res_pid = tid_it;
262 found = true;
263 }
264 }
265 close(fd);
266
267 if (found)
268 break;
269 }
270
271 closedir(proc_dir);
272 return found;
273}
274
275/* Parse VTY THREADNAME variable, return its type and fill discovered res_pid if required */
276static enum sched_vty_thread_id procname2pid(pid_t *res_pid, const char *str, bool applynow)
277{
278 size_t i, len;
Pau Espin Pedroleb6882f2020-07-28 11:57:51 +0200279 bool is_pid = true;
280
281 if (strcmp(str, "all") == 0) {
282 *res_pid = 0;
283 return SCHED_VTY_THREAD_ALL;
284 }
285
286 if (strcmp(str, "self") == 0) {
287 *res_pid = 0;
288 return SCHED_VTY_THREAD_SELF;
289 }
290
291 len = strlen(str);
292 for (i = 0; i < len; i++) {
293 if (!isdigit(str[i])) {
294 is_pid = false;
295 break;
296 }
297 }
298 if (is_pid) {
Neels Hofmeyr34907fe2021-09-05 19:50:34 +0200299 int64_t val;
300 if (osmo_str_to_int64(&val, str, 0, 0, INT64_MAX))
Pau Espin Pedroleb6882f2020-07-28 11:57:51 +0200301 return SCHED_VTY_THREAD_UNKNOWN;
Neels Hofmeyr34907fe2021-09-05 19:50:34 +0200302 *res_pid = (pid_t)val;
303 if (*res_pid != val)
304 return SCHED_VTY_THREAD_UNKNOWN;
Pau Espin Pedroleb6882f2020-07-28 11:57:51 +0200305 if (!applynow || proc_tid_exists(*res_pid))
306 return SCHED_VTY_THREAD_ID;
307 else
308 return SCHED_VTY_THREAD_UNKNOWN;
309 }
310
311 if (len > 15) {
312 /* Thread names only allow up to 15+1 null chars, see man pthread_setname_np */
313 return SCHED_VTY_THREAD_UNKNOWN;
314 }
315
316 if (applynow) {
317 if (proc_name_exists(str, res_pid))
318 return SCHED_VTY_THREAD_NAME;
319 else
320 return SCHED_VTY_THREAD_UNKNOWN;
321 } else {
322 /* assume a thread will be named after it */
323 *res_pid = 0;
324 return SCHED_VTY_THREAD_NAME;
325 }
326}
327
328/* Wrapper for sched_setaffinity applying to single thread or all threads in process based on tid_type. */
329static int my_sched_setaffinity(enum sched_vty_thread_id tid_type, pid_t pid,
330 cpu_set_t *cpuset, size_t cpuset_size)
331{
332 DIR *proc_dir;
333 struct dirent *entry;
334 char dirname[100];
335 char str_mask[1024];
336 int tid_it;
337 int rc = 0;
338
339 if (generate_cpu_hex_mask(str_mask, sizeof(str_mask), cpuset, cpuset_size) < 0)
340 str_mask[0] = '\0';
341
342 if (tid_type != SCHED_VTY_THREAD_ALL) {
343 LOGP(DLGLOBAL, LOGL_NOTICE, "Setting CPU affinity mask for tid %lu to: %s\n",
344 (unsigned long) pid, str_mask);
345
346 rc = sched_setaffinity(pid, sizeof(cpu_set_t), cpuset);
347 return rc;
348 }
349
350 snprintf(dirname, sizeof(dirname), "/proc/%ld/task", (long int)getpid());
351 proc_dir = opendir(dirname);
352 if (!proc_dir)
353 return -EINVAL;
354
355 while ((entry = readdir(proc_dir)))
356 {
357 if (entry->d_name[0] == '.')
358 continue;
359 tid_it = atoi(entry->d_name);
360 LOGP(DLGLOBAL, LOGL_NOTICE, "Setting CPU affinity mask for tid %lu to: %s\n",
361 (unsigned long) tid_it, str_mask);
362
363 rc = sched_setaffinity(tid_it, sizeof(cpu_set_t), cpuset);
364 if (rc == -1)
365 break;
366 }
367
368 closedir(proc_dir);
369 return rc;
370
371}
372
Pau Espin Pedrol64c67bb2020-11-09 11:24:21 +0100373DEFUN_ATTR(cfg_sched_cpu_affinity, cfg_sched_cpu_affinity_cmd,
Pau Espin Pedroleb6882f2020-07-28 11:57:51 +0200374 "cpu-affinity (self|all|<0-4294967295>|THREADNAME) CPUHEXMASK [delay]",
375 "Set CPU affinity mask on a (group of) thread(s)\n"
376 "Set CPU affinity mask on thread running the VTY\n"
377 "Set CPU affinity mask on all process' threads\n"
378 "Set CPU affinity mask on a thread with specified PID\n"
379 "Set CPU affinity mask on a thread with specified thread name\n"
380 "CPU affinity mask\n"
Pau Espin Pedrol64c67bb2020-11-09 11:24:21 +0100381 "If set, delay applying the affinity mask now and let the app handle it at a later point\n",
382 CMD_ATTR_IMMEDIATE)
Pau Espin Pedroleb6882f2020-07-28 11:57:51 +0200383{
384 const char* str_who = argv[0];
385 const char *str_mask = argv[1];
386 bool applynow = (argc != 3);
387 int rc;
388 pid_t pid;
389 enum sched_vty_thread_id tid_type;
390 struct cpu_affinity_it *it, *it_next;
391 cpu_set_t *cpuset;
392 size_t cpuset_size;
393
394 tid_type = procname2pid(&pid, str_who, applynow);
395 if (tid_type == SCHED_VTY_THREAD_UNKNOWN) {
396 vty_out(vty, "%% Failed parsing target thread %s%s",
397 str_who, VTY_NEWLINE);
398 return CMD_WARNING;
399 }
400
401 if (tid_type == SCHED_VTY_THREAD_ID && !applynow) {
402 vty_out(vty, "%% It makes no sense to delay applying cpu-affinity on tid %lu%s",
403 (unsigned long)pid, VTY_NEWLINE);
404 return CMD_WARNING;
405 }
406 if (tid_type == SCHED_VTY_THREAD_ALL && !applynow) {
407 vty_out(vty, "%% It makes no sense to delay applying cpu-affinity on all threads%s",
408 VTY_NEWLINE);
409 return CMD_WARNING;
410 }
411
412 cpuset = CPU_ALLOC(get_num_cpus());
413 cpuset_size = CPU_ALLOC_SIZE(get_num_cpus());
414 if (parse_cpu_hex_mask(str_mask, cpuset, cpuset_size) < 0) {
415 vty_out(vty, "%% Failed parsing CPU Affinity Mask %s%s",
416 str_mask, VTY_NEWLINE);
417 CPU_FREE(cpuset);
418 return CMD_WARNING;
419 }
420
421 if (applynow) {
422 rc = my_sched_setaffinity(tid_type, pid, cpuset, cpuset_size);
423 if (rc == -1) {
424 vty_out(vty, "%% Failed setting sched CPU Affinity Mask %s: %s%s",
425 str_mask, strerror(errno), VTY_NEWLINE);
426 CPU_FREE(cpuset);
427 return CMD_WARNING;
428 }
429 }
430
431 /* Keep history of cmds applied to be able to rewrite config. If PID was passed
432 directly it makes no sense to store it since PIDs are temporary */
433 if (tid_type == SCHED_VTY_THREAD_SELF ||
434 tid_type == SCHED_VTY_THREAD_ALL ||
435 tid_type == SCHED_VTY_THREAD_NAME) {
436 pthread_mutex_lock(&sched_vty_opts->cpu_affinity_li_mutex);
437
438 /* Drop previous entries matching, since they will be overwritten */
439 llist_for_each_entry_safe(it, it_next, &sched_vty_opts->cpu_affinity_li, entry) {
440 if (strcmp(it->bufname, str_who) == 0) {
441 llist_del(&it->entry);
442 CPU_FREE(it->cpuset);
443 talloc_free(it);
444 break;
445 }
446 }
447 it = talloc_zero(sched_vty_opts->tall_ctx, struct cpu_affinity_it);
448 OSMO_STRLCPY_ARRAY(it->bufname, str_who);
449 it->tid_type = tid_type;
450 it->cpuset = cpuset;
451 it->cpuset_size = cpuset_size;
452 it->delay = !applynow;
453 llist_add_tail(&it->entry, &sched_vty_opts->cpu_affinity_li);
454
455 pthread_mutex_unlock(&sched_vty_opts->cpu_affinity_li_mutex);
456 } else {
457 /* We don't need cpuset for later, free it: */
458 CPU_FREE(cpuset);
459 }
460 return CMD_SUCCESS;
461}
462
463static int set_sched_rr(unsigned int prio)
464{
465 struct sched_param param;
466 int rc;
467 memset(&param, 0, sizeof(param));
468 param.sched_priority = prio;
469 LOGP(DLGLOBAL, LOGL_NOTICE, "Setting SCHED_RR priority %d\n", param.sched_priority);
470 rc = sched_setscheduler(getpid(), SCHED_RR, &param);
471 if (rc == -1) {
Ericdde32722020-08-14 03:15:10 +0200472 LOGP(DLGLOBAL, LOGL_ERROR, "Setting SCHED_RR priority %d failed: %s\n",
Pau Espin Pedroleb6882f2020-07-28 11:57:51 +0200473 param.sched_priority, strerror(errno));
474 return -1;
475 }
476 return 0;
477}
478
Pau Espin Pedrol64c67bb2020-11-09 11:24:21 +0100479DEFUN_ATTR(cfg_sched_policy, cfg_sched_policy_cmd,
Pau Espin Pedroleb6882f2020-07-28 11:57:51 +0200480 "policy rr <1-32>",
481 "Set the scheduling policy to use for the process\n"
482 "Use the SCHED_RR real-time scheduling algorithm\n"
Pau Espin Pedrol64c67bb2020-11-09 11:24:21 +0100483 "Set the SCHED_RR real-time priority\n",
484 CMD_ATTR_IMMEDIATE)
Pau Espin Pedroleb6882f2020-07-28 11:57:51 +0200485{
486 sched_vty_opts->sched_rr_prio = atoi(argv[0]);
487
488 if (set_sched_rr(sched_vty_opts->sched_rr_prio) < 0) {
489 vty_out(vty, "%% Failed setting SCHED_RR priority %d%s",
490 sched_vty_opts->sched_rr_prio, VTY_NEWLINE);
491 return CMD_WARNING;
492 }
493
494 return CMD_SUCCESS;
495}
496
497DEFUN(cfg_sched,
498 cfg_sched_cmd,
499 "cpu-sched", "Configure CPU Scheduler related settings")
500{
501 vty->index = NULL;
502 vty->node = L_CPU_SCHED_NODE;
503
504 return CMD_SUCCESS;
505}
506
507DEFUN(show_sched_threads, show_sched_threads_cmd,
508 "show cpu-sched threads",
509 SHOW_STR
510 "Show Sched section information\n"
511 "Show information about running threads)\n")
512{
513 DIR *proc_dir;
514 struct dirent *entry;
515 char path[100];
516 char name[17];
517 char str_mask[1024];
518 int tid_it;
519 int fd;
520 pid_t mypid = getpid();
521 int rc;
522 cpu_set_t *cpuset;
523 size_t cpuset_size;
524
525 vty_out(vty, "Thread list for PID %lu:%s", (unsigned long) mypid, VTY_NEWLINE);
526
527 snprintf(path, sizeof(path), "/proc/%ld/task", (long int)mypid);
528 proc_dir = opendir(path);
529 if (!proc_dir) {
530 vty_out(vty, "%% Failed opening dir%s%s", path, VTY_NEWLINE);
531 return CMD_WARNING;
532 }
533
534 while ((entry = readdir(proc_dir)))
535 {
536 if (entry->d_name[0] == '.')
537 continue;
538
539 tid_it = atoi(entry->d_name);
540 snprintf(path, sizeof(path), "/proc/%ld/task/%ld/comm", (long int)mypid, (long int)tid_it);
541 if ((fd = open(path, O_RDONLY)) != -1) {
542 rc = read(fd, name, sizeof(name) - 1);
543 if (rc >= 0) {
544 /* Last may char contain a '\n', get rid of it */
545 if (rc > 0 && name[rc - 1] == '\n')
546 name[rc - 1] = '\0';
547 else
548 name[rc] = '\0';
549 }
550 close(fd);
551 } else {
552 name[0] = '\0';
553 }
554
555 str_mask[0] = '\0';
556 cpuset = CPU_ALLOC(get_num_cpus());
557 cpuset_size = CPU_ALLOC_SIZE(get_num_cpus());
558 CPU_ZERO_S(cpuset_size, cpuset);
559 if (sched_getaffinity(tid_it, cpuset_size, cpuset) == 0) {
560 if (generate_cpu_hex_mask(str_mask, sizeof(str_mask), cpuset, cpuset_size) < 0)
561 str_mask[0] = '\0';
562 }
563 CPU_FREE(cpuset);
564
565 vty_out(vty, " TID: %lu, NAME: '%s', cpu-affinity: %s%s",
566 (unsigned long) tid_it, name, str_mask, VTY_NEWLINE);
567 }
568
569 closedir(proc_dir);
570 return CMD_SUCCESS;
571}
572
573static int config_write_sched(struct vty *vty)
574{
575 struct cpu_affinity_it *it;
576 char str_mask[1024];
577
578 /* Only add the node if there's something to write under it */
579 if (sched_vty_opts->sched_rr_prio || !llist_empty(&sched_vty_opts->cpu_affinity_li))
580 vty_out(vty, "cpu-sched%s", VTY_NEWLINE);
581
582 if (sched_vty_opts->sched_rr_prio)
583 vty_out(vty, " policy rr %d%s", sched_vty_opts->sched_rr_prio, VTY_NEWLINE);
584
585 llist_for_each_entry(it, &sched_vty_opts->cpu_affinity_li, entry) {
586 if (generate_cpu_hex_mask(str_mask, sizeof(str_mask), it->cpuset, it->cpuset_size) < 0)
587 OSMO_STRLCPY_ARRAY(str_mask, "ERROR");
588 vty_out(vty, " cpu-affinity %s %s%s%s", it->bufname, str_mask,
589 it->delay ? " delay" : "", VTY_NEWLINE);
590 }
591
592 return CMD_SUCCESS;
593}
594
595/*! Initialize sched VTY nodes
596 * \param[in] tall_ctx Talloc context to use internally by vty_sched subsystem.
597 * \return 0 on success, non-zero on error.
598 */
599int osmo_cpu_sched_vty_init(void *tall_ctx)
600{
601 OSMO_ASSERT(!sched_vty_opts); /* assert only called once */
602
603 sched_vty_opts = talloc_zero(tall_ctx, struct sched_vty_opts);
604 sched_vty_opts->tall_ctx = tall_ctx;
605 INIT_LLIST_HEAD(&sched_vty_opts->cpu_affinity_li);
606 pthread_mutex_init(&sched_vty_opts->cpu_affinity_li_mutex, NULL);
607
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +0700608 install_lib_element(CONFIG_NODE, &cfg_sched_cmd);
Pau Espin Pedroleb6882f2020-07-28 11:57:51 +0200609 install_node(&sched_node, config_write_sched);
610
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +0700611 install_lib_element(L_CPU_SCHED_NODE, &cfg_sched_policy_cmd);
612 install_lib_element(L_CPU_SCHED_NODE, &cfg_sched_cpu_affinity_cmd);
Pau Espin Pedroleb6882f2020-07-28 11:57:51 +0200613
Vadim Yanitskiy8e7c4962020-10-04 15:37:31 +0700614 install_lib_element_ve(&show_sched_threads_cmd);
Pau Espin Pedroleb6882f2020-07-28 11:57:51 +0200615
616 /* Initialize amount of cpus now */
617 if (get_num_cpus() < 0)
618 return -1;
619
620 return 0;
621}
622
623/*! Apply cpu-affinity on calling thread based on VTY configuration
624 * \return 0 on success, non-zero on error.
625 */
626int osmo_cpu_sched_vty_apply_localthread(void)
627{
628 struct cpu_affinity_it *it, *it_match = NULL;
629 char name[16]; /* 15 + \0 */
630 char str_mask[1024];
631 bool has_name = false;
632 int rc = 0;
633
634 /* Assert subsystem was inited and structs are preset */
Ericdde32722020-08-14 03:15:10 +0200635 if (!sched_vty_opts) {
636 LOGP(DLGLOBAL, LOGL_FATAL, "Setting cpu-affinity mask impossible: no opts!\n");
637 return 0;
638 }
Pau Espin Pedroleb6882f2020-07-28 11:57:51 +0200639
640 if (pthread_getname_np(pthread_self(), name, sizeof(name)) == 0)
641 has_name = true;
642
643 /* Get latest matching mask for the thread */
644 pthread_mutex_lock(&sched_vty_opts->cpu_affinity_li_mutex);
645 llist_for_each_entry(it, &sched_vty_opts->cpu_affinity_li, entry) {
646 switch (it->tid_type) {
647 case SCHED_VTY_THREAD_SELF:
648 continue; /* self to the VTY thread, not us */
649 case SCHED_VTY_THREAD_ALL:
650 it_match = it;
651 break;
652 case SCHED_VTY_THREAD_NAME:
653 if (!has_name)
654 continue;
655 if (strcmp(name, it->bufname) != 0)
656 continue;
657 it_match = it;
658 break;
659 default:
660 OSMO_ASSERT(0);
661 }
662 }
663
664 if (it_match) {
665 rc = my_sched_setaffinity(SCHED_VTY_THREAD_SELF, 0, it_match->cpuset, it_match->cpuset_size);
666 if (rc == -1) {
667 if (generate_cpu_hex_mask(str_mask, sizeof(str_mask),
668 it_match->cpuset, it_match->cpuset_size) < 0)
669 str_mask[0] = '\0';
670 LOGP(DLGLOBAL, LOGL_FATAL, "Setting cpu-affinity mask %s failed: %s\n",
671 str_mask, strerror(errno));
672 }
673 }
674 pthread_mutex_unlock(&sched_vty_opts->cpu_affinity_li_mutex);
675 return rc;
676}
677
678/*! @} */