Merge branch 'bpf-type-format'
Martin KaFai Lau says: ==================== This patch introduces BPF Type Format (BTF). BTF (BPF Type Format) is the meta data format which describes the data types of BPF program/map. Hence, it basically focus on the C programming language which the modern BPF is primary using. The first use case is to provide a generic pretty print capability for a BPF map. A modified pahole that can convert dwarf to BTF is here: https://github.com/iamkafai/pahole/tree/btf Please see individual patch for details. v5: - Remove BTF_KIND_FLOAT and BTF_KIND_FUNC which are not currently used. They can be added in the future. Some bpf_df_xxx() are removed together. - Add comment in patch 7 to clarify that the new bpffs_map_fops should not be extended further. v4: - Fix warning (remove unneeded semicolon) - Remove a redundant variable (nr_bytes) from btf_int_check_meta() in patch 1. Caught by W=1. v3: - Rebase to bpf-next - Fix sparse warning (by adding static) - Add BTF header logging: btf_verifier_log_hdr() - Fix the alignment test on btf->type_off - Add tests for the BTF header - Lower the max BTF size to 16MB. It should be enough for some time. We could raise it later if it would be needed. v2: - Use kvfree where needed in patch 1 and 2 - Also consider BTF_INT_OFFSET() in the btf_int_check_meta() in patch 1 - Fix an incorrect goto target in map_create() during the btf-error-path in patch 7 - re-org some local vars to keep the rev xmas tree in btf.c ==================== Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
This commit is contained in:
commit
34df9d37ab
|
@ -22,6 +22,8 @@ struct perf_event;
|
|||
struct bpf_prog;
|
||||
struct bpf_map;
|
||||
struct sock;
|
||||
struct seq_file;
|
||||
struct btf;
|
||||
|
||||
/* map is generic key/value storage optionally accesible by eBPF programs */
|
||||
struct bpf_map_ops {
|
||||
|
@ -43,10 +45,14 @@ struct bpf_map_ops {
|
|||
void (*map_fd_put_ptr)(void *ptr);
|
||||
u32 (*map_gen_lookup)(struct bpf_map *map, struct bpf_insn *insn_buf);
|
||||
u32 (*map_fd_sys_lookup_elem)(void *ptr);
|
||||
void (*map_seq_show_elem)(struct bpf_map *map, void *key,
|
||||
struct seq_file *m);
|
||||
int (*map_check_btf)(const struct bpf_map *map, const struct btf *btf,
|
||||
u32 key_type_id, u32 value_type_id);
|
||||
};
|
||||
|
||||
struct bpf_map {
|
||||
/* 1st cacheline with read-mostly members of which some
|
||||
/* The first two cachelines with read-mostly members of which some
|
||||
* are also accessed in fast-path (e.g. ops, max_entries).
|
||||
*/
|
||||
const struct bpf_map_ops *ops ____cacheline_aligned;
|
||||
|
@ -62,10 +68,13 @@ struct bpf_map {
|
|||
u32 pages;
|
||||
u32 id;
|
||||
int numa_node;
|
||||
u32 btf_key_id;
|
||||
u32 btf_value_id;
|
||||
struct btf *btf;
|
||||
bool unpriv_array;
|
||||
/* 7 bytes hole */
|
||||
/* 55 bytes hole */
|
||||
|
||||
/* 2nd cacheline with misc members to avoid false sharing
|
||||
/* The 3rd and 4th cacheline with misc members to avoid false sharing
|
||||
* particularly with refcounting.
|
||||
*/
|
||||
struct user_struct *user ____cacheline_aligned;
|
||||
|
@ -100,6 +109,11 @@ static inline struct bpf_offloaded_map *map_to_offmap(struct bpf_map *map)
|
|||
return container_of(map, struct bpf_offloaded_map, map);
|
||||
}
|
||||
|
||||
static inline bool bpf_map_support_seq_show(const struct bpf_map *map)
|
||||
{
|
||||
return map->ops->map_seq_show_elem && map->ops->map_check_btf;
|
||||
}
|
||||
|
||||
extern const struct bpf_map_ops bpf_map_offload_ops;
|
||||
|
||||
/* function argument constraints */
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2018 Facebook */
|
||||
|
||||
#ifndef _LINUX_BTF_H
|
||||
#define _LINUX_BTF_H 1
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct btf;
|
||||
struct btf_type;
|
||||
union bpf_attr;
|
||||
|
||||
extern const struct file_operations btf_fops;
|
||||
|
||||
void btf_put(struct btf *btf);
|
||||
int btf_new_fd(const union bpf_attr *attr);
|
||||
struct btf *btf_get_by_fd(int fd);
|
||||
int btf_get_info_by_fd(const struct btf *btf,
|
||||
const union bpf_attr *attr,
|
||||
union bpf_attr __user *uattr);
|
||||
/* Figure out the size of a type_id. If type_id is a modifier
|
||||
* (e.g. const), it will be resolved to find out the type with size.
|
||||
*
|
||||
* For example:
|
||||
* In describing "const void *", type_id is "const" and "const"
|
||||
* refers to "void *". The return type will be "void *".
|
||||
*
|
||||
* If type_id is a simple "int", then return type will be "int".
|
||||
*
|
||||
* @btf: struct btf object
|
||||
* @type_id: Find out the size of type_id. The type_id of the return
|
||||
* type is set to *type_id.
|
||||
* @ret_size: It can be NULL. If not NULL, the size of the return
|
||||
* type is set to *ret_size.
|
||||
* Return: The btf_type (resolved to another type with size info if needed).
|
||||
* NULL is returned if type_id itself does not have size info
|
||||
* (e.g. void) or it cannot be resolved to another type that
|
||||
* has size info.
|
||||
* *type_id and *ret_size will not be changed in the
|
||||
* NULL return case.
|
||||
*/
|
||||
const struct btf_type *btf_type_id_size(const struct btf *btf,
|
||||
u32 *type_id,
|
||||
u32 *ret_size);
|
||||
void btf_type_seq_show(const struct btf *btf, u32 type_id, void *obj,
|
||||
struct seq_file *m);
|
||||
|
||||
#endif
|
|
@ -95,6 +95,7 @@ enum bpf_cmd {
|
|||
BPF_OBJ_GET_INFO_BY_FD,
|
||||
BPF_PROG_QUERY,
|
||||
BPF_RAW_TRACEPOINT_OPEN,
|
||||
BPF_BTF_LOAD,
|
||||
};
|
||||
|
||||
enum bpf_map_type {
|
||||
|
@ -279,6 +280,9 @@ union bpf_attr {
|
|||
*/
|
||||
char map_name[BPF_OBJ_NAME_LEN];
|
||||
__u32 map_ifindex; /* ifindex of netdev to create on */
|
||||
__u32 btf_fd; /* fd pointing to a BTF type data */
|
||||
__u32 btf_key_id; /* BTF type_id of the key */
|
||||
__u32 btf_value_id; /* BTF type_id of the value */
|
||||
};
|
||||
|
||||
struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */
|
||||
|
@ -363,6 +367,14 @@ union bpf_attr {
|
|||
__u64 name;
|
||||
__u32 prog_fd;
|
||||
} raw_tracepoint;
|
||||
|
||||
struct { /* anonymous struct for BPF_BTF_LOAD */
|
||||
__aligned_u64 btf;
|
||||
__aligned_u64 btf_log_buf;
|
||||
__u32 btf_size;
|
||||
__u32 btf_log_size;
|
||||
__u32 btf_log_level;
|
||||
};
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
/* BPF helper function descriptions:
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/* Copyright (c) 2018 Facebook */
|
||||
#ifndef _UAPI__LINUX_BTF_H__
|
||||
#define _UAPI__LINUX_BTF_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define BTF_MAGIC 0xeB9F
|
||||
#define BTF_MAGIC_SWAP 0x9FeB
|
||||
#define BTF_VERSION 1
|
||||
#define BTF_FLAGS_COMPR 0x01
|
||||
|
||||
struct btf_header {
|
||||
__u16 magic;
|
||||
__u8 version;
|
||||
__u8 flags;
|
||||
|
||||
__u32 parent_label;
|
||||
__u32 parent_name;
|
||||
|
||||
/* All offsets are in bytes relative to the end of this header */
|
||||
__u32 label_off; /* offset of label section */
|
||||
__u32 object_off; /* offset of data object section*/
|
||||
__u32 func_off; /* offset of function section */
|
||||
__u32 type_off; /* offset of type section */
|
||||
__u32 str_off; /* offset of string section */
|
||||
__u32 str_len; /* length of string section */
|
||||
};
|
||||
|
||||
/* Max # of type identifier */
|
||||
#define BTF_MAX_TYPE 0x7fffffff
|
||||
/* Max offset into the string section */
|
||||
#define BTF_MAX_NAME_OFFSET 0x7fffffff
|
||||
/* Max # of struct/union/enum members or func args */
|
||||
#define BTF_MAX_VLEN 0xffff
|
||||
|
||||
/* The type id is referring to a parent BTF */
|
||||
#define BTF_TYPE_PARENT(id) (((id) >> 31) & 0x1)
|
||||
#define BTF_TYPE_ID(id) ((id) & BTF_MAX_TYPE)
|
||||
|
||||
/* String is in the ELF string section */
|
||||
#define BTF_STR_TBL_ELF_ID(ref) (((ref) >> 31) & 0x1)
|
||||
#define BTF_STR_OFFSET(ref) ((ref) & BTF_MAX_NAME_OFFSET)
|
||||
|
||||
struct btf_type {
|
||||
__u32 name;
|
||||
/* "info" bits arrangement
|
||||
* bits 0-15: vlen (e.g. # of struct's members)
|
||||
* bits 16-23: unused
|
||||
* bits 24-28: kind (e.g. int, ptr, array...etc)
|
||||
* bits 29-30: unused
|
||||
* bits 31: root
|
||||
*/
|
||||
__u32 info;
|
||||
/* "size" is used by INT, ENUM, STRUCT and UNION.
|
||||
* "size" tells the size of the type it is describing.
|
||||
*
|
||||
* "type" is used by PTR, TYPEDEF, VOLATILE, CONST and RESTRICT.
|
||||
* "type" is a type_id referring to another type.
|
||||
*/
|
||||
union {
|
||||
__u32 size;
|
||||
__u32 type;
|
||||
};
|
||||
};
|
||||
|
||||
#define BTF_INFO_KIND(info) (((info) >> 24) & 0x1f)
|
||||
#define BTF_INFO_ISROOT(info) (!!(((info) >> 24) & 0x80))
|
||||
#define BTF_INFO_VLEN(info) ((info) & 0xffff)
|
||||
|
||||
#define BTF_KIND_UNKN 0 /* Unknown */
|
||||
#define BTF_KIND_INT 1 /* Integer */
|
||||
#define BTF_KIND_PTR 2 /* Pointer */
|
||||
#define BTF_KIND_ARRAY 3 /* Array */
|
||||
#define BTF_KIND_STRUCT 4 /* Struct */
|
||||
#define BTF_KIND_UNION 5 /* Union */
|
||||
#define BTF_KIND_ENUM 6 /* Enumeration */
|
||||
#define BTF_KIND_FWD 7 /* Forward */
|
||||
#define BTF_KIND_TYPEDEF 8 /* Typedef */
|
||||
#define BTF_KIND_VOLATILE 9 /* Volatile */
|
||||
#define BTF_KIND_CONST 10 /* Const */
|
||||
#define BTF_KIND_RESTRICT 11 /* Restrict */
|
||||
#define BTF_KIND_MAX 11
|
||||
#define NR_BTF_KINDS 12
|
||||
|
||||
/* For some specific BTF_KIND, "struct btf_type" is immediately
|
||||
* followed by extra data.
|
||||
*/
|
||||
|
||||
/* BTF_KIND_INT is followed by a u32 and the following
|
||||
* is the 32 bits arrangement:
|
||||
*/
|
||||
#define BTF_INT_ENCODING(VAL) (((VAL) & 0xff000000) >> 24)
|
||||
#define BTF_INT_OFFSET(VAL) (((VAL & 0x00ff0000)) >> 16)
|
||||
#define BTF_INT_BITS(VAL) ((VAL) & 0x0000ffff)
|
||||
|
||||
/* Attributes stored in the BTF_INT_ENCODING */
|
||||
#define BTF_INT_SIGNED 0x1
|
||||
#define BTF_INT_CHAR 0x2
|
||||
#define BTF_INT_BOOL 0x4
|
||||
#define BTF_INT_VARARGS 0x8
|
||||
|
||||
/* BTF_KIND_ENUM is followed by multiple "struct btf_enum".
|
||||
* The exact number of btf_enum is stored in the vlen (of the
|
||||
* info in "struct btf_type").
|
||||
*/
|
||||
struct btf_enum {
|
||||
__u32 name;
|
||||
__s32 val;
|
||||
};
|
||||
|
||||
/* BTF_KIND_ARRAY is followed by one "struct btf_array" */
|
||||
struct btf_array {
|
||||
__u32 type;
|
||||
__u32 index_type;
|
||||
__u32 nelems;
|
||||
};
|
||||
|
||||
/* BTF_KIND_STRUCT and BTF_KIND_UNION are followed
|
||||
* by multiple "struct btf_member". The exact number
|
||||
* of btf_member is stored in the vlen (of the info in
|
||||
* "struct btf_type").
|
||||
*/
|
||||
struct btf_member {
|
||||
__u32 name;
|
||||
__u32 type;
|
||||
__u32 offset; /* offset in bits */
|
||||
};
|
||||
|
||||
#endif /* _UAPI__LINUX_BTF_H__ */
|
|
@ -4,6 +4,7 @@ obj-y := core.o
|
|||
obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o
|
||||
obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o
|
||||
obj-$(CONFIG_BPF_SYSCALL) += disasm.o
|
||||
obj-$(CONFIG_BPF_SYSCALL) += btf.o
|
||||
ifeq ($(CONFIG_NET),y)
|
||||
obj-$(CONFIG_BPF_SYSCALL) += devmap.o
|
||||
obj-$(CONFIG_BPF_SYSCALL) += cpumap.o
|
||||
|
|
|
@ -11,11 +11,13 @@
|
|||
* General Public License for more details.
|
||||
*/
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/btf.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <uapi/linux/btf.h>
|
||||
|
||||
#include "map_in_map.h"
|
||||
|
||||
|
@ -336,6 +338,52 @@ static void array_map_free(struct bpf_map *map)
|
|||
bpf_map_area_free(array);
|
||||
}
|
||||
|
||||
static void array_map_seq_show_elem(struct bpf_map *map, void *key,
|
||||
struct seq_file *m)
|
||||
{
|
||||
void *value;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
value = array_map_lookup_elem(map, key);
|
||||
if (!value) {
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
seq_printf(m, "%u: ", *(u32 *)key);
|
||||
btf_type_seq_show(map->btf, map->btf_value_id, value, m);
|
||||
seq_puts(m, "\n");
|
||||
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static int array_map_check_btf(const struct bpf_map *map, const struct btf *btf,
|
||||
u32 btf_key_id, u32 btf_value_id)
|
||||
{
|
||||
const struct btf_type *key_type, *value_type;
|
||||
u32 key_size, value_size;
|
||||
u32 int_data;
|
||||
|
||||
key_type = btf_type_id_size(btf, &btf_key_id, &key_size);
|
||||
if (!key_type || BTF_INFO_KIND(key_type->info) != BTF_KIND_INT)
|
||||
return -EINVAL;
|
||||
|
||||
int_data = *(u32 *)(key_type + 1);
|
||||
/* bpf array can only take a u32 key. This check makes
|
||||
* sure that the btf matches the attr used during map_create.
|
||||
*/
|
||||
if (BTF_INT_BITS(int_data) != 32 || key_size != 4 ||
|
||||
BTF_INT_OFFSET(int_data))
|
||||
return -EINVAL;
|
||||
|
||||
value_type = btf_type_id_size(btf, &btf_value_id, &value_size);
|
||||
if (!value_type || value_size > map->value_size)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct bpf_map_ops array_map_ops = {
|
||||
.map_alloc_check = array_map_alloc_check,
|
||||
.map_alloc = array_map_alloc,
|
||||
|
@ -345,6 +393,8 @@ const struct bpf_map_ops array_map_ops = {
|
|||
.map_update_elem = array_map_update_elem,
|
||||
.map_delete_elem = array_map_delete_elem,
|
||||
.map_gen_lookup = array_map_gen_lookup,
|
||||
.map_seq_show_elem = array_map_seq_show_elem,
|
||||
.map_check_btf = array_map_check_btf,
|
||||
};
|
||||
|
||||
const struct bpf_map_ops percpu_array_map_ops = {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -150,8 +150,154 @@ 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 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 +305,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 +314,15 @@ 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, NULL);
|
||||
}
|
||||
|
||||
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 : NULL);
|
||||
}
|
||||
|
||||
static struct dentry *
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
*/
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/bpf_trace.h>
|
||||
#include <linux/btf.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sched/signal.h>
|
||||
|
@ -26,6 +27,7 @@
|
|||
#include <linux/cred.h>
|
||||
#include <linux/timekeeping.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/btf.h>
|
||||
|
||||
#define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PROG_ARRAY || \
|
||||
(map)->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || \
|
||||
|
@ -250,6 +252,7 @@ static void bpf_map_free_deferred(struct work_struct *work)
|
|||
|
||||
bpf_map_uncharge_memlock(map);
|
||||
security_bpf_map_free(map);
|
||||
btf_put(map->btf);
|
||||
/* implementation dependent freeing */
|
||||
map->ops->map_free(map);
|
||||
}
|
||||
|
@ -415,7 +418,7 @@ static int bpf_obj_name_cpy(char *dst, const char *src)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#define BPF_MAP_CREATE_LAST_FIELD map_ifindex
|
||||
#define BPF_MAP_CREATE_LAST_FIELD btf_value_id
|
||||
/* called via syscall */
|
||||
static int map_create(union bpf_attr *attr)
|
||||
{
|
||||
|
@ -449,6 +452,33 @@ static int map_create(union bpf_attr *attr)
|
|||
atomic_set(&map->refcnt, 1);
|
||||
atomic_set(&map->usercnt, 1);
|
||||
|
||||
if (bpf_map_support_seq_show(map) &&
|
||||
(attr->btf_key_id || attr->btf_value_id)) {
|
||||
struct btf *btf;
|
||||
|
||||
if (!attr->btf_key_id || !attr->btf_value_id) {
|
||||
err = -EINVAL;
|
||||
goto free_map_nouncharge;
|
||||
}
|
||||
|
||||
btf = btf_get_by_fd(attr->btf_fd);
|
||||
if (IS_ERR(btf)) {
|
||||
err = PTR_ERR(btf);
|
||||
goto free_map_nouncharge;
|
||||
}
|
||||
|
||||
err = map->ops->map_check_btf(map, btf, attr->btf_key_id,
|
||||
attr->btf_value_id);
|
||||
if (err) {
|
||||
btf_put(btf);
|
||||
goto free_map_nouncharge;
|
||||
}
|
||||
|
||||
map->btf = btf;
|
||||
map->btf_key_id = attr->btf_key_id;
|
||||
map->btf_value_id = attr->btf_value_id;
|
||||
}
|
||||
|
||||
err = security_bpf_map_alloc(map);
|
||||
if (err)
|
||||
goto free_map_nouncharge;
|
||||
|
@ -481,6 +511,7 @@ static int map_create(union bpf_attr *attr)
|
|||
free_map_sec:
|
||||
security_bpf_map_free(map);
|
||||
free_map_nouncharge:
|
||||
btf_put(map->btf);
|
||||
map->ops->map_free(map);
|
||||
return err;
|
||||
}
|
||||
|
@ -2016,6 +2047,8 @@ static int bpf_obj_get_info_by_fd(const union bpf_attr *attr,
|
|||
else if (f.file->f_op == &bpf_map_fops)
|
||||
err = bpf_map_get_info_by_fd(f.file->private_data, attr,
|
||||
uattr);
|
||||
else if (f.file->f_op == &btf_fops)
|
||||
err = btf_get_info_by_fd(f.file->private_data, attr, uattr);
|
||||
else
|
||||
err = -EINVAL;
|
||||
|
||||
|
@ -2023,6 +2056,19 @@ static int bpf_obj_get_info_by_fd(const union bpf_attr *attr,
|
|||
return err;
|
||||
}
|
||||
|
||||
#define BPF_BTF_LOAD_LAST_FIELD btf_log_level
|
||||
|
||||
static int bpf_btf_load(const union bpf_attr *attr)
|
||||
{
|
||||
if (CHECK_ATTR(BPF_BTF_LOAD))
|
||||
return -EINVAL;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
return btf_new_fd(attr);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size)
|
||||
{
|
||||
union bpf_attr attr = {};
|
||||
|
@ -2103,6 +2149,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
|
|||
case BPF_RAW_TRACEPOINT_OPEN:
|
||||
err = bpf_raw_tracepoint_open(&attr);
|
||||
break;
|
||||
case BPF_BTF_LOAD:
|
||||
err = bpf_btf_load(&attr);
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
|
|
|
@ -95,6 +95,7 @@ enum bpf_cmd {
|
|||
BPF_OBJ_GET_INFO_BY_FD,
|
||||
BPF_PROG_QUERY,
|
||||
BPF_RAW_TRACEPOINT_OPEN,
|
||||
BPF_BTF_LOAD,
|
||||
};
|
||||
|
||||
enum bpf_map_type {
|
||||
|
@ -279,6 +280,9 @@ union bpf_attr {
|
|||
*/
|
||||
char map_name[BPF_OBJ_NAME_LEN];
|
||||
__u32 map_ifindex; /* ifindex of netdev to create on */
|
||||
__u32 btf_fd; /* fd pointing to a BTF type data */
|
||||
__u32 btf_key_id; /* BTF type_id of the key */
|
||||
__u32 btf_value_id; /* BTF type_id of the value */
|
||||
};
|
||||
|
||||
struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */
|
||||
|
@ -363,6 +367,14 @@ union bpf_attr {
|
|||
__u64 name;
|
||||
__u32 prog_fd;
|
||||
} raw_tracepoint;
|
||||
|
||||
struct { /* anonymous struct for BPF_BTF_LOAD */
|
||||
__aligned_u64 btf;
|
||||
__aligned_u64 btf_log_buf;
|
||||
__u32 btf_size;
|
||||
__u32 btf_log_size;
|
||||
__u32 btf_log_level;
|
||||
};
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
/* BPF helper function descriptions:
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/* Copyright (c) 2018 Facebook */
|
||||
#ifndef _UAPI__LINUX_BTF_H__
|
||||
#define _UAPI__LINUX_BTF_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define BTF_MAGIC 0xeB9F
|
||||
#define BTF_MAGIC_SWAP 0x9FeB
|
||||
#define BTF_VERSION 1
|
||||
#define BTF_FLAGS_COMPR 0x01
|
||||
|
||||
struct btf_header {
|
||||
__u16 magic;
|
||||
__u8 version;
|
||||
__u8 flags;
|
||||
|
||||
__u32 parent_label;
|
||||
__u32 parent_name;
|
||||
|
||||
/* All offsets are in bytes relative to the end of this header */
|
||||
__u32 label_off; /* offset of label section */
|
||||
__u32 object_off; /* offset of data object section*/
|
||||
__u32 func_off; /* offset of function section */
|
||||
__u32 type_off; /* offset of type section */
|
||||
__u32 str_off; /* offset of string section */
|
||||
__u32 str_len; /* length of string section */
|
||||
};
|
||||
|
||||
/* Max # of type identifier */
|
||||
#define BTF_MAX_TYPE 0x7fffffff
|
||||
/* Max offset into the string section */
|
||||
#define BTF_MAX_NAME_OFFSET 0x7fffffff
|
||||
/* Max # of struct/union/enum members or func args */
|
||||
#define BTF_MAX_VLEN 0xffff
|
||||
|
||||
/* The type id is referring to a parent BTF */
|
||||
#define BTF_TYPE_PARENT(id) (((id) >> 31) & 0x1)
|
||||
#define BTF_TYPE_ID(id) ((id) & BTF_MAX_TYPE)
|
||||
|
||||
/* String is in the ELF string section */
|
||||
#define BTF_STR_TBL_ELF_ID(ref) (((ref) >> 31) & 0x1)
|
||||
#define BTF_STR_OFFSET(ref) ((ref) & BTF_MAX_NAME_OFFSET)
|
||||
|
||||
struct btf_type {
|
||||
__u32 name;
|
||||
/* "info" bits arrangement
|
||||
* bits 0-15: vlen (e.g. # of struct's members)
|
||||
* bits 16-23: unused
|
||||
* bits 24-28: kind (e.g. int, ptr, array...etc)
|
||||
* bits 29-30: unused
|
||||
* bits 31: root
|
||||
*/
|
||||
__u32 info;
|
||||
/* "size" is used by INT, ENUM, STRUCT and UNION.
|
||||
* "size" tells the size of the type it is describing.
|
||||
*
|
||||
* "type" is used by PTR, TYPEDEF, VOLATILE, CONST and RESTRICT.
|
||||
* "type" is a type_id referring to another type.
|
||||
*/
|
||||
union {
|
||||
__u32 size;
|
||||
__u32 type;
|
||||
};
|
||||
};
|
||||
|
||||
#define BTF_INFO_KIND(info) (((info) >> 24) & 0x1f)
|
||||
#define BTF_INFO_ISROOT(info) (!!(((info) >> 24) & 0x80))
|
||||
#define BTF_INFO_VLEN(info) ((info) & 0xffff)
|
||||
|
||||
#define BTF_KIND_UNKN 0 /* Unknown */
|
||||
#define BTF_KIND_INT 1 /* Integer */
|
||||
#define BTF_KIND_PTR 2 /* Pointer */
|
||||
#define BTF_KIND_ARRAY 3 /* Array */
|
||||
#define BTF_KIND_STRUCT 4 /* Struct */
|
||||
#define BTF_KIND_UNION 5 /* Union */
|
||||
#define BTF_KIND_ENUM 6 /* Enumeration */
|
||||
#define BTF_KIND_FWD 7 /* Forward */
|
||||
#define BTF_KIND_TYPEDEF 8 /* Typedef */
|
||||
#define BTF_KIND_VOLATILE 9 /* Volatile */
|
||||
#define BTF_KIND_CONST 10 /* Const */
|
||||
#define BTF_KIND_RESTRICT 11 /* Restrict */
|
||||
#define BTF_KIND_MAX 11
|
||||
#define NR_BTF_KINDS 12
|
||||
|
||||
/* For some specific BTF_KIND, "struct btf_type" is immediately
|
||||
* followed by extra data.
|
||||
*/
|
||||
|
||||
/* BTF_KIND_INT is followed by a u32 and the following
|
||||
* is the 32 bits arrangement:
|
||||
*/
|
||||
#define BTF_INT_ENCODING(VAL) (((VAL) & 0xff000000) >> 24)
|
||||
#define BTF_INT_OFFSET(VAL) (((VAL & 0x00ff0000)) >> 16)
|
||||
#define BTF_INT_BITS(VAL) ((VAL) & 0x0000ffff)
|
||||
|
||||
/* Attributes stored in the BTF_INT_ENCODING */
|
||||
#define BTF_INT_SIGNED 0x1
|
||||
#define BTF_INT_CHAR 0x2
|
||||
#define BTF_INT_BOOL 0x4
|
||||
#define BTF_INT_VARARGS 0x8
|
||||
|
||||
/* BTF_KIND_ENUM is followed by multiple "struct btf_enum".
|
||||
* The exact number of btf_enum is stored in the vlen (of the
|
||||
* info in "struct btf_type").
|
||||
*/
|
||||
struct btf_enum {
|
||||
__u32 name;
|
||||
__s32 val;
|
||||
};
|
||||
|
||||
/* BTF_KIND_ARRAY is followed by one "struct btf_array" */
|
||||
struct btf_array {
|
||||
__u32 type;
|
||||
__u32 index_type;
|
||||
__u32 nelems;
|
||||
};
|
||||
|
||||
/* BTF_KIND_STRUCT and BTF_KIND_UNION are followed
|
||||
* by multiple "struct btf_member". The exact number
|
||||
* of btf_member is stored in the vlen (of the info in
|
||||
* "struct btf_type").
|
||||
*/
|
||||
struct btf_member {
|
||||
__u32 name;
|
||||
__u32 type;
|
||||
__u32 offset; /* offset in bits */
|
||||
};
|
||||
|
||||
#endif /* _UAPI__LINUX_BTF_H__ */
|
|
@ -1 +1 @@
|
|||
libbpf-y := libbpf.o bpf.o nlattr.o
|
||||
libbpf-y := libbpf.o bpf.o nlattr.o btf.o
|
||||
|
|
|
@ -73,43 +73,76 @@ static inline int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr,
|
|||
return syscall(__NR_bpf, cmd, attr, size);
|
||||
}
|
||||
|
||||
int bpf_create_map_node(enum bpf_map_type map_type, const char *name,
|
||||
int key_size, int value_size, int max_entries,
|
||||
__u32 map_flags, int node)
|
||||
int bpf_create_map_xattr(const struct bpf_create_map_attr *create_attr)
|
||||
{
|
||||
__u32 name_len = name ? strlen(name) : 0;
|
||||
__u32 name_len = create_attr->name ? strlen(create_attr->name) : 0;
|
||||
union bpf_attr attr;
|
||||
|
||||
memset(&attr, '\0', sizeof(attr));
|
||||
|
||||
attr.map_type = map_type;
|
||||
attr.key_size = key_size;
|
||||
attr.value_size = value_size;
|
||||
attr.max_entries = max_entries;
|
||||
attr.map_flags = map_flags;
|
||||
memcpy(attr.map_name, name, min(name_len, BPF_OBJ_NAME_LEN - 1));
|
||||
|
||||
if (node >= 0) {
|
||||
attr.map_flags |= BPF_F_NUMA_NODE;
|
||||
attr.numa_node = node;
|
||||
}
|
||||
attr.map_type = create_attr->map_type;
|
||||
attr.key_size = create_attr->key_size;
|
||||
attr.value_size = create_attr->value_size;
|
||||
attr.max_entries = create_attr->max_entries;
|
||||
attr.map_flags = create_attr->map_flags;
|
||||
memcpy(attr.map_name, create_attr->name,
|
||||
min(name_len, BPF_OBJ_NAME_LEN - 1));
|
||||
attr.numa_node = create_attr->numa_node;
|
||||
attr.btf_fd = create_attr->btf_fd;
|
||||
attr.btf_key_id = create_attr->btf_key_id;
|
||||
attr.btf_value_id = create_attr->btf_value_id;
|
||||
|
||||
return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
|
||||
}
|
||||
|
||||
int bpf_create_map_node(enum bpf_map_type map_type, const char *name,
|
||||
int key_size, int value_size, int max_entries,
|
||||
__u32 map_flags, int node)
|
||||
{
|
||||
struct bpf_create_map_attr map_attr = {};
|
||||
|
||||
map_attr.name = name;
|
||||
map_attr.map_type = map_type;
|
||||
map_attr.map_flags = map_flags;
|
||||
map_attr.key_size = key_size;
|
||||
map_attr.value_size = value_size;
|
||||
map_attr.max_entries = max_entries;
|
||||
if (node >= 0) {
|
||||
map_attr.numa_node = node;
|
||||
map_attr.map_flags |= BPF_F_NUMA_NODE;
|
||||
}
|
||||
|
||||
return bpf_create_map_xattr(&map_attr);
|
||||
}
|
||||
|
||||
int bpf_create_map(enum bpf_map_type map_type, int key_size,
|
||||
int value_size, int max_entries, __u32 map_flags)
|
||||
{
|
||||
return bpf_create_map_node(map_type, NULL, key_size, value_size,
|
||||
max_entries, map_flags, -1);
|
||||
struct bpf_create_map_attr map_attr = {};
|
||||
|
||||
map_attr.map_type = map_type;
|
||||
map_attr.map_flags = map_flags;
|
||||
map_attr.key_size = key_size;
|
||||
map_attr.value_size = value_size;
|
||||
map_attr.max_entries = max_entries;
|
||||
|
||||
return bpf_create_map_xattr(&map_attr);
|
||||
}
|
||||
|
||||
int bpf_create_map_name(enum bpf_map_type map_type, const char *name,
|
||||
int key_size, int value_size, int max_entries,
|
||||
__u32 map_flags)
|
||||
{
|
||||
return bpf_create_map_node(map_type, name, key_size, value_size,
|
||||
max_entries, map_flags, -1);
|
||||
struct bpf_create_map_attr map_attr = {};
|
||||
|
||||
map_attr.name = name;
|
||||
map_attr.map_type = map_type;
|
||||
map_attr.map_flags = map_flags;
|
||||
map_attr.key_size = key_size;
|
||||
map_attr.value_size = value_size;
|
||||
map_attr.max_entries = max_entries;
|
||||
|
||||
return bpf_create_map_xattr(&map_attr);
|
||||
}
|
||||
|
||||
int bpf_create_map_in_map_node(enum bpf_map_type map_type, const char *name,
|
||||
|
@ -573,3 +606,28 @@ int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags)
|
|||
close(sock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bpf_load_btf(void *btf, __u32 btf_size, char *log_buf, __u32 log_buf_size,
|
||||
bool do_log)
|
||||
{
|
||||
union bpf_attr attr = {};
|
||||
int fd;
|
||||
|
||||
attr.btf = ptr_to_u64(btf);
|
||||
attr.btf_size = btf_size;
|
||||
|
||||
retry:
|
||||
if (do_log && log_buf && log_buf_size) {
|
||||
attr.btf_log_level = 1;
|
||||
attr.btf_log_size = log_buf_size;
|
||||
attr.btf_log_buf = ptr_to_u64(log_buf);
|
||||
}
|
||||
|
||||
fd = sys_bpf(BPF_BTF_LOAD, &attr, sizeof(attr));
|
||||
if (fd == -1 && !do_log && log_buf && log_buf_size) {
|
||||
do_log = true;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,20 @@
|
|||
#include <linux/bpf.h>
|
||||
#include <stddef.h>
|
||||
|
||||
struct bpf_create_map_attr {
|
||||
const char *name;
|
||||
enum bpf_map_type map_type;
|
||||
__u32 map_flags;
|
||||
__u32 key_size;
|
||||
__u32 value_size;
|
||||
__u32 max_entries;
|
||||
__u32 numa_node;
|
||||
__u32 btf_fd;
|
||||
__u32 btf_key_id;
|
||||
__u32 btf_value_id;
|
||||
};
|
||||
|
||||
int bpf_create_map_xattr(const struct bpf_create_map_attr *create_attr);
|
||||
int bpf_create_map_node(enum bpf_map_type map_type, const char *name,
|
||||
int key_size, int value_size, int max_entries,
|
||||
__u32 map_flags, int node);
|
||||
|
@ -87,4 +101,6 @@ int bpf_obj_get_info_by_fd(int prog_fd, void *info, __u32 *info_len);
|
|||
int bpf_prog_query(int target_fd, enum bpf_attach_type type, __u32 query_flags,
|
||||
__u32 *attach_flags, __u32 *prog_ids, __u32 *prog_cnt);
|
||||
int bpf_raw_tracepoint_open(const char *name, int prog_fd);
|
||||
int bpf_load_btf(void *btf, __u32 btf_size, char *log_buf, __u32 log_buf_size,
|
||||
bool do_log);
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,374 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2018 Facebook */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/btf.h>
|
||||
#include "btf.h"
|
||||
#include "bpf.h"
|
||||
|
||||
#define elog(fmt, ...) { if (err_log) err_log(fmt, ##__VA_ARGS__); }
|
||||
#define max(a, b) ((a) > (b) ? (a) : (b))
|
||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
#define BTF_MAX_NR_TYPES 65535
|
||||
|
||||
static struct btf_type btf_void;
|
||||
|
||||
struct btf {
|
||||
union {
|
||||
struct btf_header *hdr;
|
||||
void *data;
|
||||
};
|
||||
struct btf_type **types;
|
||||
const char *strings;
|
||||
void *nohdr_data;
|
||||
uint32_t nr_types;
|
||||
uint32_t types_size;
|
||||
uint32_t data_size;
|
||||
int fd;
|
||||
};
|
||||
|
||||
static const char *btf_name_by_offset(const struct btf *btf, uint32_t offset)
|
||||
{
|
||||
if (!BTF_STR_TBL_ELF_ID(offset) &&
|
||||
BTF_STR_OFFSET(offset) < btf->hdr->str_len)
|
||||
return &btf->strings[BTF_STR_OFFSET(offset)];
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int btf_add_type(struct btf *btf, struct btf_type *t)
|
||||
{
|
||||
if (btf->types_size - btf->nr_types < 2) {
|
||||
struct btf_type **new_types;
|
||||
u32 expand_by, new_size;
|
||||
|
||||
if (btf->types_size == BTF_MAX_NR_TYPES)
|
||||
return -E2BIG;
|
||||
|
||||
expand_by = max(btf->types_size >> 2, 16);
|
||||
new_size = min(BTF_MAX_NR_TYPES, btf->types_size + expand_by);
|
||||
|
||||
new_types = realloc(btf->types, sizeof(*new_types) * new_size);
|
||||
if (!new_types)
|
||||
return -ENOMEM;
|
||||
|
||||
if (btf->nr_types == 0)
|
||||
new_types[0] = &btf_void;
|
||||
|
||||
btf->types = new_types;
|
||||
btf->types_size = new_size;
|
||||
}
|
||||
|
||||
btf->types[++(btf->nr_types)] = t;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btf_parse_hdr(struct btf *btf, btf_print_fn_t err_log)
|
||||
{
|
||||
const struct btf_header *hdr = btf->hdr;
|
||||
u32 meta_left;
|
||||
|
||||
if (btf->data_size < sizeof(struct btf_header)) {
|
||||
elog("BTF header not found\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (hdr->magic != BTF_MAGIC) {
|
||||
elog("Invalid BTF magic:%x\n", hdr->magic);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (hdr->version != BTF_VERSION) {
|
||||
elog("Unsupported BTF version:%u\n", hdr->version);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (hdr->flags) {
|
||||
elog("Unsupported BTF flags:%x\n", hdr->flags);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
meta_left = btf->data_size - sizeof(*hdr);
|
||||
if (!meta_left) {
|
||||
elog("BTF has no data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (meta_left < hdr->type_off) {
|
||||
elog("Invalid BTF type section offset:%u\n", hdr->type_off);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (meta_left < hdr->str_off) {
|
||||
elog("Invalid BTF string section offset:%u\n", hdr->str_off);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (hdr->type_off >= hdr->str_off) {
|
||||
elog("BTF type section offset >= string section offset. No type?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (hdr->type_off & 0x02) {
|
||||
elog("BTF type section is not aligned to 4 bytes\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
btf->nohdr_data = btf->hdr + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btf_parse_str_sec(struct btf *btf, btf_print_fn_t err_log)
|
||||
{
|
||||
const struct btf_header *hdr = btf->hdr;
|
||||
const char *start = btf->nohdr_data + hdr->str_off;
|
||||
const char *end = start + btf->hdr->str_len;
|
||||
|
||||
if (!hdr->str_len || hdr->str_len - 1 > BTF_MAX_NAME_OFFSET ||
|
||||
start[0] || end[-1]) {
|
||||
elog("Invalid BTF string section\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
btf->strings = start;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btf_parse_type_sec(struct btf *btf, btf_print_fn_t err_log)
|
||||
{
|
||||
struct btf_header *hdr = btf->hdr;
|
||||
void *nohdr_data = btf->nohdr_data;
|
||||
void *next_type = nohdr_data + hdr->type_off;
|
||||
void *end_type = nohdr_data + hdr->str_off;
|
||||
|
||||
while (next_type < end_type) {
|
||||
struct btf_type *t = next_type;
|
||||
uint16_t vlen = BTF_INFO_VLEN(t->info);
|
||||
int err;
|
||||
|
||||
next_type += sizeof(*t);
|
||||
switch (BTF_INFO_KIND(t->info)) {
|
||||
case BTF_KIND_INT:
|
||||
next_type += sizeof(int);
|
||||
break;
|
||||
case BTF_KIND_ARRAY:
|
||||
next_type += sizeof(struct btf_array);
|
||||
break;
|
||||
case BTF_KIND_STRUCT:
|
||||
case BTF_KIND_UNION:
|
||||
next_type += vlen * sizeof(struct btf_member);
|
||||
break;
|
||||
case BTF_KIND_ENUM:
|
||||
next_type += vlen * sizeof(struct btf_enum);
|
||||
break;
|
||||
case BTF_KIND_TYPEDEF:
|
||||
case BTF_KIND_PTR:
|
||||
case BTF_KIND_FWD:
|
||||
case BTF_KIND_VOLATILE:
|
||||
case BTF_KIND_CONST:
|
||||
case BTF_KIND_RESTRICT:
|
||||
break;
|
||||
default:
|
||||
elog("Unsupported BTF_KIND:%u\n",
|
||||
BTF_INFO_KIND(t->info));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = btf_add_type(btf, t);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct btf_type *btf_type_by_id(const struct btf *btf,
|
||||
uint32_t type_id)
|
||||
{
|
||||
if (type_id > btf->nr_types)
|
||||
return NULL;
|
||||
|
||||
return btf->types[type_id];
|
||||
}
|
||||
|
||||
static bool btf_type_is_void(const struct btf_type *t)
|
||||
{
|
||||
return t == &btf_void || BTF_INFO_KIND(t->info) == BTF_KIND_FWD;
|
||||
}
|
||||
|
||||
static bool btf_type_is_void_or_null(const struct btf_type *t)
|
||||
{
|
||||
return !t || btf_type_is_void(t);
|
||||
}
|
||||
|
||||
static int64_t btf_type_size(const struct btf_type *t)
|
||||
{
|
||||
switch (BTF_INFO_KIND(t->info)) {
|
||||
case BTF_KIND_INT:
|
||||
case BTF_KIND_STRUCT:
|
||||
case BTF_KIND_UNION:
|
||||
case BTF_KIND_ENUM:
|
||||
return t->size;
|
||||
case BTF_KIND_PTR:
|
||||
return sizeof(void *);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#define MAX_RESOLVE_DEPTH 32
|
||||
|
||||
int64_t btf__resolve_size(const struct btf *btf, uint32_t type_id)
|
||||
{
|
||||
const struct btf_array *array;
|
||||
const struct btf_type *t;
|
||||
uint32_t nelems = 1;
|
||||
int64_t size = -1;
|
||||
int i;
|
||||
|
||||
t = btf_type_by_id(btf, type_id);
|
||||
for (i = 0; i < MAX_RESOLVE_DEPTH && !btf_type_is_void_or_null(t);
|
||||
i++) {
|
||||
size = btf_type_size(t);
|
||||
if (size >= 0)
|
||||
break;
|
||||
|
||||
switch (BTF_INFO_KIND(t->info)) {
|
||||
case BTF_KIND_TYPEDEF:
|
||||
case BTF_KIND_VOLATILE:
|
||||
case BTF_KIND_CONST:
|
||||
case BTF_KIND_RESTRICT:
|
||||
type_id = t->type;
|
||||
break;
|
||||
case BTF_KIND_ARRAY:
|
||||
array = (const struct btf_array *)(t + 1);
|
||||
if (nelems && array->nelems > UINT32_MAX / nelems)
|
||||
return -E2BIG;
|
||||
nelems *= array->nelems;
|
||||
type_id = array->type;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
t = btf_type_by_id(btf, type_id);
|
||||
}
|
||||
|
||||
if (size < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (nelems && size > UINT32_MAX / nelems)
|
||||
return -E2BIG;
|
||||
|
||||
return nelems * size;
|
||||
}
|
||||
|
||||
int32_t btf__find_by_name(const struct btf *btf, const char *type_name)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
if (!strcmp(type_name, "void"))
|
||||
return 0;
|
||||
|
||||
for (i = 1; i <= btf->nr_types; i++) {
|
||||
const struct btf_type *t = btf->types[i];
|
||||
const char *name = btf_name_by_offset(btf, t->name);
|
||||
|
||||
if (name && !strcmp(type_name, name))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
void btf__free(struct btf *btf)
|
||||
{
|
||||
if (!btf)
|
||||
return;
|
||||
|
||||
if (btf->fd != -1)
|
||||
close(btf->fd);
|
||||
|
||||
free(btf->data);
|
||||
free(btf->types);
|
||||
free(btf);
|
||||
}
|
||||
|
||||
struct btf *btf__new(uint8_t *data, uint32_t size,
|
||||
btf_print_fn_t err_log)
|
||||
{
|
||||
uint32_t log_buf_size = 0;
|
||||
char *log_buf = NULL;
|
||||
struct btf *btf;
|
||||
int err;
|
||||
|
||||
btf = calloc(1, sizeof(struct btf));
|
||||
if (!btf)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
btf->fd = -1;
|
||||
|
||||
if (err_log) {
|
||||
log_buf = malloc(BPF_LOG_BUF_SIZE);
|
||||
if (!log_buf) {
|
||||
err = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
*log_buf = 0;
|
||||
log_buf_size = BPF_LOG_BUF_SIZE;
|
||||
}
|
||||
|
||||
btf->data = malloc(size);
|
||||
if (!btf->data) {
|
||||
err = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
|
||||
memcpy(btf->data, data, size);
|
||||
btf->data_size = size;
|
||||
|
||||
btf->fd = bpf_load_btf(btf->data, btf->data_size,
|
||||
log_buf, log_buf_size, false);
|
||||
|
||||
if (btf->fd == -1) {
|
||||
err = -errno;
|
||||
elog("Error loading BTF: %s(%d)\n", strerror(errno), errno);
|
||||
if (log_buf && *log_buf)
|
||||
elog("%s\n", log_buf);
|
||||
goto done;
|
||||
}
|
||||
|
||||
err = btf_parse_hdr(btf, err_log);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
err = btf_parse_str_sec(btf, err_log);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
err = btf_parse_type_sec(btf, err_log);
|
||||
|
||||
done:
|
||||
free(log_buf);
|
||||
|
||||
if (err) {
|
||||
btf__free(btf);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
return btf;
|
||||
}
|
||||
|
||||
int btf__fd(const struct btf *btf)
|
||||
{
|
||||
return btf->fd;
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2018 Facebook */
|
||||
|
||||
#ifndef __BPF_BTF_H
|
||||
#define __BPF_BTF_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define BTF_ELF_SEC ".BTF"
|
||||
|
||||
struct btf;
|
||||
|
||||
typedef int (*btf_print_fn_t)(const char *, ...)
|
||||
__attribute__((format(printf, 1, 2)));
|
||||
|
||||
void btf__free(struct btf *btf);
|
||||
struct btf *btf__new(uint8_t *data, uint32_t size, btf_print_fn_t err_log);
|
||||
int32_t btf__find_by_name(const struct btf *btf, const char *type_name);
|
||||
int64_t btf__resolve_size(const struct btf *btf, uint32_t type_id);
|
||||
int btf__fd(const struct btf *btf);
|
||||
|
||||
#endif
|
|
@ -45,6 +45,7 @@
|
|||
|
||||
#include "libbpf.h"
|
||||
#include "bpf.h"
|
||||
#include "btf.h"
|
||||
|
||||
#ifndef EM_BPF
|
||||
#define EM_BPF 247
|
||||
|
@ -212,6 +213,8 @@ struct bpf_map {
|
|||
char *name;
|
||||
size_t offset;
|
||||
struct bpf_map_def def;
|
||||
uint32_t btf_key_id;
|
||||
uint32_t btf_value_id;
|
||||
void *priv;
|
||||
bpf_map_clear_priv_t clear_priv;
|
||||
};
|
||||
|
@ -256,6 +259,8 @@ struct bpf_object {
|
|||
*/
|
||||
struct list_head list;
|
||||
|
||||
struct btf *btf;
|
||||
|
||||
void *priv;
|
||||
bpf_object_clear_priv_t clear_priv;
|
||||
|
||||
|
@ -819,7 +824,15 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
|
|||
data->d_size);
|
||||
else if (strcmp(name, "maps") == 0)
|
||||
obj->efile.maps_shndx = idx;
|
||||
else if (sh.sh_type == SHT_SYMTAB) {
|
||||
else if (strcmp(name, BTF_ELF_SEC) == 0) {
|
||||
obj->btf = btf__new(data->d_buf, data->d_size,
|
||||
__pr_debug);
|
||||
if (IS_ERR(obj->btf)) {
|
||||
pr_warning("Error loading ELF section %s: %ld. Ignored and continue.\n",
|
||||
BTF_ELF_SEC, PTR_ERR(obj->btf));
|
||||
obj->btf = NULL;
|
||||
}
|
||||
} else if (sh.sh_type == SHT_SYMTAB) {
|
||||
if (obj->efile.symbols) {
|
||||
pr_warning("bpf: multiple SYMTAB in %s\n",
|
||||
obj->path);
|
||||
|
@ -996,33 +1009,126 @@ bpf_program__collect_reloc(struct bpf_program *prog, GElf_Shdr *shdr,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int bpf_map_find_btf_info(struct bpf_map *map, const struct btf *btf)
|
||||
{
|
||||
struct bpf_map_def *def = &map->def;
|
||||
const size_t max_name = 256;
|
||||
int64_t key_size, value_size;
|
||||
int32_t key_id, value_id;
|
||||
char name[max_name];
|
||||
|
||||
/* Find key type by name from BTF */
|
||||
if (snprintf(name, max_name, "%s_key", map->name) == max_name) {
|
||||
pr_warning("map:%s length of BTF key_type:%s_key is too long\n",
|
||||
map->name, map->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
key_id = btf__find_by_name(btf, name);
|
||||
if (key_id < 0) {
|
||||
pr_debug("map:%s key_type:%s cannot be found in BTF\n",
|
||||
map->name, name);
|
||||
return key_id;
|
||||
}
|
||||
|
||||
key_size = btf__resolve_size(btf, key_id);
|
||||
if (key_size < 0) {
|
||||
pr_warning("map:%s key_type:%s cannot get the BTF type_size\n",
|
||||
map->name, name);
|
||||
return key_size;
|
||||
}
|
||||
|
||||
if (def->key_size != key_size) {
|
||||
pr_warning("map:%s key_type:%s has BTF type_size:%ld != key_size:%u\n",
|
||||
map->name, name, key_size, def->key_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Find value type from BTF */
|
||||
if (snprintf(name, max_name, "%s_value", map->name) == max_name) {
|
||||
pr_warning("map:%s length of BTF value_type:%s_value is too long\n",
|
||||
map->name, map->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
value_id = btf__find_by_name(btf, name);
|
||||
if (value_id < 0) {
|
||||
pr_debug("map:%s value_type:%s cannot be found in BTF\n",
|
||||
map->name, name);
|
||||
return value_id;
|
||||
}
|
||||
|
||||
value_size = btf__resolve_size(btf, value_id);
|
||||
if (value_size < 0) {
|
||||
pr_warning("map:%s value_type:%s cannot get the BTF type_size\n",
|
||||
map->name, name);
|
||||
return value_size;
|
||||
}
|
||||
|
||||
if (def->value_size != value_size) {
|
||||
pr_warning("map:%s value_type:%s has BTF type_size:%ld != value_size:%u\n",
|
||||
map->name, name, value_size, def->value_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
map->btf_key_id = key_id;
|
||||
map->btf_value_id = value_id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
bpf_object__create_maps(struct bpf_object *obj)
|
||||
{
|
||||
struct bpf_create_map_attr create_attr = {};
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
for (i = 0; i < obj->nr_maps; i++) {
|
||||
struct bpf_map_def *def = &obj->maps[i].def;
|
||||
int *pfd = &obj->maps[i].fd;
|
||||
struct bpf_map *map = &obj->maps[i];
|
||||
struct bpf_map_def *def = &map->def;
|
||||
int *pfd = &map->fd;
|
||||
|
||||
create_attr.name = map->name;
|
||||
create_attr.map_type = def->type;
|
||||
create_attr.map_flags = def->map_flags;
|
||||
create_attr.key_size = def->key_size;
|
||||
create_attr.value_size = def->value_size;
|
||||
create_attr.max_entries = def->max_entries;
|
||||
create_attr.btf_fd = 0;
|
||||
create_attr.btf_key_id = 0;
|
||||
create_attr.btf_value_id = 0;
|
||||
|
||||
if (obj->btf && !bpf_map_find_btf_info(map, obj->btf)) {
|
||||
create_attr.btf_fd = btf__fd(obj->btf);
|
||||
create_attr.btf_key_id = map->btf_key_id;
|
||||
create_attr.btf_value_id = map->btf_value_id;
|
||||
}
|
||||
|
||||
*pfd = bpf_create_map_xattr(&create_attr);
|
||||
if (*pfd < 0 && create_attr.btf_key_id) {
|
||||
pr_warning("Error in bpf_create_map_xattr(%s):%s(%d). Retrying without BTF.\n",
|
||||
map->name, strerror(errno), errno);
|
||||
create_attr.btf_fd = 0;
|
||||
create_attr.btf_key_id = 0;
|
||||
create_attr.btf_value_id = 0;
|
||||
map->btf_key_id = 0;
|
||||
map->btf_value_id = 0;
|
||||
*pfd = bpf_create_map_xattr(&create_attr);
|
||||
}
|
||||
|
||||
*pfd = bpf_create_map_name(def->type,
|
||||
obj->maps[i].name,
|
||||
def->key_size,
|
||||
def->value_size,
|
||||
def->max_entries,
|
||||
def->map_flags);
|
||||
if (*pfd < 0) {
|
||||
size_t j;
|
||||
int err = *pfd;
|
||||
|
||||
err = *pfd;
|
||||
pr_warning("failed to create map (name: '%s'): %s\n",
|
||||
obj->maps[i].name,
|
||||
map->name,
|
||||
strerror(errno));
|
||||
for (j = 0; j < i; j++)
|
||||
zclose(obj->maps[j].fd);
|
||||
return err;
|
||||
}
|
||||
pr_debug("create map %s: fd=%d\n", obj->maps[i].name, *pfd);
|
||||
pr_debug("create map %s: fd=%d\n", map->name, *pfd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1641,6 +1747,7 @@ void bpf_object__close(struct bpf_object *obj)
|
|||
|
||||
bpf_object__elf_finish(obj);
|
||||
bpf_object__unload(obj);
|
||||
btf__free(obj->btf);
|
||||
|
||||
for (i = 0; i < obj->nr_maps; i++) {
|
||||
zfree(&obj->maps[i].name);
|
||||
|
@ -1692,6 +1799,11 @@ unsigned int bpf_object__kversion(struct bpf_object *obj)
|
|||
return obj ? obj->kern_version : 0;
|
||||
}
|
||||
|
||||
int bpf_object__btf_fd(const struct bpf_object *obj)
|
||||
{
|
||||
return obj->btf ? btf__fd(obj->btf) : -1;
|
||||
}
|
||||
|
||||
int bpf_object__set_priv(struct bpf_object *obj, void *priv,
|
||||
bpf_object_clear_priv_t clear_priv)
|
||||
{
|
||||
|
@ -1937,6 +2049,16 @@ const char *bpf_map__name(struct bpf_map *map)
|
|||
return map ? map->name : NULL;
|
||||
}
|
||||
|
||||
uint32_t bpf_map__btf_key_id(const struct bpf_map *map)
|
||||
{
|
||||
return map ? map->btf_key_id : 0;
|
||||
}
|
||||
|
||||
uint32_t bpf_map__btf_value_id(const struct bpf_map *map)
|
||||
{
|
||||
return map ? map->btf_value_id : 0;
|
||||
}
|
||||
|
||||
int bpf_map__set_priv(struct bpf_map *map, void *priv,
|
||||
bpf_map_clear_priv_t clear_priv)
|
||||
{
|
||||
|
|
|
@ -78,6 +78,7 @@ int bpf_object__load(struct bpf_object *obj);
|
|||
int bpf_object__unload(struct bpf_object *obj);
|
||||
const char *bpf_object__name(struct bpf_object *obj);
|
||||
unsigned int bpf_object__kversion(struct bpf_object *obj);
|
||||
int bpf_object__btf_fd(const struct bpf_object *obj);
|
||||
|
||||
struct bpf_object *bpf_object__next(struct bpf_object *prev);
|
||||
#define bpf_object__for_each_safe(pos, tmp) \
|
||||
|
@ -241,6 +242,8 @@ bpf_map__next(struct bpf_map *map, struct bpf_object *obj);
|
|||
int bpf_map__fd(struct bpf_map *map);
|
||||
const struct bpf_map_def *bpf_map__def(struct bpf_map *map);
|
||||
const char *bpf_map__name(struct bpf_map *map);
|
||||
uint32_t bpf_map__btf_key_id(const struct bpf_map *map);
|
||||
uint32_t bpf_map__btf_value_id(const struct bpf_map *map);
|
||||
|
||||
typedef void (*bpf_map_clear_priv_t)(struct bpf_map *, void *);
|
||||
int bpf_map__set_priv(struct bpf_map *map, void *priv,
|
||||
|
|
|
@ -24,14 +24,15 @@ urandom_read: urandom_read.c
|
|||
# Order correspond to 'make run_tests' order
|
||||
TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \
|
||||
test_align test_verifier_log test_dev_cgroup test_tcpbpf_user \
|
||||
test_sock test_sock_addr
|
||||
test_sock test_sock_addr test_btf
|
||||
|
||||
TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \
|
||||
test_pkt_md_access.o test_xdp_redirect.o test_xdp_meta.o sockmap_parse_prog.o \
|
||||
sockmap_verdict_prog.o dev_cgroup.o sample_ret0.o test_tracepoint.o \
|
||||
test_l4lb_noinline.o test_xdp_noinline.o test_stacktrace_map.o \
|
||||
sample_map_ret0.o test_tcpbpf_kern.o test_stacktrace_build_id.o \
|
||||
sockmap_tcp_msg_prog.o connect4_prog.o connect6_prog.o test_adjust_tail.o
|
||||
sockmap_tcp_msg_prog.o connect4_prog.o connect6_prog.o test_adjust_tail.o \
|
||||
test_btf_haskv.o test_btf_nokv.o
|
||||
|
||||
# Order correspond to 'make run_tests' order
|
||||
TEST_PROGS := test_kmod.sh \
|
||||
|
@ -66,6 +67,8 @@ $(BPFOBJ): force
|
|||
|
||||
CLANG ?= clang
|
||||
LLC ?= llc
|
||||
LLVM_OBJCOPY ?= llvm-objcopy
|
||||
BTF_PAHOLE ?= pahole
|
||||
|
||||
PROBE := $(shell $(LLC) -march=bpf -mcpu=probe -filetype=null /dev/null 2>&1)
|
||||
|
||||
|
@ -83,9 +86,26 @@ CLANG_FLAGS = -I. -I./include/uapi -I../../../include/uapi \
|
|||
$(OUTPUT)/test_l4lb_noinline.o: CLANG_FLAGS += -fno-inline
|
||||
$(OUTPUT)/test_xdp_noinline.o: CLANG_FLAGS += -fno-inline
|
||||
|
||||
BTF_LLC_PROBE := $(shell $(LLC) -march=bpf -mattr=help |& grep dwarfris)
|
||||
BTF_PAHOLE_PROBE := $(shell $(BTF_PAHOLE) --help |& grep BTF)
|
||||
BTF_OBJCOPY_PROBE := $(shell $(LLVM_OBJCOPY) --version |& grep LLVM)
|
||||
|
||||
ifneq ($(BTF_LLC_PROBE),)
|
||||
ifneq ($(BTF_PAHOLE_PROBE),)
|
||||
ifneq ($(BTF_OBJCOPY_PROBE),)
|
||||
CLANG_FLAGS += -g
|
||||
LLC_FLAGS += -mattr=dwarfris
|
||||
DWARF2BTF = y
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
$(OUTPUT)/%.o: %.c
|
||||
$(CLANG) $(CLANG_FLAGS) \
|
||||
-O2 -target bpf -emit-llvm -c $< -o - | \
|
||||
$(LLC) -march=bpf -mcpu=$(CPU) -filetype=obj -o $@
|
||||
$(LLC) -march=bpf -mcpu=$(CPU) $(LLC_FLAGS) -filetype=obj -o $@
|
||||
ifeq ($(DWARF2BTF),y)
|
||||
$(BTF_PAHOLE) -J $@
|
||||
endif
|
||||
|
||||
EXTRA_CLEAN := $(TEST_CUSTOM_PROGS)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,48 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2018 Facebook */
|
||||
#include <linux/bpf.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
int _version SEC("version") = 1;
|
||||
|
||||
struct ipv_counts {
|
||||
unsigned int v4;
|
||||
unsigned int v6;
|
||||
};
|
||||
|
||||
typedef int btf_map_key;
|
||||
typedef struct ipv_counts btf_map_value;
|
||||
btf_map_key dumm_key;
|
||||
btf_map_value dummy_value;
|
||||
|
||||
struct bpf_map_def SEC("maps") btf_map = {
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
.key_size = sizeof(int),
|
||||
.value_size = sizeof(struct ipv_counts),
|
||||
.max_entries = 4,
|
||||
};
|
||||
|
||||
struct dummy_tracepoint_args {
|
||||
unsigned long long pad;
|
||||
struct sock *sock;
|
||||
};
|
||||
|
||||
SEC("dummy_tracepoint")
|
||||
int _dummy_tracepoint(struct dummy_tracepoint_args *arg)
|
||||
{
|
||||
struct ipv_counts *counts;
|
||||
int key = 0;
|
||||
|
||||
if (!arg->sock)
|
||||
return 0;
|
||||
|
||||
counts = bpf_map_lookup_elem(&btf_map, &key);
|
||||
if (!counts)
|
||||
return 0;
|
||||
|
||||
counts->v6++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
|
@ -0,0 +1,43 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2018 Facebook */
|
||||
#include <linux/bpf.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
int _version SEC("version") = 1;
|
||||
|
||||
struct ipv_counts {
|
||||
unsigned int v4;
|
||||
unsigned int v6;
|
||||
};
|
||||
|
||||
struct bpf_map_def SEC("maps") btf_map = {
|
||||
.type = BPF_MAP_TYPE_ARRAY,
|
||||
.key_size = sizeof(int),
|
||||
.value_size = sizeof(struct ipv_counts),
|
||||
.max_entries = 4,
|
||||
};
|
||||
|
||||
struct dummy_tracepoint_args {
|
||||
unsigned long long pad;
|
||||
struct sock *sock;
|
||||
};
|
||||
|
||||
SEC("dummy_tracepoint")
|
||||
int _dummy_tracepoint(struct dummy_tracepoint_args *arg)
|
||||
{
|
||||
struct ipv_counts *counts;
|
||||
int key = 0;
|
||||
|
||||
if (!arg->sock)
|
||||
return 0;
|
||||
|
||||
counts = bpf_map_lookup_elem(&btf_map, &key);
|
||||
if (!counts)
|
||||
return 0;
|
||||
|
||||
counts->v6++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
Loading…
Reference in New Issue