nfp: bpf: support removing dead code
authorJakub Kicinski <jakub.kicinski@netronome.com>
Wed, 23 Jan 2019 06:45:29 +0000 (22:45 -0800)
committerAlexei Starovoitov <ast@kernel.org>
Thu, 24 Jan 2019 01:35:32 +0000 (17:35 -0800)
Add a verifier callback to the nfp JIT to remove the instructions
the verifier deemed to be dead.

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 a33aa7df1979336792766d1e87ef001b7d18082b..5813c3e13ebeb0d64fa5a1a6639192c470a5ac91 100644 (file)
@@ -247,9 +247,12 @@ struct nfp_bpf_reg_state {
 #define FLAG_INSN_SKIP_NOOP                    BIT(3)
 /* Instruction is optimized out based on preceding instructions */
 #define FLAG_INSN_SKIP_PREC_DEPENDENT          BIT(4)
+/* Instruction is optimized by the verifier */
+#define FLAG_INSN_SKIP_VERIFIER_OPT            BIT(5)
 
 #define FLAG_INSN_SKIP_MASK            (FLAG_INSN_SKIP_NOOP | \
-                                        FLAG_INSN_SKIP_PREC_DEPENDENT)
+                                        FLAG_INSN_SKIP_PREC_DEPENDENT | \
+                                        FLAG_INSN_SKIP_VERIFIER_OPT)
 
 /**
  * struct nfp_insn_meta - BPF instruction wrapper
@@ -533,6 +536,7 @@ 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);
+int nfp_bpf_opt_remove_insns(struct bpf_verifier_env *env, u32 off, u32 cnt);
 
 extern const struct bpf_prog_offload_ops nfp_bpf_dev_ops;
 
index 877c1b8f95e2ada131fd0d42ce9b48090dec0bee..55c7dbf8b4214be40d1e3b85d4a2a80df2637a41 100644 (file)
@@ -220,6 +220,10 @@ static int nfp_bpf_translate(struct bpf_prog *prog)
        unsigned int max_instr;
        int err;
 
+       /* We depend on dead code elimination succeeding */
+       if (prog->aux->offload->opt_failed)
+               return -EINVAL;
+
        max_instr = nn_readw(nn, NFP_NET_CFG_BPF_MAX_LEN);
        nfp_prog->__prog_alloc_len = max_instr * sizeof(u64);
 
@@ -593,6 +597,7 @@ 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,
+       .remove_insns   = nfp_bpf_opt_remove_insns,
        .prepare        = nfp_bpf_verifier_prep,
        .translate      = nfp_bpf_translate,
        .destroy        = nfp_bpf_destroy,
index 32468e1b1b735e741f570d5df5f87a719c7b53d8..36f56eb4cbe2b8dff15d737feb429bbc27fecb17 100644 (file)
@@ -820,3 +820,27 @@ int nfp_bpf_opt_replace_insn(struct bpf_verifier_env *env, u32 off,
                meta->insn.code, insn->code);
        return -EINVAL;
 }
+
+int nfp_bpf_opt_remove_insns(struct bpf_verifier_env *env, u32 off, u32 cnt)
+{
+       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;
+       unsigned int i;
+
+       meta = nfp_bpf_goto_meta(nfp_prog, meta, aux_data[off].orig_idx);
+
+       for (i = 0; i < cnt; i++) {
+               if (WARN_ON_ONCE(&meta->l == &nfp_prog->insns))
+                       return -EINVAL;
+
+               /* doesn't count if it already has the flag */
+               if (meta->flags & FLAG_INSN_SKIP_VERIFIER_OPT)
+                       i--;
+
+               meta->flags |= FLAG_INSN_SKIP_VERIFIER_OPT;
+               meta = list_next_entry(meta, l);
+       }
+
+       return 0;
+}