diff options
Diffstat (limited to 'kernel/bpf/inode.c')
| -rw-r--r-- | kernel/bpf/inode.c | 182 | 
1 files changed, 164 insertions, 18 deletions
diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c index bf6da59ae0d0..76efe9a183f5 100644 --- a/kernel/bpf/inode.c +++ b/kernel/bpf/inode.c @@ -150,8 +150,163 @@ static int bpf_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)  	return 0;  } +struct map_iter { +	void *key; +	bool done; +}; + +static struct map_iter *map_iter(struct seq_file *m) +{ +	return m->private; +} + +static struct bpf_map *seq_file_to_map(struct seq_file *m) +{ +	return file_inode(m->file)->i_private; +} + +static void map_iter_free(struct map_iter *iter) +{ +	if (iter) { +		kfree(iter->key); +		kfree(iter); +	} +} + +static struct map_iter *map_iter_alloc(struct bpf_map *map) +{ +	struct map_iter *iter; + +	iter = kzalloc(sizeof(*iter), GFP_KERNEL | __GFP_NOWARN); +	if (!iter) +		goto error; + +	iter->key = kzalloc(map->key_size, GFP_KERNEL | __GFP_NOWARN); +	if (!iter->key) +		goto error; + +	return iter; + +error: +	map_iter_free(iter); +	return NULL; +} + +static void *map_seq_next(struct seq_file *m, void *v, loff_t *pos) +{ +	struct bpf_map *map = seq_file_to_map(m); +	void *key = map_iter(m)->key; + +	if (map_iter(m)->done) +		return NULL; + +	if (unlikely(v == SEQ_START_TOKEN)) +		goto done; + +	if (map->ops->map_get_next_key(map, key, key)) { +		map_iter(m)->done = true; +		return NULL; +	} + +done: +	++(*pos); +	return key; +} + +static void *map_seq_start(struct seq_file *m, loff_t *pos) +{ +	if (map_iter(m)->done) +		return NULL; + +	return *pos ? map_iter(m)->key : SEQ_START_TOKEN; +} + +static void map_seq_stop(struct seq_file *m, void *v) +{ +} + +static int map_seq_show(struct seq_file *m, void *v) +{ +	struct bpf_map *map = seq_file_to_map(m); +	void *key = map_iter(m)->key; + +	if (unlikely(v == SEQ_START_TOKEN)) { +		seq_puts(m, "# WARNING!! The output is for debug purpose only\n"); +		seq_puts(m, "# WARNING!! The output format will change\n"); +	} else { +		map->ops->map_seq_show_elem(map, key, m); +	} + +	return 0; +} + +static const struct seq_operations bpffs_map_seq_ops = { +	.start	= map_seq_start, +	.next	= map_seq_next, +	.show	= map_seq_show, +	.stop	= map_seq_stop, +}; + +static int bpffs_map_open(struct inode *inode, struct file *file) +{ +	struct bpf_map *map = inode->i_private; +	struct map_iter *iter; +	struct seq_file *m; +	int err; + +	iter = map_iter_alloc(map); +	if (!iter) +		return -ENOMEM; + +	err = seq_open(file, &bpffs_map_seq_ops); +	if (err) { +		map_iter_free(iter); +		return err; +	} + +	m = file->private_data; +	m->private = iter; + +	return 0; +} + +static int bpffs_map_release(struct inode *inode, struct file *file) +{ +	struct seq_file *m = file->private_data; + +	map_iter_free(map_iter(m)); + +	return seq_release(inode, file); +} + +/* bpffs_map_fops should only implement the basic + * read operation for a BPF map.  The purpose is to + * provide a simple user intuitive way to do + * "cat bpffs/pathto/a-pinned-map". + * + * Other operations (e.g. write, lookup...) should be realized by + * the userspace tools (e.g. bpftool) through the + * BPF_OBJ_GET_INFO_BY_FD and the map's lookup/update + * interface. + */ +static const struct file_operations bpffs_map_fops = { +	.open		= bpffs_map_open, +	.read		= seq_read, +	.release	= bpffs_map_release, +}; + +static int bpffs_obj_open(struct inode *inode, struct file *file) +{ +	return -EIO; +} + +static const struct file_operations bpffs_obj_fops = { +	.open		= bpffs_obj_open, +}; +  static int bpf_mkobj_ops(struct dentry *dentry, umode_t mode, void *raw, -			 const struct inode_operations *iops) +			 const struct inode_operations *iops, +			 const struct file_operations *fops)  {  	struct inode *dir = dentry->d_parent->d_inode;  	struct inode *inode = bpf_get_inode(dir->i_sb, dir, mode); @@ -159,6 +314,7 @@ static int bpf_mkobj_ops(struct dentry *dentry, umode_t mode, void *raw,  		return PTR_ERR(inode);  	inode->i_op = iops; +	inode->i_fop = fops;  	inode->i_private = raw;  	bpf_dentry_finalize(dentry, inode, dir); @@ -167,12 +323,16 @@ static int bpf_mkobj_ops(struct dentry *dentry, umode_t mode, void *raw,  static int bpf_mkprog(struct dentry *dentry, umode_t mode, void *arg)  { -	return bpf_mkobj_ops(dentry, mode, arg, &bpf_prog_iops); +	return bpf_mkobj_ops(dentry, mode, arg, &bpf_prog_iops, +			     &bpffs_obj_fops);  }  static int bpf_mkmap(struct dentry *dentry, umode_t mode, void *arg)  { -	return bpf_mkobj_ops(dentry, mode, arg, &bpf_map_iops); +	struct bpf_map *map = arg; + +	return bpf_mkobj_ops(dentry, mode, arg, &bpf_map_iops, +			     map->btf ? &bpffs_map_fops : &bpffs_obj_fops);  }  static struct dentry * @@ -279,13 +439,6 @@ int bpf_obj_pin_user(u32 ufd, const char __user *pathname)  	ret = bpf_obj_do_pin(pname, raw, type);  	if (ret != 0)  		bpf_any_put(raw, type); -	if ((trace_bpf_obj_pin_prog_enabled() || -	     trace_bpf_obj_pin_map_enabled()) && !ret) { -		if (type == BPF_TYPE_PROG) -			trace_bpf_obj_pin_prog(raw, ufd, pname); -		if (type == BPF_TYPE_MAP) -			trace_bpf_obj_pin_map(raw, ufd, pname); -	}  out:  	putname(pname);  	return ret; @@ -352,15 +505,8 @@ int bpf_obj_get_user(const char __user *pathname, int flags)  	else  		goto out; -	if (ret < 0) { +	if (ret < 0)  		bpf_any_put(raw, type); -	} else if (trace_bpf_obj_get_prog_enabled() || -		   trace_bpf_obj_get_map_enabled()) { -		if (type == BPF_TYPE_PROG) -			trace_bpf_obj_get_prog(raw, ret, pname); -		if (type == BPF_TYPE_MAP) -			trace_bpf_obj_get_map(raw, ret, pname); -	}  out:  	putname(pname);  	return ret;  | 
