From eb6882fa5249c1f4fd764f4339ff7d749e1cfb99 Mon Sep 17 00:00:00 2001 From: Pau Espin Pedrol Date: Tue, 28 Jul 2020 11:57:51 +0200 Subject: vty: Introduce support to set cpu-affinity and scheduler policy Process willing to support this kind of configuration through VTY simply need to call "osmo_sched_vty_init(tall_ctx);" during startup to register the commands. For multithreaded processes, new threads willing to get their cpu-affinity mask according to VTY config should call osmo_sched_vty_apply_localthread() (potentially after setting the thread name through pthread_setname_np()). Related: SYS#4986 Change-Id: If76a4bd2cc7b3c7adf5d84790a944d78be70e10a --- include/Makefile.am | 1 + include/osmocom/vty/command.h | 1 + include/osmocom/vty/cpu_sched_vty.h | 37 ++ src/vty/Makefile.am | 2 +- src/vty/cpu_sched_vty.c | 674 ++++++++++++++++++++++++++++++++++++ 5 files changed, 714 insertions(+), 1 deletion(-) create mode 100644 include/osmocom/vty/cpu_sched_vty.h create mode 100644 src/vty/cpu_sched_vty.c diff --git a/include/Makefile.am b/include/Makefile.am index 7af7e018..b0a72bce 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -164,6 +164,7 @@ nobase_include_HEADERS += \ osmocom/vty/vector.h \ osmocom/vty/vty.h \ osmocom/vty/ports.h \ + osmocom/vty/cpu_sched_vty.h \ osmocom/vty/tdef_vty.h \ osmocom/ctrl/control_vty.h endif diff --git a/include/osmocom/vty/command.h b/include/osmocom/vty/command.h index 29206ceb..e3692dc8 100644 --- a/include/osmocom/vty/command.h +++ b/include/osmocom/vty/command.h @@ -97,6 +97,7 @@ enum node_type { L_CS7_SCCPADDR_NODE, /*!< SS7 SCCP Address */ L_CS7_SCCPADDR_GT_NODE, /*!< SS7 SCCP Global Title */ + L_CPU_SCHED_NODE, /*!< CPU Sched related options node */ /* * When adding new nodes to the libosmocore project, these nodes can be * used to avoid ABI changes for unrelated projects. diff --git a/include/osmocom/vty/cpu_sched_vty.h b/include/osmocom/vty/cpu_sched_vty.h new file mode 100644 index 00000000..171f1687 --- /dev/null +++ b/include/osmocom/vty/cpu_sched_vty.h @@ -0,0 +1,37 @@ +/*! \file cpu_sched_vty.h + * API to CPU / Threading / Scheduler properties from VTY configuration. + */ +/* (C) 2020 by sysmocom - s.f.m.c. GmbH + * + * Author: Pau Espin Pedrol + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPL-2.0+ + */ +#pragma once + +#include + +/*! \defgroup cpu_sched_VTY Configuration + * @{ + * \file cpu_sched_vty.h + */ + +void osmo_cpu_sched_vty_init(void *tall_ctx); +int osmo_cpu_sched_vty_apply_localthread(void); + +/*! @} */ diff --git a/src/vty/Makefile.am b/src/vty/Makefile.am index 35350cc3..81ff1045 100644 --- a/src/vty/Makefile.am +++ b/src/vty/Makefile.am @@ -12,7 +12,7 @@ lib_LTLIBRARIES = libosmovty.la libosmovty_la_SOURCES = buffer.c command.c vty.c vector.c utils.c \ telnet_interface.c logging_vty.c stats_vty.c \ fsm_vty.c talloc_ctx_vty.c \ - tdef_vty.c + cpu_sched_vty.c tdef_vty.c libosmovty_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined libosmovty_la_LIBADD = $(top_builddir)/src/libosmocore.la $(TALLOC_LIBS) endif diff --git a/src/vty/cpu_sched_vty.c b/src/vty/cpu_sched_vty.c new file mode 100644 index 00000000..3b6067a3 --- /dev/null +++ b/src/vty/cpu_sched_vty.c @@ -0,0 +1,674 @@ +/*! \file cpu_sched_vty.c + * Implementation to CPU / Threading / Scheduler properties from VTY configuration. + */ +/* (C) 2020 by sysmocom - s.f.m.c. GmbH + * + * Author: Pau Espin Pedrol + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * SPDX-License-Identifier: GPLv2+ + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/*! \addtogroup Tdef_VTY + * + * CPU Scheduling related VTY API. + * + * @{ + * \file cpu_sched_vty.c + */ + +enum sched_vty_thread_id { + SCHED_VTY_THREAD_SELF, + SCHED_VTY_THREAD_ALL, + SCHED_VTY_THREAD_ID, + SCHED_VTY_THREAD_NAME, + SCHED_VTY_THREAD_UNKNOWN, +}; + +struct cpu_affinity_it { + struct llist_head entry; + enum sched_vty_thread_id tid_type; + char bufname[64]; + cpu_set_t *cpuset; + size_t cpuset_size; + bool delay; +}; + +struct sched_vty_opts { + void *tall_ctx; + int sched_rr_prio; + struct llist_head cpu_affinity_li; + pthread_mutex_t cpu_affinity_li_mutex; +}; + +static struct sched_vty_opts *sched_vty_opts; + +static struct cmd_node sched_node = { + L_CPU_SCHED_NODE, + "%s(config-sched)# ", + 1, +}; + +/* returns number of configured CPUs in the system, or negative otherwise */ +static int get_num_cpus() { + static unsigned int num_cpus = 0; + long ln; + + if (num_cpus) + return num_cpus; + + /* This is expensive (goes across /sys, so let's do it only once. It is + * guaranteed it won't change during process span anyway). */ + ln = sysconf(_SC_NPROCESSORS_CONF); + if (ln < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, "sysconf(_SC_NPROCESSORS_CONF) failed: %s\n", + strerror(errno)); + return -1; + } + num_cpus = (unsigned int) ln; + return num_cpus; +} + +/* Parses string with CPU hex Affinity Mask, with right-most bit being CPU0, and + * fills a cpuset of size cpuset_size. + */ +static int parse_cpu_hex_mask(const char *str, cpu_set_t *cpuset, size_t cpuset_size) +{ + int len = strlen(str); + const char *ptr = str + len - 1; + int cpu = 0; + + /* skip optional '0x' prefix format */ + if (len >= 2 && str[0] == '0' && str[1] == 'x') + str += 2; + CPU_ZERO_S(cpuset_size, cpuset); + + while (ptr >= str) { + char c = *ptr; + uint8_t val; + + if (c >= '0' && c <= '9') { + val = c - '0'; + } else { + c = (char)tolower((int)c); + if (c >= 'a' && c <= 'f') + val = c + (10 - 'a'); + else + return -1; + } + if (val & 0x01) + CPU_SET_S(cpu, cpuset_size, cpuset); + if (val & 0x02) + CPU_SET_S(cpu + 1, cpuset_size, cpuset); + if (val & 0x04) + CPU_SET_S(cpu + 2, cpuset_size, cpuset); + if (val & 0x08) + CPU_SET_S(cpu + 3, cpuset_size, cpuset); + ptr--; + cpu += 4; + } + + return 0; +} + +/* Generates a hexstring in str from cpuset of size cpuset_size */ +static int generate_cpu_hex_mask(char *str, size_t str_buf_size, + cpu_set_t *cpuset, size_t cpuset_size) +{ + char *ptr = str; + int cpu; + bool first_nonzero_found = false; + + /* 2 char per byte, + '0x' prefix + '\0' */ + if (cpuset_size * 2 + 2 + 1 > str_buf_size) + return -1; + + *ptr++ = '0'; + *ptr++ = 'x'; + + for (cpu = cpuset_size*8 - 4; cpu >= 0; cpu -= 4) { + uint8_t val = 0; + + if (CPU_ISSET_S(cpu, cpuset_size, cpuset)) + val |= 0x01; + if (CPU_ISSET_S(cpu + 1, cpuset_size, cpuset)) + val |= 0x02; + if (CPU_ISSET_S(cpu + 2, cpuset_size, cpuset)) + val |= 0x04; + if (CPU_ISSET_S(cpu + 3, cpuset_size, cpuset)) + val |= 0x08; + + if (val >= 0 && val < 10) + *ptr = '0' + val; + else + *ptr = ('a' - 10) + val; + if (val) + first_nonzero_found = true; + if (first_nonzero_found) + ptr++; + + } + if (!first_nonzero_found) + *ptr++ = '0'; + *ptr = '\0'; + return 0; +} + +/* Checks whther a thread identified by tid exists and belongs to the running process */ +static bool proc_tid_exists(pid_t tid) +{ + DIR *proc_dir; + struct dirent *entry; + char dirname[100]; + int tid_it; + bool found = false; + + snprintf(dirname, sizeof(dirname), "/proc/%ld/task", (long int)getpid()); + proc_dir = opendir(dirname); + if (!proc_dir) + return false; /*FIXME; print error */ + + while ((entry = readdir(proc_dir))) { + if (entry->d_name[0] == '.') + continue; + tid_it = atoi(entry->d_name); + if (tid_it == tid) { + found = true; + break; + } + } + + closedir(proc_dir); + return found; +} + +/* Checks whther a thread identified by name exists and belongs to the running + * process, and returns its disocevered TID in res_pid. + */ +static bool proc_name_exists(const char *name, pid_t *res_pid) +{ + DIR *proc_dir; + struct dirent *entry; + char path[100]; + char buf[17]; /* 15 + \n + \0 */ + int tid_it; + int fd; + pid_t mypid = getpid(); + bool found = false; + int rc; + + *res_pid = 0; + + snprintf(path, sizeof(path), "/proc/%ld/task", (long int)mypid); + proc_dir = opendir(path); + if (!proc_dir) + return false; + + while ((entry = readdir(proc_dir))) + { + if (entry->d_name[0] == '.') + continue; + + tid_it = atoi(entry->d_name); + snprintf(path, sizeof(path), "/proc/%ld/task/%ld/comm", (long int)mypid, (long int) tid_it); + if ((fd = open(path, O_RDONLY)) == -1) + continue; + rc = read(fd, buf, sizeof(buf) - 1); + if (rc >= 0) { + /* Last may char contain a '\n', get rid of it */ + if (rc > 0 && buf[rc - 1] == '\n') + buf[rc - 1] = '\0'; + else + buf[rc] = '\0'; + if (strcmp(name, buf) == 0) { + *res_pid = tid_it; + found = true; + } + } + close(fd); + + if (found) + break; + } + + closedir(proc_dir); + return found; +} + +/* Parse VTY THREADNAME variable, return its type and fill discovered res_pid if required */ +static enum sched_vty_thread_id procname2pid(pid_t *res_pid, const char *str, bool applynow) +{ + size_t i, len; + char *end; + bool is_pid = true; + + if (strcmp(str, "all") == 0) { + *res_pid = 0; + return SCHED_VTY_THREAD_ALL; + } + + if (strcmp(str, "self") == 0) { + *res_pid = 0; + return SCHED_VTY_THREAD_SELF; + } + + len = strlen(str); + for (i = 0; i < len; i++) { + if (!isdigit(str[i])) { + is_pid = false; + break; + } + } + if (is_pid) { + errno = 0; + *res_pid = strtoul(str, &end, 0); + if ((errno == ERANGE && *res_pid == ULONG_MAX) || (errno && !*res_pid) || + str == end) { + return SCHED_VTY_THREAD_UNKNOWN; + } + if (!applynow || proc_tid_exists(*res_pid)) + return SCHED_VTY_THREAD_ID; + else + return SCHED_VTY_THREAD_UNKNOWN; + } + + if (len > 15) { + /* Thread names only allow up to 15+1 null chars, see man pthread_setname_np */ + return SCHED_VTY_THREAD_UNKNOWN; + } + + if (applynow) { + if (proc_name_exists(str, res_pid)) + return SCHED_VTY_THREAD_NAME; + else + return SCHED_VTY_THREAD_UNKNOWN; + } else { + /* assume a thread will be named after it */ + *res_pid = 0; + return SCHED_VTY_THREAD_NAME; + } +} + +/* Wrapper for sched_setaffinity applying to single thread or all threads in process based on tid_type. */ +static int my_sched_setaffinity(enum sched_vty_thread_id tid_type, pid_t pid, + cpu_set_t *cpuset, size_t cpuset_size) +{ + DIR *proc_dir; + struct dirent *entry; + char dirname[100]; + char str_mask[1024]; + int tid_it; + int rc = 0; + + if (generate_cpu_hex_mask(str_mask, sizeof(str_mask), cpuset, cpuset_size) < 0) + str_mask[0] = '\0'; + + if (tid_type != SCHED_VTY_THREAD_ALL) { + LOGP(DLGLOBAL, LOGL_NOTICE, "Setting CPU affinity mask for tid %lu to: %s\n", + (unsigned long) pid, str_mask); + + rc = sched_setaffinity(pid, sizeof(cpu_set_t), cpuset); + return rc; + } + + snprintf(dirname, sizeof(dirname), "/proc/%ld/task", (long int)getpid()); + proc_dir = opendir(dirname); + if (!proc_dir) + return -EINVAL; + + while ((entry = readdir(proc_dir))) + { + if (entry->d_name[0] == '.') + continue; + tid_it = atoi(entry->d_name); + LOGP(DLGLOBAL, LOGL_NOTICE, "Setting CPU affinity mask for tid %lu to: %s\n", + (unsigned long) tid_it, str_mask); + + rc = sched_setaffinity(tid_it, sizeof(cpu_set_t), cpuset); + if (rc == -1) + break; + } + + closedir(proc_dir); + return rc; + +} + +DEFUN(cfg_sched_cpu_affinity, cfg_sched_cpu_affinity_cmd, + "cpu-affinity (self|all|<0-4294967295>|THREADNAME) CPUHEXMASK [delay]", + "Set CPU affinity mask on a (group of) thread(s)\n" + "Set CPU affinity mask on thread running the VTY\n" + "Set CPU affinity mask on all process' threads\n" + "Set CPU affinity mask on a thread with specified PID\n" + "Set CPU affinity mask on a thread with specified thread name\n" + "CPU affinity mask\n" + "If set, delay applying the affinity mask now and let the app handle it at a later point\n") +{ + const char* str_who = argv[0]; + const char *str_mask = argv[1]; + bool applynow = (argc != 3); + int rc; + pid_t pid; + enum sched_vty_thread_id tid_type; + struct cpu_affinity_it *it, *it_next; + cpu_set_t *cpuset; + size_t cpuset_size; + + tid_type = procname2pid(&pid, str_who, applynow); + if (tid_type == SCHED_VTY_THREAD_UNKNOWN) { + vty_out(vty, "%% Failed parsing target thread %s%s", + str_who, VTY_NEWLINE); + return CMD_WARNING; + } + + if (tid_type == SCHED_VTY_THREAD_ID && !applynow) { + vty_out(vty, "%% It makes no sense to delay applying cpu-affinity on tid %lu%s", + (unsigned long)pid, VTY_NEWLINE); + return CMD_WARNING; + } + if (tid_type == SCHED_VTY_THREAD_ALL && !applynow) { + vty_out(vty, "%% It makes no sense to delay applying cpu-affinity on all threads%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + cpuset = CPU_ALLOC(get_num_cpus()); + cpuset_size = CPU_ALLOC_SIZE(get_num_cpus()); + if (parse_cpu_hex_mask(str_mask, cpuset, cpuset_size) < 0) { + vty_out(vty, "%% Failed parsing CPU Affinity Mask %s%s", + str_mask, VTY_NEWLINE); + CPU_FREE(cpuset); + return CMD_WARNING; + } + + if (applynow) { + rc = my_sched_setaffinity(tid_type, pid, cpuset, cpuset_size); + if (rc == -1) { + vty_out(vty, "%% Failed setting sched CPU Affinity Mask %s: %s%s", + str_mask, strerror(errno), VTY_NEWLINE); + CPU_FREE(cpuset); + return CMD_WARNING; + } + } + + /* Keep history of cmds applied to be able to rewrite config. If PID was passed + directly it makes no sense to store it since PIDs are temporary */ + if (tid_type == SCHED_VTY_THREAD_SELF || + tid_type == SCHED_VTY_THREAD_ALL || + tid_type == SCHED_VTY_THREAD_NAME) { + pthread_mutex_lock(&sched_vty_opts->cpu_affinity_li_mutex); + + /* Drop previous entries matching, since they will be overwritten */ + llist_for_each_entry_safe(it, it_next, &sched_vty_opts->cpu_affinity_li, entry) { + if (strcmp(it->bufname, str_who) == 0) { + llist_del(&it->entry); + CPU_FREE(it->cpuset); + talloc_free(it); + break; + } + } + it = talloc_zero(sched_vty_opts->tall_ctx, struct cpu_affinity_it); + OSMO_STRLCPY_ARRAY(it->bufname, str_who); + it->tid_type = tid_type; + it->cpuset = cpuset; + it->cpuset_size = cpuset_size; + it->delay = !applynow; + llist_add_tail(&it->entry, &sched_vty_opts->cpu_affinity_li); + + pthread_mutex_unlock(&sched_vty_opts->cpu_affinity_li_mutex); + } else { + /* We don't need cpuset for later, free it: */ + CPU_FREE(cpuset); + } + return CMD_SUCCESS; +} + +static int set_sched_rr(unsigned int prio) +{ + struct sched_param param; + int rc; + memset(¶m, 0, sizeof(param)); + param.sched_priority = prio; + LOGP(DLGLOBAL, LOGL_NOTICE, "Setting SCHED_RR priority %d\n", param.sched_priority); + rc = sched_setscheduler(getpid(), SCHED_RR, ¶m); + if (rc == -1) { + LOGP(DLGLOBAL, LOGL_FATAL, "Setting SCHED_RR priority %d failed: %s\n", + param.sched_priority, strerror(errno)); + return -1; + } + return 0; +} + +DEFUN(cfg_sched_policy, cfg_sched_policy_cmd, + "policy rr <1-32>", + "Set the scheduling policy to use for the process\n" + "Use the SCHED_RR real-time scheduling algorithm\n" + "Set the SCHED_RR real-time priority\n") +{ + sched_vty_opts->sched_rr_prio = atoi(argv[0]); + + if (set_sched_rr(sched_vty_opts->sched_rr_prio) < 0) { + vty_out(vty, "%% Failed setting SCHED_RR priority %d%s", + sched_vty_opts->sched_rr_prio, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_sched, + cfg_sched_cmd, + "cpu-sched", "Configure CPU Scheduler related settings") +{ + vty->index = NULL; + vty->node = L_CPU_SCHED_NODE; + + return CMD_SUCCESS; +} + +DEFUN(show_sched_threads, show_sched_threads_cmd, + "show cpu-sched threads", + SHOW_STR + "Show Sched section information\n" + "Show information about running threads)\n") +{ + DIR *proc_dir; + struct dirent *entry; + char path[100]; + char name[17]; + char str_mask[1024]; + int tid_it; + int fd; + pid_t mypid = getpid(); + int rc; + cpu_set_t *cpuset; + size_t cpuset_size; + + vty_out(vty, "Thread list for PID %lu:%s", (unsigned long) mypid, VTY_NEWLINE); + + snprintf(path, sizeof(path), "/proc/%ld/task", (long int)mypid); + proc_dir = opendir(path); + if (!proc_dir) { + vty_out(vty, "%% Failed opening dir%s%s", path, VTY_NEWLINE); + return CMD_WARNING; + } + + while ((entry = readdir(proc_dir))) + { + if (entry->d_name[0] == '.') + continue; + + tid_it = atoi(entry->d_name); + snprintf(path, sizeof(path), "/proc/%ld/task/%ld/comm", (long int)mypid, (long int)tid_it); + if ((fd = open(path, O_RDONLY)) != -1) { + rc = read(fd, name, sizeof(name) - 1); + if (rc >= 0) { + /* Last may char contain a '\n', get rid of it */ + if (rc > 0 && name[rc - 1] == '\n') + name[rc - 1] = '\0'; + else + name[rc] = '\0'; + } + close(fd); + } else { + name[0] = '\0'; + } + + str_mask[0] = '\0'; + cpuset = CPU_ALLOC(get_num_cpus()); + cpuset_size = CPU_ALLOC_SIZE(get_num_cpus()); + CPU_ZERO_S(cpuset_size, cpuset); + if (sched_getaffinity(tid_it, cpuset_size, cpuset) == 0) { + if (generate_cpu_hex_mask(str_mask, sizeof(str_mask), cpuset, cpuset_size) < 0) + str_mask[0] = '\0'; + } + CPU_FREE(cpuset); + + vty_out(vty, " TID: %lu, NAME: '%s', cpu-affinity: %s%s", + (unsigned long) tid_it, name, str_mask, VTY_NEWLINE); + } + + closedir(proc_dir); + return CMD_SUCCESS; +} + +static int config_write_sched(struct vty *vty) +{ + struct cpu_affinity_it *it; + char str_mask[1024]; + + /* Only add the node if there's something to write under it */ + if (sched_vty_opts->sched_rr_prio || !llist_empty(&sched_vty_opts->cpu_affinity_li)) + vty_out(vty, "cpu-sched%s", VTY_NEWLINE); + + if (sched_vty_opts->sched_rr_prio) + vty_out(vty, " policy rr %d%s", sched_vty_opts->sched_rr_prio, VTY_NEWLINE); + + llist_for_each_entry(it, &sched_vty_opts->cpu_affinity_li, entry) { + if (generate_cpu_hex_mask(str_mask, sizeof(str_mask), it->cpuset, it->cpuset_size) < 0) + OSMO_STRLCPY_ARRAY(str_mask, "ERROR"); + vty_out(vty, " cpu-affinity %s %s%s%s", it->bufname, str_mask, + it->delay ? " delay" : "", VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +/*! Initialize sched VTY nodes + * \param[in] tall_ctx Talloc context to use internally by vty_sched subsystem. + * \return 0 on success, non-zero on error. + */ +int osmo_cpu_sched_vty_init(void *tall_ctx) +{ + OSMO_ASSERT(!sched_vty_opts); /* assert only called once */ + + sched_vty_opts = talloc_zero(tall_ctx, struct sched_vty_opts); + sched_vty_opts->tall_ctx = tall_ctx; + INIT_LLIST_HEAD(&sched_vty_opts->cpu_affinity_li); + pthread_mutex_init(&sched_vty_opts->cpu_affinity_li_mutex, NULL); + + install_element(CONFIG_NODE, &cfg_sched_cmd); + install_node(&sched_node, config_write_sched); + + install_element(L_CPU_SCHED_NODE, &cfg_sched_policy_cmd); + install_element(L_CPU_SCHED_NODE, &cfg_sched_cpu_affinity_cmd); + + install_element_ve(&show_sched_threads_cmd); + + /* Initialize amount of cpus now */ + if (get_num_cpus() < 0) + return -1; + + return 0; +} + +/*! Apply cpu-affinity on calling thread based on VTY configuration + * \return 0 on success, non-zero on error. + */ +int osmo_cpu_sched_vty_apply_localthread(void) +{ + struct cpu_affinity_it *it, *it_match = NULL; + char name[16]; /* 15 + \0 */ + char str_mask[1024]; + bool has_name = false; + int rc = 0; + + /* Assert subsystem was inited and structs are preset */ + OSMO_ASSERT(sched_vty_opts); + + if (pthread_getname_np(pthread_self(), name, sizeof(name)) == 0) + has_name = true; + + /* Get latest matching mask for the thread */ + pthread_mutex_lock(&sched_vty_opts->cpu_affinity_li_mutex); + llist_for_each_entry(it, &sched_vty_opts->cpu_affinity_li, entry) { + switch (it->tid_type) { + case SCHED_VTY_THREAD_SELF: + continue; /* self to the VTY thread, not us */ + case SCHED_VTY_THREAD_ALL: + it_match = it; + break; + case SCHED_VTY_THREAD_NAME: + if (!has_name) + continue; + if (strcmp(name, it->bufname) != 0) + continue; + it_match = it; + break; + default: + OSMO_ASSERT(0); + } + } + + if (it_match) { + rc = my_sched_setaffinity(SCHED_VTY_THREAD_SELF, 0, it_match->cpuset, it_match->cpuset_size); + if (rc == -1) { + if (generate_cpu_hex_mask(str_mask, sizeof(str_mask), + it_match->cpuset, it_match->cpuset_size) < 0) + str_mask[0] = '\0'; + LOGP(DLGLOBAL, LOGL_FATAL, "Setting cpu-affinity mask %s failed: %s\n", + str_mask, strerror(errno)); + } + } + pthread_mutex_unlock(&sched_vty_opts->cpu_affinity_li_mutex); + return rc; +} + +/*! @} */ -- cgit v1.2.3