2020-05-09 20:59:11 +03:00
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2020 Facebook */
# include <linux/init.h>
# include <linux/namei.h>
# include <linux/pid_namespace.h>
# include <linux/fs.h>
# include <linux/fdtable.h>
# include <linux/filter.h>
struct bpf_iter_seq_task_common {
struct pid_namespace * ns ;
} ;
struct bpf_iter_seq_task_info {
/* The first field must be struct bpf_iter_seq_task_common.
* this is assumed by { init , fini } _seq_pidns ( ) callback functions .
*/
struct bpf_iter_seq_task_common common ;
u32 tid ;
} ;
static struct task_struct * task_seq_get_next ( struct pid_namespace * ns ,
u32 * tid )
{
struct task_struct * task = NULL ;
struct pid * pid ;
rcu_read_lock ( ) ;
2020-05-14 08:51:37 +03:00
retry :
2020-05-09 20:59:11 +03:00
pid = idr_get_next ( & ns - > idr , tid ) ;
2020-05-14 08:51:37 +03:00
if ( pid ) {
2020-05-09 20:59:11 +03:00
task = get_pid_task ( pid , PIDTYPE_PID ) ;
2020-05-14 08:51:37 +03:00
if ( ! task ) {
+ + * tid ;
goto retry ;
}
}
2020-05-09 20:59:11 +03:00
rcu_read_unlock ( ) ;
return task ;
}
static void * task_seq_start ( struct seq_file * seq , loff_t * pos )
{
struct bpf_iter_seq_task_info * info = seq - > private ;
struct task_struct * task ;
task = task_seq_get_next ( info - > common . ns , & info - > tid ) ;
if ( ! task )
return NULL ;
+ + * pos ;
return task ;
}
static void * task_seq_next ( struct seq_file * seq , void * v , loff_t * pos )
{
struct bpf_iter_seq_task_info * info = seq - > private ;
struct task_struct * task ;
+ + * pos ;
+ + info - > tid ;
put_task_struct ( ( struct task_struct * ) v ) ;
task = task_seq_get_next ( info - > common . ns , & info - > tid ) ;
if ( ! task )
return NULL ;
return task ;
}
struct bpf_iter__task {
__bpf_md_ptr ( struct bpf_iter_meta * , meta ) ;
__bpf_md_ptr ( struct task_struct * , task ) ;
} ;
DEFINE_BPF_ITER_FUNC ( task , struct bpf_iter_meta * meta , struct task_struct * task )
static int __task_seq_show ( struct seq_file * seq , struct task_struct * task ,
bool in_stop )
{
struct bpf_iter_meta meta ;
struct bpf_iter__task ctx ;
struct bpf_prog * prog ;
meta . seq = seq ;
prog = bpf_iter_get_info ( & meta , in_stop ) ;
if ( ! prog )
return 0 ;
meta . seq = seq ;
ctx . meta = & meta ;
ctx . task = task ;
return bpf_iter_run_prog ( prog , & ctx ) ;
}
static int task_seq_show ( struct seq_file * seq , void * v )
{
return __task_seq_show ( seq , v , false ) ;
}
static void task_seq_stop ( struct seq_file * seq , void * v )
{
if ( ! v )
( void ) __task_seq_show ( seq , v , true ) ;
else
put_task_struct ( ( struct task_struct * ) v ) ;
}
static const struct seq_operations task_seq_ops = {
. start = task_seq_start ,
. next = task_seq_next ,
. stop = task_seq_stop ,
. show = task_seq_show ,
} ;
struct bpf_iter_seq_task_file_info {
/* The first field must be struct bpf_iter_seq_task_common.
* this is assumed by { init , fini } _seq_pidns ( ) callback functions .
*/
struct bpf_iter_seq_task_common common ;
struct task_struct * task ;
struct files_struct * files ;
u32 tid ;
u32 fd ;
} ;
static struct file *
task_file_seq_get_next ( struct bpf_iter_seq_task_file_info * info ,
struct task_struct * * task , struct files_struct * * fstruct )
{
struct pid_namespace * ns = info - > common . ns ;
u32 curr_tid = info - > tid , max_fds ;
struct files_struct * curr_files ;
struct task_struct * curr_task ;
int curr_fd = info - > fd ;
/* If this function returns a non-NULL file object,
* it held a reference to the task / files_struct / file .
* Otherwise , it does not hold any reference .
*/
again :
if ( * task ) {
curr_task = * task ;
curr_files = * fstruct ;
curr_fd = info - > fd ;
} else {
curr_task = task_seq_get_next ( ns , & curr_tid ) ;
if ( ! curr_task )
return NULL ;
curr_files = get_files_struct ( curr_task ) ;
if ( ! curr_files ) {
put_task_struct ( curr_task ) ;
curr_tid = + + ( info - > tid ) ;
info - > fd = 0 ;
goto again ;
}
/* set *fstruct, *task and info->tid */
* fstruct = curr_files ;
* task = curr_task ;
if ( curr_tid = = info - > tid ) {
curr_fd = info - > fd ;
} else {
info - > tid = curr_tid ;
curr_fd = 0 ;
}
}
rcu_read_lock ( ) ;
max_fds = files_fdtable ( curr_files ) - > max_fds ;
for ( ; curr_fd < max_fds ; curr_fd + + ) {
struct file * f ;
f = fcheck_files ( curr_files , curr_fd ) ;
if ( ! f )
continue ;
/* set info->fd */
info - > fd = curr_fd ;
get_file ( f ) ;
rcu_read_unlock ( ) ;
return f ;
}
/* the current task is done, go to the next task */
rcu_read_unlock ( ) ;
put_files_struct ( curr_files ) ;
put_task_struct ( curr_task ) ;
* task = NULL ;
* fstruct = NULL ;
info - > fd = 0 ;
curr_tid = + + ( info - > tid ) ;
goto again ;
}
static void * task_file_seq_start ( struct seq_file * seq , loff_t * pos )
{
struct bpf_iter_seq_task_file_info * info = seq - > private ;
struct files_struct * files = NULL ;
struct task_struct * task = NULL ;
struct file * file ;
file = task_file_seq_get_next ( info , & task , & files ) ;
if ( ! file ) {
info - > files = NULL ;
info - > task = NULL ;
return NULL ;
}
+ + * pos ;
info - > task = task ;
info - > files = files ;
return file ;
}
static void * task_file_seq_next ( struct seq_file * seq , void * v , loff_t * pos )
{
struct bpf_iter_seq_task_file_info * info = seq - > private ;
struct files_struct * files = info - > files ;
struct task_struct * task = info - > task ;
struct file * file ;
+ + * pos ;
+ + info - > fd ;
fput ( ( struct file * ) v ) ;
file = task_file_seq_get_next ( info , & task , & files ) ;
if ( ! file ) {
info - > files = NULL ;
info - > task = NULL ;
return NULL ;
}
info - > task = task ;
info - > files = files ;
return file ;
}
struct bpf_iter__task_file {
__bpf_md_ptr ( struct bpf_iter_meta * , meta ) ;
__bpf_md_ptr ( struct task_struct * , task ) ;
u32 fd __aligned ( 8 ) ;
__bpf_md_ptr ( struct file * , file ) ;
} ;
DEFINE_BPF_ITER_FUNC ( task_file , struct bpf_iter_meta * meta ,
struct task_struct * task , u32 fd ,
struct file * file )
static int __task_file_seq_show ( struct seq_file * seq , struct file * file ,
bool in_stop )
{
struct bpf_iter_seq_task_file_info * info = seq - > private ;
struct bpf_iter__task_file ctx ;
struct bpf_iter_meta meta ;
struct bpf_prog * prog ;
meta . seq = seq ;
prog = bpf_iter_get_info ( & meta , in_stop ) ;
if ( ! prog )
return 0 ;
ctx . meta = & meta ;
ctx . task = info - > task ;
ctx . fd = info - > fd ;
ctx . file = file ;
return bpf_iter_run_prog ( prog , & ctx ) ;
}
static int task_file_seq_show ( struct seq_file * seq , void * v )
{
return __task_file_seq_show ( seq , v , false ) ;
}
static void task_file_seq_stop ( struct seq_file * seq , void * v )
{
struct bpf_iter_seq_task_file_info * info = seq - > private ;
if ( ! v ) {
( void ) __task_file_seq_show ( seq , v , true ) ;
} else {
fput ( ( struct file * ) v ) ;
put_files_struct ( info - > files ) ;
put_task_struct ( info - > task ) ;
info - > files = NULL ;
info - > task = NULL ;
}
}
static int init_seq_pidns ( void * priv_data )
{
struct bpf_iter_seq_task_common * common = priv_data ;
common - > ns = get_pid_ns ( task_active_pid_ns ( current ) ) ;
return 0 ;
}
static void fini_seq_pidns ( void * priv_data )
{
struct bpf_iter_seq_task_common * common = priv_data ;
put_pid_ns ( common - > ns ) ;
}
static const struct seq_operations task_file_seq_ops = {
. start = task_file_seq_start ,
. next = task_file_seq_next ,
. stop = task_file_seq_stop ,
. show = task_file_seq_show ,
} ;
2020-05-13 21:02:19 +03:00
static const struct bpf_iter_reg task_reg_info = {
. target = " task " ,
. seq_ops = & task_seq_ops ,
. init_seq_private = init_seq_pidns ,
. fini_seq_private = fini_seq_pidns ,
. seq_priv_size = sizeof ( struct bpf_iter_seq_task_info ) ,
2020-05-13 21:02:21 +03:00
. ctx_arg_info_size = 1 ,
. ctx_arg_info = {
{ offsetof ( struct bpf_iter__task , task ) ,
PTR_TO_BTF_ID_OR_NULL } ,
} ,
2020-05-13 21:02:19 +03:00
} ;
static const struct bpf_iter_reg task_file_reg_info = {
. target = " task_file " ,
. seq_ops = & task_file_seq_ops ,
. init_seq_private = init_seq_pidns ,
. fini_seq_private = fini_seq_pidns ,
. seq_priv_size = sizeof ( struct bpf_iter_seq_task_file_info ) ,
2020-05-13 21:02:21 +03:00
. ctx_arg_info_size = 2 ,
. ctx_arg_info = {
{ offsetof ( struct bpf_iter__task_file , task ) ,
PTR_TO_BTF_ID_OR_NULL } ,
{ offsetof ( struct bpf_iter__task_file , file ) ,
PTR_TO_BTF_ID_OR_NULL } ,
} ,
2020-05-13 21:02:19 +03:00
} ;
2020-05-09 20:59:11 +03:00
static int __init task_iter_init ( void )
{
int ret ;
ret = bpf_iter_reg_target ( & task_reg_info ) ;
if ( ret )
return ret ;
return bpf_iter_reg_target ( & task_file_reg_info ) ;
}
late_initcall ( task_iter_init ) ;