* [RFC PATCH 1/4] ftrace: Generalize function task filter names
2026-08-30 11:07 [RFC PATCH 0/4] ftrace: Add task comm filtering for function tracing hu.shengming
@ 2026-08-30 11:09 ` hu.shengming
2026-08-30 11:10 ` [RFC PATCH 2/4] ftrace: Centralize task filter state updates hu.shengming
` (4 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: hu.shengming @ 2026-08-30 11:09 UTC (permalink / raw)
To: hu.shengming
Cc: rostedt, mhiramat, mathieu.desnoyers, mark.rutland, corbet, skhan,
rdunlap, linux-kernel, linux-trace-kernel, linux-doc, ran.xiaokai,
xu.xin16, zhang.run
From: Shengming Hu <hu.shengming@zte.com.cn>
Function tracing decides whether to trace a task when it is scheduled
in and caches the result per CPU. This mechanism currently handles PID
filters, but it will also be used by other task filters.
Rename the shared enable check and sched_switch callback to use more
general task-filter names. Rename trace_ignore_this_task() to
trace_ignore_pid_task(), since that helper specifically checks PID
filters.
Signed-off-by: Shengming Hu <hu.shengming@zte.com.cn>
---
include/linux/ftrace.h | 4 ++--
kernel/trace/fgraph.c | 4 ++--
kernel/trace/ftrace.c | 16 ++++++++--------
kernel/trace/trace.h | 2 +-
kernel/trace/trace_events.c | 16 ++++++++--------
kernel/trace/trace_functions.c | 2 +-
kernel/trace/trace_pid.c | 8 ++++----
7 files changed, 26 insertions(+), 26 deletions(-)
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 02bc5027523a..60e1dede1e68 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -331,7 +331,7 @@ ftrace_func_t ftrace_ops_get_func(struct ftrace_ops *ops);
* SAVE_REGS. If another ops with this flag set is already registered
* for any of the functions that this ops will be registered for, then
* this ops will fail to register or set_filter_ip.
- * PID - Is affected by set_ftrace_pid (allows filtering on those pids)
+ * PID - Is affected by function task filters
* RCU - Set when the ops can only be called when RCU is watching.
* TRACE_ARRAY - The ops->private points to a trace_array descriptor.
* PERMANENT - Set when the ops is permanent and should not be affected by
@@ -1251,7 +1251,7 @@ typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *,
extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace,
struct fgraph_ops *gops,
struct ftrace_regs *fregs);
-bool ftrace_pids_enabled(struct ftrace_ops *ops);
+bool ftrace_task_filters_enabled(struct ftrace_ops *ops);
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
diff --git a/kernel/trace/fgraph.c b/kernel/trace/fgraph.c
index 40d373d65f9b..c8bbba4eecf0 100644
--- a/kernel/trace/fgraph.c
+++ b/kernel/trace/fgraph.c
@@ -1223,7 +1223,7 @@ void fgraph_update_pid_func(void)
list_for_each_entry(op, &graph_ops.subop_list, list) {
if (op->flags & FTRACE_OPS_FL_PID) {
gops = container_of(op, struct fgraph_ops, ops);
- gops->entryfunc = ftrace_pids_enabled(op) ?
+ gops->entryfunc = ftrace_task_filters_enabled(op) ?
fgraph_pid_func : gops->saved_func;
if (ftrace_graph_active == 1)
static_call_update(fgraph_func, gops->entryfunc);
@@ -1382,7 +1382,7 @@ int register_ftrace_graph(struct fgraph_ops *gops)
/* Always save the function, and reset at unregistering */
gops->saved_func = gops->entryfunc;
#ifdef CONFIG_DYNAMIC_FTRACE
- if (ftrace_pids_enabled(&gops->ops))
+ if (ftrace_task_filters_enabled(&gops->ops))
gops->entryfunc = fgraph_pid_func;
#endif
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index f9d80c7bd9f1..479ea004adc3 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -99,7 +99,7 @@ struct ftrace_ops *function_trace_op __read_mostly = &ftrace_list_end;
/* What to set function_trace_op to */
static struct ftrace_ops *set_function_trace_op;
-bool ftrace_pids_enabled(struct ftrace_ops *ops)
+bool ftrace_task_filters_enabled(struct ftrace_ops *ops)
{
struct trace_array *tr;
@@ -359,7 +359,7 @@ int __register_ftrace_function(struct ftrace_ops *ops)
/* Always save the function, and reset at unregistering */
ops->saved_func = ops->func;
- if (ftrace_pids_enabled(ops))
+ if (ftrace_task_filters_enabled(ops))
ops->func = ftrace_pid_func;
ftrace_update_trampoline(ops);
@@ -400,7 +400,7 @@ static void ftrace_update_pid_func(void)
do_for_each_ftrace_op(op, ftrace_ops_list) {
if (op->flags & FTRACE_OPS_FL_PID) {
- op->func = ftrace_pids_enabled(op) ?
+ op->func = ftrace_task_filters_enabled(op) ?
ftrace_pid_func : op->saved_func;
ftrace_update_trampoline(op);
}
@@ -8638,7 +8638,7 @@ ftrace_func_t ftrace_ops_get_func(struct ftrace_ops *ops)
}
static void
-ftrace_filter_pid_sched_switch_probe(void *data, bool preempt,
+ftrace_filter_task_sched_switch_probe(void *data, bool preempt,
struct task_struct *prev,
struct task_struct *next,
unsigned int prev_state)
@@ -8650,7 +8650,7 @@ ftrace_filter_pid_sched_switch_probe(void *data, bool preempt,
pid_list = rcu_dereference_sched(tr->function_pids);
no_pid_list = rcu_dereference_sched(tr->function_no_pids);
- if (trace_ignore_this_task(pid_list, no_pid_list, next))
+ if (trace_ignore_pid_task(pid_list, no_pid_list, next))
this_cpu_write(tr->array_buffer.data->ftrace_ignore_pid,
FTRACE_PID_IGNORE);
else
@@ -8720,7 +8720,7 @@ static void clear_ftrace_pids(struct trace_array *tr, int type)
/* See if the pids still need to be checked after this */
if (!still_need_pid_events(type, pid_list, no_pid_list)) {
- unregister_trace_sched_switch(ftrace_filter_pid_sched_switch_probe, tr);
+ unregister_trace_sched_switch(ftrace_filter_task_sched_switch_probe, tr);
for_each_possible_cpu(cpu)
per_cpu_ptr(tr->array_buffer.data, cpu)->ftrace_ignore_pid = FTRACE_PID_TRACE;
}
@@ -8920,7 +8920,7 @@ static void ignore_task_cpu(void *data)
no_pid_list = rcu_dereference_protected(tr->function_no_pids,
mutex_is_locked(&ftrace_lock));
- if (trace_ignore_this_task(pid_list, no_pid_list, current))
+ if (trace_ignore_pid_task(pid_list, no_pid_list, current))
this_cpu_write(tr->array_buffer.data->ftrace_ignore_pid,
FTRACE_PID_IGNORE);
else
@@ -8981,7 +8981,7 @@ pid_write(struct file *filp, const char __user *ubuf,
trace_pid_list_free(filtered_pids);
} else if (pid_list && !other_pids) {
/* Register a probe to set whether to ignore the tracing of a task */
- register_trace_sched_switch(ftrace_filter_pid_sched_switch_probe, tr);
+ register_trace_sched_switch(ftrace_filter_task_sched_switch_probe, tr);
}
/*
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 74a7a50d1e78..f73913eed307 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -845,7 +845,7 @@ extern struct workqueue_struct *trace_init_wq __initdata;
bool trace_find_filtered_pid(struct trace_pid_list *filtered_pids,
pid_t search_pid);
-bool trace_ignore_this_task(struct trace_pid_list *filtered_pids,
+bool trace_ignore_pid_task(struct trace_pid_list *filtered_pids,
struct trace_pid_list *filtered_no_pids,
struct task_struct *task);
void trace_filter_add_remove_task(struct trace_pid_list *pid_list,
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 9f8f2d02276c..3fb0d2af5740 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -1123,12 +1123,12 @@ event_filter_pid_sched_switch_probe_pre(void *data, bool preempt,
* Sched switch is funny, as we only want to ignore it
* in the notrace case if both prev and next should be ignored.
*/
- ret = trace_ignore_this_task(NULL, no_pid_list, prev) &&
- trace_ignore_this_task(NULL, no_pid_list, next);
+ ret = trace_ignore_pid_task(NULL, no_pid_list, prev) &&
+ trace_ignore_pid_task(NULL, no_pid_list, next);
this_cpu_write(tr->array_buffer.data->ignore_pid, ret ||
- (trace_ignore_this_task(pid_list, NULL, prev) &&
- trace_ignore_this_task(pid_list, NULL, next)));
+ (trace_ignore_pid_task(pid_list, NULL, prev) &&
+ trace_ignore_pid_task(pid_list, NULL, next)));
}
static void
@@ -1145,7 +1145,7 @@ event_filter_pid_sched_switch_probe_post(void *data, bool preempt,
no_pid_list = rcu_dereference_sched(tr->filtered_no_pids);
this_cpu_write(tr->array_buffer.data->ignore_pid,
- trace_ignore_this_task(pid_list, no_pid_list, next));
+ trace_ignore_pid_task(pid_list, no_pid_list, next));
}
static void
@@ -1163,7 +1163,7 @@ event_filter_pid_sched_wakeup_probe_pre(void *data, struct task_struct *task)
no_pid_list = rcu_dereference_sched(tr->filtered_no_pids);
this_cpu_write(tr->array_buffer.data->ignore_pid,
- trace_ignore_this_task(pid_list, no_pid_list, task));
+ trace_ignore_pid_task(pid_list, no_pid_list, task));
}
static void
@@ -1182,7 +1182,7 @@ event_filter_pid_sched_wakeup_probe_post(void *data, struct task_struct *task)
/* Set tracing if current is enabled */
this_cpu_write(tr->array_buffer.data->ignore_pid,
- trace_ignore_this_task(pid_list, no_pid_list, current));
+ trace_ignore_pid_task(pid_list, no_pid_list, current));
}
static void unregister_pid_events(struct trace_array *tr)
@@ -2556,7 +2556,7 @@ static void ignore_task_cpu(void *data)
mutex_is_locked(&event_mutex));
this_cpu_write(tr->array_buffer.data->ignore_pid,
- trace_ignore_this_task(pid_list, no_pid_list, current));
+ trace_ignore_pid_task(pid_list, no_pid_list, current));
}
static void register_pid_events(struct trace_array *tr)
diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c
index cd37f2013758..d569233df7c9 100644
--- a/kernel/trace/trace_functions.c
+++ b/kernel/trace/trace_functions.c
@@ -297,7 +297,7 @@ function_stack_trace_call(unsigned long ip, unsigned long parent_ip,
trace_ctx = tracing_gen_ctx_flags(flags);
trace_function(tr, ip, parent_ip, trace_ctx, NULL);
#ifdef CONFIG_UNWINDER_FRAME_POINTER
- if (ftrace_pids_enabled(op))
+ if (ftrace_task_filters_enabled(op))
skip++;
#endif
__trace_stack(tr, trace_ctx, skip);
diff --git a/kernel/trace/trace_pid.c b/kernel/trace/trace_pid.c
index 7127c8de4174..ec9d3558b543 100644
--- a/kernel/trace/trace_pid.c
+++ b/kernel/trace/trace_pid.c
@@ -16,17 +16,17 @@ trace_find_filtered_pid(struct trace_pid_list *filtered_pids, pid_t search_pid)
}
/**
- * trace_ignore_this_task - should a task be ignored for tracing
+ * trace_ignore_pid_task - should a task be ignored by PID filters
* @filtered_pids: The list of pids to check
* @filtered_no_pids: The list of pids not to be traced
- * @task: The task that should be ignored if not filtered
+ * @task: The task to test against the PID filters
*
- * Checks if @task should be traced or not from @filtered_pids.
+ * Checks whether @task should be ignored by the PID include/exclude filters.
* Returns true if @task should *NOT* be traced.
* Returns false if @task should be traced.
*/
bool
-trace_ignore_this_task(struct trace_pid_list *filtered_pids,
+trace_ignore_pid_task(struct trace_pid_list *filtered_pids,
struct trace_pid_list *filtered_no_pids,
struct task_struct *task)
{
--
2.25.1
^ permalink raw reply related [flat|nested] 11+ messages in thread* [RFC PATCH 2/4] ftrace: Centralize task filter state updates
2026-08-30 11:07 [RFC PATCH 0/4] ftrace: Add task comm filtering for function tracing hu.shengming
2026-08-30 11:09 ` [RFC PATCH 1/4] ftrace: Generalize function task filter names hu.shengming
@ 2026-08-30 11:10 ` hu.shengming
2026-08-30 11:12 ` [RFC PATCH 3/4] ftrace: Add exact task comm filtering hu.shengming
` (3 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: hu.shengming @ 2026-08-30 11:10 UTC (permalink / raw)
To: hu.shengming
Cc: rostedt, mhiramat, mathieu.desnoyers, mark.rutland, corbet, skhan,
rdunlap, linux-kernel, linux-trace-kernel, linux-doc, ran.xiaokai,
xu.xin16, zhang.run
From: Shengming Hu <hu.shengming@zte.com.cn>
A later change will add task comm filtering alongside the existing PID
filters. Both filters need to share the sched_switch probe and the
per-CPU cached task decision.
Move the probe registration and cache refresh logic into
ftrace_task_filters_changed(). This gives PID and future task filters
a single place to update the shared state when a filter changes.
The helper also refreshes the cached result for currently running tasks
when one PID filter is cleared while the other remains active, instead
of leaving the result unchanged until the next schedule-in.
Signed-off-by: Shengming Hu <hu.shengming@zte.com.cn>
---
kernel/trace/ftrace.c | 58 +++++++++++++++++++++++++++----------------
1 file changed, 37 insertions(+), 21 deletions(-)
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 479ea004adc3..0c73abb8fec8 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -8637,6 +8637,16 @@ ftrace_func_t ftrace_ops_get_func(struct ftrace_ops *ops)
return ops->func;
}
+static bool ftrace_task_filters_active(struct trace_array *tr)
+{
+ return rcu_dereference_protected(tr->function_pids,
+ lockdep_is_held(&ftrace_lock)) ||
+ rcu_dereference_protected(tr->function_no_pids,
+ lockdep_is_held(&ftrace_lock));
+}
+
+static void ignore_task_cpu(void *data);
+
static void
ftrace_filter_task_sched_switch_probe(void *data, bool preempt,
struct task_struct *prev,
@@ -8703,11 +8713,31 @@ void ftrace_pid_follow_fork(struct trace_array *tr, bool enable)
}
}
+static void ftrace_task_filters_changed(struct trace_array *tr,
+ bool was_enabled)
+{
+ bool enabled = ftrace_task_filters_active(tr);
+ int cpu;
+
+ if (!was_enabled && enabled)
+ register_trace_sched_switch(ftrace_filter_task_sched_switch_probe, tr);
+ else if (was_enabled && !enabled) {
+ unregister_trace_sched_switch(ftrace_filter_task_sched_switch_probe, tr);
+ for_each_possible_cpu(cpu)
+ per_cpu_ptr(tr->array_buffer.data, cpu)->ftrace_ignore_pid =
+ FTRACE_PID_TRACE;
+ return;
+ }
+
+ if (enabled)
+ on_each_cpu(ignore_task_cpu, tr, 1);
+}
+
static void clear_ftrace_pids(struct trace_array *tr, int type)
{
struct trace_pid_list *pid_list;
struct trace_pid_list *no_pid_list;
- int cpu;
+ bool task_filters_enabled;
pid_list = rcu_dereference_protected(tr->function_pids,
lockdep_is_held(&ftrace_lock));
@@ -8718,12 +8748,7 @@ static void clear_ftrace_pids(struct trace_array *tr, int type)
if (!pid_type_enabled(type, pid_list, no_pid_list))
return;
- /* See if the pids still need to be checked after this */
- if (!still_need_pid_events(type, pid_list, no_pid_list)) {
- unregister_trace_sched_switch(ftrace_filter_task_sched_switch_probe, tr);
- for_each_possible_cpu(cpu)
- per_cpu_ptr(tr->array_buffer.data, cpu)->ftrace_ignore_pid = FTRACE_PID_TRACE;
- }
+ task_filters_enabled = ftrace_task_filters_active(tr);
if (type & TRACE_PIDS)
rcu_assign_pointer(tr->function_pids, NULL);
@@ -8731,6 +8756,8 @@ static void clear_ftrace_pids(struct trace_array *tr, int type)
if (type & TRACE_NO_PIDS)
rcu_assign_pointer(tr->function_no_pids, NULL);
+ ftrace_task_filters_changed(tr, task_filters_enabled);
+
/* Wait till all users are no longer using pid filtering */
synchronize_rcu();
@@ -8935,27 +8962,24 @@ pid_write(struct file *filp, const char __user *ubuf,
struct seq_file *m = filp->private_data;
struct trace_array *tr = m->private;
struct trace_pid_list *filtered_pids;
- struct trace_pid_list *other_pids;
struct trace_pid_list *pid_list;
+ bool task_filters_enabled;
ssize_t ret;
if (!cnt)
return 0;
guard(mutex)(&ftrace_lock);
+ task_filters_enabled = ftrace_task_filters_active(tr);
switch (type) {
case TRACE_PIDS:
filtered_pids = rcu_dereference_protected(tr->function_pids,
lockdep_is_held(&ftrace_lock));
- other_pids = rcu_dereference_protected(tr->function_no_pids,
- lockdep_is_held(&ftrace_lock));
break;
case TRACE_NO_PIDS:
filtered_pids = rcu_dereference_protected(tr->function_no_pids,
lockdep_is_held(&ftrace_lock));
- other_pids = rcu_dereference_protected(tr->function_pids,
- lockdep_is_held(&ftrace_lock));
break;
default:
WARN_ON_ONCE(1);
@@ -8979,17 +9003,9 @@ pid_write(struct file *filp, const char __user *ubuf,
if (filtered_pids) {
synchronize_rcu();
trace_pid_list_free(filtered_pids);
- } else if (pid_list && !other_pids) {
- /* Register a probe to set whether to ignore the tracing of a task */
- register_trace_sched_switch(ftrace_filter_task_sched_switch_probe, tr);
}
- /*
- * Ignoring of pids is done at task switch. But we have to
- * check for those tasks that are currently running.
- * Always do this in case a pid was appended or removed.
- */
- on_each_cpu(ignore_task_cpu, tr, 1);
+ ftrace_task_filters_changed(tr, task_filters_enabled);
ftrace_update_pid_func();
ftrace_startup_all(0);
--
2.25.1
^ permalink raw reply related [flat|nested] 11+ messages in thread* [RFC PATCH 3/4] ftrace: Add exact task comm filtering
2026-08-30 11:07 [RFC PATCH 0/4] ftrace: Add task comm filtering for function tracing hu.shengming
2026-08-30 11:09 ` [RFC PATCH 1/4] ftrace: Generalize function task filter names hu.shengming
2026-08-30 11:10 ` [RFC PATCH 2/4] ftrace: Centralize task filter state updates hu.shengming
@ 2026-08-30 11:12 ` hu.shengming
2026-08-30 11:12 ` [RFC PATCH 4/4] Documentation/ftrace: Document function comm filters hu.shengming
` (2 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: hu.shengming @ 2026-08-30 11:12 UTC (permalink / raw)
To: hu.shengming
Cc: rostedt, mhiramat, mathieu.desnoyers, mark.rutland, corbet, skhan,
rdunlap, linux-kernel, linux-trace-kernel, linux-doc, ran.xiaokai,
xu.xin16, zhang.run
From: Shengming Hu <hu.shengming@zte.com.cn>
PID filters only select tasks that already have a known PID. This makes
it difficult to set up function tracing for a service before it starts
or to keep tracing it after it restarts with a different PID.
Add set_ftrace_comm and set_ftrace_notrace_comm to filter function and
function_graph tracing by task comm.
Comm filters share the existing sched_switch probe and per-CPU cached
task decision with PID filters. The filters are checked when a task is
scheduled in, so the function tracing fast path remains unchanged.
Comm names are matched exactly. Each write adds one name to the list.
A trailing newline is ignored, while embedded newlines and names longer
than TASK_COMM_LEN - 1 are rejected. Empty writes have no effect,
duplicate names are ignored, and opening the file with O_TRUNC clears
the list.
When both PID and comm include filters are set, a task must match both.
A match in either exclude filter prevents the task from being traced.
If a running task changes its comm, the new name takes effect the next
time the task is scheduled in.
Signed-off-by: Shengming Hu <hu.shengming@zte.com.cn>
---
kernel/trace/Makefile | 1 +
kernel/trace/comm_list.c | 314 ++++++++++++++++++++++++++++++++++++
kernel/trace/comm_list.h | 17 ++
kernel/trace/ftrace.c | 336 ++++++++++++++++++++++++++++++++++++++-
kernel/trace/trace.c | 5 +
kernel/trace/trace.h | 32 ++++
6 files changed, 701 insertions(+), 4 deletions(-)
create mode 100644 kernel/trace/comm_list.c
create mode 100644 kernel/trace/comm_list.h
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile
index f934ff586bd4..415cd9db9c3e 100644
--- a/kernel/trace/Makefile
+++ b/kernel/trace/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_TRACING) += trace_printk.o
obj-$(CONFIG_TRACING) += trace_pid.o
obj-$(CONFIG_TRACER_SNAPSHOT) += trace_snapshot.o
obj-$(CONFIG_TRACING) += pid_list.o
+obj-$(CONFIG_TRACING) += comm_list.o
obj-$(CONFIG_TRACING_MAP) += tracing_map.o
obj-$(CONFIG_PREEMPTIRQ_DELAY_TEST) += preemptirq_delay_test.o
obj-$(CONFIG_SYNTH_EVENT_GEN_TEST) += synth_event_gen_test.o
diff --git a/kernel/trace/comm_list.c b/kernel/trace/comm_list.c
new file mode 100644
index 000000000000..c94c53d7d70c
--- /dev/null
+++ b/kernel/trace/comm_list.c
@@ -0,0 +1,314 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 ZTE Inc, Shengming Hu <hu.shengming@zte.com.cn>
+ */
+
+#include <linux/limits.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+
+#include "trace.h"
+#include "comm_list.h"
+
+#define COMM_LIST_INIT_SIZE 4
+
+static int comm_cmp(const char a[TASK_COMM_LEN],
+ const char b[TASK_COMM_LEN])
+{
+ return memcmp(a, b, TASK_COMM_LEN);
+}
+
+/*
+ * Return true if @comm exists. @pos is either the matching position or the
+ * insertion position that keeps the array sorted.
+ */
+static bool trace_comm_list_find(struct trace_comm_list *comm_list,
+ const char comm[TASK_COMM_LEN],
+ unsigned int *pos)
+{
+ unsigned int low = 0;
+ unsigned int high;
+
+ if (!comm_list) {
+ *pos = 0;
+ return false;
+ }
+
+ high = comm_list->nr_comms;
+ while (low < high) {
+ unsigned int mid = low + (high - low) / 2;
+ int cmp = comm_cmp(comm_list->comms[mid], comm);
+
+ if (cmp < 0)
+ low = mid + 1;
+ else
+ high = mid;
+ }
+
+ *pos = low;
+ return low < comm_list->nr_comms &&
+ !comm_cmp(comm_list->comms[low], comm);
+}
+
+/**
+ * trace_comm_list_alloc - create a new comm_list
+ *
+ * Allocates a new comm_list to store comms into.
+ *
+ * Returns the comm_list on success, NULL otherwise.
+ */
+struct trace_comm_list *trace_comm_list_alloc(void)
+{
+ return kzalloc(sizeof(struct trace_comm_list), GFP_KERNEL);
+}
+
+/**
+ * trace_comm_list_free - Frees an allocated comm_list.
+ * @comm_list: The comm list to free.
+ *
+ * Frees the memory for a comm_list that was allocated.
+ */
+void trace_comm_list_free(struct trace_comm_list *comm_list)
+{
+ if (!comm_list)
+ return;
+
+ kfree(comm_list->comms);
+ kfree(comm_list);
+}
+
+/**
+ * trace_comm_list_is_set - test if the comm is set in the list
+ * @comm_list: The comm list to test
+ * @comm: The comm to see if set in the list.
+ *
+ * Tests if @comm is set in the @comm_list.
+ *
+ * Return true if the comm is in the list, false otherwise.
+ */
+bool trace_comm_list_is_set(struct trace_comm_list *comm_list,
+ const char comm[TASK_COMM_LEN])
+{
+ unsigned int pos;
+
+ return trace_comm_list_find(comm_list, comm, &pos);
+}
+
+static int trace_comm_list_grow(struct trace_comm_list *comm_list)
+{
+ char (*comms)[TASK_COMM_LEN];
+ unsigned int max_comms;
+
+ if (comm_list->nr_comms < comm_list->max_comms)
+ return 0;
+
+ if (!comm_list->max_comms) {
+ max_comms = COMM_LIST_INIT_SIZE;
+ } else {
+ if (comm_list->max_comms > UINT_MAX / 2)
+ return -E2BIG;
+ max_comms = comm_list->max_comms * 2;
+ }
+
+ comms = krealloc_array(comm_list->comms, max_comms,
+ sizeof(*comm_list->comms), GFP_KERNEL);
+ if (!comms)
+ return -ENOMEM;
+
+ comm_list->comms = comms;
+ comm_list->max_comms = max_comms;
+ return 0;
+}
+
+/**
+ * trace_comm_list_set - add a comm to the list
+ * @comm_list: The comm list to add the @comm to.
+ * @comm: The comm to add.
+ *
+ * Adds @comm to @comm_list. The comms are kept sorted and duplicate
+ * entries are ignored.
+ *
+ * Return 0 on success, negative otherwise.
+ */
+int trace_comm_list_set(struct trace_comm_list *comm_list,
+ const char comm[TASK_COMM_LEN])
+{
+ unsigned int pos;
+ int ret;
+
+ if (!comm_list)
+ return -ENODEV;
+
+ if (trace_comm_list_find(comm_list, comm, &pos))
+ return 0;
+
+ ret = trace_comm_list_grow(comm_list);
+ if (ret)
+ return ret;
+
+ if (pos < comm_list->nr_comms)
+ memmove(&comm_list->comms[pos + 1], &comm_list->comms[pos],
+ (comm_list->nr_comms - pos) * sizeof(*comm_list->comms));
+
+ memcpy(comm_list->comms[pos], comm, TASK_COMM_LEN);
+ comm_list->nr_comms++;
+
+ return 0;
+}
+
+/**
+ * trace_ignore_comm_task - should a task be ignored by comm filters
+ * @filtered_comms: The list of comms to trace
+ * @filtered_no_comms: The list of comms not to be traced
+ * @task: The task to test against the comm filters
+ *
+ * Checks whether @task should be ignored by the comm include/exclude
+ * filters.
+ *
+ * Returns true if @task should not be traced, false otherwise.
+ */
+bool trace_ignore_comm_task(struct trace_comm_list *filtered_comms,
+ struct trace_comm_list *filtered_no_comms,
+ struct task_struct *task)
+{
+ char comm[TASK_COMM_LEN];
+
+ get_task_comm(comm, task);
+
+ return (filtered_comms &&
+ !trace_comm_list_is_set(filtered_comms, comm)) ||
+ (filtered_no_comms &&
+ trace_comm_list_is_set(filtered_no_comms, comm));
+}
+
+/**
+ * trace_comm_start - start iterating over a comm list
+ * @comm_list: The comm list to show
+ * @pos: The position of the file
+ *
+ * Returns the comm at @pos, or NULL if there are no more comms.
+ */
+void *trace_comm_start(struct trace_comm_list *comm_list, loff_t *pos)
+{
+ if (!comm_list || *pos < 0 || *pos >= (loff_t)comm_list->nr_comms)
+ return NULL;
+
+ return comm_list->comms[*pos];
+}
+
+/**
+ * trace_comm_next - return the next comm in the list
+ * @comm_list: The comm list to show
+ * @v: The current comm
+ * @pos: The position of the file
+ *
+ * Returns the next comm in @comm_list, or NULL if there are no more comms.
+ */
+void *trace_comm_next(struct trace_comm_list *comm_list, void *v, loff_t *pos)
+{
+ (void)v;
+
+ (*pos)++;
+ return trace_comm_start(comm_list, pos);
+}
+
+/**
+ * trace_comm_show - show the current comm
+ * @m: The seq_file structure to write into
+ * @v: The comm to display
+ *
+ * Displays the current comm in the seq_file.
+ */
+int trace_comm_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "%s\n", (char *)v);
+ return 0;
+}
+
+static int trace_comm_from_user(char comm[TASK_COMM_LEN],
+ const char __user *ubuf, size_t cnt)
+{
+ char buf[TASK_COMM_LEN];
+ size_t len = cnt;
+
+ if (cnt > sizeof(buf))
+ return -EINVAL;
+
+ if (copy_from_user(buf, ubuf, cnt))
+ return -EFAULT;
+
+ if (len && buf[len - 1] == '\n')
+ len--;
+
+ if (!len) {
+ comm[0] = '\0';
+ return 0;
+ }
+
+ if (len >= TASK_COMM_LEN || memchr(buf, '\0', len) ||
+ memchr(buf, '\n', len))
+ return -EINVAL;
+
+ memset(comm, 0, TASK_COMM_LEN);
+ memcpy(comm, buf, len);
+
+ return 1;
+}
+
+/**
+ * trace_comm_write - add a comm to a comm list
+ * @filtered_comms: The current comm list
+ * @new_comm_list: The pointer to place the new comm list
+ * @ubuf: The user buffer containing the comm
+ * @cnt: The size of the user buffer
+ *
+ * Creates a new comm list containing the current comms and the comm
+ * specified by the user.
+ *
+ * Return the number of bytes written on success, negative otherwise.
+ */
+int trace_comm_write(struct trace_comm_list *filtered_comms,
+ struct trace_comm_list **new_comm_list,
+ const char __user *ubuf, size_t cnt)
+{
+ struct trace_comm_list *comm_list;
+ char comm[TASK_COMM_LEN];
+ unsigned int i;
+ int parsed;
+ int ret;
+
+ parsed = trace_comm_from_user(comm, ubuf, cnt);
+ if (parsed < 0)
+ return parsed;
+
+ if (!parsed || trace_comm_list_is_set(filtered_comms, comm)) {
+ *new_comm_list = filtered_comms;
+ return cnt;
+ }
+
+ comm_list = trace_comm_list_alloc();
+ if (!comm_list)
+ return -ENOMEM;
+
+ if (filtered_comms) {
+ for (i = 0; i < filtered_comms->nr_comms; i++) {
+ ret = trace_comm_list_set(comm_list,
+ filtered_comms->comms[i]);
+ if (ret < 0)
+ goto fail;
+ }
+ }
+
+ ret = trace_comm_list_set(comm_list, comm);
+ if (ret < 0)
+ goto fail;
+
+ *new_comm_list = comm_list;
+ return cnt;
+
+fail:
+ trace_comm_list_free(comm_list);
+ return ret;
+}
diff --git a/kernel/trace/comm_list.h b/kernel/trace/comm_list.h
new file mode 100644
index 000000000000..1f2a6b329ed3
--- /dev/null
+++ b/kernel/trace/comm_list.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/* Do not include this file directly. */
+
+#ifndef _TRACE_INTERNAL_COMM_LIST_H
+#define _TRACE_INTERNAL_COMM_LIST_H
+
+#include <linux/sched.h>
+
+struct trace_comm_list {
+ unsigned int nr_comms;
+ unsigned int max_comms;
+ char (*comms)[TASK_COMM_LEN];
+};
+
+#endif /* _TRACE_INTERNAL_COMM_LIST_H */
+
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index 0c73abb8fec8..e2a466448a9b 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -108,7 +108,10 @@ bool ftrace_task_filters_enabled(struct ftrace_ops *ops)
tr = ops->private;
- return tr->function_pids != NULL || tr->function_no_pids != NULL;
+ return rcu_access_pointer(tr->function_pids) ||
+ rcu_access_pointer(tr->function_no_pids) ||
+ rcu_access_pointer(tr->function_comms) ||
+ rcu_access_pointer(tr->function_no_comms);
}
static void ftrace_update_trampoline(struct ftrace_ops *ops);
@@ -168,7 +171,7 @@ static inline void ftrace_ops_init(struct ftrace_ops *ops)
#endif
}
-/* Call this function for when a callback filters on set_ftrace_pid */
+/* Call this function for when a callback uses task filters */
static void ftrace_pid_func(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *op, struct ftrace_regs *fregs)
{
@@ -8642,11 +8645,32 @@ static bool ftrace_task_filters_active(struct trace_array *tr)
return rcu_dereference_protected(tr->function_pids,
lockdep_is_held(&ftrace_lock)) ||
rcu_dereference_protected(tr->function_no_pids,
+ lockdep_is_held(&ftrace_lock)) ||
+ rcu_dereference_protected(tr->function_comms,
+ lockdep_is_held(&ftrace_lock)) ||
+ rcu_dereference_protected(tr->function_no_comms,
lockdep_is_held(&ftrace_lock));
}
static void ignore_task_cpu(void *data);
+static bool __ftrace_ignore_task(struct trace_pid_list *pid_list,
+ struct trace_pid_list *no_pid_list,
+ struct trace_comm_list *comm_filter,
+ struct trace_comm_list *no_comm_filter,
+ struct task_struct *task)
+{
+ if ((pid_list || no_pid_list) &&
+ trace_ignore_pid_task(pid_list, no_pid_list, task))
+ return true;
+
+ if ((comm_filter || no_comm_filter) &&
+ trace_ignore_comm_task(comm_filter, no_comm_filter, task))
+ return true;
+
+ return false;
+}
+
static void
ftrace_filter_task_sched_switch_probe(void *data, bool preempt,
struct task_struct *prev,
@@ -8656,11 +8680,15 @@ ftrace_filter_task_sched_switch_probe(void *data, bool preempt,
struct trace_array *tr = data;
struct trace_pid_list *pid_list;
struct trace_pid_list *no_pid_list;
+ struct trace_comm_list *comm_filter;
+ struct trace_comm_list *no_comm_filter;
pid_list = rcu_dereference_sched(tr->function_pids);
no_pid_list = rcu_dereference_sched(tr->function_no_pids);
- if (trace_ignore_pid_task(pid_list, no_pid_list, next))
+ comm_filter = rcu_dereference_sched(tr->function_comms);
+ no_comm_filter = rcu_dereference_sched(tr->function_no_comms);
+ if (__ftrace_ignore_task(pid_list, no_pid_list, comm_filter, no_comm_filter, next))
this_cpu_write(tr->array_buffer.data->ftrace_ignore_pid,
FTRACE_PID_IGNORE);
else
@@ -8788,6 +8816,54 @@ static void ftrace_pid_reset(struct trace_array *tr, int type)
mutex_unlock(&ftrace_lock);
}
+static void clear_ftrace_comms(struct trace_array *tr, int type)
+{
+ struct trace_comm_list *comm_filter;
+ struct trace_comm_list *no_comm_filter;
+ bool task_filters_enabled;
+
+ comm_filter = rcu_dereference_protected(tr->function_comms,
+ lockdep_is_held(&ftrace_lock));
+ no_comm_filter = rcu_dereference_protected(tr->function_no_comms,
+ lockdep_is_held(&ftrace_lock));
+
+ if (!comm_type_enabled(type, comm_filter, no_comm_filter))
+ return;
+
+ task_filters_enabled = ftrace_task_filters_active(tr);
+
+ if (type & TRACE_COMMS)
+ rcu_assign_pointer(tr->function_comms, NULL);
+
+ if (type & TRACE_NO_COMMS)
+ rcu_assign_pointer(tr->function_no_comms, NULL);
+
+ ftrace_task_filters_changed(tr, task_filters_enabled);
+ synchronize_rcu();
+
+ if ((type & TRACE_COMMS) && comm_filter)
+ trace_comm_list_free(comm_filter);
+
+ if ((type & TRACE_NO_COMMS) && no_comm_filter)
+ trace_comm_list_free(no_comm_filter);
+}
+
+void ftrace_clear_comms(struct trace_array *tr)
+{
+ mutex_lock(&ftrace_lock);
+ clear_ftrace_comms(tr, TRACE_COMMS | TRACE_NO_COMMS);
+ mutex_unlock(&ftrace_lock);
+}
+
+static void ftrace_comm_reset(struct trace_array *tr, int type)
+{
+ mutex_lock(&ftrace_lock);
+ clear_ftrace_comms(tr, type);
+ ftrace_update_pid_func();
+ ftrace_startup_all(0);
+ mutex_unlock(&ftrace_lock);
+}
+
/* Greater than any max PID */
#define FTRACE_NO_PIDS (void *)(PID_MAX_LIMIT + 1)
@@ -8880,6 +8956,102 @@ static const struct seq_operations ftrace_no_pid_sops = {
.show = fpid_show,
};
+/* Not a valid comm pointer */
+#define FTRACE_NO_COMM ((void *)1)
+
+static void *fcomm_start(struct seq_file *m, loff_t *pos)
+ __acquires(RCU)
+{
+ struct trace_comm_list *comm_list;
+ struct trace_array *tr = m->private;
+
+ mutex_lock(&ftrace_lock);
+ rcu_read_lock_sched();
+
+ comm_list = rcu_dereference_sched(tr->function_comms);
+
+ if (!comm_list)
+ return !(*pos) ? FTRACE_NO_COMM : NULL;
+
+ return trace_comm_start(comm_list, pos);
+}
+
+static void *fcomm_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ struct trace_array *tr = m->private;
+ struct trace_comm_list *comm_list;
+
+ if (v == FTRACE_NO_COMM) {
+ (*pos)++;
+ return NULL;
+ }
+
+ comm_list = rcu_dereference_sched(tr->function_comms);
+ return trace_comm_next(comm_list, v, pos);
+}
+
+static void fcomm_stop(struct seq_file *m, void *p)
+ __releases(RCU)
+{
+ rcu_read_unlock_sched();
+ mutex_unlock(&ftrace_lock);
+}
+
+static int fcomm_show(struct seq_file *m, void *v)
+{
+ if (v == FTRACE_NO_COMM) {
+ seq_puts(m, "no comm\n");
+ return 0;
+ }
+
+ return trace_comm_show(m, v);
+}
+
+static const struct seq_operations ftrace_comm_sops = {
+ .start = fcomm_start,
+ .next = fcomm_next,
+ .stop = fcomm_stop,
+ .show = fcomm_show,
+};
+
+static void *fncomm_start(struct seq_file *m, loff_t *pos)
+ __acquires(RCU)
+{
+ struct trace_comm_list *comm_list;
+ struct trace_array *tr = m->private;
+
+ mutex_lock(&ftrace_lock);
+ rcu_read_lock_sched();
+
+ comm_list = rcu_dereference_sched(tr->function_no_comms);
+
+ if (!comm_list)
+ return !(*pos) ? FTRACE_NO_COMM : NULL;
+
+ return trace_comm_start(comm_list, pos);
+}
+
+static void *fncomm_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ struct trace_array *tr = m->private;
+ struct trace_comm_list *comm_list;
+
+ if (v == FTRACE_NO_COMM) {
+ (*pos)++;
+ return NULL;
+ }
+
+ comm_list = rcu_dereference_sched(tr->function_no_comms);
+ return trace_comm_next(comm_list, v, pos);
+}
+
+static const struct seq_operations ftrace_no_comm_sops = {
+ .start = fncomm_start,
+ .next = fncomm_next,
+ .stop = fcomm_stop,
+ .show = fcomm_show,
+};
+
static int pid_open(struct inode *inode, struct file *file, int type)
{
const struct seq_operations *seq_ops;
@@ -8932,11 +9104,62 @@ ftrace_no_pid_open(struct inode *inode, struct file *file)
return pid_open(inode, file, TRACE_NO_PIDS);
}
+static int comm_open(struct inode *inode, struct file *file, int type)
+{
+ const struct seq_operations *seq_ops;
+ struct trace_array *tr = inode->i_private;
+ struct seq_file *m;
+ int ret = 0;
+
+ ret = tracing_check_open_get_tr(tr);
+ if (ret)
+ return ret;
+
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_flags & O_TRUNC))
+ ftrace_comm_reset(tr, type);
+
+ switch (type) {
+ case TRACE_COMMS:
+ seq_ops = &ftrace_comm_sops;
+ break;
+ case TRACE_NO_COMMS:
+ seq_ops = &ftrace_no_comm_sops;
+ break;
+ default:
+ trace_array_put(tr);
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
+
+ ret = seq_open(file, seq_ops);
+ if (ret < 0) {
+ trace_array_put(tr);
+ } else {
+ m = file->private_data;
+ m->private = tr;
+ }
+
+ return ret;
+}
+
+static int ftrace_comm_open(struct inode *inode, struct file *file)
+{
+ return comm_open(inode, file, TRACE_COMMS);
+}
+
+static int ftrace_no_comm_open(struct inode *inode, struct file *file)
+{
+ return comm_open(inode, file, TRACE_NO_COMMS);
+}
+
static void ignore_task_cpu(void *data)
{
struct trace_array *tr = data;
struct trace_pid_list *pid_list;
struct trace_pid_list *no_pid_list;
+ struct trace_comm_list *comm_filter;
+ struct trace_comm_list *no_comm_filter;
/*
* This function is called by on_each_cpu() while the
@@ -8947,7 +9170,12 @@ static void ignore_task_cpu(void *data)
no_pid_list = rcu_dereference_protected(tr->function_no_pids,
mutex_is_locked(&ftrace_lock));
- if (trace_ignore_pid_task(pid_list, no_pid_list, current))
+ comm_filter = rcu_dereference_protected(tr->function_comms,
+ mutex_is_locked(&ftrace_lock));
+ no_comm_filter = rcu_dereference_protected(tr->function_no_comms,
+ mutex_is_locked(&ftrace_lock));
+
+ if (__ftrace_ignore_task(pid_list, no_pid_list, comm_filter, no_comm_filter, current))
this_cpu_write(tr->array_buffer.data->ftrace_ignore_pid,
FTRACE_PID_IGNORE);
else
@@ -9022,6 +9250,86 @@ ftrace_pid_write(struct file *filp, const char __user *ubuf,
return pid_write(filp, ubuf, cnt, ppos, TRACE_PIDS);
}
+static ssize_t comm_write(struct file *filp, const char __user *ubuf,
+ size_t cnt, loff_t *ppos, int type)
+{
+ struct seq_file *m = filp->private_data;
+ struct trace_array *tr = m->private;
+ struct trace_comm_list *filtered_comms;
+ struct trace_comm_list *comm_list;
+ bool task_filters_enabled;
+ ssize_t ret;
+
+ if (!cnt)
+ return 0;
+
+ guard(mutex)(&ftrace_lock);
+ task_filters_enabled = ftrace_task_filters_active(tr);
+
+ switch (type) {
+ case TRACE_COMMS:
+ filtered_comms = rcu_dereference_protected(tr->function_comms,
+ lockdep_is_held(&ftrace_lock));
+ break;
+ case TRACE_NO_COMMS:
+ filtered_comms = rcu_dereference_protected(tr->function_no_comms,
+ lockdep_is_held(&ftrace_lock));
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
+
+ ret = trace_comm_write(filtered_comms, &comm_list, ubuf, cnt);
+ if (ret < 0)
+ return ret;
+
+ if (comm_list == filtered_comms) {
+ *ppos += ret;
+ return ret;
+ }
+
+ if (type == TRACE_COMMS)
+ rcu_assign_pointer(tr->function_comms, comm_list);
+ else
+ rcu_assign_pointer(tr->function_no_comms, comm_list);
+
+ if (filtered_comms) {
+ synchronize_rcu();
+ trace_comm_list_free(filtered_comms);
+ }
+
+ ftrace_task_filters_changed(tr, task_filters_enabled);
+ ftrace_update_pid_func();
+ ftrace_startup_all(0);
+
+ *ppos += ret;
+ return ret;
+}
+
+static ssize_t ftrace_comm_write(struct file *filp, const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ return comm_write(filp, ubuf, cnt, ppos, TRACE_COMMS);
+}
+
+static ssize_t ftrace_no_comm_write(struct file *filp,
+ const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ return comm_write(filp, ubuf, cnt, ppos, TRACE_NO_COMMS);
+}
+
+static int
+ftrace_comm_release(struct inode *inode, struct file *file)
+{
+ struct trace_array *tr = inode->i_private;
+
+ trace_array_put(tr);
+
+ return seq_release(inode, file);
+}
+
static ssize_t
ftrace_no_pid_write(struct file *filp, const char __user *ubuf,
size_t cnt, loff_t *ppos)
@@ -9055,12 +9363,32 @@ static const struct file_operations ftrace_no_pid_fops = {
.release = ftrace_pid_release,
};
+static const struct file_operations ftrace_comm_fops = {
+ .open = ftrace_comm_open,
+ .write = ftrace_comm_write,
+ .read = seq_read,
+ .llseek = tracing_lseek,
+ .release = ftrace_comm_release,
+};
+
+static const struct file_operations ftrace_no_comm_fops = {
+ .open = ftrace_no_comm_open,
+ .write = ftrace_no_comm_write,
+ .read = seq_read,
+ .llseek = tracing_lseek,
+ .release = ftrace_comm_release,
+};
+
void ftrace_init_tracefs(struct trace_array *tr, struct dentry *d_tracer)
{
trace_create_file("set_ftrace_pid", TRACE_MODE_WRITE, d_tracer,
tr, &ftrace_pid_fops);
trace_create_file("set_ftrace_notrace_pid", TRACE_MODE_WRITE,
d_tracer, tr, &ftrace_no_pid_fops);
+ trace_create_file("set_ftrace_comm", TRACE_MODE_WRITE, d_tracer,
+ tr, &ftrace_comm_fops);
+ trace_create_file("set_ftrace_notrace_comm", TRACE_MODE_WRITE,
+ d_tracer, tr, &ftrace_no_comm_fops);
}
void __init ftrace_init_tracefs_toplevel(struct trace_array *tr,
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 3e0907aef172..8cbfaed24810 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -4261,6 +4261,10 @@ static const char readme_msg[] =
"\t\t (function)\n"
" set_ftrace_notrace_pid\t- Write pid(s) to not function trace those pids\n"
"\t\t (function)\n"
+ " set_ftrace_comm\t- Write task comms to only function trace those tasks\n"
+ "\t\t (function)\n"
+ " set_ftrace_notrace_comm\t- Write task comms to not function trace those tasks\n"
+ "\t\t (function)\n"
#endif
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
" set_graph_function\t- Trace the nested calls of a function (function_graph)\n"
@@ -8789,6 +8793,7 @@ static int __remove_instance(struct trace_array *tr)
clear_ftrace_function_probes(tr);
event_trace_del_tracer(tr);
ftrace_clear_pids(tr);
+ ftrace_clear_comms(tr);
ftrace_destroy_function_files(tr);
tracefs_remove(tr->dir);
free_percpu(tr->last_func_repeats);
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index f73913eed307..218bd26dc73a 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -182,6 +182,7 @@ struct fexit_trace_entry_head {
#define TRACE_BUF_SIZE 1024
struct trace_array;
+struct trace_comm_list;
/*
* The CPU trace array - it consists of thousands of trace entries
@@ -237,12 +238,32 @@ int trace_pid_list_clear(struct trace_pid_list *pid_list, unsigned int pid);
int trace_pid_list_first(struct trace_pid_list *pid_list, unsigned int *pid);
int trace_pid_list_next(struct trace_pid_list *pid_list, unsigned int pid,
unsigned int *next);
+struct trace_comm_list *trace_comm_list_alloc(void);
+void trace_comm_list_free(struct trace_comm_list *comm_list);
+bool trace_comm_list_is_set(struct trace_comm_list *comm_list,
+ const char comm[TASK_COMM_LEN]);
+int trace_comm_list_set(struct trace_comm_list *comm_list,
+ const char comm[TASK_COMM_LEN]);
+bool trace_ignore_comm_task(struct trace_comm_list *filtered_comms,
+ struct trace_comm_list *filtered_no_comms,
+ struct task_struct *task);
+void *trace_comm_next(struct trace_comm_list *comm_list, void *v, loff_t *pos);
+void *trace_comm_start(struct trace_comm_list *comm_list, loff_t *pos);
+int trace_comm_show(struct seq_file *m, void *v);
+int trace_comm_write(struct trace_comm_list *filtered_comms,
+ struct trace_comm_list **new_comm_list,
+ const char __user *ubuf, size_t cnt);
enum {
TRACE_PIDS = BIT(0),
TRACE_NO_PIDS = BIT(1),
};
+enum {
+ TRACE_COMMS = BIT(0),
+ TRACE_NO_COMMS = BIT(1),
+};
+
static inline bool pid_type_enabled(int type, struct trace_pid_list *pid_list,
struct trace_pid_list *no_pid_list)
{
@@ -251,6 +272,13 @@ static inline bool pid_type_enabled(int type, struct trace_pid_list *pid_list,
((type & TRACE_NO_PIDS) && no_pid_list);
}
+static inline bool comm_type_enabled(int type, struct trace_comm_list *comm_list,
+ struct trace_comm_list *no_comm_list)
+{
+ return ((type & TRACE_COMMS) && comm_list) ||
+ ((type & TRACE_NO_COMMS) && no_comm_list);
+}
+
static inline bool still_need_pid_events(int type, struct trace_pid_list *pid_list,
struct trace_pid_list *no_pid_list)
{
@@ -434,6 +462,8 @@ struct trace_array {
struct ftrace_ops *ops;
struct trace_pid_list __rcu *function_pids;
struct trace_pid_list __rcu *function_no_pids;
+ struct trace_comm_list __rcu *function_comms;
+ struct trace_comm_list __rcu *function_no_comms;
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
struct fgraph_ops *gops;
#endif
@@ -1267,6 +1297,7 @@ void ftrace_init_tracefs(struct trace_array *tr, struct dentry *d_tracer);
void ftrace_init_tracefs_toplevel(struct trace_array *tr,
struct dentry *d_tracer);
void ftrace_clear_pids(struct trace_array *tr);
+void ftrace_clear_comms(struct trace_array *tr);
int init_function_trace(void);
void ftrace_pid_follow_fork(struct trace_array *tr, bool enable);
#else
@@ -1289,6 +1320,7 @@ static inline void ftrace_reset_array_ops(struct trace_array *tr) { }
static inline void ftrace_init_tracefs(struct trace_array *tr, struct dentry *d) { }
static inline void ftrace_init_tracefs_toplevel(struct trace_array *tr, struct dentry *d) { }
static inline void ftrace_clear_pids(struct trace_array *tr) { }
+static inline void ftrace_clear_comms(struct trace_array *tr) { }
static inline int init_function_trace(void) { return 0; }
static inline void ftrace_pid_follow_fork(struct trace_array *tr, bool enable) { }
/* ftace_func_t type is not defined, use macro instead of static inline */
--
2.25.1
^ permalink raw reply related [flat|nested] 11+ messages in thread