From: Shengming Hu <[email protected]>

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 <[email protected]>
---
 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 <[email protected]>
+ */
+
+#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

Reply via email to