Allow bpf qdisc programs to update Qdisc qstats directly with btf struct access. Signed-off-by: Amery Hung <amery.hung@xxxxxxxxxxxxx> --- net/sched/bpf_qdisc.c | 53 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/net/sched/bpf_qdisc.c b/net/sched/bpf_qdisc.c index b5ac3b9923fb..3901f855effc 100644 --- a/net/sched/bpf_qdisc.c +++ b/net/sched/bpf_qdisc.c @@ -57,6 +57,7 @@ bpf_qdisc_get_func_proto(enum bpf_func_id func_id, } } +BTF_ID_LIST_SINGLE(bpf_qdisc_ids, struct, Qdisc) BTF_ID_LIST_SINGLE(bpf_sk_buff_ids, struct, sk_buff) BTF_ID_LIST_SINGLE(bpf_sk_buff_ptr_ids, struct, bpf_sk_buff_ptr) @@ -81,20 +82,37 @@ static bool bpf_qdisc_is_valid_access(int off, int size, return bpf_tracing_btf_ctx_access(off, size, type, prog, info); } -static int bpf_qdisc_btf_struct_access(struct bpf_verifier_log *log, - const struct bpf_reg_state *reg, - int off, int size) +static int bpf_qdisc_qdisc_access(struct bpf_verifier_log *log, + const struct bpf_reg_state *reg, + int off, int size) { - const struct btf_type *t, *skbt; size_t end; - skbt = btf_type_by_id(reg->btf, bpf_sk_buff_ids[0]); - t = btf_type_by_id(reg->btf, reg->btf_id); - if (t != skbt) { - bpf_log(log, "only read is supported\n"); + switch (off) { + case offsetof(struct Qdisc, qstats) ... offsetofend(struct Qdisc, qstats) - 1: + end = offsetofend(struct Qdisc, qstats); + break; + default: + bpf_log(log, "no write support to Qdisc at off %d\n", off); + return -EACCES; + } + + if (off + size > end) { + bpf_log(log, + "write access at off %d with size %d beyond the member of Qdisc ended at %zu\n", + off, size, end); return -EACCES; } + return 0; +} + +static int bpf_qdisc_sk_buff_access(struct bpf_verifier_log *log, + const struct bpf_reg_state *reg, + int off, int size) +{ + size_t end; + switch (off) { case offsetof(struct sk_buff, tstamp): end = offsetofend(struct sk_buff, tstamp); @@ -136,6 +154,25 @@ static int bpf_qdisc_btf_struct_access(struct bpf_verifier_log *log, return 0; } +static int bpf_qdisc_btf_struct_access(struct bpf_verifier_log *log, + const struct bpf_reg_state *reg, + int off, int size) +{ + const struct btf_type *t, *skbt, *qdisct; + + skbt = btf_type_by_id(reg->btf, bpf_sk_buff_ids[0]); + qdisct = btf_type_by_id(reg->btf, bpf_qdisc_ids[0]); + t = btf_type_by_id(reg->btf, reg->btf_id); + + if (t == skbt) + return bpf_qdisc_sk_buff_access(log, reg, off, size); + else if (t == qdisct) + return bpf_qdisc_qdisc_access(log, reg, off, size); + + bpf_log(log, "only read is supported\n"); + return -EACCES; +} + __bpf_kfunc_start_defs(); /* bpf_skb_get_hash - Get the flow hash of an skb. -- 2.20.1