xref: /linux/tools/tracing/rtla/src/timerlat_u.c (revision 5779de8d36ac5a0c929f276096a499b03ae0afa7)
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  */
timerlat_u_main(int cpu,struct timerlat_u_params * params)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 		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 			fatal("Error setting timerlat u default priority: %s", strerror(errno));
56 	} else {
57 		retval = __set_sched_attr(getpid(), params->sched_param);
58 		if (retval) {
59 			/* __set_sched_attr prints an error message, so */
60 			exit(0);
61 		}
62 	}
63 
64 	if (params->cgroup_name) {
65 		retval = set_pid_cgroup(gettid(), params->cgroup_name);
66 		if (!retval) {
67 			err_msg("Error setting timerlat u cgroup pid\n");
68 			pthread_exit(&retval);
69 		}
70 	}
71 
72 	/*
73 	 * This is the tool's loop. If you want to use as base for your own tool...
74 	 * go ahead.
75 	 */
76 	snprintf(buffer, sizeof(buffer), "osnoise/per_cpu/cpu%d/timerlat_fd", cpu);
77 
78 	timerlat_fd = tracefs_instance_file_open(NULL, buffer, O_RDONLY);
79 	if (timerlat_fd < 0)
80 		fatal("Error opening %s:%s", buffer, strerror(errno));
81 
82 	debug_msg("User-space timerlat pid %d on cpu %d\n", gettid(), cpu);
83 
84 	/* add should continue with a signal handler */
85 	while (true) {
86 		retval = read(timerlat_fd, buffer, 1024);
87 		if (retval < 0)
88 			break;
89 	}
90 
91 	close(timerlat_fd);
92 
93 	debug_msg("Leaving timerlat pid %d on cpu %d\n", gettid(), cpu);
94 	exit(0);
95 }
96 
97 /*
98  * timerlat_u_send_kill - send a kill signal for all processes
99  *
100  * Return the number of processes that received the kill.
101  */
timerlat_u_send_kill(pid_t * procs,int nr_cpus)102 static int timerlat_u_send_kill(pid_t *procs, int nr_cpus)
103 {
104 	int killed = 0;
105 	int i, retval;
106 
107 	for (i = 0; i < nr_cpus; i++) {
108 		if (!procs[i])
109 			continue;
110 		retval = kill(procs[i], SIGKILL);
111 		if (!retval)
112 			killed++;
113 		else
114 			err_msg("Error killing child process %d\n", procs[i]);
115 	}
116 
117 	return killed;
118 }
119 
120 /**
121  * timerlat_u_dispatcher - dispatch one timerlatu/ process per monitored CPU
122  *
123  * This is a thread main that will fork one new process for each monitored
124  * CPU. It will wait for:
125  *
126  *  - rtla to tell to kill the child processes
127  *  - some child process to die, and the cleanup all the processes
128  *
129  * whichever comes first.
130  *
131  */
timerlat_u_dispatcher(void * data)132 void *timerlat_u_dispatcher(void *data)
133 {
134 	int nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
135 	struct timerlat_u_params *params = data;
136 	char proc_name[128];
137 	int procs_count = 0;
138 	int retval = 1;
139 	pid_t *procs;
140 	int wstatus;
141 	pid_t pid;
142 	int i;
143 
144 	debug_msg("Dispatching timerlat u procs\n");
145 
146 	procs = calloc(nr_cpus, sizeof(pid_t));
147 	if (!procs)
148 		pthread_exit(&retval);
149 
150 	for (i = 0; i < nr_cpus; i++) {
151 		if (params->set && !CPU_ISSET(i, params->set))
152 			continue;
153 
154 		pid = fork();
155 
156 		/* child */
157 		if (!pid) {
158 
159 			/*
160 			 * rename the process
161 			 */
162 			snprintf(proc_name, sizeof(proc_name), "timerlatu/%d", i);
163 			pthread_setname_np(pthread_self(), proc_name);
164 			prctl(PR_SET_NAME, (unsigned long)proc_name, 0, 0, 0);
165 
166 			timerlat_u_main(i, params);
167 			/* timerlat_u_main should exit()! Anyways... */
168 			pthread_exit(&retval);
169 		}
170 
171 		/* parent */
172 		if (pid == -1) {
173 			timerlat_u_send_kill(procs, nr_cpus);
174 			debug_msg("Failed to create child processes");
175 			pthread_exit(&retval);
176 		}
177 
178 		procs_count++;
179 		procs[i] = pid;
180 	}
181 
182 	while (params->should_run) {
183 		/* check if processes died */
184 		pid = waitpid(-1, &wstatus, WNOHANG);
185 		if (pid != 0) {
186 			for (i = 0; i < nr_cpus; i++) {
187 				if (procs[i] == pid) {
188 					procs[i] = 0;
189 					procs_count--;
190 				}
191 			}
192 
193 			if (!procs_count)
194 				break;
195 		}
196 
197 		sleep(1);
198 	}
199 
200 	timerlat_u_send_kill(procs, nr_cpus);
201 
202 	while (procs_count) {
203 		pid = waitpid(-1, &wstatus, 0);
204 		if (pid == -1) {
205 			err_msg("Failed to monitor child processes");
206 			pthread_exit(&retval);
207 		}
208 		for (i = 0; i < nr_cpus; i++) {
209 			if (procs[i] == pid) {
210 				procs[i] = 0;
211 				procs_count--;
212 			}
213 		}
214 	}
215 
216 	params->stopped_running = 1;
217 
218 	free(procs);
219 	retval = 0;
220 	pthread_exit(&retval);
221 
222 }
223