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