Path: blob/master/drivers/hid/bpf/progs/hid_bpf_async.h
26285 views
/* SPDX-License-Identifier: GPL-2.0-only1* Copyright (c) 2024 Benjamin Tissoires2*/34#ifndef __HID_BPF_ASYNC_H__5#define __HID_BPF_ASYNC_H__67#ifndef HID_BPF_ASYNC_MAX_CTX8#error "HID_BPF_ASYNC_MAX_CTX should be set to the maximum number of concurrent async functions"9#endif /* HID_BPF_ASYNC_MAX_CTX */1011#define CLOCK_MONOTONIC 11213typedef int (*hid_bpf_async_callback_t)(void *map, int *key, void *value);1415enum hid_bpf_async_state {16HID_BPF_ASYNC_STATE_UNSET = 0,17HID_BPF_ASYNC_STATE_INITIALIZING,18HID_BPF_ASYNC_STATE_INITIALIZED,19HID_BPF_ASYNC_STATE_STARTING,20HID_BPF_ASYNC_STATE_RUNNING,21};2223struct hid_bpf_async_map_elem {24struct bpf_spin_lock lock;25enum hid_bpf_async_state state;26struct bpf_timer t;27struct bpf_wq wq;28u32 hid;29};3031struct {32__uint(type, BPF_MAP_TYPE_ARRAY);33__uint(max_entries, HID_BPF_ASYNC_MAX_CTX);34__type(key, u32);35__type(value, struct hid_bpf_async_map_elem);36} hid_bpf_async_ctx_map SEC(".maps");3738/**39* HID_BPF_ASYNC_CB: macro to define an async callback used in a bpf_wq40*41* The caller is responsible for allocating a key in the async map42* with hid_bpf_async_get_ctx().43*/44#define HID_BPF_ASYNC_CB(cb) \45cb(void *map, int *key, void *value); \46static __always_inline int \47____##cb(struct hid_bpf_ctx *ctx); \48typeof(cb(0, 0, 0)) cb(void *map, int *key, void *value) \49{ \50struct hid_bpf_async_map_elem *e; \51struct hid_bpf_ctx *ctx; \52\53e = (struct hid_bpf_async_map_elem *)value; \54ctx = hid_bpf_allocate_context(e->hid); \55if (!ctx) \56return 0; /* EPERM check */ \57\58e->state = HID_BPF_ASYNC_STATE_RUNNING; \59\60____##cb(ctx); \61\62e->state = HID_BPF_ASYNC_STATE_INITIALIZED; \63hid_bpf_release_context(ctx); \64return 0; \65} \66static __always_inline int \67____##cb6869/**70* ASYNC: macro to automatically handle async callbacks contexts71*72* Needs to be used in conjunction with HID_BPF_ASYNC_INIT and HID_BPF_ASYNC_DELAYED_CALL73*/74#define HID_BPF_ASYNC_FUN(fun) \75fun(struct hid_bpf_ctx *ctx); \76int ____key__##fun; \77static int ____async_init_##fun(void) \78{ \79____key__##fun = hid_bpf_async_get_ctx(); \80if (____key__##fun < 0) \81return ____key__##fun; \82return 0; \83} \84static int HID_BPF_ASYNC_CB(____##fun##_cb)(struct hid_bpf_ctx *hctx) \85{ \86return fun(hctx); \87} \88typeof(fun(0)) fun8990#define HID_BPF_ASYNC_INIT(fun) ____async_init_##fun()91#define HID_BPF_ASYNC_DELAYED_CALL(fun, ctx, delay) \92hid_bpf_async_delayed_call(ctx, delay, ____key__##fun, ____##fun##_cb)9394/*95* internal cb for starting the delayed work callback in a workqueue.96*/97static int __start_wq_timer_cb(void *map, int *key, void *value)98{99struct hid_bpf_async_map_elem *e = (struct hid_bpf_async_map_elem *)value;100101bpf_wq_start(&e->wq, 0);102103return 0;104}105106static int hid_bpf_async_find_empty_key(void)107{108int i;109110bpf_for(i, 0, HID_BPF_ASYNC_MAX_CTX) {111struct hid_bpf_async_map_elem *elem;112int key = i;113114elem = bpf_map_lookup_elem(&hid_bpf_async_ctx_map, &key);115if (!elem)116return -ENOMEM; /* should never happen */117118bpf_spin_lock(&elem->lock);119120if (elem->state == HID_BPF_ASYNC_STATE_UNSET) {121elem->state = HID_BPF_ASYNC_STATE_INITIALIZING;122bpf_spin_unlock(&elem->lock);123return i;124}125126bpf_spin_unlock(&elem->lock);127}128129return -EINVAL;130}131132static int hid_bpf_async_get_ctx(void)133{134int key = hid_bpf_async_find_empty_key();135struct hid_bpf_async_map_elem *elem;136int err;137138if (key < 0)139return key;140141elem = bpf_map_lookup_elem(&hid_bpf_async_ctx_map, &key);142if (!elem)143return -EINVAL;144145err = bpf_timer_init(&elem->t, &hid_bpf_async_ctx_map, CLOCK_MONOTONIC);146if (err)147return err;148149err = bpf_timer_set_callback(&elem->t, __start_wq_timer_cb);150if (err)151return err;152153err = bpf_wq_init(&elem->wq, &hid_bpf_async_ctx_map, 0);154if (err)155return err;156157elem->state = HID_BPF_ASYNC_STATE_INITIALIZED;158159return key;160}161162static inline u64 ms_to_ns(u64 milliseconds)163{164return (u64)milliseconds * 1000UL * 1000UL;165}166167static int hid_bpf_async_delayed_call(struct hid_bpf_ctx *hctx, u64 milliseconds, int key,168hid_bpf_async_callback_t wq_cb)169{170struct hid_bpf_async_map_elem *elem;171int err;172173elem = bpf_map_lookup_elem(&hid_bpf_async_ctx_map, &key);174if (!elem)175return -EINVAL;176177bpf_spin_lock(&elem->lock);178/* The wq must be:179* - HID_BPF_ASYNC_STATE_INITIALIZED -> it's been initialized and ready to be called180* - HID_BPF_ASYNC_STATE_RUNNING -> possible re-entry from the wq itself181*/182if (elem->state != HID_BPF_ASYNC_STATE_INITIALIZED &&183elem->state != HID_BPF_ASYNC_STATE_RUNNING) {184bpf_spin_unlock(&elem->lock);185return -EINVAL;186}187elem->state = HID_BPF_ASYNC_STATE_STARTING;188bpf_spin_unlock(&elem->lock);189190elem->hid = hctx->hid->id;191192err = bpf_wq_set_callback(&elem->wq, wq_cb, 0);193if (err)194return err;195196if (milliseconds) {197/* needed for every call because a cancel might unset this */198err = bpf_timer_set_callback(&elem->t, __start_wq_timer_cb);199if (err)200return err;201202err = bpf_timer_start(&elem->t, ms_to_ns(milliseconds), 0);203if (err)204return err;205206return 0;207}208209return bpf_wq_start(&elem->wq, 0);210}211212static inline int hid_bpf_async_call(struct hid_bpf_ctx *ctx, int key,213hid_bpf_async_callback_t wq_cb)214{215return hid_bpf_async_delayed_call(ctx, 0, key, wq_cb);216}217218#endif /* __HID_BPF_ASYNC_H__ */219220221