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: Use kallsyms to find the function name of a struct_ops's stub function

In commit 1611603537a4 ("bpf: Create argument information for nullable arguments."),
it introduced a "__nullable" tagging at the argument name of a
stub function. Some background on the commit:
it requires to tag the stub function instead of directly tagging
the "ops" of a struct. This is because the btf func_proto of the "ops"
does not have the argument name and the "__nullable" is tagged at
the argument name.

To find the stub function of a "ops", it currently relies on a naming
convention on the stub function "st_ops__ops_name".
e.g. tcp_congestion_ops__ssthresh. However, the new kernel
sub system implementing bpf_struct_ops have missed this and
have been surprised that the "__nullable" and the to-be-landed
"__ref" tagging was not effective.

One option would be to give a warning whenever the stub function does
not follow the naming convention, regardless if it requires arg tagging
or not.

Instead, this patch uses the kallsyms_lookup approach and removes
the requirement on the naming convention. The st_ops->cfi_stubs has
all the stub function kernel addresses. kallsyms_lookup() is used to
lookup the function name. With the function name, BTF can be used to
find the BTF func_proto. The existing "__nullable" arg name searching
logic will then fall through.

One notable change is,
if it failed in kallsyms_lookup or it failed in looking up the stub
function name from the BTF, the bpf_struct_ops registration will fail.
This is different from the previous behavior that it silently ignored
the "st_ops__ops_name" function not found error.

The "tcp_congestion_ops", "sched_ext_ops", and "hid_bpf_ops" can still be
registered successfully after this patch. There is struct_ops_maybe_null
selftest to cover the "__nullable" tagging.

Other minor changes:
1. Removed the "%s__%s" format from the pr_warn because the naming
convention is removed.
2. The existing bpf_struct_ops_supported() is also moved earlier
because prepare_arg_info needs to use it to decide if the
stub function is NULL before calling the prepare_arg_info.

Cc: Tejun Heo <tj@kernel.org>
Cc: Benjamin Tissoires <bentiss@kernel.org>
Cc: Yonghong Song <yonghong.song@linux.dev>
Cc: Amery Hung <ameryhung@gmail.com>
Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
Reviewed-by: Amery Hung <ameryhung@gmail.com>
Link: https://lore.kernel.org/r/20250127222719.2544255-1-martin.lau@linux.dev
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Martin KaFai Lau and committed by
Alexei Starovoitov
12fdd29d 0053f7d3

