221 lines · 4.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Copyright (C) 2023 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org> */ #define _GNU_SOURCE#include <sched.h>#include <fcntl.h>#include <stdlib.h>#include <unistd.h>#include <stdio.h>#include <errno.h>#include <string.h>#include <tracefs.h>#include <pthread.h>#include <sys/wait.h>#include <sys/prctl.h> #include "common.h"#include "timerlat_u.h" /* * This is the user-space main for the tool timerlatu/ threads. * * It is as simple as this: *  - set affinity *  - set priority *  - open tracer fd *  - spin *  - close */static int timerlat_u_main(int cpu, struct timerlat_u_params *params){	struct sched_param sp = { .sched_priority = 95 };	char buffer[MAX_PATH];	int timerlat_fd;	cpu_set_t set;	int retval; 	/*	 * This all is only setting up the tool.	 */	CPU_ZERO(&set);	CPU_SET(cpu, &set); 	retval = sched_setaffinity(gettid(), sizeof(set), &set);	if (retval == -1) {		debug_msg("Error setting user thread affinity %d, is the CPU online?\n", cpu);		exit(1);	} 	if (!params->sched_param) {		retval = sched_setscheduler(0, SCHED_FIFO, &sp);		if (retval < 0)			fatal("Error setting timerlat u default priority: %s", strerror(errno));	} else {		retval = __set_sched_attr(getpid(), params->sched_param);		if (retval) {			/* __set_sched_attr prints an error message, so */			exit(0);		}	} 	if (params->cgroup_name) {		retval = set_pid_cgroup(gettid(), params->cgroup_name);		if (!retval) {			err_msg("Error setting timerlat u cgroup pid\n");			pthread_exit(&retval);		}	} 	/*	 * This is the tool's loop. If you want to use as base for your own tool...	 * go ahead.	 */	snprintf(buffer, sizeof(buffer), "osnoise/per_cpu/cpu%d/timerlat_fd", cpu); 	timerlat_fd = tracefs_instance_file_open(NULL, buffer, O_RDONLY);	if (timerlat_fd < 0)		fatal("Error opening %s:%s", buffer, strerror(errno)); 	debug_msg("User-space timerlat pid %d on cpu %d\n", gettid(), cpu); 	/* add should continue with a signal handler */	while (true) {		retval = read(timerlat_fd, buffer, ARRAY_SIZE(buffer));		if (retval < 0)			break;	} 	close(timerlat_fd); 	debug_msg("Leaving timerlat pid %d on cpu %d\n", gettid(), cpu);	exit(0);} /* * timerlat_u_send_kill - send a kill signal for all processes * * Return the number of processes that received the kill. */static int timerlat_u_send_kill(pid_t *procs){	int killed = 0;	int i, retval; 	for (i = 0; i < nr_cpus; i++) {		if (!procs[i])			continue;		retval = kill(procs[i], SIGKILL);		if (!retval)			killed++;		else			err_msg("Error killing child process %d\n", procs[i]);	} 	return killed;} /** * timerlat_u_dispatcher - dispatch one timerlatu/ process per monitored CPU * * This is a thread main that will fork one new process for each monitored * CPU. It will wait for: * *  - rtla to tell to kill the child processes *  - some child process to die, and the cleanup all the processes * * whichever comes first. * */void *timerlat_u_dispatcher(void *data){	struct timerlat_u_params *params = data;	char proc_name[128];	int procs_count = 0;	int retval = 1;	pid_t *procs;	int wstatus;	pid_t pid;	int i; 	debug_msg("Dispatching timerlat u procs\n"); 	procs = calloc(nr_cpus, sizeof(pid_t));	if (!procs)		pthread_exit(&retval); 	for (i = 0; i < nr_cpus; i++) {		if (params->set && !CPU_ISSET(i, params->set))			continue; 		pid = fork(); 		/* child */		if (!pid) { 			/*			 * rename the process			 */			snprintf(proc_name, sizeof(proc_name), "timerlatu/%d", i);			pthread_setname_np(pthread_self(), proc_name);			prctl(PR_SET_NAME, (unsigned long)proc_name, 0, 0, 0); 			timerlat_u_main(i, params);			/* timerlat_u_main should exit()! Anyways... */			pthread_exit(&retval);		} 		/* parent */		if (pid == -1) {			timerlat_u_send_kill(procs);			debug_msg("Failed to create child processes");			pthread_exit(&retval);		} 		procs_count++;		procs[i] = pid;	} 	while (params->should_run) {		/* check if processes died */		pid = waitpid(-1, &wstatus, WNOHANG);		if (pid != 0) {			for (i = 0; i < nr_cpus; i++) {				if (procs[i] == pid) {					procs[i] = 0;					procs_count--;				}			} 			if (!procs_count)				break;		} 		sleep(1);	} 	timerlat_u_send_kill(procs); 	while (procs_count) {		pid = waitpid(-1, &wstatus, 0);		if (pid == -1) {			err_msg("Failed to monitor child processes");			pthread_exit(&retval);		}		for (i = 0; i < nr_cpus; i++) {			if (procs[i] == pid) {				procs[i] = 0;				procs_count--;			}		}	} 	params->stopped_running = 1; 	free(procs);	retval = 0;	pthread_exit(&retval); }