Skip to content

Commit fa03f39

Browse files
author
Benjamin Tissoires
committed
HID: bpf: make hid_bpf_input_report() sleep until the device is ready
hid_bpf_input_report() is already marked to be used in sleepable context only. So instead of hammering with timers the device to hopefully get an available slot where the device is not sending events, we can make that kfunc wait for the current event to be terminated before it goes in. This allows to work with the following pseudo code: in struct_ops/hid_device_event: - schedule a bpf_wq, which calls hid_bpf_input_report() - once this struct_ops function terminates, hid_bpf_input_report() immediately starts before the next event Link: https://patch.msgid.link/[email protected] Acked-by: Jiri Kosina <[email protected]> Signed-off-by: Benjamin Tissoires <[email protected]>
1 parent 3ac83fc commit fa03f39

File tree

3 files changed

+26
-9
lines changed

3 files changed

+26
-9
lines changed

drivers/hid/bpf/hid_bpf_dispatch.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -496,24 +496,32 @@ hid_bpf_hw_output_report(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz)
496496
* @buf: a %PTR_TO_MEM buffer
497497
* @buf__sz: the size of the data to transfer
498498
*
499-
* Returns %0 on success, a negative error code otherwise.
499+
* Returns %0 on success, a negative error code otherwise. This function will wait for the
500+
* device to be available before injecting the event, thus needs to be called in sleepable
501+
* context.
500502
*/
501503
__bpf_kfunc int
502504
hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf,
503505
const size_t buf__sz)
504506
{
505-
struct hid_device *hdev;
506507
size_t size = buf__sz;
507508
int ret;
508509

510+
ret = down_interruptible(&ctx->hid->driver_input_lock);
511+
if (ret)
512+
return ret;
513+
509514
/* check arguments */
510515
ret = __hid_bpf_hw_check_params(ctx, buf, &size, type);
511516
if (ret)
512517
return ret;
513518

514-
hdev = (struct hid_device *)ctx->hid; /* discard const */
519+
ret = hid_ops->hid_input_report(ctx->hid, type, buf, size, 0, (__u64)ctx,
520+
true /* lock_already_taken */);
521+
522+
up(&ctx->hid->driver_input_lock);
515523

516-
return hid_ops->hid_input_report(hdev, type, buf, size, 0, (__u64)ctx);
524+
return ret;
517525
}
518526
__bpf_kfunc_end_defs();
519527

drivers/hid/hid-core.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2027,7 +2027,8 @@ EXPORT_SYMBOL_GPL(hid_report_raw_event);
20272027

20282028

20292029
static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
2030-
u8 *data, u32 size, int interrupt, u64 source)
2030+
u8 *data, u32 size, int interrupt, u64 source,
2031+
bool lock_already_taken)
20312032
{
20322033
struct hid_report_enum *report_enum;
20332034
struct hid_driver *hdrv;
@@ -2037,8 +2038,13 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
20372038
if (!hid)
20382039
return -ENODEV;
20392040

2040-
if (down_trylock(&hid->driver_input_lock))
2041+
ret = down_trylock(&hid->driver_input_lock);
2042+
if (lock_already_taken && !ret) {
2043+
up(&hid->driver_input_lock);
2044+
return -EINVAL;
2045+
} else if (!lock_already_taken && ret) {
20412046
return -EBUSY;
2047+
}
20422048

20432049
if (!hid->driver) {
20442050
ret = -ENODEV;
@@ -2079,7 +2085,8 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
20792085
ret = hid_report_raw_event(hid, type, data, size, interrupt);
20802086

20812087
unlock:
2082-
up(&hid->driver_input_lock);
2088+
if (!lock_already_taken)
2089+
up(&hid->driver_input_lock);
20832090
return ret;
20842091
}
20852092

@@ -2097,7 +2104,8 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
20972104
int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size,
20982105
int interrupt)
20992106
{
2100-
return __hid_input_report(hid, type, data, size, interrupt, 0);
2107+
return __hid_input_report(hid, type, data, size, interrupt, 0,
2108+
false /* lock_already_taken */);
21012109
}
21022110
EXPORT_SYMBOL_GPL(hid_input_report);
21032111

include/linux/hid_bpf.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ struct hid_ops {
7272
int (*hid_hw_output_report)(struct hid_device *hdev, __u8 *buf, size_t len,
7373
__u64 source, bool from_bpf);
7474
int (*hid_input_report)(struct hid_device *hid, enum hid_report_type type,
75-
u8 *data, u32 size, int interrupt, u64 source);
75+
u8 *data, u32 size, int interrupt, u64 source,
76+
bool lock_already_taken);
7677
struct module *owner;
7778
const struct bus_type *bus_type;
7879
};

0 commit comments

Comments
 (0)