1 // SPDX-License-Identifier: GPL-2.0 2 3 #include "trace.h" 4 5 /** 6 * trace_find_filtered_pid - check if a pid exists in a filtered_pid list 7 * @filtered_pids: The list of pids to check 8 * @search_pid: The PID to find in @filtered_pids 9 * 10 * Returns true if @search_pid is found in @filtered_pids, and false otherwise. 11 */ 12 bool 13 trace_find_filtered_pid(struct trace_pid_list *filtered_pids, pid_t search_pid) 14 { 15 return trace_pid_list_is_set(filtered_pids, search_pid); 16 } 17 18 /** 19 * trace_ignore_this_task - should a task be ignored for tracing 20 * @filtered_pids: The list of pids to check 21 * @filtered_no_pids: The list of pids not to be traced 22 * @task: The task that should be ignored if not filtered 23 * 24 * Checks if @task should be traced or not from @filtered_pids. 25 * Returns true if @task should *NOT* be traced. 26 * Returns false if @task should be traced. 27 */ 28 bool 29 trace_ignore_this_task(struct trace_pid_list *filtered_pids, 30 struct trace_pid_list *filtered_no_pids, 31 struct task_struct *task) 32 { 33 /* 34 * If filtered_no_pids is not empty, and the task's pid is listed 35 * in filtered_no_pids, then return true. 36 * Otherwise, if filtered_pids is empty, that means we can 37 * trace all tasks. If it has content, then only trace pids 38 * within filtered_pids. 39 */ 40 41 return (filtered_pids && 42 !trace_find_filtered_pid(filtered_pids, task->pid)) || 43 (filtered_no_pids && 44 trace_find_filtered_pid(filtered_no_pids, task->pid)); 45 } 46 47 /** 48 * trace_filter_add_remove_task - Add or remove a task from a pid_list 49 * @pid_list: The list to modify 50 * @self: The current task for fork or NULL for exit 51 * @task: The task to add or remove 52 * 53 * If adding a task, if @self is defined, the task is only added if @self 54 * is also included in @pid_list. This happens on fork and tasks should 55 * only be added when the parent is listed. If @self is NULL, then the 56 * @task pid will be removed from the list, which would happen on exit 57 * of a task. 58 */ 59 void trace_filter_add_remove_task(struct trace_pid_list *pid_list, 60 struct task_struct *self, 61 struct task_struct *task) 62 { 63 if (!pid_list) 64 return; 65 66 /* For forks, we only add if the forking task is listed */ 67 if (self) { 68 if (!trace_find_filtered_pid(pid_list, self->pid)) 69 return; 70 } 71 72 /* "self" is set for forks, and NULL for exits */ 73 if (self) 74 trace_pid_list_set(pid_list, task->pid); 75 else 76 trace_pid_list_clear(pid_list, task->pid); 77 } 78 79 /** 80 * trace_pid_next - Used for seq_file to get to the next pid of a pid_list 81 * @pid_list: The pid list to show 82 * @v: The last pid that was shown (+1 the actual pid to let zero be displayed) 83 * @pos: The position of the file 84 * 85 * This is used by the seq_file "next" operation to iterate the pids 86 * listed in a trace_pid_list structure. 87 * 88 * Returns the pid+1 as we want to display pid of zero, but NULL would 89 * stop the iteration. 90 */ 91 void *trace_pid_next(struct trace_pid_list *pid_list, void *v, loff_t *pos) 92 { 93 long pid = (unsigned long)v; 94 unsigned int next; 95 96 (*pos)++; 97 98 /* pid already is +1 of the actual previous bit */ 99 if (trace_pid_list_next(pid_list, pid, &next) < 0) 100 return NULL; 101 102 pid = next; 103 104 /* Return pid + 1 to allow zero to be represented */ 105 return (void *)(pid + 1); 106 } 107 108 /** 109 * trace_pid_start - Used for seq_file to start reading pid lists 110 * @pid_list: The pid list to show 111 * @pos: The position of the file 112 * 113 * This is used by seq_file "start" operation to start the iteration 114 * of listing pids. 115 * 116 * Returns the pid+1 as we want to display pid of zero, but NULL would 117 * stop the iteration. 118 */ 119 void *trace_pid_start(struct trace_pid_list *pid_list, loff_t *pos) 120 { 121 unsigned long pid; 122 unsigned int first; 123 loff_t l = 0; 124 125 if (trace_pid_list_first(pid_list, &first) < 0) 126 return NULL; 127 128 pid = first; 129 130 /* Return pid + 1 so that zero can be the exit value */ 131 for (pid++; pid && l < *pos; 132 pid = (unsigned long)trace_pid_next(pid_list, (void *)pid, &l)) 133 ; 134 return (void *)pid; 135 } 136 137 /** 138 * trace_pid_show - show the current pid in seq_file processing 139 * @m: The seq_file structure to write into 140 * @v: A void pointer of the pid (+1) value to display 141 * 142 * Can be directly used by seq_file operations to display the current 143 * pid value. 144 */ 145 int trace_pid_show(struct seq_file *m, void *v) 146 { 147 unsigned long pid = (unsigned long)v - 1; 148 149 seq_printf(m, "%lu\n", pid); 150 return 0; 151 } 152 153 /* 128 should be much more than enough */ 154 #define PID_BUF_SIZE 127 155 156 int trace_pid_write(struct trace_pid_list *filtered_pids, 157 struct trace_pid_list **new_pid_list, 158 const char __user *ubuf, size_t cnt) 159 { 160 struct trace_pid_list *pid_list; 161 struct trace_parser parser; 162 unsigned long val; 163 int nr_pids = 0; 164 ssize_t read = 0; 165 ssize_t ret; 166 loff_t pos; 167 pid_t pid; 168 169 if (trace_parser_get_init(&parser, PID_BUF_SIZE + 1)) 170 return -ENOMEM; 171 172 /* 173 * Always recreate a new array. The write is an all or nothing 174 * operation. Always create a new array when adding new pids by 175 * the user. If the operation fails, then the current list is 176 * not modified. 177 */ 178 pid_list = trace_pid_list_alloc(); 179 if (!pid_list) { 180 trace_parser_put(&parser); 181 return -ENOMEM; 182 } 183 184 if (filtered_pids) { 185 /* copy the current bits to the new max */ 186 ret = trace_pid_list_first(filtered_pids, &pid); 187 while (!ret) { 188 ret = trace_pid_list_set(pid_list, pid); 189 if (ret < 0) 190 goto out; 191 192 ret = trace_pid_list_next(filtered_pids, pid + 1, &pid); 193 nr_pids++; 194 } 195 } 196 197 ret = 0; 198 while (cnt > 0) { 199 200 pos = 0; 201 202 ret = trace_get_user(&parser, ubuf, cnt, &pos); 203 if (ret < 0) 204 break; 205 206 read += ret; 207 ubuf += ret; 208 cnt -= ret; 209 210 if (!trace_parser_loaded(&parser)) 211 break; 212 213 ret = -EINVAL; 214 if (kstrtoul(parser.buffer, 0, &val)) 215 break; 216 217 pid = (pid_t)val; 218 219 if (trace_pid_list_set(pid_list, pid) < 0) { 220 ret = -1; 221 break; 222 } 223 nr_pids++; 224 225 trace_parser_clear(&parser); 226 ret = 0; 227 } 228 out: 229 trace_parser_put(&parser); 230 231 if (ret < 0) { 232 trace_pid_list_free(pid_list); 233 return ret; 234 } 235 236 if (!nr_pids) { 237 /* Cleared the list of pids */ 238 trace_pid_list_free(pid_list); 239 pid_list = NULL; 240 } 241 242 *new_pid_list = pid_list; 243 244 return read; 245 } 246 247