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:
David S. Miller 2019-07-08 19:14:38 -07:00
commit 17ccf9e31e
67 changed files with 2473 additions and 1045 deletions

View File

@ -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;

View File

@ -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)

View File

@ -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);
} }

View File

@ -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;
}; };

View File

@ -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);

View File

@ -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;

View File

@ -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); \

View File

@ -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);

View File

@ -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.

View File

@ -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
;; ;;

View File

@ -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;
} }

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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 }
}; };

View File

@ -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__ */

View File

@ -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);

View File

@ -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
-------------------- --------------------

View File

@ -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 */

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -42,3 +42,4 @@ xdping
test_sockopt test_sockopt
test_sockopt_sk test_sockopt_sk
test_sockopt_multi test_sockopt_multi
test_tcp_rtt

View File

@ -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, ...) \
({ \ ({ \

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
} }

View File

@ -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);
} }

View File

@ -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);
} }

View File

@ -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);
} }

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)
{ {

View File

@ -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)

View File

@ -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)

View File

@ -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 */

View File

@ -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;

View File

@ -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;

View File

@ -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)

View File

@ -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;

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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;

View File

@ -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); \

View File

@ -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)

View File

@ -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)
{ {

View File

@ -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)

View File

@ -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 {

View File

@ -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 {

View File

@ -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;

View File

@ -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)
{ {

View File

@ -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;

View File

@ -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)
{ {

View File

@ -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)
{ {

View File

@ -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];

View File

@ -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)

View File

@ -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)
{ {

View File

@ -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))"},

View File

@ -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",

View File

@ -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)

View File

@ -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)

View File

@ -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;

View File

@ -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