From: "Alexei Starovoitov" <alexei.starovoitov@gmail.com>
To: "Daniel Borkmann" <daniel@iogearbox.net>, <memxor@gmail.com>
Cc: <bpf@vger.kernel.org>
Subject: Re: [PATCH bpf-next 2/2] selftests/bpf: Add JIT cBPF-related instruction tests
Date: Thu, 15 Oct 2026 19:08:24 +0000 [thread overview]
Message-ID: <DM5NCJVIY5GR.3KH59CI540I82@gmail.com> (raw)
In-Reply-To: <20261015171917.599025-2-daniel@iogearbox.net>
On Thu Oct 15, 2026 at 5:19 PM UTC, Daniel Borkmann wrote:
> The classic BPF cases of the removed test_bpf module ran their programs
> on a hand-built skb. Run them as socket filters instead via jit_insns_cbpf
> test suite. This covers all previous test_bpf cases as well as a few corner
> cases beyond that.
>
> # LDLIBS=-static PKG_CONFIG='pkg-config --static' ./vmtest.sh -- ./test_progs -t jit_insns_cbpf
> [...]
> #193/1 jit_insns_cbpf/classic:OK
> #193/2 jit_insns_cbpf/checker:OK
> #193/3 jit_insns_cbpf/frags:OK
> #193/4 jit_insns_cbpf/packets:OK
> #193/5 jit_insns_cbpf/blinded:OK
> #193 jit_insns_cbpf:OK
> Summary: 1/5 PASSED, 0 SKIPPED, 0/0 FAILED
>
> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
> ---
> [ Small fyi, the gso related tests I pushed here for net-next
> https://lore.kernel.org/netdev/20261015151033.591439-1-daniel@iogearbox.net/
> thus this concludes all of old test_bpf. ]
>
> .../selftests/bpf/prog_tests/jit_insns_cbpf.c | 2620 +++++++++++++++++
> 1 file changed, 2620 insertions(+)
> create mode 100644 tools/testing/selftests/bpf/prog_tests/jit_insns_cbpf.c
>
> diff --git a/tools/testing/selftests/bpf/prog_tests/jit_insns_cbpf.c b/tools/testing/selftests/bpf/prog_tests/jit_insns_cbpf.c
> new file mode 100644
> index 000000000000..5471c06605fe
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/jit_insns_cbpf.c
> @@ -0,0 +1,2620 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2026 Isovalent */
> +/*
> + * The classic BPF cases of the former test_bpf module, run as socket
> + * filters: the data cases on an AF_UNIX datagram pair, which takes any
> + * length down to zero and puts the tail of a long datagram into page
> + * frags, the cases reading the link layer or the metadata of a packet on
> + * a veth pair in their own netns. The return of a filter decides over the
> + * packet, zero drops it and any other value truncates it to that length,
> + * so the returns of a program get rewritten: the expected result stays,
> + * any other constant becomes zero and a returned accumulator is compared
> + * at the end, so that a wrong result shows as a dropped packet.
> + */
> +#include <test_progs.h>
> +#include <poll.h>
> +#include <sys/ioctl.h>
> +#include <linux/filter.h>
> +
> +#include "network_helpers.h"
> +#include "sysctl_helpers.h"
> +
> +#define MAX_K 0xffffffff
> +#define SKB_TYPE PACKET_OTHERHOST /* the frames go to a foreign MAC */
> +#define SKB_MARK 0x1234aaaa /* the mark of the sending socket */
> +#define SKB_VLAN_TCI 0xffff
> +#define SKB_VLAN_PRESENT 1
> +#define SKB_DEV_IFINDEX 0 /* that of the receiving veth */
> +#define SKB_DEV_TYPE 1 /* ARPHRD_ETHER, that of a veth */
> +/*
> + * A datagram of four pages splits into three pages of head and one page of
> + * frag data: what exceeds SKB_MAX_ALLOC, four pages less the shared info,
> + * goes into frags, rounded up to a page. The table cases take 4K pages, a
> + * larger page keeps their datagram linear; the sweep goes by the page size.
> + */
> +#define FRAG_OFF (3 * 4096) /* the frag data starts here */
> +#define SWEEP_SPAN 16 /* offsets swept either side of it */
> +#define NETNS "jit_insns_cbpf"
> +
> +static int page_size;
> +static int dgram_max; /* four pages */
> +
> +enum {
> + CB_FAIL = 1, /* the attach has to fail */
> + CB_PKT = 2, /* needs the link layer or the metadata of a packet */
> + CB_VLAN = 4, /* a tagged frame, the stack pulls the tag out */
> + CB_FRAG = 8, /* the length is the head, the frag data goes at its end */
> + CB_IFINDEX = 16, /* the result is the ifindex of the receiving veth */
> + CB_DSTMAC = 32, /* the frame goes to the MAC of the receiving veth */
> + CB_NOBLIND = 64, /* blinding grows the converted jump past 16 bits */
> + CB_RAW = 128, /* the returns stay, the kept length shows the result */
> +};
> +
> +struct cbpf_run {
> + int len;
> + __u32 result;
> +};
> +
> +struct cbpf_case {
> + const char *descr;
> + const struct sock_filter *insns;
> + int cnt;
> + int (*fill)(struct sock_filter *insns);
> + const __u8 *data;
> + int data_len;
> + const __u8 *frag;
> + int frag_len;
> + const struct cbpf_run *runs;
> + int nr_runs;
> + unsigned int flags;
> +};
> +
> +#define INSNS(...) \
> + (const struct sock_filter[]){ __VA_ARGS__ }, \
> + .cnt = ARRAY_SIZE(((const struct sock_filter[]){ __VA_ARGS__ }))
> +#define DATA(...) \
> + (const __u8[]){ __VA_ARGS__ }, \
> + .data_len = sizeof((const __u8[]){ __VA_ARGS__ })
> +#define FRAG(...) \
> + (const __u8[]){ __VA_ARGS__ }, \
> + .frag_len = sizeof((const __u8[]){ __VA_ARGS__ })
> +#define RUNS(...) \
> + (const struct cbpf_run[]){ __VA_ARGS__ }, \
> + .nr_runs = ARRAY_SIZE(((const struct cbpf_run[]){ __VA_ARGS__ }))
> +
> +/* The uapi forms are initializers, these are values */
> +#define STMT(code, k) ((struct sock_filter)BPF_STMT(code, k))
> +#define JUMP(code, k, jt, jf) ((struct sock_filter)BPF_JUMP(code, k, jt, jf))
> +
> +/* Jumps with gaps in between, the former __bpf_fill_ja() */
> +static int fill_ja(struct sock_filter *insns, int len, int plen)
> +{
> + int rlen = (len % plen) - 1, i, j;
> +
> + for (i = 0; i + plen < len; i += plen)
> + for (j = 0; j < plen; j++)
> + insns[i + j] = JUMP(BPF_JMP | BPF_JA, plen - 1 - j, 0, 0);
> + for (j = 0; j < rlen; j++)
> + insns[i + j] = JUMP(BPF_JMP | BPF_JA, rlen - 1 - j, 0, 0);
> + insns[len - 1] = STMT(BPF_RET | BPF_K, 0xababcbac);
> + return len;
> +}
> +
> +static int fill_ja_short(struct sock_filter *insns)
> +{
> + return fill_ja(insns, 12, 9);
> +}
> +
> +static int fill_ja_long(struct sock_filter *insns)
> +{
> + return fill_ja(insns, BPF_MAXINSNS, 68);
> +}
> +
> +static int fill_too_many(struct sock_filter *insns)
> +{
> + int i;
> +
> + for (i = 0; i <= BPF_MAXINSNS; i++)
> + insns[i] = STMT(BPF_RET | BPF_K, 0xfefefefe);
> + return BPF_MAXINSNS + 1;
> +}
> +
> +/*
> + * A jump over MSH loads, each of which becomes 18 instructions: the
> + * converted offset has to fit 16 bits, which 1820 of them do.
> + */
> +static int fill_jump_over_msh(struct sock_filter *insns, int nr)
> +{
> + int i;
> +
> + insns[0] = JUMP(BPF_JMP | BPF_JA, nr, 0, 0);
> + for (i = 1; i <= nr; i++)
> + insns[i] = STMT(BPF_LDX | BPF_B | BPF_MSH, 0);
> + insns[nr + 1] = STMT(BPF_RET | BPF_K, 0xabababab);
> + return nr + 2;
> +}
> +
> +static int fill_jump_over_all_msh(struct sock_filter *insns)
> +{
> + return fill_jump_over_msh(insns, BPF_MAXINSNS - 2);
> +}
> +
> +static int fill_jump_over_1821_msh(struct sock_filter *insns)
> +{
> + return fill_jump_over_msh(insns, 1821);
> +}
> +
> +static int fill_jump_over_1820_msh(struct sock_filter *insns)
> +{
> + return fill_jump_over_msh(insns, 1820);
> +}
> +
> +static int fill_empty(struct sock_filter *insns)
> +{
> + return 0;
> +}
> +
> +/* A conditional jump past 32K converted instructions */
> +static int fill_late_jump(struct sock_filter *insns)
> +{
> + int i;
> +
> + for (i = 0; i < 2000; i++)
> + insns[i] = STMT(BPF_LDX | BPF_B | BPF_MSH, 0);
> + insns[2000] = STMT(BPF_LD | BPF_IMM, 1);
> + insns[2001] = JUMP(BPF_JMP | BPF_JEQ | BPF_K, 1, 0, 1);
> + insns[2002] = STMT(BPF_RET | BPF_K, 1);
> + insns[2003] = STMT(BPF_RET | BPF_K, 2);
> + return 2004;
> +}
> +
> +/* A conditional jump over 254 loads, which expand to 12 instructions each */
> +static int fill_jump_over_ld_abs(struct sock_filter *insns, bool taken)
> +{
> + int i;
> +
> + insns[0] = STMT(BPF_LD | BPF_B | BPF_ABS, 0);
> + insns[1] = JUMP(BPF_JMP | BPF_JEQ | BPF_K, 1, taken ? 255 : 0, taken ? 0 : 255);
> + for (i = 2; i < 256; i++)
> + insns[i] = STMT(BPF_LD | BPF_B | BPF_ABS, 0);
> + insns[256] = STMT(BPF_RET | BPF_K, 2);
> + insns[257] = STMT(BPF_RET | BPF_K, 3);
> + return 258;
> +}
> +
> +static int fill_jt_over_ld_abs(struct sock_filter *insns)
> +{
> + return fill_jump_over_ld_abs(insns, true);
> +}
> +
> +static int fill_jf_over_ld_abs(struct sock_filter *insns)
> +{
> + return fill_jump_over_ld_abs(insns, false);
> +}
> +
> +/* The result is small enough for the kept length to show it as is */
> +static int fill_all_msh(struct sock_filter *insns)
> +{
> + int len = BPF_MAXINSNS, i;
> +
> + for (i = 0; i < len - 3; i++)
> + insns[i] = STMT(BPF_LDX | BPF_B | BPF_MSH, 0);
> + insns[len - 3] = STMT(BPF_LD | BPF_IMM, 0x2a);
> + insns[len - 2] = STMT(BPF_ALU | BPF_XOR | BPF_X, 0);
> + insns[len - 1] = STMT(BPF_RET | BPF_A, 0);
> + return len;
> +}
> +
> +static int fill_ld_abs_cpu(struct sock_filter *insns)
> +{
> + int len = BPF_MAXINSNS, i;
> +
> + for (i = 0; i < len - 1; i += 2) {
> + insns[i] = STMT(BPF_LD | BPF_B | BPF_ABS, 0);
> + insns[i + 1] = STMT(BPF_LD | BPF_W | BPF_ABS, SKF_AD_OFF + SKF_AD_CPU);
> + }
> + insns[len - 1] = STMT(BPF_RET | BPF_K, 0xbee);
> + return len;
> +}
> +
> +static const struct cbpf_case cases[] = {
> + {
> + .descr = "TAX",
> + .insns = INSNS(
> + BPF_STMT(BPF_LD | BPF_IMM, 1),
> + BPF_STMT(BPF_MISC | BPF_TAX, 0),
> + BPF_STMT(BPF_LD | BPF_IMM, 2),
> + BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
> + BPF_STMT(BPF_ALU | BPF_NEG, 0), /* A == -3 */
> + BPF_STMT(BPF_MISC | BPF_TAX, 0),
> + BPF_STMT(BPF_LD | BPF_LEN, 0),
> + BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
> + BPF_STMT(BPF_MISC | BPF_TAX, 0), /* X == len - 3 */
> + BPF_STMT(BPF_LD | BPF_B | BPF_IND, 1),
> + BPF_STMT(BPF_RET | BPF_A, 0)
> + ),
> + .data = DATA(10, 20, 30, 40, 50),
> + .runs = RUNS({ 2, 10 }, { 3, 20 }, { 4, 30 }),
> + },
> + {
> + .descr = "TXA",
> + .insns = INSNS(
> + BPF_STMT(BPF_LDX | BPF_LEN, 0),
> + BPF_STMT(BPF_MISC | BPF_TXA, 0),
> + BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
> + BPF_STMT(BPF_RET | BPF_A, 0) /* A == len * 2 */
> + ),
> + .data = DATA(10, 20, 30, 40, 50),
> + .runs = RUNS({ 1, 2 }, { 3, 6 }, { 4, 8 }),
> + },
I don't like it. It just copies old test_lib.ko into test_progs.
I'd prefer to keep cbpf untested. It's not like anyone is working on it.
pw-bot: cr
next prev parent reply other threads:[~2026-10-15 19:08 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-15 17:19 [PATCH bpf-next 1/2] selftests/bpf: Follow-up coverage to JIT instruction tests Daniel Borkmann
2026-10-15 17:19 ` [PATCH bpf-next 2/2] selftests/bpf: Add JIT cBPF-related " Daniel Borkmann
2026-10-15 17:31 ` sashiko-bot
2026-10-15 18:08 ` bot+bpf-ci
2026-10-15 19:08 ` Alexei Starovoitov [this message]
2026-10-15 19:11 ` Daniel Borkmann
2026-10-15 18:08 ` [PATCH bpf-next 1/2] selftests/bpf: Follow-up coverage to JIT " bot+bpf-ci
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=DM5NCJVIY5GR.3KH59CI540I82@gmail.com \
--to=alexei.starovoitov@gmail.com \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=memxor@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.