Skip to content

Commit

Permalink
rtla/timerlat_top: Add timerlat user-space support
Browse files Browse the repository at this point in the history
Add the support for running timerlat threads in user-space. In this
mode, enabled with -u/--user-threads, timerlat dispatches user-space
processes that will loop in the timerlat_fd, measuring the overhead
for going to user-space and then returning to the kernel - in addition
to the existing measurements.

Here is one example of the tool's output with -u enabled:

  $ sudo timerlat top -u -d 600 -q
                                       Timer Latency
    0 00:10:01   |          IRQ Timer Latency (us)        |         Thread Timer Latency (us)      |    Ret user Timer Latency (us)
  CPU COUNT      |      cur       min       avg       max |      cur       min       avg       max |      cur       min       avg       max
    0 #600001    |        0         0         0         3 |        2         1         2         9 |        3         2         3        15
    1 #600001    |        0         0         0         2 |        2         1         2        13 |        2         2         3        18
    2 #600001    |        0         0         0        10 |        2         1         2        16 |        3         2         3        20
    3 #600001    |        0         0         0         7 |        2         1         2        10 |        3         2         3        11
    4 #600000    |        0         0         0        16 |        2         1         2        41 |        3         2         3        58
    5 #600000    |        0         0         0         3 |        2         1         2        10 |        3         2         3        13
    6 #600000    |        0         0         0         5 |        2         1         2         7 |        3         2         3        10
    7 #600000    |        0         0         0         1 |        2         1         2         7 |        3         2         3        10

The tuning setup like -p or -C work for the user-space threads as well.

Link: https://lkml.kernel.org/r/758ad2292a0a1d884138d08219e1a0f572d257a2.1686066600.git.bristot@kernel.org

Cc: William White <chwhite@redhat.com>
Cc: Jonathan Corbet <corbet@lwn.net>
Tested-by: Juri Lelli <juri.lelli@redhat.com>
Signed-off-by: Daniel Bristot de Oliveira <bristot@kernel.org>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
  • Loading branch information
Daniel Bristot de Oliveira authored and rostedt committed Jun 13, 2023
1 parent 7bc4d30 commit cdca4f4
Show file tree
Hide file tree
Showing 7 changed files with 474 additions and 6 deletions.
65 changes: 65 additions & 0 deletions tools/tracing/rtla/src/osnoise.c
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,67 @@ static void osnoise_put_irq_disable(struct osnoise_context *context)
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
*/
Expand Down Expand Up @@ -908,6 +969,9 @@ struct osnoise_context *osnoise_context_alloc(void)
context->orig_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);

return context;
Expand Down Expand Up @@ -935,6 +999,7 @@ void osnoise_put_context(struct osnoise_context *context)
osnoise_put_print_stack(context);
osnoise_put_tracing_thresh(context);
osnoise_put_irq_disable(context);
osnoise_put_workload(context);

free(context);
}
Expand Down
5 changes: 5 additions & 0 deletions tools/tracing/rtla/src/osnoise.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ struct osnoise_context {
/* -1 as init value because 0 is off */
int orig_opt_irq_disable;
int opt_irq_disable;

/* -1 as init value because 0 is off */
int orig_opt_workload;
int opt_workload;
};