+44 -54
+44 -54
kernel/bpf/bpf_struct_ops.c
··· 146 146 } 147 147 148 148 #define MAYBE_NULL_SUFFIX "__nullable" 149 - #define MAX_STUB_NAME 128 150 - 151 - /* Return the type info of a stub function, if it exists. 152 - * 153 - * The name of a stub function is made up of the name of the struct_ops and 154 - * the name of the function pointer member, separated by "__". For example, 155 - * if the struct_ops type is named "foo_ops" and the function pointer 156 - * member is named "bar", the stub function name would be "foo_ops__bar". 157 - */ 158 - static const struct btf_type * 159 - find_stub_func_proto(const struct btf *btf, const char *st_op_name, 160 - const char *member_name) 161 - { 162 - char stub_func_name[MAX_STUB_NAME]; 163 - const struct btf_type *func_type; 164 - s32 btf_id; 165 - int cp; 166 - 167 - cp = snprintf(stub_func_name, MAX_STUB_NAME, "%s__%s", 168 - st_op_name, member_name); 169 - if (cp >= MAX_STUB_NAME) { 170 - pr_warn("Stub function name too long\n"); 171 - return NULL; 172 - } 173 - btf_id = btf_find_by_name_kind(btf, stub_func_name, BTF_KIND_FUNC); 174 - if (btf_id < 0) 175 - return NULL; 176 - func_type = btf_type_by_id(btf, btf_id); 177 - if (!func_type) 178 - return NULL; 179 - 180 - return btf_type_by_id(btf, func_type->type); /* FUNC_PROTO */ 181 - } 182 149 183 150 /* Prepare argument info for every nullable argument of a member of a 184 151 * struct_ops type. ··· 170 203 static int prepare_arg_info(struct btf *btf, 171 204 const char *st_ops_name, 172 205 const char *member_name, 173 - const struct btf_type *func_proto, 206 + const struct btf_type *func_proto, void *stub_func_addr, 174 207 struct bpf_struct_ops_arg_info *arg_info) 175 208 { 176 209 const struct btf_type *stub_func_proto, *pointed_type; 177 210 const struct btf_param *stub_args, *args; 178 211 struct bpf_ctx_arg_aux *info, *info_buf; 179 212 u32 nargs, arg_no, info_cnt = 0; 213 + char ksym[KSYM_SYMBOL_LEN]; 214 + const char *stub_fname; 215 + s32 stub_func_id; 180 216 u32 arg_btf_id; 181 217 int offset; 182 218 183 - stub_func_proto = find_stub_func_proto(btf, st_ops_name, member_name); 184 - if (!stub_func_proto) 185 - return 0; 219 + stub_fname = kallsyms_lookup((unsigned long)stub_func_addr, NULL, NULL, NULL, ksym); 220 + if (!stub_fname) { 221 + pr_warn("Cannot find the stub function name for the %s in struct %s\n", 222 + member_name, st_ops_name); 223 + return -ENOENT; 224 + } 225 + 226 + stub_func_id = btf_find_by_name_kind(btf, stub_fname, BTF_KIND_FUNC); 227 + if (stub_func_id < 0) { 228 + pr_warn("Cannot find the stub function %s in btf\n", stub_fname); 229 + return -ENOENT; 230 + } 231 + 232 + stub_func_proto = btf_type_by_id(btf, stub_func_id); 233 + stub_func_proto = btf_type_by_id(btf, stub_func_proto->type); 186 234 187 235 /* Check if the number of arguments of the stub function is the same 188 236 * as the number of arguments of the function pointer. 189 237 */ 190 238 nargs = btf_type_vlen(func_proto); 191 239 if (nargs != btf_type_vlen(stub_func_proto)) { 192 - pr_warn("the number of arguments of the stub function %s__%s does not match the number of arguments of the member %s of struct %s\n", 193 - st_ops_name, member_name, member_name, st_ops_name); 240 + pr_warn("the number of arguments of the stub function %s does not match the number of arguments of the member %s of struct %s\n", 241 + stub_fname, member_name, st_ops_name); 194 242 return -EINVAL; 195 243 } 196 244 ··· 235 253 &arg_btf_id); 236 254 if (!pointed_type || 237 255 !btf_type_is_struct(pointed_type)) { 238 - pr_warn("stub function %s__%s has %s tagging to an unsupported type\n", 239 - st_ops_name, member_name, MAYBE_NULL_SUFFIX); 256 + pr_warn("stub function %s has %s tagging to an unsupported type\n", 257 + stub_fname, MAYBE_NULL_SUFFIX); 240 258 goto err_out; 241 259 } 242 260 243 261 offset = btf_ctx_arg_offset(btf, func_proto, arg_no); 244 262 if (offset < 0) { 245 - pr_warn("stub function %s__%s has an invalid trampoline ctx offset for arg#%u\n", 246 - st_ops_name, member_name, arg_no); 263 + pr_warn("stub function %s has an invalid trampoline ctx offset for arg#%u\n", 264 + stub_fname, arg_no); 247 265 goto err_out; 248 266 } 249 267 250 268 if (args[arg_no].type != stub_args[arg_no].type) { 251 - pr_warn("arg#%u type in stub function %s__%s does not match with its original func_proto\n", 252 - arg_no, st_ops_name, member_name); 269 + pr_warn("arg#%u type in stub function %s does not match with its original func_proto\n", 270 + arg_no, stub_fname); 253 271 goto err_out; 254 272 } 255 273 ··· 304 322 return false; 305 323 306 324 return !strcmp(btf_name_by_offset(btf, t->name_off), "module"); 325 + } 326 + 327 + int bpf_struct_ops_supported(const struct bpf_struct_ops *st_ops, u32 moff) 328 + { 329 + void *func_ptr = *(void **)(st_ops->cfi_stubs + moff); 330 + 331 + return func_ptr ? 0 : -ENOTSUPP; 307 332 } 308 333 309 334 int bpf_struct_ops_desc_init(struct bpf_struct_ops_desc *st_ops_desc, ··· 376 387 377 388 for_each_member(i, t, member) { 378 389 const struct btf_type *func_proto; 390 + void **stub_func_addr; 391 + u32 moff; 379 392 393 + moff = __btf_member_bit_offset(t, member) / 8; 380 394 mname = btf_name_by_offset(btf, member->name_off); 381 395 if (!*mname) { 382 396 pr_warn("anon member in struct %s is not supported\n", ··· 405 413 func_proto = btf_type_resolve_func_ptr(btf, 406 414 member->type, 407 415 NULL); 408 - if (!func_proto) 416 + 417 + /* The member is not a function pointer or 418 + * the function pointer is not supported. 419 + */ 420 + if (!func_proto || bpf_struct_ops_supported(st_ops, moff)) 409 421 continue; 410 422 411 423 if (btf_distill_func_proto(log, btf, ··· 421 425 goto errout; 422 426 } 423 427 428 + stub_func_addr = *(void **)(st_ops->cfi_stubs + moff); 424 429 err = prepare_arg_info(btf, st_ops->name, mname, 425 - func_proto, 430 + func_proto, stub_func_addr, 426 431 arg_info + i); 427 432 if (err) 428 433 goto errout; ··· 1147 1150 st_map = container_of(kvalue, struct bpf_struct_ops_map, kvalue); 1148 1151 1149 1152 bpf_map_put(&st_map->map); 1150 - } 1151 - 1152 - int bpf_struct_ops_supported(const struct bpf_struct_ops *st_ops, u32 moff) 1153 - { 1154 - void *func_ptr = *(void **)(st_ops->cfi_stubs + moff); 1155 - 1156 - return func_ptr ? 0 : -ENOTSUPP; 1157 1153 } 1158 1154 1159 1155 static bool bpf_struct_ops_valid_to_reg(struct bpf_map *map)