Skip to content

Commit 4b9a3f4

Browse files
bentissJiri Kosina
authored andcommitted
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 <[email protected]> Acked-by: Alexei Starovoitov <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent 2574917 commit 4b9a3f4

File tree

4 files changed

+95
-68
lines changed

4 files changed

+95
-68
lines changed

Documentation/hid/hid-bpf.rst

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,12 +427,22 @@ program first::
427427
prog_fd = bpf_program__fd(hid_skel->progs.attach_prog);
428428

429429
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 */
431434
}
432435

433436
Our userspace program can now listen to notifications on the ring buffer, and
434437
is awaken only when the value changes.
435438

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.
445+
436446
Controlling the device
437447
----------------------
438448

drivers/hid/bpf/hid_bpf_dispatch.c

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -249,15 +249,17 @@ int hid_bpf_reconnect(struct hid_device *hdev)
249249
* @prog_fd: an fd in the user process representing the program to attach
250250
* @flags: any logical OR combination of &enum hid_bpf_attach_flags
251251
*
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).
253255
*/
254256
/* called from syscall */
255257
noinline int
256258
hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags)
257259
{
258260
struct hid_device *hdev;
259261
struct device *dev;
260-
int err, prog_type = hid_bpf_get_prog_attach_type(prog_fd);
262+
int fd, err, prog_type = hid_bpf_get_prog_attach_type(prog_fd);
261263

262264
if (!hid_bpf_ops)
263265
return -EINVAL;
@@ -283,17 +285,19 @@ hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags)
283285
return err;
284286
}
285287

286-
err = __hid_bpf_attach_prog(hdev, prog_type, prog_fd, flags);
287-
if (err)
288-
return err;
288+
fd = __hid_bpf_attach_prog(hdev, prog_type, prog_fd, flags);
289+
if (fd < 0)
290+
return fd;
289291

290292
if (prog_type == HID_BPF_PROG_TYPE_RDESC_FIXUP) {
291293
err = hid_bpf_reconnect(hdev);
292-
if (err)
294+
if (err) {
295+
close_fd(fd);
293296
return err;
297+
}
294298
}
295299

296-
return 0;
300+
return fd;
297301
}
298302

299303
/**

drivers/hid/bpf/hid_bpf_jmp_table.c

Lines changed: 66 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -322,16 +322,6 @@ static int hid_bpf_insert_prog(int prog_fd, struct bpf_prog *prog)
322322
if (err)
323323
goto out;
324324

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-
335325
/* return the index */
336326
err = index;
337327

@@ -365,14 +355,46 @@ int hid_bpf_get_prog_attach_type(int prog_fd)
365355
return prog_type;
366356
}
367357

