The timerlat tracer provides an interface for any application to wait for the timerlat's periodic wakeup. Currently, rtla timerlat uses it to dispatch its user-space workload (-u option).
But as the tracer interface is generic, rtla timerlat can also be used to monitor any workload that uses it. For example, a user might place their own workload to wait on the tracer interface, and monitor the results with rtla timerlat. Add the -U option to rtla timerlat top and hist. With this option, rtla timerlat will not dispatch its workload but only setting up the system, waiting for a user to dispatch its workload. The sample code in this patch is an example of python application that loops in the timerlat tracer fd. To use it, dispatch: # rtla timerlat -U In a terminal, then run the python program on another terminal, specifying the CPU to run it. For example, setting on CPU 1: #./timerlat_load.py 1 Then rtla timerlat will start printing the statistics of the ./timerlat_load.py app. An interesting point is that the "Ret user Timer Latency" value is the overall response time of the load. The sample load does a memory copy to exemplify that. The stop tracing options on rtla timerlat works in this setup as well, including auto analysis. Signed-off-by: Daniel Bristot de Oliveira <bris...@kernel.org> --- .../tools/rtla/common_timerlat_options.rst | 6 ++ tools/tracing/rtla/sample/timerlat_load.py | 74 +++++++++++++++++++ tools/tracing/rtla/src/timerlat_hist.c | 16 ++-- tools/tracing/rtla/src/timerlat_top.c | 14 +++- 4 files changed, 101 insertions(+), 9 deletions(-) create mode 100644 tools/tracing/rtla/sample/timerlat_load.py diff --git a/Documentation/tools/rtla/common_timerlat_options.rst b/Documentation/tools/rtla/common_timerlat_options.rst index 88506b397c2d..d3255ed70195 100644 --- a/Documentation/tools/rtla/common_timerlat_options.rst +++ b/Documentation/tools/rtla/common_timerlat_options.rst @@ -33,3 +33,9 @@ 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. + +**-U**, **--user-load** + + Set timerlat to run without workload, waiting for the user to dispatch a per-cpu + task that waits for a new period on the tracing/osnoise/per_cpu/cpu$ID/timerlat_fd. + See linux/tools/rtla/sample/timerlat_load.py for an example of user-load code. diff --git a/tools/tracing/rtla/sample/timerlat_load.py b/tools/tracing/rtla/sample/timerlat_load.py new file mode 100644 index 000000000000..8cc5eb2d2e69 --- /dev/null +++ b/tools/tracing/rtla/sample/timerlat_load.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +# +# Copyright (C) 2024 Red Hat, Inc. Daniel Bristot de Oliveira <bris...@kernel.org> +# +# This is a sample code about how to use timerlat's timer by any workload +# so rtla can measure and provide auto-analysis for the overall latency (IOW +# the response time) for a task. +# +# Before running it, you need to dispatch timerlat with -U option in a terminal. +# Then # run this script pinned to a CPU on another terminal. For example: +# +# timerlat_load.py 1 -p 95 +# +# The "Timerlat IRQ" is the IRQ latency, The thread latency is the latency +# for the python process to get the CPU. The Ret from user Timer Latency is +# the overall latency. In other words, it is the response time for that +# activation. +# +# This is just an example, the load is reading 20MB of data from /dev/full +# It is in python because it is easy to read :-) + +import argparse +import sys +import os + +parser = argparse.ArgumentParser(description='user-space timerlat thread in Python') +parser.add_argument("cpu", help='CPU to run timerlat thread') +parser.add_argument("-p", "--prio", help='FIFO priority') + +args = parser.parse_args() + +try: + affinity_mask = { int(args.cpu) } +except: + print("Invalid cpu: " + args.cpu) + exit(1) + +try: + os.sched_setaffinity(0, affinity_mask); +except: + print("Error setting affinity") + exit(1) + +if (args.prio): + try: + param = os.sched_param(int(args.prio)) + os.sched_setscheduler(0, os.SCHED_FIFO, param) + except: + print("Error setting priority") + exit(1) + +try: + timerlat_path = "/sys/kernel/tracing/osnoise/per_cpu/cpu" + args.cpu + "/timerlat_fd" + timerlat_fd = open(timerlat_path, 'r') +except: + print("Error opening timerlat fd, did you run timerlat -U?") + exit(1) + +try: + data_fd = open("/dev/full", 'r'); +except: + print("Error opening data fd") + +while True: + try: + timerlat_fd.read(1) + data_fd.read(20*1024*1024) + except: + print("Leaving") + break + +timerlat_fd.close() +data_fd.close() diff --git a/tools/tracing/rtla/src/timerlat_hist.c b/tools/tracing/rtla/src/timerlat_hist.c index 3a5b8c409e7d..a668bf52e78e 100644 --- a/tools/tracing/rtla/src/timerlat_hist.c +++ b/tools/tracing/rtla/src/timerlat_hist.c @@ -39,6 +39,7 @@ struct timerlat_hist_params { int hk_cpus; int no_aa; int dump_tasks; + int user_workload; int user_hist; cpu_set_t hk_cpu_set; struct sched_attr sched_param; @@ -534,6 +535,7 @@ static void timerlat_hist_usage(char *usage) " 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", + " -U/--user-load: enable timerlat for user-defined user-space workload", NULL, }; @@ -591,6 +593,7 @@ static struct timerlat_hist_params {"thread", required_argument, 0, 'T'}, {"trace", optional_argument, 0, 't'}, {"user-threads", no_argument, 0, 'u'}, + {"user-load", no_argument, 0, 'U'}, {"event", required_argument, 0, 'e'}, {"no-irq", no_argument, 0, '0'}, {"no-thread", no_argument, 0, '1'}, @@ -609,7 +612,7 @@ static struct timerlat_hist_params /* getopt_long stores the option index here. */ int option_index = 0; - c = getopt_long(argc, argv, "a:c:C::b:d:e:E:DhH:i:np:P:s:t::T:u0123456:7:8:9\1", + c = getopt_long(argc, argv, "a:c:C::b:d:e:E:DhH:i:np:P:s:t::T:uU0123456:7:8:9\1", long_options, &option_index); /* detect the end of the options. */ @@ -720,6 +723,9 @@ static struct timerlat_hist_params params->trace_output = "timerlat_trace.txt"; break; case 'u': + params->user_workload = 1; + /* fallback: -u implies in -U */ + case 'U': params->user_hist = 1; break; case '0': /* no irq */ @@ -981,7 +987,7 @@ int timerlat_hist_main(int argc, char *argv[]) } } - if (params->cgroup && !params->user_hist) { + if (params->cgroup && !params->user_workload) { retval = set_comm_cgroup("timerlat/", params->cgroup_name); if (!retval) { err_msg("Failed to move threads to cgroup\n"); @@ -1045,7 +1051,7 @@ int timerlat_hist_main(int argc, char *argv[]) tool->start_time = time(NULL); timerlat_hist_set_signals(params); - if (params->user_hist) { + if (params->user_workload) { /* rtla asked to stop */ params_u.should_run = 1; /* all threads left */ @@ -1082,14 +1088,14 @@ int timerlat_hist_main(int argc, char *argv[]) break; /* is there still any user-threads ? */ - if (params->user_hist) { + if (params->user_workload) { if (params_u.stopped_running) { debug_msg("timerlat user-space threads stopped!\n"); break; } } } - if (params->user_hist && !params_u.stopped_running) { + if (params->user_workload && !params_u.stopped_running) { params_u.should_run = 0; sleep(1); } diff --git a/tools/tracing/rtla/src/timerlat_top.c b/tools/tracing/rtla/src/timerlat_top.c index 1640f121baca..6af47f21a1a3 100644 --- a/tools/tracing/rtla/src/timerlat_top.c +++ b/tools/tracing/rtla/src/timerlat_top.c @@ -43,6 +43,7 @@ struct timerlat_top_params { int cgroup; int hk_cpus; int user_top; + int user_workload; cpu_set_t hk_cpu_set; struct sched_attr sched_param; struct trace_events *events; @@ -364,6 +365,7 @@ static void timerlat_top_usage(char *usage) " 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", + " -U/--user-load: enable timerlat for user-defined user-space workload", NULL, }; @@ -419,6 +421,7 @@ static struct timerlat_top_params {"thread", required_argument, 0, 'T'}, {"trace", optional_argument, 0, 't'}, {"user-threads", no_argument, 0, 'u'}, + {"user-load", no_argument, 0, 'U'}, {"trigger", required_argument, 0, '0'}, {"filter", required_argument, 0, '1'}, {"dma-latency", required_argument, 0, '2'}, @@ -431,7 +434,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:u0:1:2:345:", + c = getopt_long(argc, argv, "a:c:C::d:De:hH:i:np:P:qs:t::T:uU0:1:2:345:", long_options, &option_index); /* detect the end of the options. */ @@ -548,6 +551,9 @@ static struct timerlat_top_params break; case 'u': + params->user_workload = true; + /* fallback: -u implies -U */ + case 'U': params->user_top = true; break; case '0': /* trigger */ @@ -865,7 +871,7 @@ int timerlat_top_main(int argc, char *argv[]) top->start_time = time(NULL); timerlat_top_set_signals(params); - if (params->user_top) { + if (params->user_workload) { /* rtla asked to stop */ params_u.should_run = 1; /* all threads left */ @@ -908,7 +914,7 @@ int timerlat_top_main(int argc, char *argv[]) break; /* is there still any user-threads ? */ - if (params->user_top) { + if (params->user_workload) { if (params_u.stopped_running) { debug_msg("timerlat user space threads stopped!\n"); break; @@ -916,7 +922,7 @@ int timerlat_top_main(int argc, char *argv[]) } } - if (params->user_top && !params_u.stopped_running) { + if (params->user_workload && !params_u.stopped_running) { params_u.should_run = 0; sleep(1); } -- 2.43.0