Commit 2784d74b authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'trace-tools-v6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace

Pull tracing tooling updates from Steven Rostedt:

 - Add cgroup support for rtla via the -C option

 - Add --house-keeping option that tells rtla where to place the
   housekeeping threads

 - Have rtla/timerlat have its own tracing instance instead of using the
   top level tracing instance that is the default for other tracing
   users to use

 - Add auto analysis to timerlat_hist

 - Have rtla start the tracers after creating the instances

 - Reduce rtla hwnoise down to 75% from 100% as it runs with preemption
   disabled and can cause system instability at 100%

 - Add support to run timerlat_top and timerlat_hist threads in
   user-space instead of just using the kernel tasks

 - Some minor clean ups and documentation changes

* tag 'trace-tools-v6.5' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace:
  Documentation: Add tools/rtla timerlat -u option documentation
  rtla/timerlat_hist: Add timerlat user-space support
  rtla/timerlat_top: Add timerlat user-space support
  rtla/hwnoise: Reduce runtime to 75%
  rtla: Start the tracers after creating all instances
  rtla/timerlat_hist: Add auto-analysis support
  rtla/timerlat: Give timerlat auto analysis its own instance
  rtla: Automatically move rtla to a house-keeping cpu
  rtla: Change monitored_cpus from char * to cpu_set_t
  rtla: Add --house-keeping option
  rtla: Add -C cgroup support
