2024-06-12 14:56:06

by Tomas Glozar

[permalink] [raw]
Subject: [PATCH 0/5] rtla: Support idle state disabling via libcpupower in timerlat

From: Tomas Glozar <[email protected]>

rtla-timerlat allows reducing latency on wake up from idle by setting
/dev/cpu_dma_latency during the timerlat measurement. This has an effect on
the idle states of all CPUs, including those which are not used by timerlat.

Add option --disable-idle-states that disables all idle states only on
the CPUs where timerlat measurements are running.

libcpupower is used to do the disabling of idle states via the corresponding
sysfs interface.

Tomas Glozar (5):
rtla: Add dependency on libcpupower
rtla/utils: Add idle state disabling via libcpupower
rtla/timerlat: Add --disable-idle-states for top
rtla/timerlat: Add --disable-idle-states for hist
rtla: Documentation: Add --disable-idle-states

.../tools/rtla/common_timerlat_options.rst | 6 +
tools/build/Makefile.feature | 1 +
tools/build/feature/Makefile | 4 +
tools/build/feature/test-libcpupower.c | 8 ++
tools/tracing/rtla/Makefile | 2 +
tools/tracing/rtla/Makefile.config | 9 ++
tools/tracing/rtla/README.txt | 4 +
tools/tracing/rtla/src/timerlat_hist.c | 35 ++++-
tools/tracing/rtla/src/timerlat_top.c | 35 ++++-
tools/tracing/rtla/src/utils.c | 133 ++++++++++++++++++
tools/tracing/rtla/src/utils.h | 4 +
11 files changed, 239 insertions(+), 2 deletions(-)
create mode 100644 tools/build/feature/test-libcpupower.c

--
2.43.0



2024-06-12 14:56:30

by Tomas Glozar

[permalink] [raw]
Subject: [PATCH 2/5] rtla/utils: Add idle state disabling via libcpupower

From: Tomas Glozar <[email protected]>

Add functions to utils.c to disable idle states through functions of
libcpupower. This will serve as the basis for disabling idle states
per cpu when running timerlat.

Signed-off-by: Tomas Glozar <[email protected]>
---
tools/tracing/rtla/src/utils.c | 133 +++++++++++++++++++++++++++++++++
tools/tracing/rtla/src/utils.h | 4 +
2 files changed, 137 insertions(+)

diff --git a/tools/tracing/rtla/src/utils.c b/tools/tracing/rtla/src/utils.c
index 9ac71a66840c..638c50f0f88e 100644
--- a/tools/tracing/rtla/src/utils.c
+++ b/tools/tracing/rtla/src/utils.c
@@ -4,6 +4,7 @@
*/

#define _GNU_SOURCE
+#include <cpuidle.h>
#include <dirent.h>
#include <stdarg.h>
#include <stdlib.h>
@@ -519,6 +520,138 @@ int set_cpu_dma_latency(int32_t latency)
return fd;
}

