diff --git a/Documentation/hid/hid-bpf.rst b/Documentation/hid/hid-bpf.rst index 8ae8f49801cb..5939eeafb361 100644 --- a/Documentation/hid/hid-bpf.rst +++ b/Documentation/hid/hid-bpf.rst @@ -202,7 +202,7 @@ Available API that can be used in syscall HID-BPF programs or in sleepable HID-B ------------------------------------------------------------------------------------------------------- .. kernel-doc:: drivers/hid/bpf/hid_bpf_dispatch.c - :identifiers: hid_bpf_hw_request hid_bpf_hw_output_report hid_bpf_input_report hid_bpf_allocate_context hid_bpf_release_context + :identifiers: hid_bpf_hw_request hid_bpf_hw_output_report hid_bpf_input_report hid_bpf_try_input_report hid_bpf_allocate_context hid_bpf_release_context General overview of a HID-BPF program ===================================== diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index 5174dc6d9d18..23daf94510bb 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -24,7 +24,7 @@ EXPORT_SYMBOL(hid_ops); u8 * dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type, u8 *data, - u32 *size, int interrupt, u64 source) + u32 *size, int interrupt, u64 source, bool from_bpf) { struct hid_bpf_ctx_kern ctx_kern = { .ctx = { @@ -33,6 +33,7 @@ dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type .size = *size, }, .data = hdev->bpf.device_data, + .from_bpf = from_bpf, }; struct hid_bpf_ops *e; int ret; @@ -488,6 +489,50 @@ hid_bpf_hw_output_report(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz) return ret; } +static int +__hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf, + size_t size, bool lock_already_taken) +{ + struct hid_bpf_ctx_kern *ctx_kern; + int ret; + + ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx); + if (ctx_kern->from_bpf) + return -EDEADLOCK; + + /* check arguments */ + ret = __hid_bpf_hw_check_params(ctx, buf, &size, type); + if (ret) + return ret; + + return hid_ops->hid_input_report(ctx->hid, type, buf, size, 0, (__u64)ctx, true, + lock_already_taken); +} + +/** + * hid_bpf_try_input_report - Inject a HID report in the kernel from a HID device + * + * @ctx: the HID-BPF context previously allocated in hid_bpf_allocate_context() + * @type: the type of the report (%HID_INPUT_REPORT, %HID_FEATURE_REPORT, %HID_OUTPUT_REPORT) + * @buf: a %PTR_TO_MEM buffer + * @buf__sz: the size of the data to transfer + * + * Returns %0 on success, a negative error code otherwise. This function will immediately + * fail if the device is not available, thus can be safely used in IRQ context. + */ +__bpf_kfunc int +hid_bpf_try_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf, + const size_t buf__sz) +{ + struct hid_bpf_ctx_kern *ctx_kern; + bool from_hid_event_hook; + + ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx); + from_hid_event_hook = ctx_kern->data && ctx_kern->data == ctx->hid->bpf.device_data; + + return __hid_bpf_input_report(ctx, type, buf, buf__sz, from_hid_event_hook); +} + /** * hid_bpf_input_report - Inject a HID report in the kernel from a HID device * @@ -504,7 +549,6 @@ __bpf_kfunc int hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf, const size_t buf__sz) { - size_t size = buf__sz; int ret; ret = down_interruptible(&ctx->hid->driver_input_lock); @@ -512,12 +556,7 @@ hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf return ret; /* check arguments */ - ret = __hid_bpf_hw_check_params(ctx, buf, &size, type); - if (ret) - return ret; - - ret = hid_ops->hid_input_report(ctx->hid, type, buf, size, 0, (__u64)ctx, - true /* lock_already_taken */); + ret = __hid_bpf_input_report(ctx, type, buf, buf__sz, true /* lock_already_taken */); up(&ctx->hid->driver_input_lock); @@ -536,6 +575,7 @@ BTF_ID_FLAGS(func, hid_bpf_release_context, KF_RELEASE | KF_SLEEPABLE) BTF_ID_FLAGS(func, hid_bpf_hw_request, KF_SLEEPABLE) BTF_ID_FLAGS(func, hid_bpf_hw_output_report, KF_SLEEPABLE) BTF_ID_FLAGS(func, hid_bpf_input_report, KF_SLEEPABLE) +BTF_ID_FLAGS(func, hid_bpf_try_input_report) BTF_KFUNCS_END(hid_bpf_kfunc_ids) static const struct btf_kfunc_id_set hid_bpf_kfunc_set = { diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index e9b5f44683fd..52a75afe3e7d 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2027,7 +2027,7 @@ EXPORT_SYMBOL_GPL(hid_report_raw_event); static int __hid_input_report(struct hid_device *hid, enum hid_report_type type, - u8 *data, u32 size, int interrupt, u64 source, + u8 *data, u32 size, int interrupt, u64 source, bool from_bpf, bool lock_already_taken) { struct hid_report_enum *report_enum; @@ -2053,7 +2053,7 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type, report_enum = hid->report_enum + type; hdrv = hid->driver; - data = dispatch_hid_bpf_device_event(hid, type, data, &size, interrupt, source); + data = dispatch_hid_bpf_device_event(hid, type, data, &size, interrupt, source, from_bpf); if (IS_ERR(data)) { ret = PTR_ERR(data); goto unlock; @@ -2105,6 +2105,7 @@ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data int interrupt) { return __hid_input_report(hid, type, data, size, interrupt, 0, + false, /* from_bpf */ false /* lock_already_taken */); } EXPORT_SYMBOL_GPL(hid_input_report); diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h index 7f04353d09e9..93546ee7677a 100644 --- a/include/linux/hid_bpf.h +++ b/include/linux/hid_bpf.h @@ -72,7 +72,7 @@ struct hid_ops { int (*hid_hw_output_report)(struct hid_device *hdev, __u8 *buf, size_t len, __u64 source, bool from_bpf); int (*hid_input_report)(struct hid_device *hid, enum hid_report_type type, - u8 *data, u32 size, int interrupt, u64 source, + u8 *data, u32 size, int interrupt, u64 source, bool from_bpf, bool lock_already_taken); struct module *owner; const struct bus_type *bus_type; @@ -195,7 +195,7 @@ struct hid_bpf { #ifdef CONFIG_HID_BPF u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, u8 *data, - u32 *size, int interrupt, u64 source); + u32 *size, int interrupt, u64 source, bool from_bpf); int dispatch_hid_bpf_raw_requests(struct hid_device *hdev, unsigned char reportnum, __u8 *buf, u32 size, enum hid_report_type rtype, @@ -211,7 +211,7 @@ u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *s #else /* CONFIG_HID_BPF */ static inline u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 *size, int interrupt, - u64 source) { return data; } + u64 source, bool from_bpf) { return data; } static inline int dispatch_hid_bpf_raw_requests(struct hid_device *hdev, unsigned char reportnum, u8 *buf, u32 size, enum hid_report_type rtype,