/*
Expand Down Expand Up @@ -84,6 +88,7 @@ int osnoise_set_print_stack(struct osnoise_context *context,
long long print_stack);

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.
Expand Down
108 changes: 102 additions & 6 deletions tools/tracing/rtla/src/timerlat_top.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
#include <time.h>
#include <errno.h>
#include <sched.h>
#include <pthread.h>

#include "utils.h"
#include "osnoise.h"
#include "timerlat.h"
#include "timerlat_aa.h"
#include "timerlat_u.h"

struct timerlat_top_params {
char *cpus;
Expand All @@ -40,6 +42,7 @@ struct timerlat_top_params {
int dump_tasks;
int cgroup;
int hk_cpus;
int user_top;
cpu_set_t hk_cpu_set;
struct sched_attr sched_param;
struct trace_events *events;
Expand All @@ -48,6 +51,7 @@ struct timerlat_top_params {
struct timerlat_top_cpu {
int irq_count;
int thread_count;
int user_count;

unsigned long long cur_irq;
unsigned long long min_irq;
Expand All @@ -58,6 +62,11 @@ struct timerlat_top_cpu {
unsigned long long min_thread;
unsigned long long sum_thread;
unsigned long long max_thread;

unsigned long long cur_user;
unsigned long long min_user;
unsigned long long sum_user;
unsigned long long max_user;
};

struct timerlat_top_data {
Expand Down Expand Up @@ -98,6 +107,7 @@ static struct timerlat_top_data *timerlat_alloc_top(int nr_cpus)
for (cpu = 0; cpu < nr_cpus; cpu++) {
data->cpu_data[cpu].min_irq = ~0;
data->cpu_data[cpu].min_thread = ~0;
data->cpu_data[cpu].min_user = ~0;
}

return data;
Expand All @@ -124,12 +134,18 @@ timerlat_top_update(struct osnoise_tool *tool, int cpu,
update_min(&cpu_data->min_irq, &latency);
update_sum(&cpu_data->sum_irq, &latency);
update_max(&cpu_data->max_irq, &latency);
} else {
} else if (thread == 1) {
cpu_data->thread_count++;
cpu_data->cur_thread = latency;
update_min(&cpu_data->min_thread, &latency);
update_sum(&cpu_data->sum_thread, &latency);
update_max(&cpu_data->max_thread, &latency);
} else {
cpu_data->user_count++;
cpu_data->cur_user = latency;
update_min(&cpu_data->min_user, &latency);
update_sum(&cpu_data->sum_user, &latency);
update_max(&cpu_data->max_user, &latency);
}
}

Expand Down Expand Up @@ -172,15 +188,25 @@ static void timerlat_top_header(struct osnoise_tool *top)

trace_seq_printf(s, "\033[2;37;40m");
trace_seq_printf(s, " Timer Latency ");
if (params->user_top)
trace_seq_printf(s, " ");
trace_seq_printf(s, "\033[0;0;0m");
trace_seq_printf(s, "\n");

trace_seq_printf(s, "%-6s | IRQ Timer Latency (%s) | Thread Timer Latency (%s)\n", duration,
trace_seq_printf(s, "%-6s | IRQ Timer Latency (%s) | Thread Timer Latency (%s)", duration,
params->output_divisor == 1 ? "ns" : "us",
params->output_divisor == 1 ? "ns" : "us");

if (params->user_top) {
trace_seq_printf(s, " | Ret user Timer Latency (%s)",
params->output_divisor == 1 ? "ns" : "us");
}

trace_seq_printf(s, "\n");
trace_seq_printf(s, "\033[2;30;47m");
trace_seq_printf(s, "CPU COUNT | cur min avg max | cur min avg max");
if (params->user_top)
trace_seq_printf(s, " | cur min avg max");
trace_seq_printf(s, "\033[0;0;0m");
trace_seq_printf(s, "\n");
}
Expand Down Expand Up @@ -233,7 +259,27 @@ static void timerlat_top_print(struct osnoise_tool *top, int cpu)
trace_seq_printf(s, "%9llu ", cpu_data->min_thread / divisor);
trace_seq_printf(s, "%9llu ",
(cpu_data->sum_thread / cpu_data->thread_count) / divisor);
trace_seq_printf(s, "%9llu\n", cpu_data->max_thread / divisor);
trace_seq_printf(s, "%9llu", cpu_data->max_thread / divisor);
}

if (!params->user_top) {
trace_seq_printf(s, "\n");
return;
}

trace_seq_printf(s, " |");

if (!cpu_data->user_count) {
trace_seq_printf(s, " - ");
trace_seq_printf(s, " - ");
trace_seq_printf(s, " - ");
trace_seq_printf(s, " -\n");
} else {
trace_seq_printf(s, "%9llu ", cpu_data->cur_user / divisor);
trace_seq_printf(s, "%9llu ", cpu_data->min_user / divisor);
trace_seq_printf(s, "%9llu ",
(cpu_data->sum_user / cpu_data->user_count) / divisor);
trace_seq_printf(s, "%9llu\n", cpu_data->max_user / divisor);
}
}

Expand Down Expand Up @@ -288,7 +334,7 @@ static void timerlat_top_usage(char *usage)
"",
" usage: rtla timerlat [top] [-h] [-q] [-a us] [-d s] [-D] [-n] [-p us] [-i us] [-T us] [-s us] \\",
" [[-t[=file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] [-c cpu-list] [-H cpu-list]\\",
" [-P priority] [--dma-latency us] [--aa-only us] [-C[=cgroup_name]]",
" [-P priority] [--dma-latency us] [--aa-only us] [-C[=cgroup_name]] [-u]",
"",
" -h/--help: print this menu",
" -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit",
Expand Down Expand Up @@ -317,6 +363,7 @@ static void timerlat_top_usage(char *usage)
" 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",
" -u/--user-threads: use rtla user-space threads instead of in-kernel timerlat threads",
NULL,
};

Expand Down Expand Up @@ -371,6 +418,7 @@ static struct timerlat_top_params
{"stack", required_argument, 0, 's'},
{"thread", required_argument, 0, 'T'},
{"trace", optional_argument, 0, 't'},
{"user-threads", no_argument, 0, 'u'},
{"trigger", required_argument, 0, '0'},
{"filter", required_argument, 0, '1'},
{"dma-latency", required_argument, 0, '2'},
Expand All @@ -383,7 +431,7 @@ static struct timerlat_top_params
/* getopt_long stores the option index here. */
int option_index = 0;