+static unsigned int **saved_cpu_idle_disable_state;
+static size_t saved_cpu_idle_disable_state_alloc_ctr;
+
+/*
+ * save_cpu_idle_state_disable - save disable for all idle states of a cpu
+ *
+ * Saves the current disable of all idle states of a cpu, to be subsequently
+ * restored via restore_cpu_idle_disable_state.
+ *
+ * Return: idle state count on success, negative on error
+ */
+int save_cpu_idle_disable_state(unsigned int cpu)
+{
+ unsigned int nr_states;
+ unsigned int state;
+ int disabled;
+ int nr_cpus;
+
+ nr_states = cpuidle_state_count(cpu);
+
+ if (nr_states == 0)
+ return 0;
+
+ if (saved_cpu_idle_disable_state == NULL) {
+ nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
+ saved_cpu_idle_disable_state = calloc(nr_cpus, sizeof(unsigned int *));
+ }
+
+ saved_cpu_idle_disable_state[cpu] = calloc(nr_states, sizeof(unsigned int));
+ saved_cpu_idle_disable_state_alloc_ctr++;
+
+ for (state = 0; state < nr_states; state++) {
+ disabled = cpuidle_is_state_disabled(cpu, state);
+ if (disabled < 0)
+ return disabled;
+ saved_cpu_idle_disable_state[cpu][state] = disabled;
+ }
+
+ return nr_states;
+}
+
+/*
+ * restore_cpu_idle_disable_state - restore disable for all idle states of a cpu
+ *
+ * Restores the current disable state of all idle states of a cpu that was
+ * previously saved by save_cpu_idle_disable_state.
+ *
+ * Return: idle state count on success, negative on error
+ */
+int restore_cpu_idle_disable_state(unsigned int cpu)
+{
+ unsigned int nr_states;
+ unsigned int state;
+ int disabled;
+ int result;
+
+ nr_states = cpuidle_state_count(cpu);
+
+ if (nr_states == 0)
+ return 0;
+
+ for (state = 0; state < nr_states; state++) {
+ disabled = saved_cpu_idle_disable_state[cpu][state];
+ result = cpuidle_state_disable(cpu, state, disabled);
+ if (result < 0)
+ return result;
+ }
+
+ free(saved_cpu_idle_disable_state[cpu]);
+ saved_cpu_idle_disable_state[cpu] = NULL;
+ saved_cpu_idle_disable_state_alloc_ctr--;
+ if (saved_cpu_idle_disable_state_alloc_ctr == 0) {
+ free(saved_cpu_idle_disable_state);
+ saved_cpu_idle_disable_state = NULL;
+ }
+
+ return nr_states;
+}
+
+/*
+ * free_cpu_idle_disable_states - free saved idle state disable for all cpus
+ *
+ * Frees the memory used for storing cpu idle state disable for all cpus
+ * and states.
+ *
+ * Normally, the memory is freed automatically in
+ * restore_cpu_idle_disable_state; this is mostly for cleaning up after an
+ * error.
+ */
+void free_cpu_idle_disable_states(void)
+{
+ int cpu;
+
+ if (!saved_cpu_idle_disable_state)
+ return;
+
+ for (cpu = 0; cpu < sysconf(_SC_NPROCESSORS_CONF); cpu++) {
+ if (!saved_cpu_idle_disable_state[cpu])
+ continue;
+ free(saved_cpu_idle_disable_state[cpu]);
+ saved_cpu_idle_disable_state[cpu] = NULL;
+ }
+
+ free(saved_cpu_idle_disable_state);
+ saved_cpu_idle_disable_state = NULL;
+}
+
+/*
+ * set_cpu_idle_disable_state - set disable for all idle states of a cpu
+ *
+ * This is used to reduce the exit from idle latency. Unlike
+ * set_cpu_dma_latency, it can disable idle states per cpu.
+ *
+ * Return: idle state count on success, negative on error
+ */
+int set_cpu_idle_disable_state(unsigned int cpu, unsigned int disable)
+{
+ unsigned int nr_states;
+ unsigned int state;
+ int result;
+
+ nr_states = cpuidle_state_count(cpu);
+
+ for (state = 0; state < nr_states; state++) {
+ result = cpuidle_state_disable(cpu, state, disable);
+ if (result < 0)
+ return result;
+ }
+
+ return nr_states;
+}
+
#define _STR(x) #x
#define STR(x) _STR(x)

diff --git a/tools/tracing/rtla/src/utils.h b/tools/tracing/rtla/src/utils.h
index d44513e6c66a..b310ed5cd867 100644
--- a/tools/tracing/rtla/src/utils.h
+++ b/tools/tracing/rtla/src/utils.h
@@ -64,6 +64,10 @@ 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 save_cpu_idle_disable_state(unsigned int cpu);
+int restore_cpu_idle_disable_state(unsigned int cpu);
+void free_cpu_idle_disable_states(void);
+int set_cpu_idle_disable_state(unsigned int cpu, unsigned int disable);
int auto_house_keeping(cpu_set_t *monitored_cpus);

