Subject: Re: [PATCH v3 net-next 1/1] bpf32->bpf64 mapper and bpf64 interpreter

From: Daniel Borkmann <[email-masked]>
Date: Fri, 28 Feb 2014 13:45:20 +0100

Hi Alexei,

[also cc'ing Hagen and Jesse]

Just some minor comments below ... let me know what you think.

On 02/27/2014 03:38 AM, Alexei Starovoitov wrote:
> Extended BPF (or 64-bit BPF) is an instruction set to
> create safe dynamically loadable filters that can call fixed set
> of kernel functions and take generic bpf_context as an input.
> BPF filter is a glue between kernel functions and bpf_context.
> Different kernel subsystems can define their own set of available functions
> and alter BPF machinery for specific use case.
> BPF64 instruction set is designed for efficient mapping to native
> instructions on 64-bit CPUs
>
> Old BPF instructions are remapped on the fly to BPF64
> when sysctl net.core.bpf64_enable=1

Would be nice if the commit message could be extended with what you
have posted in your [PATCH v3 net-next 0/1] email (but without the
changelog, changelog should go after "---" line), so that also this
information will appear here and in the Git log later on. Also please
elaborate more on this commit message. I think, at least, as it's a
bigger change it also needs to include future planned usage scenarios
for user space and for inside the kernel e.g. for OVS and ftrace.

You could make this patch a 2 patch patch-series and put into patch
2/2 all documentation you had in your previous version of the set.
Would be nice to extend the file Documentation/networking/filter.txt
with a description of your extended BPF so that users can read about
them in the same file.

Did you also test that seccomp-BPF still works out?

> Signed-off-by: Alexei Starovoitov <[email-masked]>
> ---
>   include/linux/filter.h      |    9 +-
>   include/linux/netdevice.h   |    1 +
>   include/uapi/linux/filter.h |   37 ++-
>   net/core/Makefile           |    2 +-
>   net/core/bpf_run.c          |  766 +++++++++++++++++++++++++++++++++++++++++++
>   net/core/filter.c           |  114 ++++++-

I would have liked to see the content from net/core/bpf_run.c to go
directly into net/core/filter.c, not as a separate file, if that's
possible.

>   net/core/sysctl_net_core.c  |    7 +
>   7 files changed, 913 insertions(+), 23 deletions(-)
>   create mode 100644 net/core/bpf_run.c
>
> diff --git a/include/linux/filter.h b/include/linux/filter.h
> index e568c8ef896b..bf3085258f4c 100644
> --- a/include/linux/filter.h
> +++ b/include/linux/filter.h
> @@ -53,6 +53,13 @@ extern int sk_chk_filter(struct sock_filter *filter, unsigned int flen);
>   extern int sk_get_filter(struct sock *sk, struct sock_filter __user *filter, unsigned len);
>   extern void sk_decode_filter(struct sock_filter *filt, struct sock_filter *to);
>
> +/* function remaps 'sock_filter' style insns to 'bpf_insn' style insns */
> +int bpf_convert(struct sock_filter *fp, int len, struct bpf_insn *new_prog,
> +		int *p_new_len);
> +/* execute bpf64 program */
> +u32 bpf_run(struct bpf_context *ctx, const struct bpf_insn *insn);
> +
> +#define SK_RUN_FILTER(FILTER, SKB) (*FILTER->bpf_func)(SKB, FILTER->insns)
>   #ifdef CONFIG_BPF_JIT
>   #include <stdarg.h>
>   #include <linux/linkage.h>
> @@ -70,7 +77,6 @@ static inline void bpf_jit_dump(unsigned int flen, unsigned int proglen,
>   		print_hex_dump(KERN_ERR, "JIT code: ", DUMP_PREFIX_OFFSET,
>   			       16, 1, image, proglen, false);
>   }
> -#define SK_RUN_FILTER(FILTER, SKB) (*FILTER->bpf_func)(SKB, FILTER->insns)
>   #else
>   #include <linux/slab.h>
>   static inline void bpf_jit_compile(struct sk_filter *fp)
> @@ -80,7 +86,6 @@ static inline void bpf_jit_free(struct sk_filter *fp)
>   {
>   	kfree(fp);
>   }
> -#define SK_RUN_FILTER(FILTER, SKB) sk_run_filter(SKB, FILTER->insns)
>   #endif
>
>   static inline int bpf_tell_extensions(void)
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index 5e84483c0650..7b1acefc244e 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -2971,6 +2971,7 @@ extern int		netdev_max_backlog;
>   extern int		netdev_tstamp_prequeue;
>   extern int		weight_p;
>   extern int		bpf_jit_enable;
> +extern int		bpf64_enable;

We should keep naming consistent (so either extended BPF or BPF64),
so maybe bpf_ext_enable ? I'd presume rather {bpf,sk_filter}*_ext
as in 'struct bpf_insn' the immediate value is 32 bit, so for 64 bit
comparisons, you'd still need to load to immediate values, right?

After all your changes, we will still have the bpf_jit_enable knob
in tact, right?

>   bool netdev_has_upper_dev(struct net_device *dev, struct net_device *upper_dev);
>   struct net_device *netdev_all_upper_get_next_dev_rcu(struct net_device *dev,
> diff --git a/include/uapi/linux/filter.h b/include/uapi/linux/filter.h
> index 8eb9ccaa5b48..70ff29ee6825 100644
> --- a/include/uapi/linux/filter.h
> +++ b/include/uapi/linux/filter.h
> @@ -1,3 +1,4 @@
> +/* extended BPF is Copyright (c) 2011-2014, PLUMgrid, http://plumgrid.com */
>   /*
>    * Linux Socket Filter Data Structures
>    */

You can merge both comments into one.

> @@ -19,7 +20,7 @@
>    *	Try and keep these values and structures similar to BSD, especially
>    *	the BPF code definitions which need to match so you can share filters
>    */
> -
> +
>   struct sock_filter {	/* Filter block */
>   	__u16	code;   /* Actual filter code */
>   	__u8	jt;	/* Jump true */
> @@ -45,12 +46,26 @@ struct sock_fprog {	/* Required for SO_ATTACH_FILTER. */
>   #define         BPF_JMP         0x05
>   #define         BPF_RET         0x06
>   #define         BPF_MISC        0x07
> +#define         BPF_ALU64       0x07
> +
> +struct bpf_insn {
> +	__u8	code;    /* opcode */
> +	__u8    a_reg:4; /* dest register*/
> +	__u8    x_reg:4; /* source register */
> +	__s16	off;     /* signed offset */
> +	__s32	imm;     /* signed immediate constant */
> +};

As we have struct sock_filter and it's immutable due to uapi, I
would have liked to see the new data structure with a consistent
naming scheme, e.g. struct sock_filter_ext {...} for extended
BPF.

> +/* pointer to bpf_context is the first and only argument to BPF program
> + * its definition is use-case specific */
> +struct bpf_context;
>
>   /* ld/ldx fields */
>   #define BPF_SIZE(code)  ((code) & 0x18)
>   #define         BPF_W           0x00
>   #define         BPF_H           0x08
>   #define         BPF_B           0x10
> +#define         BPF_DW          0x18
>   #define BPF_MODE(code)  ((code) & 0xe0)
>   #define         BPF_IMM         0x00
>   #define         BPF_ABS         0x20
> @@ -58,6 +73,7 @@ struct sock_fprog {	/* Required for SO_ATTACH_FILTER. */
>   #define         BPF_MEM         0x60
>   #define         BPF_LEN         0x80
>   #define         BPF_MSH         0xa0
> +#define         BPF_XADD        0xc0 /* exclusive add */
>
>   /* alu/jmp fields */
>   #define BPF_OP(code)    ((code) & 0xf0)
> @@ -68,16 +84,24 @@ struct sock_fprog {	/* Required for SO_ATTACH_FILTER. */
>   #define         BPF_OR          0x40
>   #define         BPF_AND         0x50
>   #define         BPF_LSH         0x60
> -#define         BPF_RSH         0x70
> +#define         BPF_RSH         0x70 /* logical shift right */
>   #define         BPF_NEG         0x80
>   #define		BPF_MOD		0x90
>   #define		BPF_XOR		0xa0
> +#define		BPF_MOV		0xb0 /* mov reg to reg */
> +#define		BPF_ARSH	0xc0 /* sign extending arithmetic shift right */
> +#define		BPF_BSWAP32	0xd0 /* swap lower 4 bytes of 64-bit register */
> +#define		BPF_BSWAP64	0xe0 /* swap all 8 bytes of 64-bit register */
>
>   #define         BPF_JA          0x00
> -#define         BPF_JEQ         0x10
> -#define         BPF_JGT         0x20
> -#define         BPF_JGE         0x30
> -#define         BPF_JSET        0x40
> +#define         BPF_JEQ         0x10 /* jump == */
> +#define         BPF_JGT         0x20 /* GT is unsigned '>', JA in x86 */
> +#define         BPF_JGE         0x30 /* GE is unsigned '>=', JAE in x86 */
> +#define         BPF_JSET        0x40 /* if (A & X) */
> +#define         BPF_JNE         0x50 /* jump != */
> +#define         BPF_JSGT        0x60 /* SGT is signed '>', GT in x86 */
> +#define         BPF_JSGE        0x70 /* SGE is signed '>=', GE in x86 */
> +#define         BPF_CALL        0x80 /* function call */
>   #define BPF_SRC(code)   ((code) & 0x08)
>   #define         BPF_K           0x00
>   #define         BPF_X           0x08
> @@ -134,5 +158,4 @@ struct sock_fprog {	/* Required for SO_ATTACH_FILTER. */
>   #define SKF_NET_OFF   (-0x100000)
>   #define SKF_LL_OFF    (-0x200000)
>
> -
>   #endif /* _UAPI__LINUX_FILTER_H__ */
> diff --git a/net/core/Makefile b/net/core/Makefile
> index 9628c20acff6..e622b97f58dc 100644
> --- a/net/core/Makefile
> +++ b/net/core/Makefile
> @@ -8,7 +8,7 @@ obj-y := sock.o request_sock.o skbuff.o iovec.o datagram.o stream.o scm.o \
>   obj-$(CONFIG_SYSCTL) += sysctl_net_core.o
>
>   obj-y		     += dev.o ethtool.o dev_addr_lists.o dst.o netevent.o \
> -			neighbour.o rtnetlink.o utils.o link_watch.o filter.o \
> +			neighbour.o rtnetlink.o utils.o link_watch.o filter.o bpf_run.o \
>   			sock_diag.o dev_ioctl.o
>
>   obj-$(CONFIG_XFRM) += flow.o
> diff --git a/net/core/bpf_run.c b/net/core/bpf_run.c
> new file mode 100644
> index 000000000000..fa1862fcbc74
> --- /dev/null
> +++ b/net/core/bpf_run.c
> @@ -0,0 +1,766 @@
> +/* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of version 2 of the GNU General Public
> + * License as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * General Public License for more details.
> + */
> +#include <linux/kernel.h>
> +#include <linux/types.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>
> +#include <linux/filter.h>
> +#include <linux/skbuff.h>
> +#include <asm/unaligned.h>
> +
> +int bpf64_enable __read_mostly;
> +
> +void *bpf_internal_load_pointer_neg_helper(const struct sk_buff *skb, int k,
> +					   unsigned int size);
> +
> +static inline void *load_pointer(const struct sk_buff *skb, int k,
> +				 unsigned int size, void *buffer)
> +{
> +	if (k >= 0)
> +		return skb_header_pointer(skb, k, size, buffer);
> +	return bpf_internal_load_pointer_neg_helper(skb, k, size);
> +}
> +
> +static const char *const bpf_class_string[] = {
> +	"ld", "ldx", "st", "stx", "alu", "jmp", "ret", "misc"
> +};
> +
> +static const char *const bpf_alu_string[] = {
> +	"+=", "-=", "*=", "/=", "|=", "&=", "<<=", ">>=", "neg",
> +	"%=", "^=", "=", "s>>=", "bswap32", "bswap64", "???"
> +};
> +
> +static const char *const bpf_ldst_string[] = {
> +	"u32", "u16", "u8", "u64"
> +};
> +
> +static const char *const bpf_jmp_string[] = {
> +	"jmp", "==", ">", ">=", "&", "!=", "s>", "s>=", "call"
> +};
> +
> +static const char *reg_to_str(int regno, u64 *regs)
> +{
> +	static char reg_value[16][32];
> +	if (!regs)
> +		return "";
> +	snprintf(reg_value[regno], sizeof(reg_value[regno]), "(0x%llx)",
> +		 regs[regno]);
> +	return reg_value[regno];
> +}
> +
> +#define R(regno) reg_to_str(regno, regs)
> +
> +void pr_info_bpf_insn(const struct bpf_insn *insn, u64 *regs)
> +{
> +	u16 class = BPF_CLASS(insn->code);
> +	if (class == BPF_ALU || class == BPF_ALU64) {
> +		if (BPF_SRC(insn->code) == BPF_X)
> +			pr_info("code_%02x %sr%d%s %s r%d%s\n",
> +				insn->code, class == BPF_ALU ? "(u32)" : "",
> +				insn->a_reg, R(insn->a_reg),
> +				bpf_alu_string[BPF_OP(insn->code) >> 4],
> +				insn->x_reg, R(insn->x_reg));
> +		else
> +			pr_info("code_%02x %sr%d%s %s %d\n",
> +				insn->code, class == BPF_ALU ? "(u32)" : "",
> +				insn->a_reg, R(insn->a_reg),
> +				bpf_alu_string[BPF_OP(insn->code) >> 4],
> +				insn->imm);
> +	} else if (class == BPF_STX) {
> +		if (BPF_MODE(insn->code) == BPF_MEM)
> +			pr_info("code_%02x *(%s *)(r%d%s %+d) = r%d%s\n",
> +				insn->code,
> +				bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
> +				insn->a_reg, R(insn->a_reg),
> +				insn->off, insn->x_reg, R(insn->x_reg));
> +		else if (BPF_MODE(insn->code) == BPF_XADD)
> +			pr_info("code_%02x lock *(%s *)(r%d%s %+d) += r%d%s\n",
> +				insn->code,
> +				bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
> +				insn->a_reg, R(insn->a_reg), insn->off,
> +				insn->x_reg, R(insn->x_reg));
> +		else
> +			pr_info("BUG_%02x\n", insn->code);
> +	} else if (class == BPF_ST) {
> +		if (BPF_MODE(insn->code) != BPF_MEM) {
> +			pr_info("BUG_st_%02x\n", insn->code);
> +			return;
> +		}
> +		pr_info("code_%02x *(%s *)(r%d%s %+d) = %d\n",
> +			insn->code,
> +			bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
> +			insn->a_reg, R(insn->a_reg),
> +			insn->off, insn->imm);
> +	} else if (class == BPF_LDX) {
> +		if (BPF_MODE(insn->code) == BPF_MEM) {
> +			pr_info("code_%02x r%d = *(%s *)(r%d%s %+d)\n",
> +				insn->code, insn->a_reg,
> +				bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
> +				insn->x_reg, R(insn->x_reg), insn->off);
> +		} else {
> +			pr_info("BUG_ldx_%02x\n", insn->code);
> +		}
> +	} else if (class == BPF_LD) {
> +		if (BPF_MODE(insn->code) == BPF_ABS) {
> +			pr_info("code_%02x r%d = *(%s *)(skb %+d)\n",
> +				insn->code, insn->a_reg,
> +				bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
> +				insn->imm);
> +		} else if (BPF_MODE(insn->code) == BPF_IND) {
> +			pr_info("code_%02x r%d = *(%s *)(skb + r%d%s %+d)\n",
> +				insn->code, insn->a_reg,
> +				bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
> +				insn->x_reg, R(insn->x_reg), insn->imm);
> +		} else {
> +			pr_info("BUG_ld_%02x\n", insn->code);
> +		}
> +	} else if (class == BPF_JMP) {
> +		u16 opcode = BPF_OP(insn->code);
> +		if (opcode == BPF_CALL) {
> +			pr_info("code_%02x call %d\n", insn->code, insn->imm);
> +		} else if (insn->code == (BPF_JMP | BPF_JA)) {
> +			pr_info("code_%02x goto pc%+d\n",
> +				insn->code, insn->off);
> +		} else if (BPF_SRC(insn->code) == BPF_X) {
> +			pr_info("code_%02x if r%d%s %s r%d%s goto pc%+d\n",
> +				insn->code, insn->a_reg, R(insn->a_reg),
> +				bpf_jmp_string[BPF_OP(insn->code) >> 4],
> +				insn->x_reg, R(insn->x_reg), insn->off);
> +		} else {
> +			pr_info("code_%02x if r%d%s %s 0x%x goto pc%+d\n",
> +				insn->code, insn->a_reg, R(insn->a_reg),
> +				bpf_jmp_string[BPF_OP(insn->code) >> 4],
> +				insn->imm, insn->off);
> +		}
> +	} else {
> +		pr_info("code_%02x %s\n", insn->code, bpf_class_string[class]);
> +	}
> +}
> +EXPORT_SYMBOL(pr_info_bpf_insn);

Why would that need to be exported as a symbol?

I would actually like to avoid having this pr_info* inside the kernel.
Couldn't this be done e.g. through systemtap script that could e.g. be
placed under tools/net/ or inside the documentation file?

> +/* remap 'sock_filter' style BPF instruction set to 'bpf_insn' style (BPF64)
> + *
> + * first, call bpf_convert(old_prog, len, NULL, &new_len) to calculate new
> + * program length in one pass
> + *
> + * then new_prog = kmalloc(sizeof(struct bpf_insn) * new_len);
> + *
> + * and call it again: bpf_convert(old_prog, len, new_prog, &new_len);
> + * to remap in two passes: 1st pass finds new jump offsets, 2nd pass remaps
> + */

Would be nice to have the API comment it in kdoc format.

> +int bpf_convert(struct sock_filter *old_prog, int len,
> +		struct bpf_insn *new_prog, int *p_new_len)
> +{

If we place it into net/core/filter.c, it would be nice to keep naming
conventions consistent, e.g. sk_convert_filter() or so.

> +	struct bpf_insn *new_insn;
> +	struct sock_filter *fp;
> +	int *addrs = NULL;
> +	int new_len = 0;
> +	int pass = 0;
> +	int tgt, i;
> +
> +	if (len <= 0 || len >= BPF_MAXINSNS)
> +		return -EINVAL;
> +
> +	if (new_prog) {
> +		addrs = kzalloc(len * sizeof(*addrs), GFP_KERNEL);
> +		if (!addrs)
> +			return -ENOMEM;
> +	}
> +
> +do_pass:
> +	new_insn = new_prog;
> +	fp = old_prog;
> +	for (i = 0; i < len; fp++, i++) {
> +		struct bpf_insn tmp_insns[3] = {};
> +		struct bpf_insn *insn = tmp_insns;
> +
> +		if (addrs)
> +			addrs[i] = new_insn - new_prog;
> +
> +		switch (fp->code) {
> +		/* all arithmetic insns and skb loads map as-is */
> +		case BPF_ALU | BPF_ADD | BPF_X:
> +		case BPF_ALU | BPF_ADD | BPF_K:
> +		case BPF_ALU | BPF_SUB | BPF_X:
> +		case BPF_ALU | BPF_SUB | BPF_K:
> +		case BPF_ALU | BPF_AND | BPF_X:
> +		case BPF_ALU | BPF_AND | BPF_K:
> +		case BPF_ALU | BPF_OR | BPF_X:
> +		case BPF_ALU | BPF_OR | BPF_K:
> +		case BPF_ALU | BPF_LSH | BPF_X:
> +		case BPF_ALU | BPF_LSH | BPF_K:
> +		case BPF_ALU | BPF_RSH | BPF_X:
> +		case BPF_ALU | BPF_RSH | BPF_K:
> +		case BPF_ALU | BPF_XOR | BPF_X:
> +		case BPF_ALU | BPF_XOR | BPF_K:
> +		case BPF_ALU | BPF_MUL | BPF_X:
> +		case BPF_ALU | BPF_MUL | BPF_K:
> +		case BPF_ALU | BPF_DIV | BPF_X:
> +		case BPF_ALU | BPF_DIV | BPF_K:
> +		case BPF_ALU | BPF_MOD | BPF_X:
> +		case BPF_ALU | BPF_MOD | BPF_K:
> +		case BPF_ALU | BPF_NEG:
> +		case BPF_LD | BPF_ABS | BPF_W:
> +		case BPF_LD | BPF_ABS | BPF_H:
> +		case BPF_LD | BPF_ABS | BPF_B:
> +		case BPF_LD | BPF_IND | BPF_W:
> +		case BPF_LD | BPF_IND | BPF_H:
> +		case BPF_LD | BPF_IND | BPF_B:

I think here and elsewhere for similar constructions, we could use
BPF_S_* helpers that was introduced by Hagen in commit 01f2f3f6ef4d076
("net: optimize Berkeley Packet Filter (BPF) processing").

> +			insn->code = fp->code;
> +			insn->a_reg = 6;
> +			insn->x_reg = 7;
> +			insn->imm = fp->k;
> +			break;
> +
> +		/* jump opcodes map as-is, but offsets need adjustment */
> +		case BPF_JMP | BPF_JA:
> +			tgt = i + fp->k + 1;
> +			insn->code = fp->code;
> +#define EMIT_JMP \
> +	do { \
> +		if (tgt >= len || tgt < 0) \
> +			goto err; \
> +		insn->off = addrs ? addrs[tgt] - addrs[i] - 1 : 0; \
> +	} while (0)
> +
> +			EMIT_JMP;
> +			break;
> +
> +		case BPF_JMP | BPF_JEQ | BPF_K:
> +		case BPF_JMP | BPF_JEQ | BPF_X:
> +		case BPF_JMP | BPF_JSET | BPF_K:
> +		case BPF_JMP | BPF_JSET | BPF_X:
> +		case BPF_JMP | BPF_JGT | BPF_K:
> +		case BPF_JMP | BPF_JGT | BPF_X:
> +		case BPF_JMP | BPF_JGE | BPF_K:
> +		case BPF_JMP | BPF_JGE | BPF_X:
> +			insn->a_reg = 6;
> +			insn->x_reg = 7;
> +			insn->imm = fp->k;
> +			/* common case where 'jump_false' is next insn */
> +			if (fp->jf == 0) {
> +				insn->code = fp->code;
> +				tgt = i + fp->jt + 1;
> +				EMIT_JMP;
> +				break;
> +			}
> +			/* convert JEQ into JNE when 'jump_true' is next insn */
> +			if (fp->jt == 0 && BPF_OP(fp->code) == BPF_JEQ) {
> +				insn->code = BPF_JMP | BPF_JNE |
> +					BPF_SRC(fp->code);
> +				tgt = i + fp->jf + 1;
> +				EMIT_JMP;
> +				break;
> +			}
> +			/* other jumps are mapped into two insns: Jxx and JA */
> +			tgt = i + fp->jt + 1;
> +			insn->code = fp->code;
> +			EMIT_JMP;
> +
> +			insn++;
> +			insn->code = BPF_JMP | BPF_JA;
> +			tgt = i + fp->jf + 1;
> +			EMIT_JMP;
> +			/* adjust pc relative offset, since it's a 2nd insn */
> +			insn->off--;
> +			break;
> +
> +			/* ldxb 4*([14]&0xf) is remaped into 3 insns */
> +		case BPF_LDX | BPF_MSH | BPF_B:
> +			insn->code = BPF_LD | BPF_ABS | BPF_B;
> +			insn->a_reg = 7;
> +			insn->imm = fp->k;
> +
> +			insn++;
> +			insn->code = BPF_ALU | BPF_AND | BPF_K;
> +			insn->a_reg = 7;
> +			insn->imm = 0xf;
> +
> +			insn++;
> +			insn->code = BPF_ALU | BPF_LSH | BPF_K;
> +			insn->a_reg = 7;
> +			insn->imm = 2;
> +			break;
> +
> +			/* RET_K, RET_A are remaped into 2 insns */
> +		case BPF_RET | BPF_A:
> +		case BPF_RET | BPF_K:
> +			insn->code = BPF_ALU | BPF_MOV |
> +				(BPF_SRC(fp->code) == BPF_K ? BPF_K : BPF_X);
> +			insn->a_reg = 0;
> +			insn->x_reg = 6;
> +			insn->imm = fp->k;
> +
> +			insn++;
> +			insn->code = BPF_RET | BPF_K;
> +			break;
> +
> +			/* store to stack */
> +		case BPF_ST:
> +		case BPF_STX:
> +			insn->code = BPF_STX | BPF_MEM | BPF_W;
> +			insn->a_reg = 10;
> +			insn->x_reg = fp->code == BPF_ST ? 6 : 7;
> +			insn->off = -(BPF_MEMWORDS - fp->k) * 4;
> +			break;
> +
> +			/* load from stack */
> +		case BPF_LD | BPF_MEM:
> +		case BPF_LDX | BPF_MEM:
> +			insn->code = BPF_LDX | BPF_MEM | BPF_W;
> +			insn->a_reg = BPF_CLASS(fp->code) == BPF_LD ? 6 : 7;
> +			insn->x_reg = 10;
> +			insn->off = -(BPF_MEMWORDS - fp->k) * 4;
> +			break;
> +
> +			/* A = K or X = K */
> +		case BPF_LD | BPF_IMM:
> +		case BPF_LDX | BPF_IMM:
> +			insn->code = BPF_ALU | BPF_MOV | BPF_K;
> +			insn->a_reg = BPF_CLASS(fp->code) == BPF_LD ? 6 : 7;
> +			insn->imm = fp->k;
> +			break;
> +
> +			/* X = A */
> +		case BPF_MISC | BPF_TAX:
> +			insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
> +			insn->a_reg = 7;
> +			insn->x_reg = 6;
> +			break;
> +
> +			/* A = X */
> +		case BPF_MISC | BPF_TXA:
> +			insn->code = BPF_ALU64 | BPF_MOV | BPF_X;
> +			insn->a_reg = 6;
> +			insn->x_reg = 7;
> +			break;
> +
> +			/* A = skb->len or X = skb->len */
> +		case BPF_LD|BPF_W|BPF_LEN:
> +		case BPF_LDX|BPF_W|BPF_LEN:
> +			insn->code = BPF_LDX | BPF_MEM | BPF_W;
> +			insn->a_reg = BPF_CLASS(fp->code) == BPF_LD ? 6 : 7;
> +			insn->x_reg = 1;
> +			insn->off = offsetof(struct sk_buff, len);
> +			break;
> +
> +		default:
> +			/* pr_err("unknown opcode %02x\n", fp->code); */
> +			goto err;
> +		}
> +
> +		insn++;
> +		if (new_prog) {
> +			memcpy(new_insn, tmp_insns,
> +			       sizeof(*insn) * (insn - tmp_insns));
> +		}
> +		new_insn += insn - tmp_insns;
> +	}
> +
> +	if (!new_prog) {
> +		/* only calculating new length */
> +		*p_new_len = new_insn - new_prog;
> +		return 0;
> +	}
> +
> +	pass++;
> +	if (new_len != new_insn - new_prog) {
> +		new_len = new_insn - new_prog;
> +		if (pass > 2)
> +			goto err;
> +		goto do_pass;
> +	}
> +	kfree(addrs);
> +	if (*p_new_len != new_len)
> +		/* inconsistent new program length */
> +		pr_err("bpf_convert() usage error\n");
> +	return 0;
> +err:
> +	kfree(addrs);
> +	return -EINVAL;
> +}
> +
> +notrace u32 bpf_run(struct bpf_context *ctx, const struct bpf_insn *insn)
> +{

Similarly, sk_run_filter_ext()?

> +	u64 stack[64];
> +	u64 regs[16];
> +	void *ptr;
> +	u64 tmp;
> +	int off;
> +
> +#ifdef __x86_64
> +#define LOAD_IMM /**/
> +#define K insn->imm
> +#else
> +#define LOAD_IMM (K = insn->imm)
> +	s32 K = insn->imm;
> +#endif
> +
> +#ifdef DEBUG
> +#define DEBUG_INSN pr_info_bpf_insn(insn, regs)
> +#else
> +#define DEBUG_INSN
> +#endif

This DEBUG hunk could then be removed when we use a stap script
instead, for example.

> +#define A regs[insn->a_reg]
> +#define X regs[insn->x_reg]
> +
> +#define DL(A, B, C) [A|B|C] = &&L_##A##B##C,
> +#define L(A, B, C) L_##A##B##C:

Could we get rid of these two defines? I know you're defining
and using that as labels, but it's not obvious at first what
'jt' does. Maybe 'jt_labels' ?

> +#define CONT ({insn++; LOAD_IMM; DEBUG_INSN; goto select_insn; })
> +#define CONT_JMP ({insn++; LOAD_IMM; DEBUG_INSN; goto select_insn; })

Not a big fan of macros, but here it seems fine though.

> +/* some compilers may need help:
> + * #define CONT_JMP ({insn++; LOAD_IMM; DEBUG_INSN; goto *jt[insn->code]; })
> + */
> +
> +	static const void *jt[256] = {
> +		[0 ... 255] = &&L_default,

Nit: please just one define per line below:

> +		DL(BPF_ALU, BPF_ADD, BPF_X) DL(BPF_ALU, BPF_ADD, BPF_K)
> +		DL(BPF_ALU, BPF_SUB, BPF_X) DL(BPF_ALU, BPF_SUB, BPF_K)
> +		DL(BPF_ALU, BPF_AND, BPF_X) DL(BPF_ALU, BPF_AND, BPF_K)
> +		DL(BPF_ALU, BPF_OR, BPF_X)  DL(BPF_ALU, BPF_OR, BPF_K)
> +		DL(BPF_ALU, BPF_LSH, BPF_X) DL(BPF_ALU, BPF_LSH, BPF_K)
> +		DL(BPF_ALU, BPF_RSH, BPF_X) DL(BPF_ALU, BPF_RSH, BPF_K)
> +		DL(BPF_ALU, BPF_XOR, BPF_X) DL(BPF_ALU, BPF_XOR, BPF_K)
> +		DL(BPF_ALU, BPF_MUL, BPF_X) DL(BPF_ALU, BPF_MUL, BPF_K)
> +		DL(BPF_ALU, BPF_MOV, BPF_X) DL(BPF_ALU, BPF_MOV, BPF_K)
> +		DL(BPF_ALU, BPF_DIV, BPF_X) DL(BPF_ALU, BPF_DIV, BPF_K)
> +		DL(BPF_ALU, BPF_MOD, BPF_X) DL(BPF_ALU, BPF_MOD, BPF_K)
> +		DL(BPF_ALU64, BPF_ADD, BPF_X) DL(BPF_ALU64, BPF_ADD, BPF_K)
> +		DL(BPF_ALU64, BPF_SUB, BPF_X) DL(BPF_ALU64, BPF_SUB, BPF_K)
> +		DL(BPF_ALU64, BPF_AND, BPF_X) DL(BPF_ALU64, BPF_AND, BPF_K)
> +		DL(BPF_ALU64, BPF_OR, BPF_X)  DL(BPF_ALU64, BPF_OR, BPF_K)
> +		DL(BPF_ALU64, BPF_LSH, BPF_X) DL(BPF_ALU64, BPF_LSH, BPF_K)
> +		DL(BPF_ALU64, BPF_RSH, BPF_X) DL(BPF_ALU64, BPF_RSH, BPF_K)
> +		DL(BPF_ALU64, BPF_XOR, BPF_X) DL(BPF_ALU64, BPF_XOR, BPF_K)
> +		DL(BPF_ALU64, BPF_MUL, BPF_X) DL(BPF_ALU64, BPF_MUL, BPF_K)
> +		DL(BPF_ALU64, BPF_MOV, BPF_X) DL(BPF_ALU64, BPF_MOV, BPF_K)
> +		DL(BPF_ALU64, BPF_ARSH, BPF_X)DL(BPF_ALU64, BPF_ARSH, BPF_K)
> +		DL(BPF_ALU64, BPF_DIV, BPF_X) DL(BPF_ALU64, BPF_DIV, BPF_K)
> +		DL(BPF_ALU64, BPF_MOD, BPF_X) DL(BPF_ALU64, BPF_MOD, BPF_K)
> +		DL(BPF_ALU64, BPF_BSWAP32, BPF_X)
> +		DL(BPF_ALU64, BPF_BSWAP64, BPF_X)
> +		DL(BPF_ALU, BPF_NEG, 0)
> +		DL(BPF_JMP, BPF_CALL, 0)
> +		DL(BPF_JMP, BPF_JA, 0)
> +		DL(BPF_JMP, BPF_JEQ, BPF_X) DL(BPF_JMP, BPF_JEQ, BPF_K)
> +		DL(BPF_JMP, BPF_JNE, BPF_X) DL(BPF_JMP, BPF_JNE, BPF_K)
> +		DL(BPF_JMP, BPF_JGT, BPF_X) DL(BPF_JMP, BPF_JGT, BPF_K)
> +		DL(BPF_JMP, BPF_JGE, BPF_X) DL(BPF_JMP, BPF_JGE, BPF_K)
> +		DL(BPF_JMP, BPF_JSGT, BPF_X) DL(BPF_JMP, BPF_JSGT, BPF_K)
> +		DL(BPF_JMP, BPF_JSGE, BPF_X) DL(BPF_JMP, BPF_JSGE, BPF_K)
> +		DL(BPF_JMP, BPF_JSET, BPF_X) DL(BPF_JMP, BPF_JSET, BPF_K)
> +		DL(BPF_STX, BPF_MEM, BPF_B) DL(BPF_STX, BPF_MEM, BPF_H)
> +		DL(BPF_STX, BPF_MEM, BPF_W) DL(BPF_STX, BPF_MEM, BPF_DW)
> +		DL(BPF_ST, BPF_MEM, BPF_B) DL(BPF_ST, BPF_MEM, BPF_H)
> +		DL(BPF_ST, BPF_MEM, BPF_W) DL(BPF_ST, BPF_MEM, BPF_DW)
> +		DL(BPF_LDX, BPF_MEM, BPF_B) DL(BPF_LDX, BPF_MEM, BPF_H)
> +		DL(BPF_LDX, BPF_MEM, BPF_W) DL(BPF_LDX, BPF_MEM, BPF_DW)
> +		DL(BPF_STX, BPF_XADD, BPF_W)
> +#ifdef CONFIG_64BIT
> +		DL(BPF_STX, BPF_XADD, BPF_DW)
> +#endif
> +		DL(BPF_LD, BPF_ABS, BPF_W) DL(BPF_LD, BPF_ABS, BPF_H)
> +		DL(BPF_LD, BPF_ABS, BPF_B) DL(BPF_LD, BPF_IND, BPF_W)
> +		DL(BPF_LD, BPF_IND, BPF_H) DL(BPF_LD, BPF_IND, BPF_B)
> +		DL(BPF_RET, BPF_K, 0)
> +	};
> +
> +	regs[10/* BPF R10 */] = (u64)(ulong)&stack[64];
> +	regs[1/* BPF R1 */] = (u64)(ulong)ctx;
> +
> +	DEBUG_INSN;
> +	/* execute 1st insn */
> +select_insn:
> +	goto *jt[insn->code];
> +
> +		/* ALU */
> +#define ALU(OPCODE, OP) \
> +	L_BPF_ALU64##OPCODE##BPF_X: \
> +		A = A OP X; \
> +		CONT; \
> +	L_BPF_ALU##OPCODE##BPF_X: \
> +		A = (u32)A OP (u32)X; \
> +		CONT; \
> +	L_BPF_ALU64##OPCODE##BPF_K: \
> +		A = A OP K; \
> +		CONT; \
> +	L_BPF_ALU##OPCODE##BPF_K: \
> +		A = (u32)A OP (u32)K; \
> +		CONT;
> +
> +	ALU(BPF_ADD, +)
> +	ALU(BPF_SUB, -)
> +	ALU(BPF_AND, &)
> +	ALU(BPF_OR, |)
> +	ALU(BPF_LSH, <<)
> +	ALU(BPF_RSH, >>)
> +	ALU(BPF_XOR, ^)
> +	ALU(BPF_MUL, *)
> +
> +	L(BPF_ALU, BPF_NEG, 0)
> +		A = (u32)-A;
> +		CONT;
> +	L(BPF_ALU, BPF_MOV, BPF_X)
> +		A = (u32)X;
> +		CONT;
> +	L(BPF_ALU, BPF_MOV, BPF_K)
> +		A = (u32)K;
> +		CONT;
> +	L(BPF_ALU64, BPF_MOV, BPF_X)
> +		A = X;
> +		CONT;
> +	L(BPF_ALU64, BPF_MOV, BPF_K)
> +		A = K;
> +		CONT;
> +	L(BPF_ALU64, BPF_ARSH, BPF_X)
> +		(*(s64 *) &A) >>= X;
> +		CONT;
> +	L(BPF_ALU64, BPF_ARSH, BPF_K)
> +		(*(s64 *) &A) >>= K;
> +		CONT;
> +	L(BPF_ALU64, BPF_MOD, BPF_X)
> +		tmp = A;
> +		if (X)
> +			A = do_div(tmp, X);
> +		CONT;
> +	L(BPF_ALU, BPF_MOD, BPF_X)
> +		tmp = (u32)A;
> +		if (X)
> +			A = do_div(tmp, (u32)X);
> +		CONT;
> +	L(BPF_ALU64, BPF_MOD, BPF_K)
> +		tmp = A;
> +		if (K)
> +			A = do_div(tmp, K);
> +		CONT;
> +	L(BPF_ALU, BPF_MOD, BPF_K)
> +		tmp = (u32)A;
> +		if (K)
> +			A = do_div(tmp, (u32)K);
> +		CONT;
> +	L(BPF_ALU64, BPF_DIV, BPF_X)
> +		if (X)
> +			do_div(A, X);
> +		CONT;
> +	L(BPF_ALU, BPF_DIV, BPF_X)
> +		tmp = (u32)A;
> +		if (X)
> +			do_div(tmp, (u32)X);
> +		A = (u32)tmp;
> +		CONT;
> +	L(BPF_ALU64, BPF_DIV, BPF_K)
> +		if (K)
> +			do_div(A, K);
> +		CONT;
> +	L(BPF_ALU, BPF_DIV, BPF_K)
> +		tmp = (u32)A;
> +		if (K)
> +			do_div(tmp, (u32)K);
> +		A = (u32)tmp;
> +		CONT;
> +	L(BPF_ALU64, BPF_BSWAP32, BPF_X)
> +		A = swab32(A);
> +		CONT;
> +	L(BPF_ALU64, BPF_BSWAP64, BPF_X)
> +		A = swab64(A);
> +		CONT;
> +
> +		/* CALL */
> +	L(BPF_JMP, BPF_CALL, 0)
> +		/* TODO execute_func(K, regs); */
> +		CONT;

Would the filter checker for now just return -EINVAL when this is used?

> +		/* JMP */
> +	L(BPF_JMP, BPF_JA, 0)
> +		insn += insn->off;
> +		CONT;
> +	L(BPF_JMP, BPF_JEQ, BPF_X)
> +		if (A == X) {
> +			insn += insn->off;
> +			CONT_JMP;
> +		}
> +		CONT;
> +	L(BPF_JMP, BPF_JEQ, BPF_K)
> +		if (A == K) {
> +			insn += insn->off;
> +			CONT_JMP;
> +		}
> +		CONT;
> +	L(BPF_JMP, BPF_JNE, BPF_X)
> +		if (A != X) {
> +			insn += insn->off;
> +			CONT_JMP;
> +		}
> +		CONT;
> +	L(BPF_JMP, BPF_JNE, BPF_K)
> +		if (A != K) {
> +			insn += insn->off;
> +			CONT_JMP;
> +		}
> +		CONT;
> +	L(BPF_JMP, BPF_JGT, BPF_X)
> +		if (A > X) {
> +			insn += insn->off;
> +			CONT_JMP;
> +		}
> +		CONT;
> +	L(BPF_JMP, BPF_JGT, BPF_K)
> +		if (A > K) {
> +			insn += insn->off;
> +			CONT_JMP;
> +		}
> +		CONT;
> +	L(BPF_JMP, BPF_JGE, BPF_X)
> +		if (A >= X) {
> +			insn += insn->off;
> +			CONT_JMP;
> +		}
> +		CONT;
> +	L(BPF_JMP, BPF_JGE, BPF_K)
> +		if (A >= K) {
> +			insn += insn->off;
> +			CONT_JMP;
> +		}
> +		CONT;
> +	L(BPF_JMP, BPF_JSGT, BPF_X)
> +		if (((s64)A) > ((s64)X)) {
> +			insn += insn->off;
> +			CONT_JMP;
> +		}
> +		CONT;
> +	L(BPF_JMP, BPF_JSGT, BPF_K)
> +		if (((s64)A) > ((s64)K)) {
> +			insn += insn->off;
> +			CONT_JMP;
> +		}
> +		CONT;
> +	L(BPF_JMP, BPF_JSGE, BPF_X)
> +		if (((s64)A) >= ((s64)X)) {
> +			insn += insn->off;
> +			CONT_JMP;
> +		}
> +		CONT;
> +	L(BPF_JMP, BPF_JSGE, BPF_K)
> +		if (((s64)A) >= ((s64)K)) {
> +			insn += insn->off;
> +			CONT_JMP;
> +		}
> +		CONT;
> +	L(BPF_JMP, BPF_JSET, BPF_X)
> +		if (A & X) {
> +			insn += insn->off;
> +			CONT_JMP;
> +		}
> +		CONT;
> +	L(BPF_JMP, BPF_JSET, BPF_K)
> +		if (A & (u32)K) {
> +			insn += insn->off;
> +			CONT_JMP;
> +		}
> +		CONT;

Okay, so right now we only support forward jumps, right? And these
are checked by the old checker code I'd assume?

> +		/* STX and ST and LDX*/
> +#define LDST(SIZEOP, SIZE) \
> +	L_BPF_STXBPF_MEM##SIZEOP: \
> +		*(SIZE *)(ulong)(A + insn->off) = X; \
> +		CONT; \
> +	L_BPF_STBPF_MEM##SIZEOP: \
> +		*(SIZE *)(ulong)(A + insn->off) = K; \
> +		CONT; \
> +	L_BPF_LDXBPF_MEM##SIZEOP: \
> +		A = *(SIZE *)(ulong)(X + insn->off); \
> +		CONT;
> +
> +		LDST(BPF_B, u8)
> +		LDST(BPF_H, u16)
> +		LDST(BPF_W, u32)
> +		LDST(BPF_DW, u64)
> +
> +		/* STX XADD */
> +	L(BPF_STX, BPF_XADD, BPF_W)
> +		atomic_add((u32)X, (atomic_t *)(ulong)(A + insn->off));
> +		CONT;
> +#ifdef CONFIG_64BIT
> +	L(BPF_STX, BPF_XADD, BPF_DW)
> +		atomic64_add((u64)X, (atomic64_t *)(ulong)(A + insn->off));
> +		CONT;
> +#endif
> +
> +		/* LD from SKB + K */

Nit: indent one tab too much (here and elsewhere)

> +	L(BPF_LD, BPF_ABS, BPF_W)
> +		off = K;
> +load_word:
> +		ptr = load_pointer((struct sk_buff *)ctx, off, 4, &tmp);
> +		if (likely(ptr != NULL)) {
> +			A = get_unaligned_be32(ptr);
> +			CONT;
> +		}
> +		return 0;
> +
> +	L(BPF_LD, BPF_ABS, BPF_H)
> +		off = K;
> +load_half:
> +		ptr = load_pointer((struct sk_buff *)ctx, off, 2, &tmp);
> +		if (likely(ptr != NULL)) {
> +			A = get_unaligned_be16(ptr);
> +			CONT;
> +		}
> +		return 0;
> +
> +	L(BPF_LD, BPF_ABS, BPF_B)
> +		off = K;
> +load_byte:
> +		ptr = load_pointer((struct sk_buff *)ctx, off, 1, &tmp);
> +		if (likely(ptr != NULL)) {
> +			A = *(u8 *)ptr;
> +			CONT;
> +		}
> +		return 0;
> +
> +		/* LD from SKB + X + K */

Nit: ditto

> +	L(BPF_LD, BPF_IND, BPF_W)
> +		off = K + X;
> +		goto load_word;
> +
> +	L(BPF_LD, BPF_IND, BPF_H)
> +		off = K + X;
> +		goto load_half;
> +
> +	L(BPF_LD, BPF_IND, BPF_B)
> +		off = K + X;
> +		goto load_byte;
> +
> +		/* RET */
> +	L(BPF_RET, BPF_K, 0)
> +		return regs[0/* R0 */];
> +
> +	L_default:
> +		/* bpf_check() or bpf_convert() will guarantee that
> +		 * we never reach here
> +		 */
> +		WARN_RATELIMIT(1, "unknown opcode %02x\n", insn->code);
> +		return 0;
> +#undef DL
> +#undef L
> +#undef CONT
> +#undef A
> +#undef X
> +#undef K
> +#undef LOAD_IMM
> +#undef DEBUG_INSN
> +#undef ALU
> +#undef LDST
> +}
> +EXPORT_SYMBOL(bpf_run);
> +
> diff --git a/net/core/filter.c b/net/core/filter.c
> index ad30d626a5bd..575bf5fd4335 100644
> --- a/net/core/filter.c
> +++ b/net/core/filter.c
> @@ -637,6 +637,10 @@ void sk_filter_release_rcu(struct rcu_head *rcu)
>   {
>   	struct sk_filter *fp = container_of(rcu, struct sk_filter, rcu);
>
> +	if ((void *)fp->bpf_func == (void *)bpf_run)
> +		/* arch specific jit_free are expecting this value */
> +		fp->bpf_func = sk_run_filter;
> +
>   	bpf_jit_free(fp);
>   }
>   EXPORT_SYMBOL(sk_filter_release_rcu);
> @@ -655,6 +659,81 @@ static int __sk_prepare_filter(struct sk_filter *fp)
>   	return 0;
>   }
>
> +static int bpf64_prepare(struct sk_filter **pfp, struct sock_fprog *fprog,
> +			 struct sock *sk)
> +{

sk_prepare_filter_ext()?

> +	unsigned int fsize = sizeof(struct sock_filter) * fprog->len;
> +	struct sock_filter *old_prog;
> +	unsigned int sk_fsize;
> +	struct sk_filter *fp;
> +	int new_len;
> +	int err;
> +
> +	/* store old program into buffer, since chk_filter will remap opcodes */
> +	old_prog = kmalloc(fsize, GFP_KERNEL);
> +	if (!old_prog)
> +		return -ENOMEM;
> +
> +	if (sk) {
> +		if (copy_from_user(old_prog, fprog->filter, fsize)) {
> +			err = -EFAULT;
> +			goto free_prog;
> +		}
> +	} else {
> +		memcpy(old_prog, fprog->filter, fsize);
> +	}
> +
> +	/* calculate bpf64 program length */
> +	err = bpf_convert(fprog->filter, fprog->len, NULL, &new_len);
> +	if (err)
> +		goto free_prog;
> +
> +	sk_fsize = sk_filter_size(new_len);
> +	/* allocate sk_filter to store bpf64 program */
> +	if (sk)
> +		fp = sock_kmalloc(sk, sk_fsize, GFP_KERNEL);
> +	else
> +		fp = kmalloc(sk_fsize, GFP_KERNEL);
> +	if (!fp) {
> +		err = -ENOMEM;
> +		goto free_prog;
> +	}
> +
> +	/* remap old insns into bpf64 */
> +	err = bpf_convert(old_prog, fprog->len,
> +			  (struct bpf_insn *)fp->insns, &new_len);
> +	if (err)
> +		/* 2nd bpf_convert() can fail only if it fails
> +		 * to allocate memory, remapping must succeed
> +		 */
> +		goto free_fp;
> +
> +	/* now chk_filter can overwrite old_prog while checking */
> +	err = sk_chk_filter(old_prog, fprog->len);
> +	if (err)
> +		goto free_fp;
> +
> +	/* discard old prog */
> +	kfree(old_prog);
> +
> +	atomic_set(&fp->refcnt, 1);
> +	fp->len = new_len;
> +
> +	/* bpf64 insns must be executed by bpf_run */
> +	fp->bpf_func = (typeof(fp->bpf_func))bpf_run;
> +
> +	*pfp = fp;
> +	return 0;
> +free_fp:
> +	if (sk)
> +		sock_kfree_s(sk, fp, sk_fsize);
> +	else
> +		kfree(fp);
> +free_prog:
> +	kfree(old_prog);
> +	return err;
> +}
> +
>   /**
>    *	sk_unattached_filter_create - create an unattached filter
>    *	@fprog: the filter program
> @@ -676,6 +755,9 @@ int sk_unattached_filter_create(struct sk_filter **pfp,
>   	if (fprog->filter == NULL)
>   		return -EINVAL;
>
> +	if (bpf64_enable)
> +		return bpf64_prepare(pfp, fprog, NULL);
> +
>   	fp = kmalloc(sk_filter_size(fprog->len), GFP_KERNEL);
>   	if (!fp)
>   		return -ENOMEM;
> @@ -726,21 +808,27 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
>   	if (fprog->filter == NULL)
>   		return -EINVAL;
>
> -	fp = sock_kmalloc(sk, sk_fsize, GFP_KERNEL);
> -	if (!fp)
> -		return -ENOMEM;
> -	if (copy_from_user(fp->insns, fprog->filter, fsize)) {
> -		sock_kfree_s(sk, fp, sk_fsize);
> -		return -EFAULT;
> -	}
> +	if (bpf64_enable) {
> +		err = bpf64_prepare(&fp, fprog, sk);
> +		if (err)
> +			return err;
> +	} else {
> +		fp = sock_kmalloc(sk, sk_fsize, GFP_KERNEL);
> +		if (!fp)
> +			return -ENOMEM;
> +		if (copy_from_user(fp->insns, fprog->filter, fsize)) {
> +			sock_kfree_s(sk, fp, sk_fsize);
> +			return -EFAULT;
> +		}
>
> -	atomic_set(&fp->refcnt, 1);
> -	fp->len = fprog->len;
> +		atomic_set(&fp->refcnt, 1);
> +		fp->len = fprog->len;
>
> -	err = __sk_prepare_filter(fp);
> -	if (err) {
> -		sk_filter_uncharge(sk, fp);
> -		return err;
> +		err = __sk_prepare_filter(fp);
> +		if (err) {
> +			sk_filter_uncharge(sk, fp);
> +			return err;
> +		}
>   	}
>
>   	old_fp = rcu_dereference_protected(sk->sk_filter,
> diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c
> index cf9cd13509a7..f03acc0e8950 100644
> --- a/net/core/sysctl_net_core.c
> +++ b/net/core/sysctl_net_core.c
> @@ -273,6 +273,13 @@ static struct ctl_table net_core_table[] = {
>   	},
>   #endif
>   	{
> +		.procname	= "bpf64_enable",
> +		.data		= &bpf64_enable,
> +		.maxlen		= sizeof(int),
> +		.mode		= 0644,
> +		.proc_handler	= proc_dointvec
> +	},
> +	{
>   		.procname	= "netdev_tstamp_prequeue",
>   		.data		= &netdev_tstamp_prequeue,
>   		.maxlen		= sizeof(int),
>

Hope some of the comments made sense. ;-)

Thanks,

Daniel