This patch allows the bpf-tcp-cc to call bpf_setsockopt. One use case is to allow a bpf-tcp-cc switching to another cc during init(). For example, when the tcp flow is not ecn ready, the bpf_dctcp can switch to another cc by calling setsockopt(TCP_CONGESTION). During setsockopt(TCP_CONGESTION), the new tcp-cc's init() will be called and this could cause a recursion but it is stopped by the current trampoline's logic (in the prog->active counter). While retiring a bpf-tcp-cc (e.g. in tcp_v[46]_destroy_sock()), the tcp stack calls bpf-tcp-cc's release(). To avoid the retiring bpf-tcp-cc making further changes to the sk, bpf_setsockopt is not available to the bpf-tcp-cc's release(). This will avoid release() making setsockopt() call that will potentially allocate new resources. Although the bpf-tcp-cc already has a more powerful way to read tcp_sock from the PTR_TO_BTF_ID, it is usually expected that bpf_getsockopt and bpf_setsockopt are available together. Thus, bpf_getsockopt() is also added to all tcp_congestion_ops except release(). When the old bpf-tcp-cc is calling setsockopt(TCP_CONGESTION) to switch to a new cc, the old bpf-tcp-cc will be released by bpf_struct_ops_put(). Thus, this patch also puts the bpf_struct_ops_map after a rcu grace period because the trampoline's image cannot be freed while the old bpf-tcp-cc is still running. bpf-tcp-cc can only access icsk_ca_priv as SCALAR. All kernel's tcp-cc is also accessing the icsk_ca_priv as SCALAR. The size of icsk_ca_priv has already been raised a few times to avoid extra kmalloc and memory referencing. The only exception is the kernel's tcp_cdg.c that stores a kmalloc()-ed pointer in icsk_ca_priv. To avoid the old bpf-tcp-cc accidentally overriding this tcp_cdg's pointer value stored in icsk_ca_priv after switching and without over-complicating the bpf's verifier for this one exception in tcp_cdg, this patch does not allow switching to tcp_cdg. If there is a need, bpf_tcp_cdg can be implemented and then use the bpf_sk_storage as the extended storage. bpf_sk_setsockopt proto has only been recently added and used in bpf-sockopt and bpf-iter-tcp, so impose the tcp_cdg limitation in the same proto instead of adding a new proto specifically for bpf-tcp-cc.
Showing the changes to kernel (1 of 3 files). Show all
Unified · Split
| @@ -28,6 +28,7 @@ struct bpf_struct_ops_value { · 27 unmodified lines | |||
| 28 | 28 | ||
| 29 | struct bpf_struct_ops_map { | 29 | struct bpf_struct_ops_map { |
| 30 | struct bpf_map map; | 30 | struct bpf_map map; |
| 31 | struct rcu_head rcu; | ||
| 31 | const struct bpf_struct_ops *st_ops; | 32 | const struct bpf_struct_ops *st_ops; |
| 32 | /* protect map_update */ | 33 | /* protect map_update */ |
| 33 | struct mutex lock; | 34 | struct mutex lock; |
| @@ -622,6 +623,14 @@ bool bpf_struct_ops_get(const void *kdata) · 588 unmodified lines | |||
| 622 | return refcount_inc_not_zero(&kvalue->refcnt); | 623 | return refcount_inc_not_zero(&kvalue->refcnt); |
| 623 | } | 624 | } |
| 624 | 625 | ||
| 626 | static void bpf_struct_ops_put_rcu(struct rcu_head *head) | ||
| 627 | { | ||
| 628 | struct bpf_struct_ops_map *st_map; | ||
| 629 | |||
| 630 | st_map = container_of(head, struct bpf_struct_ops_map, rcu); | ||
| 631 | bpf_map_put(&st_map->map); | ||
| 632 | } | ||
| 633 | |||
| 625 | void bpf_struct_ops_put(const void *kdata) | 634 | void bpf_struct_ops_put(const void *kdata) |
| 626 | { | 635 | { |
| 627 | struct bpf_struct_ops_value *kvalue; | 636 | struct bpf_struct_ops_value *kvalue; |
| @@ -632,6 +641,17 @@ void bpf_struct_ops_put(const void *kdata) · 4 unmodified lines | |||
| 632 | 641 | ||
| 633 | st_map = container_of(kvalue, struct bpf_struct_ops_map, | 642 | st_map = container_of(kvalue, struct bpf_struct_ops_map, |
| 634 | kvalue); | 643 | kvalue); |
| 635 | bpf_map_put(&st_map->map); | 644 | /* The struct_ops's function may switch to another struct_ops. |
| 645 | * | ||
| 646 | * For example, bpf_tcp_cc_x->init() may switch to | ||
| 647 | * another tcp_cc_y by calling | ||
| 648 | * setsockopt(TCP_CONGESTION, "tcp_cc_y"). | ||
| 649 | * During the switch, bpf_struct_ops_put(tcp_cc_x) is called | ||
| 650 | * and its map->refcnt may reach 0 which then free its | ||
| 651 | * trampoline image while tcp_cc_x is still running. | ||
| 652 | * | ||
| 653 | * Thus, a rcu grace period is needed here. | ||
| 654 | */ | ||
| 655 | call_rcu(&st_map->rcu, bpf_struct_ops_put_rcu); | ||
| 636 | } | 656 | } |
| 637 | } | 657 | } |