[PATCH 5/5] trace/osnoise: Allow multiple instances of the same tracer

From: Daniel Bristot de Oliveira
Date: Thu Aug 12 2021 - 10:44:48 EST


Allow more than one instance of the same tracer. The workload will
start when the first trace_array (instance) is registered and will
stop when the last instance is unregistered.

Osnoise and timerlat are still mutually exclusive because of the
different behavior of the osnoise: tracepoints.

Cc: Steven Rostedt <rostedt@xxxxxxxxxxx>
Cc: Ingo Molnar <mingo@xxxxxxxxxx>
Cc: Tom Zanussi <zanussi@xxxxxxxxxx>
Cc: Masami Hiramatsu <mhiramat@xxxxxxxxxx>
Cc: Daniel Bristot de Oliveira <bristot@xxxxxxxxxx>
Cc: linux-trace-devel@xxxxxxxxxxxxxxx
Cc: linux-kernel@xxxxxxxxxxxxxxx
Signed-off-by: Daniel Bristot de Oliveira (Red Hat) <bristot@xxxxxxxxxx>
---
kernel/trace/trace_osnoise.c | 106 ++++++++++++++++++++++++++---------
1 file changed, 80 insertions(+), 26 deletions(-)

diff --git a/kernel/trace/trace_osnoise.c b/kernel/trace/trace_osnoise.c
index 9193c256377e..5f09fb55b359 100644
--- a/kernel/trace/trace_osnoise.c
+++ b/kernel/trace/trace_osnoise.c
@@ -64,6 +64,24 @@ static bool osnoise_has_registered_instances(void)
list);
}

+/*
+ * osnoise_instance_registered - check if a tr is already registered
+ */
+static int osnoise_instance_registered(struct trace_array *tr)
+{
+ struct osnoise_instance *inst;
+ int found = 0;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(inst, &osnoise_instances, list) {
+ if (inst->tr == tr)
+ found = 1;
+ }
+ rcu_read_unlock();
+
+ return found;
+}
+
/*
* osnoise_register_instance - register a new trace instance
*
@@ -106,10 +124,8 @@ static void osnoise_unregister_instance(struct trace_array *tr)
}
rcu_read_unlock();

- if (!found) {
- WARN("osnoise unregister did not find tr %s\n", tr->name);
+ if (!found)
return;
- }

synchronize_rcu();
kfree(inst);
@@ -1190,6 +1206,7 @@ static __always_inline void osnoise_stop_tracing(void)
"stop tracing hit on cpu %d\n", smp_processor_id());

tracer_tracing_off(tr);
+
}
rcu_read_unlock();
}
@@ -2041,6 +2058,16 @@ static int osnoise_workload_start(void)
{
int retval;

+ /*
+ * Instances need to be registered after calling workload
+ * start. Hence, if there is already an instance, the
+ * workload was already registered. Otherwise, this
+ * code is on the way to register the first instance,
+ * and the workload will start.
+ */
+ if (osnoise_has_registered_instances())
+ return 0;
+
osn_var_reset_all();

retval = osnoise_hook_events();
@@ -2066,6 +2093,13 @@ static int osnoise_workload_start(void)
*/
static void osnoise_workload_stop(void)
{
+ /*
+ * Instances need to be unregistered before calling
+ * stop. Hence, if there is a registered instance, more
+ * than one instance is running, and the workload will not
+ * yet stop. Otherwise, this code is on the way to disable
+ * the last instance, and the workload can stop.
+ */
if (osnoise_has_registered_instances())
return;

@@ -2083,7 +2117,11 @@ static void osnoise_tracer_start(struct trace_array *tr)
{
int retval;

- if (osnoise_has_registered_instances())
+ /*
+ * If the instance is already registered, there is no need to
+ * register it again.
+ */
+ if (osnoise_instance_registered(tr))
return;

retval = osnoise_workload_start();
@@ -2095,18 +2133,17 @@ static void osnoise_tracer_start(struct trace_array *tr)

static void osnoise_tracer_stop(struct trace_array *tr)
{
- if (!osnoise_has_registered_instances())
- return;
-
osnoise_unregister_instance(tr);
osnoise_workload_stop();
}

static int osnoise_tracer_init(struct trace_array *tr)
{
-
- /* Only allow one instance to enable this */
- if (osnoise_has_registered_instances())
+ /*
+ * Only allow osnoise tracer if timerlat tracer is not running
+ * already.
+ */
+ if (osnoise_data.timerlat_tracer)
return -EBUSY;

tr->max_latency = 0;
@@ -2135,45 +2172,55 @@ static void timerlat_tracer_start(struct trace_array *tr)
{
int retval;

- if (osnoise_has_registered_instances())
+ /*
+ * If the instance is already registered, there is no need to
+ * register it again.
+ */
+ if (osnoise_instance_registered(tr))
return;

- osnoise_data.timerlat_tracer = 1;
-
retval = osnoise_workload_start();
if (retval)
- goto out_err;
+ pr_err(BANNER "Error starting timerlat tracer\n");

osnoise_register_instance(tr);

return;
-out_err:
- pr_err(BANNER "Error starting timerlat tracer\n");
}

static void timerlat_tracer_stop(struct trace_array *tr)
{
int cpu;

- if (!osnoise_has_registered_instances())
- return;
-
- for_each_online_cpu(cpu)
- per_cpu(per_cpu_osnoise_var, cpu).sampling = 0;
+ osnoise_unregister_instance(tr);

- osnoise_tracer_stop(tr);
+ /*
+ * Instruct the threads to stop only if this is the last instance.
+ */
+ if (!osnoise_has_registered_instances()) {
+ for_each_online_cpu(cpu)
+ per_cpu(per_cpu_osnoise_var, cpu).sampling = 0;
+ }

- osnoise_data.timerlat_tracer = 0;
+ osnoise_workload_stop();
}

static int timerlat_tracer_init(struct trace_array *tr)
{
- /* Only allow one instance to enable this */
- if (osnoise_has_registered_instances())
+ /*
+ * Only allow timerlat tracer if osnoise tracer is not running already.
+ */
+ if (osnoise_has_registered_instances() && !osnoise_data.timerlat_tracer)
return -EBUSY;

- tr->max_latency = 0;
+ /*
+ * If this is the first instance, set timerlat_tracer to block
+ * osnoise tracer start.
+ */
+ if (!osnoise_has_registered_instances())
+ osnoise_data.timerlat_tracer = 1;

+ tr->max_latency = 0;
timerlat_tracer_start(tr);

return 0;
@@ -2182,6 +2229,13 @@ static int timerlat_tracer_init(struct trace_array *tr)
static void timerlat_tracer_reset(struct trace_array *tr)
{
timerlat_tracer_stop(tr);
+
+ /*
+ * If this is the last instance, reset timerlat_tracer allowing
+ * osnoise to be started.
+ */
+ if (!osnoise_has_registered_instances())
+ osnoise_data.timerlat_tracer = 0;
}

static struct tracer timerlat_tracer __read_mostly = {
--
2.31.1