358+
static void hid_bpf_link_release(struct bpf_link *link)
359+
{
360+
struct hid_bpf_link *hid_link =
361+
container_of(link, struct hid_bpf_link, link);
362+
363+
__clear_bit(hid_link->hid_table_index, jmp_table.enabled);
364+
schedule_work(&release_work);
365+
}
366+
367+
static void hid_bpf_link_dealloc(struct bpf_link *link)
368+
{
369+
struct hid_bpf_link *hid_link =
370+
container_of(link, struct hid_bpf_link, link);
371+
372+
kfree(hid_link);
373+
}
374+
375+
static void hid_bpf_link_show_fdinfo(const struct bpf_link *link,
376+
struct seq_file *seq)
377+
{
378+
seq_printf(seq,
379+
"attach_type:\tHID-BPF\n");
380+
}
381+
382+
static const struct bpf_link_ops hid_bpf_link_lops = {
383+
.release = hid_bpf_link_release,
384+
.dealloc = hid_bpf_link_dealloc,
385+
.show_fdinfo = hid_bpf_link_show_fdinfo,
386+
};
387+
368388
/* called from syscall */
369389
noinline int
370390
__hid_bpf_attach_prog(struct hid_device *hdev, enum hid_bpf_prog_type prog_type,
371391
int prog_fd, __u32 flags)
372392
{
393+
struct bpf_link_primer link_primer;
394+
struct hid_bpf_link *link;
373395
struct bpf_prog *prog = NULL;
374396
struct hid_bpf_prog_entry *prog_entry;
375-
int cnt, err = -EINVAL, prog_idx = -1;
397+
int cnt, err = -EINVAL, prog_table_idx = -1;
376398

377399
/* take a ref on the prog itself */
378400
prog = bpf_prog_get(prog_fd);
@@ -381,23 +403,32 @@ __hid_bpf_attach_prog(struct hid_device *hdev, enum hid_bpf_prog_type prog_type,
381403

382404
mutex_lock(&hid_bpf_attach_lock);
383405

406+
link = kzalloc(sizeof(*link), GFP_USER);
407+
if (!link) {
408+
err = -ENOMEM;
409+
goto err_unlock;
410+
}
411+
412+
bpf_link_init(&link->link, BPF_LINK_TYPE_UNSPEC,
413+
&hid_bpf_link_lops, prog);
414+
384415
/* do not attach too many programs to a given HID device */
385416
cnt = hid_bpf_program_count(hdev, NULL, prog_type);
386417
if (cnt < 0) {
387418
err = cnt;
388-
goto out_unlock;
419+
goto err_unlock;
389420
}
390421

391422
if (cnt >= hid_bpf_max_programs(prog_type)) {
392423
err = -E2BIG;
393-
goto out_unlock;
424+
goto err_unlock;
394425
}
395426

396-
prog_idx = hid_bpf_insert_prog(prog_fd, prog);
427+
prog_table_idx = hid_bpf_insert_prog(prog_fd, prog);
397428
/* if the jmp table is full, abort */
398-
if (prog_idx < 0) {
399-
err = prog_idx;
400-
goto out_unlock;
429+
if (prog_table_idx < 0) {
430+
err = prog_table_idx;
431+
goto err_unlock;
401432
}
402433

403434
if (flags & HID_BPF_FLAG_INSERT_HEAD) {
@@ -412,22 +443,32 @@ __hid_bpf_attach_prog(struct hid_device *hdev, enum hid_bpf_prog_type prog_type,
412443

413444
/* we steal the ref here */
414445
prog_entry->prog = prog;
415-
prog_entry->idx = prog_idx;
446+
prog_entry->idx = prog_table_idx;
416447
prog_entry->hdev = hdev;
417448
prog_entry->type = prog_type;
418449

419450
/* finally store the index in the device list */
420451
err = hid_bpf_populate_hdev(hdev, prog_type);
452+
if (err) {
453+
hid_bpf_release_prog_at(prog_table_idx);
454+
goto err_unlock;
455+
}
456+
457+
link->hid_table_index = prog_table_idx;
458+
459+
err = bpf_link_prime(&link->link, &link_primer);
421460
if (err)
422-
hid_bpf_release_prog_at(prog_idx);
461+
goto err_unlock;
423462

424-
out_unlock:
425463
mutex_unlock(&hid_bpf_attach_lock);
426464

427-
/* we only use prog as a key in the various tables, so we don't need to actually
428-
* increment the ref count.
429-
*/
465+
return bpf_link_settle(&link_primer);
466+
467+
err_unlock:
468+
mutex_unlock(&hid_bpf_attach_lock);
469+
430470
bpf_prog_put(prog);
471+
kfree(link);
431472

432473
return err;
433474
}
@@ -460,36 +501,10 @@ void __hid_bpf_destroy_device(struct hid_device *hdev)
460501

461502
void call_hid_bpf_prog_put_deferred(struct work_struct *work)
462503
{
463-
struct bpf_prog_aux *aux;
464-
struct bpf_prog *prog;
465-
bool found = false;
466-
int i;
467-
468-
aux = container_of(work, struct bpf_prog_aux, work);
469-
prog = aux->prog;
470-
471-
/* we don't need locking here because the entries in the progs table
472-
* are stable:
473-
* if there are other users (and the progs entries might change), we
474-
* would simply not have been called.
475-
*/
476-
for (i = 0; i < HID_BPF_MAX_PROGS; i++) {
477-
if (jmp_table.progs[i] == prog) {
478-
__clear_bit(i, jmp_table.enabled);
479-
found = true;
480-
}
481-
}
482-
483-
if (found)
484-
/* schedule release of all detached progs */
485-
schedule_work(&release_work);
486-
}
487-
488-
static void hid_bpf_prog_fd_array_put_ptr(void *ptr)
489-
{
504+
/* kept around for patch readability, to be dropped in the next commmit */
490505
}
491506

492-
#define HID_BPF_PROGS_COUNT 2
507+
#define HID_BPF_PROGS_COUNT 1
493508

494509
static struct bpf_link *links[HID_BPF_PROGS_COUNT];
495510
static struct entrypoints_bpf *skel;
@@ -528,8 +543,6 @@ void hid_bpf_free_links_and_skel(void)
528543
idx++; \
529544
} while (0)
530545

531-
static struct bpf_map_ops hid_bpf_prog_fd_maps_ops;
532-
533546
int hid_bpf_preload_skel(void)
534547
{
535548
int err, idx = 0;
@@ -548,14 +561,7 @@ int hid_bpf_preload_skel(void)
548561
goto out;
549562
}
550563

551-
/* our jump table is stealing refs, so we should not decrement on removal of elements */
552-
hid_bpf_prog_fd_maps_ops = *jmp_table.map->ops;
553-
hid_bpf_prog_fd_maps_ops.map_fd_put_ptr = hid_bpf_prog_fd_array_put_ptr;
554-
555-
jmp_table.map->ops = &hid_bpf_prog_fd_maps_ops;
556-
557564
ATTACH_AND_STORE_LINK(hid_tail_call);
558-
ATTACH_AND_STORE_LINK(hid_bpf_prog_put_deferred);
559565

560566
return 0;
561567
out:

include/linux/hid_bpf.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#ifndef __HID_BPF_H
44
#define __HID_BPF_H
55

6+
#include <linux/bpf.h>
67
#include <linux/spinlock.h>
78
#include <uapi/linux/hid.h>
89

@@ -138,6 +139,12 @@ struct hid_bpf {
138139
spinlock_t progs_lock; /* protects RCU update of progs */
139140
};
140141

142+
/* specific HID-BPF link when a program is attached to a device */
143+
struct hid_bpf_link {
144+
struct bpf_link link;
145+
int hid_table_index;
146+
};
147+
141148
#ifdef CONFIG_HID_BPF
142149
u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, u8 *data,
143150
u32 *size, int interrupt);

0 commit comments

Comments
 (0)