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