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