Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next
Daniel Borkmann says: ==================== pull-request: bpf-next 2019-07-09 The following pull-request contains BPF updates for your *net-next* tree. The main changes are: 1) Lots of libbpf improvements: i) addition of new APIs to attach BPF programs to tracing entities such as {k,u}probes or tracepoints, ii) improve specification of BTF-defined maps by eliminating the need for data initialization for some of the members, iii) addition of a high-level API for setting up and polling perf buffers for BPF event output helpers, all from Andrii. 2) Add "prog run" subcommand to bpftool in order to test-run programs through the kernel testing infrastructure of BPF, from Quentin. 3) Improve verifier for BPF sockaddr programs to support 8-byte stores for user_ip6 and msg_src_ip6 members given clang tends to generate such stores, from Stanislav. 4) Enable the new BPF JIT zero-extension optimization for further riscv64 ALU ops, from Luke. 5) Fix a bpftool json JIT dump crash on powerpc, from Jiri. 6) Fix an AF_XDP race in generic XDP's receive path, from Ilya. 7) Various smaller fixes from Ilya, Yue and Arnd. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
17ccf9e31e
|
@ -757,31 +757,31 @@ static int emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
|
||||||
case BPF_ALU | BPF_ADD | BPF_X:
|
case BPF_ALU | BPF_ADD | BPF_X:
|
||||||
case BPF_ALU64 | BPF_ADD | BPF_X:
|
case BPF_ALU64 | BPF_ADD | BPF_X:
|
||||||
emit(is64 ? rv_add(rd, rd, rs) : rv_addw(rd, rd, rs), ctx);
|
emit(is64 ? rv_add(rd, rd, rs) : rv_addw(rd, rd, rs), ctx);
|
||||||
if (!is64)
|
if (!is64 && !aux->verifier_zext)
|
||||||
emit_zext_32(rd, ctx);
|
emit_zext_32(rd, ctx);
|
||||||
break;
|
break;
|
||||||
case BPF_ALU | BPF_SUB | BPF_X:
|
case BPF_ALU | BPF_SUB | BPF_X:
|
||||||
case BPF_ALU64 | BPF_SUB | BPF_X:
|
case BPF_ALU64 | BPF_SUB | BPF_X:
|
||||||
emit(is64 ? rv_sub(rd, rd, rs) : rv_subw(rd, rd, rs), ctx);
|
emit(is64 ? rv_sub(rd, rd, rs) : rv_subw(rd, rd, rs), ctx);
|
||||||
if (!is64)
|
if (!is64 && !aux->verifier_zext)
|
||||||
emit_zext_32(rd, ctx);
|
emit_zext_32(rd, ctx);
|
||||||
break;
|
break;
|
||||||
case BPF_ALU | BPF_AND | BPF_X:
|
case BPF_ALU | BPF_AND | BPF_X:
|
||||||
case BPF_ALU64 | BPF_AND | BPF_X:
|
case BPF_ALU64 | BPF_AND | BPF_X:
|
||||||
emit(rv_and(rd, rd, rs), ctx);
|
emit(rv_and(rd, rd, rs), ctx);
|
||||||
if (!is64)
|
if (!is64 && !aux->verifier_zext)
|
||||||
emit_zext_32(rd, ctx);
|
emit_zext_32(rd, ctx);
|
||||||
break;
|
break;
|
||||||
case BPF_ALU | BPF_OR | BPF_X:
|
case BPF_ALU | BPF_OR | BPF_X:
|
||||||
case BPF_ALU64 | BPF_OR | BPF_X:
|
case BPF_ALU64 | BPF_OR | BPF_X:
|
||||||
emit(rv_or(rd, rd, rs), ctx);
|
emit(rv_or(rd, rd, rs), ctx);
|
||||||
if (!is64)
|
if (!is64 && !aux->verifier_zext)
|
||||||
emit_zext_32(rd, ctx);
|
emit_zext_32(rd, ctx);
|
||||||
break;
|
break;
|
||||||
case BPF_ALU | BPF_XOR | BPF_X:
|
case BPF_ALU | BPF_XOR | BPF_X:
|
||||||
case BPF_ALU64 | BPF_XOR | BPF_X:
|
case BPF_ALU64 | BPF_XOR | BPF_X:
|
||||||
emit(rv_xor(rd, rd, rs), ctx);
|
emit(rv_xor(rd, rd, rs), ctx);
|
||||||
if (!is64)
|
if (!is64 && !aux->verifier_zext)
|
||||||
emit_zext_32(rd, ctx);
|
emit_zext_32(rd, ctx);
|
||||||
break;
|
break;
|
||||||
case BPF_ALU | BPF_MUL | BPF_X:
|
case BPF_ALU | BPF_MUL | BPF_X:
|
||||||
|
@ -811,13 +811,13 @@ static int emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
|
||||||
case BPF_ALU | BPF_RSH | BPF_X:
|
case BPF_ALU | BPF_RSH | BPF_X:
|
||||||
case BPF_ALU64 | BPF_RSH | BPF_X:
|
case BPF_ALU64 | BPF_RSH | BPF_X:
|
||||||
emit(is64 ? rv_srl(rd, rd, rs) : rv_srlw(rd, rd, rs), ctx);
|
emit(is64 ? rv_srl(rd, rd, rs) : rv_srlw(rd, rd, rs), ctx);
|
||||||
if (!is64)
|
if (!is64 && !aux->verifier_zext)
|
||||||
emit_zext_32(rd, ctx);
|
emit_zext_32(rd, ctx);
|
||||||
break;
|
break;
|
||||||
case BPF_ALU | BPF_ARSH | BPF_X:
|
case BPF_ALU | BPF_ARSH | BPF_X:
|
||||||
case BPF_ALU64 | BPF_ARSH | BPF_X:
|
case BPF_ALU64 | BPF_ARSH | BPF_X:
|
||||||
emit(is64 ? rv_sra(rd, rd, rs) : rv_sraw(rd, rd, rs), ctx);
|
emit(is64 ? rv_sra(rd, rd, rs) : rv_sraw(rd, rd, rs), ctx);
|
||||||
if (!is64)
|
if (!is64 && !aux->verifier_zext)
|
||||||
emit_zext_32(rd, ctx);
|
emit_zext_32(rd, ctx);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -826,7 +826,7 @@ static int emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
|
||||||
case BPF_ALU64 | BPF_NEG:
|
case BPF_ALU64 | BPF_NEG:
|
||||||
emit(is64 ? rv_sub(rd, RV_REG_ZERO, rd) :
|
emit(is64 ? rv_sub(rd, RV_REG_ZERO, rd) :
|
||||||
rv_subw(rd, RV_REG_ZERO, rd), ctx);
|
rv_subw(rd, RV_REG_ZERO, rd), ctx);
|
||||||
if (!is64)
|
if (!is64 && !aux->verifier_zext)
|
||||||
emit_zext_32(rd, ctx);
|
emit_zext_32(rd, ctx);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -747,6 +747,12 @@ bpf_ctx_narrow_access_ok(u32 off, u32 size, u32 size_default)
|
||||||
return size <= size_default && (size & (size - 1)) == 0;
|
return size <= size_default && (size & (size - 1)) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define bpf_ctx_wide_store_ok(off, size, type, field) \
|
||||||
|
(size == sizeof(__u64) && \
|
||||||
|
off >= offsetof(type, field) && \
|
||||||
|
off + sizeof(__u64) <= offsetofend(type, field) && \
|
||||||
|
off % sizeof(__u64) == 0)
|
||||||
|
|
||||||
#define bpf_classic_proglen(fprog) (fprog->len * sizeof(fprog->filter[0]))
|
#define bpf_classic_proglen(fprog) (fprog->len * sizeof(fprog->filter[0]))
|
||||||
|
|
||||||
static inline void bpf_prog_lock_ro(struct bpf_prog *fp)
|
static inline void bpf_prog_lock_ro(struct bpf_prog *fp)
|
||||||
|
|
|
@ -2223,9 +2223,7 @@ static inline bool tcp_bpf_ca_needs_ecn(struct sock *sk)
|
||||||
|
|
||||||
static inline void tcp_bpf_rtt(struct sock *sk)
|
static inline void tcp_bpf_rtt(struct sock *sk)
|
||||||
{
|
{
|
||||||
struct tcp_sock *tp = tcp_sk(sk);
|
if (BPF_SOCK_OPS_TEST_FLAG(tcp_sk(sk), BPF_SOCK_OPS_RTT_CB_FLAG))
|
||||||
|
|
||||||
if (BPF_SOCK_OPS_TEST_FLAG(tp, BPF_SOCK_OPS_RTT_CB_FLAG))
|
|
||||||
tcp_call_bpf(sk, BPF_SOCK_OPS_RTT_CB, 0, NULL);
|
tcp_call_bpf(sk, BPF_SOCK_OPS_RTT_CB, 0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,8 @@ struct xdp_sock {
|
||||||
* in the SKB destructor callback.
|
* in the SKB destructor callback.
|
||||||
*/
|
*/
|
||||||
spinlock_t tx_completion_lock;
|
spinlock_t tx_completion_lock;
|
||||||
|
/* Protects generic receive. */
|
||||||
|
spinlock_t rx_lock;
|
||||||
u64 rx_dropped;
|
u64 rx_dropped;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3247,7 +3247,7 @@ struct bpf_sock_addr {
|
||||||
__u32 user_ip4; /* Allows 1,2,4-byte read and 4-byte write.
|
__u32 user_ip4; /* Allows 1,2,4-byte read and 4-byte write.
|
||||||
* Stored in network byte order.
|
* Stored in network byte order.
|
||||||
*/
|
*/
|
||||||
__u32 user_ip6[4]; /* Allows 1,2,4-byte read an 4-byte write.
|
__u32 user_ip6[4]; /* Allows 1,2,4-byte read and 4,8-byte write.
|
||||||
* Stored in network byte order.
|
* Stored in network byte order.
|
||||||
*/
|
*/
|
||||||
__u32 user_port; /* Allows 4-byte read and write.
|
__u32 user_port; /* Allows 4-byte read and write.
|
||||||
|
@ -3256,10 +3256,10 @@ struct bpf_sock_addr {
|
||||||
__u32 family; /* Allows 4-byte read, but no write */
|
__u32 family; /* Allows 4-byte read, but no write */
|
||||||
__u32 type; /* Allows 4-byte read, but no write */
|
__u32 type; /* Allows 4-byte read, but no write */
|
||||||
__u32 protocol; /* Allows 4-byte read, but no write */
|
__u32 protocol; /* Allows 4-byte read, but no write */
|
||||||
__u32 msg_src_ip4; /* Allows 1,2,4-byte read an 4-byte write.
|
__u32 msg_src_ip4; /* Allows 1,2,4-byte read and 4-byte write.
|
||||||
* Stored in network byte order.
|
* Stored in network byte order.
|
||||||
*/
|
*/
|
||||||
__u32 msg_src_ip6[4]; /* Allows 1,2,4-byte read an 4-byte write.
|
__u32 msg_src_ip6[4]; /* Allows 1,2,4-byte read and 4,8-byte write.
|
||||||
* Stored in network byte order.
|
* Stored in network byte order.
|
||||||
*/
|
*/
|
||||||
__bpf_md_ptr(struct bpf_sock *, sk);
|
__bpf_md_ptr(struct bpf_sock *, sk);
|
||||||
|
|
|
@ -939,6 +939,7 @@ int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(__cgroup_bpf_run_filter_sysctl);
|
EXPORT_SYMBOL(__cgroup_bpf_run_filter_sysctl);
|
||||||
|
|
||||||
|
#ifdef CONFIG_NET
|
||||||
static bool __cgroup_bpf_prog_array_is_empty(struct cgroup *cgrp,
|
static bool __cgroup_bpf_prog_array_is_empty(struct cgroup *cgrp,
|
||||||
enum bpf_attach_type attach_type)
|
enum bpf_attach_type attach_type)
|
||||||
{
|
{
|
||||||
|
@ -1120,6 +1121,7 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(__cgroup_bpf_run_filter_getsockopt);
|
EXPORT_SYMBOL(__cgroup_bpf_run_filter_getsockopt);
|
||||||
|
#endif
|
||||||
|
|
||||||
static ssize_t sysctl_cpy_dir(const struct ctl_dir *dir, char **bufp,
|
static ssize_t sysctl_cpy_dir(const struct ctl_dir *dir, char **bufp,
|
||||||
size_t *lenp)
|
size_t *lenp)
|
||||||
|
@ -1386,10 +1388,12 @@ static const struct bpf_func_proto *
|
||||||
cg_sockopt_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
cg_sockopt_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
||||||
{
|
{
|
||||||
switch (func_id) {
|
switch (func_id) {
|
||||||
|
#ifdef CONFIG_NET
|
||||||
case BPF_FUNC_sk_storage_get:
|
case BPF_FUNC_sk_storage_get:
|
||||||
return &bpf_sk_storage_get_proto;
|
return &bpf_sk_storage_get_proto;
|
||||||
case BPF_FUNC_sk_storage_delete:
|
case BPF_FUNC_sk_storage_delete:
|
||||||
return &bpf_sk_storage_delete_proto;
|
return &bpf_sk_storage_delete_proto;
|
||||||
|
#endif
|
||||||
#ifdef CONFIG_INET
|
#ifdef CONFIG_INET
|
||||||
case BPF_FUNC_tcp_sock:
|
case BPF_FUNC_tcp_sock:
|
||||||
return &bpf_tcp_sock_proto;
|
return &bpf_tcp_sock_proto;
|
||||||
|
|
|
@ -6890,6 +6890,16 @@ static bool sock_addr_is_valid_access(int off, int size,
|
||||||
if (!bpf_ctx_narrow_access_ok(off, size, size_default))
|
if (!bpf_ctx_narrow_access_ok(off, size, size_default))
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
|
if (bpf_ctx_wide_store_ok(off, size,
|
||||||
|
struct bpf_sock_addr,
|
||||||
|
user_ip6))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (bpf_ctx_wide_store_ok(off, size,
|
||||||
|
struct bpf_sock_addr,
|
||||||
|
msg_src_ip6))
|
||||||
|
return true;
|
||||||
|
|
||||||
if (size != size_default)
|
if (size != size_default)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -7730,9 +7740,6 @@ static u32 xdp_convert_ctx_access(enum bpf_access_type type,
|
||||||
/* SOCK_ADDR_STORE_NESTED_FIELD_OFF() has semantic similar to
|
/* SOCK_ADDR_STORE_NESTED_FIELD_OFF() has semantic similar to
|
||||||
* SOCK_ADDR_LOAD_NESTED_FIELD_SIZE_OFF() but for store operation.
|
* SOCK_ADDR_LOAD_NESTED_FIELD_SIZE_OFF() but for store operation.
|
||||||
*
|
*
|
||||||
* It doesn't support SIZE argument though since narrow stores are not
|
|
||||||
* supported for now.
|
|
||||||
*
|
|
||||||
* In addition it uses Temporary Field TF (member of struct S) as the 3rd
|
* In addition it uses Temporary Field TF (member of struct S) as the 3rd
|
||||||
* "register" since two registers available in convert_ctx_access are not
|
* "register" since two registers available in convert_ctx_access are not
|
||||||
* enough: we can't override neither SRC, since it contains value to store, nor
|
* enough: we can't override neither SRC, since it contains value to store, nor
|
||||||
|
@ -7740,7 +7747,7 @@ static u32 xdp_convert_ctx_access(enum bpf_access_type type,
|
||||||
* instructions. But we need a temporary place to save pointer to nested
|
* instructions. But we need a temporary place to save pointer to nested
|
||||||
* structure whose field we want to store to.
|
* structure whose field we want to store to.
|
||||||
*/
|
*/
|
||||||
#define SOCK_ADDR_STORE_NESTED_FIELD_OFF(S, NS, F, NF, OFF, TF) \
|
#define SOCK_ADDR_STORE_NESTED_FIELD_OFF(S, NS, F, NF, SIZE, OFF, TF) \
|
||||||
do { \
|
do { \
|
||||||
int tmp_reg = BPF_REG_9; \
|
int tmp_reg = BPF_REG_9; \
|
||||||
if (si->src_reg == tmp_reg || si->dst_reg == tmp_reg) \
|
if (si->src_reg == tmp_reg || si->dst_reg == tmp_reg) \
|
||||||
|
@ -7751,8 +7758,7 @@ static u32 xdp_convert_ctx_access(enum bpf_access_type type,
|
||||||
offsetof(S, TF)); \
|
offsetof(S, TF)); \
|
||||||
*insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(S, F), tmp_reg, \
|
*insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(S, F), tmp_reg, \
|
||||||
si->dst_reg, offsetof(S, F)); \
|
si->dst_reg, offsetof(S, F)); \
|
||||||
*insn++ = BPF_STX_MEM( \
|
*insn++ = BPF_STX_MEM(SIZE, tmp_reg, si->src_reg, \
|
||||||
BPF_FIELD_SIZEOF(NS, NF), tmp_reg, si->src_reg, \
|
|
||||||
bpf_target_off(NS, NF, FIELD_SIZEOF(NS, NF), \
|
bpf_target_off(NS, NF, FIELD_SIZEOF(NS, NF), \
|
||||||
target_size) \
|
target_size) \
|
||||||
+ OFF); \
|
+ OFF); \
|
||||||
|
@ -7764,8 +7770,8 @@ static u32 xdp_convert_ctx_access(enum bpf_access_type type,
|
||||||
TF) \
|
TF) \
|
||||||
do { \
|
do { \
|
||||||
if (type == BPF_WRITE) { \
|
if (type == BPF_WRITE) { \
|
||||||
SOCK_ADDR_STORE_NESTED_FIELD_OFF(S, NS, F, NF, OFF, \
|
SOCK_ADDR_STORE_NESTED_FIELD_OFF(S, NS, F, NF, SIZE, \
|
||||||
TF); \
|
OFF, TF); \
|
||||||
} else { \
|
} else { \
|
||||||
SOCK_ADDR_LOAD_NESTED_FIELD_SIZE_OFF( \
|
SOCK_ADDR_LOAD_NESTED_FIELD_SIZE_OFF( \
|
||||||
S, NS, F, NF, SIZE, OFF); \
|
S, NS, F, NF, SIZE, OFF); \
|
||||||
|
|
|
@ -129,13 +129,17 @@ int xsk_generic_rcv(struct xdp_sock *xs, struct xdp_buff *xdp)
|
||||||
u64 addr;
|
u64 addr;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (xs->dev != xdp->rxq->dev || xs->queue_id != xdp->rxq->queue_index)
|
spin_lock_bh(&xs->rx_lock);
|
||||||
return -EINVAL;
|
|
||||||
|
if (xs->dev != xdp->rxq->dev || xs->queue_id != xdp->rxq->queue_index) {
|
||||||
|
err = -EINVAL;
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
if (!xskq_peek_addr(xs->umem->fq, &addr) ||
|
if (!xskq_peek_addr(xs->umem->fq, &addr) ||
|
||||||
len > xs->umem->chunk_size_nohr - XDP_PACKET_HEADROOM) {
|
len > xs->umem->chunk_size_nohr - XDP_PACKET_HEADROOM) {
|
||||||
xs->rx_dropped++;
|
err = -ENOSPC;
|
||||||
return -ENOSPC;
|
goto out_drop;
|
||||||
}
|
}
|
||||||
|
|
||||||
addr += xs->umem->headroom;
|
addr += xs->umem->headroom;
|
||||||
|
@ -144,13 +148,21 @@ int xsk_generic_rcv(struct xdp_sock *xs, struct xdp_buff *xdp)
|
||||||
memcpy(buffer, xdp->data_meta, len + metalen);
|
memcpy(buffer, xdp->data_meta, len + metalen);
|
||||||
addr += metalen;
|
addr += metalen;
|
||||||
err = xskq_produce_batch_desc(xs->rx, addr, len);
|
err = xskq_produce_batch_desc(xs->rx, addr, len);
|
||||||
if (!err) {
|
if (err)
|
||||||
xskq_discard_addr(xs->umem->fq);
|
goto out_drop;
|
||||||
xsk_flush(xs);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
xskq_discard_addr(xs->umem->fq);
|
||||||
|
xskq_produce_flush_desc(xs->rx);
|
||||||
|
|
||||||
|
spin_unlock_bh(&xs->rx_lock);
|
||||||
|
|
||||||
|
xs->sk.sk_data_ready(&xs->sk);
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
out_drop:
|
||||||
xs->rx_dropped++;
|
xs->rx_dropped++;
|
||||||
|
out_unlock:
|
||||||
|
spin_unlock_bh(&xs->rx_lock);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -787,6 +799,7 @@ static int xsk_create(struct net *net, struct socket *sock, int protocol,
|
||||||
|
|
||||||
xs = xdp_sk(sk);
|
xs = xdp_sk(sk);
|
||||||
mutex_init(&xs->mutex);
|
mutex_init(&xs->mutex);
|
||||||
|
spin_lock_init(&xs->rx_lock);
|
||||||
spin_lock_init(&xs->tx_completion_lock);
|
spin_lock_init(&xs->tx_completion_lock);
|
||||||
|
|
||||||
mutex_lock(&net->xdp.lock);
|
mutex_lock(&net->xdp.lock);
|
||||||
|
|
|
@ -29,6 +29,7 @@ PROG COMMANDS
|
||||||
| **bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*]
|
| **bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*]
|
||||||
| **bpftool** **prog detach** *PROG* *ATTACH_TYPE* [*MAP*]
|
| **bpftool** **prog detach** *PROG* *ATTACH_TYPE* [*MAP*]
|
||||||
| **bpftool** **prog tracelog**
|
| **bpftool** **prog tracelog**
|
||||||
|
| **bpftool** **prog run** *PROG* **data_in** *FILE* [**data_out** *FILE* [**data_size_out** *L*]] [**ctx_in** *FILE* [**ctx_out** *FILE* [**ctx_size_out** *M*]]] [**repeat** *N*]
|
||||||
| **bpftool** **prog help**
|
| **bpftool** **prog help**
|
||||||
|
|
|
|
||||||
| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
|
| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
|
||||||
|
@ -146,6 +147,39 @@ DESCRIPTION
|
||||||
streaming data from BPF programs to user space, one can use
|
streaming data from BPF programs to user space, one can use
|
||||||
perf events (see also **bpftool-map**\ (8)).
|
perf events (see also **bpftool-map**\ (8)).
|
||||||
|
|
||||||
|
**bpftool prog run** *PROG* **data_in** *FILE* [**data_out** *FILE* [**data_size_out** *L*]] [**ctx_in** *FILE* [**ctx_out** *FILE* [**ctx_size_out** *M*]]] [**repeat** *N*]
|
||||||
|
Run BPF program *PROG* in the kernel testing infrastructure
|
||||||
|
for BPF, meaning that the program works on the data and
|
||||||
|
context provided by the user, and not on actual packets or
|
||||||
|
monitored functions etc. Return value and duration for the
|
||||||
|
test run are printed out to the console.
|
||||||
|
|
||||||
|
Input data is read from the *FILE* passed with **data_in**.
|
||||||
|
If this *FILE* is "**-**", input data is read from standard
|
||||||
|
input. Input context, if any, is read from *FILE* passed with
|
||||||
|
**ctx_in**. Again, "**-**" can be used to read from standard
|
||||||
|
input, but only if standard input is not already in use for
|
||||||
|
input data. If a *FILE* is passed with **data_out**, output
|
||||||
|
data is written to that file. Similarly, output context is
|
||||||
|
written to the *FILE* passed with **ctx_out**. For both
|
||||||
|
output flows, "**-**" can be used to print to the standard
|
||||||
|
output (as plain text, or JSON if relevant option was
|
||||||
|
passed). If output keywords are omitted, output data and
|
||||||
|
context are discarded. Keywords **data_size_out** and
|
||||||
|
**ctx_size_out** are used to pass the size (in bytes) for the
|
||||||
|
output buffers to the kernel, although the default of 32 kB
|
||||||
|
should be more than enough for most cases.
|
||||||
|
|
||||||
|
Keyword **repeat** is used to indicate the number of
|
||||||
|
consecutive runs to perform. Note that output data and
|
||||||
|
context printed to files correspond to the last of those
|
||||||
|
runs. The duration printed out at the end of the runs is an
|
||||||
|
average over all runs performed by the command.
|
||||||
|
|
||||||
|
Not all program types support test run. Among those which do,
|
||||||
|
not all of them can take the **ctx_in**/**ctx_out**
|
||||||
|
arguments. bpftool does not perform checks on program types.
|
||||||
|
|
||||||
**bpftool prog help**
|
**bpftool prog help**
|
||||||
Print short help message.
|
Print short help message.
|
||||||
|
|
||||||
|
|
|
@ -342,6 +342,13 @@ _bpftool()
|
||||||
load|loadall)
|
load|loadall)
|
||||||
local obj
|
local obj
|
||||||
|
|
||||||
|
# Propose "load/loadall" to complete "bpftool prog load",
|
||||||
|
# or bash tries to complete "load" as a filename below.
|
||||||
|
if [[ ${#words[@]} -eq 3 ]]; then
|
||||||
|
COMPREPLY=( $( compgen -W "load loadall" -- "$cur" ) )
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ ${#words[@]} -lt 6 ]]; then
|
if [[ ${#words[@]} -lt 6 ]]; then
|
||||||
_filedir
|
_filedir
|
||||||
return 0
|
return 0
|
||||||
|
@ -408,10 +415,34 @@ _bpftool()
|
||||||
tracelog)
|
tracelog)
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
|
run)
|
||||||
|
if [[ ${#words[@]} -lt 5 ]]; then
|
||||||
|
_filedir
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
case $prev in
|
||||||
|
id)
|
||||||
|
_bpftool_get_prog_ids
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
data_in|data_out|ctx_in|ctx_out)
|
||||||
|
_filedir
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
repeat|data_size_out|ctx_size_out)
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
_bpftool_once_attr 'data_in data_out data_size_out \
|
||||||
|
ctx_in ctx_out ctx_size_out repeat'
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
[[ $prev == $object ]] && \
|
[[ $prev == $object ]] && \
|
||||||
COMPREPLY=( $( compgen -W 'dump help pin attach detach load \
|
COMPREPLY=( $( compgen -W 'dump help pin attach detach \
|
||||||
show list tracelog' -- "$cur" ) )
|
load loadall show list tracelog run' -- "$cur" ) )
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
;;
|
;;
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
* Licensed under the GNU General Public License, version 2.0 (GPLv2)
|
* Licensed under the GNU General Public License, version 2.0 (GPLv2)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -44,11 +46,13 @@ static int fprintf_json(void *out, const char *fmt, ...)
|
||||||
char *s;
|
char *s;
|
||||||
|
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
|
if (vasprintf(&s, fmt, ap) < 0)
|
||||||
|
return -1;
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
if (!oper_count) {
|
if (!oper_count) {
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
s = va_arg(ap, char *);
|
|
||||||
|
|
||||||
/* Strip trailing spaces */
|
/* Strip trailing spaces */
|
||||||
i = strlen(s) - 1;
|
i = strlen(s) - 1;
|
||||||
while (s[i] == ' ')
|
while (s[i] == ' ')
|
||||||
|
@ -61,11 +65,10 @@ static int fprintf_json(void *out, const char *fmt, ...)
|
||||||
} else if (!strcmp(fmt, ",")) {
|
} else if (!strcmp(fmt, ",")) {
|
||||||
/* Skip */
|
/* Skip */
|
||||||
} else {
|
} else {
|
||||||
s = va_arg(ap, char *);
|
|
||||||
jsonw_string(json_wtr, s);
|
jsonw_string(json_wtr, s);
|
||||||
oper_count++;
|
oper_count++;
|
||||||
}
|
}
|
||||||
va_end(ap);
|
free(s);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -117,6 +117,35 @@ bool is_prefix(const char *pfx, const char *str)
|
||||||
return !memcmp(str, pfx, strlen(pfx));
|
return !memcmp(str, pfx, strlen(pfx));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Last argument MUST be NULL pointer */
|
||||||
|
int detect_common_prefix(const char *arg, ...)
|
||||||
|
{
|
||||||
|
unsigned int count = 0;
|
||||||
|
const char *ref;
|
||||||
|
char msg[256];
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
snprintf(msg, sizeof(msg), "ambiguous prefix: '%s' could be '", arg);
|
||||||
|
va_start(ap, arg);
|
||||||
|
while ((ref = va_arg(ap, const char *))) {
|
||||||
|
if (!is_prefix(arg, ref))
|
||||||
|
continue;
|
||||||
|
count++;
|
||||||
|
if (count > 1)
|
||||||
|
strncat(msg, "' or '", sizeof(msg) - strlen(msg) - 1);
|
||||||
|
strncat(msg, ref, sizeof(msg) - strlen(msg) - 1);
|
||||||
|
}
|
||||||
|
va_end(ap);
|
||||||
|
strncat(msg, "'", sizeof(msg) - strlen(msg) - 1);
|
||||||
|
|
||||||
|
if (count >= 2) {
|
||||||
|
p_err(msg);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep)
|
void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep)
|
||||||
{
|
{
|
||||||
unsigned char *data = arg;
|
unsigned char *data = arg;
|
||||||
|
|
|
@ -101,6 +101,7 @@ void p_err(const char *fmt, ...);
|
||||||
void p_info(const char *fmt, ...);
|
void p_info(const char *fmt, ...);
|
||||||
|
|
||||||
bool is_prefix(const char *pfx, const char *str);
|
bool is_prefix(const char *pfx, const char *str);
|
||||||
|
int detect_common_prefix(const char *arg, ...);
|
||||||
void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep);
|
void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep);
|
||||||
void usage(void) __noreturn;
|
void usage(void) __noreturn;
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
|
|
||||||
#define MMAP_PAGE_CNT 16
|
#define MMAP_PAGE_CNT 16
|
||||||
|
|
||||||
static bool stop;
|
static volatile bool stop;
|
||||||
|
|
||||||
struct event_ring_info {
|
struct event_ring_info {
|
||||||
int fd;
|
int fd;
|
||||||
|
@ -44,32 +44,44 @@ struct perf_event_sample {
|
||||||
unsigned char data[];
|
unsigned char data[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct perf_event_lost {
|
||||||
|
struct perf_event_header header;
|
||||||
|
__u64 id;
|
||||||
|
__u64 lost;
|
||||||
|
};
|
||||||
|
|
||||||
static void int_exit(int signo)
|
static void int_exit(int signo)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Stopping...\n");
|
fprintf(stderr, "Stopping...\n");
|
||||||
stop = true;
|
stop = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct event_pipe_ctx {
|
||||||
|
bool all_cpus;
|
||||||
|
int cpu;
|
||||||
|
int idx;
|
||||||
|
};
|
||||||
|
|
||||||
static enum bpf_perf_event_ret
|
static enum bpf_perf_event_ret
|
||||||
print_bpf_output(struct perf_event_header *event, void *private_data)
|
print_bpf_output(void *private_data, int cpu, struct perf_event_header *event)
|
||||||
{
|
{
|
||||||
struct perf_event_sample *e = container_of(event, struct perf_event_sample,
|
struct perf_event_sample *e = container_of(event,
|
||||||
|
struct perf_event_sample,
|
||||||
header);
|
header);
|
||||||
struct event_ring_info *ring = private_data;
|
struct perf_event_lost *lost = container_of(event,
|
||||||
struct {
|
struct perf_event_lost,
|
||||||
struct perf_event_header header;
|
header);
|
||||||
__u64 id;
|
struct event_pipe_ctx *ctx = private_data;
|
||||||
__u64 lost;
|
int idx = ctx->all_cpus ? cpu : ctx->idx;
|
||||||
} *lost = (typeof(lost))event;
|
|
||||||
|
|
||||||
if (json_output) {
|
if (json_output) {
|
||||||
jsonw_start_object(json_wtr);
|
jsonw_start_object(json_wtr);
|
||||||
jsonw_name(json_wtr, "type");
|
jsonw_name(json_wtr, "type");
|
||||||
jsonw_uint(json_wtr, e->header.type);
|
jsonw_uint(json_wtr, e->header.type);
|
||||||
jsonw_name(json_wtr, "cpu");
|
jsonw_name(json_wtr, "cpu");
|
||||||
jsonw_uint(json_wtr, ring->cpu);
|
jsonw_uint(json_wtr, cpu);
|
||||||
jsonw_name(json_wtr, "index");
|
jsonw_name(json_wtr, "index");
|
||||||
jsonw_uint(json_wtr, ring->key);
|
jsonw_uint(json_wtr, idx);
|
||||||
if (e->header.type == PERF_RECORD_SAMPLE) {
|
if (e->header.type == PERF_RECORD_SAMPLE) {
|
||||||
jsonw_name(json_wtr, "timestamp");
|
jsonw_name(json_wtr, "timestamp");
|
||||||
jsonw_uint(json_wtr, e->time);
|
jsonw_uint(json_wtr, e->time);
|
||||||
|
@ -89,7 +101,7 @@ print_bpf_output(struct perf_event_header *event, void *private_data)
|
||||||
if (e->header.type == PERF_RECORD_SAMPLE) {
|
if (e->header.type == PERF_RECORD_SAMPLE) {
|
||||||
printf("== @%lld.%09lld CPU: %d index: %d =====\n",
|
printf("== @%lld.%09lld CPU: %d index: %d =====\n",
|
||||||
e->time / 1000000000ULL, e->time % 1000000000ULL,
|
e->time / 1000000000ULL, e->time % 1000000000ULL,
|
||||||
ring->cpu, ring->key);
|
cpu, idx);
|
||||||
fprint_hex(stdout, e->data, e->size, " ");
|
fprint_hex(stdout, e->data, e->size, " ");
|
||||||
printf("\n");
|
printf("\n");
|
||||||
} else if (e->header.type == PERF_RECORD_LOST) {
|
} else if (e->header.type == PERF_RECORD_LOST) {
|
||||||
|
@ -103,87 +115,25 @@ print_bpf_output(struct perf_event_header *event, void *private_data)
|
||||||
return LIBBPF_PERF_EVENT_CONT;
|
return LIBBPF_PERF_EVENT_CONT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
int do_event_pipe(int argc, char **argv)
|
||||||
perf_event_read(struct event_ring_info *ring, void **buf, size_t *buf_len)
|
|
||||||
{
|
{
|
||||||
enum bpf_perf_event_ret ret;
|
struct perf_event_attr perf_attr = {
|
||||||
|
|
||||||
ret = bpf_perf_event_read_simple(ring->mem,
|
|
||||||
MMAP_PAGE_CNT * get_page_size(),
|
|
||||||
get_page_size(), buf, buf_len,
|
|
||||||
print_bpf_output, ring);
|
|
||||||
if (ret != LIBBPF_PERF_EVENT_CONT) {
|
|
||||||
fprintf(stderr, "perf read loop failed with %d\n", ret);
|
|
||||||
stop = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int perf_mmap_size(void)
|
|
||||||
{
|
|
||||||
return get_page_size() * (MMAP_PAGE_CNT + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *perf_event_mmap(int fd)
|
|
||||||
{
|
|
||||||
int mmap_size = perf_mmap_size();
|
|
||||||
void *base;
|
|
||||||
|
|
||||||
base = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
|
||||||
if (base == MAP_FAILED) {
|
|
||||||
p_err("event mmap failed: %s\n", strerror(errno));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void perf_event_unmap(void *mem)
|
|
||||||
{
|
|
||||||
if (munmap(mem, perf_mmap_size()))
|
|
||||||
fprintf(stderr, "Can't unmap ring memory!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static int bpf_perf_event_open(int map_fd, int key, int cpu)
|
|
||||||
{
|
|
||||||
struct perf_event_attr attr = {
|
|
||||||
.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_TIME,
|
.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_TIME,
|
||||||
.type = PERF_TYPE_SOFTWARE,
|
.type = PERF_TYPE_SOFTWARE,
|
||||||
.config = PERF_COUNT_SW_BPF_OUTPUT,
|
.config = PERF_COUNT_SW_BPF_OUTPUT,
|
||||||
|
.sample_period = 1,
|
||||||
|
.wakeup_events = 1,
|
||||||
};
|
};
|
||||||
int pmu_fd;
|
|
||||||
|
|
||||||
pmu_fd = sys_perf_event_open(&attr, -1, cpu, -1, 0);
|
|
||||||
if (pmu_fd < 0) {
|
|
||||||
p_err("failed to open perf event %d for CPU %d", key, cpu);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bpf_map_update_elem(map_fd, &key, &pmu_fd, BPF_ANY)) {
|
|
||||||
p_err("failed to update map for event %d for CPU %d", key, cpu);
|
|
||||||
goto err_close;
|
|
||||||
}
|
|
||||||
if (ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0)) {
|
|
||||||
p_err("failed to enable event %d for CPU %d", key, cpu);
|
|
||||||
goto err_close;
|
|
||||||
}
|
|
||||||
|
|
||||||
return pmu_fd;
|
|
||||||
|
|
||||||
err_close:
|
|
||||||
close(pmu_fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int do_event_pipe(int argc, char **argv)
|
|
||||||
{
|
|
||||||
int i, nfds, map_fd, index = -1, cpu = -1;
|
|
||||||
struct bpf_map_info map_info = {};
|
struct bpf_map_info map_info = {};
|
||||||
struct event_ring_info *rings;
|
struct perf_buffer_raw_opts opts = {};
|
||||||
size_t tmp_buf_sz = 0;
|
struct event_pipe_ctx ctx = {
|
||||||
void *tmp_buf = NULL;
|
.all_cpus = true,
|
||||||
struct pollfd *pfds;
|
.cpu = -1,
|
||||||
|
.idx = -1,
|
||||||
|
};
|
||||||
|
struct perf_buffer *pb;
|
||||||
__u32 map_info_len;
|
__u32 map_info_len;
|
||||||
bool do_all = true;
|
int err, map_fd;
|
||||||
|
|
||||||
map_info_len = sizeof(map_info);
|
map_info_len = sizeof(map_info);
|
||||||
map_fd = map_parse_fd_and_info(&argc, &argv, &map_info, &map_info_len);
|
map_fd = map_parse_fd_and_info(&argc, &argv, &map_info, &map_info_len);
|
||||||
|
@ -205,7 +155,7 @@ int do_event_pipe(int argc, char **argv)
|
||||||
char *endptr;
|
char *endptr;
|
||||||
|
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
cpu = strtoul(*argv, &endptr, 0);
|
ctx.cpu = strtoul(*argv, &endptr, 0);
|
||||||
if (*endptr) {
|
if (*endptr) {
|
||||||
p_err("can't parse %s as CPU ID", **argv);
|
p_err("can't parse %s as CPU ID", **argv);
|
||||||
goto err_close_map;
|
goto err_close_map;
|
||||||
|
@ -216,7 +166,7 @@ int do_event_pipe(int argc, char **argv)
|
||||||
char *endptr;
|
char *endptr;
|
||||||
|
|
||||||
NEXT_ARG();
|
NEXT_ARG();
|
||||||
index = strtoul(*argv, &endptr, 0);
|
ctx.idx = strtoul(*argv, &endptr, 0);
|
||||||
if (*endptr) {
|
if (*endptr) {
|
||||||
p_err("can't parse %s as index", **argv);
|
p_err("can't parse %s as index", **argv);
|
||||||
goto err_close_map;
|
goto err_close_map;
|
||||||
|
@ -228,45 +178,32 @@ int do_event_pipe(int argc, char **argv)
|
||||||
goto err_close_map;
|
goto err_close_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
do_all = false;
|
ctx.all_cpus = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!do_all) {
|
if (!ctx.all_cpus) {
|
||||||
if (index == -1 || cpu == -1) {
|
if (ctx.idx == -1 || ctx.cpu == -1) {
|
||||||
p_err("cpu and index must be specified together");
|
p_err("cpu and index must be specified together");
|
||||||
goto err_close_map;
|
goto err_close_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
nfds = 1;
|
|
||||||
} else {
|
} else {
|
||||||
nfds = min(get_possible_cpus(), map_info.max_entries);
|
ctx.cpu = 0;
|
||||||
cpu = 0;
|
ctx.idx = 0;
|
||||||
index = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rings = calloc(nfds, sizeof(rings[0]));
|
opts.attr = &perf_attr;
|
||||||
if (!rings)
|
opts.event_cb = print_bpf_output;
|
||||||
|
opts.ctx = &ctx;
|
||||||
|
opts.cpu_cnt = ctx.all_cpus ? 0 : 1;
|
||||||
|
opts.cpus = &ctx.cpu;
|
||||||
|
opts.map_keys = &ctx.idx;
|
||||||
|
|
||||||
|
pb = perf_buffer__new_raw(map_fd, MMAP_PAGE_CNT, &opts);
|
||||||
|
err = libbpf_get_error(pb);
|
||||||
|
if (err) {
|
||||||
|
p_err("failed to create perf buffer: %s (%d)",
|
||||||
|
strerror(err), err);
|
||||||
goto err_close_map;
|
goto err_close_map;
|
||||||
|
|
||||||
pfds = calloc(nfds, sizeof(pfds[0]));
|
|
||||||
if (!pfds)
|
|
||||||
goto err_free_rings;
|
|
||||||
|
|
||||||
for (i = 0; i < nfds; i++) {
|
|
||||||
rings[i].cpu = cpu + i;
|
|
||||||
rings[i].key = index + i;
|
|
||||||
|
|
||||||
rings[i].fd = bpf_perf_event_open(map_fd, rings[i].key,
|
|
||||||
rings[i].cpu);
|
|
||||||
if (rings[i].fd < 0)
|
|
||||||
goto err_close_fds_prev;
|
|
||||||
|
|
||||||
rings[i].mem = perf_event_mmap(rings[i].fd);
|
|
||||||
if (!rings[i].mem)
|
|
||||||
goto err_close_fds_current;
|
|
||||||
|
|
||||||
pfds[i].fd = rings[i].fd;
|
|
||||||
pfds[i].events = POLLIN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
signal(SIGINT, int_exit);
|
signal(SIGINT, int_exit);
|
||||||
|
@ -277,34 +214,24 @@ int do_event_pipe(int argc, char **argv)
|
||||||
jsonw_start_array(json_wtr);
|
jsonw_start_array(json_wtr);
|
||||||
|
|
||||||
while (!stop) {
|
while (!stop) {
|
||||||
poll(pfds, nfds, 200);
|
err = perf_buffer__poll(pb, 200);
|
||||||
for (i = 0; i < nfds; i++)
|
if (err < 0 && err != -EINTR) {
|
||||||
perf_event_read(&rings[i], &tmp_buf, &tmp_buf_sz);
|
p_err("perf buffer polling failed: %s (%d)",
|
||||||
|
strerror(err), err);
|
||||||
|
goto err_close_pb;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
free(tmp_buf);
|
|
||||||
|
|
||||||
if (json_output)
|
if (json_output)
|
||||||
jsonw_end_array(json_wtr);
|
jsonw_end_array(json_wtr);
|
||||||
|
|
||||||
for (i = 0; i < nfds; i++) {
|
perf_buffer__free(pb);
|
||||||
perf_event_unmap(rings[i].mem);
|
|
||||||
close(rings[i].fd);
|
|
||||||
}
|
|
||||||
free(pfds);
|
|
||||||
free(rings);
|
|
||||||
close(map_fd);
|
close(map_fd);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_close_fds_prev:
|
err_close_pb:
|
||||||
while (i--) {
|
perf_buffer__free(pb);
|
||||||
perf_event_unmap(rings[i].mem);
|
|
||||||
err_close_fds_current:
|
|
||||||
close(rings[i].fd);
|
|
||||||
}
|
|
||||||
free(pfds);
|
|
||||||
err_free_rings:
|
|
||||||
free(rings);
|
|
||||||
err_close_map:
|
err_close_map:
|
||||||
close(map_fd);
|
close(map_fd);
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
#include <linux/sizes.h>
|
||||||
|
|
||||||
#include <bpf.h>
|
#include <bpf.h>
|
||||||
#include <btf.h>
|
#include <btf.h>
|
||||||
|
@ -748,6 +749,344 @@ static int do_detach(int argc, char **argv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int check_single_stdin(char *file_data_in, char *file_ctx_in)
|
||||||
|
{
|
||||||
|
if (file_data_in && file_ctx_in &&
|
||||||
|
!strcmp(file_data_in, "-") && !strcmp(file_ctx_in, "-")) {
|
||||||
|
p_err("cannot use standard input for both data_in and ctx_in");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_run_data(const char *fname, void **data_ptr, unsigned int *size)
|
||||||
|
{
|
||||||
|
size_t block_size = 256;
|
||||||
|
size_t buf_size = block_size;
|
||||||
|
size_t nb_read = 0;
|
||||||
|
void *tmp;
|
||||||
|
FILE *f;
|
||||||
|
|
||||||
|
if (!fname) {
|
||||||
|
*data_ptr = NULL;
|
||||||
|
*size = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(fname, "-"))
|
||||||
|
f = stdin;
|
||||||
|
else
|
||||||
|
f = fopen(fname, "r");
|
||||||
|
if (!f) {
|
||||||
|
p_err("failed to open %s: %s", fname, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*data_ptr = malloc(block_size);
|
||||||
|
if (!*data_ptr) {
|
||||||
|
p_err("failed to allocate memory for data_in/ctx_in: %s",
|
||||||
|
strerror(errno));
|
||||||
|
goto err_fclose;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((nb_read += fread(*data_ptr + nb_read, 1, block_size, f))) {
|
||||||
|
if (feof(f))
|
||||||
|
break;
|
||||||
|
if (ferror(f)) {
|
||||||
|
p_err("failed to read data_in/ctx_in from %s: %s",
|
||||||
|
fname, strerror(errno));
|
||||||
|
goto err_free;
|
||||||
|
}
|
||||||
|
if (nb_read > buf_size - block_size) {
|
||||||
|
if (buf_size == UINT32_MAX) {
|
||||||
|
p_err("data_in/ctx_in is too long (max: %d)",
|
||||||
|
UINT32_MAX);
|
||||||
|
goto err_free;
|
||||||
|
}
|
||||||
|
/* No space for fread()-ing next chunk; realloc() */
|
||||||
|
buf_size *= 2;
|
||||||
|
tmp = realloc(*data_ptr, buf_size);
|
||||||
|
if (!tmp) {
|
||||||
|
p_err("failed to reallocate data_in/ctx_in: %s",
|
||||||
|
strerror(errno));
|
||||||
|
goto err_free;
|
||||||
|
}
|
||||||
|
*data_ptr = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (f != stdin)
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
*size = nb_read;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_free:
|
||||||
|
free(*data_ptr);
|
||||||
|
*data_ptr = NULL;
|
||||||
|
err_fclose:
|
||||||
|
if (f != stdin)
|
||||||
|
fclose(f);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hex_print(void *data, unsigned int size, FILE *f)
|
||||||
|
{
|
||||||
|
size_t i, j;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
for (i = 0; i < size; i += 16) {
|
||||||
|
/* Row offset */
|
||||||
|
fprintf(f, "%07zx\t", i);
|
||||||
|
|
||||||
|
/* Hexadecimal values */
|
||||||
|
for (j = i; j < i + 16 && j < size; j++)
|
||||||
|
fprintf(f, "%02x%s", *(uint8_t *)(data + j),
|
||||||
|
j % 2 ? " " : "");
|
||||||
|
for (; j < i + 16; j++)
|
||||||
|
fprintf(f, " %s", j % 2 ? " " : "");
|
||||||
|
|
||||||
|
/* ASCII values (if relevant), '.' otherwise */
|
||||||
|
fprintf(f, "| ");
|
||||||
|
for (j = i; j < i + 16 && j < size; j++) {
|
||||||
|
c = *(char *)(data + j);
|
||||||
|
if (c < ' ' || c > '~')
|
||||||
|
c = '.';
|
||||||
|
fprintf(f, "%c%s", c, j == i + 7 ? " " : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(f, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
print_run_output(void *data, unsigned int size, const char *fname,
|
||||||
|
const char *json_key)
|
||||||
|
{
|
||||||
|
size_t nb_written;
|
||||||
|
FILE *f;
|
||||||
|
|
||||||
|
if (!fname)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!strcmp(fname, "-")) {
|
||||||
|
f = stdout;
|
||||||
|
if (json_output) {
|
||||||
|
jsonw_name(json_wtr, json_key);
|
||||||
|
print_data_json(data, size);
|
||||||
|
} else {
|
||||||
|
hex_print(data, size, f);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
f = fopen(fname, "w");
|
||||||
|
if (!f) {
|
||||||
|
p_err("failed to open %s: %s", fname, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
nb_written = fwrite(data, 1, size, f);
|
||||||
|
fclose(f);
|
||||||
|
if (nb_written != size) {
|
||||||
|
p_err("failed to write output data/ctx: %s", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int alloc_run_data(void **data_ptr, unsigned int size_out)
|
||||||
|
{
|
||||||
|
*data_ptr = calloc(size_out, 1);
|
||||||
|
if (!*data_ptr) {
|
||||||
|
p_err("failed to allocate memory for output data/ctx: %s",
|
||||||
|
strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_run(int argc, char **argv)
|
||||||
|
{
|
||||||
|
char *data_fname_in = NULL, *data_fname_out = NULL;
|
||||||
|
char *ctx_fname_in = NULL, *ctx_fname_out = NULL;
|
||||||
|
struct bpf_prog_test_run_attr test_attr = {0};
|
||||||
|
const unsigned int default_size = SZ_32K;
|
||||||
|
void *data_in = NULL, *data_out = NULL;
|
||||||
|
void *ctx_in = NULL, *ctx_out = NULL;
|
||||||
|
unsigned int repeat = 1;
|
||||||
|
int fd, err;
|
||||||
|
|
||||||
|
if (!REQ_ARGS(4))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
fd = prog_parse_fd(&argc, &argv);
|
||||||
|
if (fd < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
while (argc) {
|
||||||
|
if (detect_common_prefix(*argv, "data_in", "data_out",
|
||||||
|
"data_size_out", NULL))
|
||||||
|
return -1;
|
||||||
|
if (detect_common_prefix(*argv, "ctx_in", "ctx_out",
|
||||||
|
"ctx_size_out", NULL))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (is_prefix(*argv, "data_in")) {
|
||||||
|
NEXT_ARG();
|
||||||
|
if (!REQ_ARGS(1))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
data_fname_in = GET_ARG();
|
||||||
|
if (check_single_stdin(data_fname_in, ctx_fname_in))
|
||||||
|
return -1;
|
||||||
|
} else if (is_prefix(*argv, "data_out")) {
|
||||||
|
NEXT_ARG();
|
||||||
|
if (!REQ_ARGS(1))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
data_fname_out = GET_ARG();
|
||||||
|
} else if (is_prefix(*argv, "data_size_out")) {
|
||||||
|
char *endptr;
|
||||||
|
|
||||||
|
NEXT_ARG();
|
||||||
|
if (!REQ_ARGS(1))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
test_attr.data_size_out = strtoul(*argv, &endptr, 0);
|
||||||
|
if (*endptr) {
|
||||||
|
p_err("can't parse %s as output data size",
|
||||||
|
*argv);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
NEXT_ARG();
|
||||||
|
} else if (is_prefix(*argv, "ctx_in")) {
|
||||||
|
NEXT_ARG();
|
||||||
|
if (!REQ_ARGS(1))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ctx_fname_in = GET_ARG();
|
||||||
|
if (check_single_stdin(data_fname_in, ctx_fname_in))
|
||||||
|
return -1;
|
||||||
|
} else if (is_prefix(*argv, "ctx_out")) {
|
||||||
|
NEXT_ARG();
|
||||||
|
if (!REQ_ARGS(1))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ctx_fname_out = GET_ARG();
|
||||||
|
} else if (is_prefix(*argv, "ctx_size_out")) {
|
||||||
|
char *endptr;
|
||||||
|
|
||||||
|
NEXT_ARG();
|
||||||
|
if (!REQ_ARGS(1))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
test_attr.ctx_size_out = strtoul(*argv, &endptr, 0);
|
||||||
|
if (*endptr) {
|
||||||
|
p_err("can't parse %s as output context size",
|
||||||
|
*argv);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
NEXT_ARG();
|
||||||
|
} else if (is_prefix(*argv, "repeat")) {
|
||||||
|
char *endptr;
|
||||||
|
|
||||||
|
NEXT_ARG();
|
||||||
|
if (!REQ_ARGS(1))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
repeat = strtoul(*argv, &endptr, 0);
|
||||||
|
if (*endptr) {
|
||||||
|
p_err("can't parse %s as repeat number",
|
||||||
|
*argv);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
NEXT_ARG();
|
||||||
|
} else {
|
||||||
|
p_err("expected no more arguments, 'data_in', 'data_out', 'data_size_out', 'ctx_in', 'ctx_out', 'ctx_size_out' or 'repeat', got: '%s'?",
|
||||||
|
*argv);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = get_run_data(data_fname_in, &data_in, &test_attr.data_size_in);
|
||||||
|
if (err)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (data_in) {
|
||||||
|
if (!test_attr.data_size_out)
|
||||||
|
test_attr.data_size_out = default_size;
|
||||||
|
err = alloc_run_data(&data_out, test_attr.data_size_out);
|
||||||
|
if (err)
|
||||||
|
goto free_data_in;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = get_run_data(ctx_fname_in, &ctx_in, &test_attr.ctx_size_in);
|
||||||
|
if (err)
|
||||||
|
goto free_data_out;
|
||||||
|
|
||||||
|
if (ctx_in) {
|
||||||
|
if (!test_attr.ctx_size_out)
|
||||||
|
test_attr.ctx_size_out = default_size;
|
||||||
|
err = alloc_run_data(&ctx_out, test_attr.ctx_size_out);
|
||||||
|
if (err)
|
||||||
|
goto free_ctx_in;
|
||||||
|
}
|
||||||
|
|
||||||
|
test_attr.prog_fd = fd;
|
||||||
|
test_attr.repeat = repeat;
|
||||||
|
test_attr.data_in = data_in;
|
||||||
|
test_attr.data_out = data_out;
|
||||||
|
test_attr.ctx_in = ctx_in;
|
||||||
|
test_attr.ctx_out = ctx_out;
|
||||||
|
|
||||||
|
err = bpf_prog_test_run_xattr(&test_attr);
|
||||||
|
if (err) {
|
||||||
|
p_err("failed to run program: %s", strerror(errno));
|
||||||
|
goto free_ctx_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = 0;
|
||||||
|
|
||||||
|
if (json_output)
|
||||||
|
jsonw_start_object(json_wtr); /* root */
|
||||||
|
|
||||||
|
/* Do not exit on errors occurring when printing output data/context,
|
||||||
|
* we still want to print return value and duration for program run.
|
||||||
|
*/
|
||||||
|
if (test_attr.data_size_out)
|
||||||
|
err += print_run_output(test_attr.data_out,
|
||||||
|
test_attr.data_size_out,
|
||||||
|
data_fname_out, "data_out");
|
||||||
|
if (test_attr.ctx_size_out)
|
||||||
|
err += print_run_output(test_attr.ctx_out,
|
||||||
|
test_attr.ctx_size_out,
|
||||||
|
ctx_fname_out, "ctx_out");
|
||||||
|
|
||||||
|
if (json_output) {
|
||||||
|
jsonw_uint_field(json_wtr, "retval", test_attr.retval);
|
||||||
|
jsonw_uint_field(json_wtr, "duration", test_attr.duration);
|
||||||
|
jsonw_end_object(json_wtr); /* root */
|
||||||
|
} else {
|
||||||
|
fprintf(stdout, "Return value: %u, duration%s: %uns\n",
|
||||||
|
test_attr.retval,
|
||||||
|
repeat > 1 ? " (average)" : "", test_attr.duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
free_ctx_out:
|
||||||
|
free(ctx_out);
|
||||||
|
free_ctx_in:
|
||||||
|
free(ctx_in);
|
||||||
|
free_data_out:
|
||||||
|
free(data_out);
|
||||||
|
free_data_in:
|
||||||
|
free(data_in);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static int load_with_options(int argc, char **argv, bool first_prog_only)
|
static int load_with_options(int argc, char **argv, bool first_prog_only)
|
||||||
{
|
{
|
||||||
struct bpf_object_load_attr load_attr = { 0 };
|
struct bpf_object_load_attr load_attr = { 0 };
|
||||||
|
@ -1058,6 +1397,11 @@ static int do_help(int argc, char **argv)
|
||||||
" [pinmaps MAP_DIR]\n"
|
" [pinmaps MAP_DIR]\n"
|
||||||
" %s %s attach PROG ATTACH_TYPE [MAP]\n"
|
" %s %s attach PROG ATTACH_TYPE [MAP]\n"
|
||||||
" %s %s detach PROG ATTACH_TYPE [MAP]\n"
|
" %s %s detach PROG ATTACH_TYPE [MAP]\n"
|
||||||
|
" %s %s run PROG \\\n"
|
||||||
|
" data_in FILE \\\n"
|
||||||
|
" [data_out FILE [data_size_out L]] \\\n"
|
||||||
|
" [ctx_in FILE [ctx_out FILE [ctx_size_out M]]] \\\n"
|
||||||
|
" [repeat N]\n"
|
||||||
" %s %s tracelog\n"
|
" %s %s tracelog\n"
|
||||||
" %s %s help\n"
|
" %s %s help\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
@ -1079,7 +1423,8 @@ static int do_help(int argc, char **argv)
|
||||||
"",
|
"",
|
||||||
bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
|
bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
|
||||||
bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
|
bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
|
||||||
bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]);
|
bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
|
||||||
|
bin_name, argv[-2]);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1095,6 +1440,7 @@ static const struct cmd cmds[] = {
|
||||||
{ "attach", do_attach },
|
{ "attach", do_attach },
|
||||||
{ "detach", do_detach },
|
{ "detach", do_detach },
|
||||||
{ "tracelog", do_tracelog },
|
{ "tracelog", do_tracelog },
|
||||||
|
{ "run", do_run },
|
||||||
{ 0 }
|
{ 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||||
|
/*
|
||||||
|
* include/linux/sizes.h
|
||||||
|
*/
|
||||||
|
#ifndef __LINUX_SIZES_H__
|
||||||
|
#define __LINUX_SIZES_H__
|
||||||
|
|
||||||
|
#include <linux/const.h>
|
||||||
|
|
||||||
|
#define SZ_1 0x00000001
|
||||||
|
#define SZ_2 0x00000002
|
||||||
|
#define SZ_4 0x00000004
|
||||||
|
#define SZ_8 0x00000008
|
||||||
|
#define SZ_16 0x00000010
|
||||||
|
#define SZ_32 0x00000020
|
||||||
|
#define SZ_64 0x00000040
|
||||||
|
#define SZ_128 0x00000080
|
||||||
|
#define SZ_256 0x00000100
|
||||||
|
#define SZ_512 0x00000200
|
||||||
|
|
||||||
|
#define SZ_1K 0x00000400
|
||||||
|
#define SZ_2K 0x00000800
|
||||||
|
#define SZ_4K 0x00001000
|
||||||
|
#define SZ_8K 0x00002000
|
||||||
|
#define SZ_16K 0x00004000
|
||||||
|
#define SZ_32K 0x00008000
|
||||||
|
#define SZ_64K 0x00010000
|
||||||
|
#define SZ_128K 0x00020000
|
||||||
|
#define SZ_256K 0x00040000
|
||||||
|
#define SZ_512K 0x00080000
|
||||||
|
|
||||||
|
#define SZ_1M 0x00100000
|
||||||
|
#define SZ_2M 0x00200000
|
||||||
|
#define SZ_4M 0x00400000
|
||||||
|
#define SZ_8M 0x00800000
|
||||||
|
#define SZ_16M 0x01000000
|
||||||
|
#define SZ_32M 0x02000000
|
||||||
|
#define SZ_64M 0x04000000
|
||||||
|
#define SZ_128M 0x08000000
|
||||||
|
#define SZ_256M 0x10000000
|
||||||
|
#define SZ_512M 0x20000000
|
||||||
|
|
||||||
|
#define SZ_1G 0x40000000
|
||||||
|
#define SZ_2G 0x80000000
|
||||||
|
|
||||||
|
#define SZ_4G _AC(0x100000000, ULL)
|
||||||
|
|
||||||
|
#endif /* __LINUX_SIZES_H__ */
|
|
@ -3244,7 +3244,7 @@ struct bpf_sock_addr {
|
||||||
__u32 user_ip4; /* Allows 1,2,4-byte read and 4-byte write.
|
__u32 user_ip4; /* Allows 1,2,4-byte read and 4-byte write.
|
||||||
* Stored in network byte order.
|
* Stored in network byte order.
|
||||||
*/
|
*/
|
||||||
__u32 user_ip6[4]; /* Allows 1,2,4-byte read an 4-byte write.
|
__u32 user_ip6[4]; /* Allows 1,2,4-byte read and 4,8-byte write.
|
||||||
* Stored in network byte order.
|
* Stored in network byte order.
|
||||||
*/
|
*/
|
||||||
__u32 user_port; /* Allows 4-byte read and write.
|
__u32 user_port; /* Allows 4-byte read and write.
|
||||||
|
@ -3253,10 +3253,10 @@ struct bpf_sock_addr {
|
||||||
__u32 family; /* Allows 4-byte read, but no write */
|
__u32 family; /* Allows 4-byte read, but no write */
|
||||||
__u32 type; /* Allows 4-byte read, but no write */
|
__u32 type; /* Allows 4-byte read, but no write */
|
||||||
__u32 protocol; /* Allows 4-byte read, but no write */
|
__u32 protocol; /* Allows 4-byte read, but no write */
|
||||||
__u32 msg_src_ip4; /* Allows 1,2,4-byte read an 4-byte write.
|
__u32 msg_src_ip4; /* Allows 1,2,4-byte read and 4-byte write.
|
||||||
* Stored in network byte order.
|
* Stored in network byte order.
|
||||||
*/
|
*/
|
||||||
__u32 msg_src_ip6[4]; /* Allows 1,2,4-byte read an 4-byte write.
|
__u32 msg_src_ip6[4]; /* Allows 1,2,4-byte read and 4,8-byte write.
|
||||||
* Stored in network byte order.
|
* Stored in network byte order.
|
||||||
*/
|
*/
|
||||||
__bpf_md_ptr(struct bpf_sock *, sk);
|
__bpf_md_ptr(struct bpf_sock *, sk);
|
||||||
|
|
|
@ -9,7 +9,8 @@ described here. It's recommended to follow these conventions whenever a
|
||||||
new function or type is added to keep libbpf API clean and consistent.
|
new function or type is added to keep libbpf API clean and consistent.
|
||||||
|
|
||||||
All types and functions provided by libbpf API should have one of the
|
All types and functions provided by libbpf API should have one of the
|
||||||
following prefixes: ``bpf_``, ``btf_``, ``libbpf_``, ``xsk_``.
|
following prefixes: ``bpf_``, ``btf_``, ``libbpf_``, ``xsk_``,
|
||||||
|
``perf_buffer_``.
|
||||||
|
|
||||||
System call wrappers
|
System call wrappers
|
||||||
--------------------
|
--------------------
|
||||||
|
|
|
@ -32,6 +32,9 @@
|
||||||
#include <linux/limits.h>
|
#include <linux/limits.h>
|
||||||
#include <linux/perf_event.h>
|
#include <linux/perf_event.h>
|
||||||
#include <linux/ring_buffer.h>
|
#include <linux/ring_buffer.h>
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/vfs.h>
|
#include <sys/vfs.h>
|
||||||
|
@ -1028,40 +1031,40 @@ static const struct btf_type *skip_mods_and_typedefs(const struct btf *btf,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool get_map_field_int(const char *map_name,
|
/*
|
||||||
const struct btf *btf,
|
* Fetch integer attribute of BTF map definition. Such attributes are
|
||||||
|
* represented using a pointer to an array, in which dimensionality of array
|
||||||
|
* encodes specified integer value. E.g., int (*type)[BPF_MAP_TYPE_ARRAY];
|
||||||
|
* encodes `type => BPF_MAP_TYPE_ARRAY` key/value pair completely using BTF
|
||||||
|
* type definition, while using only sizeof(void *) space in ELF data section.
|
||||||
|
*/
|
||||||
|
static bool get_map_field_int(const char *map_name, const struct btf *btf,
|
||||||
const struct btf_type *def,
|
const struct btf_type *def,
|
||||||
const struct btf_member *m,
|
const struct btf_member *m, __u32 *res) {
|
||||||
const void *data, __u32 *res) {
|
|
||||||
const struct btf_type *t = skip_mods_and_typedefs(btf, m->type);
|
const struct btf_type *t = skip_mods_and_typedefs(btf, m->type);
|
||||||
const char *name = btf__name_by_offset(btf, m->name_off);
|
const char *name = btf__name_by_offset(btf, m->name_off);
|
||||||
__u32 int_info = *(const __u32 *)(const void *)(t + 1);
|
const struct btf_array *arr_info;
|
||||||
|
const struct btf_type *arr_t;
|
||||||
|
|
||||||
if (BTF_INFO_KIND(t->info) != BTF_KIND_INT) {
|
if (BTF_INFO_KIND(t->info) != BTF_KIND_PTR) {
|
||||||
pr_warning("map '%s': attr '%s': expected INT, got %u.\n",
|
pr_warning("map '%s': attr '%s': expected PTR, got %u.\n",
|
||||||
map_name, name, BTF_INFO_KIND(t->info));
|
map_name, name, BTF_INFO_KIND(t->info));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (t->size != 4 || BTF_INT_BITS(int_info) != 32 ||
|
|
||||||
BTF_INT_OFFSET(int_info)) {
|
|
||||||
pr_warning("map '%s': attr '%s': expected 32-bit non-bitfield integer, "
|
|
||||||
"got %u-byte (%d-bit) one with bit offset %d.\n",
|
|
||||||
map_name, name, t->size, BTF_INT_BITS(int_info),
|
|
||||||
BTF_INT_OFFSET(int_info));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (BTF_INFO_KFLAG(def->info) && BTF_MEMBER_BITFIELD_SIZE(m->offset)) {
|
|
||||||
pr_warning("map '%s': attr '%s': bitfield is not supported.\n",
|
|
||||||
map_name, name);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (m->offset % 32) {
|
|
||||||
pr_warning("map '%s': attr '%s': unaligned fields are not supported.\n",
|
|
||||||
map_name, name);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
*res = *(const __u32 *)(data + m->offset / 8);
|
arr_t = btf__type_by_id(btf, t->type);
|
||||||
|
if (!arr_t) {
|
||||||
|
pr_warning("map '%s': attr '%s': type [%u] not found.\n",
|
||||||
|
map_name, name, t->type);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (BTF_INFO_KIND(arr_t->info) != BTF_KIND_ARRAY) {
|
||||||
|
pr_warning("map '%s': attr '%s': expected ARRAY, got %u.\n",
|
||||||
|
map_name, name, BTF_INFO_KIND(arr_t->info));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
arr_info = (const void *)(arr_t + 1);
|
||||||
|
*res = arr_info->nelems;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1074,7 +1077,6 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj,
|
||||||
const struct btf_var_secinfo *vi;
|
const struct btf_var_secinfo *vi;
|
||||||
const struct btf_var *var_extra;
|
const struct btf_var *var_extra;
|
||||||
const struct btf_member *m;
|
const struct btf_member *m;
|
||||||
const void *def_data;
|
|
||||||
const char *map_name;
|
const char *map_name;
|
||||||
struct bpf_map *map;
|
struct bpf_map *map;
|
||||||
int vlen, i;
|
int vlen, i;
|
||||||
|
@ -1131,7 +1133,6 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj,
|
||||||
pr_debug("map '%s': at sec_idx %d, offset %zu.\n",
|
pr_debug("map '%s': at sec_idx %d, offset %zu.\n",
|
||||||
map_name, map->sec_idx, map->sec_offset);
|
map_name, map->sec_idx, map->sec_offset);
|
||||||
|
|
||||||
def_data = data->d_buf + vi->offset;
|
|
||||||
vlen = BTF_INFO_VLEN(def->info);
|
vlen = BTF_INFO_VLEN(def->info);
|
||||||
m = (const void *)(def + 1);
|
m = (const void *)(def + 1);
|
||||||
for (i = 0; i < vlen; i++, m++) {
|
for (i = 0; i < vlen; i++, m++) {
|
||||||
|
@ -1144,19 +1145,19 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj,
|
||||||
}
|
}
|
||||||
if (strcmp(name, "type") == 0) {
|
if (strcmp(name, "type") == 0) {
|
||||||
if (!get_map_field_int(map_name, obj->btf, def, m,
|
if (!get_map_field_int(map_name, obj->btf, def, m,
|
||||||
def_data, &map->def.type))
|
&map->def.type))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
pr_debug("map '%s': found type = %u.\n",
|
pr_debug("map '%s': found type = %u.\n",
|
||||||
map_name, map->def.type);
|
map_name, map->def.type);
|
||||||
} else if (strcmp(name, "max_entries") == 0) {
|
} else if (strcmp(name, "max_entries") == 0) {
|
||||||
if (!get_map_field_int(map_name, obj->btf, def, m,
|
if (!get_map_field_int(map_name, obj->btf, def, m,
|
||||||
def_data, &map->def.max_entries))
|
&map->def.max_entries))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
pr_debug("map '%s': found max_entries = %u.\n",
|
pr_debug("map '%s': found max_entries = %u.\n",
|
||||||
map_name, map->def.max_entries);
|
map_name, map->def.max_entries);
|
||||||
} else if (strcmp(name, "map_flags") == 0) {
|
} else if (strcmp(name, "map_flags") == 0) {
|
||||||
if (!get_map_field_int(map_name, obj->btf, def, m,
|
if (!get_map_field_int(map_name, obj->btf, def, m,
|
||||||
def_data, &map->def.map_flags))
|
&map->def.map_flags))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
pr_debug("map '%s': found map_flags = %u.\n",
|
pr_debug("map '%s': found map_flags = %u.\n",
|
||||||
map_name, map->def.map_flags);
|
map_name, map->def.map_flags);
|
||||||
|
@ -1164,7 +1165,7 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj,
|
||||||
__u32 sz;
|
__u32 sz;
|
||||||
|
|
||||||
if (!get_map_field_int(map_name, obj->btf, def, m,
|
if (!get_map_field_int(map_name, obj->btf, def, m,
|
||||||
def_data, &sz))
|
&sz))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
pr_debug("map '%s': found key_size = %u.\n",
|
pr_debug("map '%s': found key_size = %u.\n",
|
||||||
map_name, sz);
|
map_name, sz);
|
||||||
|
@ -1207,7 +1208,7 @@ static int bpf_object__init_user_btf_map(struct bpf_object *obj,
|
||||||
__u32 sz;
|
__u32 sz;
|
||||||
|
|
||||||
if (!get_map_field_int(map_name, obj->btf, def, m,
|
if (!get_map_field_int(map_name, obj->btf, def, m,
|
||||||
def_data, &sz))
|
&sz))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
pr_debug("map '%s': found value_size = %u.\n",
|
pr_debug("map '%s': found value_size = %u.\n",
|
||||||
map_name, sz);
|
map_name, sz);
|
||||||
|
@ -2115,6 +2116,7 @@ static int
|
||||||
bpf_object__create_maps(struct bpf_object *obj)
|
bpf_object__create_maps(struct bpf_object *obj)
|
||||||
{
|
{
|
||||||
struct bpf_create_map_attr create_attr = {};
|
struct bpf_create_map_attr create_attr = {};
|
||||||
|
int nr_cpus = 0;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
@ -2137,7 +2139,22 @@ bpf_object__create_maps(struct bpf_object *obj)
|
||||||
create_attr.map_flags = def->map_flags;
|
create_attr.map_flags = def->map_flags;
|
||||||
create_attr.key_size = def->key_size;
|
create_attr.key_size = def->key_size;
|
||||||
create_attr.value_size = def->value_size;
|
create_attr.value_size = def->value_size;
|
||||||
create_attr.max_entries = def->max_entries;
|
if (def->type == BPF_MAP_TYPE_PERF_EVENT_ARRAY &&
|
||||||
|
!def->max_entries) {
|
||||||
|
if (!nr_cpus)
|
||||||
|
nr_cpus = libbpf_num_possible_cpus();
|
||||||
|
if (nr_cpus < 0) {
|
||||||
|
pr_warning("failed to determine number of system CPUs: %d\n",
|
||||||
|
nr_cpus);
|
||||||
|
err = nr_cpus;
|
||||||
|
goto err_out;
|
||||||
|
}
|
||||||
|
pr_debug("map '%s': setting size to %d\n",
|
||||||
|
map->name, nr_cpus);
|
||||||
|
create_attr.max_entries = nr_cpus;
|
||||||
|
} else {
|
||||||
|
create_attr.max_entries = def->max_entries;
|
||||||
|
}
|
||||||
create_attr.btf_fd = 0;
|
create_attr.btf_fd = 0;
|
||||||
create_attr.btf_key_type_id = 0;
|
create_attr.btf_key_type_id = 0;
|
||||||
create_attr.btf_value_type_id = 0;
|
create_attr.btf_value_type_id = 0;
|
||||||
|
@ -2154,9 +2171,10 @@ bpf_object__create_maps(struct bpf_object *obj)
|
||||||
*pfd = bpf_create_map_xattr(&create_attr);
|
*pfd = bpf_create_map_xattr(&create_attr);
|
||||||
if (*pfd < 0 && (create_attr.btf_key_type_id ||
|
if (*pfd < 0 && (create_attr.btf_key_type_id ||
|
||||||
create_attr.btf_value_type_id)) {
|
create_attr.btf_value_type_id)) {
|
||||||
cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg));
|
err = -errno;
|
||||||
|
cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg));
|
||||||
pr_warning("Error in bpf_create_map_xattr(%s):%s(%d). Retrying without BTF.\n",
|
pr_warning("Error in bpf_create_map_xattr(%s):%s(%d). Retrying without BTF.\n",
|
||||||
map->name, cp, errno);
|
map->name, cp, err);
|
||||||
create_attr.btf_fd = 0;
|
create_attr.btf_fd = 0;
|
||||||
create_attr.btf_key_type_id = 0;
|
create_attr.btf_key_type_id = 0;
|
||||||
create_attr.btf_value_type_id = 0;
|
create_attr.btf_value_type_id = 0;
|
||||||
|
@ -2168,11 +2186,11 @@ bpf_object__create_maps(struct bpf_object *obj)
|
||||||
if (*pfd < 0) {
|
if (*pfd < 0) {
|
||||||
size_t j;
|
size_t j;
|
||||||
|
|
||||||
err = *pfd;
|
err = -errno;
|
||||||
err_out:
|
err_out:
|
||||||
cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg));
|
cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg));
|
||||||
pr_warning("failed to create map (name: '%s'): %s\n",
|
pr_warning("failed to create map (name: '%s'): %s(%d)\n",
|
||||||
map->name, cp);
|
map->name, cp, err);
|
||||||
for (j = 0; j < i; j++)
|
for (j = 0; j < i; j++)
|
||||||
zclose(obj->maps[j].fd);
|
zclose(obj->maps[j].fd);
|
||||||
return err;
|
return err;
|
||||||
|
@ -3941,6 +3959,372 @@ int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct bpf_link {
|
||||||
|
int (*destroy)(struct bpf_link *link);
|
||||||
|
};
|
||||||
|
|
||||||
|
int bpf_link__destroy(struct bpf_link *link)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!link)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err = link->destroy(link);
|
||||||
|
free(link);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct bpf_link_fd {
|
||||||
|
struct bpf_link link; /* has to be at the top of struct */
|
||||||
|
int fd; /* hook FD */
|
||||||
|
};
|
||||||
|
|
||||||
|
static int bpf_link__destroy_perf_event(struct bpf_link *link)
|
||||||
|
{
|
||||||
|
struct bpf_link_fd *l = (void *)link;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = ioctl(l->fd, PERF_EVENT_IOC_DISABLE, 0);
|
||||||
|
if (err)
|
||||||
|
err = -errno;
|
||||||
|
|
||||||
|
close(l->fd);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct bpf_link *bpf_program__attach_perf_event(struct bpf_program *prog,
|
||||||
|
int pfd)
|
||||||
|
{
|
||||||
|
char errmsg[STRERR_BUFSIZE];
|
||||||
|
struct bpf_link_fd *link;
|
||||||
|
int prog_fd, err;
|
||||||
|
|
||||||
|
if (pfd < 0) {
|
||||||
|
pr_warning("program '%s': invalid perf event FD %d\n",
|
||||||
|
bpf_program__title(prog, false), pfd);
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
}
|
||||||
|
prog_fd = bpf_program__fd(prog);
|
||||||
|
if (prog_fd < 0) {
|
||||||
|
pr_warning("program '%s': can't attach BPF program w/o FD (did you load it?)\n",
|
||||||
|
bpf_program__title(prog, false));
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
link = malloc(sizeof(*link));
|
||||||
|
if (!link)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
link->link.destroy = &bpf_link__destroy_perf_event;
|
||||||
|
link->fd = pfd;
|
||||||
|
|
||||||
|
if (ioctl(pfd, PERF_EVENT_IOC_SET_BPF, prog_fd) < 0) {
|
||||||
|
err = -errno;
|
||||||
|
free(link);
|
||||||
|
pr_warning("program '%s': failed to attach to pfd %d: %s\n",
|
||||||
|
bpf_program__title(prog, false), pfd,
|
||||||
|
libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
|
||||||
|
return ERR_PTR(err);
|
||||||
|
}
|
||||||
|
if (ioctl(pfd, PERF_EVENT_IOC_ENABLE, 0) < 0) {
|
||||||
|
err = -errno;
|
||||||
|
free(link);
|
||||||
|
pr_warning("program '%s': failed to enable pfd %d: %s\n",
|
||||||
|
bpf_program__title(prog, false), pfd,
|
||||||
|
libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
|
||||||
|
return ERR_PTR(err);
|
||||||
|
}
|
||||||
|
return (struct bpf_link *)link;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* this function is expected to parse integer in the range of [0, 2^31-1] from
|
||||||
|
* given file using scanf format string fmt. If actual parsed value is
|
||||||
|
* negative, the result might be indistinguishable from error
|
||||||
|
*/
|
||||||
|
static int parse_uint_from_file(const char *file, const char *fmt)
|
||||||
|
{
|
||||||
|
char buf[STRERR_BUFSIZE];
|
||||||
|
int err, ret;
|
||||||
|
FILE *f;
|
||||||
|
|
||||||
|
f = fopen(file, "r");
|
||||||
|
if (!f) {
|
||||||
|
err = -errno;
|
||||||
|
pr_debug("failed to open '%s': %s\n", file,
|
||||||
|
libbpf_strerror_r(err, buf, sizeof(buf)));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
err = fscanf(f, fmt, &ret);
|
||||||
|
if (err != 1) {
|
||||||
|
err = err == EOF ? -EIO : -errno;
|
||||||
|
pr_debug("failed to parse '%s': %s\n", file,
|
||||||
|
libbpf_strerror_r(err, buf, sizeof(buf)));
|
||||||
|
fclose(f);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int determine_kprobe_perf_type(void)
|
||||||
|
{
|
||||||
|
const char *file = "/sys/bus/event_source/devices/kprobe/type";
|
||||||
|
|
||||||
|
return parse_uint_from_file(file, "%d\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int determine_uprobe_perf_type(void)
|
||||||
|
{
|
||||||
|
const char *file = "/sys/bus/event_source/devices/uprobe/type";
|
||||||
|
|
||||||
|
return parse_uint_from_file(file, "%d\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int determine_kprobe_retprobe_bit(void)
|
||||||
|
{
|
||||||
|
const char *file = "/sys/bus/event_source/devices/kprobe/format/retprobe";
|
||||||
|
|
||||||
|
return parse_uint_from_file(file, "config:%d\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int determine_uprobe_retprobe_bit(void)
|
||||||
|
{
|
||||||
|
const char *file = "/sys/bus/event_source/devices/uprobe/format/retprobe";
|
||||||
|
|
||||||
|
return parse_uint_from_file(file, "config:%d\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int perf_event_open_probe(bool uprobe, bool retprobe, const char *name,
|
||||||
|
uint64_t offset, int pid)
|
||||||
|
{
|
||||||
|
struct perf_event_attr attr = {};
|
||||||
|
char errmsg[STRERR_BUFSIZE];
|
||||||
|
int type, pfd, err;
|
||||||
|
|
||||||
|
type = uprobe ? determine_uprobe_perf_type()
|
||||||
|
: determine_kprobe_perf_type();
|
||||||
|
if (type < 0) {
|
||||||
|
pr_warning("failed to determine %s perf type: %s\n",
|
||||||
|
uprobe ? "uprobe" : "kprobe",
|
||||||
|
libbpf_strerror_r(type, errmsg, sizeof(errmsg)));
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
if (retprobe) {
|
||||||
|
int bit = uprobe ? determine_uprobe_retprobe_bit()
|
||||||
|
: determine_kprobe_retprobe_bit();
|
||||||
|
|
||||||
|
if (bit < 0) {
|
||||||
|
pr_warning("failed to determine %s retprobe bit: %s\n",
|
||||||
|
uprobe ? "uprobe" : "kprobe",
|
||||||
|
libbpf_strerror_r(bit, errmsg,
|
||||||
|
sizeof(errmsg)));
|
||||||
|
return bit;
|
||||||
|
}
|
||||||
|
attr.config |= 1 << bit;
|
||||||
|
}
|
||||||
|
attr.size = sizeof(attr);
|
||||||
|
attr.type = type;
|
||||||
|
attr.config1 = (uint64_t)(void *)name; /* kprobe_func or uprobe_path */
|
||||||
|
attr.config2 = offset; /* kprobe_addr or probe_offset */
|
||||||
|
|
||||||
|
/* pid filter is meaningful only for uprobes */
|
||||||
|
pfd = syscall(__NR_perf_event_open, &attr,
|
||||||
|
pid < 0 ? -1 : pid /* pid */,
|
||||||
|
pid == -1 ? 0 : -1 /* cpu */,
|
||||||
|
-1 /* group_fd */, PERF_FLAG_FD_CLOEXEC);
|
||||||
|
if (pfd < 0) {
|
||||||
|
err = -errno;
|
||||||
|
pr_warning("%s perf_event_open() failed: %s\n",
|
||||||
|
uprobe ? "uprobe" : "kprobe",
|
||||||
|
libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
return pfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct bpf_link *bpf_program__attach_kprobe(struct bpf_program *prog,
|
||||||
|
bool retprobe,
|
||||||
|
const char *func_name)
|
||||||
|
{
|
||||||
|
char errmsg[STRERR_BUFSIZE];
|
||||||
|
struct bpf_link *link;
|
||||||
|
int pfd, err;
|
||||||
|
|
||||||
|
pfd = perf_event_open_probe(false /* uprobe */, retprobe, func_name,
|
||||||
|
0 /* offset */, -1 /* pid */);
|
||||||
|
if (pfd < 0) {
|
||||||
|
pr_warning("program '%s': failed to create %s '%s' perf event: %s\n",
|
||||||
|
bpf_program__title(prog, false),
|
||||||
|
retprobe ? "kretprobe" : "kprobe", func_name,
|
||||||
|
libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
|
||||||
|
return ERR_PTR(pfd);
|
||||||
|
}
|
||||||
|
link = bpf_program__attach_perf_event(prog, pfd);
|
||||||
|
if (IS_ERR(link)) {
|
||||||
|
close(pfd);
|
||||||
|
err = PTR_ERR(link);
|
||||||
|
pr_warning("program '%s': failed to attach to %s '%s': %s\n",
|
||||||
|
bpf_program__title(prog, false),
|
||||||
|
retprobe ? "kretprobe" : "kprobe", func_name,
|
||||||
|
libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
|
||||||
|
return link;
|
||||||
|
}
|
||||||
|
return link;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct bpf_link *bpf_program__attach_uprobe(struct bpf_program *prog,
|
||||||
|
bool retprobe, pid_t pid,
|
||||||
|
const char *binary_path,
|
||||||
|
size_t func_offset)
|
||||||
|
{
|
||||||
|
char errmsg[STRERR_BUFSIZE];
|
||||||
|
struct bpf_link *link;
|
||||||
|
int pfd, err;
|
||||||
|
|
||||||
|
pfd = perf_event_open_probe(true /* uprobe */, retprobe,
|
||||||
|
binary_path, func_offset, pid);
|
||||||
|
if (pfd < 0) {
|
||||||
|
pr_warning("program '%s': failed to create %s '%s:0x%zx' perf event: %s\n",
|
||||||
|
bpf_program__title(prog, false),
|
||||||
|
retprobe ? "uretprobe" : "uprobe",
|
||||||
|
binary_path, func_offset,
|
||||||
|
libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
|
||||||
|
return ERR_PTR(pfd);
|
||||||
|
}
|
||||||
|
link = bpf_program__attach_perf_event(prog, pfd);
|
||||||
|
if (IS_ERR(link)) {
|
||||||
|
close(pfd);
|
||||||
|
err = PTR_ERR(link);
|
||||||
|
pr_warning("program '%s': failed to attach to %s '%s:0x%zx': %s\n",
|
||||||
|
bpf_program__title(prog, false),
|
||||||
|
retprobe ? "uretprobe" : "uprobe",
|
||||||
|
binary_path, func_offset,
|
||||||
|
libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
|
||||||
|
return link;
|
||||||
|
}
|
||||||
|
return link;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int determine_tracepoint_id(const char *tp_category,
|
||||||
|
const char *tp_name)
|
||||||
|
{
|
||||||
|
char file[PATH_MAX];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = snprintf(file, sizeof(file),
|
||||||
|
"/sys/kernel/debug/tracing/events/%s/%s/id",
|
||||||
|
tp_category, tp_name);
|
||||||
|
if (ret < 0)
|
||||||
|
return -errno;
|
||||||
|
if (ret >= sizeof(file)) {
|
||||||
|
pr_debug("tracepoint %s/%s path is too long\n",
|
||||||
|
tp_category, tp_name);
|
||||||
|
return -E2BIG;
|
||||||
|
}
|
||||||
|
return parse_uint_from_file(file, "%d\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static int perf_event_open_tracepoint(const char *tp_category,
|
||||||
|
const char *tp_name)
|
||||||
|
{
|
||||||
|
struct perf_event_attr attr = {};
|
||||||
|
char errmsg[STRERR_BUFSIZE];
|
||||||
|
int tp_id, pfd, err;
|
||||||
|
|
||||||
|
tp_id = determine_tracepoint_id(tp_category, tp_name);
|
||||||
|
if (tp_id < 0) {
|
||||||
|
pr_warning("failed to determine tracepoint '%s/%s' perf event ID: %s\n",
|
||||||
|
tp_category, tp_name,
|
||||||
|
libbpf_strerror_r(tp_id, errmsg, sizeof(errmsg)));
|
||||||
|
return tp_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
attr.type = PERF_TYPE_TRACEPOINT;
|
||||||
|
attr.size = sizeof(attr);
|
||||||
|
attr.config = tp_id;
|
||||||
|
|
||||||
|
pfd = syscall(__NR_perf_event_open, &attr, -1 /* pid */, 0 /* cpu */,
|
||||||
|
-1 /* group_fd */, PERF_FLAG_FD_CLOEXEC);
|
||||||
|
if (pfd < 0) {
|
||||||
|
err = -errno;
|
||||||
|
pr_warning("tracepoint '%s/%s' perf_event_open() failed: %s\n",
|
||||||
|
tp_category, tp_name,
|
||||||
|
libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
return pfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct bpf_link *bpf_program__attach_tracepoint(struct bpf_program *prog,
|
||||||
|
const char *tp_category,
|
||||||
|
const char *tp_name)
|
||||||
|
{
|
||||||
|
char errmsg[STRERR_BUFSIZE];
|
||||||
|
struct bpf_link *link;
|
||||||
|
int pfd, err;
|
||||||
|
|
||||||
|
pfd = perf_event_open_tracepoint(tp_category, tp_name);
|
||||||
|
if (pfd < 0) {
|
||||||
|
pr_warning("program '%s': failed to create tracepoint '%s/%s' perf event: %s\n",
|
||||||
|
bpf_program__title(prog, false),
|
||||||
|
tp_category, tp_name,
|
||||||
|
libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
|
||||||
|
return ERR_PTR(pfd);
|
||||||
|
}
|
||||||
|
link = bpf_program__attach_perf_event(prog, pfd);
|
||||||
|
if (IS_ERR(link)) {
|
||||||
|
close(pfd);
|
||||||
|
err = PTR_ERR(link);
|
||||||
|
pr_warning("program '%s': failed to attach to tracepoint '%s/%s': %s\n",
|
||||||
|
bpf_program__title(prog, false),
|
||||||
|
tp_category, tp_name,
|
||||||
|
libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
|
||||||
|
return link;
|
||||||
|
}
|
||||||
|
return link;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bpf_link__destroy_fd(struct bpf_link *link)
|
||||||
|
{
|
||||||
|
struct bpf_link_fd *l = (void *)link;
|
||||||
|
|
||||||
|
return close(l->fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct bpf_link *bpf_program__attach_raw_tracepoint(struct bpf_program *prog,
|
||||||
|
const char *tp_name)
|
||||||
|
{
|
||||||
|
char errmsg[STRERR_BUFSIZE];
|
||||||
|
struct bpf_link_fd *link;
|
||||||
|
int prog_fd, pfd;
|
||||||
|
|
||||||
|
prog_fd = bpf_program__fd(prog);
|
||||||
|
if (prog_fd < 0) {
|
||||||
|
pr_warning("program '%s': can't attach before loaded\n",
|
||||||
|
bpf_program__title(prog, false));
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
link = malloc(sizeof(*link));
|
||||||
|
if (!link)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
link->link.destroy = &bpf_link__destroy_fd;
|
||||||
|
|
||||||
|
pfd = bpf_raw_tracepoint_open(tp_name, prog_fd);
|
||||||
|
if (pfd < 0) {
|
||||||
|
pfd = -errno;
|
||||||
|
free(link);
|
||||||
|
pr_warning("program '%s': failed to attach to raw tracepoint '%s': %s\n",
|
||||||
|
bpf_program__title(prog, false), tp_name,
|
||||||
|
libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
|
||||||
|
return ERR_PTR(pfd);
|
||||||
|
}
|
||||||
|
link->fd = pfd;
|
||||||
|
return (struct bpf_link *)link;
|
||||||
|
}
|
||||||
|
|
||||||
enum bpf_perf_event_ret
|
enum bpf_perf_event_ret
|
||||||
bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size,
|
bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size,
|
||||||
void **copy_mem, size_t *copy_size,
|
void **copy_mem, size_t *copy_size,
|
||||||
|
@ -3989,6 +4373,370 @@ bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct perf_buffer;
|
||||||
|
|
||||||
|
struct perf_buffer_params {
|
||||||
|
struct perf_event_attr *attr;
|
||||||
|
/* if event_cb is specified, it takes precendence */
|
||||||
|
perf_buffer_event_fn event_cb;
|
||||||
|
/* sample_cb and lost_cb are higher-level common-case callbacks */
|
||||||
|
perf_buffer_sample_fn sample_cb;
|
||||||
|
perf_buffer_lost_fn lost_cb;
|
||||||
|
void *ctx;
|
||||||
|
int cpu_cnt;
|
||||||
|
int *cpus;
|
||||||
|
int *map_keys;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct perf_cpu_buf {
|
||||||
|
struct perf_buffer *pb;
|
||||||
|
void *base; /* mmap()'ed memory */
|
||||||
|
void *buf; /* for reconstructing segmented data */
|
||||||
|
size_t buf_size;
|
||||||
|
int fd;
|
||||||
|
int cpu;
|
||||||
|
int map_key;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct perf_buffer {
|
||||||
|
perf_buffer_event_fn event_cb;
|
||||||
|
perf_buffer_sample_fn sample_cb;
|
||||||
|
perf_buffer_lost_fn lost_cb;
|
||||||
|
void *ctx; /* passed into callbacks */
|
||||||
|
|
||||||
|
size_t page_size;
|
||||||
|
size_t mmap_size;
|
||||||
|
struct perf_cpu_buf **cpu_bufs;
|
||||||
|
struct epoll_event *events;
|
||||||
|
int cpu_cnt;
|
||||||
|
int epoll_fd; /* perf event FD */
|
||||||
|
int map_fd; /* BPF_MAP_TYPE_PERF_EVENT_ARRAY BPF map FD */
|
||||||
|
};
|
||||||
|
|
||||||
|
static void perf_buffer__free_cpu_buf(struct perf_buffer *pb,
|
||||||
|
struct perf_cpu_buf *cpu_buf)
|
||||||
|
{
|
||||||
|
if (!cpu_buf)
|
||||||
|
return;
|
||||||
|
if (cpu_buf->base &&
|
||||||
|
munmap(cpu_buf->base, pb->mmap_size + pb->page_size))
|
||||||
|
pr_warning("failed to munmap cpu_buf #%d\n", cpu_buf->cpu);
|
||||||
|
if (cpu_buf->fd >= 0) {
|
||||||
|
ioctl(cpu_buf->fd, PERF_EVENT_IOC_DISABLE, 0);
|
||||||
|
close(cpu_buf->fd);
|
||||||
|
}
|
||||||
|
free(cpu_buf->buf);
|
||||||
|
free(cpu_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void perf_buffer__free(struct perf_buffer *pb)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!pb)
|
||||||
|
return;
|
||||||
|
if (pb->cpu_bufs) {
|
||||||
|
for (i = 0; i < pb->cpu_cnt && pb->cpu_bufs[i]; i++) {
|
||||||
|
struct perf_cpu_buf *cpu_buf = pb->cpu_bufs[i];
|
||||||
|
|
||||||
|
bpf_map_delete_elem(pb->map_fd, &cpu_buf->map_key);
|
||||||
|
perf_buffer__free_cpu_buf(pb, cpu_buf);
|
||||||
|
}
|
||||||
|
free(pb->cpu_bufs);
|
||||||
|
}
|
||||||
|
if (pb->epoll_fd >= 0)
|
||||||
|
close(pb->epoll_fd);
|
||||||
|
free(pb->events);
|
||||||
|
free(pb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct perf_cpu_buf *
|
||||||
|
perf_buffer__open_cpu_buf(struct perf_buffer *pb, struct perf_event_attr *attr,
|
||||||
|
int cpu, int map_key)
|
||||||
|
{
|
||||||
|
struct perf_cpu_buf *cpu_buf;
|
||||||
|
char msg[STRERR_BUFSIZE];
|
||||||
|
int err;
|
||||||
|
|
||||||
|
cpu_buf = calloc(1, sizeof(*cpu_buf));
|
||||||
|
if (!cpu_buf)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
cpu_buf->pb = pb;
|
||||||
|
cpu_buf->cpu = cpu;
|
||||||
|
cpu_buf->map_key = map_key;
|
||||||
|
|
||||||
|
cpu_buf->fd = syscall(__NR_perf_event_open, attr, -1 /* pid */, cpu,
|
||||||
|
-1, PERF_FLAG_FD_CLOEXEC);
|
||||||
|
if (cpu_buf->fd < 0) {
|
||||||
|
err = -errno;
|
||||||
|
pr_warning("failed to open perf buffer event on cpu #%d: %s\n",
|
||||||
|
cpu, libbpf_strerror_r(err, msg, sizeof(msg)));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu_buf->base = mmap(NULL, pb->mmap_size + pb->page_size,
|
||||||
|
PROT_READ | PROT_WRITE, MAP_SHARED,
|
||||||
|
cpu_buf->fd, 0);
|
||||||
|
if (cpu_buf->base == MAP_FAILED) {
|
||||||
|
cpu_buf->base = NULL;
|
||||||
|
err = -errno;
|
||||||
|
pr_warning("failed to mmap perf buffer on cpu #%d: %s\n",
|
||||||
|
cpu, libbpf_strerror_r(err, msg, sizeof(msg)));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(cpu_buf->fd, PERF_EVENT_IOC_ENABLE, 0) < 0) {
|
||||||
|
err = -errno;
|
||||||
|
pr_warning("failed to enable perf buffer event on cpu #%d: %s\n",
|
||||||
|
cpu, libbpf_strerror_r(err, msg, sizeof(msg)));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cpu_buf;
|
||||||
|
|
||||||
|
error:
|
||||||
|
perf_buffer__free_cpu_buf(pb, cpu_buf);
|
||||||
|
return (struct perf_cpu_buf *)ERR_PTR(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct perf_buffer *__perf_buffer__new(int map_fd, size_t page_cnt,
|
||||||
|
struct perf_buffer_params *p);
|
||||||
|
|
||||||
|
struct perf_buffer *perf_buffer__new(int map_fd, size_t page_cnt,
|
||||||
|
const struct perf_buffer_opts *opts)
|
||||||
|
{
|
||||||
|
struct perf_buffer_params p = {};
|
||||||
|
struct perf_event_attr attr = {
|
||||||
|
.config = PERF_COUNT_SW_BPF_OUTPUT,
|
||||||
|
.type = PERF_TYPE_SOFTWARE,
|
||||||
|
.sample_type = PERF_SAMPLE_RAW,
|
||||||
|
.sample_period = 1,
|
||||||
|
.wakeup_events = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
p.attr = &attr;
|
||||||
|
p.sample_cb = opts ? opts->sample_cb : NULL;
|
||||||
|
p.lost_cb = opts ? opts->lost_cb : NULL;
|
||||||
|
p.ctx = opts ? opts->ctx : NULL;
|
||||||
|
|
||||||
|
return __perf_buffer__new(map_fd, page_cnt, &p);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct perf_buffer *
|
||||||
|
perf_buffer__new_raw(int map_fd, size_t page_cnt,
|
||||||
|
const struct perf_buffer_raw_opts *opts)
|
||||||
|
{
|
||||||
|
struct perf_buffer_params p = {};
|
||||||
|
|
||||||
|
p.attr = opts->attr;
|
||||||
|
p.event_cb = opts->event_cb;
|
||||||
|
p.ctx = opts->ctx;
|
||||||
|
p.cpu_cnt = opts->cpu_cnt;
|
||||||
|
p.cpus = opts->cpus;
|
||||||
|
p.map_keys = opts->map_keys;
|
||||||
|
|
||||||
|
return __perf_buffer__new(map_fd, page_cnt, &p);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct perf_buffer *__perf_buffer__new(int map_fd, size_t page_cnt,
|
||||||
|
struct perf_buffer_params *p)
|
||||||
|
{
|
||||||
|
struct bpf_map_info map = {};
|
||||||
|
char msg[STRERR_BUFSIZE];
|
||||||
|
struct perf_buffer *pb;
|
||||||
|
__u32 map_info_len;
|
||||||
|
int err, i;
|
||||||
|
|
||||||
|
if (page_cnt & (page_cnt - 1)) {
|
||||||
|
pr_warning("page count should be power of two, but is %zu\n",
|
||||||
|
page_cnt);
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
map_info_len = sizeof(map);
|
||||||
|
err = bpf_obj_get_info_by_fd(map_fd, &map, &map_info_len);
|
||||||
|
if (err) {
|
||||||
|
err = -errno;
|
||||||
|
pr_warning("failed to get map info for map FD %d: %s\n",
|
||||||
|
map_fd, libbpf_strerror_r(err, msg, sizeof(msg)));
|
||||||
|
return ERR_PTR(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (map.type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) {
|
||||||
|
pr_warning("map '%s' should be BPF_MAP_TYPE_PERF_EVENT_ARRAY\n",
|
||||||
|
map.name);
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
pb = calloc(1, sizeof(*pb));
|
||||||
|
if (!pb)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
pb->event_cb = p->event_cb;
|
||||||
|
pb->sample_cb = p->sample_cb;
|
||||||
|
pb->lost_cb = p->lost_cb;
|
||||||
|
pb->ctx = p->ctx;
|
||||||
|
|
||||||
|
pb->page_size = getpagesize();
|
||||||
|
pb->mmap_size = pb->page_size * page_cnt;
|
||||||
|
pb->map_fd = map_fd;
|
||||||
|
|
||||||
|
pb->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
|
||||||
|
if (pb->epoll_fd < 0) {
|
||||||
|
err = -errno;
|
||||||
|
pr_warning("failed to create epoll instance: %s\n",
|
||||||
|
libbpf_strerror_r(err, msg, sizeof(msg)));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p->cpu_cnt > 0) {
|
||||||
|
pb->cpu_cnt = p->cpu_cnt;
|
||||||
|
} else {
|
||||||
|
pb->cpu_cnt = libbpf_num_possible_cpus();
|
||||||
|
if (pb->cpu_cnt < 0) {
|
||||||
|
err = pb->cpu_cnt;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (map.max_entries < pb->cpu_cnt)
|
||||||
|
pb->cpu_cnt = map.max_entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
pb->events = calloc(pb->cpu_cnt, sizeof(*pb->events));
|
||||||
|
if (!pb->events) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
pr_warning("failed to allocate events: out of memory\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
pb->cpu_bufs = calloc(pb->cpu_cnt, sizeof(*pb->cpu_bufs));
|
||||||
|
if (!pb->cpu_bufs) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
pr_warning("failed to allocate buffers: out of memory\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < pb->cpu_cnt; i++) {
|
||||||
|
struct perf_cpu_buf *cpu_buf;
|
||||||
|
int cpu, map_key;
|
||||||
|
|
||||||
|
cpu = p->cpu_cnt > 0 ? p->cpus[i] : i;
|
||||||
|
map_key = p->cpu_cnt > 0 ? p->map_keys[i] : i;
|
||||||
|
|
||||||
|
cpu_buf = perf_buffer__open_cpu_buf(pb, p->attr, cpu, map_key);
|
||||||
|
if (IS_ERR(cpu_buf)) {
|
||||||
|
err = PTR_ERR(cpu_buf);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
pb->cpu_bufs[i] = cpu_buf;
|
||||||
|
|
||||||
|
err = bpf_map_update_elem(pb->map_fd, &map_key,
|
||||||
|
&cpu_buf->fd, 0);
|
||||||
|
if (err) {
|
||||||
|
err = -errno;
|
||||||
|
pr_warning("failed to set cpu #%d, key %d -> perf FD %d: %s\n",
|
||||||
|
cpu, map_key, cpu_buf->fd,
|
||||||
|
libbpf_strerror_r(err, msg, sizeof(msg)));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
pb->events[i].events = EPOLLIN;
|
||||||
|
pb->events[i].data.ptr = cpu_buf;
|
||||||
|
if (epoll_ctl(pb->epoll_fd, EPOLL_CTL_ADD, cpu_buf->fd,
|
||||||
|
&pb->events[i]) < 0) {
|
||||||
|
err = -errno;
|
||||||
|
pr_warning("failed to epoll_ctl cpu #%d perf FD %d: %s\n",
|
||||||
|
cpu, cpu_buf->fd,
|
||||||
|
libbpf_strerror_r(err, msg, sizeof(msg)));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pb;
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (pb)
|
||||||
|
perf_buffer__free(pb);
|
||||||
|
return ERR_PTR(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct perf_sample_raw {
|
||||||
|
struct perf_event_header header;
|
||||||
|
uint32_t size;
|
||||||
|
char data[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct perf_sample_lost {
|
||||||
|
struct perf_event_header header;
|
||||||
|
uint64_t id;
|
||||||
|
uint64_t lost;
|
||||||
|
uint64_t sample_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
static enum bpf_perf_event_ret
|
||||||
|
perf_buffer__process_record(struct perf_event_header *e, void *ctx)
|
||||||
|
{
|
||||||
|
struct perf_cpu_buf *cpu_buf = ctx;
|
||||||
|
struct perf_buffer *pb = cpu_buf->pb;
|
||||||
|
void *data = e;
|
||||||
|
|
||||||
|
/* user wants full control over parsing perf event */
|
||||||
|
if (pb->event_cb)
|
||||||
|
return pb->event_cb(pb->ctx, cpu_buf->cpu, e);
|
||||||
|
|
||||||
|
switch (e->type) {
|
||||||
|
case PERF_RECORD_SAMPLE: {
|
||||||
|
struct perf_sample_raw *s = data;
|
||||||
|
|
||||||
|
if (pb->sample_cb)
|
||||||
|
pb->sample_cb(pb->ctx, cpu_buf->cpu, s->data, s->size);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PERF_RECORD_LOST: {
|
||||||
|
struct perf_sample_lost *s = data;
|
||||||
|
|
||||||
|
if (pb->lost_cb)
|
||||||
|
pb->lost_cb(pb->ctx, cpu_buf->cpu, s->lost);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
pr_warning("unknown perf sample type %d\n", e->type);
|
||||||
|
return LIBBPF_PERF_EVENT_ERROR;
|
||||||
|
}
|
||||||
|
return LIBBPF_PERF_EVENT_CONT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int perf_buffer__process_records(struct perf_buffer *pb,
|
||||||
|
struct perf_cpu_buf *cpu_buf)
|
||||||
|
{
|
||||||
|
enum bpf_perf_event_ret ret;
|
||||||
|
|
||||||
|
ret = bpf_perf_event_read_simple(cpu_buf->base, pb->mmap_size,
|
||||||
|
pb->page_size, &cpu_buf->buf,
|
||||||
|
&cpu_buf->buf_size,
|
||||||
|
perf_buffer__process_record, cpu_buf);
|
||||||
|
if (ret != LIBBPF_PERF_EVENT_CONT)
|
||||||
|
return ret;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int perf_buffer__poll(struct perf_buffer *pb, int timeout_ms)
|
||||||
|
{
|
||||||
|
int i, cnt, err;
|
||||||
|
|
||||||
|
cnt = epoll_wait(pb->epoll_fd, pb->events, pb->cpu_cnt, timeout_ms);
|
||||||
|
for (i = 0; i < cnt; i++) {
|
||||||
|
struct perf_cpu_buf *cpu_buf = pb->events[i].data.ptr;
|
||||||
|
|
||||||
|
err = perf_buffer__process_records(pb, cpu_buf);
|
||||||
|
if (err) {
|
||||||
|
pr_warning("error while processing records: %d\n", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cnt < 0 ? -errno : cnt;
|
||||||
|
}
|
||||||
|
|
||||||
struct bpf_prog_info_array_desc {
|
struct bpf_prog_info_array_desc {
|
||||||
int array_offset; /* e.g. offset of jited_prog_insns */
|
int array_offset; /* e.g. offset of jited_prog_insns */
|
||||||
int count_offset; /* e.g. offset of jited_prog_len */
|
int count_offset; /* e.g. offset of jited_prog_len */
|
||||||
|
|
|
@ -165,6 +165,27 @@ LIBBPF_API int bpf_program__pin(struct bpf_program *prog, const char *path);
|
||||||
LIBBPF_API int bpf_program__unpin(struct bpf_program *prog, const char *path);
|
LIBBPF_API int bpf_program__unpin(struct bpf_program *prog, const char *path);
|
||||||
LIBBPF_API void bpf_program__unload(struct bpf_program *prog);
|
LIBBPF_API void bpf_program__unload(struct bpf_program *prog);
|
||||||
|
|
||||||
|
struct bpf_link;
|
||||||
|
|
||||||
|
LIBBPF_API int bpf_link__destroy(struct bpf_link *link);
|
||||||
|
|
||||||
|
LIBBPF_API struct bpf_link *
|
||||||
|
bpf_program__attach_perf_event(struct bpf_program *prog, int pfd);
|
||||||
|
LIBBPF_API struct bpf_link *
|
||||||
|
bpf_program__attach_kprobe(struct bpf_program *prog, bool retprobe,
|
||||||
|
const char *func_name);
|
||||||
|
LIBBPF_API struct bpf_link *
|
||||||
|
bpf_program__attach_uprobe(struct bpf_program *prog, bool retprobe,
|
||||||
|
pid_t pid, const char *binary_path,
|
||||||
|
size_t func_offset);
|
||||||
|
LIBBPF_API struct bpf_link *
|
||||||
|
bpf_program__attach_tracepoint(struct bpf_program *prog,
|
||||||
|
const char *tp_category,
|
||||||
|
const char *tp_name);
|
||||||
|
LIBBPF_API struct bpf_link *
|
||||||
|
bpf_program__attach_raw_tracepoint(struct bpf_program *prog,
|
||||||
|
const char *tp_name);
|
||||||
|
|
||||||
struct bpf_insn;
|
struct bpf_insn;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -337,6 +358,26 @@ LIBBPF_API int bpf_prog_load(const char *file, enum bpf_prog_type type,
|
||||||
LIBBPF_API int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags);
|
LIBBPF_API int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags);
|
||||||
LIBBPF_API int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags);
|
LIBBPF_API int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags);
|
||||||
|
|
||||||
|
struct perf_buffer;
|
||||||
|
|
||||||
|
typedef void (*perf_buffer_sample_fn)(void *ctx, int cpu,
|
||||||
|
void *data, __u32 size);
|
||||||
|
typedef void (*perf_buffer_lost_fn)(void *ctx, int cpu, __u64 cnt);
|
||||||
|
|
||||||
|
/* common use perf buffer options */
|
||||||
|
struct perf_buffer_opts {
|
||||||
|
/* if specified, sample_cb is called for each sample */
|
||||||
|
perf_buffer_sample_fn sample_cb;
|
||||||
|
/* if specified, lost_cb is called for each batch of lost samples */
|
||||||
|
perf_buffer_lost_fn lost_cb;
|
||||||
|
/* ctx is provided to sample_cb and lost_cb */
|
||||||
|
void *ctx;
|
||||||
|
};
|
||||||
|
|
||||||
|
LIBBPF_API struct perf_buffer *
|
||||||
|
perf_buffer__new(int map_fd, size_t page_cnt,
|
||||||
|
const struct perf_buffer_opts *opts);
|
||||||
|
|
||||||
enum bpf_perf_event_ret {
|
enum bpf_perf_event_ret {
|
||||||
LIBBPF_PERF_EVENT_DONE = 0,
|
LIBBPF_PERF_EVENT_DONE = 0,
|
||||||
LIBBPF_PERF_EVENT_ERROR = -1,
|
LIBBPF_PERF_EVENT_ERROR = -1,
|
||||||
|
@ -344,6 +385,35 @@ enum bpf_perf_event_ret {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct perf_event_header;
|
struct perf_event_header;
|
||||||
|
|
||||||
|
typedef enum bpf_perf_event_ret
|
||||||
|
(*perf_buffer_event_fn)(void *ctx, int cpu, struct perf_event_header *event);
|
||||||
|
|
||||||
|
/* raw perf buffer options, giving most power and control */
|
||||||
|
struct perf_buffer_raw_opts {
|
||||||
|
/* perf event attrs passed directly into perf_event_open() */
|
||||||
|
struct perf_event_attr *attr;
|
||||||
|
/* raw event callback */
|
||||||
|
perf_buffer_event_fn event_cb;
|
||||||
|
/* ctx is provided to event_cb */
|
||||||
|
void *ctx;
|
||||||
|
/* if cpu_cnt == 0, open all on all possible CPUs (up to the number of
|
||||||
|
* max_entries of given PERF_EVENT_ARRAY map)
|
||||||
|
*/
|
||||||
|
int cpu_cnt;
|
||||||
|
/* if cpu_cnt > 0, cpus is an array of CPUs to open ring buffers on */
|
||||||
|
int *cpus;
|
||||||
|
/* if cpu_cnt > 0, map_keys specify map keys to set per-CPU FDs for */
|
||||||
|
int *map_keys;
|
||||||
|
};
|
||||||
|
|
||||||
|
LIBBPF_API struct perf_buffer *
|
||||||
|
perf_buffer__new_raw(int map_fd, size_t page_cnt,
|
||||||
|
const struct perf_buffer_raw_opts *opts);
|
||||||
|
|
||||||
|
LIBBPF_API void perf_buffer__free(struct perf_buffer *pb);
|
||||||
|
LIBBPF_API int perf_buffer__poll(struct perf_buffer *pb, int timeout_ms);
|
||||||
|
|
||||||
typedef enum bpf_perf_event_ret
|
typedef enum bpf_perf_event_ret
|
||||||
(*bpf_perf_event_print_t)(struct perf_event_header *hdr,
|
(*bpf_perf_event_print_t)(struct perf_event_header *hdr,
|
||||||
void *private_data);
|
void *private_data);
|
||||||
|
|
|
@ -167,10 +167,20 @@ LIBBPF_0.0.3 {
|
||||||
|
|
||||||
LIBBPF_0.0.4 {
|
LIBBPF_0.0.4 {
|
||||||
global:
|
global:
|
||||||
|
bpf_link__destroy;
|
||||||
|
bpf_object__load_xattr;
|
||||||
|
bpf_program__attach_kprobe;
|
||||||
|
bpf_program__attach_perf_event;
|
||||||
|
bpf_program__attach_raw_tracepoint;
|
||||||
|
bpf_program__attach_tracepoint;
|
||||||
|
bpf_program__attach_uprobe;
|
||||||
btf_dump__dump_type;
|
btf_dump__dump_type;
|
||||||
btf_dump__free;
|
btf_dump__free;
|
||||||
btf_dump__new;
|
btf_dump__new;
|
||||||
btf__parse_elf;
|
btf__parse_elf;
|
||||||
bpf_object__load_xattr;
|
|
||||||
libbpf_num_possible_cpus;
|
libbpf_num_possible_cpus;
|
||||||
|
perf_buffer__free;
|
||||||
|
perf_buffer__new;
|
||||||
|
perf_buffer__new_raw;
|
||||||
|
perf_buffer__poll;
|
||||||
} LIBBPF_0.0.3;
|
} LIBBPF_0.0.3;
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
*/
|
*/
|
||||||
char *libbpf_strerror_r(int err, char *dst, int len)
|
char *libbpf_strerror_r(int err, char *dst, int len)
|
||||||
{
|
{
|
||||||
int ret = strerror_r(err, dst, len);
|
int ret = strerror_r(err < 0 ? -err : err, dst, len);
|
||||||
if (ret)
|
if (ret)
|
||||||
snprintf(dst, len, "ERROR: strerror_r(%d)=%d", err, ret);
|
snprintf(dst, len, "ERROR: strerror_r(%d)=%d", err, ret);
|
||||||
return dst;
|
return dst;
|
||||||
|
|
|
@ -42,3 +42,4 @@ xdping
|
||||||
test_sockopt
|
test_sockopt
|
||||||
test_sockopt_sk
|
test_sockopt_sk
|
||||||
test_sockopt_multi
|
test_sockopt_multi
|
||||||
|
test_tcp_rtt
|
||||||
|
|
|
@ -8,6 +8,9 @@
|
||||||
*/
|
*/
|
||||||
#define SEC(NAME) __attribute__((section(NAME), used))
|
#define SEC(NAME) __attribute__((section(NAME), used))
|
||||||
|
|
||||||
|
#define __uint(name, val) int (*name)[val]
|
||||||
|
#define __type(name, val) val *name
|
||||||
|
|
||||||
/* helper macro to print out debug messages */
|
/* helper macro to print out debug messages */
|
||||||
#define bpf_printk(fmt, ...) \
|
#define bpf_printk(fmt, ...) \
|
||||||
({ \
|
({ \
|
||||||
|
|
|
@ -0,0 +1,166 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
#include <test_progs.h>
|
||||||
|
|
||||||
|
ssize_t get_base_addr() {
|
||||||
|
size_t start;
|
||||||
|
char buf[256];
|
||||||
|
FILE *f;
|
||||||
|
|
||||||
|
f = fopen("/proc/self/maps", "r");
|
||||||
|
if (!f)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
while (fscanf(f, "%zx-%*x %s %*s\n", &start, buf) == 2) {
|
||||||
|
if (strcmp(buf, "r-xp") == 0) {
|
||||||
|
fclose(f);
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __x86_64__
|
||||||
|
#define SYS_KPROBE_NAME "__x64_sys_nanosleep"
|
||||||
|
#else
|
||||||
|
#define SYS_KPROBE_NAME "sys_nanosleep"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void test_attach_probe(void)
|
||||||
|
{
|
||||||
|
const char *kprobe_name = "kprobe/sys_nanosleep";
|
||||||
|
const char *kretprobe_name = "kretprobe/sys_nanosleep";
|
||||||
|
const char *uprobe_name = "uprobe/trigger_func";
|
||||||
|
const char *uretprobe_name = "uretprobe/trigger_func";
|
||||||
|
const int kprobe_idx = 0, kretprobe_idx = 1;
|
||||||
|
const int uprobe_idx = 2, uretprobe_idx = 3;
|
||||||
|
const char *file = "./test_attach_probe.o";
|
||||||
|
struct bpf_program *kprobe_prog, *kretprobe_prog;
|
||||||
|
struct bpf_program *uprobe_prog, *uretprobe_prog;
|
||||||
|
struct bpf_object *obj;
|
||||||
|
int err, prog_fd, duration = 0, res;
|
||||||
|
struct bpf_link *kprobe_link = NULL;
|
||||||
|
struct bpf_link *kretprobe_link = NULL;
|
||||||
|
struct bpf_link *uprobe_link = NULL;
|
||||||
|
struct bpf_link *uretprobe_link = NULL;
|
||||||
|
int results_map_fd;
|
||||||
|
size_t uprobe_offset;
|
||||||
|
ssize_t base_addr;
|
||||||
|
|
||||||
|
base_addr = get_base_addr();
|
||||||
|
if (CHECK(base_addr < 0, "get_base_addr",
|
||||||
|
"failed to find base addr: %zd", base_addr))
|
||||||
|
return;
|
||||||
|
uprobe_offset = (size_t)&get_base_addr - base_addr;
|
||||||
|
|
||||||
|
/* load programs */
|
||||||
|
err = bpf_prog_load(file, BPF_PROG_TYPE_KPROBE, &obj, &prog_fd);
|
||||||
|
if (CHECK(err, "obj_load", "err %d errno %d\n", err, errno))
|
||||||
|
return;
|
||||||
|
|
||||||
|
kprobe_prog = bpf_object__find_program_by_title(obj, kprobe_name);
|
||||||
|
if (CHECK(!kprobe_prog, "find_probe",
|
||||||
|
"prog '%s' not found\n", kprobe_name))
|
||||||
|
goto cleanup;
|
||||||
|
kretprobe_prog = bpf_object__find_program_by_title(obj, kretprobe_name);
|
||||||
|
if (CHECK(!kretprobe_prog, "find_probe",
|
||||||
|
"prog '%s' not found\n", kretprobe_name))
|
||||||
|
goto cleanup;
|
||||||
|
uprobe_prog = bpf_object__find_program_by_title(obj, uprobe_name);
|
||||||
|
if (CHECK(!uprobe_prog, "find_probe",
|
||||||
|
"prog '%s' not found\n", uprobe_name))
|
||||||
|
goto cleanup;
|
||||||
|
uretprobe_prog = bpf_object__find_program_by_title(obj, uretprobe_name);
|
||||||
|
if (CHECK(!uretprobe_prog, "find_probe",
|
||||||
|
"prog '%s' not found\n", uretprobe_name))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
/* load maps */
|
||||||
|
results_map_fd = bpf_find_map(__func__, obj, "results_map");
|
||||||
|
if (CHECK(results_map_fd < 0, "find_results_map",
|
||||||
|
"err %d\n", results_map_fd))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
kprobe_link = bpf_program__attach_kprobe(kprobe_prog,
|
||||||
|
false /* retprobe */,
|
||||||
|
SYS_KPROBE_NAME);
|
||||||
|
if (CHECK(IS_ERR(kprobe_link), "attach_kprobe",
|
||||||
|
"err %ld\n", PTR_ERR(kprobe_link))) {
|
||||||
|
kprobe_link = NULL;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
kretprobe_link = bpf_program__attach_kprobe(kretprobe_prog,
|
||||||
|
true /* retprobe */,
|
||||||
|
SYS_KPROBE_NAME);
|
||||||
|
if (CHECK(IS_ERR(kretprobe_link), "attach_kretprobe",
|
||||||
|
"err %ld\n", PTR_ERR(kretprobe_link))) {
|
||||||
|
kretprobe_link = NULL;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
uprobe_link = bpf_program__attach_uprobe(uprobe_prog,
|
||||||
|
false /* retprobe */,
|
||||||
|
0 /* self pid */,
|
||||||
|
"/proc/self/exe",
|
||||||
|
uprobe_offset);
|
||||||
|
if (CHECK(IS_ERR(uprobe_link), "attach_uprobe",
|
||||||
|
"err %ld\n", PTR_ERR(uprobe_link))) {
|
||||||
|
uprobe_link = NULL;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
uretprobe_link = bpf_program__attach_uprobe(uretprobe_prog,
|
||||||
|
true /* retprobe */,
|
||||||
|
-1 /* any pid */,
|
||||||
|
"/proc/self/exe",
|
||||||
|
uprobe_offset);
|
||||||
|
if (CHECK(IS_ERR(uretprobe_link), "attach_uretprobe",
|
||||||
|
"err %ld\n", PTR_ERR(uretprobe_link))) {
|
||||||
|
uretprobe_link = NULL;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* trigger & validate kprobe && kretprobe */
|
||||||
|
usleep(1);
|
||||||
|
|
||||||
|
err = bpf_map_lookup_elem(results_map_fd, &kprobe_idx, &res);
|
||||||
|
if (CHECK(err, "get_kprobe_res",
|
||||||
|
"failed to get kprobe res: %d\n", err))
|
||||||
|
goto cleanup;
|
||||||
|
if (CHECK(res != kprobe_idx + 1, "check_kprobe_res",
|
||||||
|
"wrong kprobe res: %d\n", res))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
err = bpf_map_lookup_elem(results_map_fd, &kretprobe_idx, &res);
|
||||||
|
if (CHECK(err, "get_kretprobe_res",
|
||||||
|
"failed to get kretprobe res: %d\n", err))
|
||||||
|
goto cleanup;
|
||||||
|
if (CHECK(res != kretprobe_idx + 1, "check_kretprobe_res",
|
||||||
|
"wrong kretprobe res: %d\n", res))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
/* trigger & validate uprobe & uretprobe */
|
||||||
|
get_base_addr();
|
||||||
|
|
||||||
|
err = bpf_map_lookup_elem(results_map_fd, &uprobe_idx, &res);
|
||||||
|
if (CHECK(err, "get_uprobe_res",
|
||||||
|
"failed to get uprobe res: %d\n", err))
|
||||||
|
goto cleanup;
|
||||||
|
if (CHECK(res != uprobe_idx + 1, "check_uprobe_res",
|
||||||
|
"wrong uprobe res: %d\n", res))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
err = bpf_map_lookup_elem(results_map_fd, &uretprobe_idx, &res);
|
||||||
|
if (CHECK(err, "get_uretprobe_res",
|
||||||
|
"failed to get uretprobe res: %d\n", err))
|
||||||
|
goto cleanup;
|
||||||
|
if (CHECK(res != uretprobe_idx + 1, "check_uretprobe_res",
|
||||||
|
"wrong uretprobe res: %d\n", res))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
bpf_link__destroy(kprobe_link);
|
||||||
|
bpf_link__destroy(kretprobe_link);
|
||||||
|
bpf_link__destroy(uprobe_link);
|
||||||
|
bpf_link__destroy(uretprobe_link);
|
||||||
|
bpf_object__close(obj);
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <test_progs.h>
|
||||||
|
|
||||||
|
#ifdef __x86_64__
|
||||||
|
#define SYS_KPROBE_NAME "__x64_sys_nanosleep"
|
||||||
|
#else
|
||||||
|
#define SYS_KPROBE_NAME "sys_nanosleep"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void on_sample(void *ctx, int cpu, void *data, __u32 size)
|
||||||
|
{
|
||||||
|
int cpu_data = *(int *)data, duration = 0;
|
||||||
|
cpu_set_t *cpu_seen = ctx;
|
||||||
|
|
||||||
|
if (cpu_data != cpu)
|
||||||
|
CHECK(cpu_data != cpu, "check_cpu_data",
|
||||||
|
"cpu_data %d != cpu %d\n", cpu_data, cpu);
|
||||||
|
|
||||||
|
CPU_SET(cpu, cpu_seen);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_perf_buffer(void)
|
||||||
|
{
|
||||||
|
int err, prog_fd, nr_cpus, i, duration = 0;
|
||||||
|
const char *prog_name = "kprobe/sys_nanosleep";
|
||||||
|
const char *file = "./test_perf_buffer.o";
|
||||||
|
struct perf_buffer_opts pb_opts = {};
|
||||||
|
struct bpf_map *perf_buf_map;
|
||||||
|
cpu_set_t cpu_set, cpu_seen;
|
||||||
|
struct bpf_program *prog;
|
||||||
|
struct bpf_object *obj;
|
||||||
|
struct perf_buffer *pb;
|
||||||
|
struct bpf_link *link;
|
||||||
|
|
||||||
|
nr_cpus = libbpf_num_possible_cpus();
|
||||||
|
if (CHECK(nr_cpus < 0, "nr_cpus", "err %d\n", nr_cpus))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* load program */
|
||||||
|
err = bpf_prog_load(file, BPF_PROG_TYPE_KPROBE, &obj, &prog_fd);
|
||||||
|
if (CHECK(err, "obj_load", "err %d errno %d\n", err, errno))
|
||||||
|
return;
|
||||||
|
|
||||||
|
prog = bpf_object__find_program_by_title(obj, prog_name);
|
||||||
|
if (CHECK(!prog, "find_probe", "prog '%s' not found\n", prog_name))
|
||||||
|
goto out_close;
|
||||||
|
|
||||||
|
/* load map */
|
||||||
|
perf_buf_map = bpf_object__find_map_by_name(obj, "perf_buf_map");
|
||||||
|
if (CHECK(!perf_buf_map, "find_perf_buf_map", "not found\n"))
|
||||||
|
goto out_close;
|
||||||
|
|
||||||
|
/* attach kprobe */
|
||||||
|
link = bpf_program__attach_kprobe(prog, false /* retprobe */,
|
||||||
|
SYS_KPROBE_NAME);
|
||||||
|
if (CHECK(IS_ERR(link), "attach_kprobe", "err %ld\n", PTR_ERR(link)))
|
||||||
|
goto out_close;
|
||||||
|
|
||||||
|
/* set up perf buffer */
|
||||||
|
pb_opts.sample_cb = on_sample;
|
||||||
|
pb_opts.ctx = &cpu_seen;
|
||||||
|
pb = perf_buffer__new(bpf_map__fd(perf_buf_map), 1, &pb_opts);
|
||||||
|
if (CHECK(IS_ERR(pb), "perf_buf__new", "err %ld\n", PTR_ERR(pb)))
|
||||||
|
goto out_detach;
|
||||||
|
|
||||||
|
/* trigger kprobe on every CPU */
|
||||||
|
CPU_ZERO(&cpu_seen);
|
||||||
|
for (i = 0; i < nr_cpus; i++) {
|
||||||
|
CPU_ZERO(&cpu_set);
|
||||||
|
CPU_SET(i, &cpu_set);
|
||||||
|
|
||||||
|
err = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set),
|
||||||
|
&cpu_set);
|
||||||
|
if (err && CHECK(err, "set_affinity", "cpu #%d, err %d\n",
|
||||||
|
i, err))
|
||||||
|
goto out_detach;
|
||||||
|
|
||||||
|
usleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read perf buffer */
|
||||||
|
err = perf_buffer__poll(pb, 100);
|
||||||
|
if (CHECK(err < 0, "perf_buffer__poll", "err %d\n", err))
|
||||||
|
goto out_free_pb;
|
||||||
|
|
||||||
|
if (CHECK(CPU_COUNT(&cpu_seen) != nr_cpus, "seen_cpu_cnt",
|
||||||
|
"expect %d, seen %d\n", nr_cpus, CPU_COUNT(&cpu_seen)))
|
||||||
|
goto out_free_pb;
|
||||||
|
|
||||||
|
out_free_pb:
|
||||||
|
perf_buffer__free(pb);
|
||||||
|
out_detach:
|
||||||
|
bpf_link__destroy(link);
|
||||||
|
out_close:
|
||||||
|
bpf_object__close(obj);
|
||||||
|
}
|
|
@ -4,11 +4,13 @@
|
||||||
void test_stacktrace_build_id(void)
|
void test_stacktrace_build_id(void)
|
||||||
{
|
{
|
||||||
int control_map_fd, stackid_hmap_fd, stackmap_fd, stack_amap_fd;
|
int control_map_fd, stackid_hmap_fd, stackmap_fd, stack_amap_fd;
|
||||||
|
const char *prog_name = "tracepoint/random/urandom_read";
|
||||||
const char *file = "./test_stacktrace_build_id.o";
|
const char *file = "./test_stacktrace_build_id.o";
|
||||||
int bytes, efd, err, pmu_fd, prog_fd, stack_trace_len;
|
int err, prog_fd, stack_trace_len;
|
||||||
struct perf_event_attr attr = {};
|
|
||||||
__u32 key, previous_key, val, duration = 0;
|
__u32 key, previous_key, val, duration = 0;
|
||||||
|
struct bpf_program *prog;
|
||||||
struct bpf_object *obj;
|
struct bpf_object *obj;
|
||||||
|
struct bpf_link *link = NULL;
|
||||||
char buf[256];
|
char buf[256];
|
||||||
int i, j;
|
int i, j;
|
||||||
struct bpf_stack_build_id id_offs[PERF_MAX_STACK_DEPTH];
|
struct bpf_stack_build_id id_offs[PERF_MAX_STACK_DEPTH];
|
||||||
|
@ -18,44 +20,16 @@ void test_stacktrace_build_id(void)
|
||||||
retry:
|
retry:
|
||||||
err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd);
|
err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd);
|
||||||
if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno))
|
if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno))
|
||||||
goto out;
|
return;
|
||||||
|
|
||||||
/* Get the ID for the sched/sched_switch tracepoint */
|
prog = bpf_object__find_program_by_title(obj, prog_name);
|
||||||
snprintf(buf, sizeof(buf),
|
if (CHECK(!prog, "find_prog", "prog '%s' not found\n", prog_name))
|
||||||
"/sys/kernel/debug/tracing/events/random/urandom_read/id");
|
|
||||||
efd = open(buf, O_RDONLY, 0);
|
|
||||||
if (CHECK(efd < 0, "open", "err %d errno %d\n", efd, errno))
|
|
||||||
goto close_prog;
|
goto close_prog;
|
||||||
|
|
||||||
bytes = read(efd, buf, sizeof(buf));
|
link = bpf_program__attach_tracepoint(prog, "random", "urandom_read");
|
||||||
close(efd);
|
if (CHECK(IS_ERR(link), "attach_tp", "err %ld\n", PTR_ERR(link)))
|
||||||
if (CHECK(bytes <= 0 || bytes >= sizeof(buf),
|
|
||||||
"read", "bytes %d errno %d\n", bytes, errno))
|
|
||||||
goto close_prog;
|
goto close_prog;
|
||||||
|
|
||||||
/* Open the perf event and attach bpf progrram */
|
|
||||||
attr.config = strtol(buf, NULL, 0);
|
|
||||||
attr.type = PERF_TYPE_TRACEPOINT;
|
|
||||||
attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_CALLCHAIN;
|
|
||||||
attr.sample_period = 1;
|
|
||||||
attr.wakeup_events = 1;
|
|
||||||
pmu_fd = syscall(__NR_perf_event_open, &attr, -1 /* pid */,
|
|
||||||
0 /* cpu 0 */, -1 /* group id */,
|
|
||||||
0 /* flags */);
|
|
||||||
if (CHECK(pmu_fd < 0, "perf_event_open", "err %d errno %d\n",
|
|
||||||
pmu_fd, errno))
|
|
||||||
goto close_prog;
|
|
||||||
|
|
||||||
err = ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0);
|
|
||||||
if (CHECK(err, "perf_event_ioc_enable", "err %d errno %d\n",
|
|
||||||
err, errno))
|
|
||||||
goto close_pmu;
|
|
||||||
|
|
||||||
err = ioctl(pmu_fd, PERF_EVENT_IOC_SET_BPF, prog_fd);
|
|
||||||
if (CHECK(err, "perf_event_ioc_set_bpf", "err %d errno %d\n",
|
|
||||||
err, errno))
|
|
||||||
goto disable_pmu;
|
|
||||||
|
|
||||||
/* find map fds */
|
/* find map fds */
|
||||||
control_map_fd = bpf_find_map(__func__, obj, "control_map");
|
control_map_fd = bpf_find_map(__func__, obj, "control_map");
|
||||||
if (CHECK(control_map_fd < 0, "bpf_find_map control_map",
|
if (CHECK(control_map_fd < 0, "bpf_find_map control_map",
|
||||||
|
@ -133,8 +107,7 @@ void test_stacktrace_build_id(void)
|
||||||
* try it one more time.
|
* try it one more time.
|
||||||
*/
|
*/
|
||||||
if (build_id_matches < 1 && retry--) {
|
if (build_id_matches < 1 && retry--) {
|
||||||
ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE);
|
bpf_link__destroy(link);
|
||||||
close(pmu_fd);
|
|
||||||
bpf_object__close(obj);
|
bpf_object__close(obj);
|
||||||
printf("%s:WARN:Didn't find expected build ID from the map, retrying\n",
|
printf("%s:WARN:Didn't find expected build ID from the map, retrying\n",
|
||||||
__func__);
|
__func__);
|
||||||
|
@ -152,14 +125,8 @@ void test_stacktrace_build_id(void)
|
||||||
"err %d errno %d\n", err, errno);
|
"err %d errno %d\n", err, errno);
|
||||||
|
|
||||||
disable_pmu:
|
disable_pmu:
|
||||||
ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE);
|
bpf_link__destroy(link);
|
||||||
|
|
||||||
close_pmu:
|
|
||||||
close(pmu_fd);
|
|
||||||
|
|
||||||
close_prog:
|
close_prog:
|
||||||
bpf_object__close(obj);
|
bpf_object__close(obj);
|
||||||
|
|
||||||
out:
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ static __u64 read_perf_max_sample_freq(void)
|
||||||
void test_stacktrace_build_id_nmi(void)
|
void test_stacktrace_build_id_nmi(void)
|
||||||
{
|
{
|
||||||
int control_map_fd, stackid_hmap_fd, stackmap_fd, stack_amap_fd;
|
int control_map_fd, stackid_hmap_fd, stackmap_fd, stack_amap_fd;
|
||||||
|
const char *prog_name = "tracepoint/random/urandom_read";
|
||||||
const char *file = "./test_stacktrace_build_id.o";
|
const char *file = "./test_stacktrace_build_id.o";
|
||||||
int err, pmu_fd, prog_fd;
|
int err, pmu_fd, prog_fd;
|
||||||
struct perf_event_attr attr = {
|
struct perf_event_attr attr = {
|
||||||
|
@ -25,7 +26,9 @@ void test_stacktrace_build_id_nmi(void)
|
||||||
.config = PERF_COUNT_HW_CPU_CYCLES,
|
.config = PERF_COUNT_HW_CPU_CYCLES,
|
||||||
};
|
};
|
||||||
__u32 key, previous_key, val, duration = 0;
|
__u32 key, previous_key, val, duration = 0;
|
||||||
|
struct bpf_program *prog;
|
||||||
struct bpf_object *obj;
|
struct bpf_object *obj;
|
||||||
|
struct bpf_link *link;
|
||||||
char buf[256];
|
char buf[256];
|
||||||
int i, j;
|
int i, j;
|
||||||
struct bpf_stack_build_id id_offs[PERF_MAX_STACK_DEPTH];
|
struct bpf_stack_build_id id_offs[PERF_MAX_STACK_DEPTH];
|
||||||
|
@ -39,6 +42,10 @@ void test_stacktrace_build_id_nmi(void)
|
||||||
if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno))
|
if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
prog = bpf_object__find_program_by_title(obj, prog_name);
|
||||||
|
if (CHECK(!prog, "find_prog", "prog '%s' not found\n", prog_name))
|
||||||
|
goto close_prog;
|
||||||
|
|
||||||
pmu_fd = syscall(__NR_perf_event_open, &attr, -1 /* pid */,
|
pmu_fd = syscall(__NR_perf_event_open, &attr, -1 /* pid */,
|
||||||
0 /* cpu 0 */, -1 /* group id */,
|
0 /* cpu 0 */, -1 /* group id */,
|
||||||
0 /* flags */);
|
0 /* flags */);
|
||||||
|
@ -47,15 +54,12 @@ void test_stacktrace_build_id_nmi(void)
|
||||||
pmu_fd, errno))
|
pmu_fd, errno))
|
||||||
goto close_prog;
|
goto close_prog;
|
||||||
|
|
||||||
err = ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0);
|
link = bpf_program__attach_perf_event(prog, pmu_fd);
|
||||||
if (CHECK(err, "perf_event_ioc_enable", "err %d errno %d\n",
|
if (CHECK(IS_ERR(link), "attach_perf_event",
|
||||||
err, errno))
|
"err %ld\n", PTR_ERR(link))) {
|
||||||
goto close_pmu;
|
close(pmu_fd);
|
||||||
|
goto close_prog;
|
||||||
err = ioctl(pmu_fd, PERF_EVENT_IOC_SET_BPF, prog_fd);
|
}
|
||||||
if (CHECK(err, "perf_event_ioc_set_bpf", "err %d errno %d\n",
|
|
||||||
err, errno))
|
|
||||||
goto disable_pmu;
|
|
||||||
|
|
||||||
/* find map fds */
|
/* find map fds */
|
||||||
control_map_fd = bpf_find_map(__func__, obj, "control_map");
|
control_map_fd = bpf_find_map(__func__, obj, "control_map");
|
||||||
|
@ -134,8 +138,7 @@ void test_stacktrace_build_id_nmi(void)
|
||||||
* try it one more time.
|
* try it one more time.
|
||||||
*/
|
*/
|
||||||
if (build_id_matches < 1 && retry--) {
|
if (build_id_matches < 1 && retry--) {
|
||||||
ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE);
|
bpf_link__destroy(link);
|
||||||
close(pmu_fd);
|
|
||||||
bpf_object__close(obj);
|
bpf_object__close(obj);
|
||||||
printf("%s:WARN:Didn't find expected build ID from the map, retrying\n",
|
printf("%s:WARN:Didn't find expected build ID from the map, retrying\n",
|
||||||
__func__);
|
__func__);
|
||||||
|
@ -154,11 +157,7 @@ void test_stacktrace_build_id_nmi(void)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
disable_pmu:
|
disable_pmu:
|
||||||
ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE);
|
bpf_link__destroy(link);
|
||||||
|
|
||||||
close_pmu:
|
|
||||||
close(pmu_fd);
|
|
||||||
|
|
||||||
close_prog:
|
close_prog:
|
||||||
bpf_object__close(obj);
|
bpf_object__close(obj);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,50 +4,26 @@
|
||||||
void test_stacktrace_map(void)
|
void test_stacktrace_map(void)
|
||||||
{
|
{
|
||||||
int control_map_fd, stackid_hmap_fd, stackmap_fd, stack_amap_fd;
|
int control_map_fd, stackid_hmap_fd, stackmap_fd, stack_amap_fd;
|
||||||
|
const char *prog_name = "tracepoint/sched/sched_switch";
|
||||||
|
int err, prog_fd, stack_trace_len;
|
||||||
const char *file = "./test_stacktrace_map.o";
|
const char *file = "./test_stacktrace_map.o";
|
||||||
int bytes, efd, err, pmu_fd, prog_fd, stack_trace_len;
|
|
||||||
struct perf_event_attr attr = {};
|
|
||||||
__u32 key, val, duration = 0;
|
__u32 key, val, duration = 0;
|
||||||
|
struct bpf_program *prog;
|
||||||
struct bpf_object *obj;
|
struct bpf_object *obj;
|
||||||
char buf[256];
|
struct bpf_link *link;
|
||||||
|
|
||||||
err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd);
|
err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd);
|
||||||
if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno))
|
if (CHECK(err, "prog_load", "err %d errno %d\n", err, errno))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Get the ID for the sched/sched_switch tracepoint */
|
prog = bpf_object__find_program_by_title(obj, prog_name);
|
||||||
snprintf(buf, sizeof(buf),
|
if (CHECK(!prog, "find_prog", "prog '%s' not found\n", prog_name))
|
||||||
"/sys/kernel/debug/tracing/events/sched/sched_switch/id");
|
|
||||||
efd = open(buf, O_RDONLY, 0);
|
|
||||||
if (CHECK(efd < 0, "open", "err %d errno %d\n", efd, errno))
|
|
||||||
goto close_prog;
|
goto close_prog;
|
||||||
|
|
||||||
bytes = read(efd, buf, sizeof(buf));
|
link = bpf_program__attach_tracepoint(prog, "sched", "sched_switch");
|
||||||
close(efd);
|
if (CHECK(IS_ERR(link), "attach_tp", "err %ld\n", PTR_ERR(link)))
|
||||||
if (bytes <= 0 || bytes >= sizeof(buf))
|
|
||||||
goto close_prog;
|
goto close_prog;
|
||||||
|
|
||||||
/* Open the perf event and attach bpf progrram */
|
|
||||||
attr.config = strtol(buf, NULL, 0);
|
|
||||||
attr.type = PERF_TYPE_TRACEPOINT;
|
|
||||||
attr.sample_type = PERF_SAMPLE_RAW | PERF_SAMPLE_CALLCHAIN;
|
|
||||||
attr.sample_period = 1;
|
|
||||||
attr.wakeup_events = 1;
|
|
||||||
pmu_fd = syscall(__NR_perf_event_open, &attr, -1 /* pid */,
|
|
||||||
0 /* cpu 0 */, -1 /* group id */,
|
|
||||||
0 /* flags */);
|
|
||||||
if (CHECK(pmu_fd < 0, "perf_event_open", "err %d errno %d\n",
|
|
||||||
pmu_fd, errno))
|
|
||||||
goto close_prog;
|
|
||||||
|
|
||||||
err = ioctl(pmu_fd, PERF_EVENT_IOC_ENABLE, 0);
|
|
||||||
if (err)
|
|
||||||
goto disable_pmu;
|
|
||||||
|
|
||||||
err = ioctl(pmu_fd, PERF_EVENT_IOC_SET_BPF, prog_fd);
|
|
||||||
if (err)
|
|
||||||
goto disable_pmu;
|
|
||||||
|
|
||||||
/* find map fds */
|
/* find map fds */
|
||||||
control_map_fd = bpf_find_map(__func__, obj, "control_map");
|
control_map_fd = bpf_find_map(__func__, obj, "control_map");
|
||||||
if (control_map_fd < 0)
|
if (control_map_fd < 0)
|
||||||
|
@ -96,8 +72,7 @@ void test_stacktrace_map(void)
|
||||||
disable_pmu:
|
disable_pmu:
|
||||||
error_cnt++;
|
error_cnt++;
|
||||||
disable_pmu_noerr:
|
disable_pmu_noerr:
|
||||||
ioctl(pmu_fd, PERF_EVENT_IOC_DISABLE);
|
bpf_link__destroy(link);
|
||||||
close(pmu_fd);
|
|
||||||
close_prog:
|
close_prog:
|
||||||
bpf_object__close(obj);
|
bpf_object__close(obj);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,18 +3,25 @@
|
||||||
|
|
||||||
void test_stacktrace_map_raw_tp(void)
|
void test_stacktrace_map_raw_tp(void)
|
||||||
{
|
{
|
||||||
|
const char *prog_name = "tracepoint/sched/sched_switch";
|
||||||
int control_map_fd, stackid_hmap_fd, stackmap_fd;
|
int control_map_fd, stackid_hmap_fd, stackmap_fd;
|
||||||
const char *file = "./test_stacktrace_map.o";
|
const char *file = "./test_stacktrace_map.o";
|
||||||
int efd, err, prog_fd;
|
|
||||||
__u32 key, val, duration = 0;
|
__u32 key, val, duration = 0;
|
||||||
|
int err, prog_fd;
|
||||||
|
struct bpf_program *prog;
|
||||||
struct bpf_object *obj;
|
struct bpf_object *obj;
|
||||||
|
struct bpf_link *link = NULL;
|
||||||
|
|
||||||
err = bpf_prog_load(file, BPF_PROG_TYPE_RAW_TRACEPOINT, &obj, &prog_fd);
|
err = bpf_prog_load(file, BPF_PROG_TYPE_RAW_TRACEPOINT, &obj, &prog_fd);
|
||||||
if (CHECK(err, "prog_load raw tp", "err %d errno %d\n", err, errno))
|
if (CHECK(err, "prog_load raw tp", "err %d errno %d\n", err, errno))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
efd = bpf_raw_tracepoint_open("sched_switch", prog_fd);
|
prog = bpf_object__find_program_by_title(obj, prog_name);
|
||||||
if (CHECK(efd < 0, "raw_tp_open", "err %d errno %d\n", efd, errno))
|
if (CHECK(!prog, "find_prog", "prog '%s' not found\n", prog_name))
|
||||||
|
goto close_prog;
|
||||||
|
|
||||||
|
link = bpf_program__attach_raw_tracepoint(prog, "sched_switch");
|
||||||
|
if (CHECK(IS_ERR(link), "attach_raw_tp", "err %ld\n", PTR_ERR(link)))
|
||||||
goto close_prog;
|
goto close_prog;
|
||||||
|
|
||||||
/* find map fds */
|
/* find map fds */
|
||||||
|
@ -55,5 +62,7 @@ void test_stacktrace_map_raw_tp(void)
|
||||||
close_prog:
|
close_prog:
|
||||||
error_cnt++;
|
error_cnt++;
|
||||||
close_prog_noerr:
|
close_prog_noerr:
|
||||||
|
if (!IS_ERR_OR_NULL(link))
|
||||||
|
bpf_link__destroy(link);
|
||||||
bpf_object__close(obj);
|
bpf_object__close(obj);
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,26 +58,18 @@ struct frag_hdr {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 8);
|
||||||
__u32 key_size;
|
__uint(key_size, sizeof(__u32));
|
||||||
__u32 value_size;
|
__uint(value_size, sizeof(__u32));
|
||||||
} jmp_table SEC(".maps") = {
|
} jmp_table SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_PROG_ARRAY,
|
|
||||||
.max_entries = 8,
|
|
||||||
.key_size = sizeof(__u32),
|
|
||||||
.value_size = sizeof(__u32),
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 1);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
struct bpf_flow_keys *value;
|
__type(value, struct bpf_flow_keys);
|
||||||
} last_dissection SEC(".maps") = {
|
} last_dissection SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
static __always_inline int export_flow_keys(struct bpf_flow_keys *keys,
|
static __always_inline int export_flow_keys(struct bpf_flow_keys *keys,
|
||||||
int ret)
|
int ret)
|
||||||
|
|
|
@ -4,19 +4,19 @@
|
||||||
#include <linux/bpf.h>
|
#include <linux/bpf.h>
|
||||||
#include "bpf_helpers.h"
|
#include "bpf_helpers.h"
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") cg_ids = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
.key_size = sizeof(__u32),
|
__uint(max_entries, 1);
|
||||||
.value_size = sizeof(__u64),
|
__type(key, __u32);
|
||||||
.max_entries = 1,
|
__type(value, __u64);
|
||||||
};
|
} cg_ids SEC(".maps");
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") pidmap = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
.key_size = sizeof(__u32),
|
__uint(max_entries, 1);
|
||||||
.value_size = sizeof(__u32),
|
__type(key, __u32);
|
||||||
.max_entries = 1,
|
__type(value, __u32);
|
||||||
};
|
} pidmap SEC(".maps");
|
||||||
|
|
||||||
SEC("tracepoint/syscalls/sys_enter_nanosleep")
|
SEC("tracepoint/syscalls/sys_enter_nanosleep")
|
||||||
int trace(void *ctx)
|
int trace(void *ctx)
|
||||||
|
|
|
@ -11,20 +11,16 @@
|
||||||
#define NS_PER_SEC 1000000000
|
#define NS_PER_SEC 1000000000
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE);
|
||||||
struct bpf_cgroup_storage_key *key;
|
__type(key, struct bpf_cgroup_storage_key);
|
||||||
struct percpu_net_cnt *value;
|
__type(value, struct percpu_net_cnt);
|
||||||
} percpu_netcnt SEC(".maps") = {
|
} percpu_netcnt SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_CGROUP_STORAGE);
|
||||||
struct bpf_cgroup_storage_key *key;
|
__type(key, struct bpf_cgroup_storage_key);
|
||||||
struct net_cnt *value;
|
__type(value, struct net_cnt);
|
||||||
} netcnt SEC(".maps") = {
|
} netcnt SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_CGROUP_STORAGE,
|
|
||||||
};
|
|
||||||
|
|
||||||
SEC("cgroup/skb")
|
SEC("cgroup/skb")
|
||||||
int bpf_nextcnt(struct __sk_buff *skb)
|
int bpf_nextcnt(struct __sk_buff *skb)
|
||||||
|
|
|
@ -58,14 +58,6 @@ typedef struct {
|
||||||
} Event;
|
} Event;
|
||||||
|
|
||||||
|
|
||||||
struct bpf_elf_map {
|
|
||||||
__u32 type;
|
|
||||||
__u32 size_key;
|
|
||||||
__u32 size_value;
|
|
||||||
__u32 max_elem;
|
|
||||||
__u32 flags;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef int pid_t;
|
typedef int pid_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -118,47 +110,47 @@ static __always_inline bool get_frame_data(void *frame_ptr, PidData *pidData,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct bpf_elf_map SEC("maps") pidmap = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_HASH,
|
__uint(type, BPF_MAP_TYPE_HASH);
|
||||||
.size_key = sizeof(int),
|
__uint(max_entries, 1);
|
||||||
.size_value = sizeof(PidData),
|
__type(key, int);
|
||||||
.max_elem = 1,
|
__type(value, PidData);
|
||||||
};
|
} pidmap SEC(".maps");
|
||||||
|
|
||||||
struct bpf_elf_map SEC("maps") eventmap = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_HASH,
|
__uint(type, BPF_MAP_TYPE_HASH);
|
||||||
.size_key = sizeof(int),
|
__uint(max_entries, 1);
|
||||||
.size_value = sizeof(Event),
|
__type(key, int);
|
||||||
.max_elem = 1,
|
__type(value, Event);
|
||||||
};
|
} eventmap SEC(".maps");
|
||||||
|
|
||||||
struct bpf_elf_map SEC("maps") symbolmap = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_HASH,
|
__uint(type, BPF_MAP_TYPE_HASH);
|
||||||
.size_key = sizeof(Symbol),
|
__uint(max_entries, 1);
|
||||||
.size_value = sizeof(int),
|
__type(key, Symbol);
|
||||||
.max_elem = 1,
|
__type(value, int);
|
||||||
};
|
} symbolmap SEC(".maps");
|
||||||
|
|
||||||
struct bpf_elf_map SEC("maps") statsmap = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
.size_key = sizeof(Stats),
|
__uint(max_entries, 1);
|
||||||
.size_value = sizeof(int),
|
__type(key, int);
|
||||||
.max_elem = 1,
|
__type(value, Stats);
|
||||||
};
|
} statsmap SEC(".maps");
|
||||||
|
|
||||||
struct bpf_elf_map SEC("maps") perfmap = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
|
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
|
||||||
.size_key = sizeof(int),
|
__uint(max_entries, 32);
|
||||||
.size_value = sizeof(int),
|
__uint(key_size, sizeof(int));
|
||||||
.max_elem = 32,
|
__uint(value_size, sizeof(int));
|
||||||
};
|
} perfmap SEC(".maps");
|
||||||
|
|
||||||
struct bpf_elf_map SEC("maps") stackmap = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_STACK_TRACE,
|
__uint(type, BPF_MAP_TYPE_STACK_TRACE);
|
||||||
.size_key = sizeof(int),
|
__uint(max_entries, 1000);
|
||||||
.size_value = sizeof(long long) * 127,
|
__uint(key_size, sizeof(int));
|
||||||
.max_elem = 1000,
|
__uint(value_size, sizeof(long long) * 127);
|
||||||
};
|
} stackmap SEC(".maps");
|
||||||
|
|
||||||
static __always_inline int __on_event(struct pt_regs *ctx)
|
static __always_inline int __on_event(struct pt_regs *ctx)
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,14 +13,11 @@ struct socket_cookie {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_SK_STORAGE);
|
||||||
__u32 map_flags;
|
__uint(map_flags, BPF_F_NO_PREALLOC);
|
||||||
int *key;
|
__type(key, int);
|
||||||
struct socket_cookie *value;
|
__type(value, struct socket_cookie);
|
||||||
} socket_cookies SEC(".maps") = {
|
} socket_cookies SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_SK_STORAGE,
|
|
||||||
.map_flags = BPF_F_NO_PREALLOC,
|
|
||||||
};
|
|
||||||
|
|
||||||
SEC("cgroup/connect6")
|
SEC("cgroup/connect6")
|
||||||
int set_cookie(struct bpf_sock_addr *ctx)
|
int set_cookie(struct bpf_sock_addr *ctx)
|
||||||
|
|
|
@ -4,33 +4,33 @@
|
||||||
|
|
||||||
int _version SEC("version") = 1;
|
int _version SEC("version") = 1;
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") sock_map_rx = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_SOCKMAP,
|
__uint(type, BPF_MAP_TYPE_SOCKMAP);
|
||||||
.key_size = sizeof(int),
|
__uint(max_entries, 20);
|
||||||
.value_size = sizeof(int),
|
__uint(key_size, sizeof(int));
|
||||||
.max_entries = 20,
|
__uint(value_size, sizeof(int));
|
||||||
};
|
} sock_map_rx SEC(".maps");
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") sock_map_tx = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_SOCKMAP,
|
__uint(type, BPF_MAP_TYPE_SOCKMAP);
|
||||||
.key_size = sizeof(int),
|
__uint(max_entries, 20);
|
||||||
.value_size = sizeof(int),
|
__uint(key_size, sizeof(int));
|
||||||
.max_entries = 20,
|
__uint(value_size, sizeof(int));
|
||||||
};
|
} sock_map_tx SEC(".maps");
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") sock_map_msg = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_SOCKMAP,
|
__uint(type, BPF_MAP_TYPE_SOCKMAP);
|
||||||
.key_size = sizeof(int),
|
__uint(max_entries, 20);
|
||||||
.value_size = sizeof(int),
|
__uint(key_size, sizeof(int));
|
||||||
.max_entries = 20,
|
__uint(value_size, sizeof(int));
|
||||||
};
|
} sock_map_msg SEC(".maps");
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") sock_map_break = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
.key_size = sizeof(int),
|
__uint(max_entries, 20);
|
||||||
.value_size = sizeof(int),
|
__type(key, int);
|
||||||
.max_entries = 20,
|
__type(value, int);
|
||||||
};
|
} sock_map_break SEC(".maps");
|
||||||
|
|
||||||
SEC("sk_skb2")
|
SEC("sk_skb2")
|
||||||
int bpf_prog2(struct __sk_buff *skb)
|
int bpf_prog2(struct __sk_buff *skb)
|
||||||
|
|
|
@ -204,40 +204,40 @@ struct strobelight_bpf_sample {
|
||||||
char dummy_safeguard;
|
char dummy_safeguard;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") samples = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
|
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
|
||||||
.key_size = sizeof(int),
|
__uint(max_entries, 32);
|
||||||
.value_size = sizeof(int),
|
__uint(key_size, sizeof(int));
|
||||||
.max_entries = 32,
|
__uint(value_size, sizeof(int));
|
||||||
};
|
} samples SEC(".maps");
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") stacks_0 = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_STACK_TRACE,
|
__uint(type, BPF_MAP_TYPE_STACK_TRACE);
|
||||||
.key_size = sizeof(uint32_t),
|
__uint(max_entries, 16);
|
||||||
.value_size = sizeof(uint64_t) * PERF_MAX_STACK_DEPTH,
|
__uint(key_size, sizeof(uint32_t));
|
||||||
.max_entries = 16,
|
__uint(value_size, sizeof(uint64_t) * PERF_MAX_STACK_DEPTH);
|
||||||
};
|
} stacks_0 SEC(".maps");
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") stacks_1 = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_STACK_TRACE,
|
__uint(type, BPF_MAP_TYPE_STACK_TRACE);
|
||||||
.key_size = sizeof(uint32_t),
|
__uint(max_entries, 16);
|
||||||
.value_size = sizeof(uint64_t) * PERF_MAX_STACK_DEPTH,
|
__uint(key_size, sizeof(uint32_t));
|
||||||
.max_entries = 16,
|
__uint(value_size, sizeof(uint64_t) * PERF_MAX_STACK_DEPTH);
|
||||||
};
|
} stacks_1 SEC(".maps");
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") sample_heap = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_PERCPU_ARRAY,
|
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
||||||
.key_size = sizeof(uint32_t),
|
__uint(max_entries, 1);
|
||||||
.value_size = sizeof(struct strobelight_bpf_sample),
|
__type(key, uint32_t);
|
||||||
.max_entries = 1,
|
__type(value, struct strobelight_bpf_sample);
|
||||||
};
|
} sample_heap SEC(".maps");
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") strobemeta_cfgs = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_PERCPU_ARRAY,
|
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
||||||
.key_size = sizeof(pid_t),
|
__uint(max_entries, STROBE_MAX_CFGS);
|
||||||
.value_size = sizeof(struct strobemeta_cfg),
|
__type(key, pid_t);
|
||||||
.max_entries = STROBE_MAX_CFGS,
|
__type(value, struct strobemeta_cfg);
|
||||||
};
|
} strobemeta_cfgs SEC(".maps");
|
||||||
|
|
||||||
/* Type for the dtv. */
|
/* Type for the dtv. */
|
||||||
/* https://github.com/lattera/glibc/blob/master/nptl/sysdeps/x86_64/tls.h#L34 */
|
/* https://github.com/lattera/glibc/blob/master/nptl/sysdeps/x86_64/tls.h#L34 */
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
// Copyright (c) 2017 Facebook
|
||||||
|
|
||||||
|
#include <linux/ptrace.h>
|
||||||
|
#include <linux/bpf.h>
|
||||||
|
#include "bpf_helpers.h"
|
||||||
|
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
|
__uint(max_entries, 4);
|
||||||
|
__type(key, int);
|
||||||
|
__type(value, int);
|
||||||
|
} results_map SEC(".maps");
|
||||||
|
|
||||||
|
SEC("kprobe/sys_nanosleep")
|
||||||
|
int handle_sys_nanosleep_entry(struct pt_regs *ctx)
|
||||||
|
{
|
||||||
|
const int key = 0, value = 1;
|
||||||
|
|
||||||
|
bpf_map_update_elem(&results_map, &key, &value, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SEC("kretprobe/sys_nanosleep")
|
||||||
|
int handle_sys_getpid_return(struct pt_regs *ctx)
|
||||||
|
{
|
||||||
|
const int key = 1, value = 2;
|
||||||
|
|
||||||
|
bpf_map_update_elem(&results_map, &key, &value, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SEC("uprobe/trigger_func")
|
||||||
|
int handle_uprobe_entry(struct pt_regs *ctx)
|
||||||
|
{
|
||||||
|
const int key = 2, value = 3;
|
||||||
|
|
||||||
|
bpf_map_update_elem(&results_map, &key, &value, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SEC("uretprobe/trigger_func")
|
||||||
|
int handle_uprobe_return(struct pt_regs *ctx)
|
||||||
|
{
|
||||||
|
const int key = 3, value = 4;
|
||||||
|
|
||||||
|
bpf_map_update_elem(&results_map, &key, &value, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char _license[] SEC("license") = "GPL";
|
||||||
|
__u32 _version SEC("version") = 1;
|
|
@ -21,14 +21,11 @@ struct bpf_map_def SEC("maps") btf_map_legacy = {
|
||||||
BPF_ANNOTATE_KV_PAIR(btf_map_legacy, int, struct ipv_counts);
|
BPF_ANNOTATE_KV_PAIR(btf_map_legacy, int, struct ipv_counts);
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
int *key;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
struct ipv_counts *value;
|
__uint(max_entries, 4);
|
||||||
unsigned int type;
|
__type(key, int);
|
||||||
unsigned int max_entries;
|
__type(value, struct ipv_counts);
|
||||||
} btf_map SEC(".maps") = {
|
} btf_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dummy_tracepoint_args {
|
struct dummy_tracepoint_args {
|
||||||
unsigned long long pad;
|
unsigned long long pad;
|
||||||
|
|
|
@ -16,26 +16,18 @@ struct stack_trace_t {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 2);
|
||||||
__u32 key_size;
|
__uint(key_size, sizeof(int));
|
||||||
__u32 value_size;
|
__uint(value_size, sizeof(__u32));
|
||||||
} perfmap SEC(".maps") = {
|
} perfmap SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
|
|
||||||
.max_entries = 2,
|
|
||||||
.key_size = sizeof(int),
|
|
||||||
.value_size = sizeof(__u32),
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 1);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
struct stack_trace_t *value;
|
__type(value, struct stack_trace_t);
|
||||||
} stackdata_map SEC(".maps") = {
|
} stackdata_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_PERCPU_ARRAY,
|
|
||||||
.max_entries = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Allocate per-cpu space twice the needed. For the code below
|
/* Allocate per-cpu space twice the needed. For the code below
|
||||||
* usize = bpf_get_stack(ctx, raw_data, max_len, BPF_F_USER_STACK);
|
* usize = bpf_get_stack(ctx, raw_data, max_len, BPF_F_USER_STACK);
|
||||||
|
@ -56,14 +48,11 @@ struct {
|
||||||
* This is an acceptable workaround since there is one entry here.
|
* This is an acceptable workaround since there is one entry here.
|
||||||
*/
|
*/
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 1);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
__u64 (*value)[2 * MAX_STACK_RAWTP];
|
__u64 (*value)[2 * MAX_STACK_RAWTP];
|
||||||
} rawdata_map SEC(".maps") = {
|
} rawdata_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_PERCPU_ARRAY,
|
|
||||||
.max_entries = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
SEC("tracepoint/raw_syscalls/sys_enter")
|
SEC("tracepoint/raw_syscalls/sys_enter")
|
||||||
int bpf_prog1(void *ctx)
|
int bpf_prog1(void *ctx)
|
||||||
|
|
|
@ -8,24 +8,18 @@
|
||||||
#include "bpf_helpers.h"
|
#include "bpf_helpers.h"
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 11);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
__u64 *value;
|
__type(value, __u64);
|
||||||
} result_number SEC(".maps") = {
|
} result_number SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = 11,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 5);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
const char (*value)[32];
|
const char (*value)[32];
|
||||||
} result_string SEC(".maps") = {
|
} result_string SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = 5,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct foo {
|
struct foo {
|
||||||
__u8 a;
|
__u8 a;
|
||||||
|
@ -34,14 +28,11 @@ struct foo {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 5);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
struct foo *value;
|
__type(value, struct foo);
|
||||||
} result_struct SEC(".maps") = {
|
} result_struct SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = 5,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Relocation tests for __u64s. */
|
/* Relocation tests for __u64s. */
|
||||||
static __u64 num0;
|
static __u64 num0;
|
||||||
|
|
|
@ -170,54 +170,39 @@ struct eth_hdr {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_HASH);
|
||||||
__u32 max_entries;
|
__uint(max_entries, MAX_VIPS);
|
||||||
struct vip *key;
|
__type(key, struct vip);
|
||||||
struct vip_meta *value;
|
__type(value, struct vip_meta);
|
||||||
} vip_map SEC(".maps") = {
|
} vip_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_HASH,
|
|
||||||
.max_entries = MAX_VIPS,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, CH_RINGS_SIZE);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
__u32 *value;
|
__type(value, __u32);
|
||||||
} ch_rings SEC(".maps") = {
|
} ch_rings SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = CH_RINGS_SIZE,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, MAX_REALS);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
struct real_definition *value;
|
__type(value, struct real_definition);
|
||||||
} reals SEC(".maps") = {
|
} reals SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = MAX_REALS,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, MAX_VIPS);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
struct vip_stats *value;
|
__type(value, struct vip_stats);
|
||||||
} stats SEC(".maps") = {
|
} stats SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_PERCPU_ARRAY,
|
|
||||||
.max_entries = MAX_VIPS,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, CTL_MAP_SIZE);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
struct ctl_value *value;
|
__type(value, struct ctl_value);
|
||||||
} ctl_array SEC(".maps") = {
|
} ctl_array SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = CTL_MAP_SIZE,
|
|
||||||
};
|
|
||||||
|
|
||||||
static __always_inline __u32 get_packet_hash(struct packet_description *pckt,
|
static __always_inline __u32 get_packet_hash(struct packet_description *pckt,
|
||||||
bool ipv6)
|
bool ipv6)
|
||||||
|
|
|
@ -166,54 +166,39 @@ struct eth_hdr {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_HASH);
|
||||||
__u32 max_entries;
|
__uint(max_entries, MAX_VIPS);
|
||||||
struct vip *key;
|
__type(key, struct vip);
|
||||||
struct vip_meta *value;
|
__type(value, struct vip_meta);
|
||||||
} vip_map SEC(".maps") = {
|
} vip_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_HASH,
|
|
||||||
.max_entries = MAX_VIPS,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, CH_RINGS_SIZE);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
__u32 *value;
|
__type(value, __u32);
|
||||||
} ch_rings SEC(".maps") = {
|
} ch_rings SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = CH_RINGS_SIZE,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, MAX_REALS);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
struct real_definition *value;
|
__type(value, struct real_definition);
|
||||||
} reals SEC(".maps") = {
|
} reals SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = MAX_REALS,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, MAX_VIPS);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
struct vip_stats *value;
|
__type(value, struct vip_stats);
|
||||||
} stats SEC(".maps") = {
|
} stats SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_PERCPU_ARRAY,
|
|
||||||
.max_entries = MAX_VIPS,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, CTL_MAP_SIZE);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
struct ctl_value *value;
|
__type(value, struct ctl_value);
|
||||||
} ctl_array SEC(".maps") = {
|
} ctl_array SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = CTL_MAP_SIZE,
|
|
||||||
};
|
|
||||||
|
|
||||||
static __u32 get_packet_hash(struct packet_description *pckt,
|
static __u32 get_packet_hash(struct packet_description *pckt,
|
||||||
bool ipv6)
|
bool ipv6)
|
||||||
|
|
|
@ -5,23 +5,23 @@
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include "bpf_helpers.h"
|
#include "bpf_helpers.h"
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") mim_array = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_ARRAY_OF_MAPS,
|
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
|
||||||
.key_size = sizeof(int),
|
__uint(max_entries, 1);
|
||||||
|
__uint(map_flags, 0);
|
||||||
|
__uint(key_size, sizeof(__u32));
|
||||||
/* must be sizeof(__u32) for map in map */
|
/* must be sizeof(__u32) for map in map */
|
||||||
.value_size = sizeof(__u32),
|
__uint(value_size, sizeof(__u32));
|
||||||
.max_entries = 1,
|
} mim_array SEC(".maps");
|
||||||
.map_flags = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") mim_hash = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_HASH_OF_MAPS,
|
__uint(type, BPF_MAP_TYPE_HASH_OF_MAPS);
|
||||||
.key_size = sizeof(int),
|
__uint(max_entries, 1);
|
||||||
|
__uint(map_flags, 0);
|
||||||
|
__uint(key_size, sizeof(int));
|
||||||
/* must be sizeof(__u32) for map in map */
|
/* must be sizeof(__u32) for map in map */
|
||||||
.value_size = sizeof(__u32),
|
__uint(value_size, sizeof(__u32));
|
||||||
.max_entries = 1,
|
} mim_hash SEC(".maps");
|
||||||
.map_flags = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
SEC("xdp_mimtest")
|
SEC("xdp_mimtest")
|
||||||
int xdp_mimtest0(struct xdp_md *ctx)
|
int xdp_mimtest0(struct xdp_md *ctx)
|
||||||
|
|
|
@ -12,14 +12,11 @@ struct hmap_elem {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_HASH);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 1);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
struct hmap_elem *value;
|
__type(value, struct hmap_elem);
|
||||||
} hash_map SEC(".maps") = {
|
} hash_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_HASH,
|
|
||||||
.max_entries = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct array_elem {
|
struct array_elem {
|
||||||
struct bpf_spin_lock lock;
|
struct bpf_spin_lock lock;
|
||||||
|
@ -27,14 +24,11 @@ struct array_elem {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 1);
|
||||||
int *key;
|
__type(key, int);
|
||||||
struct array_elem *value;
|
__type(value, struct array_elem);
|
||||||
} array_map SEC(".maps") = {
|
} array_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
SEC("map_lock_demo")
|
SEC("map_lock_demo")
|
||||||
int bpf_map_lock_test(struct __sk_buff *skb)
|
int bpf_map_lock_test(struct __sk_buff *skb)
|
||||||
|
|
|
@ -13,12 +13,12 @@
|
||||||
|
|
||||||
int _version SEC("version") = 1;
|
int _version SEC("version") = 1;
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") test_map_id = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
.key_size = sizeof(__u32),
|
__uint(max_entries, 1);
|
||||||
.value_size = sizeof(__u64),
|
__type(key, __u32);
|
||||||
.max_entries = 1,
|
__type(value, __u64);
|
||||||
};
|
} test_map_id SEC(".maps");
|
||||||
|
|
||||||
SEC("test_obj_id_dummy")
|
SEC("test_obj_id_dummy")
|
||||||
int test_obj_id(struct __sk_buff *skb)
|
int test_obj_id(struct __sk_buff *skb)
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
// Copyright (c) 2019 Facebook
|
||||||
|
|
||||||
|
#include <linux/ptrace.h>
|
||||||
|
#include <linux/bpf.h>
|
||||||
|
#include "bpf_helpers.h"
|
||||||
|
|
||||||
|
struct {
|
||||||
|
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
|
||||||
|
__uint(key_size, sizeof(int));
|
||||||
|
__uint(value_size, sizeof(int));
|
||||||
|
} perf_buf_map SEC(".maps");
|
||||||
|
|
||||||
|
SEC("kprobe/sys_nanosleep")
|
||||||
|
int handle_sys_nanosleep_entry(struct pt_regs *ctx)
|
||||||
|
{
|
||||||
|
int cpu = bpf_get_smp_processor_id();
|
||||||
|
|
||||||
|
bpf_perf_event_output(ctx, &perf_buf_map, BPF_F_CURRENT_CPU,
|
||||||
|
&cpu, sizeof(cpu));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char _license[] SEC("license") = "GPL";
|
||||||
|
__u32 _version SEC("version") = 1;
|
|
@ -22,56 +22,39 @@ int _version SEC("version") = 1;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 1);
|
||||||
__u32 key_size;
|
__uint(key_size, sizeof(__u32));
|
||||||
__u32 value_size;
|
__uint(value_size, sizeof(__u32));
|
||||||
} outer_map SEC(".maps") = {
|
} outer_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY_OF_MAPS,
|
|
||||||
.max_entries = 1,
|
|
||||||
.key_size = sizeof(__u32),
|
|
||||||
.value_size = sizeof(__u32),
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, NR_RESULTS);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
__u32 *value;
|
__type(value, __u32);
|
||||||
} result_map SEC(".maps") = {
|
} result_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = NR_RESULTS,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 1);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
int *value;
|
__type(value, int);
|
||||||
} tmp_index_ovr_map SEC(".maps") = {
|
} tmp_index_ovr_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 1);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
__u32 *value;
|
__type(value, __u32);
|
||||||
} linum_map SEC(".maps") = {
|
} linum_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 1);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
struct data_check *value;
|
__type(value, struct data_check);
|
||||||
} data_check_map SEC(".maps") = {
|
} data_check_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
#define GOTO_DONE(_result) ({ \
|
#define GOTO_DONE(_result) ({ \
|
||||||
result = (_result); \
|
result = (_result); \
|
||||||
|
|
|
@ -5,24 +5,18 @@
|
||||||
#include "bpf_helpers.h"
|
#include "bpf_helpers.h"
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 1);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
__u64 *value;
|
__type(value, __u64);
|
||||||
} info_map SEC(".maps") = {
|
} info_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 1);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
__u64 *value;
|
__type(value, __u64);
|
||||||
} status_map SEC(".maps") = {
|
} status_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
SEC("send_signal_demo")
|
SEC("send_signal_demo")
|
||||||
int bpf_send_signal_test(void *ctx)
|
int bpf_send_signal_test(void *ctx)
|
||||||
|
|
|
@ -28,44 +28,32 @@ enum bpf_linum_array_idx {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, __NR_BPF_ADDR_ARRAY_IDX);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
struct sockaddr_in6 *value;
|
__type(value, struct sockaddr_in6);
|
||||||
} addr_map SEC(".maps") = {
|
} addr_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = __NR_BPF_ADDR_ARRAY_IDX,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, __NR_BPF_RESULT_ARRAY_IDX);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
struct bpf_sock *value;
|
__type(value, struct bpf_sock);
|
||||||
} sock_result_map SEC(".maps") = {
|
} sock_result_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = __NR_BPF_RESULT_ARRAY_IDX,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, __NR_BPF_RESULT_ARRAY_IDX);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
struct bpf_tcp_sock *value;
|
__type(value, struct bpf_tcp_sock);
|
||||||
} tcp_sock_result_map SEC(".maps") = {
|
} tcp_sock_result_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = __NR_BPF_RESULT_ARRAY_IDX,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, __NR_BPF_LINUM_ARRAY_IDX);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
__u32 *value;
|
__type(value, __u32);
|
||||||
} linum_map SEC(".maps") = {
|
} linum_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = __NR_BPF_LINUM_ARRAY_IDX,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct bpf_spinlock_cnt {
|
struct bpf_spinlock_cnt {
|
||||||
struct bpf_spin_lock lock;
|
struct bpf_spin_lock lock;
|
||||||
|
@ -73,24 +61,18 @@ struct bpf_spinlock_cnt {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_SK_STORAGE);
|
||||||
__u32 map_flags;
|
__uint(map_flags, BPF_F_NO_PREALLOC);
|
||||||
int *key;
|
__type(key, int);
|
||||||
struct bpf_spinlock_cnt *value;
|
__type(value, struct bpf_spinlock_cnt);
|
||||||
} sk_pkt_out_cnt SEC(".maps") = {
|
} sk_pkt_out_cnt SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_SK_STORAGE,
|
|
||||||
.map_flags = BPF_F_NO_PREALLOC,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_SK_STORAGE);
|
||||||
__u32 map_flags;
|
__uint(map_flags, BPF_F_NO_PREALLOC);
|
||||||
int *key;
|
__type(key, int);
|
||||||
struct bpf_spinlock_cnt *value;
|
__type(value, struct bpf_spinlock_cnt);
|
||||||
} sk_pkt_out_cnt10 SEC(".maps") = {
|
} sk_pkt_out_cnt10 SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_SK_STORAGE,
|
|
||||||
.map_flags = BPF_F_NO_PREALLOC,
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool is_loopback6(__u32 *a6)
|
static bool is_loopback6(__u32 *a6)
|
||||||
{
|
{
|
||||||
|
|
|
@ -11,14 +11,11 @@ struct hmap_elem {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_HASH);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 1);
|
||||||
int *key;
|
__type(key, int);
|
||||||
struct hmap_elem *value;
|
__type(value, struct hmap_elem);
|
||||||
} hmap SEC(".maps") = {
|
} hmap SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_HASH,
|
|
||||||
.max_entries = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct cls_elem {
|
struct cls_elem {
|
||||||
struct bpf_spin_lock lock;
|
struct bpf_spin_lock lock;
|
||||||
|
@ -26,12 +23,10 @@ struct cls_elem {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_CGROUP_STORAGE);
|
||||||
struct bpf_cgroup_storage_key *key;
|
__type(key, struct bpf_cgroup_storage_key);
|
||||||
struct cls_elem *value;
|
__type(value, struct cls_elem);
|
||||||
} cls_map SEC(".maps") = {
|
} cls_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_CGROUP_STORAGE,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct bpf_vqueue {
|
struct bpf_vqueue {
|
||||||
struct bpf_spin_lock lock;
|
struct bpf_spin_lock lock;
|
||||||
|
@ -42,14 +37,11 @@ struct bpf_vqueue {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 1);
|
||||||
int *key;
|
__type(key, int);
|
||||||
struct bpf_vqueue *value;
|
__type(value, struct bpf_vqueue);
|
||||||
} vqueue SEC(".maps") = {
|
} vqueue SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
#define CREDIT_PER_NS(delta, rate) (((delta) * rate) >> 20)
|
#define CREDIT_PER_NS(delta, rate) (((delta) * rate) >> 20)
|
||||||
|
|
||||||
|
|
|
@ -9,51 +9,36 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 1);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
__u32 *value;
|
__type(value, __u32);
|
||||||
} control_map SEC(".maps") = {
|
} control_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_HASH);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 16384);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
__u32 *value;
|
__type(value, __u32);
|
||||||
} stackid_hmap SEC(".maps") = {
|
} stackid_hmap SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_HASH,
|
|
||||||
.max_entries = 16384,
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct bpf_stack_build_id stack_trace_t[PERF_MAX_STACK_DEPTH];
|
typedef struct bpf_stack_build_id stack_trace_t[PERF_MAX_STACK_DEPTH];
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_STACK_TRACE);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 128);
|
||||||
__u32 map_flags;
|
__uint(map_flags, BPF_F_STACK_BUILD_ID);
|
||||||
__u32 key_size;
|
__uint(key_size, sizeof(__u32));
|
||||||
__u32 value_size;
|
__uint(value_size, sizeof(stack_trace_t));
|
||||||
} stackmap SEC(".maps") = {
|
} stackmap SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_STACK_TRACE,
|
|
||||||
.max_entries = 128,
|
|
||||||
.map_flags = BPF_F_STACK_BUILD_ID,
|
|
||||||
.key_size = sizeof(__u32),
|
|
||||||
.value_size = sizeof(stack_trace_t),
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 128);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
/* there seems to be a bug in kernel not handling typedef properly */
|
/* there seems to be a bug in kernel not handling typedef properly */
|
||||||
struct bpf_stack_build_id (*value)[PERF_MAX_STACK_DEPTH];
|
struct bpf_stack_build_id (*value)[PERF_MAX_STACK_DEPTH];
|
||||||
} stack_amap SEC(".maps") = {
|
} stack_amap SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = 128,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* taken from /sys/kernel/debug/tracing/events/random/urandom_read/format */
|
/* taken from /sys/kernel/debug/tracing/events/random/urandom_read/format */
|
||||||
struct random_urandom_args {
|
struct random_urandom_args {
|
||||||
|
|
|
@ -9,48 +9,34 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 1);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
__u32 *value;
|
__type(value, __u32);
|
||||||
} control_map SEC(".maps") = {
|
} control_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_HASH);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 16384);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
__u32 *value;
|
__type(value, __u32);
|
||||||
} stackid_hmap SEC(".maps") = {
|
} stackid_hmap SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_HASH,
|
|
||||||
.max_entries = 16384,
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef __u64 stack_trace_t[PERF_MAX_STACK_DEPTH];
|
typedef __u64 stack_trace_t[PERF_MAX_STACK_DEPTH];
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_STACK_TRACE);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 16384);
|
||||||
__u32 key_size;
|
__uint(key_size, sizeof(__u32));
|
||||||
__u32 value_size;
|
__uint(value_size, sizeof(stack_trace_t));
|
||||||
} stackmap SEC(".maps") = {
|
} stackmap SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_STACK_TRACE,
|
|
||||||
.max_entries = 16384,
|
|
||||||
.key_size = sizeof(__u32),
|
|
||||||
.value_size = sizeof(stack_trace_t),
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 16384);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
__u64 (*value)[PERF_MAX_STACK_DEPTH];
|
__u64 (*value)[PERF_MAX_STACK_DEPTH];
|
||||||
} stack_amap SEC(".maps") = {
|
} stack_amap SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = 16384,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* taken from /sys/kernel/debug/tracing/events/sched/sched_switch/format */
|
/* taken from /sys/kernel/debug/tracing/events/sched/sched_switch/format */
|
||||||
struct sched_switch_args {
|
struct sched_switch_args {
|
||||||
|
|
|
@ -149,14 +149,11 @@ struct tcp_estats_basic_event {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_HASH);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 1024);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
struct tcp_estats_basic_event *value;
|
__type(value, struct tcp_estats_basic_event);
|
||||||
} ev_record_map SEC(".maps") = {
|
} ev_record_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_HASH,
|
|
||||||
.max_entries = 1024,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dummy_tracepoint_args {
|
struct dummy_tracepoint_args {
|
||||||
unsigned long long pad;
|
unsigned long long pad;
|
||||||
|
|
|
@ -15,24 +15,18 @@
|
||||||
#include "test_tcpbpf.h"
|
#include "test_tcpbpf.h"
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 4);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
struct tcpbpf_globals *value;
|
__type(value, struct tcpbpf_globals);
|
||||||
} global_map SEC(".maps") = {
|
} global_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 2);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
int *value;
|
__type(value, int);
|
||||||
} sockopt_results SEC(".maps") = {
|
} sockopt_results SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline void update_event_map(int event)
|
static inline void update_event_map(int event)
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,26 +15,18 @@
|
||||||
#include "test_tcpnotify.h"
|
#include "test_tcpnotify.h"
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 4);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
struct tcpnotify_globals *value;
|
__type(value, struct tcpnotify_globals);
|
||||||
} global_map SEC(".maps") = {
|
} global_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 2);
|
||||||
__u32 key_size;
|
__uint(key_size, sizeof(int));
|
||||||
__u32 value_size;
|
__uint(value_size, sizeof(__u32));
|
||||||
} perf_event_map SEC(".maps") = {
|
} perf_event_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_PERF_EVENT_ARRAY,
|
|
||||||
.max_entries = 2,
|
|
||||||
.key_size = sizeof(int),
|
|
||||||
.value_size = sizeof(__u32),
|
|
||||||
};
|
|
||||||
|
|
||||||
int _version SEC("version") = 1;
|
int _version SEC("version") = 1;
|
||||||
|
|
||||||
|
|
|
@ -23,24 +23,18 @@
|
||||||
int _version SEC("version") = 1;
|
int _version SEC("version") = 1;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 256);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
__u64 *value;
|
__type(value, __u64);
|
||||||
} rxcnt SEC(".maps") = {
|
} rxcnt SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_PERCPU_ARRAY,
|
|
||||||
.max_entries = 256,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_HASH);
|
||||||
__u32 max_entries;
|
__uint(max_entries, MAX_IPTNL_ENTRIES);
|
||||||
struct vip *key;
|
__type(key, struct vip);
|
||||||
struct iptnl_info *value;
|
__type(value, struct iptnl_info);
|
||||||
} vip2tnl SEC(".maps") = {
|
} vip2tnl SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_HASH,
|
|
||||||
.max_entries = MAX_IPTNL_ENTRIES,
|
|
||||||
};
|
|
||||||
|
|
||||||
static __always_inline void count_tx(__u32 protocol)
|
static __always_inline void count_tx(__u32 protocol)
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,19 +18,19 @@
|
||||||
|
|
||||||
int _version SEC("version") = 1;
|
int _version SEC("version") = 1;
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") rxcnt = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_PERCPU_ARRAY,
|
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
||||||
.key_size = sizeof(__u32),
|
__uint(max_entries, 256);
|
||||||
.value_size = sizeof(__u64),
|
__type(key, __u32);
|
||||||
.max_entries = 256,
|
__type(value, __u64);
|
||||||
};
|
} rxcnt SEC(".maps");
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") vip2tnl = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_HASH,
|
__uint(type, BPF_MAP_TYPE_HASH);
|
||||||
.key_size = sizeof(struct vip),
|
__uint(max_entries, MAX_IPTNL_ENTRIES);
|
||||||
.value_size = sizeof(struct iptnl_info),
|
__type(key, struct vip);
|
||||||
.max_entries = MAX_IPTNL_ENTRIES,
|
__type(value, struct iptnl_info);
|
||||||
};
|
} vip2tnl SEC(".maps");
|
||||||
|
|
||||||
static __always_inline void count_tx(__u32 protocol)
|
static __always_inline void count_tx(__u32 protocol)
|
||||||
{
|
{
|
||||||
|
|
|
@ -164,66 +164,47 @@ struct lb_stats {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_HASH);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 512);
|
||||||
struct vip_definition *key;
|
__type(key, struct vip_definition);
|
||||||
struct vip_meta *value;
|
__type(value, struct vip_meta);
|
||||||
} vip_map SEC(".maps") = {
|
} vip_map SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_HASH,
|
|
||||||
.max_entries = 512,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_LRU_HASH);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 300);
|
||||||
__u32 map_flags;
|
__uint(map_flags, 1U << 1);
|
||||||
struct flow_key *key;
|
__type(key, struct flow_key);
|
||||||
struct real_pos_lru *value;
|
__type(value, struct real_pos_lru);
|
||||||
} lru_cache SEC(".maps") = {
|
} lru_cache SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_LRU_HASH,
|
|
||||||
.max_entries = 300,
|
|
||||||
.map_flags = 1U << 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 12 * 655);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
__u32 *value;
|
__type(value, __u32);
|
||||||
} ch_rings SEC(".maps") = {
|
} ch_rings SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = 12 * 655,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 40);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
struct real_definition *value;
|
__type(value, struct real_definition);
|
||||||
} reals SEC(".maps") = {
|
} reals SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = 40,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 515);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
struct lb_stats *value;
|
__type(value, struct lb_stats);
|
||||||
} stats SEC(".maps") = {
|
} stats SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_PERCPU_ARRAY,
|
|
||||||
.max_entries = 515,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
__u32 type;
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
__u32 max_entries;
|
__uint(max_entries, 16);
|
||||||
__u32 *key;
|
__type(key, __u32);
|
||||||
struct ctl_value *value;
|
__type(value, struct ctl_value);
|
||||||
} ctl_array SEC(".maps") = {
|
} ctl_array SEC(".maps");
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
|
||||||
.max_entries = 16,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct eth_hdr {
|
struct eth_hdr {
|
||||||
unsigned char eth_dest[6];
|
unsigned char eth_dest[6];
|
||||||
|
|
|
@ -3,12 +3,12 @@
|
||||||
#include <linux/bpf.h>
|
#include <linux/bpf.h>
|
||||||
#include "bpf_helpers.h"
|
#include "bpf_helpers.h"
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") tx_port = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_DEVMAP,
|
__uint(type, BPF_MAP_TYPE_DEVMAP);
|
||||||
.key_size = sizeof(int),
|
__uint(max_entries, 8);
|
||||||
.value_size = sizeof(int),
|
__uint(key_size, sizeof(int));
|
||||||
.max_entries = 8,
|
__uint(value_size, sizeof(int));
|
||||||
};
|
} tx_port SEC(".maps");
|
||||||
|
|
||||||
SEC("redirect_map_0")
|
SEC("redirect_map_0")
|
||||||
int xdp_redirect_map_0(struct xdp_md *xdp)
|
int xdp_redirect_map_0(struct xdp_md *xdp)
|
||||||
|
|
|
@ -17,12 +17,12 @@
|
||||||
|
|
||||||
#include "xdping.h"
|
#include "xdping.h"
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") ping_map = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_HASH,
|
__uint(type, BPF_MAP_TYPE_HASH);
|
||||||
.key_size = sizeof(__u32),
|
__uint(max_entries, 256);
|
||||||
.value_size = sizeof(struct pinginfo),
|
__type(key, __u32);
|
||||||
.max_entries = 256,
|
__type(value, struct pinginfo);
|
||||||
};
|
} ping_map SEC(".maps");
|
||||||
|
|
||||||
static __always_inline void swap_src_dst_mac(void *data)
|
static __always_inline void swap_src_dst_mac(void *data)
|
||||||
{
|
{
|
||||||
|
|
|
@ -180,7 +180,7 @@ static struct bpf_align_test tests[] = {
|
||||||
},
|
},
|
||||||
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
||||||
.matches = {
|
.matches = {
|
||||||
{7, "R0=pkt(id=0,off=8,r=8,imm=0)"},
|
{7, "R0_w=pkt(id=0,off=8,r=8,imm=0)"},
|
||||||
{7, "R3_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
|
{7, "R3_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
|
||||||
{8, "R3_w=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"},
|
{8, "R3_w=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"},
|
||||||
{9, "R3_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
{9, "R3_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||||
|
@ -315,7 +315,7 @@ static struct bpf_align_test tests[] = {
|
||||||
/* Calculated offset in R6 has unknown value, but known
|
/* Calculated offset in R6 has unknown value, but known
|
||||||
* alignment of 4.
|
* alignment of 4.
|
||||||
*/
|
*/
|
||||||
{8, "R2=pkt(id=0,off=0,r=8,imm=0)"},
|
{8, "R2_w=pkt(id=0,off=0,r=8,imm=0)"},
|
||||||
{8, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
{8, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||||
/* Offset is added to packet pointer R5, resulting in
|
/* Offset is added to packet pointer R5, resulting in
|
||||||
* known fixed offset, and variable offset from R6.
|
* known fixed offset, and variable offset from R6.
|
||||||
|
@ -405,7 +405,7 @@ static struct bpf_align_test tests[] = {
|
||||||
/* Calculated offset in R6 has unknown value, but known
|
/* Calculated offset in R6 has unknown value, but known
|
||||||
* alignment of 4.
|
* alignment of 4.
|
||||||
*/
|
*/
|
||||||
{8, "R2=pkt(id=0,off=0,r=8,imm=0)"},
|
{8, "R2_w=pkt(id=0,off=0,r=8,imm=0)"},
|
||||||
{8, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
{8, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||||
/* Adding 14 makes R6 be (4n+2) */
|
/* Adding 14 makes R6 be (4n+2) */
|
||||||
{9, "R6_w=inv(id=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"},
|
{9, "R6_w=inv(id=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"},
|
||||||
|
@ -473,12 +473,12 @@ static struct bpf_align_test tests[] = {
|
||||||
/* (4n) + 14 == (4n+2). We blow our bounds, because
|
/* (4n) + 14 == (4n+2). We blow our bounds, because
|
||||||
* the add could overflow.
|
* the add could overflow.
|
||||||
*/
|
*/
|
||||||
{7, "R5=inv(id=0,var_off=(0x2; 0xfffffffffffffffc))"},
|
{7, "R5_w=inv(id=0,var_off=(0x2; 0xfffffffffffffffc))"},
|
||||||
/* Checked s>=0 */
|
/* Checked s>=0 */
|
||||||
{9, "R5=inv(id=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"},
|
{9, "R5=inv(id=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"},
|
||||||
/* packet pointer + nonnegative (4n+2) */
|
/* packet pointer + nonnegative (4n+2) */
|
||||||
{11, "R6_w=pkt(id=1,off=0,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"},
|
{11, "R6_w=pkt(id=1,off=0,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"},
|
||||||
{13, "R4=pkt(id=1,off=4,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"},
|
{13, "R4_w=pkt(id=1,off=4,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"},
|
||||||
/* NET_IP_ALIGN + (4n+2) == (4n), alignment is fine.
|
/* NET_IP_ALIGN + (4n+2) == (4n), alignment is fine.
|
||||||
* We checked the bounds, but it might have been able
|
* We checked the bounds, but it might have been able
|
||||||
* to overflow if the packet pointer started in the
|
* to overflow if the packet pointer started in the
|
||||||
|
@ -486,7 +486,7 @@ static struct bpf_align_test tests[] = {
|
||||||
* So we did not get a 'range' on R6, and the access
|
* So we did not get a 'range' on R6, and the access
|
||||||
* attempt will fail.
|
* attempt will fail.
|
||||||
*/
|
*/
|
||||||
{15, "R6=pkt(id=1,off=0,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"},
|
{15, "R6_w=pkt(id=1,off=0,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -521,7 +521,7 @@ static struct bpf_align_test tests[] = {
|
||||||
/* Calculated offset in R6 has unknown value, but known
|
/* Calculated offset in R6 has unknown value, but known
|
||||||
* alignment of 4.
|
* alignment of 4.
|
||||||
*/
|
*/
|
||||||
{7, "R2=pkt(id=0,off=0,r=8,imm=0)"},
|
{7, "R2_w=pkt(id=0,off=0,r=8,imm=0)"},
|
||||||
{9, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
{9, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
|
||||||
/* Adding 14 makes R6 be (4n+2) */
|
/* Adding 14 makes R6 be (4n+2) */
|
||||||
{10, "R6_w=inv(id=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"},
|
{10, "R6_w=inv(id=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"},
|
||||||
|
@ -574,7 +574,7 @@ static struct bpf_align_test tests[] = {
|
||||||
/* Calculated offset in R6 has unknown value, but known
|
/* Calculated offset in R6 has unknown value, but known
|
||||||
* alignment of 4.
|
* alignment of 4.
|
||||||
*/
|
*/
|
||||||
{7, "R2=pkt(id=0,off=0,r=8,imm=0)"},
|
{7, "R2_w=pkt(id=0,off=0,r=8,imm=0)"},
|
||||||
{10, "R6_w=inv(id=0,umax_value=60,var_off=(0x0; 0x3c))"},
|
{10, "R6_w=inv(id=0,umax_value=60,var_off=(0x0; 0x3c))"},
|
||||||
/* Adding 14 makes R6 be (4n+2) */
|
/* Adding 14 makes R6 be (4n+2) */
|
||||||
{11, "R6_w=inv(id=0,umin_value=14,umax_value=74,var_off=(0x2; 0x7c))"},
|
{11, "R6_w=inv(id=0,umin_value=14,umax_value=74,var_off=(0x2; 0x7c))"},
|
||||||
|
|
|
@ -1418,7 +1418,7 @@ static void test_map_wronly(void)
|
||||||
assert(bpf_map_get_next_key(fd, &key, &value) == -1 && errno == EPERM);
|
assert(bpf_map_get_next_key(fd, &key, &value) == -1 && errno == EPERM);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prepare_reuseport_grp(int type, int map_fd,
|
static void prepare_reuseport_grp(int type, int map_fd, size_t map_elem_size,
|
||||||
__s64 *fds64, __u64 *sk_cookies,
|
__s64 *fds64, __u64 *sk_cookies,
|
||||||
unsigned int n)
|
unsigned int n)
|
||||||
{
|
{
|
||||||
|
@ -1428,6 +1428,8 @@ static void prepare_reuseport_grp(int type, int map_fd,
|
||||||
const int optval = 1;
|
const int optval = 1;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
u64 sk_cookie;
|
u64 sk_cookie;
|
||||||
|
void *value;
|
||||||
|
__s32 fd32;
|
||||||
__s64 fd64;
|
__s64 fd64;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
@ -1449,8 +1451,14 @@ static void prepare_reuseport_grp(int type, int map_fd,
|
||||||
"err:%d errno:%d\n", err, errno);
|
"err:%d errno:%d\n", err, errno);
|
||||||
|
|
||||||
/* reuseport_array does not allow unbound sk */
|
/* reuseport_array does not allow unbound sk */
|
||||||
err = bpf_map_update_elem(map_fd, &index0, &fd64,
|
if (map_elem_size == sizeof(__u64))
|
||||||
BPF_ANY);
|
value = &fd64;
|
||||||
|
else {
|
||||||
|
assert(map_elem_size == sizeof(__u32));
|
||||||
|
fd32 = (__s32)fd64;
|
||||||
|
value = &fd32;
|
||||||
|
}
|
||||||
|
err = bpf_map_update_elem(map_fd, &index0, value, BPF_ANY);
|
||||||
CHECK(err != -1 || errno != EINVAL,
|
CHECK(err != -1 || errno != EINVAL,
|
||||||
"reuseport array update unbound sk",
|
"reuseport array update unbound sk",
|
||||||
"sock_type:%d err:%d errno:%d\n",
|
"sock_type:%d err:%d errno:%d\n",
|
||||||
|
@ -1478,7 +1486,7 @@ static void prepare_reuseport_grp(int type, int map_fd,
|
||||||
* reuseport_array does not allow
|
* reuseport_array does not allow
|
||||||
* non-listening tcp sk.
|
* non-listening tcp sk.
|
||||||
*/
|
*/
|
||||||
err = bpf_map_update_elem(map_fd, &index0, &fd64,
|
err = bpf_map_update_elem(map_fd, &index0, value,
|
||||||
BPF_ANY);
|
BPF_ANY);
|
||||||
CHECK(err != -1 || errno != EINVAL,
|
CHECK(err != -1 || errno != EINVAL,
|
||||||
"reuseport array update non-listening sk",
|
"reuseport array update non-listening sk",
|
||||||
|
@ -1541,7 +1549,7 @@ static void test_reuseport_array(void)
|
||||||
for (t = 0; t < ARRAY_SIZE(types); t++) {
|
for (t = 0; t < ARRAY_SIZE(types); t++) {
|
||||||
type = types[t];
|
type = types[t];
|
||||||
|
|
||||||
prepare_reuseport_grp(type, map_fd, grpa_fds64,
|
prepare_reuseport_grp(type, map_fd, sizeof(__u64), grpa_fds64,
|
||||||
grpa_cookies, ARRAY_SIZE(grpa_fds64));
|
grpa_cookies, ARRAY_SIZE(grpa_fds64));
|
||||||
|
|
||||||
/* Test BPF_* update flags */
|
/* Test BPF_* update flags */
|
||||||
|
@ -1649,7 +1657,8 @@ static void test_reuseport_array(void)
|
||||||
sizeof(__u32), sizeof(__u32), array_size, 0);
|
sizeof(__u32), sizeof(__u32), array_size, 0);
|
||||||
CHECK(map_fd == -1, "reuseport array create",
|
CHECK(map_fd == -1, "reuseport array create",
|
||||||
"map_fd:%d, errno:%d\n", map_fd, errno);
|
"map_fd:%d, errno:%d\n", map_fd, errno);
|
||||||
prepare_reuseport_grp(SOCK_STREAM, map_fd, &fd64, &sk_cookie, 1);
|
prepare_reuseport_grp(SOCK_STREAM, map_fd, sizeof(__u32), &fd64,
|
||||||
|
&sk_cookie, 1);
|
||||||
fd = fd64;
|
fd = fd64;
|
||||||
err = bpf_map_update_elem(map_fd, &index3, &fd, BPF_NOEXIST);
|
err = bpf_map_update_elem(map_fd, &index3, &fd, BPF_NOEXIST);
|
||||||
CHECK(err == -1, "reuseport array update 32 bit fd",
|
CHECK(err == -1, "reuseport array update 32 bit fd",
|
||||||
|
|
|
@ -10,21 +10,21 @@
|
||||||
|
|
||||||
int _version SEC("version") = 1;
|
int _version SEC("version") = 1;
|
||||||
|
|
||||||
struct bpf_map_def __attribute__ ((section("maps"), used)) map_in = {
|
struct {
|
||||||
.type = MAP_TYPE,
|
__uint(type, MAP_TYPE);
|
||||||
.key_size = 0,
|
__uint(max_entries, 32);
|
||||||
.value_size = sizeof(__u32),
|
__uint(map_flags, 0);
|
||||||
.max_entries = 32,
|
__uint(key_size, 0);
|
||||||
.map_flags = 0,
|
__uint(value_size, sizeof(__u32));
|
||||||
};
|
} map_in SEC(".maps");
|
||||||
|
|
||||||
struct bpf_map_def __attribute__ ((section("maps"), used)) map_out = {
|
struct {
|
||||||
.type = MAP_TYPE,
|
__uint(type, MAP_TYPE);
|
||||||
.key_size = 0,
|
__uint(max_entries, 32);
|
||||||
.value_size = sizeof(__u32),
|
__uint(map_flags, 0);
|
||||||
.max_entries = 32,
|
__uint(key_size, 0);
|
||||||
.map_flags = 0,
|
__uint(value_size, sizeof(__u32));
|
||||||
};
|
} map_out SEC(".maps");
|
||||||
|
|
||||||
SEC("test")
|
SEC("test")
|
||||||
int _test(struct __sk_buff *skb)
|
int _test(struct __sk_buff *skb)
|
||||||
|
|
|
@ -28,61 +28,61 @@
|
||||||
* are established and verdicts are decided.
|
* are established and verdicts are decided.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") sock_map = {
|
struct {
|
||||||
.type = TEST_MAP_TYPE,
|
__uint(type, TEST_MAP_TYPE);
|
||||||
.key_size = sizeof(int),
|
__uint(max_entries, 20);
|
||||||
.value_size = sizeof(int),
|
__uint(key_size, sizeof(int));
|
||||||
.max_entries = 20,
|
__uint(value_size, sizeof(int));
|
||||||
};
|
} sock_map SEC(".maps");
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") sock_map_txmsg = {
|
struct {
|
||||||
.type = TEST_MAP_TYPE,
|
__uint(type, TEST_MAP_TYPE);
|
||||||
.key_size = sizeof(int),
|
__uint(max_entries, 20);
|
||||||
.value_size = sizeof(int),
|
__uint(key_size, sizeof(int));
|
||||||
.max_entries = 20,
|
__uint(value_size, sizeof(int));
|
||||||
};
|
} sock_map_txmsg SEC(".maps");
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") sock_map_redir = {
|
struct {
|
||||||
.type = TEST_MAP_TYPE,
|
__uint(type, TEST_MAP_TYPE);
|
||||||
.key_size = sizeof(int),
|
__uint(max_entries, 20);
|
||||||
.value_size = sizeof(int),
|
__uint(key_size, sizeof(int));
|
||||||
.max_entries = 20,
|
__uint(value_size, sizeof(int));
|
||||||
};
|
} sock_map_redir SEC(".maps");
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") sock_apply_bytes = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
.key_size = sizeof(int),
|
__uint(max_entries, 1);
|
||||||
.value_size = sizeof(int),
|
__type(key, int);
|
||||||
.max_entries = 1
|
__type(value, int);
|
||||||
};
|
} sock_apply_bytes SEC(".maps");
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") sock_cork_bytes = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
.key_size = sizeof(int),
|
__uint(max_entries, 1);
|
||||||
.value_size = sizeof(int),
|
__type(key, int);
|
||||||
.max_entries = 1
|
__type(value, int);
|
||||||
};
|
} sock_cork_bytes SEC(".maps");
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") sock_bytes = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
.key_size = sizeof(int),
|
__uint(max_entries, 6);
|
||||||
.value_size = sizeof(int),
|
__type(key, int);
|
||||||
.max_entries = 6
|
__type(value, int);
|
||||||
};
|
} sock_bytes SEC(".maps");
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") sock_redir_flags = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
.key_size = sizeof(int),
|
__uint(max_entries, 1);
|
||||||
.value_size = sizeof(int),
|
__type(key, int);
|
||||||
.max_entries = 1
|
__type(value, int);
|
||||||
};
|
} sock_redir_flags SEC(".maps");
|
||||||
|
|
||||||
struct bpf_map_def SEC("maps") sock_skb_opts = {
|
struct {
|
||||||
.type = BPF_MAP_TYPE_ARRAY,
|
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||||
.key_size = sizeof(int),
|
__uint(max_entries, 1);
|
||||||
.value_size = sizeof(int),
|
__type(key, int);
|
||||||
.max_entries = 1
|
__type(value, int);
|
||||||
};
|
} sock_skb_opts SEC(".maps");
|
||||||
|
|
||||||
SEC("sk_skb1")
|
SEC("sk_skb1")
|
||||||
int bpf_prog1(struct __sk_buff *skb)
|
int bpf_prog1(struct __sk_buff *skb)
|
||||||
|
|
|
@ -105,6 +105,7 @@ struct bpf_test {
|
||||||
__u64 data64[TEST_DATA_LEN / 8];
|
__u64 data64[TEST_DATA_LEN / 8];
|
||||||
};
|
};
|
||||||
} retvals[MAX_TEST_RUNS];
|
} retvals[MAX_TEST_RUNS];
|
||||||
|
enum bpf_attach_type expected_attach_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Note we want this to be 64 bit aligned so that the end of our array is
|
/* Note we want this to be 64 bit aligned so that the end of our array is
|
||||||
|
@ -850,6 +851,7 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
|
||||||
int fd_prog, expected_ret, alignment_prevented_execution;
|
int fd_prog, expected_ret, alignment_prevented_execution;
|
||||||
int prog_len, prog_type = test->prog_type;
|
int prog_len, prog_type = test->prog_type;
|
||||||
struct bpf_insn *prog = test->insns;
|
struct bpf_insn *prog = test->insns;
|
||||||
|
struct bpf_load_program_attr attr;
|
||||||
int run_errs, run_successes;
|
int run_errs, run_successes;
|
||||||
int map_fds[MAX_NR_MAPS];
|
int map_fds[MAX_NR_MAPS];
|
||||||
const char *expected_err;
|
const char *expected_err;
|
||||||
|
@ -881,8 +883,17 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
|
||||||
pflags |= BPF_F_STRICT_ALIGNMENT;
|
pflags |= BPF_F_STRICT_ALIGNMENT;
|
||||||
if (test->flags & F_NEEDS_EFFICIENT_UNALIGNED_ACCESS)
|
if (test->flags & F_NEEDS_EFFICIENT_UNALIGNED_ACCESS)
|
||||||
pflags |= BPF_F_ANY_ALIGNMENT;
|
pflags |= BPF_F_ANY_ALIGNMENT;
|
||||||
fd_prog = bpf_verify_program(prog_type, prog, prog_len, pflags,
|
|
||||||
"GPL", 0, bpf_vlog, sizeof(bpf_vlog), 4);
|
memset(&attr, 0, sizeof(attr));
|
||||||
|
attr.prog_type = prog_type;
|
||||||
|
attr.expected_attach_type = test->expected_attach_type;
|
||||||
|
attr.insns = prog;
|
||||||
|
attr.insns_cnt = prog_len;
|
||||||
|
attr.license = "GPL";
|
||||||
|
attr.log_level = 4;
|
||||||
|
attr.prog_flags = pflags;
|
||||||
|
|
||||||
|
fd_prog = bpf_load_program_xattr(&attr, bpf_vlog, sizeof(bpf_vlog));
|
||||||
if (fd_prog < 0 && !bpf_probe_prog_type(prog_type, 0)) {
|
if (fd_prog < 0 && !bpf_probe_prog_type(prog_type, 0)) {
|
||||||
printf("SKIP (unsupported program type %d)\n", prog_type);
|
printf("SKIP (unsupported program type %d)\n", prog_type);
|
||||||
skips++;
|
skips++;
|
||||||
|
@ -912,7 +923,7 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
|
||||||
printf("FAIL\nUnexpected success to load!\n");
|
printf("FAIL\nUnexpected success to load!\n");
|
||||||
goto fail_log;
|
goto fail_log;
|
||||||
}
|
}
|
||||||
if (!strstr(bpf_vlog, expected_err)) {
|
if (!expected_err || !strstr(bpf_vlog, expected_err)) {
|
||||||
printf("FAIL\nUnexpected error message!\n\tEXP: %s\n\tRES: %s\n",
|
printf("FAIL\nUnexpected error message!\n\tEXP: %s\n\tRES: %s\n",
|
||||||
expected_err, bpf_vlog);
|
expected_err, bpf_vlog);
|
||||||
goto fail_log;
|
goto fail_log;
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
#define BPF_SOCK_ADDR(field, off, res, err) \
|
||||||
|
{ \
|
||||||
|
"wide store to bpf_sock_addr." #field "[" #off "]", \
|
||||||
|
.insns = { \
|
||||||
|
BPF_MOV64_IMM(BPF_REG_0, 1), \
|
||||||
|
BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, \
|
||||||
|
offsetof(struct bpf_sock_addr, field[off])), \
|
||||||
|
BPF_EXIT_INSN(), \
|
||||||
|
}, \
|
||||||
|
.result = res, \
|
||||||
|
.prog_type = BPF_PROG_TYPE_CGROUP_SOCK_ADDR, \
|
||||||
|
.expected_attach_type = BPF_CGROUP_UDP6_SENDMSG, \
|
||||||
|
.errstr = err, \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* user_ip6[0] is u64 aligned */
|
||||||
|
BPF_SOCK_ADDR(user_ip6, 0, ACCEPT,
|
||||||
|
NULL),
|
||||||
|
BPF_SOCK_ADDR(user_ip6, 1, REJECT,
|
||||||
|
"invalid bpf_context access off=12 size=8"),
|
||||||
|
BPF_SOCK_ADDR(user_ip6, 2, ACCEPT,
|
||||||
|
NULL),
|
||||||
|
BPF_SOCK_ADDR(user_ip6, 3, REJECT,
|
||||||
|
"invalid bpf_context access off=20 size=8"),
|
||||||
|
|
||||||
|
/* msg_src_ip6[0] is _not_ u64 aligned */
|
||||||
|
BPF_SOCK_ADDR(msg_src_ip6, 0, REJECT,
|
||||||
|
"invalid bpf_context access off=44 size=8"),
|
||||||
|
BPF_SOCK_ADDR(msg_src_ip6, 1, ACCEPT,
|
||||||
|
NULL),
|
||||||
|
BPF_SOCK_ADDR(msg_src_ip6, 2, REJECT,
|
||||||
|
"invalid bpf_context access off=52 size=8"),
|
||||||
|
BPF_SOCK_ADDR(msg_src_ip6, 3, REJECT,
|
||||||
|
"invalid bpf_context access off=56 size=8"),
|
||||||
|
|
||||||
|
#undef BPF_SOCK_ADDR
|
Loading…
Reference in New Issue