*/
        subprog[env->subprog_cnt].start = insn_cnt;
 
-       if (env->log.level > 1)
+       if (env->log.level & BPF_LOG_LEVEL2)
                for (i = 0; i < env->subprog_cnt; i++)
                        verbose(env, "func#%d @%d\n", i, subprog[i].start);
 
                         struct bpf_reg_state *parent)
 {
        bool writes = parent == state->parent; /* Observe write marks */
+       int cnt = 0;
 
        while (parent) {
                /* if read wasn't screened by an earlier write ... */
                state = parent;
                parent = state->parent;
                writes = true;
+               cnt++;
        }
+
+       if (env->longest_mark_read_walk < cnt)
+               env->longest_mark_read_walk = cnt;
        return 0;
 }
 
         * need to try adding each of min_value and max_value to off
         * to make sure our theoretical access will be safe.
         */
-       if (env->log.level)
+       if (env->log.level & BPF_LOG_LEVEL)
                print_verifier_state(env, state);
 
        /* The minimum value is only important with signed
        /* and go analyze first insn of the callee */
        *insn_idx = target_insn;
 
-       if (env->log.level) {
+       if (env->log.level & BPF_LOG_LEVEL) {
                verbose(env, "caller:\n");
                print_verifier_state(env, caller);
                verbose(env, "callee:\n");
                return err;
 
        *insn_idx = callee->callsite + 1;
-       if (env->log.level) {
+       if (env->log.level & BPF_LOG_LEVEL) {
                verbose(env, "returning from callee:\n");
                print_verifier_state(env, callee);
                verbose(env, "to caller at %d:\n", *insn_idx);
                        insn->dst_reg);
                return -EACCES;
        }
-       if (env->log.level)
+       if (env->log.level & BPF_LOG_LEVEL)
                print_verifier_state(env, this_branch->frame[this_branch->curframe]);
        return 0;
 }
                states_cnt++;
        }
 
+       if (env->max_states_per_insn < states_cnt)
+               env->max_states_per_insn = states_cnt;
+
        if (!env->allow_ptr_leaks && states_cnt > BPF_COMPLEXITY_LIMIT_STATES)
                return 0;
 
        new_sl = kzalloc(sizeof(struct bpf_verifier_state_list), GFP_KERNEL);
        if (!new_sl)
                return -ENOMEM;
+       env->total_states++;
+       env->peak_states++;
 
        /* add new state to the head of linked list */
        new = &new_sl->state;
        struct bpf_verifier_state *state;
        struct bpf_insn *insns = env->prog->insnsi;
        struct bpf_reg_state *regs;
-       int insn_cnt = env->prog->len, i;
-       int insn_processed = 0;
+       int insn_cnt = env->prog->len;
        bool do_print_state = false;
 
        env->prev_linfo = NULL;
                insn = &insns[env->insn_idx];
                class = BPF_CLASS(insn->code);
 
-               if (++insn_processed > BPF_COMPLEXITY_LIMIT_INSNS) {
+               if (++env->insn_processed > BPF_COMPLEXITY_LIMIT_INSNS) {
                        verbose(env,
                                "BPF program is too large. Processed %d insn\n",
-                               insn_processed);
+                               env->insn_processed);
                        return -E2BIG;
                }
 
                        return err;
                if (err == 1) {
                        /* found equivalent state, can prune the search */
-                       if (env->log.level) {
+                       if (env->log.level & BPF_LOG_LEVEL) {
                                if (do_print_state)
                                        verbose(env, "\nfrom %d to %d%s: safe\n",
                                                env->prev_insn_idx, env->insn_idx,
                if (need_resched())
                        cond_resched();
 
-               if (env->log.level > 1 || (env->log.level && do_print_state)) {
-                       if (env->log.level > 1)
+               if (env->log.level & BPF_LOG_LEVEL2 ||
+                   (env->log.level & BPF_LOG_LEVEL && do_print_state)) {
+                       if (env->log.level & BPF_LOG_LEVEL2)
                                verbose(env, "%d:", env->insn_idx);
                        else
                                verbose(env, "\nfrom %d to %d%s:",
                        do_print_state = false;
                }
 
-               if (env->log.level) {
+               if (env->log.level & BPF_LOG_LEVEL) {
                        const struct bpf_insn_cbs cbs = {
                                .cb_print       = verbose,
                                .private_data   = env,
                env->insn_idx++;
        }
 
-       verbose(env, "processed %d insns (limit %d), stack depth ",
-               insn_processed, BPF_COMPLEXITY_LIMIT_INSNS);
-       for (i = 0; i < env->subprog_cnt; i++) {
-               u32 depth = env->subprog_info[i].stack_depth;
-
-               verbose(env, "%d", depth);
-               if (i + 1 < env->subprog_cnt)
-                       verbose(env, "+");
-       }
-       verbose(env, "\n");
        env->prog->aux->stack_depth = env->subprog_info[0].stack_depth;
        return 0;
 }
        kfree(env->explored_states);
 }
 
+static void print_verification_stats(struct bpf_verifier_env *env)
+{
+       int i;
+
+       if (env->log.level & BPF_LOG_STATS) {
+               verbose(env, "verification time %lld usec\n",
+                       div_u64(env->verification_time, 1000));
+               verbose(env, "stack depth ");
+               for (i = 0; i < env->subprog_cnt; i++) {
+                       u32 depth = env->subprog_info[i].stack_depth;
+
+                       verbose(env, "%d", depth);
+                       if (i + 1 < env->subprog_cnt)
+                               verbose(env, "+");
+               }
+               verbose(env, "\n");
+       }
+       verbose(env, "processed %d insns (limit %d) max_states_per_insn %d "
+               "total_states %d peak_states %d mark_read %d\n",
+               env->insn_processed, BPF_COMPLEXITY_LIMIT_INSNS,
+               env->max_states_per_insn, env->total_states,
+               env->peak_states, env->longest_mark_read_walk);
+}
+
 int bpf_check(struct bpf_prog **prog, union bpf_attr *attr,
              union bpf_attr __user *uattr)
 {
+       u64 start_time = ktime_get_ns();
        struct bpf_verifier_env *env;
        struct bpf_verifier_log *log;
        int i, len, ret = -EINVAL;
                ret = -EINVAL;
                /* log attributes have to be sane */
                if (log->len_total < 128 || log->len_total > UINT_MAX >> 8 ||
-                   !log->level || !log->ubuf)
+                   !log->level || !log->ubuf || log->level & ~BPF_LOG_MASK)
                        goto err_unlock;
        }
 
        if (ret == 0)
                ret = fixup_call_args(env);
 
+       env->verification_time = ktime_get_ns() - start_time;
+       print_verification_stats(env);
+
        if (log->level && bpf_verifier_log_full(log))
                ret = -ENOSPC;
        if (log->level && !log->ubuf) {