162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2023 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org>
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#define _GNU_SOURCE
762306a36Sopenharmony_ci#include <sched.h>
862306a36Sopenharmony_ci#include <fcntl.h>
962306a36Sopenharmony_ci#include <stdlib.h>
1062306a36Sopenharmony_ci#include <unistd.h>
1162306a36Sopenharmony_ci#include <stdio.h>
1262306a36Sopenharmony_ci#include <errno.h>
1362306a36Sopenharmony_ci#include <string.h>
1462306a36Sopenharmony_ci#include <tracefs.h>
1562306a36Sopenharmony_ci#include <pthread.h>
1662306a36Sopenharmony_ci#include <sys/wait.h>
1762306a36Sopenharmony_ci#include <sys/prctl.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "utils.h"
2062306a36Sopenharmony_ci#include "timerlat_u.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/*
2362306a36Sopenharmony_ci * This is the user-space main for the tool timerlatu/ threads.
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * It is as simple as this:
2662306a36Sopenharmony_ci *  - set affinity
2762306a36Sopenharmony_ci *  - set priority
2862306a36Sopenharmony_ci *  - open tracer fd
2962306a36Sopenharmony_ci *  - spin
3062306a36Sopenharmony_ci *  - close
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_cistatic int timerlat_u_main(int cpu, struct timerlat_u_params *params)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	struct sched_param sp = { .sched_priority = 95 };
3562306a36Sopenharmony_ci	char buffer[1024];
3662306a36Sopenharmony_ci	int timerlat_fd;
3762306a36Sopenharmony_ci	cpu_set_t set;
3862306a36Sopenharmony_ci	int retval;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	/*
4162306a36Sopenharmony_ci	 * This all is only setting up the tool.
4262306a36Sopenharmony_ci	 */
4362306a36Sopenharmony_ci	CPU_ZERO(&set);
4462306a36Sopenharmony_ci	CPU_SET(cpu, &set);
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	retval = sched_setaffinity(gettid(), sizeof(set), &set);
4762306a36Sopenharmony_ci	if (retval == -1) {
4862306a36Sopenharmony_ci		debug_msg("Error setting user thread affinity %d, is the CPU online?\n", cpu);
4962306a36Sopenharmony_ci		exit(1);
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (!params->sched_param) {
5362306a36Sopenharmony_ci		retval = sched_setscheduler(0, SCHED_FIFO, &sp);
5462306a36Sopenharmony_ci		if (retval < 0) {
5562306a36Sopenharmony_ci			err_msg("Error setting timerlat u default priority: %s\n", strerror(errno));
5662306a36Sopenharmony_ci			exit(1);
5762306a36Sopenharmony_ci		}
5862306a36Sopenharmony_ci	} else {
5962306a36Sopenharmony_ci		retval = __set_sched_attr(getpid(), params->sched_param);
6062306a36Sopenharmony_ci		if (retval) {
6162306a36Sopenharmony_ci			/* __set_sched_attr prints an error message, so */
6262306a36Sopenharmony_ci			exit(0);
6362306a36Sopenharmony_ci		}
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	if (params->cgroup_name) {
6762306a36Sopenharmony_ci		retval = set_pid_cgroup(gettid(), params->cgroup_name);
6862306a36Sopenharmony_ci		if (!retval) {
6962306a36Sopenharmony_ci			err_msg("Error setting timerlat u cgroup pid\n");
7062306a36Sopenharmony_ci			pthread_exit(&retval);
7162306a36Sopenharmony_ci		}
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	/*
7562306a36Sopenharmony_ci	 * This is the tool's loop. If you want to use as base for your own tool...
7662306a36Sopenharmony_ci	 * go ahead.
7762306a36Sopenharmony_ci	 */
7862306a36Sopenharmony_ci	snprintf(buffer, sizeof(buffer), "osnoise/per_cpu/cpu%d/timerlat_fd", cpu);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	timerlat_fd = tracefs_instance_file_open(NULL, buffer, O_RDONLY);
8162306a36Sopenharmony_ci	if (timerlat_fd < 0) {
8262306a36Sopenharmony_ci		err_msg("Error opening %s:%s\n", buffer, strerror(errno));
8362306a36Sopenharmony_ci		exit(1);
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	debug_msg("User-space timerlat pid %d on cpu %d\n", gettid(), cpu);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/* add should continue with a signal handler */
8962306a36Sopenharmony_ci	while (true) {
9062306a36Sopenharmony_ci		retval = read(timerlat_fd, buffer, 1024);
9162306a36Sopenharmony_ci		if (retval < 0)
9262306a36Sopenharmony_ci			break;
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	close(timerlat_fd);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	debug_msg("Leaving timerlat pid %d on cpu %d\n", gettid(), cpu);
9862306a36Sopenharmony_ci	exit(0);
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci/*
10262306a36Sopenharmony_ci * timerlat_u_send_kill - send a kill signal for all processes
10362306a36Sopenharmony_ci *
10462306a36Sopenharmony_ci * Return the number of processes that received the kill.
10562306a36Sopenharmony_ci */
10662306a36Sopenharmony_cistatic int timerlat_u_send_kill(pid_t *procs, int nr_cpus)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	int killed = 0;
10962306a36Sopenharmony_ci	int i, retval;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	for (i = 0; i < nr_cpus; i++) {
11262306a36Sopenharmony_ci		if (!procs[i])
11362306a36Sopenharmony_ci			continue;
11462306a36Sopenharmony_ci		retval = kill(procs[i], SIGKILL);
11562306a36Sopenharmony_ci		if (!retval)
11662306a36Sopenharmony_ci			killed++;
11762306a36Sopenharmony_ci		else
11862306a36Sopenharmony_ci			err_msg("Error killing child process %d\n", procs[i]);
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	return killed;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/**
12562306a36Sopenharmony_ci * timerlat_u_dispatcher - dispatch one timerlatu/ process per monitored CPU
12662306a36Sopenharmony_ci *
12762306a36Sopenharmony_ci * This is a thread main that will fork one new process for each monitored
12862306a36Sopenharmony_ci * CPU. It will wait for:
12962306a36Sopenharmony_ci *
13062306a36Sopenharmony_ci *  - rtla to tell to kill the child processes
13162306a36Sopenharmony_ci *  - some child process to die, and the cleanup all the processes
13262306a36Sopenharmony_ci *
13362306a36Sopenharmony_ci * whichever comes first.
13462306a36Sopenharmony_ci *
13562306a36Sopenharmony_ci */
13662306a36Sopenharmony_civoid *timerlat_u_dispatcher(void *data)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	int nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
13962306a36Sopenharmony_ci	struct timerlat_u_params *params = data;
14062306a36Sopenharmony_ci	char proc_name[128];
14162306a36Sopenharmony_ci	int procs_count = 0;
14262306a36Sopenharmony_ci	int retval = 1;
14362306a36Sopenharmony_ci	pid_t *procs;
14462306a36Sopenharmony_ci	int wstatus;
14562306a36Sopenharmony_ci	pid_t pid;
14662306a36Sopenharmony_ci	int i;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	debug_msg("Dispatching timerlat u procs\n");
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	procs = calloc(nr_cpus, sizeof(pid_t));
15162306a36Sopenharmony_ci	if (!procs)
15262306a36Sopenharmony_ci		pthread_exit(&retval);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	for (i = 0; i < nr_cpus; i++) {
15562306a36Sopenharmony_ci		if (params->set && !CPU_ISSET(i, params->set))
15662306a36Sopenharmony_ci			continue;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci		pid = fork();
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci		/* child */
16162306a36Sopenharmony_ci		if (!pid) {
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci			/*
16462306a36Sopenharmony_ci			 * rename the process
16562306a36Sopenharmony_ci			 */
16662306a36Sopenharmony_ci			snprintf(proc_name, sizeof(proc_name), "timerlatu/%d", i);
16762306a36Sopenharmony_ci			pthread_setname_np(pthread_self(), proc_name);
16862306a36Sopenharmony_ci			prctl(PR_SET_NAME, (unsigned long)proc_name, 0, 0, 0);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci			timerlat_u_main(i, params);
17162306a36Sopenharmony_ci			/* timerlat_u_main should exit()! Anyways... */
17262306a36Sopenharmony_ci			pthread_exit(&retval);
17362306a36Sopenharmony_ci		}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci		/* parent */
17662306a36Sopenharmony_ci		if (pid == -1) {
17762306a36Sopenharmony_ci			timerlat_u_send_kill(procs, nr_cpus);
17862306a36Sopenharmony_ci			debug_msg("Failed to create child processes");
17962306a36Sopenharmony_ci			pthread_exit(&retval);
18062306a36Sopenharmony_ci		}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci		procs_count++;
18362306a36Sopenharmony_ci		procs[i] = pid;
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	while (params->should_run) {
18762306a36Sopenharmony_ci		/* check if processes died */
18862306a36Sopenharmony_ci		pid = waitpid(-1, &wstatus, WNOHANG);
18962306a36Sopenharmony_ci		if (pid != 0) {
19062306a36Sopenharmony_ci			for (i = 0; i < nr_cpus; i++) {
19162306a36Sopenharmony_ci				if (procs[i] == pid) {
19262306a36Sopenharmony_ci					procs[i] = 0;
19362306a36Sopenharmony_ci					procs_count--;
19462306a36Sopenharmony_ci				}
19562306a36Sopenharmony_ci			}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci			if (!procs_count)
19862306a36Sopenharmony_ci				break;
19962306a36Sopenharmony_ci		}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci		sleep(1);
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	timerlat_u_send_kill(procs, nr_cpus);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	while (procs_count) {
20762306a36Sopenharmony_ci		pid = waitpid(-1, &wstatus, 0);
20862306a36Sopenharmony_ci		if (pid == -1) {
20962306a36Sopenharmony_ci			err_msg("Failed to monitor child processes");
21062306a36Sopenharmony_ci			pthread_exit(&retval);
21162306a36Sopenharmony_ci		}
21262306a36Sopenharmony_ci		for (i = 0; i < nr_cpus; i++) {
21362306a36Sopenharmony_ci			if (procs[i] == pid) {
21462306a36Sopenharmony_ci				procs[i] = 0;
21562306a36Sopenharmony_ci				procs_count--;
21662306a36Sopenharmony_ci			}
21762306a36Sopenharmony_ci		}
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	params->stopped_running = 1;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	free(procs);
22362306a36Sopenharmony_ci	retval = 0;
22462306a36Sopenharmony_ci	pthread_exit(&retval);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci}
227