From f5c27da4e3c8a2e42fb4f41a0c685debcb9af294 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 3 Nov 2022 16:57:44 +0100 Subject: HID: initial BPF implementation Declare an entry point that can use fmod_ret BPF programs, and also an API to access and change the incoming data. A simpler implementation would consist in just calling hid_bpf_device_event() for any incoming event and let users deal with the fact that they will be called for any event of any device. The goal of HID-BPF is to partially replace drivers, so this situation can be problematic because we might have programs which will step on each other toes. For that, we add a new API hid_bpf_attach_prog() that can be called from a syscall and we manually deal with a jump table in hid-bpf. Whenever we add a program to the jump table (in other words, when we attach a program to a HID device), we keep the number of time we added this program in the jump table so we can release it whenever there are no other users. HID devices have an RCU protected list of available programs in the jump table, and those programs are called one after the other thanks to bpf_tail_call(). To achieve the detection of users losing their fds on the programs we attached, we add 2 tracing facilities on bpf_prog_release() (for when a fd is closed) and bpf_free_inode() (for when a pinned program gets unpinned). Reviewed-by: Greg Kroah-Hartman Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- include/linux/hid.h | 5 +++ include/linux/hid_bpf.h | 117 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 include/linux/hid_bpf.h (limited to 'include/linux') diff --git a/include/linux/hid.h b/include/linux/hid.h index 8677ae38599e..cd3c52fae7b1 100644 --- a/include/linux/hid.h +++ b/include/linux/hid.h @@ -26,6 +26,7 @@ #include #include #include +#include /* * We parse each description item into this structure. Short items data @@ -651,6 +652,10 @@ struct hid_device { /* device report descriptor */ wait_queue_head_t debug_wait; unsigned int id; /* system unique id */ + +#ifdef CONFIG_BPF + struct hid_bpf bpf; /* hid-bpf data */ +#endif /* CONFIG_BPF */ }; #define to_hid_device(pdev) \ diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h new file mode 100644 index 000000000000..de3fb1c376d2 --- /dev/null +++ b/include/linux/hid_bpf.h @@ -0,0 +1,117 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ + +#ifndef __HID_BPF_H +#define __HID_BPF_H + +#include +#include + +struct hid_device; + +/* + * The following is the user facing HID BPF API. + * + * Extra care should be taken when editing this part, as + * it might break existing out of the tree bpf programs. + */ + +/** + * struct hid_bpf_ctx - User accessible data for all HID programs + * + * ``data`` is not directly accessible from the context. We need to issue + * a call to ``hid_bpf_get_data()`` in order to get a pointer to that field. + * + * All of these fields are currently read-only. + * + * @index: program index in the jump table. No special meaning (a smaller index + * doesn't mean the program will be executed before another program with + * a bigger index). + * @hid: the ``struct hid_device`` representing the device itself + * @report_type: used for ``hid_bpf_device_event()`` + * @size: Valid data in the data field. + * + * Programs can get the available valid size in data by fetching this field. + */ +struct hid_bpf_ctx { + __u32 index; + const struct hid_device *hid; + enum hid_report_type report_type; + __s32 size; +}; + +/** + * enum hid_bpf_attach_flags - flags used when attaching a HIF-BPF program + * + * @HID_BPF_FLAG_NONE: no specific flag is used, the kernel choses where to + * insert the program + * @HID_BPF_FLAG_INSERT_HEAD: insert the given program before any other program + * currently attached to the device. This doesn't + * guarantee that this program will always be first + * @HID_BPF_FLAG_MAX: sentinel value, not to be used by the callers + */ +enum hid_bpf_attach_flags { + HID_BPF_FLAG_NONE = 0, + HID_BPF_FLAG_INSERT_HEAD = _BITUL(0), + HID_BPF_FLAG_MAX, +}; + +/* Following functions are tracepoints that BPF programs can attach to */ +int hid_bpf_device_event(struct hid_bpf_ctx *ctx); + +/* Following functions are kfunc that we export to BPF programs */ +/* only available in tracing */ +__u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx, unsigned int offset, const size_t __sz); + +/* only available in syscall */ +int hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags); + +/* + * Below is HID internal + */ + +/* internal function to call eBPF programs, not to be used by anybody */ +int __hid_bpf_tail_call(struct hid_bpf_ctx *ctx); + +#define HID_BPF_MAX_PROGS_PER_DEV 64 +#define HID_BPF_FLAG_MASK (((HID_BPF_FLAG_MAX - 1) << 1) - 1) + +/* types of HID programs to attach to */ +enum hid_bpf_prog_type { + HID_BPF_PROG_TYPE_UNDEF = -1, + HID_BPF_PROG_TYPE_DEVICE_EVENT, /* an event is emitted from the device */ + HID_BPF_PROG_TYPE_MAX, +}; + +struct hid_bpf_ops { + struct module *owner; + struct bus_type *bus_type; +}; + +extern struct hid_bpf_ops *hid_bpf_ops; + +struct hid_bpf_prog_list { + u16 prog_idx[HID_BPF_MAX_PROGS_PER_DEV]; + u8 prog_cnt; +}; + +/* stored in each device */ +struct hid_bpf { + struct hid_bpf_prog_list __rcu *progs[HID_BPF_PROG_TYPE_MAX]; /* attached BPF progs */ + bool destroyed; /* prevents the assignment of any progs */ + + spinlock_t progs_lock; /* protects RCU update of progs */ +}; + +#ifdef CONFIG_HID_BPF +int dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, u8 *data, + u32 size, int interrupt); +void hid_bpf_destroy_device(struct hid_device *hid); +void hid_bpf_device_init(struct hid_device *hid); +#else /* CONFIG_HID_BPF */ +static inline int dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, + u8 *data, u32 size, int interrupt) { return 0; } +static inline void hid_bpf_destroy_device(struct hid_device *hid) {} +static inline void hid_bpf_device_init(struct hid_device *hid) {} +#endif /* CONFIG_HID_BPF */ + +#endif /* __HID_BPF_H */ -- cgit v1.2.3 From 658ee5a64fcfbbf758447fa3af425729eaabb0dc Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 3 Nov 2022 16:57:47 +0100 Subject: HID: bpf: allocate data memory for device_event BPF programs We need to also be able to change the size of the report. Reducing it is easy, because we already have the incoming buffer that is big enough, but extending it is harder. Pre-allocate a buffer that is big enough to handle all reports of the device, and use that as the primary buffer for BPF programs. To be able to change the size of the buffer, we change the device_event API and request it to return the size of the buffer. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/bpf/hid_bpf_dispatch.c | 116 ++++++++++++++++++++++++++++++++---- drivers/hid/bpf/hid_bpf_jmp_table.c | 4 +- drivers/hid/hid-core.c | 12 +++- include/linux/hid_bpf.h | 37 ++++++++++-- 4 files changed, 151 insertions(+), 18 deletions(-) (limited to 'include/linux') diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index 0ef99caf937a..efd4e4b4a615 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -28,8 +28,9 @@ EXPORT_SYMBOL(hid_bpf_ops); * * @ctx: The HID-BPF context * - * @return %0 on success and keep processing; a negative error code to interrupt - * the processing of this event + * @return %0 on success and keep processing; a positive value to change the + * incoming size buffer; a negative error code to interrupt the processing + * of this event * * Declare an %fmod_ret tracing bpf program to this function and attach this * program through hid_bpf_attach_prog() to have this helper called for @@ -44,23 +45,43 @@ __weak noinline int hid_bpf_device_event(struct hid_bpf_ctx *ctx) } ALLOW_ERROR_INJECTION(hid_bpf_device_event, ERRNO); -int +u8 * dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type, u8 *data, - u32 size, int interrupt) + u32 *size, int interrupt) { struct hid_bpf_ctx_kern ctx_kern = { .ctx = { .hid = hdev, .report_type = type, - .size = size, + .allocated_size = hdev->bpf.allocated_data, + .size = *size, }, - .data = data, + .data = hdev->bpf.device_data, }; + int ret; if (type >= HID_REPORT_TYPES) - return -EINVAL; + return ERR_PTR(-EINVAL); + + /* no program has been attached yet */ + if (!hdev->bpf.device_data) + return data; + + memset(ctx_kern.data, 0, hdev->bpf.allocated_data); + memcpy(ctx_kern.data, data, *size); + + ret = hid_bpf_prog_run(hdev, HID_BPF_PROG_TYPE_DEVICE_EVENT, &ctx_kern); + if (ret < 0) + return ERR_PTR(ret); + + if (ret) { + if (ret > ctx_kern.ctx.allocated_size) + return ERR_PTR(-EINVAL); - return hid_bpf_prog_run(hdev, HID_BPF_PROG_TYPE_DEVICE_EVENT, &ctx_kern); + *size = ret; + } + + return ctx_kern.data; } EXPORT_SYMBOL_GPL(dispatch_hid_bpf_device_event); @@ -83,7 +104,7 @@ hid_bpf_get_data(struct hid_bpf_ctx *ctx, unsigned int offset, const size_t rdwr ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx); - if (rdwr_buf_size + offset > ctx->size) + if (rdwr_buf_size + offset > ctx->allocated_size) return NULL; return ctx_kern->data + offset; @@ -110,6 +131,51 @@ static int device_match_id(struct device *dev, const void *id) return hdev->id == *(int *)id; } +static int __hid_bpf_allocate_data(struct hid_device *hdev, u8 **data, u32 *size) +{ + u8 *alloc_data; + unsigned int i, j, max_report_len = 0; + size_t alloc_size = 0; + + /* compute the maximum report length for this device */ + for (i = 0; i < HID_REPORT_TYPES; i++) { + struct hid_report_enum *report_enum = hdev->report_enum + i; + + for (j = 0; j < HID_MAX_IDS; j++) { + struct hid_report *report = report_enum->report_id_hash[j]; + + if (report) + max_report_len = max(max_report_len, hid_report_len(report)); + } + } + + /* + * Give us a little bit of extra space and some predictability in the + * buffer length we create. This way, we can tell users that they can + * work on chunks of 64 bytes of memory without having the bpf verifier + * scream at them. + */ + alloc_size = DIV_ROUND_UP(max_report_len, 64) * 64; + + alloc_data = kzalloc(alloc_size, GFP_KERNEL); + if (!alloc_data) + return -ENOMEM; + + *data = alloc_data; + *size = alloc_size; + + return 0; +} + +static int hid_bpf_allocate_event_data(struct hid_device *hdev) +{ + /* hdev->bpf.device_data is already allocated, abort */ + if (hdev->bpf.device_data) + return 0; + + return __hid_bpf_allocate_data(hdev, &hdev->bpf.device_data, &hdev->bpf.allocated_data); +} + /** * hid_bpf_attach_prog - Attach the given @prog_fd to the given HID device * @@ -125,7 +191,7 @@ hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags) { struct hid_device *hdev; struct device *dev; - int prog_type = hid_bpf_get_prog_attach_type(prog_fd); + int err, prog_type = hid_bpf_get_prog_attach_type(prog_fd); if (!hid_bpf_ops) return -EINVAL; @@ -145,6 +211,12 @@ hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags) hdev = to_hid_device(dev); + if (prog_type == HID_BPF_PROG_TYPE_DEVICE_EVENT) { + err = hid_bpf_allocate_event_data(hdev); + if (err) + return err; + } + return __hid_bpf_attach_prog(hdev, prog_type, prog_fd, flags); } @@ -158,6 +230,30 @@ static const struct btf_kfunc_id_set hid_bpf_syscall_kfunc_set = { .set = &hid_bpf_syscall_kfunc_ids, }; +int hid_bpf_connect_device(struct hid_device *hdev) +{ + struct hid_bpf_prog_list *prog_list; + + rcu_read_lock(); + prog_list = rcu_dereference(hdev->bpf.progs[HID_BPF_PROG_TYPE_DEVICE_EVENT]); + rcu_read_unlock(); + + /* only allocate BPF data if there are programs attached */ + if (!prog_list) + return 0; + + return hid_bpf_allocate_event_data(hdev); +} +EXPORT_SYMBOL_GPL(hid_bpf_connect_device); + +void hid_bpf_disconnect_device(struct hid_device *hdev) +{ + kfree(hdev->bpf.device_data); + hdev->bpf.device_data = NULL; + hdev->bpf.allocated_data = 0; +} +EXPORT_SYMBOL_GPL(hid_bpf_disconnect_device); + void hid_bpf_destroy_device(struct hid_device *hdev) { if (!hdev) diff --git a/drivers/hid/bpf/hid_bpf_jmp_table.c b/drivers/hid/bpf/hid_bpf_jmp_table.c index e5f171ab480c..bfcdd7b805a9 100644 --- a/drivers/hid/bpf/hid_bpf_jmp_table.c +++ b/drivers/hid/bpf/hid_bpf_jmp_table.c @@ -122,8 +122,10 @@ int hid_bpf_prog_run(struct hid_device *hdev, enum hid_bpf_prog_type type, ctx_kern->ctx.index = idx; err = __hid_bpf_tail_call(&ctx_kern->ctx); - if (err) + if (err < 0) break; + if (err) + ctx_kern->ctx.retval = err; } out_unlock: diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 1098c49b5d2e..400a7b8133c6 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2040,9 +2040,11 @@ int hid_input_report(struct hid_device *hid, enum hid_report_type type, u8 *data report_enum = hid->report_enum + type; hdrv = hid->driver; - ret = dispatch_hid_bpf_device_event(hid, type, data, size, interrupt); - if (ret) + data = dispatch_hid_bpf_device_event(hid, type, data, &size, interrupt); + if (IS_ERR(data)) { + ret = PTR_ERR(data); goto unlock; + } if (!size) { dbg_hid("empty report\n"); @@ -2158,6 +2160,10 @@ int hid_connect(struct hid_device *hdev, unsigned int connect_mask) int len; int ret; + ret = hid_bpf_connect_device(hdev); + if (ret) + return ret; + if (hdev->quirks & HID_QUIRK_HIDDEV_FORCE) connect_mask |= (HID_CONNECT_HIDDEV_FORCE | HID_CONNECT_HIDDEV); if (hdev->quirks & HID_QUIRK_HIDINPUT_FORCE) @@ -2259,6 +2265,8 @@ void hid_disconnect(struct hid_device *hdev) if (hdev->claimed & HID_CLAIMED_HIDRAW) hidraw_disconnect(hdev); hdev->claimed = 0; + + hid_bpf_disconnect_device(hdev); } EXPORT_SYMBOL_GPL(hid_disconnect); diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h index de3fb1c376d2..b3de462ef3a6 100644 --- a/include/linux/hid_bpf.h +++ b/include/linux/hid_bpf.h @@ -28,15 +28,32 @@ struct hid_device; * a bigger index). * @hid: the ``struct hid_device`` representing the device itself * @report_type: used for ``hid_bpf_device_event()`` + * @allocated_size: Allocated size of data. + * + * This is how much memory is available and can be requested + * by the HID program. + * Note that for ``HID_BPF_RDESC_FIXUP``, that memory is set to + * ``4096`` (4 KB) * @size: Valid data in the data field. * * Programs can get the available valid size in data by fetching this field. + * Programs can also change this value by returning a positive number in the + * program. + * To discard the event, return a negative error code. + * + * ``size`` must always be less or equal than ``allocated_size`` (it is enforced + * once all BPF programs have been run). + * @retval: Return value of the previous program. */ struct hid_bpf_ctx { __u32 index; const struct hid_device *hid; + __u32 allocated_size; enum hid_report_type report_type; - __s32 size; + union { + __s32 retval; + __s32 size; + }; }; /** @@ -96,6 +113,12 @@ struct hid_bpf_prog_list { /* stored in each device */ struct hid_bpf { + u8 *device_data; /* allocated when a bpf program of type + * SEC(f.../hid_bpf_device_event) has been attached + * to this HID device + */ + u32 allocated_data; + struct hid_bpf_prog_list __rcu *progs[HID_BPF_PROG_TYPE_MAX]; /* attached BPF progs */ bool destroyed; /* prevents the assignment of any progs */ @@ -103,13 +126,17 @@ struct hid_bpf { }; #ifdef CONFIG_HID_BPF -int dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, u8 *data, - u32 size, int interrupt); +u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, u8 *data, + u32 *size, int interrupt); +int hid_bpf_connect_device(struct hid_device *hdev); +void hid_bpf_disconnect_device(struct hid_device *hdev); void hid_bpf_destroy_device(struct hid_device *hid); void hid_bpf_device_init(struct hid_device *hid); #else /* CONFIG_HID_BPF */ -static inline int dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, - u8 *data, u32 size, int interrupt) { return 0; } +static inline u8 *dispatch_hid_bpf_device_event(struct hid_device *hid, enum hid_report_type type, + u8 *data, u32 *size, int interrupt) { return NULL; } +static inline int hid_bpf_connect_device(struct hid_device *hdev) { return 0; } +static inline void hid_bpf_disconnect_device(struct hid_device *hdev) {} static inline void hid_bpf_destroy_device(struct hid_device *hid) {} static inline void hid_bpf_device_init(struct hid_device *hid) {} #endif /* CONFIG_HID_BPF */ -- cgit v1.2.3 From 91a7f802d1852f60139712bdcfa98db547ce0531 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 3 Nov 2022 16:57:49 +0100 Subject: HID: bpf: introduce hid_hw_request() This function can not be called under IRQ, thus it is only available while in SEC("syscall"). For consistency, this function requires a HID-BPF context to work with, and so we also provide a helper to create one based on the HID unique ID. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Benjamin Tissoires -- changes in v12: - variable dereferenced before check 'ctx' |Reported-by: kernel test robot |Reported-by: Dan Carpenter no changes in v11 no changes in v10 changes in v9: - fixed kfunc declaration aaccording to latest upstream changes no changes in v8 changes in v7: - hid_bpf_allocate_context: remove unused variable - ensures buf is not NULL changes in v6: - rename parameter size into buf__sz to teach the verifier about the actual buffer size used by the call - remove the allocated data in the user created context, it's not used new-ish in v5 Signed-off-by: Jiri Kosina --- drivers/hid/bpf/hid_bpf_dispatch.c | 134 +++++++++++++++++++++++++++++++++++++ drivers/hid/hid-core.c | 2 + include/linux/hid_bpf.h | 13 +++- 3 files changed, 148 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index efd4e4b4a615..0fe856d67f12 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -220,9 +220,143 @@ hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags) return __hid_bpf_attach_prog(hdev, prog_type, prog_fd, flags); } +/** + * hid_bpf_allocate_context - Allocate a context to the given HID device + * + * @hid_id: the system unique identifier of the HID device + * + * @returns A pointer to &struct hid_bpf_ctx on success, %NULL on error. + */ +noinline struct hid_bpf_ctx * +hid_bpf_allocate_context(unsigned int hid_id) +{ + struct hid_device *hdev; + struct hid_bpf_ctx_kern *ctx_kern = NULL; + struct device *dev; + + if (!hid_bpf_ops) + return NULL; + + dev = bus_find_device(hid_bpf_ops->bus_type, NULL, &hid_id, device_match_id); + if (!dev) + return NULL; + + hdev = to_hid_device(dev); + + ctx_kern = kzalloc(sizeof(*ctx_kern), GFP_KERNEL); + if (!ctx_kern) + return NULL; + + ctx_kern->ctx.hid = hdev; + + return &ctx_kern->ctx; +} + +/** + * hid_bpf_release_context - Release the previously allocated context @ctx + * + * @ctx: the HID-BPF context to release + * + */ +noinline void +hid_bpf_release_context(struct hid_bpf_ctx *ctx) +{ + struct hid_bpf_ctx_kern *ctx_kern; + + if (!ctx) + return; + + ctx_kern = container_of(ctx, struct hid_bpf_ctx_kern, ctx); + + kfree(ctx_kern); +} + +/** + * hid_bpf_hw_request - Communicate with a HID device + * + * @ctx: the HID-BPF context previously allocated in hid_bpf_allocate_context() + * @buf: a %PTR_TO_MEM buffer + * @buf__sz: the size of the data to transfer + * @rtype: the type of the report (%HID_INPUT_REPORT, %HID_FEATURE_REPORT, %HID_OUTPUT_REPORT) + * @reqtype: the type of the request (%HID_REQ_GET_REPORT, %HID_REQ_SET_REPORT, ...) + * + * @returns %0 on success, a negative error code otherwise. + */ +noinline int +hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz, + enum hid_report_type rtype, enum hid_class_request reqtype) +{ + struct hid_device *hdev; + struct hid_report *report; + struct hid_report_enum *report_enum; + u8 *dma_data; + u32 report_len; + int ret; + + /* check arguments */ + if (!ctx || !hid_bpf_ops || !buf) + return -EINVAL; + + switch (rtype) { + case HID_INPUT_REPORT: + case HID_OUTPUT_REPORT: + case HID_FEATURE_REPORT: + break; + default: + return -EINVAL; + } + + switch (reqtype) { + case HID_REQ_GET_REPORT: + case HID_REQ_GET_IDLE: + case HID_REQ_GET_PROTOCOL: + case HID_REQ_SET_REPORT: + case HID_REQ_SET_IDLE: + case HID_REQ_SET_PROTOCOL: + break; + default: + return -EINVAL; + } + + if (buf__sz < 1) + return -EINVAL; + + hdev = (struct hid_device *)ctx->hid; /* discard const */ + + report_enum = hdev->report_enum + rtype; + report = hid_bpf_ops->hid_get_report(report_enum, buf); + if (!report) + return -EINVAL; + + report_len = hid_report_len(report); + + if (buf__sz > report_len) + buf__sz = report_len; + + dma_data = kmemdup(buf, buf__sz, GFP_KERNEL); + if (!dma_data) + return -ENOMEM; + + ret = hid_bpf_ops->hid_hw_raw_request(hdev, + dma_data[0], + dma_data, + buf__sz, + rtype, + reqtype); + + if (ret > 0) + memcpy(buf, dma_data, ret); + + kfree(dma_data); + return ret; +} + /* for syscall HID-BPF */ BTF_SET8_START(hid_bpf_syscall_kfunc_ids) BTF_ID_FLAGS(func, hid_bpf_attach_prog) +BTF_ID_FLAGS(func, hid_bpf_allocate_context, KF_ACQUIRE | KF_RET_NULL) +BTF_ID_FLAGS(func, hid_bpf_release_context, KF_RELEASE) +BTF_ID_FLAGS(func, hid_bpf_hw_request) BTF_SET8_END(hid_bpf_syscall_kfunc_ids) static const struct btf_kfunc_id_set hid_bpf_syscall_kfunc_set = { diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 400a7b8133c6..6bf47e352f23 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -2919,6 +2919,8 @@ EXPORT_SYMBOL_GPL(hid_check_keys_pressed); #ifdef CONFIG_HID_BPF static struct hid_bpf_ops hid_ops = { + .hid_get_report = hid_get_report, + .hid_hw_raw_request = hid_hw_raw_request, .owner = THIS_MODULE, .bus_type = &hid_bus_type, }; diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h index b3de462ef3a6..1d24b72059bc 100644 --- a/include/linux/hid_bpf.h +++ b/include/linux/hid_bpf.h @@ -76,11 +76,15 @@ enum hid_bpf_attach_flags { int hid_bpf_device_event(struct hid_bpf_ctx *ctx); /* Following functions are kfunc that we export to BPF programs */ -/* only available in tracing */ +/* available everywhere in HID-BPF */ __u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx, unsigned int offset, const size_t __sz); /* only available in syscall */ int hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags); +int hid_bpf_hw_request(struct hid_bpf_ctx *ctx, __u8 *buf, size_t buf__sz, + enum hid_report_type rtype, enum hid_class_request reqtype); +struct hid_bpf_ctx *hid_bpf_allocate_context(unsigned int hid_id); +void hid_bpf_release_context(struct hid_bpf_ctx *ctx); /* * Below is HID internal @@ -99,7 +103,14 @@ enum hid_bpf_prog_type { HID_BPF_PROG_TYPE_MAX, }; +struct hid_report_enum; + struct hid_bpf_ops { + struct hid_report *(*hid_get_report)(struct hid_report_enum *report_enum, const u8 *data); + int (*hid_hw_raw_request)(struct hid_device *hdev, + unsigned char reportnum, __u8 *buf, + size_t len, enum hid_report_type rtype, + enum hid_class_request reqtype); struct module *owner; struct bus_type *bus_type; }; -- cgit v1.2.3 From ad190df11a024c1214fbc8dcaab72c592c79d9b5 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 3 Nov 2022 16:57:51 +0100 Subject: HID: bpf: allow to change the report descriptor Add a new tracepoint hid_bpf_rdesc_fixup() so we can trigger a report descriptor fixup in the bpf world. Whenever the program gets attached/detached, the device is reconnected meaning that userspace will see it disappearing and reappearing with the new report descriptor. Reviewed-by: Greg Kroah-Hartman Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- drivers/hid/bpf/hid_bpf_dispatch.c | 80 ++++++++++++++++++++++++++++++++++++- drivers/hid/bpf/hid_bpf_dispatch.h | 1 + drivers/hid/bpf/hid_bpf_jmp_table.c | 7 ++++ drivers/hid/hid-core.c | 3 +- include/linux/hid_bpf.h | 8 ++++ 5 files changed, 97 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index 0fe856d67f12..c3920c7964dc 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include "hid_bpf_dispatch.h" @@ -85,6 +86,65 @@ dispatch_hid_bpf_device_event(struct hid_device *hdev, enum hid_report_type type } EXPORT_SYMBOL_GPL(dispatch_hid_bpf_device_event); +/** + * hid_bpf_rdesc_fixup - Called when the probe function parses the report + * descriptor of the HID device + * + * @ctx: The HID-BPF context + * + * @return 0 on success and keep processing; a positive value to change the + * incoming size buffer; a negative error code to interrupt the processing + * of this event + * + * Declare an %fmod_ret tracing bpf program to this function and attach this + * program through hid_bpf_attach_prog() to have this helper called before any + * parsing of the report descriptor by HID. + */ +/* never used by the kernel but declared so we can load and attach a tracepoint */ +__weak noinline int hid_bpf_rdesc_fixup(struct hid_bpf_ctx *ctx) +{ + return 0; +} +ALLOW_ERROR_INJECTION(hid_bpf_rdesc_fixup, ERRNO); + +u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size) +{ + int ret; + struct hid_bpf_ctx_kern ctx_kern = { + .ctx = { + .hid = hdev, + .size = *size, + .allocated_size = HID_MAX_DESCRIPTOR_SIZE, + }, + }; + + ctx_kern.data = kzalloc(ctx_kern.ctx.allocated_size, GFP_KERNEL); + if (!ctx_kern.data) + goto ignore_bpf; + + memcpy(ctx_kern.data, rdesc, min_t(unsigned int, *size, HID_MAX_DESCRIPTOR_SIZE)); + + ret = hid_bpf_prog_run(hdev, HID_BPF_PROG_TYPE_RDESC_FIXUP, &ctx_kern); + if (ret < 0) + goto ignore_bpf; + + if (ret) { + if (ret > ctx_kern.ctx.allocated_size) + goto ignore_bpf; + + *size = ret; + } + + rdesc = krealloc(ctx_kern.data, *size, GFP_KERNEL); + + return rdesc; + + ignore_bpf: + kfree(ctx_kern.data); + return kmemdup(rdesc, *size, GFP_KERNEL); +} +EXPORT_SYMBOL_GPL(call_hid_bpf_rdesc_fixup); + /** * hid_bpf_get_data - Get the kernel memory pointer associated with the context @ctx * @@ -176,6 +236,14 @@ static int hid_bpf_allocate_event_data(struct hid_device *hdev) return __hid_bpf_allocate_data(hdev, &hdev->bpf.device_data, &hdev->bpf.allocated_data); } +int hid_bpf_reconnect(struct hid_device *hdev) +{ + if (!test_and_set_bit(ffs(HID_STAT_REPROBED), &hdev->status)) + return device_reprobe(&hdev->dev); + + return 0; +} + /** * hid_bpf_attach_prog - Attach the given @prog_fd to the given HID device * @@ -217,7 +285,17 @@ hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags) return err; } - return __hid_bpf_attach_prog(hdev, prog_type, prog_fd, flags); + err = __hid_bpf_attach_prog(hdev, prog_type, prog_fd, flags); + if (err) + return err; + + if (prog_type == HID_BPF_PROG_TYPE_RDESC_FIXUP) { + err = hid_bpf_reconnect(hdev); + if (err) + return err; + } + + return 0; } /** diff --git a/drivers/hid/bpf/hid_bpf_dispatch.h b/drivers/hid/bpf/hid_bpf_dispatch.h index e2d64faa3932..2eeab1f746dd 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.h +++ b/drivers/hid/bpf/hid_bpf_dispatch.h @@ -18,6 +18,7 @@ int __hid_bpf_attach_prog(struct hid_device *hdev, enum hid_bpf_prog_type prog_t void __hid_bpf_destroy_device(struct hid_device *hdev); int hid_bpf_prog_run(struct hid_device *hdev, enum hid_bpf_prog_type type, struct hid_bpf_ctx_kern *ctx_kern); +int hid_bpf_reconnect(struct hid_device *hdev); struct bpf_prog; diff --git a/drivers/hid/bpf/hid_bpf_jmp_table.c b/drivers/hid/bpf/hid_bpf_jmp_table.c index bfcdd7b805a9..579a6c06906e 100644 --- a/drivers/hid/bpf/hid_bpf_jmp_table.c +++ b/drivers/hid/bpf/hid_bpf_jmp_table.c @@ -58,12 +58,15 @@ static DECLARE_WORK(release_work, hid_bpf_release_progs); BTF_ID_LIST(hid_bpf_btf_ids) BTF_ID(func, hid_bpf_device_event) /* HID_BPF_PROG_TYPE_DEVICE_EVENT */ +BTF_ID(func, hid_bpf_rdesc_fixup) /* HID_BPF_PROG_TYPE_RDESC_FIXUP */ static int hid_bpf_max_programs(enum hid_bpf_prog_type type) { switch (type) { case HID_BPF_PROG_TYPE_DEVICE_EVENT: return HID_BPF_MAX_PROGS_PER_DEV; + case HID_BPF_PROG_TYPE_RDESC_FIXUP: + return 1; default: return -EINVAL; } @@ -233,6 +236,10 @@ static void hid_bpf_release_progs(struct work_struct *work) if (next->hdev == hdev && next->type == type) next->hdev = NULL; } + + /* if type was rdesc fixup, reconnect device */ + if (type == HID_BPF_PROG_TYPE_RDESC_FIXUP) + hid_bpf_reconnect(hdev); } } diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 6bf47e352f23..fa4436b8101e 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1218,7 +1218,8 @@ int hid_open_report(struct hid_device *device) return -ENODEV; size = device->dev_rsize; - buf = kmemdup(start, size, GFP_KERNEL); + /* call_hid_bpf_rdesc_fixup() ensures we work on a copy of rdesc */ + buf = call_hid_bpf_rdesc_fixup(device, start, &size); if (buf == NULL) return -ENOMEM; diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h index 1d24b72059bc..9b11f8f25ad5 100644 --- a/include/linux/hid_bpf.h +++ b/include/linux/hid_bpf.h @@ -74,6 +74,7 @@ enum hid_bpf_attach_flags { /* Following functions are tracepoints that BPF programs can attach to */ int hid_bpf_device_event(struct hid_bpf_ctx *ctx); +int hid_bpf_rdesc_fixup(struct hid_bpf_ctx *ctx); /* Following functions are kfunc that we export to BPF programs */ /* available everywhere in HID-BPF */ @@ -100,6 +101,7 @@ int __hid_bpf_tail_call(struct hid_bpf_ctx *ctx); enum hid_bpf_prog_type { HID_BPF_PROG_TYPE_UNDEF = -1, HID_BPF_PROG_TYPE_DEVICE_EVENT, /* an event is emitted from the device */ + HID_BPF_PROG_TYPE_RDESC_FIXUP, HID_BPF_PROG_TYPE_MAX, }; @@ -143,6 +145,7 @@ int hid_bpf_connect_device(struct hid_device *hdev); void hid_bpf_disconnect_device(struct hid_device *hdev); void hid_bpf_destroy_device(struct hid_device *hid); void hid_bpf_device_init(struct hid_device *hid); +u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size); #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) { return NULL; } @@ -150,6 +153,11 @@ static inline int hid_bpf_connect_device(struct hid_device *hdev) { return 0; } static inline void hid_bpf_disconnect_device(struct hid_device *hdev) {} static inline void hid_bpf_destroy_device(struct hid_device *hid) {} static inline void hid_bpf_device_init(struct hid_device *hid) {} +static inline u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size) +{ + return kmemdup(rdesc, *size, GFP_KERNEL); +} + #endif /* CONFIG_HID_BPF */ #endif /* __HID_BPF_H */ -- cgit v1.2.3 From 576e619ba4a47329bf55ada2d184e72529793fbd Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 16 Nov 2022 11:31:10 +0100 Subject: HID: bpf: return non NULL data pointer when CONFIG_HID_BPF is not set dispatch_hid_bpf_device_event() is supposed to return either an error, or a valid pointer to memory containing the data. Returning NULL simply makes a segfault when CONFIG_HID_BPF is not set for any processed event. Fixes: 658ee5a64fcfbbf ("HID: bpf: allocate data memory for device_event BPF program") Signed-off-by: Benjamin Tissoires Signed-off-by: Jiri Kosina --- include/linux/hid_bpf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h index 9b11f8f25ad5..3ca85ab91325 100644 --- a/include/linux/hid_bpf.h +++ b/include/linux/hid_bpf.h @@ -148,7 +148,7 @@ void hid_bpf_device_init(struct hid_device *hid); u8 *call_hid_bpf_rdesc_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size); #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) { return NULL; } + u8 *data, u32 *size, int interrupt) { return data; } static inline int hid_bpf_connect_device(struct hid_device *hdev) { return 0; } static inline void hid_bpf_disconnect_device(struct hid_device *hdev) {} static inline void hid_bpf_destroy_device(struct hid_device *hid) {} -- cgit v1.2.3 From 4b9a3f49f02bf682eedfde23ef56a8df82c1e5d2 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Fri, 13 Jan 2023 10:09:32 +0100 Subject: 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 Acked-by: Alexei Starovoitov Signed-off-by: Jiri Kosina --- Documentation/hid/hid-bpf.rst | 12 +++- drivers/hid/bpf/hid_bpf_dispatch.c | 18 ++++-- drivers/hid/bpf/hid_bpf_jmp_table.c | 126 +++++++++++++++++++----------------- include/linux/hid_bpf.h | 7 ++ 4 files changed, 95 insertions(+), 68 deletions(-) (limited to 'include/linux') diff --git a/Documentation/hid/hid-bpf.rst b/Documentation/hid/hid-bpf.rst index a55f191d49a3..c205f92b7bcc 100644 --- a/Documentation/hid/hid-bpf.rst +++ b/Documentation/hid/hid-bpf.rst @@ -427,12 +427,22 @@ program first:: prog_fd = bpf_program__fd(hid_skel->progs.attach_prog); err = bpf_prog_test_run_opts(prog_fd, &tattrs); - return err; + if (err) + return err; + + return args.retval; /* the fd of the created bpf_link */ } Our userspace program can now listen to notifications on the ring buffer, and is awaken only when the value changes. +When the userspace program doesn't need to listen to events anymore, it can just +close the returned fd from :c:func:`attach_filter`, which will tell the kernel to +detach the program from the HID device. + +Of course, in other use cases, the userspace program can also pin the fd to the +BPF filesystem through a call to :c:func:`bpf_obj_pin`, as with any bpf_link. + Controlling the device ---------------------- diff --git a/drivers/hid/bpf/hid_bpf_dispatch.c b/drivers/hid/bpf/hid_bpf_dispatch.c index 58e608ebf0fa..8d29fd361ab9 100644 --- a/drivers/hid/bpf/hid_bpf_dispatch.c +++ b/drivers/hid/bpf/hid_bpf_dispatch.c @@ -249,7 +249,9 @@ int hid_bpf_reconnect(struct hid_device *hdev) * @prog_fd: an fd in the user process representing the program to attach * @flags: any logical OR combination of &enum hid_bpf_attach_flags * - * @returns %0 on success, an error code otherwise. + * @returns an fd of a bpf_link object on success (> %0), an error code otherwise. + * Closing this fd will detach the program from the HID device (unless the bpf_link + * is pinned to the BPF file system). */ /* called from syscall */ noinline int @@ -257,7 +259,7 @@ hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags) { struct hid_device *hdev; struct device *dev; - int err, prog_type = hid_bpf_get_prog_attach_type(prog_fd); + int fd, err, prog_type = hid_bpf_get_prog_attach_type(prog_fd); if (!hid_bpf_ops) return -EINVAL; @@ -283,17 +285,19 @@ hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, __u32 flags) return err; } - err = __hid_bpf_attach_prog(hdev, prog_type, prog_fd, flags); - if (err) - return err; + fd = __hid_bpf_attach_prog(hdev, prog_type, prog_fd, flags); + if (fd < 0) + return fd; if (prog_type == HID_BPF_PROG_TYPE_RDESC_FIXUP) { err = hid_bpf_reconnect(hdev); - if (err) + if (err) { + close_fd(fd); return err; + } } - return 0; + return fd; } /** diff --git a/drivers/hid/bpf/hid_bpf_jmp_table.c b/drivers/hid/bpf/hid_bpf_jmp_table.c index 207972b028d9..b7eb5c5e435f 100644 --- a/drivers/hid/bpf/hid_bpf_jmp_table.c +++ b/drivers/hid/bpf/hid_bpf_jmp_table.c @@ -322,16 +322,6 @@ static int hid_bpf_insert_prog(int prog_fd, struct bpf_prog *prog) if (err) goto out; - /* - * The program has been safely inserted, decrement the reference count - * so it doesn't interfere with the number of actual user handles. - * This is safe to do because: - * - we overrite the put_ptr in the prog fd map - * - we also have a cleanup function that monitors when a program gets - * released and we manually do the cleanup in the prog fd map - */ - bpf_prog_sub(prog, 1); - /* return the index */ err = index; @@ -365,14 +355,46 @@ int hid_bpf_get_prog_attach_type(int prog_fd) return prog_type; } +static void hid_bpf_link_release(struct bpf_link *link) +{ + struct hid_bpf_link *hid_link = + container_of(link, struct hid_bpf_link, link); + + __clear_bit(hid_link->hid_table_index, jmp_table.enabled); + schedule_work(&release_work); +} + +static void hid_bpf_link_dealloc(struct bpf_link *link) +{ + struct hid_bpf_link *hid_link = + container_of(link, struct hid_bpf_link, link); + + kfree(hid_link); +} + +static void hid_bpf_link_show_fdinfo(const struct bpf_link *link, + struct seq_file *seq) +{ + seq_printf(seq, + "attach_type:\tHID-BPF\n"); +} + +static const struct bpf_link_ops hid_bpf_link_lops = { + .release = hid_bpf_link_release, + .dealloc = hid_bpf_link_dealloc, + .show_fdinfo = hid_bpf_link_show_fdinfo, +}; + /* called from syscall */ noinline int __hid_bpf_attach_prog(struct hid_device *hdev, enum hid_bpf_prog_type prog_type, int prog_fd, __u32 flags) { + struct bpf_link_primer link_primer; + struct hid_bpf_link *link; struct bpf_prog *prog = NULL; struct hid_bpf_prog_entry *prog_entry; - int cnt, err = -EINVAL, prog_idx = -1; + int cnt, err = -EINVAL, prog_table_idx = -1; /* take a ref on the prog itself */ 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, mutex_lock(&hid_bpf_attach_lock); + link = kzalloc(sizeof(*link), GFP_USER); + if (!link) { + err = -ENOMEM; + goto err_unlock; + } + + bpf_link_init(&link->link, BPF_LINK_TYPE_UNSPEC, + &hid_bpf_link_lops, prog); + /* do not attach too many programs to a given HID device */ cnt = hid_bpf_program_count(hdev, NULL, prog_type); if (cnt < 0) { err = cnt; - goto out_unlock; + goto err_unlock; } if (cnt >= hid_bpf_max_programs(prog_type)) { err = -E2BIG; - goto out_unlock; + goto err_unlock; } - prog_idx = hid_bpf_insert_prog(prog_fd, prog); + prog_table_idx = hid_bpf_insert_prog(prog_fd, prog); /* if the jmp table is full, abort */ - if (prog_idx < 0) { - err = prog_idx; - goto out_unlock; + if (prog_table_idx < 0) { + err = prog_table_idx; + goto err_unlock; } 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, /* we steal the ref here */ prog_entry->prog = prog; - prog_entry->idx = prog_idx; + prog_entry->idx = prog_table_idx; prog_entry->hdev = hdev; prog_entry->type = prog_type; /* finally store the index in the device list */ err = hid_bpf_populate_hdev(hdev, prog_type); + if (err) { + hid_bpf_release_prog_at(prog_table_idx); + goto err_unlock; + } + + link->hid_table_index = prog_table_idx; + + err = bpf_link_prime(&link->link, &link_primer); if (err) - hid_bpf_release_prog_at(prog_idx); + goto err_unlock; - out_unlock: mutex_unlock(&hid_bpf_attach_lock); - /* we only use prog as a key in the various tables, so we don't need to actually - * increment the ref count. - */ + return bpf_link_settle(&link_primer); + + err_unlock: + mutex_unlock(&hid_bpf_attach_lock); + bpf_prog_put(prog); + kfree(link); return err; } @@ -460,36 +501,10 @@ void __hid_bpf_destroy_device(struct hid_device *hdev) void call_hid_bpf_prog_put_deferred(struct work_struct *work) { - struct bpf_prog_aux *aux; - struct bpf_prog *prog; - bool found = false; - int i; - - aux = container_of(work, struct bpf_prog_aux, work); - prog = aux->prog; - - /* we don't need locking here because the entries in the progs table - * are stable: - * if there are other users (and the progs entries might change), we - * would simply not have been called. - */ - for (i = 0; i < HID_BPF_MAX_PROGS; i++) { - if (jmp_table.progs[i] == prog) { - __clear_bit(i, jmp_table.enabled); - found = true; - } - } - - if (found) - /* schedule release of all detached progs */ - schedule_work(&release_work); -} - -static void hid_bpf_prog_fd_array_put_ptr(void *ptr) -{ + /* kept around for patch readability, to be dropped in the next commmit */ } -#define HID_BPF_PROGS_COUNT 2 +#define HID_BPF_PROGS_COUNT 1 static struct bpf_link *links[HID_BPF_PROGS_COUNT]; static struct entrypoints_bpf *skel; @@ -528,8 +543,6 @@ void hid_bpf_free_links_and_skel(void) idx++; \ } while (0) -static struct bpf_map_ops hid_bpf_prog_fd_maps_ops; - int hid_bpf_preload_skel(void) { int err, idx = 0; @@ -548,14 +561,7 @@ int hid_bpf_preload_skel(void) goto out; } - /* our jump table is stealing refs, so we should not decrement on removal of elements */ - hid_bpf_prog_fd_maps_ops = *jmp_table.map->ops; - hid_bpf_prog_fd_maps_ops.map_fd_put_ptr = hid_bpf_prog_fd_array_put_ptr; - - jmp_table.map->ops = &hid_bpf_prog_fd_maps_ops; - ATTACH_AND_STORE_LINK(hid_tail_call); - ATTACH_AND_STORE_LINK(hid_bpf_prog_put_deferred); return 0; out: diff --git a/include/linux/hid_bpf.h b/include/linux/hid_bpf.h index 3ca85ab91325..e9afb61e6ee0 100644 --- a/include/linux/hid_bpf.h +++ b/include/linux/hid_bpf.h @@ -3,6 +3,7 @@ #ifndef __HID_BPF_H #define __HID_BPF_H +#include #include #include @@ -138,6 +139,12 @@ struct hid_bpf { spinlock_t progs_lock; /* protects RCU update of progs */ }; +/* specific HID-BPF link when a program is attached to a device */ +struct hid_bpf_link { + struct bpf_link link; + int hid_table_index; +}; + #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); -- cgit v1.2.3