Skip to content

Commit d1542d4

Browse files
Sebastian Andrzej Siewiorkuba-moo
authored andcommitted
seg6: Use nested-BH locking for seg6_bpf_srh_states.
The access to seg6_bpf_srh_states is protected by disabling preemption. Based on the code, the entry point is input_action_end_bpf() and every other function (the bpf helper functions bpf_lwt_seg6_*()), that is accessing seg6_bpf_srh_states, should be called from within input_action_end_bpf(). input_action_end_bpf() accesses seg6_bpf_srh_states first at the top of the function and then disables preemption. This looks wrong because if preemption needs to be disabled as part of the locking mechanism then the variable shouldn't be accessed beforehand. Looking at how it is used via test_lwt_seg6local.sh then input_action_end_bpf() is always invoked from softirq context. If this is always the case then the preempt_disable() statement is superfluous. If this is not always invoked from softirq then disabling only preemption is not sufficient. Replace the preempt_disable() statement with nested-BH locking. This is not an equivalent replacement as it assumes that the invocation of input_action_end_bpf() always occurs in softirq context and thus the preempt_disable() is superfluous. Add a local_lock_t the data structure and use local_lock_nested_bh() for locking. Add lockdep_assert_held() to ensure the lock is held while the per-CPU variable is referenced in the helper functions. Cc: Alexei Starovoitov <[email protected]> Cc: Andrii Nakryiko <[email protected]> Cc: David Ahern <[email protected]> Cc: Hao Luo <[email protected]> Cc: Jiri Olsa <[email protected]> Cc: John Fastabend <[email protected]> Cc: KP Singh <[email protected]> Cc: Martin KaFai Lau <[email protected]> Cc: Song Liu <[email protected]> Cc: Stanislav Fomichev <[email protected]> Cc: Yonghong Song <[email protected]> Signed-off-by: Sebastian Andrzej Siewior <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 3414adb commit d1542d4

File tree

3 files changed

+18
-8
lines changed

3 files changed

+18
-8
lines changed

include/net/seg6_local.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ extern int seg6_lookup_nexthop(struct sk_buff *skb, struct in6_addr *nhaddr,
1919
extern bool seg6_bpf_has_valid_srh(struct sk_buff *skb);
2020

2121
struct seg6_bpf_srh_state {
22+
local_lock_t bh_lock;
2223
struct ipv6_sr_hdr *srh;
2324
u16 hdrlen;
2425
bool valid;

net/core/filter.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6455,6 +6455,7 @@ BPF_CALL_4(bpf_lwt_seg6_store_bytes, struct sk_buff *, skb, u32, offset,
64556455
void *srh_tlvs, *srh_end, *ptr;
64566456
int srhoff = 0;
64576457

6458+
lockdep_assert_held(&srh_state->bh_lock);
64586459
if (srh == NULL)
64596460
return -EINVAL;
64606461

@@ -6511,6 +6512,7 @@ BPF_CALL_4(bpf_lwt_seg6_action, struct sk_buff *, skb,
65116512
int hdroff = 0;
65126513
int err;
65136514

6515+
lockdep_assert_held(&srh_state->bh_lock);
65146516
switch (action) {
65156517
case SEG6_LOCAL_ACTION_END_X:
65166518
if (!seg6_bpf_has_valid_srh(skb))
@@ -6587,6 +6589,7 @@ BPF_CALL_3(bpf_lwt_seg6_adjust_srh, struct sk_buff *, skb, u32, offset,
65876589
int srhoff = 0;
65886590
int ret;
65896591

6592+
lockdep_assert_held(&srh_state->bh_lock);
65906593
if (unlikely(srh == NULL))
65916594
return -EINVAL;
65926595

net/ipv6/seg6_local.c

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1380,14 +1380,17 @@ static int input_action_end_b6_encap(struct sk_buff *skb,
13801380
return err;
13811381
}
13821382

1383-
DEFINE_PER_CPU(struct seg6_bpf_srh_state, seg6_bpf_srh_states);
1383+
DEFINE_PER_CPU(struct seg6_bpf_srh_state, seg6_bpf_srh_states) = {
1384+
.bh_lock = INIT_LOCAL_LOCK(bh_lock),
1385+
};
13841386

13851387
bool seg6_bpf_has_valid_srh(struct sk_buff *skb)
13861388
{
13871389
struct seg6_bpf_srh_state *srh_state =
13881390
this_cpu_ptr(&seg6_bpf_srh_states);
13891391
struct ipv6_sr_hdr *srh = srh_state->srh;
13901392

1393+
lockdep_assert_held(&srh_state->bh_lock);
13911394
if (unlikely(srh == NULL))
13921395
return false;
13931396

@@ -1408,8 +1411,7 @@ bool seg6_bpf_has_valid_srh(struct sk_buff *skb)
14081411
static int input_action_end_bpf(struct sk_buff *skb,
14091412
struct seg6_local_lwt *slwt)
14101413
{
1411-
struct seg6_bpf_srh_state *srh_state =
1412-
this_cpu_ptr(&seg6_bpf_srh_states);
1414+
struct seg6_bpf_srh_state *srh_state;
14131415
struct ipv6_sr_hdr *srh;
14141416
int ret;
14151417

@@ -1420,10 +1422,14 @@ static int input_action_end_bpf(struct sk_buff *skb,
14201422
}
14211423
advance_nextseg(srh, &ipv6_hdr(skb)->daddr);
14221424

1423-
/* preempt_disable is needed to protect the per-CPU buffer srh_state,
1424-
* which is also accessed by the bpf_lwt_seg6_* helpers
1425+
/* The access to the per-CPU buffer srh_state is protected by running
1426+
* always in softirq context (with disabled BH). On PREEMPT_RT the
1427+
* required locking is provided by the following local_lock_nested_bh()
1428+
* statement. It is also accessed by the bpf_lwt_seg6_* helpers via
1429+
* bpf_prog_run_save_cb().
14251430
*/
1426-
preempt_disable();
1431+
local_lock_nested_bh(&seg6_bpf_srh_states.bh_lock);
1432+
srh_state = this_cpu_ptr(&seg6_bpf_srh_states);
14271433
srh_state->srh = srh;
14281434
srh_state->hdrlen = srh->hdrlen << 3;
14291435
srh_state->valid = true;
@@ -1446,15 +1452,15 @@ static int input_action_end_bpf(struct sk_buff *skb,
14461452

14471453
if (srh_state->srh && !seg6_bpf_has_valid_srh(skb))
14481454
goto drop;
1455+
local_unlock_nested_bh(&seg6_bpf_srh_states.bh_lock);
14491456

1450-
preempt_enable();
14511457
if (ret != BPF_REDIRECT)
14521458
seg6_lookup_nexthop(skb, NULL, 0);
14531459

14541460
return dst_input(skb);
14551461

14561462
drop:
1457-
preempt_enable();
1463+
local_unlock_nested_bh(&seg6_bpf_srh_states.bh_lock);
14581464
kfree_skb(skb);
14591465
return -EINVAL;
14601466
}

0 commit comments

Comments
 (0)