227 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2023 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org>4 */5 6#define _GNU_SOURCE7#include <sched.h>8#include <fcntl.h>9#include <stdlib.h>10#include <unistd.h>11#include <stdio.h>12#include <errno.h>13#include <string.h>14#include <tracefs.h>15#include <pthread.h>16#include <sys/wait.h>17#include <sys/prctl.h>18 19#include "utils.h"20#include "timerlat_u.h"21 22/*23 * This is the user-space main for the tool timerlatu/ threads.24 *25 * It is as simple as this:26 * - set affinity27 * - set priority28 * - open tracer fd29 * - spin30 * - close31 */32static int timerlat_u_main(int cpu, struct timerlat_u_params *params)33{34 struct sched_param sp = { .sched_priority = 95 };35 char buffer[1024];36 int timerlat_fd;37 cpu_set_t set;38 int retval;39 40 /*41 * This all is only setting up the tool.42 */43 CPU_ZERO(&set);44 CPU_SET(cpu, &set);45 46 retval = sched_setaffinity(gettid(), sizeof(set), &set);47 if (retval == -1) {48 debug_msg("Error setting user thread affinity %d, is the CPU online?\n", cpu);49 exit(1);50 }51 52 if (!params->sched_param) {53 retval = sched_setscheduler(0, SCHED_FIFO, &sp);54 if (retval < 0) {55 err_msg("Error setting timerlat u default priority: %s\n", strerror(errno));56 exit(1);57 }58 } else {59 retval = __set_sched_attr(getpid(), params->sched_param);60 if (retval) {61 /* __set_sched_attr prints an error message, so */62 exit(0);63 }64 }65 66 if (params->cgroup_name) {67 retval = set_pid_cgroup(gettid(), params->cgroup_name);68 if (!retval) {69 err_msg("Error setting timerlat u cgroup pid\n");70 pthread_exit(&retval);71 }72 }73 74 /*75 * This is the tool's loop. If you want to use as base for your own tool...76 * go ahead.77 */78 snprintf(buffer, sizeof(buffer), "osnoise/per_cpu/cpu%d/timerlat_fd", cpu);79 80 timerlat_fd = tracefs_instance_file_open(NULL, buffer, O_RDONLY);81 if (timerlat_fd < 0) {82 err_msg("Error opening %s:%s\n", buffer, strerror(errno));83 exit(1);84 }85 86 debug_msg("User-space timerlat pid %d on cpu %d\n", gettid(), cpu);87 88 /* add should continue with a signal handler */89 while (true) {90 retval = read(timerlat_fd, buffer, 1024);91 if (retval < 0)92 break;93 }94 95 close(timerlat_fd);96 97 debug_msg("Leaving timerlat pid %d on cpu %d\n", gettid(), cpu);98 exit(0);99}100 101/*102 * timerlat_u_send_kill - send a kill signal for all processes103 *104 * Return the number of processes that received the kill.105 */106static int timerlat_u_send_kill(pid_t *procs, int nr_cpus)107{108 int killed = 0;109 int i, retval;110 111 for (i = 0; i < nr_cpus; i++) {112 if (!procs[i])113 continue;114 retval = kill(procs[i], SIGKILL);115 if (!retval)116 killed++;117 else118 err_msg("Error killing child process %d\n", procs[i]);119 }120 121 return killed;122}123 124/**125 * timerlat_u_dispatcher - dispatch one timerlatu/ process per monitored CPU126 *127 * This is a thread main that will fork one new process for each monitored128 * CPU. It will wait for:129 *130 * - rtla to tell to kill the child processes131 * - some child process to die, and the cleanup all the processes132 *133 * whichever comes first.134 *135 */136void *timerlat_u_dispatcher(void *data)137{138 int nr_cpus = sysconf(_SC_NPROCESSORS_CONF);139 struct timerlat_u_params *params = data;140 char proc_name[128];141 int procs_count = 0;142 int retval = 1;143 pid_t *procs;144 int wstatus;145 pid_t pid;146 int i;147 148 debug_msg("Dispatching timerlat u procs\n");149 150 procs = calloc(nr_cpus, sizeof(pid_t));151 if (!procs)152 pthread_exit(&retval);153 154 for (i = 0; i < nr_cpus; i++) {155 if (params->set && !CPU_ISSET(i, params->set))156 continue;157 158 pid = fork();159 160 /* child */161 if (!pid) {162 163 /*164 * rename the process165 */166 snprintf(proc_name, sizeof(proc_name), "timerlatu/%d", i);167 pthread_setname_np(pthread_self(), proc_name);168 prctl(PR_SET_NAME, (unsigned long)proc_name, 0, 0, 0);169 170 timerlat_u_main(i, params);171 /* timerlat_u_main should exit()! Anyways... */172 pthread_exit(&retval);173 }174 175 /* parent */176 if (pid == -1) {177 timerlat_u_send_kill(procs, nr_cpus);178 debug_msg("Failed to create child processes");179 pthread_exit(&retval);180 }181 182 procs_count++;183 procs[i] = pid;184 }185 186 while (params->should_run) {187 /* check if processes died */188 pid = waitpid(-1, &wstatus, WNOHANG);189 if (pid != 0) {190 for (i = 0; i < nr_cpus; i++) {191 if (procs[i] == pid) {192 procs[i] = 0;193 procs_count--;194 }195 }196 197 if (!procs_count)198 break;199 }200 201 sleep(1);202 }203 204 timerlat_u_send_kill(procs, nr_cpus);205 206 while (procs_count) {207 pid = waitpid(-1, &wstatus, 0);208 if (pid == -1) {209 err_msg("Failed to monitor child processes");210 pthread_exit(&retval);211 }212 for (i = 0; i < nr_cpus; i++) {213 if (procs[i] == pid) {214 procs[i] = 0;215 procs_count--;216 }217 }218 }219 220 params->stopped_running = 1;221 222 free(procs);223 retval = 0;224 pthread_exit(&retval);225 226}227