1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2023 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org> 4 */ 5 6 #define _GNU_SOURCE 7 #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 affinity 27 * - set priority 28 * - open tracer fd 29 * - spin 30 * - close 31 */ 32 static 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 err_msg("Error setting user thread affinity\n"); 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 processes 103 * 104 * Return the number of processes that received the kill. 105 */ 106 static 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 else 118 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 CPU 126 * 127 * This is a thread main that will fork one new process for each monitored 128 * CPU. It will wait for: 129 * 130 * - rtla to tell to kill the child processes 131 * - some child process to die, and the cleanup all the processes 132 * 133 * whichever comes first. 134 * 135 */ 136 void *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 process 165 */ 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 break; 197 } 198 199 sleep(1); 200 } 201 202 timerlat_u_send_kill(procs, nr_cpus); 203 204 while (procs_count) { 205 pid = waitpid(-1, &wstatus, 0); 206 if (pid == -1) { 207 err_msg("Failed to monitor child processes"); 208 pthread_exit(&retval); 209 } 210 for (i = 0; i < nr_cpus; i++) { 211 if (procs[i] == pid) { 212 procs[i] = 0; 213 procs_count--; 214 } 215 } 216 } 217 218 params->stopped_running = 1; 219 220 free(procs); 221 retval = 0; 222 pthread_exit(&retval); 223 224 } 225