[PATCH 5/8] hid: bpf: hid_bpf_helpers: add helper for having read/write udev properties

Benjamin Tissoires posted 8 patches 3 days, 20 hours ago
[PATCH 5/8] hid: bpf: hid_bpf_helpers: add helper for having read/write udev properties
Posted by Benjamin Tissoires 3 days, 20 hours ago
We want udev-hid-bpf to be able to set udev properties by printing them
out after the BPF object has been loaded. This allows to make a query to
the device, and set a udev prop based on the answer.

Because the way udev works, the properties are cleared on bind/unbind,
and we need a way to store them. After several attempts to keep the
property alive without re-running the udev-hid-bpf tool to communicate
with the device, it came out that HID-BPF maps are pinned in the bpffs
and we can then query them.

So the following would export a UDEV property in the bpffs:
   EXPORT_UDEV_PROP(HID_FOO, 32);

   SEC("syscall")
   int probe(struct hid_bpf_probe_args *ctx)
   {
     const char *foo = "foo";
     UDEV_PROP_SPRINTF(HID_FOO, "%s", foo);

     return 0;
   }

Then, we can debug it with a simple cat:
   sudo cat /sys/fs/bpf/hid/.../UDEV_PROP_HID_FOO
0: {['f','o','o',],}

This way, the property is always accessible without talking to the
device

Link: https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/merge_requests/220
Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>
---
 drivers/hid/bpf/progs/hid_bpf_helpers.h | 38 +++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)

diff --git a/drivers/hid/bpf/progs/hid_bpf_helpers.h b/drivers/hid/bpf/progs/hid_bpf_helpers.h
index c67facdefff3..0fd8e7d90742 100644
--- a/drivers/hid/bpf/progs/hid_bpf_helpers.h
+++ b/drivers/hid/bpf/progs/hid_bpf_helpers.h
@@ -340,6 +340,44 @@ DEFINE_GUARD(bpf_spin, struct bpf_spin_lock, bpf_spin_lock, bpf_spin_unlock);
 #define hid_bpf_cpu_to_be32(x)	bpf_htonl(x)
 #define hid_bpf_cpu_to_be64(x)	bpf_cpu_to_be64(x)
 
+/*
+ * The following macros are helpers for exporting udev properties:
+ *
+ * EXPORT_UDEV_PROP(name, len) generates:
+ *  - a map with a single element UDEV_PROP_##name, of size len
+ *  - a const global declaration of that len: SIZEOF_##name
+ *
+ * udev_prop_ptr(name) retrieves the data pointer behind the map.
+ *
+ * UDEV_PROP_SPRINTF(name, fmt, ...) writes data into the udev property.
+ *
+ *  Can be used as such:
+ *  EXPORT_UDEV_PROP(HID_FOO, 32);
+ *
+ *  SEC("syscall")
+ *  int probe(struct hid_bpf_probe_args *ctx)
+ *  {
+ *    const char *foo = "foo";
+ *    UDEV_PROP_SPRINTF(HID_FOO, "%s", foo);
+ *
+ *    return 0;
+ *  }
+ */
+#define EXPORT_UDEV_PROP(name, len) \
+	const __u32 SIZEOF_##name = len; \
+	struct COMBINE(udev_prop, __LINE__) { \
+		__uint(type, BPF_MAP_TYPE_ARRAY); \
+		__uint(max_entries, 1); \
+		__type(key, __u32); \
+		__type(value, __u8[len]); \
+	} UDEV_PROP_##name SEC(".maps");
+
+#define udev_prop_ptr(name) \
+	bpf_map_lookup_elem(&UDEV_PROP_##name, &(__u32){0})
+
+#define UDEV_PROP_SPRINTF(name, fmt, ...) \
+	BPF_SNPRINTF(udev_prop_ptr(name), SIZEOF_##name, fmt, ##__VA_ARGS__)
+
 static inline __maybe_unused __u16 field_start_byte(struct hid_rdesc_field *field)
 {
 	return field->bits_start / 8;

-- 
2.53.0