Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bpf: Avoid iterating duplicated files for task_file iterator

Currently, task_file iterator iterates all files from all tasks.
This may potentially visit a lot of duplicated files if there are
many tasks sharing the same files, e.g., typical pthreads
where these pthreads and the main thread are sharing the same files.

This patch changed task_file iterator to skip a particular task
if that task shares the same files as its group_leader (the task
having the same tgid and also task->tgid == task->pid).
This will preserve the same result, visiting all files from all
tasks, and will reduce runtime cost significantl, e.g., if there are
a lot of pthreads and the process has a lot of open files.

Suggested-by: Andrii Nakryiko <andriin@fb.com>
Signed-off-by: Yonghong Song <yhs@fb.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Link: https://lore.kernel.org/bpf/20200902023112.1672792-1-yhs@fb.com

authored by

Yonghong Song and committed by
Daniel Borkmann
203d7b05 0697fecf

+11 -4
+11 -4
kernel/bpf/task_iter.c
··· 22 22 }; 23 23 24 24 static struct task_struct *task_seq_get_next(struct pid_namespace *ns, 25 - u32 *tid) 25 + u32 *tid, 26 + bool skip_if_dup_files) 26 27 { 27 28 struct task_struct *task = NULL; 28 29 struct pid *pid; ··· 35 34 *tid = pid_nr_ns(pid, ns); 36 35 task = get_pid_task(pid, PIDTYPE_PID); 37 36 if (!task) { 37 + ++*tid; 38 + goto retry; 39 + } else if (skip_if_dup_files && task->tgid != task->pid && 40 + task->files == task->group_leader->files) { 41 + put_task_struct(task); 42 + task = NULL; 38 43 ++*tid; 39 44 goto retry; 40 45 } ··· 55 48 struct bpf_iter_seq_task_info *info = seq->private; 56 49 struct task_struct *task; 57 50 58 - task = task_seq_get_next(info->common.ns, &info->tid); 51 + task = task_seq_get_next(info->common.ns, &info->tid, false); 59 52 if (!task) 60 53 return NULL; 61 54 ··· 72 65 ++*pos; 73 66 ++info->tid; 74 67 put_task_struct((struct task_struct *)v); 75 - task = task_seq_get_next(info->common.ns, &info->tid); 68 + task = task_seq_get_next(info->common.ns, &info->tid, false); 76 69 if (!task) 77 70 return NULL; 78 71 ··· 155 148 curr_files = *fstruct; 156 149 curr_fd = info->fd; 157 150 } else { 158 - curr_task = task_seq_get_next(ns, &curr_tid); 151 + curr_task = task_seq_get_next(ns, &curr_tid, true); 159 152 if (!curr_task) 160 153 return NULL; 161 154