Skip to content

Commit eaaacd2

Browse files
yonghong-songAlexei Starovoitov
authored andcommitted
bpf: Add task and task/file iterator targets
Only the tasks belonging to "current" pid namespace are enumerated. For task/file target, the bpf program will have access to struct task_struct *task u32 fd struct file *file where fd/file is an open file for the task. Signed-off-by: Yonghong Song <yhs@fb.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Acked-by: Andrii Nakryiko <andriin@fb.com> Link: https://lore.kernel.org/bpf/20200509175911.2476407-1-yhs@fb.com
1 parent 138d0be commit eaaacd2

File tree

2 files changed

+334
-1
lines changed

2 files changed

+334
-1
lines changed

kernel/bpf/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
obj-y := core.o
33
CFLAGS_core.o += $(call cc-disable-warning, override-init)
44

5-
obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o bpf_iter.o map_iter.o
5+
obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o bpf_iter.o map_iter.o task_iter.o
66
obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o
77
obj-$(CONFIG_BPF_SYSCALL) += local_storage.o queue_stack_maps.o
88
obj-$(CONFIG_BPF_SYSCALL) += disasm.o

kernel/bpf/task_iter.c

Lines changed: 333 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,333 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/* Copyright (c) 2020 Facebook */
3+
4+
#include <linux/init.h>
5+
#include <linux/namei.h>
6+
#include <linux/pid_namespace.h>
7+
#include <linux/fs.h>
8+
#include <linux/fdtable.h>
9+
#include <linux/filter.h>
10+
11+
struct bpf_iter_seq_task_common {
12+
struct pid_namespace *ns;
13+
};
14+
15+
struct bpf_iter_seq_task_info {
16+
/* The first field must be struct bpf_iter_seq_task_common.
17+
* this is assumed by {init, fini}_seq_pidns() callback functions.
18+
*/
19+
struct bpf_iter_seq_task_common common;
20+
u32 tid;
21+
};
22+
23+
static struct task_struct *task_seq_get_next(struct pid_namespace *ns,
24+
u32 *tid)
25+
{
26+
struct task_struct *task = NULL;
27+
struct pid *pid;
28+
29+
rcu_read_lock();
30+
pid = idr_get_next(&ns->idr, tid);
31+
if (pid)
32+
task = get_pid_task(pid, PIDTYPE_PID);
33+
rcu_read_unlock();
34+
35+
return task;
36+
}
37+
38+
static void *task_seq_start(struct seq_file *seq, loff_t *pos)
39+
{
40+
struct bpf_iter_seq_task_info *info = seq->private;
41+
struct task_struct *task;
42+
43+
task = task_seq_get_next(info->common.ns, &info->tid);
44+
if (!task)
45+
return NULL;
46+
47+
++*pos;
48+
return task;
49+
}
50+
51+
static void *task_seq_next(struct seq_file *seq, void *v, loff_t *pos)
52+
{
53+
struct bpf_iter_seq_task_info *info = seq->private;
54+
struct task_struct *task;
55+
56+
++*pos;
57+
++info->tid;
58+
put_task_struct((struct task_struct *)v);
59+
task = task_seq_get_next(info->common.ns, &info->tid);
60+
if (!task)
61+
return NULL;
62+
63+
return task;
64+
}
65+
66+
struct bpf_iter__task {
67+
__bpf_md_ptr(struct bpf_iter_meta *, meta);
68+
__bpf_md_ptr(struct task_struct *, task);
69+
};
70+
71+
DEFINE_BPF_ITER_FUNC(task, struct bpf_iter_meta *meta, struct task_struct *task)
72+
73+
static int __task_seq_show(struct seq_file *seq, struct task_struct *task,
74+
bool in_stop)
75+
{
76+
struct bpf_iter_meta meta;
77+
struct bpf_iter__task ctx;
78+
struct bpf_prog *prog;
79+
80+
meta.seq = seq;
81+
prog = bpf_iter_get_info(&meta, in_stop);
82+
if (!prog)
83+
return 0;
84+
85+
meta.seq = seq;
86+
ctx.meta = &meta;
87+
ctx.task = task;
88+
return bpf_iter_run_prog(prog, &ctx);
89+
}
90+
91+
static int task_seq_show(struct seq_file *seq, void *v)
92+
{
93+
return __task_seq_show(seq, v, false);
94+
}
95+
96+
static void task_seq_stop(struct seq_file *seq, void *v)
97+
{
98+
if (!v)
99+
(void)__task_seq_show(seq, v, true);
100+
else
101+
put_task_struct((struct task_struct *)v);
102+
}
103+
104+
static const struct seq_operations task_seq_ops = {
105+
.start = task_seq_start,
106+
.next = task_seq_next,
107+
.stop = task_seq_stop,
108+
.show = task_seq_show,
109+
};
110+
111+
struct bpf_iter_seq_task_file_info {
112+
/* The first field must be struct bpf_iter_seq_task_common.
113+
* this is assumed by {init, fini}_seq_pidns() callback functions.
114+
*/
115+
struct bpf_iter_seq_task_common common;
116+
struct task_struct *task;
117+
struct files_struct *files;
118+
u32 tid;
119+
u32 fd;
120+
};
121+
122+
static struct file *
123+
task_file_seq_get_next(struct bpf_iter_seq_task_file_info *info,
124+
struct task_struct **task, struct files_struct **fstruct)
125+
{
126+
struct pid_namespace *ns = info->common.ns;
127+
u32 curr_tid = info->tid, max_fds;
128+
struct files_struct *curr_files;
129+
struct task_struct *curr_task;
130+
int curr_fd = info->fd;
131+
132+
/* If this function returns a non-NULL file object,
133+
* it held a reference to the task/files_struct/file.
134+
* Otherwise, it does not hold any reference.
135+
*/
136+
again:
137+
if (*task) {
138+
curr_task = *task;
139+
curr_files = *fstruct;
140+
curr_fd = info->fd;
141+
} else {
142+
curr_task = task_seq_get_next(ns, &curr_tid);
143+
if (!curr_task)
144+
return NULL;
145+
146+
curr_files = get_files_struct(curr_task);
147+
if (!curr_files) {
148+
put_task_struct(curr_task);
149+
curr_tid = ++(info->tid);
150+
info->fd = 0;
151+
goto again;
152+
}
153+
154+
/* set *fstruct, *task and info->tid */
155+
*fstruct = curr_files;
156+
*task = curr_task;
157+
if (curr_tid == info->tid) {
158+
curr_fd = info->fd;
159+
} else {
160+
info->tid = curr_tid;
161+
curr_fd = 0;
162+
}
163+
}
164+
165+
rcu_read_lock();
166+
max_fds = files_fdtable(curr_files)->max_fds;
167+
for (; curr_fd < max_fds; curr_fd++) {
168+
struct file *f;
169+
170+
f = fcheck_files(curr_files, curr_fd);
171+
if (!f)
172+
continue;
173+
174+
/* set info->fd */
175+
info->fd = curr_fd;
176+
get_file(f);
177+
rcu_read_unlock();
178+
return f;
179+
}
180+
181+
/* the current task is done, go to the next task */
182+
rcu_read_unlock();
183+
put_files_struct(curr_files);
184+
put_task_struct(curr_task);
185+
*task = NULL;
186+
*fstruct = NULL;
187+
info->fd = 0;
188+
curr_tid = ++(info->tid);
189+
goto again;
190+
}
191+
192+
static void *task_file_seq_start(struct seq_file *seq, loff_t *pos)
193+
{
194+
struct bpf_iter_seq_task_file_info *info = seq->private;
195+
struct files_struct *files = NULL;
196+
struct task_struct *task = NULL;
197+
struct file *file;
198+
199+
file = task_file_seq_get_next(info, &task, &files);
200+
if (!file) {
201+
info->files = NULL;
202+
info->task = NULL;
203+
return NULL;
204+
}
205+
206+
++*pos;
207+
info->task = task;
208+
info->files = files;
209+
210+
return file;
211+
}
212+
213+
static void *task_file_seq_next(struct seq_file *seq, void *v, loff_t *pos)
214+
{
215+
struct bpf_iter_seq_task_file_info *info = seq->private;
216+
struct files_struct *files = info->files;
217+
struct task_struct *task = info->task;
218+
struct file *file;
219+
220+
++*pos;
221+
++info->fd;
222+
fput((struct file *)v);
223+
file = task_file_seq_get_next(info, &task, &files);
224+
if (!file) {
225+
info->files = NULL;
226+
info->task = NULL;
227+
return NULL;
228+
}
229+
230+
info->task = task;
231+
info->files = files;
232+
233+
return file;
234+
}
235+
236+
struct bpf_iter__task_file {
237+
__bpf_md_ptr(struct bpf_iter_meta *, meta);
238+
__bpf_md_ptr(struct task_struct *, task);
239+
u32 fd __aligned(8);
240+
__bpf_md_ptr(struct file *, file);
241+
};
242+
243+
DEFINE_BPF_ITER_FUNC(task_file, struct bpf_iter_meta *meta,
244+
struct task_struct *task, u32 fd,
245+
struct file *file)
246+
247+
static int __task_file_seq_show(struct seq_file *seq, struct file *file,
248+
bool in_stop)
249+
{
250+
struct bpf_iter_seq_task_file_info *info = seq->private;
251+
struct bpf_iter__task_file ctx;
252+
struct bpf_iter_meta meta;
253+
struct bpf_prog *prog;
254+
255+
meta.seq = seq;
256+
prog = bpf_iter_get_info(&meta, in_stop);
257+
if (!prog)
258+
return 0;
259+
260+
ctx.meta = &meta;
261+
ctx.task = info->task;
262+
ctx.fd = info->fd;
263+
ctx.file = file;
264+
return bpf_iter_run_prog(prog, &ctx);
265+
}
266+
267+
static int task_file_seq_show(struct seq_file *seq, void *v)
268+
{
269+
return __task_file_seq_show(seq, v, false);
270+
}
271+
272+
static void task_file_seq_stop(struct seq_file *seq, void *v)
273+
{
274+
struct bpf_iter_seq_task_file_info *info = seq->private;
275+
276+
if (!v) {
277+
(void)__task_file_seq_show(seq, v, true);
278+
} else {
279+
fput((struct file *)v);
280+
put_files_struct(info->files);
281+
put_task_struct(info->task);
282+
info->files = NULL;
283+
info->task = NULL;
284+
}
285+
}
286+
287+
static int init_seq_pidns(void *priv_data)
288+
{
289+
struct bpf_iter_seq_task_common *common = priv_data;
290+
291+
common->ns = get_pid_ns(task_active_pid_ns(current));
292+
return 0;
293+
}
294+
295+
static void fini_seq_pidns(void *priv_data)
296+
{
297+
struct bpf_iter_seq_task_common *common = priv_data;
298+
299+
put_pid_ns(common->ns);
300+
}
301+
302+
static const struct seq_operations task_file_seq_ops = {
303+
.start = task_file_seq_start,
304+
.next = task_file_seq_next,
305+
.stop = task_file_seq_stop,
306+
.show = task_file_seq_show,
307+
};
308+
309+
static int __init task_iter_init(void)
310+
{
311+
struct bpf_iter_reg task_file_reg_info = {
312+
.target = "task_file",
313+
.seq_ops = &task_file_seq_ops,
314+
.init_seq_private = init_seq_pidns,
315+
.fini_seq_private = fini_seq_pidns,
316+
.seq_priv_size = sizeof(struct bpf_iter_seq_task_file_info),
317+
};
318+
struct bpf_iter_reg task_reg_info = {
319+
.target = "task",
320+
.seq_ops = &task_seq_ops,
321+
.init_seq_private = init_seq_pidns,
322+
.fini_seq_private = fini_seq_pidns,
323+
.seq_priv_size = sizeof(struct bpf_iter_seq_task_info),
324+
};
325+
int ret;
326+
327+
ret = bpf_iter_reg_target(&task_reg_info);
328+
if (ret)
329+
return ret;
330+
331+
return bpf_iter_reg_target(&task_file_reg_info);
332+
}
333+
late_initcall(task_iter_init);

0 commit comments

Comments
 (0)