]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
HID: bpf: make hid_bpf_input_report() sleep until the device is ready
authorBenjamin Tissoires <bentiss@kernel.org>
Wed, 26 Jun 2024 13:46:30 +0000 (15:46 +0200)
committerBenjamin Tissoires <bentiss@kernel.org>
Thu, 27 Jun 2024 09:00:48 +0000 (11:00 +0200)
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/20240626-hid_hw_req_bpf-v2-9-cfd60fb6c79f@kernel.org
Acked-by: Jiri Kosina <jkosina@suse.com>
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
drivers/hid/bpf/hid_bpf_dispatch.c
drivers/hid/hid-core.c
include/linux/hid_bpf.h

index 68a9a2fcbc06b70803e5d5ec51329908909523f0..5174dc6d9d186b564f7c52e31179b72f19a869ef 100644 (file)
@@ -496,24 +496,32 @@ hid_bpf_hw_output_report(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz)
  * @buf: a %PTR_TO_MEM buffer
  * @buf__sz: the size of the data to transfer
  *
- * Returns %0 on success, a negative error code otherwise.
+ * Returns %0 on success, a negative error code otherwise. This function will wait for the
+ * device to be available before injecting the event, thus needs to be called in sleepable
+ * context.
  */
 __bpf_kfunc int
 hid_bpf_input_report(struct hid_bpf_ctx *ctx, enum hid_report_type type, u8 *buf,
                     const size_t buf__sz)
 {
-       struct hid_device *hdev;
        size_t size = buf__sz;
        int ret;
 
+       ret = down_interruptible(&ctx->hid->driver_input_lock);
+       if (ret)
+               return ret;
+
        /* check arguments */
        ret = __hid_bpf_hw_check_params(ctx, buf, &size, type);
        if (ret)
                return ret;
 
-       hdev = (struct hid_device *)ctx->hid; /* discard const */
+       ret = hid_ops->hid_input_report(ctx->hid, type, buf, size, 0, (__u64)ctx,
+                                       true /* lock_already_taken */);
+
+       up(&ctx->hid->driver_input_lock);
 
-       return hid_ops->hid_input_report(hdev, type, buf, size, 0, (__u64)ctx);
+       return ret;
 }
 __bpf_kfunc_end_defs();
 
index bb6f334f05bd755601a218e63ace0f3bde233851..e9b5f44683fd20e8f2ec1f998448751316f7212d 100644 (file)
@@ -2027,7 +2027,8 @@ 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 lock_already_taken)
 {
        struct hid_report_enum *report_enum;
        struct hid_driver *hdrv;
@@ -2037,8 +2038,13 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
        if (!hid)
                return -ENODEV;
 
-       if (down_trylock(&hid->driver_input_lock))
+       ret = down_trylock(&hid->driver_input_lock);
+       if (lock_already_taken && !ret) {
+               up(&hid->driver_input_lock);
+               return -EINVAL;
+       } else if (!lock_already_taken && ret) {
                return -EBUSY;
+       }
 
        if (!hid->driver) {
                ret = -ENODEV;
@@ -2079,7 +2085,8 @@ static int __hid_input_report(struct hid_device *hid, enum hid_report_type type,
        ret = hid_report_raw_event(hid, type, data, size, interrupt);
 
 unlock:
-       up(&hid->driver_input_lock);
+       if (!lock_already_taken)
+               up(&hid->driver_input_lock);
        return ret;
 }
 
@@ -2097,7 +2104,8 @@ unlock:
 int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data, u32 size,
                     int interrupt)
 {
-       return __hid_input_report(hid, type, data, size, interrupt, 0);
+       return __hid_input_report(hid, type, data, size, interrupt, 0,
+                                 false /* lock_already_taken */);
 }
 EXPORT_SYMBOL_GPL(hid_input_report);
 
index f35508a73067e0f5f8a192f86872cb467e5db17c..7f04353d09e9a23fe26c6b7e335632fdb3ec7775 100644 (file)
@@ -72,7 +72,8 @@ 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 lock_already_taken);
        struct module *owner;
        const struct bus_type *bus_type;
 };