#define ns_to_usf(x) (((double)x/1000))
--
2.43.0


2024-06-12 14:56:58

by Tomas Glozar

[permalink] [raw]
Subject: [PATCH 3/5] rtla/timerlat: Add --disable-idle-states for top

From: Tomas Glozar <[email protected]>

Add option to disable idle states on CPUs where timerlat is running for
the duration of the workload.

Signed-off-by: Tomas Glozar <[email protected]>
---
tools/tracing/rtla/src/timerlat_top.c | 35 ++++++++++++++++++++++++++-
1 file changed, 34 insertions(+), 1 deletion(-)

diff --git a/tools/tracing/rtla/src/timerlat_top.c b/tools/tracing/rtla/src/timerlat_top.c
index 8c16419fe22a..dd1e5b03d781 100644
--- a/tools/tracing/rtla/src/timerlat_top.c
+++ b/tools/tracing/rtla/src/timerlat_top.c
@@ -48,6 +48,7 @@ struct timerlat_top_params {
int pretty_output;
int warmup;
int buffer_size;
+ int disable_idle_states;
cpu_set_t hk_cpu_set;
struct sched_attr sched_param;
struct trace_events *events;
@@ -447,7 +448,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]] [-u|-k] [--warm-up s]",
+ " [-P priority] [--dma-latency us] [--aa-only us] [-C[=cgroup_name]] [-u|-k] [--warm-up s] [--disable-idle-states]",
"",
" -h/--help: print this menu",
" -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit",
@@ -481,6 +482,7 @@ static void timerlat_top_usage(char *usage)
" -U/--user-load: enable timerlat for user-defined user-space workload",
" --warm-up s: let the workload run for s seconds before collecting data",
" --trace-buffer-size kB: set the per-cpu trace buffer size in kB",
+ " --disable-idle-states: disable all idle states for cpus used by timerlat to reduce exit from idle latency",
NULL,
};

@@ -518,6 +520,9 @@ static struct timerlat_top_params
/* disabled by default */
params->dma_latency = -1;

+ /* disabled by default */
+ params->disable_idle_states = 0;
+
/* display data in microseconds */
params->output_divisor = 1000;

@@ -550,6 +555,7 @@ static struct timerlat_top_params
{"aa-only", required_argument, 0, '5'},
{"warm-up", required_argument, 0, '6'},
{"trace-buffer-size", required_argument, 0, '7'},
+ {"disable-idle-states", no_argument, 0, '8'},
{0, 0, 0, 0}
};

@@ -726,6 +732,9 @@ static struct timerlat_top_params
case '7':
params->buffer_size = get_llong_from_str(optarg);
break;
+ case '8':
+ params->disable_idle_states = 1;
+ break;
default:
timerlat_top_usage("Invalid option");
}
@@ -922,6 +931,7 @@ int timerlat_top_main(int argc, char *argv[])
int return_value = 1;
char *max_lat;
int retval;
+ int i;

params = timerlat_top_parse_args(argc, argv);
if (!params)
@@ -971,6 +981,21 @@ int timerlat_top_main(int argc, char *argv[])
}
}