c = getopt_long(argc, argv, "a:c:C::d:De:hH:i:np:P:qs:t::T:0:1:2:345:",
c = getopt_long(argc, argv, "a:c:C::d:De:hH:i:np:P:qs:t::T:u0:1:2:345:",
long_options, &option_index);

/* detect the end of the options. */
Expand Down Expand Up @@ -498,6 +546,9 @@ static struct timerlat_top_params
else
params->trace_output = "timerlat_trace.txt";

break;
case 'u':
params->user_top = true;
break;
case '0': /* trigger */
if (params->events) {
Expand Down Expand Up @@ -563,6 +614,7 @@ static int
timerlat_top_apply_config(struct osnoise_tool *top, struct timerlat_top_params *params)
{
int retval;
int i;

if (!params->sleep_time)
params->sleep_time = 1;
Expand All @@ -573,6 +625,9 @@ timerlat_top_apply_config(struct osnoise_tool *top, struct timerlat_top_params *
err_msg("Failed to apply CPUs config\n");
goto out_err;
}
} else {
for (i = 0; i < sysconf(_SC_NPROCESSORS_CONF); i++)
CPU_SET(i, &params->monitored_cpus);
}

if (params->stop_us) {
Expand Down Expand Up @@ -627,6 +682,14 @@ timerlat_top_apply_config(struct osnoise_tool *top, struct timerlat_top_params *
auto_house_keeping(&params->monitored_cpus);
}

if (params->user_top) {
retval = osnoise_set_workload(top->context, 0);
if (retval) {
err_msg("Failed to set OSNOISE_WORKLOAD option\n");
goto out_err;
}
}

return 0;

out_err:
Expand Down Expand Up @@ -687,10 +750,12 @@ int timerlat_top_main(int argc, char *argv[])
{
struct timerlat_top_params *params;
struct osnoise_tool *record = NULL;
struct timerlat_u_params params_u;
struct osnoise_tool *top = NULL;
struct osnoise_tool *aa = NULL;
struct trace_instance *trace;
int dma_latency_fd = -1;
pthread_t timerlat_u;
int return_value = 1;
char *max_lat;
int retval;
Expand Down Expand Up @@ -727,7 +792,7 @@ int timerlat_top_main(int argc, char *argv[])
}
}

if (params->cgroup) {
if (params->cgroup && !params->user_top) {
retval = set_comm_cgroup("timerlat/", params->cgroup_name);
if (!retval) {
err_msg("Failed to move threads to cgroup\n");
Expand Down Expand Up @@ -800,6 +865,25 @@ int timerlat_top_main(int argc, char *argv[])
top->start_time = time(NULL);
timerlat_top_set_signals(params);

if (params->user_top) {
/* rtla asked to stop */
params_u.should_run = 1;
/* all threads left */
params_u.stopped_running = 0;

params_u.set = &params->monitored_cpus;
if (params->set_sched)
params_u.sched_param = &params->sched_param;
else
params_u.sched_param = NULL;

params_u.cgroup_name = params->cgroup_name;

retval = pthread_create(&timerlat_u, NULL, timerlat_u_dispatcher, &params_u);
if (retval)
err_msg("Error creating timerlat user-space threads\n");
}

while (!stop_tracing) {
sleep(params->sleep_time);

Expand All @@ -823,6 +907,18 @@ int timerlat_top_main(int argc, char *argv[])
if (trace_is_off(&top->trace, &record->trace))
break;

/* is there still any user-threads ? */
if (params->user_top) {
if (params_u.stopped_running) {
debug_msg("timerlat user space threads stopped!\n");
break;
}
}
}

if (params->user_top && !params_u.stopped_running) {
params_u.should_run = 0;
sleep(1);
}

timerlat_print_stats(params, top);
Expand Down
Loading

0 comments on commit cdca4f4

Please sign in to comment.