parents 2a95b03d 61273832
...@@ -2,6 +2,10 @@ ...@@ -2,6 +2,10 @@
Set the osnoise tracer to run the sample threads in the cpu-list. Set the osnoise tracer to run the sample threads in the cpu-list.
**-H**, **--house-keeping** *cpu-list*
Run rtla control threads only on the given cpu-list.
**-d**, **--duration** *time[s|m|h|d]* **-d**, **--duration** *time[s|m|h|d]*
Set the duration of the session. Set the duration of the session.
...@@ -42,6 +46,10 @@ ...@@ -42,6 +46,10 @@
- *f:prio* - use SCHED_FIFO with *prio*; - *f:prio* - use SCHED_FIFO with *prio*;
- *d:runtime[us|ms|s]:period[us|ms|s]* - use SCHED_DEADLINE with *runtime* and *period* in nanoseconds. - *d:runtime[us|ms|s]:period[us|ms|s]* - use SCHED_DEADLINE with *runtime* and *period* in nanoseconds.
**-C**, **--cgroup**\[*=cgroup*]
Set a *cgroup* to the tracer's threads. If the **-C** option is passed without arguments, the tracer's thread will inherit **rtla**'s *cgroup*. Otherwise, the threads will be placed on the *cgroup* passed to the option.
**-h**, **--help** **-h**, **--help**
Print help menu. Print help menu.
...@@ -5,10 +5,3 @@ ...@@ -5,10 +5,3 @@
**--no-aa** **--no-aa**
disable auto-analysis, reducing rtla timerlat cpu usage disable auto-analysis, reducing rtla timerlat cpu usage
**--aa-only** *us*
Set stop tracing conditions and run without collecting and displaying statistics.
Print the auto-analysis if the system hits the stop tracing condition. This option
is useful to reduce rtla timerlat CPU, enabling the debug without the overhead of
collecting the statistics.
...@@ -26,3 +26,10 @@ ...@@ -26,3 +26,10 @@
Set the /dev/cpu_dma_latency to *us*, aiming to bound exit from idle latencies. Set the /dev/cpu_dma_latency to *us*, aiming to bound exit from idle latencies.
*cyclictest* sets this value to *0* by default, use **--dma-latency** *0* to have *cyclictest* sets this value to *0* by default, use **--dma-latency** *0* to have
similar results. similar results.
**-u**, **--user-threads**
Set timerlat to run without a workload, and then dispatches user-space workloads
to wait on the timerlat_fd. Once the workload is awakes, it goes to sleep again
adding so the measurement for the kernel-to-user and user-to-kernel to the tracer
output.
...@@ -29,15 +29,18 @@ OPTIONS ...@@ -29,15 +29,18 @@ OPTIONS
.. include:: common_options.rst .. include:: common_options.rst
.. include:: common_timerlat_aa.rst
EXAMPLE EXAMPLE
======= =======
In the example below, **rtla timerlat hist** is set to run for *10* minutes, In the example below, **rtla timerlat hist** is set to run for *10* minutes,
in the cpus *0-4*, *skipping zero* only lines. Moreover, **rtla timerlat in the cpus *0-4*, *skipping zero* only lines. Moreover, **rtla timerlat
hist** will change the priority of the *timerlat* threads to run under hist** will change the priority of the *timerlat* threads to run under
*SCHED_DEADLINE* priority, with a *10us* runtime every *1ms* period. The *SCHED_DEADLINE* priority, with a *10us* runtime every *1ms* period. The
*1ms* period is also passed to the *timerlat* tracer:: *1ms* period is also passed to the *timerlat* tracer. Auto-analysis is disabled
to reduce overhead ::
[root@alien ~]# timerlat hist -d 10m -c 0-4 -P d:100us:1ms -p 1ms [root@alien ~]# timerlat hist -d 10m -c 0-4 -P d:100us:1ms -p 1ms --no-aa
# RTLA timerlat histogram # RTLA timerlat histogram
# Time unit is microseconds (us) # Time unit is microseconds (us)
# Duration: 0 00:10:00 # Duration: 0 00:10:00
......
...@@ -32,6 +32,13 @@ OPTIONS ...@@ -32,6 +32,13 @@ OPTIONS
.. include:: common_timerlat_aa.rst .. include:: common_timerlat_aa.rst
**--aa-only** *us*
Set stop tracing conditions and run without collecting and displaying statistics.
Print the auto-analysis if the system hits the stop tracing condition. This option
is useful to reduce rtla timerlat CPU, enabling the debug without the overhead of
collecting the statistics.
EXAMPLE EXAMPLE
======= =======
......
...@@ -841,6 +841,67 @@ static void osnoise_put_irq_disable(struct osnoise_context *context) ...@@ -841,6 +841,67 @@ static void osnoise_put_irq_disable(struct osnoise_context *context)
context->orig_opt_irq_disable = OSNOISE_OPTION_INIT_VAL; context->orig_opt_irq_disable = OSNOISE_OPTION_INIT_VAL;
} }
static int osnoise_get_workload(struct osnoise_context *context)
{
if (context->opt_workload != OSNOISE_OPTION_INIT_VAL)
return context->opt_workload;
if (context->orig_opt_workload != OSNOISE_OPTION_INIT_VAL)
return context->orig_opt_workload;
context->orig_opt_workload = osnoise_options_get_option("OSNOISE_WORKLOAD");
return context->orig_opt_workload;
}
int osnoise_set_workload(struct osnoise_context *context, bool onoff)
{
int opt_workload = osnoise_get_workload(context);
int retval;
if (opt_workload == OSNOISE_OPTION_INIT_VAL)
return -1;
if (opt_workload == onoff)
return 0;
retval = osnoise_options_set_option("OSNOISE_WORKLOAD", onoff);
if (retval < 0)
return -1;
context->opt_workload = onoff;
return 0;
}
static void osnoise_restore_workload(struct osnoise_context *context)
{
int retval;
if (context->orig_opt_workload == OSNOISE_OPTION_INIT_VAL)
return;
if (context->orig_opt_workload == context->opt_workload)
goto out_done;
retval = osnoise_options_set_option("OSNOISE_WORKLOAD", context->orig_opt_workload);
if (retval < 0)
err_msg("Could not restore original OSNOISE_WORKLOAD option\n");
out_done:
context->orig_opt_workload = OSNOISE_OPTION_INIT_VAL;
}
static void osnoise_put_workload(struct osnoise_context *context)
{
osnoise_restore_workload(context);
if (context->orig_opt_workload == OSNOISE_OPTION_INIT_VAL)
return;
context->orig_opt_workload = OSNOISE_OPTION_INIT_VAL;
}
/* /*
* enable_osnoise - enable osnoise tracer in the trace_instance * enable_osnoise - enable osnoise tracer in the trace_instance
*/ */
...@@ -908,6 +969,9 @@ struct osnoise_context *osnoise_context_alloc(void) ...@@ -908,6 +969,9 @@ struct osnoise_context *osnoise_context_alloc(void)
context->orig_opt_irq_disable = OSNOISE_OPTION_INIT_VAL; context->orig_opt_irq_disable = OSNOISE_OPTION_INIT_VAL;
context->opt_irq_disable = OSNOISE_OPTION_INIT_VAL; context->opt_irq_disable = OSNOISE_OPTION_INIT_VAL;
context->orig_opt_workload = OSNOISE_OPTION_INIT_VAL;
context->opt_workload = OSNOISE_OPTION_INIT_VAL;
osnoise_get_context(context); osnoise_get_context(context);
return context; return context;
...@@ -935,6 +999,7 @@ void osnoise_put_context(struct osnoise_context *context) ...@@ -935,6 +999,7 @@ void osnoise_put_context(struct osnoise_context *context)
osnoise_put_print_stack(context); osnoise_put_print_stack(context);
osnoise_put_tracing_thresh(context); osnoise_put_tracing_thresh(context);
osnoise_put_irq_disable(context); osnoise_put_irq_disable(context);
osnoise_put_workload(context);
free(context); free(context);
} }
......
...@@ -42,6 +42,10 @@ struct osnoise_context { ...@@ -42,6 +42,10 @@ struct osnoise_context {
/* -1 as init value because 0 is off */ /* -1 as init value because 0 is off */
int orig_opt_irq_disable; int orig_opt_irq_disable;
int opt_irq_disable; int opt_irq_disable;
/* -1 as init value because 0 is off */
int orig_opt_workload;
int opt_workload;
}; };
/* /*
...@@ -84,6 +88,7 @@ int osnoise_set_print_stack(struct osnoise_context *context, ...@@ -84,6 +88,7 @@ int osnoise_set_print_stack(struct osnoise_context *context,
long long print_stack); long long print_stack);
int osnoise_set_irq_disable(struct osnoise_context *context, bool onoff); int osnoise_set_irq_disable(struct osnoise_context *context, bool onoff);
int osnoise_set_workload(struct osnoise_context *context, bool onoff);
/* /*
* osnoise_tool - osnoise based tool definition. * osnoise_tool - osnoise based tool definition.
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org> * Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org>
*/ */
#define _GNU_SOURCE
#include <getopt.h> #include <getopt.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
...@@ -11,14 +12,16 @@ ...@@ -11,14 +12,16 @@
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <time.h> #include <time.h>
#include <sched.h>
#include "utils.h" #include "utils.h"
#include "osnoise.h" #include "osnoise.h"
struct osnoise_hist_params { struct osnoise_hist_params {
char *cpus; char *cpus;
char *monitored_cpus; cpu_set_t monitored_cpus;
char *trace_output; char *trace_output;
char *cgroup_name;
unsigned long long runtime; unsigned long long runtime;
unsigned long long period; unsigned long long period;
long long threshold; long long threshold;
...@@ -28,6 +31,9 @@ struct osnoise_hist_params { ...@@ -28,6 +31,9 @@ struct osnoise_hist_params {
int duration; int duration;
int set_sched; int set_sched;
int output_divisor; int output_divisor;
int cgroup;
int hk_cpus;
cpu_set_t hk_cpu_set;
struct sched_attr sched_param; struct sched_attr sched_param;
struct trace_events *events; struct trace_events *events;
...@@ -268,7 +274,7 @@ static void osnoise_hist_header(struct osnoise_tool *tool) ...@@ -268,7 +274,7 @@ static void osnoise_hist_header(struct osnoise_tool *tool)
trace_seq_printf(s, "Index"); trace_seq_printf(s, "Index");
for (cpu = 0; cpu < data->nr_cpus; cpu++) { for (cpu = 0; cpu < data->nr_cpus; cpu++) {
if (params->cpus && !params->monitored_cpus[cpu]) if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))
continue; continue;
if (!data->hist[cpu].count) if (!data->hist[cpu].count)
...@@ -299,7 +305,7 @@ osnoise_print_summary(struct osnoise_hist_params *params, ...@@ -299,7 +305,7 @@ osnoise_print_summary(struct osnoise_hist_params *params,
trace_seq_printf(trace->seq, "count:"); trace_seq_printf(trace->seq, "count:");
for (cpu = 0; cpu < data->nr_cpus; cpu++) { for (cpu = 0; cpu < data->nr_cpus; cpu++) {
if (params->cpus && !params->monitored_cpus[cpu]) if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))
continue; continue;
if (!data->hist[cpu].count) if (!data->hist[cpu].count)
...@@ -313,7 +319,7 @@ osnoise_print_summary(struct osnoise_hist_params *params, ...@@ -313,7 +319,7 @@ osnoise_print_summary(struct osnoise_hist_params *params,
trace_seq_printf(trace->seq, "min: "); trace_seq_printf(trace->seq, "min: ");
for (cpu = 0; cpu < data->nr_cpus; cpu++) { for (cpu = 0; cpu < data->nr_cpus; cpu++) {
if (params->cpus && !params->monitored_cpus[cpu]) if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))
continue; continue;
if (!data->hist[cpu].count) if (!data->hist[cpu].count)
...@@ -328,7 +334,7 @@ osnoise_print_summary(struct osnoise_hist_params *params, ...@@ -328,7 +334,7 @@ osnoise_print_summary(struct osnoise_hist_params *params,
trace_seq_printf(trace->seq, "avg: "); trace_seq_printf(trace->seq, "avg: ");
for (cpu = 0; cpu < data->nr_cpus; cpu++) { for (cpu = 0; cpu < data->nr_cpus; cpu++) {
if (params->cpus && !params->monitored_cpus[cpu]) if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))
continue; continue;
if (!data->hist[cpu].count) if (!data->hist[cpu].count)
...@@ -346,7 +352,7 @@ osnoise_print_summary(struct osnoise_hist_params *params, ...@@ -346,7 +352,7 @@ osnoise_print_summary(struct osnoise_hist_params *params,
trace_seq_printf(trace->seq, "max: "); trace_seq_printf(trace->seq, "max: ");
for (cpu = 0; cpu < data->nr_cpus; cpu++) { for (cpu = 0; cpu < data->nr_cpus; cpu++) {
if (params->cpus && !params->monitored_cpus[cpu]) if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))
continue; continue;
if (!data->hist[cpu].count) if (!data->hist[cpu].count)
...@@ -381,7 +387,7 @@ osnoise_print_stats(struct osnoise_hist_params *params, struct osnoise_tool *too ...@@ -381,7 +387,7 @@ osnoise_print_stats(struct osnoise_hist_params *params, struct osnoise_tool *too
bucket * data->bucket_size); bucket * data->bucket_size);
for (cpu = 0; cpu < data->nr_cpus; cpu++) { for (cpu = 0; cpu < data->nr_cpus; cpu++) {
if (params->cpus && !params->monitored_cpus[cpu]) if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))
continue; continue;
if (!data->hist[cpu].count) if (!data->hist[cpu].count)
...@@ -405,7 +411,7 @@ osnoise_print_stats(struct osnoise_hist_params *params, struct osnoise_tool *too ...@@ -405,7 +411,7 @@ osnoise_print_stats(struct osnoise_hist_params *params, struct osnoise_tool *too
trace_seq_printf(trace->seq, "over: "); trace_seq_printf(trace->seq, "over: ");
for (cpu = 0; cpu < data->nr_cpus; cpu++) { for (cpu = 0; cpu < data->nr_cpus; cpu++) {
if (params->cpus && !params->monitored_cpus[cpu]) if (params->cpus && !CPU_ISSET(cpu, &params->monitored_cpus))
continue; continue;
if (!data->hist[cpu].count) if (!data->hist[cpu].count)
...@@ -432,8 +438,8 @@ static void osnoise_hist_usage(char *usage) ...@@ -432,8 +438,8 @@ static void osnoise_hist_usage(char *usage)
"", "",
" usage: rtla osnoise hist [-h] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\", " usage: rtla osnoise hist [-h] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\",
" [-T us] [-t[=file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\", " [-T us] [-t[=file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\",
" [-c cpu-list] [-P priority] [-b N] [-E N] [--no-header] [--no-summary] [--no-index] \\", " [-c cpu-list] [-H cpu-list] [-P priority] [-b N] [-E N] [--no-header] [--no-summary] \\",
" [--with-zeros]", " [--no-index] [--with-zeros] [-C[=cgroup_name]]",
"", "",
" -h/--help: print this menu", " -h/--help: print this menu",
" -a/--auto: set automatic trace mode, stopping the session if argument in us sample is hit", " -a/--auto: set automatic trace mode, stopping the session if argument in us sample is hit",
...@@ -443,6 +449,8 @@ static void osnoise_hist_usage(char *usage) ...@@ -443,6 +449,8 @@ static void osnoise_hist_usage(char *usage)
" -S/--stop-total us: stop trace if the total sample is higher than the argument in us", " -S/--stop-total us: stop trace if the total sample is higher than the argument in us",
" -T/--threshold us: the minimum delta to be considered a noise", " -T/--threshold us: the minimum delta to be considered a noise",
" -c/--cpus cpu-list: list of cpus to run osnoise threads", " -c/--cpus cpu-list: list of cpus to run osnoise threads",
" -H/--house-keeping cpus: run rtla control threads only on the given cpus",
" -C/--cgroup[=cgroup_name]: set cgroup, if no cgroup_name is passed, the rtla's cgroup will be inherited",
" -d/--duration time[s|m|h|d]: duration of the session", " -d/--duration time[s|m|h|d]: duration of the session",
" -D/--debug: print debug info", " -D/--debug: print debug info",
" -t/--trace[=file]: save the stopped trace to [file|osnoise_trace.txt]", " -t/--trace[=file]: save the stopped trace to [file|osnoise_trace.txt]",
...@@ -501,8 +509,10 @@ static struct osnoise_hist_params ...@@ -501,8 +509,10 @@ static struct osnoise_hist_params
{"bucket-size", required_argument, 0, 'b'}, {"bucket-size", required_argument, 0, 'b'},
{"entries", required_argument, 0, 'E'}, {"entries", required_argument, 0, 'E'},
{"cpus", required_argument, 0, 'c'}, {"cpus", required_argument, 0, 'c'},
{"cgroup", optional_argument, 0, 'C'},
{"debug", no_argument, 0, 'D'}, {"debug", no_argument, 0, 'D'},
{"duration", required_argument, 0, 'd'}, {"duration", required_argument, 0, 'd'},
{"house-keeping", required_argument, 0, 'H'},
{"help", no_argument, 0, 'h'}, {"help", no_argument, 0, 'h'},
{"period", required_argument, 0, 'p'}, {"period", required_argument, 0, 'p'},
{"priority", required_argument, 0, 'P'}, {"priority", required_argument, 0, 'P'},
...@@ -524,7 +534,7 @@ static struct osnoise_hist_params ...@@ -524,7 +534,7 @@ static struct osnoise_hist_params
/* getopt_long stores the option index here. */ /* getopt_long stores the option index here. */
int option_index = 0; int option_index = 0;
c = getopt_long(argc, argv, "a:c:b:d:e:E:Dhp:P:r:s:S:t::T:01234:5:", c = getopt_long(argc, argv, "a:c:C::b:d:e:E:DhH:p:P:r:s:S:t::T:01234:5:",
long_options, &option_index); long_options, &option_index);
/* detect the end of the options. */ /* detect the end of the options. */
...@@ -549,11 +559,21 @@ static struct osnoise_hist_params ...@@ -549,11 +559,21 @@ static struct osnoise_hist_params
osnoise_hist_usage("Bucket size needs to be > 0 and <= 1000000\n"); osnoise_hist_usage("Bucket size needs to be > 0 and <= 1000000\n");
break; break;
case 'c': case 'c':
retval = parse_cpu_list(optarg, &params->monitored_cpus); retval = parse_cpu_set(optarg, &params->monitored_cpus);
if (retval) if (retval)
osnoise_hist_usage("\nInvalid -c cpu list\n"); osnoise_hist_usage("\nInvalid -c cpu list\n");
params->cpus = optarg; params->cpus = optarg;
break; break;
case 'C':
params->cgroup = 1;
if (!optarg) {
/* will inherit this cgroup */
params->cgroup_name = NULL;
} else if (*optarg == '=') {
/* skip the = */
params->cgroup_name = ++optarg;
}
break;
case 'D': case 'D':
config_debug = 1; config_debug = 1;
break; break;
...@@ -583,6 +603,14 @@ static struct osnoise_hist_params ...@@ -583,6 +603,14 @@ static struct osnoise_hist_params
case '?': case '?':
osnoise_hist_usage(NULL); osnoise_hist_usage(NULL);
break; break;
case 'H':
params->hk_cpus = 1;
retval = parse_cpu_set(optarg, &params->hk_cpu_set);
if (retval) {
err_msg("Error parsing house keeping CPUs\n");
exit(EXIT_FAILURE);
}
break;
case 'p': case 'p':
params->period = get_llong_from_str(optarg); params->period = get_llong_from_str(optarg);
if (params->period > 10000000) if (params->period > 10000000)
...@@ -718,6 +746,24 @@ osnoise_hist_apply_config(struct osnoise_tool *tool, struct osnoise_hist_params ...@@ -718,6 +746,24 @@ osnoise_hist_apply_config(struct osnoise_tool *tool, struct osnoise_hist_params
} }
} }
if (params->hk_cpus) {
retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set),
&params->hk_cpu_set);
if (retval == -1) {
err_msg("Failed to set rtla to the house keeping CPUs\n");
goto out_err;
}
} else if (params->cpus) {
/*
* Even if the user do not set a house-keeping CPU, try to
* move rtla to a CPU set different to the one where the user
* set the workload to run.
*
* No need to check results as this is an automatic attempt.
*/
auto_house_keeping(&params->monitored_cpus);
}
return 0; return 0;
out_err: out_err:
...@@ -816,7 +862,13 @@ int osnoise_hist_main(int argc, char *argv[]) ...@@ -816,7 +862,13 @@ int osnoise_hist_main(int argc, char *argv[])
} }
} }
trace_instance_start(trace); if (params->cgroup) {
retval = set_comm_cgroup("timerlat/", params->cgroup_name);
if (!retval) {
err_msg("Failed to move threads to cgroup\n");
goto out_free;
}
}
if (params->trace_output) { if (params->trace_output) {
record = osnoise_init_trace_tool("osnoise"); record = osnoise_init_trace_tool("osnoise");
...@@ -831,9 +883,19 @@ int osnoise_hist_main(int argc, char *argv[]) ...@@ -831,9 +883,19 @@ int osnoise_hist_main(int argc, char *argv[])
goto out_hist; goto out_hist;
} }
trace_instance_start(&record->trace);
} }
/*
* Start the tracer here, after having set all instances.
*
* Let the trace instance start first for the case of hitting a stop
* tracing while enabling other instances. The trace instance is the
* one with most valuable information.
*/
if (params->trace_output)
trace_instance_start(&record->trace);
trace_instance_start(trace);
tool->start_time = time(NULL); tool->start_time = time(NULL);
osnoise_hist_set_signals(params); osnoise_hist_set_signals(params);
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org> * Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org>
*/ */
#define _GNU_SOURCE
#include <getopt.h> #include <getopt.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
...@@ -10,6 +11,7 @@ ...@@ -10,6 +11,7 @@
#include <unistd.h> #include <unistd.h>
#include <stdio.h> #include <stdio.h>
#include <time.h> #include <time.h>
#include <sched.h>
#include "osnoise.h" #include "osnoise.h"
#include "utils.h" #include "utils.h"
...@@ -24,8 +26,9 @@ enum osnoise_mode { ...@@ -24,8 +26,9 @@ enum osnoise_mode {
*/ */
struct osnoise_top_params { struct osnoise_top_params {
char *cpus; char *cpus;
char *monitored_cpus; cpu_set_t monitored_cpus;
char *trace_output; char *trace_output;
char *cgroup_name;
unsigned long long runtime; unsigned long long runtime;
unsigned long long period; unsigned long long period;
long long threshold; long long threshold;
...@@ -35,6 +38,9 @@ struct osnoise_top_params { ...@@ -35,6 +38,9 @@ struct osnoise_top_params {
int duration; int duration;
int quiet; int quiet;
int set_sched; int set_sched;
int cgroup;
int hk_cpus;
cpu_set_t hk_cpu_set;
struct sched_attr sched_param; struct sched_attr sched_param;
struct trace_events *events; struct trace_events *events;
enum osnoise_mode mode; enum osnoise_mode mode;
...@@ -257,7 +263,7 @@ osnoise_print_stats(struct osnoise_top_params *params, struct osnoise_tool *top) ...@@ -257,7 +263,7 @@ osnoise_print_stats(struct osnoise_top_params *params, struct osnoise_tool *top)
osnoise_top_header(top); osnoise_top_header(top);
for (i = 0; i < nr_cpus; i++) { for (i = 0; i < nr_cpus; i++) {
if (params->cpus && !params->monitored_cpus[i]) if (params->cpus && !CPU_ISSET(i, &params->monitored_cpus))
continue; continue;
osnoise_top_print(top, i); osnoise_top_print(top, i);
} }
...@@ -276,7 +282,7 @@ static void osnoise_top_usage(struct osnoise_top_params *params, char *usage) ...@@ -276,7 +282,7 @@ static void osnoise_top_usage(struct osnoise_top_params *params, char *usage)
static const char * const msg[] = { static const char * const msg[] = {
" [-h] [-q] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\", " [-h] [-q] [-D] [-d s] [-a us] [-p us] [-r us] [-s us] [-S us] \\",
" [-T us] [-t[=file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\", " [-T us] [-t[=file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] \\",
" [-c cpu-list] [-P priority]", " [-c cpu-list] [-H cpu-list] [-P priority] [-C[=cgroup_name]]",
"", "",
" -h/--help: print this menu", " -h/--help: print this menu",
" -a/--auto: set automatic trace mode, stopping the session if argument in us sample is hit", " -a/--auto: set automatic trace mode, stopping the session if argument in us sample is hit",
...@@ -286,6 +292,8 @@ static void osnoise_top_usage(struct osnoise_top_params *params, char *usage) ...@@ -286,6 +292,8 @@ static void osnoise_top_usage(struct osnoise_top_params *params, char *usage)
" -S/--stop-total us: stop trace if the total sample is higher than the argument in us", " -S/--stop-total us: stop trace if the total sample is higher than the argument in us",
" -T/--threshold us: the minimum delta to be considered a noise", " -T/--threshold us: the minimum delta to be considered a noise",
" -c/--cpus cpu-list: list of cpus to run osnoise threads", " -c/--cpus cpu-list: list of cpus to run osnoise threads",
" -H/--house-keeping cpus: run rtla control threads only on the given cpus",
" -C/--cgroup[=cgroup_name]: set cgroup, if no cgroup_name is passed, the rtla's cgroup will be inherited",
" -d/--duration time[s|m|h|d]: duration of the session", " -d/--duration time[s|m|h|d]: duration of the session",
" -D/--debug: print debug info", " -D/--debug: print debug info",
" -t/--trace[=file]: save the stopped trace to [file|osnoise_trace.txt]", " -t/--trace[=file]: save the stopped trace to [file|osnoise_trace.txt]",
...@@ -340,16 +348,24 @@ struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv) ...@@ -340,16 +348,24 @@ struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv)
if (!params) if (!params)
exit(1); exit(1);
if (strcmp(argv[0], "hwnoise") == 0) if (strcmp(argv[0], "hwnoise") == 0) {
params->mode = MODE_HWNOISE; params->mode = MODE_HWNOISE;
/*
* Reduce CPU usage for 75% to avoid killing the system.
*/
params->runtime = 750000;
params->period = 1000000;
}
while (1) { while (1) {
static struct option long_options[] = { static struct option long_options[] = {
{"auto", required_argument, 0, 'a'}, {"auto", required_argument, 0, 'a'},
{"cpus", required_argument, 0, 'c'}, {"cpus", required_argument, 0, 'c'},
{"cgroup", optional_argument, 0, 'C'},
{"debug", no_argument, 0, 'D'}, {"debug", no_argument, 0, 'D'},
{"duration", required_argument, 0, 'd'}, {"duration", required_argument, 0, 'd'},
{"event", required_argument, 0, 'e'}, {"event", required_argument, 0, 'e'},
{"house-keeping", required_argument, 0, 'H'},
{"help", no_argument, 0, 'h'}, {"help", no_argument, 0, 'h'},
{"period", required_argument, 0, 'p'}, {"period", required_argument, 0, 'p'},
{"priority", required_argument, 0, 'P'}, {"priority", required_argument, 0, 'P'},
...@@ -367,7 +383,7 @@ struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv) ...@@ -367,7 +383,7 @@ struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv)
/* getopt_long stores the option index here. */ /* getopt_long stores the option index here. */
int option_index = 0; int option_index = 0;
c = getopt_long(argc, argv, "a:c:d:De:hp:P:qr:s:S:t::T:0:1:", c = getopt_long(argc, argv, "a:c:C::d:De:hH:p:P:qr:s:S:t::T:0:1:",
long_options, &option_index); long_options, &option_index);
/* Detect the end of the options. */ /* Detect the end of the options. */
...@@ -387,11 +403,21 @@ struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv) ...@@ -387,11 +403,21 @@ struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv)
break; break;
case 'c': case 'c':
retval = parse_cpu_list(optarg, &params->monitored_cpus); retval = parse_cpu_set(optarg, &params->monitored_cpus);
if (retval) if (retval)
osnoise_top_usage(params, "\nInvalid -c cpu list\n"); osnoise_top_usage(params, "\nInvalid -c cpu list\n");
params->cpus = optarg; params->cpus = optarg;
break; break;
case 'C':
params->cgroup = 1;
if (!optarg) {
/* will inherit this cgroup */
params->cgroup_name = NULL;
} else if (*optarg == '=') {
/* skip the = */
params->cgroup_name = ++optarg;
}
break;
case 'D': case 'D':
config_debug = 1; config_debug = 1;
break; break;
...@@ -416,6 +442,14 @@ struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv) ...@@ -416,6 +442,14 @@ struct osnoise_top_params *osnoise_top_parse_args(int argc, char **argv)
case '?': case '?':
osnoise_top_usage(params, NULL); osnoise_top_usage(params, NULL);
break; break;
case 'H':
params->hk_cpus = 1;
retval = parse_cpu_set(optarg, &params->hk_cpu_set);
if (retval) {
err_msg("Error parsing house keeping CPUs\n");
exit(EXIT_FAILURE);
}
break;
case 'p': case 'p':
params->period = get_llong_from_str(optarg); params->period = get_llong_from_str(optarg);
if (params->period > 10000000) if (params->period > 10000000)
...@@ -547,6 +581,24 @@ osnoise_top_apply_config(struct osnoise_tool *tool, struct osnoise_top_params *p ...@@ -547,6 +581,24 @@ osnoise_top_apply_config(struct osnoise_tool *tool, struct osnoise_top_params *p
} }
} }
if (params->hk_cpus) {
retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set),
&params->hk_cpu_set);
if (retval == -1) {
err_msg("Failed to set rtla to the house keeping CPUs\n");
goto out_err;
}
} else if (params->cpus) {
/*
* Even if the user do not set a house-keeping CPU, try to
* move rtla to a CPU set different to the one where the user
* set the workload to run.
*
* No need to check results as this is an automatic attempt.
*/
auto_house_keeping(&params->monitored_cpus);
}
return 0; return 0;
out_err: out_err:
...@@ -643,7 +695,13 @@ int osnoise_top_main(int argc, char **argv) ...@@ -643,7 +695,13 @@ int osnoise_top_main(int argc, char **argv)
} }
} }
trace_instance_start(trace); if (params->cgroup) {
retval = set_comm_cgroup("osnoise/", params->cgroup_name);
if (!retval) {
err_msg("Failed to move threads to cgroup\n");
goto out_free;
}
}
if (params->trace_output) { if (params->trace_output) {
record = osnoise_init_trace_tool("osnoise"); record = osnoise_init_trace_tool("osnoise");
...@@ -657,9 +715,18 @@ int osnoise_top_main(int argc, char **argv) ...@@ -657,9 +715,18 @@ int osnoise_top_main(int argc, char **argv)
if (retval) if (retval)
goto out_top; goto out_top;
} }
}
/*
* Start the tracer here, after having set all instances.
*
* Let the trace instance start first for the case of hitting a stop
* tracing while enabling other instances. The trace instance is the
* one with most valuable information.
*/
if (params->trace_output)
trace_instance_start(&record->trace); trace_instance_start(&record->trace);
} trace_instance_start(trace);
tool->start_time = time(NULL); tool->start_time = time(NULL);
osnoise_top_set_signals(params); osnoise_top_set_signals(params);
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "utils.h" #include "utils.h"
#include "osnoise.h" #include "osnoise.h"
#include "timerlat.h" #include "timerlat.h"
#include <unistd.h>
enum timelat_state { enum timelat_state {
TIMERLAT_INIT = 0, TIMERLAT_INIT = 0,
...@@ -233,7 +234,7 @@ static int timerlat_aa_thread_latency(struct timerlat_aa_data *taa_data, ...@@ -233,7 +234,7 @@ static int timerlat_aa_thread_latency(struct timerlat_aa_data *taa_data,
* *
* Returns 0 on success, -1 otherwise. * Returns 0 on success, -1 otherwise.
*/ */
int timerlat_aa_handler(struct trace_seq *s, struct tep_record *record, static int timerlat_aa_handler(struct trace_seq *s, struct tep_record *record,
struct tep_event *event, void *context) struct tep_event *event, void *context)
{ {
struct timerlat_aa_context *taa_ctx = timerlat_aa_get_ctx(); struct timerlat_aa_context *taa_ctx = timerlat_aa_get_ctx();
...@@ -665,6 +666,25 @@ static void timerlat_thread_analysis(struct timerlat_aa_data *taa_data, int cpu, ...@@ -665,6 +666,25 @@ static void timerlat_thread_analysis(struct timerlat_aa_data *taa_data, int cpu,
ns_to_usf(total)); ns_to_usf(total));
} }
static int timerlat_auto_analysis_collect_trace(struct timerlat_aa_context *taa_ctx)
{
struct trace_instance *trace = &taa_ctx->tool->trace;
int retval;
retval = tracefs_iterate_raw_events(trace->tep,
trace->inst,
NULL,
0,
collect_registered_events,
trace);
if (retval < 0) {
err_msg("Error iterating on events\n");
return 0;
}
return 1;
}
/** /**
* timerlat_auto_analysis - Analyze the collected data * timerlat_auto_analysis - Analyze the collected data
*/ */
...@@ -677,6 +697,8 @@ void timerlat_auto_analysis(int irq_thresh, int thread_thresh) ...@@ -677,6 +697,8 @@ void timerlat_auto_analysis(int irq_thresh, int thread_thresh)
struct tep_handle *tep; struct tep_handle *tep;
int cpu; int cpu;
timerlat_auto_analysis_collect_trace(taa_ctx);
/* bring stop tracing to the ns scale */ /* bring stop tracing to the ns scale */
irq_thresh = irq_thresh * 1000; irq_thresh = irq_thresh * 1000;
thread_thresh = thread_thresh * 1000; thread_thresh = thread_thresh * 1000;
...@@ -838,6 +860,10 @@ static int timerlat_aa_init_seqs(struct timerlat_aa_context *taa_ctx) ...@@ -838,6 +860,10 @@ static int timerlat_aa_init_seqs(struct timerlat_aa_context *taa_ctx)
*/ */
static void timerlat_aa_unregister_events(struct osnoise_tool *tool, int dump_tasks) static void timerlat_aa_unregister_events(struct osnoise_tool *tool, int dump_tasks)
{ {
tep_unregister_event_handler(tool->trace.tep, -1, "ftrace", "timerlat",
timerlat_aa_handler, tool);
tracefs_event_disable(tool->trace.inst, "osnoise", NULL); tracefs_event_disable(tool->trace.inst, "osnoise", NULL);
tep_unregister_event_handler(tool->trace.tep, -1, "osnoise", "nmi_noise", tep_unregister_event_handler(tool->trace.tep, -1, "osnoise", "nmi_noise",
...@@ -875,6 +901,10 @@ static int timerlat_aa_register_events(struct osnoise_tool *tool, int dump_tasks ...@@ -875,6 +901,10 @@ static int timerlat_aa_register_events(struct osnoise_tool *tool, int dump_tasks
{ {
int retval; int retval;
tep_register_event_handler(tool->trace.tep, -1, "ftrace", "timerlat",
timerlat_aa_handler, tool);
/* /*
* register auto-analysis handlers. * register auto-analysis handlers.
*/ */
...@@ -955,8 +985,9 @@ void timerlat_aa_destroy(void) ...@@ -955,8 +985,9 @@ void timerlat_aa_destroy(void)
* *
* Returns 0 on success, -1 otherwise. * Returns 0 on success, -1 otherwise.
*/ */
int timerlat_aa_init(struct osnoise_tool *tool, int nr_cpus, int dump_tasks) int timerlat_aa_init(struct osnoise_tool *tool, int dump_tasks)
{ {
int nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
struct timerlat_aa_context *taa_ctx; struct timerlat_aa_context *taa_ctx;
int retval; int retval;
......
...@@ -3,10 +3,7 @@ ...@@ -3,10 +3,7 @@
* Copyright (C) 2023 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org> * Copyright (C) 2023 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org>
*/ */
int timerlat_aa_init(struct osnoise_tool *tool, int nr_cpus, int dump_task); int timerlat_aa_init(struct osnoise_tool *tool, int dump_task);
void timerlat_aa_destroy(void); void timerlat_aa_destroy(void);
int timerlat_aa_handler(struct trace_seq *s, struct tep_record *record,
struct tep_event *event, void *context);
void timerlat_auto_analysis(int irq_thresh, int thread_thresh); void timerlat_auto_analysis(int irq_thresh, int thread_thresh);
This diff is collapsed.
This diff is collapsed.
// 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 "utils.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[1024];
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) {
err_msg("Error setting user thread affinity\n");
exit(1);
}
if (!params->sched_param) {
retval = sched_setscheduler(0, SCHED_FIFO, &sp);
if (retval < 0) {
err_msg("Error setting timerlat u default priority: %s\n", strerror(errno));
exit(1);
}
} 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) {
err_msg("Error opening %s:%s\n", buffer, strerror(errno));
exit(1);
}
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, 1024);
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 nr_cpus)
{
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)
{
int nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
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, nr_cpus);
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--;
}
}
break;
}
sleep(1);
}
timerlat_u_send_kill(procs, nr_cpus);
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);
}
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2023 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org>
*/
struct timerlat_u_params {
/* timerlat -> timerlat_u: user-space threads can keep running */
int should_run;
/* timerlat_u -> timerlat: all timerlat_u threads left, no reason to continue */
int stopped_running;
/* threads config */
cpu_set_t *set;
char *cgroup_name;
struct sched_attr *sched_param;
};
void *timerlat_u_dispatcher(void *data);
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org> * Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org>
*/ */
#define _GNU_SOURCE
#include <dirent.h> #include <dirent.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdlib.h> #include <stdlib.h>
...@@ -88,27 +89,24 @@ void get_duration(time_t start_time, char *output, int output_size) ...@@ -88,27 +89,24 @@ void get_duration(time_t start_time, char *output, int output_size)
} }
/* /*
* parse_cpu_list - parse a cpu_list filling a char vector with cpus set * parse_cpu_set - parse a cpu_list filling cpu_set_t argument
* *
* Receives a cpu list, like 1-3,5 (cpus 1, 2, 3, 5), and then set the char * Receives a cpu list, like 1-3,5 (cpus 1, 2, 3, 5), and then set
* in the monitored_cpus. * filling cpu_set_t argument.
* *
* XXX: convert to a bitmask. * Returns 1 on success, 0 otherwise.
*/ */
int parse_cpu_list(char *cpu_list, char **monitored_cpus) int parse_cpu_set(char *cpu_list, cpu_set_t *set)
{ {
char *mon_cpus;
const char *p; const char *p;
int end_cpu; int end_cpu;
int nr_cpus; int nr_cpus;
int cpu; int cpu;
int i; int i;
nr_cpus = sysconf(_SC_NPROCESSORS_CONF); CPU_ZERO(set);
mon_cpus = calloc(nr_cpus, sizeof(char)); nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
if (!mon_cpus)
goto err;
for (p = cpu_list; *p; ) { for (p = cpu_list; *p; ) {
cpu = atoi(p); cpu = atoi(p);
...@@ -128,12 +126,12 @@ int parse_cpu_list(char *cpu_list, char **monitored_cpus) ...@@ -128,12 +126,12 @@ int parse_cpu_list(char *cpu_list, char **monitored_cpus)
end_cpu = cpu; end_cpu = cpu;
if (cpu == end_cpu) { if (cpu == end_cpu) {
debug_msg("cpu_list: adding cpu %d\n", cpu); debug_msg("cpu_set: adding cpu %d\n", cpu);
mon_cpus[cpu] = 1; CPU_SET(cpu, set);
} else { } else {
for (i = cpu; i <= end_cpu; i++) { for (i = cpu; i <= end_cpu; i++) {
debug_msg("cpu_list: adding cpu %d\n", i); debug_msg("cpu_set: adding cpu %d\n", i);
mon_cpus[i] = 1; CPU_SET(i, set);
} }
} }
...@@ -141,12 +139,9 @@ int parse_cpu_list(char *cpu_list, char **monitored_cpus) ...@@ -141,12 +139,9 @@ int parse_cpu_list(char *cpu_list, char **monitored_cpus)
p++; p++;
} }
*monitored_cpus = mon_cpus;
return 0; return 0;
err: err:
debug_msg("Error parsing the cpu list %s", cpu_list); debug_msg("Error parsing the cpu set %s\n", cpu_list);
return 1; return 1;
} }
...@@ -529,3 +524,296 @@ int set_cpu_dma_latency(int32_t latency) ...@@ -529,3 +524,296 @@ int set_cpu_dma_latency(int32_t latency)
return fd; return fd;
} }
#define _STR(x) #x
#define STR(x) _STR(x)
/*
* find_mount - find a the mount point of a given fs
*
* Returns 0 if mount is not found, otherwise return 1 and fill mp
* with the mount point.
*/
static const int find_mount(const char *fs, char *mp, int sizeof_mp)
{
char mount_point[MAX_PATH];
char type[100];
int found;
FILE *fp;
fp = fopen("/proc/mounts", "r");
if (!fp)
return 0;
while (fscanf(fp, "%*s %" STR(MAX_PATH) "s %99s %*s %*d %*d\n", mount_point, type) == 2) {
if (strcmp(type, fs) == 0) {
found = 1;
break;
}
}
fclose(fp);
if (!found)
return 0;
memset(mp, 0, sizeof_mp);
strncpy(mp, mount_point, sizeof_mp - 1);
debug_msg("Fs %s found at %s\n", fs, mp);
return 1;
}
/*
* get_self_cgroup - get the current thread cgroup path
*
* Parse /proc/$$/cgroup file to get the thread's cgroup. As an example of line to parse:
*
* 0::/user.slice/user-0.slice/session-3.scope'\n'
*
* This function is interested in the content after the second : and before the '\n'.
*
* Returns 1 if a string was found, 0 otherwise.
*/
static int get_self_cgroup(char *self_cg, int sizeof_self_cg)
{
char path[MAX_PATH], *start;
int fd, retval;
snprintf(path, MAX_PATH, "/proc/%d/cgroup", getpid());
fd = open(path, O_RDONLY);
if (fd < 0)
return 0;
retval = read(fd, path, MAX_PATH);
close(fd);
if (retval <= 0)
return 0;
start = path;
start = strstr(start, ":");
if (!start)
return 0;
/* skip ":" */
start++;
start = strstr(start, ":");
if (!start)
return 0;
/* skip ":" */
start++;
if (strlen(start) >= sizeof_self_cg)
return 0;
snprintf(self_cg, sizeof_self_cg, "%s", start);
/* Swap '\n' with '\0' */
start = strstr(self_cg, "\n");
/* there must be '\n' */
if (!start)
return 0;
/* ok, it found a string after the second : and before the \n */
*start = '\0';
return 1;
}
/*
* set_comm_cgroup - Set cgroup to pid_t pid
*
* If cgroup argument is not NULL, the threads will move to the given cgroup.
* Otherwise, the cgroup of the calling, i.e., rtla, thread will be used.
*
* Supports cgroup v2.
*
* Returns 1 on success, 0 otherwise.
*/
int set_pid_cgroup(pid_t pid, const char *cgroup)
{
char cgroup_path[MAX_PATH - strlen("/cgroup.procs")];
char cgroup_procs[MAX_PATH];
char pid_str[24];
int retval;
int cg_fd;
retval = find_mount("cgroup2", cgroup_path, sizeof(cgroup_path));
if (!retval) {
err_msg("Did not find cgroupv2 mount point\n");
return 0;
}
if (!cgroup) {
retval = get_self_cgroup(&cgroup_path[strlen(cgroup_path)],
sizeof(cgroup_path) - strlen(cgroup_path));
if (!retval) {
err_msg("Did not find self cgroup\n");
return 0;
}
} else {
snprintf(&cgroup_path[strlen(cgroup_path)],
sizeof(cgroup_path) - strlen(cgroup_path), "%s/", cgroup);
}
snprintf(cgroup_procs, MAX_PATH, "%s/cgroup.procs", cgroup_path);
debug_msg("Using cgroup path at: %s\n", cgroup_procs);
cg_fd = open(cgroup_procs, O_RDWR);
if (cg_fd < 0)
return 0;
snprintf(pid_str, sizeof(pid_str), "%d\n", pid);
retval = write(cg_fd, pid_str, strlen(pid_str));
if (retval < 0)
err_msg("Error setting cgroup attributes for pid:%s - %s\n",
pid_str, strerror(errno));
else
debug_msg("Set cgroup attributes for pid:%s\n", pid_str);
close(cg_fd);
return (retval >= 0);
}
/**
* set_comm_cgroup - Set cgroup to threads starting with char *comm_prefix
*
* If cgroup argument is not NULL, the threads will move to the given cgroup.
* Otherwise, the cgroup of the calling, i.e., rtla, thread will be used.
*
* Supports cgroup v2.
*
* Returns 1 on success, 0 otherwise.
*/
int set_comm_cgroup(const char *comm_prefix, const char *cgroup)
{
char cgroup_path[MAX_PATH - strlen("/cgroup.procs")];
char cgroup_procs[MAX_PATH];
struct dirent *proc_entry;
DIR *procfs;
int retval;
int cg_fd;
if (strlen(comm_prefix) >= MAX_PATH) {
err_msg("Command prefix is too long: %d < strlen(%s)\n",
MAX_PATH, comm_prefix);
return 0;
}
retval = find_mount("cgroup2", cgroup_path, sizeof(cgroup_path));
if (!retval) {
err_msg("Did not find cgroupv2 mount point\n");
return 0;
}
if (!cgroup) {
retval = get_self_cgroup(&cgroup_path[strlen(cgroup_path)],
sizeof(cgroup_path) - strlen(cgroup_path));
if (!retval) {
err_msg("Did not find self cgroup\n");
return 0;
}
} else {
snprintf(&cgroup_path[strlen(cgroup_path)],
sizeof(cgroup_path) - strlen(cgroup_path), "%s/", cgroup);
}
snprintf(cgroup_procs, MAX_PATH, "%s/cgroup.procs", cgroup_path);
debug_msg("Using cgroup path at: %s\n", cgroup_procs);
cg_fd = open(cgroup_procs, O_RDWR);
if (cg_fd < 0)
return 0;
procfs = opendir("/proc");
if (!procfs) {
err_msg("Could not open procfs\n");
goto out_cg;
}
while ((proc_entry = readdir(procfs))) {
retval = procfs_is_workload_pid(comm_prefix, proc_entry);
if (!retval)
continue;
retval = write(cg_fd, proc_entry->d_name, strlen(proc_entry->d_name));
if (retval < 0) {
err_msg("Error setting cgroup attributes for pid:%s - %s\n",
proc_entry->d_name, strerror(errno));
goto out_procfs;
}
debug_msg("Set cgroup attributes for pid:%s\n", proc_entry->d_name);
}
closedir(procfs);
close(cg_fd);
return 1;
out_procfs:
closedir(procfs);
out_cg:
close(cg_fd);
return 0;
}
/**
* auto_house_keeping - Automatically move rtla out of measurement threads
*
* Try to move rtla away from the tracer, if possible.
*
* Returns 1 on success, 0 otherwise.
*/
int auto_house_keeping(cpu_set_t *monitored_cpus)
{
cpu_set_t rtla_cpus, house_keeping_cpus;
int retval;
/* first get the CPUs in which rtla can actually run. */
retval = sched_getaffinity(getpid(), sizeof(rtla_cpus), &rtla_cpus);
if (retval == -1) {
debug_msg("Could not get rtla affinity, rtla might run with the threads!\n");
return 0;
}
/* then check if the existing setup is already good. */
CPU_AND(&house_keeping_cpus, &rtla_cpus, monitored_cpus);
if (!CPU_COUNT(&house_keeping_cpus)) {
debug_msg("rtla and the monitored CPUs do not share CPUs.");
debug_msg("Skipping auto house-keeping\n");
return 1;
}
/* remove the intersection */
CPU_XOR(&house_keeping_cpus, &rtla_cpus, monitored_cpus);
/* get only those that rtla can run */
CPU_AND(&house_keeping_cpus, &house_keeping_cpus, &rtla_cpus);
/* is there any cpu left? */
if (!CPU_COUNT(&house_keeping_cpus)) {
debug_msg("Could not find any CPU for auto house-keeping\n");
return 0;
}
retval = sched_setaffinity(getpid(), sizeof(house_keeping_cpus), &house_keeping_cpus);
if (retval == -1) {
debug_msg("Could not set affinity for auto house-keeping\n");
return 0;
}
debug_msg("rtla automatically moved to an auto house-keeping cpu set\n");
return 1;
}
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
#include <stdint.h> #include <stdint.h>
#include <time.h> #include <time.h>
#include <sched.h>
/* /*
* '18446744073709551615\0' * '18446744073709551615\0'
...@@ -54,8 +56,13 @@ struct sched_attr { ...@@ -54,8 +56,13 @@ struct sched_attr {
}; };
int parse_prio(char *arg, struct sched_attr *sched_param); int parse_prio(char *arg, struct sched_attr *sched_param);
int parse_cpu_set(char *cpu_list, cpu_set_t *set);
int __set_sched_attr(int pid, struct sched_attr *attr);
int set_comm_sched_attr(const char *comm_prefix, struct sched_attr *attr); int set_comm_sched_attr(const char *comm_prefix, struct sched_attr *attr);
int set_comm_cgroup(const char *comm_prefix, const char *cgroup);
int set_pid_cgroup(pid_t pid, const char *cgroup);
int set_cpu_dma_latency(int32_t latency); int set_cpu_dma_latency(int32_t latency);
int auto_house_keeping(cpu_set_t *monitored_cpus);
#define ns_to_usf(x) (((double)x/1000)) #define ns_to_usf(x) (((double)x/1000))
#define ns_to_per(total, part) ((part * 100) / (double)total) #define ns_to_per(total, part) ((part * 100) / (double)total)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment