BPF helper improvements
Subsystems (8)
Daniel Borkmann
maintainercover BPF helper improvements
This set adds various BPF helper improvements, that is, cleaning up and adding BPF_F_CURRENT_CPU flag for tracing helper, allowing for preemption checks on bpf_get_smp_processor_id() helper, and adding two new helpers bpf_skb_change_{proto, type} for tc related programs. For further details please see individual patches.
Note, this set requires -net to be merged into -net-next tree first.
Thanks a lot!
Daniel Borkmann (6):
bpf: minor cleanups on fd maps and helpers
bpf, trace: fetch current cpu only once
bpf, trace: add BPF_F_CURRENT_CPU flag for bpf_perf_event_read
bpf: don't use raw processor id in generic helper
bpf: add bpf_skb_change_proto helper
bpf: add bpf_skb_change_type helper
include/uapi/linux/bpf.h | 25 ++++- kernel/bpf/core.c | 3 +- kernel/bpf/helpers.c | 2 +- kernel/trace/bpf_trace.c | 32 +++---- net/core/filter.c | 234 ++++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 275 insertions(+), 21 deletions(-)
Signature · 1 line
1.9.3
Daniel Borkmann
maintainerPATCH 1/6 bpf: minor cleanups on fd maps and helpers
Some minor cleanups: i) Remove the unlikely() from fd array map lookups and let the CPU branch predictor do its job, scenarios where there is not always a map entry are very well valid. ii) Move the attribute type check in the bpf_perf_event_read() helper a bit earlier so it's consistent wrt checks with bpf_perf_event_output() helper as well. iii) remove some comments that are self-documenting in kprobe_prog_is_valid_access() and therefore make it consistent to tp_prog_is_valid_access() as well.
--- kernel/bpf/core.c | 3 +-- kernel/trace/bpf_trace.c | 18 ++++++------------ 2 files changed, 7 insertions(+), 14 deletions(-)
kernel/bpf/core.c +1 -2
@@ -719,14 +719,13 @@ select_insn:if (unlikely (index >= array->map.max_entries)) goto out; -if (unlikely (tail_call_cnt >MAX_TAIL_CALL_CNT )) goto out; tail_call_cnt++; prog =READ_ONCE (array->ptrs[index]); -if (unlikely (!prog)) +if (!prog) goto out;/* ARG1 at this point is guaranteed to point to CTX from
kernel/trace/bpf_trace.c +6 -12
@@ -199,19 +199,19 @@ static u64 bpf_perf_event_read(u64 r1, u64 index, u64 r3, u64 r4, u64 r5)return -E2BIG ; ee =READ_ONCE (array->ptrs[index]); -if (unlikely (!ee)) +if (!ee)return -ENOENT ; event = ee->event; +if (unlikely (event->attr.type !=PERF_TYPE_HARDWARE && + event->attr.type !=PERF_TYPE_RAW )) +return -EINVAL ; +/* make sure event is local and doesn't have pmu::count */ if (event->oncpu !=smp_processor_id () || event->pmu->count)return -EINVAL ; -if (unlikely (event->attr.type !=PERF_TYPE_HARDWARE && - event->attr.type !=PERF_TYPE_RAW )) -return -EINVAL ; -/* * we don't know if the function is run successfully by the * return value. It can be judged in other places, such as
@@ -251,7 +251,7 @@ static u64 bpf_perf_event_output(u64 r1, u64 r2, u64 flags, u64 r4, u64 size)return -E2BIG ; ee =READ_ONCE (array->ptrs[index]); -if (unlikely (!ee)) +if (!ee)return -ENOENT ; event = ee->event;
@@ -354,18 +354,12 @@ static const struct bpf_func_proto *kprobe_prog_func_proto(enum bpf_func_id funcstatic bool kprobe_prog_is_valid_access (int off,int size,enum bpf_access_type type,enum bpf_reg_type *reg_type) { -/* check bounds */ if (off <0 || off >=sizeof (struct pt_regs ))return false; - -/* only read is allowed */ if (type !=BPF_READ )return false; - -/* disallow misaligned access */ if (off % size !=0 )return false; -return true; }
Signature · 1 line
1.9.3
Daniel Borkmann
maintainerPATCH 2/6 bpf, trace: fetch current cpu only once
In reply to Daniel BorkmannWe currently have two invocations, which is unnecessary. Fetch it only once and use the smp_processor_id() variant, so we also get preemption checks along with it when DEBUG_PREEMPT is set.
--- kernel/trace/bpf_trace.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
kernel/trace/bpf_trace.c +3 -2
@@ -233,6 +233,7 @@ static u64 bpf_perf_event_output(u64 r1, u64 r2, u64 flags, u64 r4, u64 size)struct pt_regs *regs = (struct pt_regs *) (long ) r1;struct bpf_map *map = (struct bpf_map *) (long ) r2; struct bpf_array *array = container_of(map, struct bpf_array, map); +unsigned int cpu =smp_processor_id ();u64 index = flags &BPF_F_INDEX_MASK ;void *data = (void *) (long ) r4;struct perf_sample_data sample_data;
@@ -246,7 +247,7 @@ static u64 bpf_perf_event_output(u64 r1, u64 r2, u64 flags, u64 r4, u64 size)if (unlikely (flags & ~(BPF_F_INDEX_MASK )))return -EINVAL ;if (index ==BPF_F_CURRENT_CPU ) - index =raw_smp_processor_id (); + index = cpu;if (unlikely (index >= array->map.max_entries))return -E2BIG ;
@@ -259,7 +260,7 @@ static u64 bpf_perf_event_output(u64 r1, u64 r2, u64 flags, u64 r4, u64 size) event->attr.config != PERF_COUNT_SW_BPF_OUTPUT))return -EINVAL ; -if (unlikely (event->oncpu !=smp_processor_id ())) +if (unlikely (event->oncpu != cpu))return -EOPNOTSUPP ;perf_sample_data_init (&sample_data,0 ,0 );
Signature · 1 line
1.9.3
Daniel Borkmann
maintainerPATCH 3/6 bpf, trace: add BPF_F_CURRENT_CPU flag for bpf_perf_event_read
In reply to Daniel BorkmannFollow-up commit to 1e33759c788c ("bpf, trace: add BPF_F_CURRENT_CPU flag for bpf_perf_event_output") to add the same functionality into bpf_perf_event_read() helper. The split of index into flags and index component is also safe here, since such large maps are rejected during map allocation time.
--- include/uapi/linux/bpf.h | 2 +- kernel/trace/bpf_trace.c | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-)
include/uapi/linux/bpf.h +1 -1
@@ -347,7 +347,7 @@ enum bpf_func_id {#define BPF_F_ZERO_CSUM_TX (1ULL << 1)#define BPF_F_DONT_FRAGMENT (1ULL << 2) -/* BPF_FUNC_perf_event_output flags. */ +/* BPF_FUNC_perf_event_output and BPF_FUNC_perf_event_read flags. */ #define BPF_F_INDEX_MASK 0xffffffffULL#define BPF_F_CURRENT_CPU BPF_F_INDEX_MASK
kernel/trace/bpf_trace.c +8 -3
@@ -188,13 +188,19 @@ const struct bpf_func_proto *bpf_get_trace_printk_proto(void)return &bpf_trace_printk_proto; } -static u64 bpf_perf_event_read (u64 r1,u64 index,u64 r3,u64 r4,u64 r5) +static u64 bpf_perf_event_read (u64 r1,u64 flags,u64 r3,u64 r4,u64 r5) {struct bpf_map *map = (struct bpf_map *) (unsigned long ) r1; struct bpf_array *array = container_of(map, struct bpf_array, map); +unsigned int cpu =smp_processor_id (); +u64 index = flags &BPF_F_INDEX_MASK ;struct bpf_event_entry *ee;struct perf_event *event; +if (unlikely (flags & ~(BPF_F_INDEX_MASK ))) +return -EINVAL ; +if (index ==BPF_F_CURRENT_CPU ) + index = cpu;if (unlikely (index >= array->map.max_entries))return -E2BIG ;
@@ -208,8 +214,7 @@ static u64 bpf_perf_event_read(u64 r1, u64 index, u64 r3, u64 r4, u64 r5)return -EINVAL ;/* make sure event is local and doesn't have pmu::count */ -if (event->oncpu !=smp_processor_id () || - event->pmu->count) +if (unlikely (event->oncpu != cpu || event->pmu->count))return -EINVAL ;/*
Signature · 1 line
1.9.3
Daniel Borkmann
maintainerPATCH 4/6 bpf: don't use raw processor id in generic helper
In reply to Daniel BorkmannUse smp_processor_id() for the generic helper bpf_get_smp_processor_id() instead of the raw variant. This allows for preemption checks when we have DEBUG_PREEMPT, and otherwise uses the raw variant anyway. We only need to keep the raw variant for socket filters, but we can reuse the helper that is already there from cBPF side.
--- kernel/bpf/helpers.c | 2 +- net/core/filter.c | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-)
kernel/bpf/helpers.c +1 -1
@@ -101,7 +101,7 @@ const struct bpf_func_proto bpf_get_prandom_u32_proto = {static u64 bpf_get_smp_processor_id (u64 r1,u64 r2,u64 r3,u64 r4,u64 r5) { -return raw_smp_processor_id (); +return smp_processor_id (); }const struct bpf_func_proto bpf_get_smp_processor_id_proto = {
net/core/filter.c +9 -1
@@ -150,6 +150,12 @@ static u64 __get_raw_cpu_id(u64 ctx, u64 a, u64 x, u64 r4, u64 r5)return raw_smp_processor_id (); } +static const struct bpf_func_proto bpf_get_raw_smp_processor_id_proto = { + .func = __get_raw_cpu_id, + .gpl_only = false, + .ret_type =RET_INTEGER , +}; +static u32 convert_skb_access (int skb_field,int dst_reg,int src_reg,struct bpf_insn *insn_buf) {
@@ -2037,7 +2043,7 @@ sk_filter_func_proto(enum bpf_func_id func_id)case BPF_FUNC_get_prandom_u32:return &bpf_get_prandom_u32_proto;case BPF_FUNC_get_smp_processor_id: -return &bpf_get_smp_processor_id_proto; +return &bpf_get_raw_smp_processor_id_proto;case BPF_FUNC_tail_call:return &bpf_tail_call_proto;case BPF_FUNC_ktime_get_ns:
@@ -2086,6 +2092,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id)return &bpf_get_route_realm_proto;case BPF_FUNC_perf_event_output:return bpf_get_event_output_proto (); +case BPF_FUNC_get_smp_processor_id: +return &bpf_get_smp_processor_id_proto;default :return sk_filter_func_proto (func_id); }
Signature · 1 line
1.9.3
Daniel Borkmann
maintainerPATCH 5/6 bpf: add bpf_skb_change_proto helper
In reply to Daniel BorkmannThis patch adds a minimal helper for doing the groundwork of changing the skb->protocol in a controlled way. Currently supported is v4 to v6 and vice versa transitions, which allows f.e. for a minimal, static nat64 implementation where applications in containers that still require IPv4 can be transparently operated in an IPv6-only environment. For example, host facing veth of the container can transparently do the transitions in a programmatic way with the help of clsact qdisc and cls_bpf.
Idea is to separate concerns for keeping complexity of the helper lower, which means that the programs utilize bpf_skb_change_proto(), bpf_skb_store_bytes() and bpf_lX_csum_replace() to get the job done, instead of doing everything in a single helper (and thus partially duplicating helper functionality). Also, bpf_skb_change_proto() shouldn't need to deal with raw packet data as this is done by other helpers.
bpf_skb_proto_6_to_4() and bpf_skb_proto_4_to_6() unclone the skb to operate on a private one, push or pop additionally required header space and migrate the gso/gro meta data from the shared info. We do mark the gso type as dodgy so that headers are checked and segs recalculated by the gso/gro engine. The gso_size target is adapted as well. The flags argument added is currently reserved and can be used for future extensions.
--- include/uapi/linux/bpf.h | 14 ++++ net/core/filter.c | 200 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 214 insertions(+)
include/uapi/linux/bpf.h +14 -0
@@ -313,6 +313,20 @@ enum bpf_func_id {*/ BPF_FUNC_skb_get_tunnel_opt, BPF_FUNC_skb_set_tunnel_opt, + +/** +* bpf_skb_change_proto(skb, proto, flags) +* Change protocol of the skb. Currently supported is +* v4 -> v6, v6 -> v4 transitions. The helper will also +* resize the skb. eBPF program is expected to fill the +* new headers via skb_store_bytes and lX_csum_replace. +* @skb: pointer to skb +* @proto: new skb->protocol type +* @flags: reserved +* Return: 0 on success or negative error +*/ + BPF_FUNC_skb_change_proto, + __BPF_FUNC_MAX_ID, };
net/core/filter.c +200 -0
218 lines not on this page · raw message
Signature · 1 line
1.9.3
Daniel Borkmann
maintainerPATCH 6/6 bpf: add bpf_skb_change_type helper
In reply to Daniel BorkmannThis work adds a helper for changing skb->pkt_type in a controlled way. We only allow a subset of possible values and can extend that in future should other use cases come up. Doing this as a helper has the advantage that errors can be handeled gracefully and thus helper kept extensible.
It's a write counterpart to pkt_type member we can already read from struct __sk_buff context. Major use case is to change incoming skbs to PACKET_HOST in a programmatic way instead of having to recirculate via redirect(..., BPF_F_INGRESS), for example.
--- include/uapi/linux/bpf.h | 9 +++++++++ net/core/filter.c | 24 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+)
include/uapi/linux/bpf.h +9 -0
@@ -327,6 +327,15 @@ enum bpf_func_id {*/ BPF_FUNC_skb_change_proto, +/** +* bpf_skb_change_type(skb, type) +* Change packet type of skb. +* @skb: pointer to skb +* @type: new skb->pkt_type type +* Return: 0 on success or negative error +*/ + BPF_FUNC_skb_change_type, + __BPF_FUNC_MAX_ID, };
net/core/filter.c +24 -0
@@ -1979,6 +1979,28 @@ static const struct bpf_func_proto bpf_skb_change_proto_proto = { .arg3_type = ARG_ANYTHING, }; +static u64 bpf_skb_change_type (u64 r1,u64 r2,u64 r3,u64 r4,u64 r5) +{ +struct sk_buff *skb = (struct sk_buff *) (long ) r1; +u32 pkt_type = r2; + +/* We only allow a restricted subset to be changed for now. */ +if (unlikely (skb->pkt_type >PACKET_OTHERHOST || + pkt_type >PACKET_OTHERHOST )) +return -EINVAL ; + + skb->pkt_type = pkt_type; +return 0 ; +} + +static const struct bpf_func_proto bpf_skb_change_type_proto = { + .func = bpf_skb_change_type, + .gpl_only = false, + .ret_type =RET_INTEGER , + .arg1_type =ARG_PTR_TO_CTX , + .arg2_type =ARG_ANYTHING , +}; +bool bpf_helper_changes_skb_data (void *func) {if (func == bpf_skb_vlan_push)
@@ -2278,6 +2300,8 @@ tc_cls_act_func_proto(enum bpf_func_id func_id)return &bpf_skb_vlan_pop_proto;case BPF_FUNC_skb_change_proto:return &bpf_skb_change_proto_proto; +case BPF_FUNC_skb_change_type: +return &bpf_skb_change_type_proto;case BPF_FUNC_skb_get_tunnel_key:return &bpf_skb_get_tunnel_key_proto;case BPF_FUNC_skb_set_tunnel_key:
Signature · 1 line
1.9.3
David Miller
maintainerFrom: Daniel Borkmann <[email protected]>
Date: Tue, 28 Jun 2016 12:18:22 +0200
Quoted 7 lines
This set adds various BPF helper improvements, that is, cleaning up and adding
BPF_F_CURRENT_CPUflag for tracing helper, allowing for preemption checks onbpf_get_smp_processor_id()helper, and adding two new helpers bpf_skb_change_{proto, type} for tc related programs. For further details please see individual patches.Note, this set requires -net to be merged into -net-next tree first.
Series applied, thanks.