Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

bpf: net_sched: Add basic bpf qdisc kfuncs

Add basic kfuncs for working on skb in qdisc.

Both bpf_qdisc_skb_drop() and bpf_kfree_skb() can be used to release
a reference to an skb. However, bpf_qdisc_skb_drop() can only be called
in .enqueue where a to_free skb list is available from kernel to defer
the release. bpf_kfree_skb() should be used elsewhere. It is also used
in bpf_obj_free_fields() when cleaning up skb in maps and collections.

bpf_skb_get_hash() returns the flow hash of an skb, which can be used
to build flow-based queueing algorithms.

Finally, allow users to create read-only dynptr via bpf_dynptr_from_skb().

Signed-off-by: Amery Hung <amery.hung@bytedance.com>
Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
Acked-by: Toke Høiland-Jørgensen <toke@redhat.com>
Link: https://patch.msgid.link/20250409214606.2000194-4-ameryhung@gmail.com

authored by

Amery Hung and committed by
Martin KaFai Lau
870c2858 c8240344

+110 -1
+110 -1
net/sched/bpf_qdisc.c
··· 8 8 #include <net/pkt_sched.h> 9 9 #include <net/pkt_cls.h> 10 10 11 + #define QDISC_OP_IDX(op) (offsetof(struct Qdisc_ops, op) / sizeof(void (*)(void))) 12 + #define QDISC_MOFF_IDX(moff) (moff / sizeof(void (*)(void))) 13 + 11 14 static struct bpf_struct_ops bpf_Qdisc_ops; 12 15 13 16 struct bpf_sk_buff_ptr { ··· 126 123 return 0; 127 124 } 128 125 126 + __bpf_kfunc_start_defs(); 127 + 128 + /* bpf_skb_get_hash - Get the flow hash of an skb. 129 + * @skb: The skb to get the flow hash from. 130 + */ 131 + __bpf_kfunc u32 bpf_skb_get_hash(struct sk_buff *skb) 132 + { 133 + return skb_get_hash(skb); 134 + } 135 + 136 + /* bpf_kfree_skb - Release an skb's reference and drop it immediately. 137 + * @skb: The skb whose reference to be released and dropped. 138 + */ 139 + __bpf_kfunc void bpf_kfree_skb(struct sk_buff *skb) 140 + { 141 + kfree_skb(skb); 142 + } 143 + 144 + /* bpf_qdisc_skb_drop - Drop an skb by adding it to a deferred free list. 145 + * @skb: The skb whose reference to be released and dropped. 146 + * @to_free_list: The list of skbs to be dropped. 147 + */ 148 + __bpf_kfunc void bpf_qdisc_skb_drop(struct sk_buff *skb, 149 + struct bpf_sk_buff_ptr *to_free_list) 150 + { 151 + __qdisc_drop(skb, (struct sk_buff **)to_free_list); 152 + } 153 + 154 + __bpf_kfunc_end_defs(); 155 + 156 + BTF_KFUNCS_START(qdisc_kfunc_ids) 157 + BTF_ID_FLAGS(func, bpf_skb_get_hash, KF_TRUSTED_ARGS) 158 + BTF_ID_FLAGS(func, bpf_kfree_skb, KF_RELEASE) 159 + BTF_ID_FLAGS(func, bpf_qdisc_skb_drop, KF_RELEASE) 160 + BTF_ID_FLAGS(func, bpf_dynptr_from_skb, KF_TRUSTED_ARGS) 161 + BTF_KFUNCS_END(qdisc_kfunc_ids) 162 + 163 + BTF_SET_START(qdisc_common_kfunc_set) 164 + BTF_ID(func, bpf_skb_get_hash) 165 + BTF_ID(func, bpf_kfree_skb) 166 + BTF_ID(func, bpf_dynptr_from_skb) 167 + BTF_SET_END(qdisc_common_kfunc_set) 168 + 169 + BTF_SET_START(qdisc_enqueue_kfunc_set) 170 + BTF_ID(func, bpf_qdisc_skb_drop) 171 + BTF_SET_END(qdisc_enqueue_kfunc_set) 172 + 173 + enum qdisc_ops_kf_flags { 174 + QDISC_OPS_KF_COMMON = 0, 175 + QDISC_OPS_KF_ENQUEUE = 1 << 0, 176 + }; 177 + 178 + static const u32 qdisc_ops_context_flags[] = { 179 + [QDISC_OP_IDX(enqueue)] = QDISC_OPS_KF_ENQUEUE, 180 + [QDISC_OP_IDX(dequeue)] = QDISC_OPS_KF_COMMON, 181 + [QDISC_OP_IDX(init)] = QDISC_OPS_KF_COMMON, 182 + [QDISC_OP_IDX(reset)] = QDISC_OPS_KF_COMMON, 183 + [QDISC_OP_IDX(destroy)] = QDISC_OPS_KF_COMMON, 184 + }; 185 + 186 + static int bpf_qdisc_kfunc_filter(const struct bpf_prog *prog, u32 kfunc_id) 187 + { 188 + u32 moff, flags; 189 + 190 + if (!btf_id_set8_contains(&qdisc_kfunc_ids, kfunc_id)) 191 + return 0; 192 + 193 + if (prog->aux->st_ops != &bpf_Qdisc_ops) 194 + return -EACCES; 195 + 196 + moff = prog->aux->attach_st_ops_member_off; 197 + flags = qdisc_ops_context_flags[QDISC_MOFF_IDX(moff)]; 198 + 199 + if ((flags & QDISC_OPS_KF_ENQUEUE) && 200 + btf_id_set_contains(&qdisc_enqueue_kfunc_set, kfunc_id)) 201 + return 0; 202 + 203 + if (btf_id_set_contains(&qdisc_common_kfunc_set, kfunc_id)) 204 + return 0; 205 + 206 + return -EACCES; 207 + } 208 + 209 + static const struct btf_kfunc_id_set bpf_qdisc_kfunc_set = { 210 + .owner = THIS_MODULE, 211 + .set = &qdisc_kfunc_ids, 212 + .filter = bpf_qdisc_kfunc_filter, 213 + }; 214 + 129 215 static const struct bpf_verifier_ops bpf_qdisc_verifier_ops = { 130 216 .get_func_proto = bpf_base_func_proto, 131 217 .is_valid_access = bpf_qdisc_is_valid_access, ··· 301 209 .owner = THIS_MODULE, 302 210 }; 303 211 212 + BTF_ID_LIST(bpf_sk_buff_dtor_ids) 213 + BTF_ID(func, bpf_kfree_skb) 214 + 304 215 static int __init bpf_qdisc_kfunc_init(void) 305 216 { 306 - return register_bpf_struct_ops(&bpf_Qdisc_ops, Qdisc_ops); 217 + int ret; 218 + const struct btf_id_dtor_kfunc skb_kfunc_dtors[] = { 219 + { 220 + .btf_id = bpf_sk_buff_ids[0], 221 + .kfunc_btf_id = bpf_sk_buff_dtor_ids[0] 222 + }, 223 + }; 224 + 225 + ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS, &bpf_qdisc_kfunc_set); 226 + ret = ret ?: register_btf_id_dtor_kfuncs(skb_kfunc_dtors, 227 + ARRAY_SIZE(skb_kfunc_dtors), 228 + THIS_MODULE); 229 + ret = ret ?: register_bpf_struct_ops(&bpf_Qdisc_ops, Qdisc_ops); 230 + 231 + return ret; 307 232 } 308 233 late_initcall(bpf_qdisc_kfunc_init);