+ if (params->disable_idle_states) {
+ for (i = 0; i < sysconf(_SC_NPROCESSORS_CONF); i++) {
+ if (params->cpus && !CPU_ISSET(i, &params->monitored_cpus))
+ continue;
+ if (save_cpu_idle_disable_state(i) < 0) {
+ err_msg("Could not save cpu idle state.\n");
+ goto out_free;
+ }
+ if (set_cpu_idle_disable_state(i, 1) < 0) {
+ err_msg("Could not disable cpu idle state.\n");
+ goto out_free;
+ }
+ }
+ }
+
if (params->trace_output) {
record = osnoise_init_trace_tool("timerlat");
if (!record) {
@@ -1125,6 +1150,13 @@ int timerlat_top_main(int argc, char *argv[])
timerlat_aa_destroy();
if (dma_latency_fd >= 0)
close(dma_latency_fd);
+ if (params->disable_idle_states) {
+ for (i = 0; i < sysconf(_SC_NPROCESSORS_CONF); i++) {
+ if (params->cpus && !CPU_ISSET(i, &params->monitored_cpus))
+ continue;
+ restore_cpu_idle_disable_state(i);
+ }
+ }
trace_events_destroy(&record->trace, params->events);
params->events = NULL;
out_free:
@@ -1134,6 +1166,7 @@ int timerlat_top_main(int argc, char *argv[])
osnoise_destroy_tool(record);
osnoise_destroy_tool(top);
free(params);
+ free_cpu_idle_disable_states();
out_exit:
exit(return_value);
}
--
2.43.0


2024-06-12 15:00:07

by Tomas Glozar

[permalink] [raw]
Subject: [PATCH 1/5] rtla: Add dependency on libcpupower

From: Tomas Glozar <[email protected]>

Add a test for libcpupower into feature tests and use it to add a
dependency on libcpupower to rtla.

Signed-off-by: Tomas Glozar <[email protected]>
---
tools/build/Makefile.feature | 1 +
tools/build/feature/Makefile | 4 ++++
tools/build/feature/test-libcpupower.c | 8 ++++++++
tools/tracing/rtla/Makefile | 2 ++
tools/tracing/rtla/Makefile.config | 9 +++++++++
5 files changed, 24 insertions(+)
create mode 100644 tools/build/feature/test-libcpupower.c

diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature
index 1e2ab148d5db..e4fb0a1fbddf 100644
--- a/tools/build/Makefile.feature
+++ b/tools/build/Makefile.feature
@@ -53,6 +53,7 @@ FEATURE_TESTS_BASIC := \
libslang-include-subdir \
libtraceevent \
libtracefs \
+ libcpupower \
libcrypto \
libunwind \
pthread-attr-setaffinity-np \
diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile
index ed54cef450f5..c93d62afc1e8 100644
--- a/tools/build/feature/Makefile
+++ b/tools/build/feature/Makefile
@@ -38,6 +38,7 @@ FILES= \
test-libslang.bin \
test-libslang-include-subdir.bin \
test-libtraceevent.bin \
+ test-libcpupower.bin \
test-libtracefs.bin \
test-libcrypto.bin \
test-libunwind.bin \
@@ -212,6 +213,9 @@ $(OUTPUT)test-libslang-include-subdir.bin:
$(OUTPUT)test-libtraceevent.bin:
$(BUILD) -ltraceevent

+$(OUTPUT)test-libcpupower.bin:
+ $(BUILD) -lcpupower
+
$(OUTPUT)test-libtracefs.bin:
$(BUILD) $(shell $(PKG_CONFIG) --cflags libtraceevent 2>/dev/null) -ltracefs

diff --git a/tools/build/feature/test-libcpupower.c b/tools/build/feature/test-libcpupower.c
new file mode 100644
index 000000000000..a346aa332a71
--- /dev/null
+++ b/tools/build/feature/test-libcpupower.c
@@ -0,0 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <cpuidle.h>
+
+int main(void)
+{
+ int rv = cpuidle_state_count(0);
+ return rv;
+}
diff --git a/tools/tracing/rtla/Makefile b/tools/tracing/rtla/Makefile
index b5878be36125..a6a7dee16622 100644
--- a/tools/tracing/rtla/Makefile
+++ b/tools/tracing/rtla/Makefile
@@ -32,8 +32,10 @@ DOCSRC := ../../../Documentation/tools/rtla/

FEATURE_TESTS := libtraceevent
FEATURE_TESTS += libtracefs
+FEATURE_TESTS += libcpupower
FEATURE_DISPLAY := libtraceevent
FEATURE_DISPLAY += libtracefs
+FEATURE_DISPLAY += libcpupower

ifeq ($(V),1)
Q =
diff --git a/tools/tracing/rtla/Makefile.config b/tools/tracing/rtla/Makefile.config
index 0b7ecfb30d19..8b6bc91e5dff 100644
--- a/tools/tracing/rtla/Makefile.config
+++ b/tools/tracing/rtla/Makefile.config
@@ -42,6 +42,15 @@ else
$(info libtracefs is missing. Please install libtracefs-dev/libtracefs-devel)
endif

+$(call feature_check,libcpupower)
+ifeq ($(feature-libcpupower), 1)
+ $(call detected,CONFIG_LIBCPUPOWER)
+ $(call lib_setup,cpupower)
+else
+ STOP_ERROR := 1
+ $(info libcpupower is missing. Please install libcpupower-dev/kernel-tools-libs-devel)
+endif
+
ifeq ($(STOP_ERROR),1)
$(error Please, check the errors above.)
endif
--
2.43.0


2024-06-12 15:03:22

by Tomas Glozar

[permalink] [raw]
Subject: [PATCH 4/5] rtla/timerlat: Add --disable-idle-states for hist

From: Tomas Glozar <[email protected]>

Support disabling idle states also for timerlat-hist.

Signed-off-by: Tomas Glozar <[email protected]>
---
tools/tracing/rtla/src/timerlat_hist.c | 35 +++++++++++++++++++++++++-
1 file changed, 34 insertions(+), 1 deletion(-)

diff --git a/tools/tracing/rtla/src/timerlat_hist.c b/tools/tracing/rtla/src/timerlat_hist.c
index a3907c390d67..69d427841d14 100644
--- a/tools/tracing/rtla/src/timerlat_hist.c
+++ b/tools/tracing/rtla/src/timerlat_hist.c
@@ -55,6 +55,7 @@ struct timerlat_hist_params {
int entries;
int warmup;
int buffer_size;
+ int disable_idle_states;
};

struct timerlat_hist_cpu {
@@ -655,7 +656,7 @@ static void timerlat_hist_usage(char *usage)
" [-t[file]] [-e sys[:event]] [--filter <filter>] [--trigger <trigger>] [-c cpu-list] [-H cpu-list]\\",
" [-P priority] [-E N] [-b N] [--no-irq] [--no-thread] [--no-header] [--no-summary] \\",
" [--no-index] [--with-zeros] [--dma-latency us] [-C[=cgroup_name]] [--no-aa] [--dump-task] [-u|-k]",
- " [--warm-up s]",
+ " [--warm-up s] [--disable-idle-states]",
"",
" -h/--help: print this menu",
" -a/--auto: set automatic trace mode, stopping the session if argument in us latency is hit",
@@ -695,6 +696,7 @@ static void timerlat_hist_usage(char *usage)
" -U/--user-load: enable timerlat for user-defined user-space workload",
" --warm-up s: let the workload run for s seconds before collecting data",
" --trace-buffer-size kB: set the per-cpu trace buffer size in kB",
+ " --disable-idle-states: disable all idle states for cpus used by timerlat to reduce exit from idle latency",
NULL,
};

@@ -732,6 +734,9 @@ static struct timerlat_hist_params
/* disabled by default */
params->dma_latency = -1;

+ /* disabled by default */
+ params->disable_idle_states = 0;
+
/* display data in microseconds */
params->output_divisor = 1000;
params->bucket_size = 1;
@@ -772,6 +777,7 @@ static struct timerlat_hist_params
{"dump-task", no_argument, 0, '\1'},
{"warm-up", required_argument, 0, '\2'},
{"trace-buffer-size", required_argument, 0, '\3'},
+ {"disable-idle-states", no_argument, 0, '\4'},
{0, 0, 0, 0}
};

