nfp: bpf: support optimizing dead branches
authorJakub Kicinski <jakub.kicinski@netronome.com>
Wed, 23 Jan 2019 06:45:28 +0000 (22:45 -0800)
committerAlexei Starovoitov <ast@kernel.org>
Thu, 24 Jan 2019 01:35:32 +0000 (17:35 -0800)
Verifier will now optimize out branches to dead code, implement
the replace_insn callback to take advantage of that optimization.

Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Reviewed-by: Quentin Monnet <quentin.monnet@netronome.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
drivers/net/ethernet/netronome/nfp/bpf/main.h
drivers/net/ethernet/netronome/nfp/bpf/offload.c
drivers/net/ethernet/netronome/nfp/bpf/verifier.c

index 07879eee3d469445f3d461885d4bb37d0567cadf..a33aa7df1979336792766d1e87ef001b7d18082b 100644 (file)
@@ -412,6 +412,17 @@ static inline bool is_mbpf_div(const struct nfp_insn_meta *meta)
        return is_mbpf_alu(meta) && mbpf_op(meta) == BPF_DIV;
 }
 
+static inline bool is_mbpf_cond_jump(const struct nfp_insn_meta *meta)
+{
+       u8 op;
+
+       if (BPF_CLASS(meta->insn.code) != BPF_JMP)
+               return false;
+
+       op = BPF_OP(meta->insn.code);
+       return op != BPF_JA && op != BPF_EXIT && op != BPF_CALL;
+}
+
 static inline bool is_mbpf_helper_call(const struct nfp_insn_meta *meta)
 {
        struct bpf_insn insn = meta->insn;
@@ -520,6 +531,9 @@ int nfp_verify_insn(struct bpf_verifier_env *env, int insn_idx,
                    int prev_insn_idx);
 int nfp_bpf_finalize(struct bpf_verifier_env *env);
 
+int nfp_bpf_opt_replace_insn(struct bpf_verifier_env *env, u32 off,
+                            struct bpf_insn *insn);
+
 extern const struct bpf_prog_offload_ops nfp_bpf_dev_ops;
 
 struct netdev_bpf;
index c10aab392cf6bed86420938d090080de14402992..877c1b8f95e2ada131fd0d42ce9b48090dec0bee 100644 (file)
@@ -592,6 +592,7 @@ int nfp_net_bpf_offload(struct nfp_net *nn, struct bpf_prog *prog,
 const struct bpf_prog_offload_ops nfp_bpf_dev_ops = {
        .insn_hook      = nfp_verify_insn,
        .finalize       = nfp_bpf_finalize,
+       .replace_insn   = nfp_bpf_opt_replace_insn,
        .prepare        = nfp_bpf_verifier_prep,
        .translate      = nfp_bpf_translate,
        .destroy        = nfp_bpf_destroy,
index 2712ab17d57c84a49a33ff485042cb1b40fe0209..32468e1b1b735e741f570d5df5f87a719c7b53d8 100644 (file)
@@ -786,3 +786,37 @@ int nfp_bpf_finalize(struct bpf_verifier_env *env)
 
        return 0;
 }
+
+int nfp_bpf_opt_replace_insn(struct bpf_verifier_env *env, u32 off,
+                            struct bpf_insn *insn)
+{
+       struct nfp_prog *nfp_prog = env->prog->aux->offload->dev_priv;
+       struct bpf_insn_aux_data *aux_data = env->insn_aux_data;
+       struct nfp_insn_meta *meta = nfp_prog->verifier_meta;
+
+       meta = nfp_bpf_goto_meta(nfp_prog, meta, aux_data[off].orig_idx);
+       nfp_prog->verifier_meta = meta;
+
+       /* conditional jump to jump conversion */
+       if (is_mbpf_cond_jump(meta) &&
+           insn->code == (BPF_JMP | BPF_JA | BPF_K)) {
+               unsigned int tgt_off;
+
+               tgt_off = off + insn->off + 1;
+
+               if (!insn->off) {
+                       meta->jmp_dst = list_next_entry(meta, l);
+                       meta->jump_neg_op = false;
+               } else if (meta->jmp_dst->n != aux_data[tgt_off].orig_idx) {
+                       pr_vlog(env, "branch hard wire at %d changes target %d -> %d\n",
+                               off, meta->jmp_dst->n,
+                               aux_data[tgt_off].orig_idx);
+                       return -EINVAL;
+               }
+               return 0;
+       }
+
+       pr_vlog(env, "unsupported instruction replacement %hhx -> %hhx\n",
+               meta->insn.code, insn->code);
+       return -EINVAL;
+}