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

HID: bpf: rework how programs are attached and stored in the kernel

Previously, HID-BPF was relying on a bpf tracing program to be notified
when a program was released from userspace. This is error prone, as
LLVM sometimes inline the function and sometimes not.

So instead of messing up with the bpf prog ref count, we can use the
bpf_link concept which actually matches exactly what we want:
- a bpf_link represents the fact that a given program is attached to a
given HID device
- as long as the bpf_link has fd opened (either by the userspace program
still being around or by pinning the bpf object in the bpffs), the
program stays attached to the HID device
- once every user has closed the fd, we get called by
hid_bpf_link_release() that we no longer have any users, and we can
disconnect the program to the device in 2 passes: first atomically clear
the bit saying that the link is active, and then calling release_work in
a scheduled work item.

This solves entirely the problems of BPF tracing not showing up and is
definitely cleaner.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>

authored by

Benjamin Tissoires and committed by
Jiri Kosina
4b9a3f49 2574917a

+96 -69
+11 -1
Documentation/hid/hid-bpf.rst
··· 427 427 prog_fd = bpf_program__fd(hid_skel->progs.attach_prog); 428 428 429 429 err = bpf_prog_test_run_opts(prog_fd, &tattrs); 430 - return err; 430 + if (err) 431 + return err; 432 + 433 + return args.retval; /* the fd of the created bpf_link */ 431 434 } 432 435 433 436 Our userspace program can now listen to notifications on the ring buffer, and 434 437 is awaken only when the value changes. 438 + 439 + When the userspace program doesn't need to listen to events anymore, it can just 440 + close the returned fd from :c:func:`attach_filter`, which will tell the kernel to 441 + detach the program from the HID device. 442 + 443 + Of course, in other use cases, the userspace program can also pin the fd to the 444 + BPF filesystem through a call to :c:func:`bpf_obj_pin`, as with any bpf_link. 435 445 436 446 Controlling the device 437 447 ----------------------
+11 -7
drivers/hid/bpf/hid_bpf_dispatch.c
··· 249 249 * @prog_fd: an fd in the user process representing the program to attach 250 250 * @flags: any logical OR combination of &enum hid_bpf_attach_flags 251 251 * 252 - * @returns %0 on success, an error code otherwise. 252 + * @returns an fd of a bpf_link object on success (> %0), an error code otherwise. 253 + * Closing this fd will detach the program from the HID device (unless the bpf_link 254 + * is pinned to the BPF file system). 253 255 */ 254 256 /* called from syscall */ 255 257 noinline int ··· 259 257 { 260 258 struct hid_device *hdev; 261 259 struct device *dev; 262 - int err, prog_type = hid_bpf_get_prog_attach_type(prog_fd); 260 + int fd, err, prog_type = hid_bpf_get_prog_attach_type(prog_fd); 263 261 264 262 if (!hid_bpf_ops) 265 263 return -EINVAL; ··· 285 283 return err; 286 284 } 287 285 288 - err = __hid_bpf_attach_prog(hdev, prog_type, prog_fd, flags); 289 - if (err) 290 - return err; 286 + fd = __hid_bpf_attach_prog(hdev, prog_type, prog_fd, flags); 287 + if (fd < 0) 288 + return fd; 291 289 292 290 if (prog_type == HID_BPF_PROG_TYPE_RDESC_FIXUP) { 293 291 err = hid_bpf_reconnect(hdev); 294 - if (err) 292 + if (err) { 293 + close_fd(fd); 295 294 return err; 295 + } 296 296 } 297 297 298 - return 0; 298 + return fd; 299 299 } 300 300 301 301 /**
+67 -61
drivers/hid/bpf/hid_bpf_jmp_table.c
··· 322 322 if (err) 323 323 goto out; 324 324 325 - /* 326 - * The program has been safely inserted, decrement the reference count 327 - * so it doesn't interfere with the number of actual user handles. 328 - * This is safe to do because: 329 - * - we overrite the put_ptr in the prog fd map 330 - * - we also have a cleanup function that monitors when a program gets 331 - * released and we manually do the cleanup in the prog fd map 332 - */ 333 - bpf_prog_sub(prog, 1); 334 - 335 325 /* return the index */ 336 326 err = index; 337 327 ··· 355 365 return prog_type; 356 366 } 357 367 368 + static void hid_bpf_link_release(struct bpf_link *link) 369 + { 370 + struct hid_bpf_link *hid_link = 371 + container_of(link, struct hid_bpf_link, link); 372 + 373 + __clear_bit(hid_link->hid_table_index, jmp_table.enabled); 374 + schedule_work(&release_work); 375 + } 376 + 377 + static void hid_bpf_link_dealloc(struct bpf_link *link) 378 + { 379 + struct hid_bpf_link *hid_link = 380 + container_of(link, struct hid_bpf_link, link); 381 + 382 + kfree(hid_link); 383 + } 384 + 385 + static void hid_bpf_link_show_fdinfo(const struct bpf_link *link, 386 + struct seq_file *seq) 387 + { 388 + seq_printf(seq, 389 + "attach_type:\tHID-BPF\n"); 390 + } 391 + 392 + static const struct bpf_link_ops hid_bpf_link_lops = { 393 + .release = hid_bpf_link_release, 394 + .dealloc = hid_bpf_link_dealloc, 395 + .show_fdinfo = hid_bpf_link_show_fdinfo, 396 + }; 397 + 358 398 /* called from syscall */ 359 399 noinline int 360 400 __hid_bpf_attach_prog(struct hid_device *hdev, enum hid_bpf_prog_type prog_type, 361 401 int prog_fd, __u32 flags) 362 402 { 403 + struct bpf_link_primer link_primer; 404 + struct hid_bpf_link *link; 363 405 struct bpf_prog *prog = NULL; 364 406 struct hid_bpf_prog_entry *prog_entry; 365 - int cnt, err = -EINVAL, prog_idx = -1; 407 + int cnt, err = -EINVAL, prog_table_idx = -1; 366 408 367 409 /* take a ref on the prog itself */ 368 410 prog = bpf_prog_get(prog_fd); ··· 403 381 404 382 mutex_lock(&hid_bpf_attach_lock); 405 383 384 + link = kzalloc(sizeof(*link), GFP_USER); 385 + if (!link) { 386 + err = -ENOMEM; 387 + goto err_unlock; 388 + } 389 + 390 + bpf_link_init(&link->link, BPF_LINK_TYPE_UNSPEC, 391 + &hid_bpf_link_lops, prog); 392 + 406 393 /* do not attach too many programs to a given HID device */ 407 394 cnt = hid_bpf_program_count(hdev, NULL, prog_type); 408 395 if (cnt < 0) { 409 396 err = cnt; 410 - goto out_unlock; 397 + goto err_unlock; 411 398 } 412 399 413 400 if (cnt >= hid_bpf_max_programs(prog_type)) { 414 401 err = -E2BIG; 415 - goto out_unlock; 402 + goto err_unlock; 416 403 } 417 404 418 - prog_idx = hid_bpf_insert_prog(prog_fd, prog); 405 + prog_table_idx = hid_bpf_insert_prog(prog_fd, prog); 419 406 /* if the jmp table is full, abort */ 420 - if (prog_idx < 0) { 421 - err = prog_idx; 422 - goto out_unlock; 407 + if (prog_table_idx < 0) { 408 + err = prog_table_idx; 409 + goto err_unlock; 423 410 } 424 411 425 412 if (flags & HID_BPF_FLAG_INSERT_HEAD) { ··· 443 412 444 413 /* we steal the ref here */ 445 414 prog_entry->prog = prog; 446 - prog_entry->idx = prog_idx; 415 + prog_entry->idx = prog_table_idx; 447 416 prog_entry->hdev = hdev; 448 417 prog_entry->type = prog_type; 449 418 450 419 /* finally store the index in the device list */ 451 420 err = hid_bpf_populate_hdev(hdev, prog_type); 452 - if (err) 453 - hid_bpf_release_prog_at(prog_idx); 421 + if (err) { 422 + hid_bpf_release_prog_at(prog_table_idx); 423 + goto err_unlock; 424 + } 454 425 455 - out_unlock: 426 + link->hid_table_index = prog_table_idx; 427 + 428 + err = bpf_link_prime(&link->link, &link_primer); 429 + if (err) 430 + goto err_unlock; 431 + 456 432 mutex_unlock(&hid_bpf_attach_lock); 457 433 458 - /* we only use prog as a key in the various tables, so we don't need to actually 459 - * increment the ref count. 460 - */ 434 + return bpf_link_settle(&link_primer); 435 + 436 + err_unlock: 437 + mutex_unlock(&hid_bpf_attach_lock); 438 + 461 439 bpf_prog_put(prog); 440 + kfree(link); 462 441 463 442 return err; 464 443 } ··· 501 460 502 461 void call_hid_bpf_prog_put_deferred(struct work_struct *work) 503 462 { 504 - struct bpf_prog_aux *aux; 505 - struct bpf_prog *prog; 506 - bool found = false; 507 - int i; 508 - 509 - aux = container_of(work, struct bpf_prog_aux, work); 510 - prog = aux->prog; 511 - 512 - /* we don't need locking here because the entries in the progs table 513 - * are stable: 514 - * if there are other users (and the progs entries might change), we 515 - * would simply not have been called. 516 - */ 517 - for (i = 0; i < HID_BPF_MAX_PROGS; i++) { 518 - if (jmp_table.progs[i] == prog) { 519 - __clear_bit(i, jmp_table.enabled); 520 - found = true; 521 - } 522 - } 523 - 524 - if (found) 525 - /* schedule release of all detached progs */ 526 - schedule_work(&release_work); 463 + /* kept around for patch readability, to be dropped in the next commmit */ 527 464 } 528 465 529 - static void hid_bpf_prog_fd_array_put_ptr(void *ptr) 530 - { 531 - } 532 - 533 - #define HID_BPF_PROGS_COUNT 2 466 + #define HID_BPF_PROGS_COUNT 1 534 467 535 468 static struct bpf_link *links[HID_BPF_PROGS_COUNT]; 536 469 static struct entrypoints_bpf *skel; ··· 543 528 idx++; \ 544 529 } while (0) 545 530 546 - static struct bpf_map_ops hid_bpf_prog_fd_maps_ops; 547 - 548 531 int hid_bpf_preload_skel(void) 549 532 { 550 533 int err, idx = 0; ··· 561 548 goto out; 562 549 } 563 550 564 - /* our jump table is stealing refs, so we should not decrement on removal of elements */ 565 - hid_bpf_prog_fd_maps_ops = *jmp_table.map->ops; 566 - hid_bpf_prog_fd_maps_ops.map_fd_put_ptr = hid_bpf_prog_fd_array_put_ptr; 567 - 568 - jmp_table.map->ops = &hid_bpf_prog_fd_maps_ops; 569 - 570 551 ATTACH_AND_STORE_LINK(hid_tail_call); 571 - ATTACH_AND_STORE_LINK(hid_bpf_prog_put_deferred); 572 552 573 553 return 0; 574 554 out:
+7
include/linux/hid_bpf.h
··· 3 3 #ifndef __HID_BPF_H 4 4 #define __HID_BPF_H 5 5 6 + #include <linux/bpf.h> 6 7 #include <linux/spinlock.h> 7 8 #include <uapi/linux/hid.h> 8 9 ··· 137 136 bool destroyed; /* prevents the assignment of any progs */ 138 137 139 138 spinlock_t progs_lock; /* protects RCU update of progs */ 139 + }; 140 + 141 + /* specific HID-BPF link when a program is attached to a device */ 142 + struct hid_bpf_link { 143 + struct bpf_link link; 144 + int hid_table_index; 140 145 }; 141 146 142 147 #ifdef CONFIG_HID_BPF