@@ -960,6 +966,9 @@ static struct timerlat_hist_params
case '\3':
params->buffer_size = get_llong_from_str(optarg);
break;
+ case '\4':
+ params->disable_idle_states = 1;
+ break;
default:
timerlat_hist_usage("Invalid option");
}
@@ -1152,6 +1161,7 @@ int timerlat_hist_main(int argc, char *argv[])
int return_value = 1;
pthread_t timerlat_u;
int retval;
+ int i;

params = timerlat_hist_parse_args(argc, argv);
if (!params)
@@ -1201,6 +1211,21 @@ int timerlat_hist_main(int argc, char *argv[])
}
}

+ if (params->disable_idle_states) {
+ for (i = 0; i < sysconf(_SC_NPROCESSORS_CONF); i++) {
+ if (params->cpus && !CPU_ISSET(i, &params->monitored_cpus))
+ continue;
+ if (save_cpu_idle_disable_state(i) < 0) {
+ err_msg("Could not save cpu idle state.\n");
+ goto out_free;
+ }
+ if (set_cpu_idle_disable_state(i, 1) < 0) {
+ err_msg("Could not disable cpu idle state.\n");
+ goto out_free;
+ }
+ }
+ }
+
if (params->trace_output) {
record = osnoise_init_trace_tool("timerlat");
if (!record) {
@@ -1332,6 +1357,13 @@ int timerlat_hist_main(int argc, char *argv[])
timerlat_aa_destroy();
if (dma_latency_fd >= 0)
close(dma_latency_fd);
+ if (params->disable_idle_states) {
+ for (i = 0; i < sysconf(_SC_NPROCESSORS_CONF); i++) {
+ if (params->cpus && !CPU_ISSET(i, &params->monitored_cpus))
+ continue;
+ restore_cpu_idle_disable_state(i);
+ }
+ }
trace_events_destroy(&record->trace, params->events);
params->events = NULL;
out_free:
@@ -1340,6 +1372,7 @@ int timerlat_hist_main(int argc, char *argv[])
osnoise_destroy_tool(record);
osnoise_destroy_tool(tool);
free(params);
+ free_cpu_idle_disable_states();
out_exit:
exit(return_value);
}
--
2.43.0


2024-06-12 15:04:03

by Tomas Glozar

[permalink] [raw]
Subject: [PATCH 5/5] rtla: Documentation: Add --disable-idle-states

From: Tomas Glozar <[email protected]>

Add --disable-idle-states to manpage and mention libcpupower dependency
in README.txt.

Signed-off-by: Tomas Glozar <[email protected]>
---
Documentation/tools/rtla/common_timerlat_options.rst | 6 ++++++
tools/tracing/rtla/README.txt | 4 ++++
2 files changed, 10 insertions(+)

diff --git a/Documentation/tools/rtla/common_timerlat_options.rst b/Documentation/tools/rtla/common_timerlat_options.rst
index cef6651f1435..7429e77f95ca 100644
--- a/Documentation/tools/rtla/common_timerlat_options.rst
+++ b/Documentation/tools/rtla/common_timerlat_options.rst
@@ -31,6 +31,12 @@
*cyclictest* sets this value to *0* by default, use **--dma-latency** *0* to have
similar results.

+**--disable-idle-states**
+ Set the /sys/devices/system/cpu/cpu<n>/cpuidle/state*/disable files to 1 for cpus
+ that are running timerlat threads to avoid exit from idle latencies. On exit from
+ timerlat, the state*/disable setting is restored to its original value before
+ running timerlat.
+
**-k**, **--kernel-threads**

Use timerlat kernel-space threads, in contrast of **-u**.
diff --git a/tools/tracing/rtla/README.txt b/tools/tracing/rtla/README.txt
index 4af3fd40f171..6617b9911c81 100644
--- a/tools/tracing/rtla/README.txt
+++ b/tools/tracing/rtla/README.txt
@@ -11,6 +11,7 @@ RTLA depends on the following libraries and tools:

- libtracefs
- libtraceevent
+ - libcpupower

It also depends on python3-docutils to compile man pages.

@@ -26,6 +27,9 @@ For development, we suggest the following steps for compiling rtla:
$ make
$ sudo make install
$ cd ..
+ $ cd $libcpupower_src
+ $ make
+ $ sudo make install
$ cd $rtla_src
$ make
$ sudo make install
--
2.43.0