[PATCH 2/8] HID: bpf: hid_bpf_helpers: provide a cleanup functions

Benjamin Tissoires posted 8 patches 3 days, 20 hours ago
[PATCH 2/8] HID: bpf: hid_bpf_helpers: provide a cleanup functions
Posted by Benjamin Tissoires 3 days, 20 hours ago
Combination of 2 udev-hid-bpf commits:
bpf: hid_bpf_helpers: provide a cleanup function for hid_bpf_release_context
bpf: helpers: add guard(bpf_spin) macro

Link: https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/merge_requests/221
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
---
 drivers/hid/bpf/progs/hid_bpf_async.h   | 36 +++++++--------
 drivers/hid/bpf/progs/hid_bpf_helpers.h | 80 +++++++++++++++++++++++++++++++++
 2 files changed, 98 insertions(+), 18 deletions(-)

diff --git a/drivers/hid/bpf/progs/hid_bpf_async.h b/drivers/hid/bpf/progs/hid_bpf_async.h
index 9ab585434239..877bb7e81f03 100644
--- a/drivers/hid/bpf/progs/hid_bpf_async.h
+++ b/drivers/hid/bpf/progs/hid_bpf_async.h
@@ -116,15 +116,14 @@ static int hid_bpf_async_find_empty_key(void)
 		if (!elem)
 			return -ENOMEM; /* should never happen */
 
-		bpf_spin_lock(&elem->lock);
+		{
+			guard(bpf_spin)(&elem->lock);
 
-		if (elem->state == HID_BPF_ASYNC_STATE_UNSET) {
-			elem->state = HID_BPF_ASYNC_STATE_INITIALIZING;
-			bpf_spin_unlock(&elem->lock);
-			return i;
+			if (elem->state == HID_BPF_ASYNC_STATE_UNSET) {
+				elem->state = HID_BPF_ASYNC_STATE_INITIALIZING;
+				return i;
+			}
 		}
-
-		bpf_spin_unlock(&elem->lock);
 	}
 
 	return -EINVAL;
@@ -175,18 +174,19 @@ static int hid_bpf_async_delayed_call(struct hid_bpf_ctx *hctx, u64 milliseconds
 	if (!elem)
 		return -EINVAL;
 
-	bpf_spin_lock(&elem->lock);
-	/* The wq must be:
-	 * - HID_BPF_ASYNC_STATE_INITIALIZED -> it's been initialized and ready to be called
-	 * - HID_BPF_ASYNC_STATE_RUNNING -> possible re-entry from the wq itself
-	 */
-	if (elem->state != HID_BPF_ASYNC_STATE_INITIALIZED &&
-	    elem->state != HID_BPF_ASYNC_STATE_RUNNING) {
-		bpf_spin_unlock(&elem->lock);
-		return -EINVAL;
+	{
+		guard(bpf_spin)(&elem->lock);
+
+		/* The wq must be:
+		 * - HID_BPF_ASYNC_STATE_INITIALIZED -> it's been initialized and ready to be called
+		 * - HID_BPF_ASYNC_STATE_RUNNING -> possible re-entry from the wq itself
+		 */
+		if (elem->state != HID_BPF_ASYNC_STATE_INITIALIZED &&
+		    elem->state != HID_BPF_ASYNC_STATE_RUNNING)
+			return -EINVAL;
+
+		elem->state = HID_BPF_ASYNC_STATE_STARTING;
 	}
-	elem->state = HID_BPF_ASYNC_STATE_STARTING;
-	bpf_spin_unlock(&elem->lock);
 
 	elem->hid = hctx->hid->id;
 
diff --git a/drivers/hid/bpf/progs/hid_bpf_helpers.h b/drivers/hid/bpf/progs/hid_bpf_helpers.h
index 228f8d787567..5e3ffca1ed7b 100644
--- a/drivers/hid/bpf/progs/hid_bpf_helpers.h
+++ b/drivers/hid/bpf/progs/hid_bpf_helpers.h
@@ -40,6 +40,86 @@ extern int bpf_wq_set_callback(struct bpf_wq *wq,
 #define HID_MAX_DESCRIPTOR_SIZE	4096
 #define HID_IGNORE_EVENT	-1
 
+/**
+ * Use: _cleanup_(somefunction) struct foo *bar;
+ */
+#define _cleanup_(_x) __attribute__((cleanup(_x)))
+
+/**
+ * Use: _release_(foo) *bar;
+ *
+ * This requires foo_releasep() to be present, use DEFINE_RELEASE_CLEANUP_FUNC.
+ */
+#define _release_(_type) struct _type __attribute__((cleanup(_type##_releasep)))
+
+/**
+ * Define a cleanup function for the struct type foo with a matching
+ * foo_release(). Use:
+ * DEFINE_RELEASE_CLEANUP_FUNC(foo)
+ * _unref_(foo) struct foo *bar;
+ */
+#define DEFINE_RELEASE_CLEANUP_FUNC(_type)				\
+	static inline void _type##_releasep(struct _type **_p) {	\
+		if (*_p)						\
+			_type##_release(*_p);				\
+	}								\
+	struct __useless_struct_to_allow_trailing_semicolon__
+
+/* for being able to have a cleanup function */
+#define hid_bpf_ctx_release hid_bpf_release_context
+DEFINE_RELEASE_CLEANUP_FUNC(hid_bpf_ctx);
+
+/*
+ * Kernel-style guard macros adapted for BPF
+ * Based on include/linux/cleanup.h from the Linux kernel
+ *
+ * These provide automatic lock/unlock using __attribute__((cleanup))
+ * similar to how _release_() works for contexts.
+ */
+
+/**
+ * DEFINE_GUARD(name, type, lock, unlock):
+ *	Define a guard for automatic lock/unlock using the same pattern as _release_()
+ *	@name: identifier for the guard (e.g., bpf_spin)
+ *	@type: lock variable type (e.g., struct bpf_spin_lock)
+ *	@lock: lock function name (e.g., bpf_spin_lock)
+ *	@unlock: unlock function name (e.g., bpf_spin_unlock)
+ *
+ * guard(name):
+ *	Declare and lock in one statement - lock held until end of scope
+ *
+ * Example:
+ *	DEFINE_GUARD(bpf_spin, struct bpf_spin_lock, bpf_spin_lock, bpf_spin_unlock)
+ *
+ *	void foo(struct bpf_spin_lock *lock) {
+ *		guard(bpf_spin)(lock);
+ *		// lock held until end of scope
+ *	}
+ */
+
+/* Guard helper struct - stores lock pointer for cleanup */
+#define DEFINE_GUARD(_name, _type, _lock, _unlock)			\
+struct _name##_guard {							\
+	_type *lock;							\
+};									\
+static inline void _name##_guard_cleanup(struct _name##_guard *g) {	\
+	if (g && g->lock) 						\
+		_unlock(g->lock);					\
+}									\
+static inline struct _name##_guard _name##_guard_init(_type *l) {	\
+	if (l)								\
+		_lock(l);						\
+	return (struct _name##_guard){.lock = l};			\
+}									\
+struct __useless_struct_to_allow_trailing_semicolon__
+
+#define guard(_name) \
+	struct _name##_guard COMBINE(guard, __LINE__) __attribute__((cleanup(_name##_guard_cleanup))) = \
+		_name##_guard_init
+
+/* Define BPF spinlock guard */
+DEFINE_GUARD(bpf_spin, struct bpf_spin_lock, bpf_spin_lock, bpf_spin_unlock);
+
 /* extracted from <linux/input.h> */
 #define BUS_ANY			0x00
 #define BUS_PCI			0x01

-- 
2.53.0