forked from Minki/linux
Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf
Alexei Starovoitov says: ==================== pull-request: bpf 2020-06-12 The following pull-request contains BPF updates for your *net* tree. We've added 26 non-merge commits during the last 10 day(s) which contain a total of 27 files changed, 348 insertions(+), 93 deletions(-). The main changes are: 1) sock_hash accounting fix, from Andrey. 2) libbpf fix and probe_mem sanitizing, from Andrii. 3) sock_hash fixes, from Jakub. 4) devmap_val fix, from Jesper. 5) load_bytes_relative fix, from YiFei. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
fa7566a0d6
@ -3761,6 +3761,19 @@ struct xdp_md {
|
||||
__u32 egress_ifindex; /* txq->dev->ifindex */
|
||||
};
|
||||
|
||||
/* DEVMAP map-value layout
|
||||
*
|
||||
* The struct data-layout of map-value is a configuration interface.
|
||||
* New members can only be added to the end of this structure.
|
||||
*/
|
||||
struct bpf_devmap_val {
|
||||
__u32 ifindex; /* device index */
|
||||
union {
|
||||
int fd; /* prog fd on map write */
|
||||
__u32 id; /* prog id on map read */
|
||||
} bpf_prog;
|
||||
};
|
||||
|
||||
enum sk_action {
|
||||
SK_DROP = 0,
|
||||
SK_PASS,
|
||||
|
@ -378,7 +378,7 @@ static struct bpf_prog_list *find_attach_entry(struct list_head *progs,
|
||||
}
|
||||
|
||||
list_for_each_entry(pl, progs, node) {
|
||||
if (prog && pl->prog == prog)
|
||||
if (prog && pl->prog == prog && prog != replace_prog)
|
||||
/* disallow attaching the same prog twice */
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (link && pl->link == link)
|
||||
|
@ -60,15 +60,6 @@ struct xdp_dev_bulk_queue {
|
||||
unsigned int count;
|
||||
};
|
||||
|
||||
/* DEVMAP values */
|
||||
struct bpf_devmap_val {
|
||||
u32 ifindex; /* device index */
|
||||
union {
|
||||
int fd; /* prog fd on map write */
|
||||
u32 id; /* prog id on map read */
|
||||
} bpf_prog;
|
||||
};
|
||||
|
||||
struct bpf_dtab_netdev {
|
||||
struct net_device *dev; /* must be first member, due to tracepoint */
|
||||
struct hlist_node index_hlist;
|
||||
@ -479,6 +470,7 @@ static struct xdp_buff *dev_map_run_prog(struct net_device *dev,
|
||||
struct xdp_txq_info txq = { .dev = dev };
|
||||
u32 act;
|
||||
|
||||
xdp_set_data_meta_invalid(xdp);
|
||||
xdp->txq = &txq;
|
||||
|
||||
act = bpf_prog_run_xdp(xdp_prog, xdp);
|
||||
@ -618,7 +610,7 @@ static struct bpf_dtab_netdev *__dev_map_alloc_node(struct net *net,
|
||||
if (!dev->dev)
|
||||
goto err_out;
|
||||
|
||||
if (val->bpf_prog.fd >= 0) {
|
||||
if (val->bpf_prog.fd > 0) {
|
||||
prog = bpf_prog_get_type_dev(val->bpf_prog.fd,
|
||||
BPF_PROG_TYPE_XDP, false);
|
||||
if (IS_ERR(prog))
|
||||
@ -652,8 +644,8 @@ static int __dev_map_update_elem(struct net *net, struct bpf_map *map,
|
||||
void *key, void *value, u64 map_flags)
|
||||
{
|
||||
struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map);
|
||||
struct bpf_devmap_val val = { .bpf_prog.fd = -1 };
|
||||
struct bpf_dtab_netdev *dev, *old_dev;
|
||||
struct bpf_devmap_val val = {};
|
||||
u32 i = *(u32 *)key;
|
||||
|
||||
if (unlikely(map_flags > BPF_EXIST))
|
||||
@ -669,7 +661,7 @@ static int __dev_map_update_elem(struct net *net, struct bpf_map *map,
|
||||
if (!val.ifindex) {
|
||||
dev = NULL;
|
||||
/* can not specify fd if ifindex is 0 */
|
||||
if (val.bpf_prog.fd != -1)
|
||||
if (val.bpf_prog.fd > 0)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
dev = __dev_map_alloc_node(net, dtab, &val, i);
|
||||
@ -699,8 +691,8 @@ static int __dev_map_hash_update_elem(struct net *net, struct bpf_map *map,
|
||||
void *key, void *value, u64 map_flags)
|
||||
{
|
||||
struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map);
|
||||
struct bpf_devmap_val val = { .bpf_prog.fd = -1 };
|
||||
struct bpf_dtab_netdev *dev, *old_dev;
|
||||
struct bpf_devmap_val val = {};
|
||||
u32 idx = *(u32 *)key;
|
||||
unsigned long flags;
|
||||
int err = -EEXIST;
|
||||
|
@ -3158,6 +3158,7 @@ static struct bpf_insn *bpf_insn_prepare_dump(const struct bpf_prog *prog)
|
||||
struct bpf_insn *insns;
|
||||
u32 off, type;
|
||||
u64 imm;
|
||||
u8 code;
|
||||
int i;
|
||||
|
||||
insns = kmemdup(prog->insnsi, bpf_prog_insn_size(prog),
|
||||
@ -3166,21 +3167,27 @@ static struct bpf_insn *bpf_insn_prepare_dump(const struct bpf_prog *prog)
|
||||
return insns;
|
||||
|
||||
for (i = 0; i < prog->len; i++) {
|
||||
if (insns[i].code == (BPF_JMP | BPF_TAIL_CALL)) {
|
||||
code = insns[i].code;
|
||||
|
||||
if (code == (BPF_JMP | BPF_TAIL_CALL)) {
|
||||
insns[i].code = BPF_JMP | BPF_CALL;
|
||||
insns[i].imm = BPF_FUNC_tail_call;
|
||||
/* fall-through */
|
||||
}
|
||||
if (insns[i].code == (BPF_JMP | BPF_CALL) ||
|
||||
insns[i].code == (BPF_JMP | BPF_CALL_ARGS)) {
|
||||
if (insns[i].code == (BPF_JMP | BPF_CALL_ARGS))
|
||||
if (code == (BPF_JMP | BPF_CALL) ||
|
||||
code == (BPF_JMP | BPF_CALL_ARGS)) {
|
||||
if (code == (BPF_JMP | BPF_CALL_ARGS))
|
||||
insns[i].code = BPF_JMP | BPF_CALL;
|
||||
if (!bpf_dump_raw_ok())
|
||||
insns[i].imm = 0;
|
||||
continue;
|
||||
}
|
||||
if (BPF_CLASS(code) == BPF_LDX && BPF_MODE(code) == BPF_PROBE_MEM) {
|
||||
insns[i].code = BPF_LDX | BPF_SIZE(code) | BPF_MEM;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (insns[i].code != (BPF_LD | BPF_IMM | BPF_DW))
|
||||
if (code != (BPF_LD | BPF_IMM | BPF_DW))
|
||||
continue;
|
||||
|
||||
imm = ((u64)insns[i + 1].imm << 32) | (u32)insns[i].imm;
|
||||
|
@ -7552,7 +7552,7 @@ static int check_btf_func(struct bpf_verifier_env *env,
|
||||
const struct btf *btf;
|
||||
void __user *urecord;
|
||||
u32 prev_offset = 0;
|
||||
int ret = 0;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
nfuncs = attr->func_info_cnt;
|
||||
if (!nfuncs)
|
||||
|
@ -1629,7 +1629,7 @@ int bpf_get_kprobe_info(const struct perf_event *event, u32 *fd_type,
|
||||
if (perf_type_tracepoint)
|
||||
tk = find_trace_kprobe(pevent, group);
|
||||
else
|
||||
tk = event->tp_event->data;
|
||||
tk = trace_kprobe_primary_from_call(event->tp_event);
|
||||
if (!tk)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -1412,7 +1412,7 @@ int bpf_get_uprobe_info(const struct perf_event *event, u32 *fd_type,
|
||||
if (perf_type_tracepoint)
|
||||
tu = find_probe_event(pevent, group);
|
||||
else
|
||||
tu = event->tp_event->data;
|
||||
tu = trace_uprobe_primary_from_call(event->tp_event);
|
||||
if (!tu)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -1755,25 +1755,27 @@ BPF_CALL_5(bpf_skb_load_bytes_relative, const struct sk_buff *, skb,
|
||||
u32, offset, void *, to, u32, len, u32, start_header)
|
||||
{
|
||||
u8 *end = skb_tail_pointer(skb);
|
||||
u8 *net = skb_network_header(skb);
|
||||
u8 *mac = skb_mac_header(skb);
|
||||
u8 *ptr;
|
||||
u8 *start, *ptr;
|
||||
|
||||
if (unlikely(offset > 0xffff || len > (end - mac)))
|
||||
if (unlikely(offset > 0xffff))
|
||||
goto err_clear;
|
||||
|
||||
switch (start_header) {
|
||||
case BPF_HDR_START_MAC:
|
||||
ptr = mac + offset;
|
||||
if (unlikely(!skb_mac_header_was_set(skb)))
|
||||
goto err_clear;
|
||||
start = skb_mac_header(skb);
|
||||
break;
|
||||
case BPF_HDR_START_NET:
|
||||
ptr = net + offset;
|
||||
start = skb_network_header(skb);
|
||||
break;
|
||||
default:
|
||||
goto err_clear;
|
||||
}
|
||||
|
||||
if (likely(ptr >= mac && ptr + len <= end)) {
|
||||
ptr = start + offset;
|
||||
|
||||
if (likely(ptr + len <= end)) {
|
||||
memcpy(to, ptr, len);
|
||||
return 0;
|
||||
}
|
||||
@ -4340,8 +4342,6 @@ static int _bpf_setsockopt(struct sock *sk, int level, int optname,
|
||||
}
|
||||
break;
|
||||
case SO_BINDTODEVICE:
|
||||
ret = -ENOPROTOOPT;
|
||||
#ifdef CONFIG_NETDEVICES
|
||||
optlen = min_t(long, optlen, IFNAMSIZ - 1);
|
||||
strncpy(devname, optval, optlen);
|
||||
devname[optlen] = 0;
|
||||
@ -4360,7 +4360,6 @@ static int _bpf_setsockopt(struct sock *sk, int level, int optname,
|
||||
dev_put(dev);
|
||||
}
|
||||
ret = sock_bindtoindex(sk, ifindex, false);
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
|
@ -424,10 +424,7 @@ static int sock_map_get_next_key(struct bpf_map *map, void *key, void *next)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool sock_map_redirect_allowed(const struct sock *sk)
|
||||
{
|
||||
return sk->sk_state != TCP_LISTEN;
|
||||
}
|
||||
static bool sock_map_redirect_allowed(const struct sock *sk);
|
||||
|
||||
static int sock_map_update_common(struct bpf_map *map, u32 idx,
|
||||
struct sock *sk, u64 flags)
|
||||
@ -508,6 +505,11 @@ static bool sk_is_udp(const struct sock *sk)
|
||||
sk->sk_protocol == IPPROTO_UDP;
|
||||
}
|
||||
|
||||
static bool sock_map_redirect_allowed(const struct sock *sk)
|
||||
{
|
||||
return sk_is_tcp(sk) && sk->sk_state != TCP_LISTEN;
|
||||
}
|
||||
|
||||
static bool sock_map_sk_is_suitable(const struct sock *sk)
|
||||
{
|
||||
return sk_is_tcp(sk) || sk_is_udp(sk);
|
||||
@ -989,11 +991,15 @@ static struct bpf_map *sock_hash_alloc(union bpf_attr *attr)
|
||||
err = -EINVAL;
|
||||
goto free_htab;
|
||||
}
|
||||
err = bpf_map_charge_init(&htab->map.memory, cost);
|
||||
if (err)
|
||||
goto free_htab;
|
||||
|
||||
htab->buckets = bpf_map_area_alloc(htab->buckets_num *
|
||||
sizeof(struct bpf_htab_bucket),
|
||||
htab->map.numa_node);
|
||||
if (!htab->buckets) {
|
||||
bpf_map_charge_finish(&htab->map.memory);
|
||||
err = -ENOMEM;
|
||||
goto free_htab;
|
||||
}
|
||||
@ -1013,6 +1019,7 @@ static void sock_hash_free(struct bpf_map *map)
|
||||
{
|
||||
struct bpf_htab *htab = container_of(map, struct bpf_htab, map);
|
||||
struct bpf_htab_bucket *bucket;
|
||||
struct hlist_head unlink_list;
|
||||
struct bpf_htab_elem *elem;
|
||||
struct hlist_node *node;
|
||||
int i;
|
||||
@ -1024,13 +1031,32 @@ static void sock_hash_free(struct bpf_map *map)
|
||||
synchronize_rcu();
|
||||
for (i = 0; i < htab->buckets_num; i++) {
|
||||
bucket = sock_hash_select_bucket(htab, i);
|
||||
hlist_for_each_entry_safe(elem, node, &bucket->head, node) {
|
||||
hlist_del_rcu(&elem->node);
|
||||
|
||||
/* We are racing with sock_hash_delete_from_link to
|
||||
* enter the spin-lock critical section. Every socket on
|
||||
* the list is still linked to sockhash. Since link
|
||||
* exists, psock exists and holds a ref to socket. That
|
||||
* lets us to grab a socket ref too.
|
||||
*/
|
||||
raw_spin_lock_bh(&bucket->lock);
|
||||
hlist_for_each_entry(elem, &bucket->head, node)
|
||||
sock_hold(elem->sk);
|
||||
hlist_move_list(&bucket->head, &unlink_list);
|
||||
raw_spin_unlock_bh(&bucket->lock);
|
||||
|
||||
/* Process removed entries out of atomic context to
|
||||
* block for socket lock before deleting the psock's
|
||||
* link to sockhash.
|
||||
*/
|
||||
hlist_for_each_entry_safe(elem, node, &unlink_list, node) {
|
||||
hlist_del(&elem->node);
|
||||
lock_sock(elem->sk);
|
||||
rcu_read_lock();
|
||||
sock_map_unref(elem->sk, elem);
|
||||
rcu_read_unlock();
|
||||
release_sock(elem->sk);
|
||||
sock_put(elem->sk);
|
||||
sock_hash_free_elem(htab, elem);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,6 +64,9 @@ int __tcp_bpf_recvmsg(struct sock *sk, struct sk_psock *psock,
|
||||
} while (i != msg_rx->sg.end);
|
||||
|
||||
if (unlikely(peek)) {
|
||||
if (msg_rx == list_last_entry(&psock->ingress_msg,
|
||||
struct sk_msg, list))
|
||||
break;
|
||||
msg_rx = list_next_entry(msg_rx, list);
|
||||
continue;
|
||||
}
|
||||
@ -242,6 +245,9 @@ static int tcp_bpf_wait_data(struct sock *sk, struct sk_psock *psock,
|
||||
DEFINE_WAIT_FUNC(wait, woken_wake_function);
|
||||
int ret = 0;
|
||||
|
||||
if (sk->sk_shutdown & RCV_SHUTDOWN)
|
||||
return 1;
|
||||
|
||||
if (!timeo)
|
||||
return ret;
|
||||
|
||||
|
@ -352,10 +352,8 @@ static int xsk_generic_xmit(struct sock *sk)
|
||||
|
||||
len = desc.len;
|
||||
skb = sock_alloc_send_skb(sk, len, 1, &err);
|
||||
if (unlikely(!skb)) {
|
||||
err = -EAGAIN;
|
||||
if (unlikely(!skb))
|
||||
goto out;
|
||||
}
|
||||
|
||||
skb_put(skb, len);
|
||||
addr = desc.addr;
|
||||
|
@ -143,8 +143,8 @@ gen_btf()
|
||||
fi
|
||||
|
||||
pahole_ver=$(${PAHOLE} --version | sed -E 's/v([0-9]+)\.([0-9]+)/\1\2/')
|
||||
if [ "${pahole_ver}" -lt "113" ]; then
|
||||
echo >&2 "BTF: ${1}: pahole version $(${PAHOLE} --version) is too old, need at least v1.13"
|
||||
if [ "${pahole_ver}" -lt "116" ]; then
|
||||
echo >&2 "BTF: ${1}: pahole version $(${PAHOLE} --version) is too old, need at least v1.16"
|
||||
return 1
|
||||
fi
|
||||
|
||||
|
@ -3,7 +3,6 @@ include ../scripts/Makefile.include
|
||||
|
||||
prefix ?= /usr/local
|
||||
|
||||
CC = gcc
|
||||
LEX = flex
|
||||
YACC = bison
|
||||
MAKE = make
|
||||
|
@ -200,7 +200,7 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int codegen(const char *template, ...)
|
||||
static void codegen(const char *template, ...)
|
||||
{
|
||||
const char *src, *end;
|
||||
int skip_tabs = 0, n;
|
||||
@ -211,7 +211,7 @@ static int codegen(const char *template, ...)
|
||||
n = strlen(template);
|
||||
s = malloc(n + 1);
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
exit(-1);
|
||||
src = template;
|
||||
dst = s;
|
||||
|
||||
@ -224,7 +224,8 @@ static int codegen(const char *template, ...)
|
||||
} else {
|
||||
p_err("unrecognized character at pos %td in template '%s'",
|
||||
src - template - 1, template);
|
||||
return -EINVAL;
|
||||
free(s);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -234,7 +235,8 @@ static int codegen(const char *template, ...)
|
||||
if (*src != '\t') {
|
||||
p_err("not enough tabs at pos %td in template '%s'",
|
||||
src - template - 1, template);
|
||||
return -EINVAL;
|
||||
free(s);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
/* trim trailing whitespace */
|
||||
@ -255,7 +257,6 @@ static int codegen(const char *template, ...)
|
||||
va_end(args);
|
||||
|
||||
free(s);
|
||||
return n;
|
||||
}
|
||||
|
||||
static int do_skeleton(int argc, char **argv)
|
||||
|
@ -3761,6 +3761,19 @@ struct xdp_md {
|
||||
__u32 egress_ifindex; /* txq->dev->ifindex */
|
||||
};
|
||||
|
||||
/* DEVMAP map-value layout
|
||||
*
|
||||
* The struct data-layout of map-value is a configuration interface.
|
||||
* New members can only be added to the end of this structure.
|
||||
*/
|
||||
struct bpf_devmap_val {
|
||||
__u32 ifindex; /* device index */
|
||||
union {
|
||||
int fd; /* prog fd on map write */
|
||||
__u32 id; /* prog id on map read */
|
||||
} bpf_prog;
|
||||
};
|
||||
|
||||
enum sk_action {
|
||||
SK_DROP = 0,
|
||||
SK_PASS,
|
||||
|
@ -1137,6 +1137,20 @@ static void btf_dump_emit_mods(struct btf_dump *d, struct id_stack *decl_stack)
|
||||
}
|
||||
}
|
||||
|
||||
static void btf_dump_drop_mods(struct btf_dump *d, struct id_stack *decl_stack)
|
||||
{
|
||||
const struct btf_type *t;
|
||||
__u32 id;
|
||||
|
||||
while (decl_stack->cnt) {
|
||||
id = decl_stack->ids[decl_stack->cnt - 1];
|
||||
t = btf__type_by_id(d->btf, id);
|
||||
if (!btf_is_mod(t))
|
||||
return;
|
||||
decl_stack->cnt--;
|
||||
}
|
||||
}
|
||||
|
||||
static void btf_dump_emit_name(const struct btf_dump *d,
|
||||
const char *name, bool last_was_ptr)
|
||||
{
|
||||
@ -1235,14 +1249,7 @@ static void btf_dump_emit_type_chain(struct btf_dump *d,
|
||||
* a const/volatile modifier for array, so we are
|
||||
* going to silently skip them here.
|
||||
*/
|
||||
while (decls->cnt) {
|
||||
next_id = decls->ids[decls->cnt - 1];
|
||||
next_t = btf__type_by_id(d->btf, next_id);
|
||||
if (btf_is_mod(next_t))
|
||||
decls->cnt--;
|
||||
else
|
||||
break;
|
||||
}
|
||||
btf_dump_drop_mods(d, decls);
|
||||
|
||||
if (decls->cnt == 0) {
|
||||
btf_dump_emit_name(d, fname, last_was_ptr);
|
||||
@ -1270,7 +1277,15 @@ static void btf_dump_emit_type_chain(struct btf_dump *d,
|
||||
__u16 vlen = btf_vlen(t);
|
||||
int i;
|
||||
|
||||
btf_dump_emit_mods(d, decls);
|
||||
/*
|
||||
* GCC emits extra volatile qualifier for
|
||||
* __attribute__((noreturn)) function pointers. Clang
|
||||
* doesn't do it. It's a GCC quirk for backwards
|
||||
* compatibility with code written for GCC <2.5. So,
|
||||
* similarly to extra qualifiers for array, just drop
|
||||
* them, instead of handling them.
|
||||
*/
|
||||
btf_dump_drop_mods(d, decls);
|
||||
if (decls->cnt) {
|
||||
btf_dump_printf(d, " (");
|
||||
btf_dump_emit_type_chain(d, decls, fname, lvl);
|
||||
|
@ -10,10 +10,9 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#ifdef __GLIBC__
|
||||
#include <bits/wordsize.h>
|
||||
#else
|
||||
#include <bits/reg.h>
|
||||
#include <limits.h>
|
||||
#ifndef __WORDSIZE
|
||||
#define __WORDSIZE (__SIZEOF_LONG__ * 8)
|
||||
#endif
|
||||
|
||||
static inline size_t hash_bits(size_t h, int bits)
|
||||
|
@ -3564,10 +3564,6 @@ bpf_object__populate_internal_map(struct bpf_object *obj, struct bpf_map *map)
|
||||
char *cp, errmsg[STRERR_BUFSIZE];
|
||||
int err, zero = 0;
|
||||
|
||||
/* kernel already zero-initializes .bss map. */
|
||||
if (map_type == LIBBPF_MAP_BSS)
|
||||
return 0;
|
||||
|
||||
err = bpf_map_update_elem(map->fd, &zero, map->mmaped, 0);
|
||||
if (err) {
|
||||
err = -errno;
|
||||
|
@ -230,6 +230,13 @@ void test_cgroup_attach_multi(void)
|
||||
"prog_replace", "errno=%d\n", errno))
|
||||
goto err;
|
||||
|
||||
/* replace program with itself */
|
||||
attach_opts.replace_prog_fd = allow_prog[6];
|
||||
if (CHECK(bpf_prog_attach_xattr(allow_prog[6], cg1,
|
||||
BPF_CGROUP_INET_EGRESS, &attach_opts),
|
||||
"prog_replace", "errno=%d\n", errno))
|
||||
goto err;
|
||||
|
||||
value = 0;
|
||||
CHECK_FAIL(bpf_map_update_elem(map_fd, &key, &value, 0));
|
||||
CHECK_FAIL(system(PING_CMD));
|
||||
|
71
tools/testing/selftests/bpf/prog_tests/load_bytes_relative.c
Normal file
71
tools/testing/selftests/bpf/prog_tests/load_bytes_relative.c
Normal file
@ -0,0 +1,71 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
/*
|
||||
* Copyright 2020 Google LLC.
|
||||
*/
|
||||
|
||||
#include <test_progs.h>
|
||||
#include <network_helpers.h>
|
||||
|
||||
void test_load_bytes_relative(void)
|
||||
{
|
||||
int server_fd, cgroup_fd, prog_fd, map_fd, client_fd;
|
||||
int err;
|
||||
struct bpf_object *obj;
|
||||
struct bpf_program *prog;
|
||||
struct bpf_map *test_result;
|
||||
__u32 duration = 0;
|
||||
|
||||
__u32 map_key = 0;
|
||||
__u32 map_value = 0;
|
||||
|
||||
cgroup_fd = test__join_cgroup("/load_bytes_relative");
|
||||
if (CHECK_FAIL(cgroup_fd < 0))
|
||||
return;
|
||||
|
||||
server_fd = start_server(AF_INET, SOCK_STREAM);
|
||||
if (CHECK_FAIL(server_fd < 0))
|
||||
goto close_cgroup_fd;
|
||||
|
||||
err = bpf_prog_load("./load_bytes_relative.o", BPF_PROG_TYPE_CGROUP_SKB,
|
||||
&obj, &prog_fd);
|
||||
if (CHECK_FAIL(err))
|
||||
goto close_server_fd;
|
||||
|
||||
test_result = bpf_object__find_map_by_name(obj, "test_result");
|
||||
if (CHECK_FAIL(!test_result))
|
||||
goto close_bpf_object;
|
||||
|
||||
map_fd = bpf_map__fd(test_result);
|
||||
if (map_fd < 0)
|
||||
goto close_bpf_object;
|
||||
|
||||
prog = bpf_object__find_program_by_name(obj, "load_bytes_relative");
|
||||
if (CHECK_FAIL(!prog))
|
||||
goto close_bpf_object;
|
||||
|
||||
err = bpf_prog_attach(prog_fd, cgroup_fd, BPF_CGROUP_INET_EGRESS,
|
||||
BPF_F_ALLOW_MULTI);
|
||||
if (CHECK_FAIL(err))
|
||||
goto close_bpf_object;
|
||||
|
||||
client_fd = connect_to_fd(AF_INET, SOCK_STREAM, server_fd);
|
||||
if (CHECK_FAIL(client_fd < 0))
|
||||
goto close_bpf_object;
|
||||
close(client_fd);
|
||||
|
||||
err = bpf_map_lookup_elem(map_fd, &map_key, &map_value);
|
||||
if (CHECK_FAIL(err))
|
||||
goto close_bpf_object;
|
||||
|
||||
CHECK(map_value != 1, "bpf", "bpf program returned failure");
|
||||
|
||||
close_bpf_object:
|
||||
bpf_object__close(obj);
|
||||
|
||||
close_server_fd:
|
||||
close(server_fd);
|
||||
|
||||
close_cgroup_fd:
|
||||
close(cgroup_fd);
|
||||
}
|
@ -25,13 +25,23 @@ struct sample {
|
||||
char comm[16];
|
||||
};
|
||||
|
||||
static volatile int sample_cnt;
|
||||
static int sample_cnt;
|
||||
|
||||
static void atomic_inc(int *cnt)
|
||||
{
|
||||
__atomic_add_fetch(cnt, 1, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static int atomic_xchg(int *cnt, int val)
|
||||
{
|
||||
return __atomic_exchange_n(cnt, val, __ATOMIC_SEQ_CST);
|
||||
}
|
||||
|
||||
static int process_sample(void *ctx, void *data, size_t len)
|
||||
{
|
||||
struct sample *s = data;
|
||||
|
||||
sample_cnt++;
|
||||
atomic_inc(&sample_cnt);
|
||||
|
||||
switch (s->seq) {
|
||||
case 0:
|
||||
@ -76,7 +86,7 @@ void test_ringbuf(void)
|
||||
const size_t rec_sz = BPF_RINGBUF_HDR_SZ + sizeof(struct sample);
|
||||
pthread_t thread;
|
||||
long bg_ret = -1;
|
||||
int err;
|
||||
int err, cnt;
|
||||
|
||||
skel = test_ringbuf__open_and_load();
|
||||
if (CHECK(!skel, "skel_open_load", "skeleton open&load failed\n"))
|
||||
@ -116,11 +126,15 @@ void test_ringbuf(void)
|
||||
/* -EDONE is used as an indicator that we are done */
|
||||
if (CHECK(err != -EDONE, "err_done", "done err: %d\n", err))
|
||||
goto cleanup;
|
||||
cnt = atomic_xchg(&sample_cnt, 0);
|
||||
CHECK(cnt != 2, "cnt", "exp %d samples, got %d\n", 2, cnt);
|
||||
|
||||
/* we expect extra polling to return nothing */
|
||||
err = ring_buffer__poll(ringbuf, 0);
|
||||
if (CHECK(err != 0, "extra_samples", "poll result: %d\n", err))
|
||||
goto cleanup;
|
||||
cnt = atomic_xchg(&sample_cnt, 0);
|
||||
CHECK(cnt != 0, "cnt", "exp %d samples, got %d\n", 0, cnt);
|
||||
|
||||
CHECK(skel->bss->dropped != 0, "err_dropped", "exp %ld, got %ld\n",
|
||||
0L, skel->bss->dropped);
|
||||
@ -136,6 +150,8 @@ void test_ringbuf(void)
|
||||
3L * rec_sz, skel->bss->cons_pos);
|
||||
err = ring_buffer__poll(ringbuf, -1);
|
||||
CHECK(err <= 0, "poll_err", "err %d\n", err);
|
||||
cnt = atomic_xchg(&sample_cnt, 0);
|
||||
CHECK(cnt != 2, "cnt", "exp %d samples, got %d\n", 2, cnt);
|
||||
|
||||
/* start poll in background w/ long timeout */
|
||||
err = pthread_create(&thread, NULL, poll_thread, (void *)(long)10000);
|
||||
@ -164,6 +180,8 @@ void test_ringbuf(void)
|
||||
2L, skel->bss->total);
|
||||
CHECK(skel->bss->discarded != 1, "err_discarded", "exp %ld, got %ld\n",
|
||||
1L, skel->bss->discarded);
|
||||
cnt = atomic_xchg(&sample_cnt, 0);
|
||||
CHECK(cnt != 0, "cnt", "exp %d samples, got %d\n", 0, cnt);
|
||||
|
||||
/* clear flags to return to "adaptive" notification mode */
|
||||
skel->bss->flags = 0;
|
||||
@ -178,10 +196,20 @@ void test_ringbuf(void)
|
||||
if (CHECK(err != EBUSY, "try_join", "err %d\n", err))
|
||||
goto cleanup;
|
||||
|
||||
/* still no samples, because consumer is behind */
|
||||
cnt = atomic_xchg(&sample_cnt, 0);
|
||||
CHECK(cnt != 0, "cnt", "exp %d samples, got %d\n", 0, cnt);
|
||||
|
||||
skel->bss->dropped = 0;
|
||||
skel->bss->total = 0;
|
||||
skel->bss->discarded = 0;
|
||||
|
||||
skel->bss->value = 333;
|
||||
syscall(__NR_getpgid);
|
||||
/* now force notifications */
|
||||
skel->bss->flags = BPF_RB_FORCE_WAKEUP;
|
||||
sample_cnt = 0;
|
||||
trigger_samples();
|
||||
skel->bss->value = 777;
|
||||
syscall(__NR_getpgid);
|
||||
|
||||
/* now we should get a pending notification */
|
||||
usleep(50000);
|
||||
@ -193,8 +221,8 @@ void test_ringbuf(void)
|
||||
goto cleanup;
|
||||
|
||||
/* 3 rounds, 2 samples each */
|
||||
CHECK(sample_cnt != 6, "wrong_sample_cnt",
|
||||
"expected to see %d samples, got %d\n", 6, sample_cnt);
|
||||
cnt = atomic_xchg(&sample_cnt, 0);
|
||||
CHECK(cnt != 6, "cnt", "exp %d samples, got %d\n", 6, cnt);
|
||||
|
||||
/* BPF side did everything right */
|
||||
CHECK(skel->bss->dropped != 0, "err_dropped", "exp %ld, got %ld\n",
|
||||
|
@ -15,6 +15,8 @@ void test_skeleton(void)
|
||||
int duration = 0, err;
|
||||
struct test_skeleton* skel;
|
||||
struct test_skeleton__bss *bss;
|
||||
struct test_skeleton__data *data;
|
||||
struct test_skeleton__rodata *rodata;
|
||||
struct test_skeleton__kconfig *kcfg;
|
||||
|
||||
skel = test_skeleton__open();
|
||||
@ -24,13 +26,45 @@ void test_skeleton(void)
|
||||
if (CHECK(skel->kconfig, "skel_kconfig", "kconfig is mmaped()!\n"))
|
||||
goto cleanup;
|
||||
|
||||
bss = skel->bss;
|
||||
data = skel->data;
|
||||
rodata = skel->rodata;
|
||||
|
||||
/* validate values are pre-initialized correctly */
|
||||
CHECK(data->in1 != -1, "in1", "got %d != exp %d\n", data->in1, -1);
|
||||
CHECK(data->out1 != -1, "out1", "got %d != exp %d\n", data->out1, -1);
|
||||
CHECK(data->in2 != -1, "in2", "got %lld != exp %lld\n", data->in2, -1LL);
|
||||
CHECK(data->out2 != -1, "out2", "got %lld != exp %lld\n", data->out2, -1LL);
|
||||
|
||||
CHECK(bss->in3 != 0, "in3", "got %d != exp %d\n", bss->in3, 0);
|
||||
CHECK(bss->out3 != 0, "out3", "got %d != exp %d\n", bss->out3, 0);
|
||||
CHECK(bss->in4 != 0, "in4", "got %lld != exp %lld\n", bss->in4, 0LL);
|
||||
CHECK(bss->out4 != 0, "out4", "got %lld != exp %lld\n", bss->out4, 0LL);
|
||||
|
||||
CHECK(rodata->in6 != 0, "in6", "got %d != exp %d\n", rodata->in6, 0);
|
||||
CHECK(bss->out6 != 0, "out6", "got %d != exp %d\n", bss->out6, 0);
|
||||
|
||||
/* validate we can pre-setup global variables, even in .bss */
|
||||
data->in1 = 10;
|
||||
data->in2 = 11;
|
||||
bss->in3 = 12;
|
||||
bss->in4 = 13;
|
||||
rodata->in6 = 14;
|
||||
|
||||
err = test_skeleton__load(skel);
|
||||
if (CHECK(err, "skel_load", "failed to load skeleton: %d\n", err))
|
||||
goto cleanup;
|
||||
|
||||
bss = skel->bss;
|
||||
bss->in1 = 1;
|
||||
bss->in2 = 2;
|
||||
/* validate pre-setup values are still there */
|
||||
CHECK(data->in1 != 10, "in1", "got %d != exp %d\n", data->in1, 10);
|
||||
CHECK(data->in2 != 11, "in2", "got %lld != exp %lld\n", data->in2, 11LL);
|
||||
CHECK(bss->in3 != 12, "in3", "got %d != exp %d\n", bss->in3, 12);
|
||||
CHECK(bss->in4 != 13, "in4", "got %lld != exp %lld\n", bss->in4, 13LL);
|
||||
CHECK(rodata->in6 != 14, "in6", "got %d != exp %d\n", rodata->in6, 14);
|
||||
|
||||
/* now set new values and attach to get them into outX variables */
|
||||
data->in1 = 1;
|
||||
data->in2 = 2;
|
||||
bss->in3 = 3;
|
||||
bss->in4 = 4;
|
||||
bss->in5.a = 5;
|
||||
@ -44,14 +78,15 @@ void test_skeleton(void)
|
||||
/* trigger tracepoint */
|
||||
usleep(1);
|
||||
|
||||
CHECK(bss->out1 != 1, "res1", "got %d != exp %d\n", bss->out1, 1);
|
||||
CHECK(bss->out2 != 2, "res2", "got %lld != exp %d\n", bss->out2, 2);
|
||||
CHECK(data->out1 != 1, "res1", "got %d != exp %d\n", data->out1, 1);
|
||||
CHECK(data->out2 != 2, "res2", "got %lld != exp %d\n", data->out2, 2);
|
||||
CHECK(bss->out3 != 3, "res3", "got %d != exp %d\n", (int)bss->out3, 3);
|
||||
CHECK(bss->out4 != 4, "res4", "got %lld != exp %d\n", bss->out4, 4);
|
||||
CHECK(bss->handler_out5.a != 5, "res5", "got %d != exp %d\n",
|
||||
bss->handler_out5.a, 5);
|
||||
CHECK(bss->handler_out5.b != 6, "res6", "got %lld != exp %d\n",
|
||||
bss->handler_out5.b, 6);
|
||||
CHECK(bss->out6 != 14, "res7", "got %d != exp %d\n", bss->out6, 14);
|
||||
|
||||
CHECK(bss->bpf_syscall != kcfg->CONFIG_BPF_SYSCALL, "ext1",
|
||||
"got %d != exp %d\n", bss->bpf_syscall, kcfg->CONFIG_BPF_SYSCALL);
|
||||
|
@ -8,14 +8,6 @@
|
||||
|
||||
#define IFINDEX_LO 1
|
||||
|
||||
struct bpf_devmap_val {
|
||||
u32 ifindex; /* device index */
|
||||
union {
|
||||
int fd; /* prog fd on map write */
|
||||
u32 id; /* prog id on map read */
|
||||
} bpf_prog;
|
||||
};
|
||||
|
||||
void test_xdp_with_devmap_helpers(void)
|
||||
{
|
||||
struct test_xdp_with_devmap_helpers *skel;
|
||||
|
48
tools/testing/selftests/bpf/progs/load_bytes_relative.c
Normal file
48
tools/testing/selftests/bpf/progs/load_bytes_relative.c
Normal file
@ -0,0 +1,48 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
/*
|
||||
* Copyright 2020 Google LLC.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/ip.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__uint(max_entries, 1);
|
||||
__type(key, __u32);
|
||||
__type(value, __u32);
|
||||
} test_result SEC(".maps");
|
||||
|
||||
SEC("cgroup_skb/egress")
|
||||
int load_bytes_relative(struct __sk_buff *skb)
|
||||
{
|
||||
struct ethhdr eth;
|
||||
struct iphdr iph;
|
||||
|
||||
__u32 map_key = 0;
|
||||
__u32 test_passed = 0;
|
||||
|
||||
/* MAC header is not set by the time cgroup_skb/egress triggers */
|
||||
if (bpf_skb_load_bytes_relative(skb, 0, ð, sizeof(eth),
|
||||
BPF_HDR_START_MAC) != -EFAULT)
|
||||
goto fail;
|
||||
|
||||
if (bpf_skb_load_bytes_relative(skb, 0, &iph, sizeof(iph),
|
||||
BPF_HDR_START_NET))
|
||||
goto fail;
|
||||
|
||||
if (bpf_skb_load_bytes_relative(skb, 0xffff, &iph, sizeof(iph),
|
||||
BPF_HDR_START_NET) != -EFAULT)
|
||||
goto fail;
|
||||
|
||||
test_passed = 1;
|
||||
|
||||
fail:
|
||||
bpf_map_update_elem(&test_result, &map_key, &test_passed, BPF_ANY);
|
||||
|
||||
return 1;
|
||||
}
|
@ -10,16 +10,26 @@ struct s {
|
||||
long long b;
|
||||
} __attribute__((packed));
|
||||
|
||||
int in1 = 0;
|
||||
long long in2 = 0;
|
||||
/* .data section */
|
||||
int in1 = -1;
|
||||
long long in2 = -1;
|
||||
|
||||
/* .bss section */
|
||||
char in3 = '\0';
|
||||
long long in4 __attribute__((aligned(64))) = 0;
|
||||
struct s in5 = {};
|
||||
|
||||
long long out2 = 0;
|
||||
/* .rodata section */
|
||||
const volatile int in6 = 0;
|
||||
|
||||
/* .data section */
|
||||
int out1 = -1;
|
||||
long long out2 = -1;
|
||||
|
||||
/* .bss section */
|
||||
char out3 = 0;
|
||||
long long out4 = 0;
|
||||
int out1 = 0;
|
||||
int out6 = 0;
|
||||
|
||||
extern bool CONFIG_BPF_SYSCALL __kconfig;
|
||||
extern int LINUX_KERNEL_VERSION __kconfig;
|
||||
@ -36,6 +46,7 @@ int handler(const void *ctx)
|
||||
out3 = in3;
|
||||
out4 = in4;
|
||||
out5 = in5;
|
||||
out6 = in6;
|
||||
|
||||
bpf_syscall = CONFIG_BPF_SYSCALL;
|
||||
kern_ver = LINUX_KERNEL_VERSION;
|
||||
|
@ -2,7 +2,7 @@
|
||||
/* fails to load without expected_attach_type = BPF_XDP_DEVMAP
|
||||
* because of access to egress_ifindex
|
||||
*/
|
||||
#include "vmlinux.h"
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
SEC("xdp_dm_log")
|
||||
|
@ -1,6 +1,5 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include "vmlinux.h"
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
|
||||
struct {
|
||||
|
Loading…
Reference in New Issue
Block a user