netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices
@ 2022-03-04 17:28 Benjamin Tissoires
  2022-03-04 17:28 ` [PATCH bpf-next v2 01/28] bpf: add new is_sys_admin_prog_type() helper Benjamin Tissoires
                   ` (29 more replies)
  0 siblings, 30 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

Hi,

This is a followup of my v1 at [0].

The short summary of the previous cover letter and discussions is that
HID could benefit from BPF for the following use cases:

- simple fixup of report descriptor:
  benefits are faster development time and testing, with the produced
  bpf program being shipped in the kernel directly (the shipping part
  is *not* addressed here).

- Universal Stylus Interface:
  allows a user-space program to define its own kernel interface

- Surface Dial:
  somehow similar to the previous one except that userspace can decide
  to change the shape of the exported device

- firewall:
  still partly missing there, there is not yet interception of hidraw
  calls, but it's coming in a followup series, I promise

- tracing:
  well, tracing.


I tried to address as many comments as I could and here is the short log
of changes:

v2:
===

- split the series by subsystem (bpf, HID, libbpf, selftests and
  samples)

- Added an extra patch at the beginning to not require CAP_NET_ADMIN for
  BPF_PROG_TYPE_LIRC_MODE2 (please shout if this is wrong)

- made the bpf context attached to HID program of dynamic size:
  * the first 1 kB will be able to be addressed directly
  * the rest can be retrieved through bpf_hid_{set|get}_data
    (note that I am definitivey not happy with that API, because there
    is part of it in bits and other in bytes. ouch)

- added an extra patch to prevent non GPL HID bpf programs to be loaded
  of type BPF_PROG_TYPE_HID
  * same here, not really happy but I don't know where to put that check
    in verifier.c

- added a new flag BPF_F_INSERT_HEAD for BPF_LINK_CREATE syscall when in
  used with HID program types.
  * this flag is used for tracing, to be able to load a program before
    any others that might already have been inserted and that might
    change the data stream.

Cheers,
Benjamin



[0] https://lore.kernel.org/linux-input/20220224110828.2168231-1-benjamin.tissoires@redhat.com/T/#t


Benjamin Tissoires (28):
  bpf: add new is_sys_admin_prog_type() helper
  bpf: introduce hid program type
  HID: hook up with bpf
  libbpf: add HID program type and API
  selftests/bpf: add tests for the HID-bpf initial implementation
  samples/bpf: add new hid_mouse example
  bpf/hid: add a new attach type to change the report descriptor
  HID: allow to change the report descriptor from an eBPF program
  libbpf: add new attach type BPF_HID_RDESC_FIXUP
  selftests/bpf: add report descriptor fixup tests
  samples/bpf: add a report descriptor fixup
  bpf/hid: add hid_{get|set}_data helpers
  HID: bpf: implement hid_bpf_get|set_data
  selftests/bpf: add tests for hid_{get|set}_data helpers
  bpf/hid: add new BPF type to trigger commands from userspace
  libbpf: add new attach type BPF_HID_USER_EVENT
  selftests/bpf: add test for user call of HID bpf programs
  selftests/bpf: hid: rely on uhid event to know if a test device is
    ready
  bpf/hid: add bpf_hid_raw_request helper function
  HID: add implementation of bpf_hid_raw_request
  selftests/bpf: add tests for bpf_hid_hw_request
  bpf/verifier: prevent non GPL programs to be loaded against HID
  HID: bpf: compute only the required buffer size for the device
  HID: bpf: only call hid_bpf_raw_event() if a ctx is available
  bpf/hid: Add a flag to add the program at the beginning of the list
  libbpf: add handling for BPF_F_INSERT_HEAD in HID programs
  selftests/bpf: Add a test for BPF_F_INSERT_HEAD
  samples/bpf: fix bpf_program__attach_hid() api change

 drivers/hid/Makefile                         |   1 +
 drivers/hid/hid-bpf.c                        | 361 +++++++++
 drivers/hid/hid-core.c                       |  34 +-
 include/linux/bpf-hid.h                      | 129 +++
 include/linux/bpf_types.h                    |   4 +
 include/linux/hid.h                          |  25 +
 include/uapi/linux/bpf.h                     |  59 ++
 include/uapi/linux/bpf_hid.h                 |  50 ++
 kernel/bpf/Makefile                          |   3 +
 kernel/bpf/hid.c                             | 652 +++++++++++++++
 kernel/bpf/syscall.c                         |  26 +-
 kernel/bpf/verifier.c                        |   7 +
 samples/bpf/.gitignore                       |   1 +
 samples/bpf/Makefile                         |   4 +
 samples/bpf/hid_mouse_kern.c                 |  91 +++
 samples/bpf/hid_mouse_user.c                 | 129 +++
 tools/include/uapi/linux/bpf.h               |  59 ++
 tools/lib/bpf/libbpf.c                       |  22 +-
 tools/lib/bpf/libbpf.h                       |   2 +
 tools/lib/bpf/libbpf.map                     |   1 +
 tools/testing/selftests/bpf/prog_tests/hid.c | 788 +++++++++++++++++++
 tools/testing/selftests/bpf/progs/hid.c      | 216 +++++
 22 files changed, 2649 insertions(+), 15 deletions(-)
 create mode 100644 drivers/hid/hid-bpf.c
 create mode 100644 include/linux/bpf-hid.h
 create mode 100644 include/uapi/linux/bpf_hid.h
 create mode 100644 kernel/bpf/hid.c
 create mode 100644 samples/bpf/hid_mouse_kern.c
 create mode 100644 samples/bpf/hid_mouse_user.c
 create mode 100644 tools/testing/selftests/bpf/prog_tests/hid.c
 create mode 100644 tools/testing/selftests/bpf/progs/hid.c

-- 
2.35.1


^ permalink raw reply	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 01/28] bpf: add new is_sys_admin_prog_type() helper
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-04 23:12   ` Song Liu
  2022-03-09  8:27   ` Sean Young
  2022-03-04 17:28 ` [PATCH bpf-next v2 02/28] bpf: introduce hid program type Benjamin Tissoires
                   ` (28 subsequent siblings)
  29 siblings, 2 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires, Sean Young

LIRC_MODE2 does not really need net_admin capability, but only sys_admin.

Extract a new helper for it, it will be also used for the HID bpf
implementation.

Cc: Sean Young <sean@mess.org>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

new in v2
---
 kernel/bpf/syscall.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index db402ebc5570..cc570891322b 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -2165,7 +2165,6 @@ static bool is_net_admin_prog_type(enum bpf_prog_type prog_type)
 	case BPF_PROG_TYPE_LWT_SEG6LOCAL:
 	case BPF_PROG_TYPE_SK_SKB:
 	case BPF_PROG_TYPE_SK_MSG:
-	case BPF_PROG_TYPE_LIRC_MODE2:
 	case BPF_PROG_TYPE_FLOW_DISSECTOR:
 	case BPF_PROG_TYPE_CGROUP_DEVICE:
 	case BPF_PROG_TYPE_CGROUP_SOCK:
@@ -2202,6 +2201,17 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type)
 	}
 }
 
+static bool is_sys_admin_prog_type(enum bpf_prog_type prog_type)
+{
+	switch (prog_type) {
+	case BPF_PROG_TYPE_LIRC_MODE2:
+	case BPF_PROG_TYPE_EXT: /* extends any prog */
+		return true;
+	default:
+		return false;
+	}
+}
+
 /* last field in 'union bpf_attr' used by this command */
 #define	BPF_PROG_LOAD_LAST_FIELD core_relo_rec_size
 
@@ -2252,6 +2262,8 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr)
 		return -EPERM;
 	if (is_perfmon_prog_type(type) && !perfmon_capable())
 		return -EPERM;
+	if (is_sys_admin_prog_type(type) && !capable(CAP_SYS_ADMIN))
+		return -EPERM;
 
 	/* attach_prog_fd/attach_btf_obj_fd can specify fd of either bpf_prog
 	 * or btf, we need to check which one it is
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 02/28] bpf: introduce hid program type
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
  2022-03-04 17:28 ` [PATCH bpf-next v2 01/28] bpf: add new is_sys_admin_prog_type() helper Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-04 18:21   ` Greg KH
                     ` (2 more replies)
  2022-03-04 17:28 ` [PATCH bpf-next v2 03/28] HID: hook up with bpf Benjamin Tissoires
                   ` (27 subsequent siblings)
  29 siblings, 3 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

HID is a protocol that could benefit from using BPF too.

This patch implements a net-like use of BPF capability for HID.
Any incoming report coming from the device can be injected into a series
of BPF programs that can modify it or even discard it by setting the
size in the context to 0.

The kernel/bpf implementation is based on net-namespace.c, with only
the bpf_link part kept, there is no real points in keeping the
bpf_prog_{attach|detach} API.

The implementation here is only focusing on the bpf changes. The HID
changes that hooks onto this are coming in a separate patch.

Given that HID can be compiled in as a module, and the functions that
kernel/bpf/hid.c needs to call in hid.ko are exported in struct hid_hooks.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

changes in v2:
- split the series by bpf/libbpf/hid/selftests and samples
- unsigned long -> __u16 in uapi/linux/bpf_hid.h
- change the bpf_ctx to be of variable size, with a min of 1024 bytes
- make this 1 kB available directly from bpf program, the rest will
  need a helper
- add some more doc comments in uapi
---
 include/linux/bpf-hid.h        | 108 ++++++++
 include/linux/bpf_types.h      |   4 +
 include/linux/hid.h            |   5 +
 include/uapi/linux/bpf.h       |   7 +
 include/uapi/linux/bpf_hid.h   |  39 +++
 kernel/bpf/Makefile            |   3 +
 kernel/bpf/hid.c               | 437 +++++++++++++++++++++++++++++++++
 kernel/bpf/syscall.c           |   8 +
 tools/include/uapi/linux/bpf.h |   7 +
 9 files changed, 618 insertions(+)
 create mode 100644 include/linux/bpf-hid.h
 create mode 100644 include/uapi/linux/bpf_hid.h
 create mode 100644 kernel/bpf/hid.c

diff --git a/include/linux/bpf-hid.h b/include/linux/bpf-hid.h
new file mode 100644
index 000000000000..3cda78051b5f
--- /dev/null
+++ b/include/linux/bpf-hid.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _BPF_HID_H
+#define _BPF_HID_H
+
+#include <linux/mutex.h>
+#include <uapi/linux/bpf.h>
+#include <uapi/linux/bpf_hid.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+
+struct bpf_prog;
+struct bpf_prog_array;
+struct hid_device;
+
+enum bpf_hid_attach_type {
+	BPF_HID_ATTACH_INVALID = -1,
+	BPF_HID_ATTACH_DEVICE_EVENT = 0,
+	MAX_BPF_HID_ATTACH_TYPE
+};
+
+struct bpf_hid {
+	struct hid_bpf_ctx *ctx;
+
+	/* Array of programs to run compiled from links */
+	struct bpf_prog_array __rcu *run_array[MAX_BPF_HID_ATTACH_TYPE];
+	struct list_head links[MAX_BPF_HID_ATTACH_TYPE];
+};
+
+static inline enum bpf_hid_attach_type
+to_bpf_hid_attach_type(enum bpf_attach_type attach_type)
+{
+	switch (attach_type) {
+	case BPF_HID_DEVICE_EVENT:
+		return BPF_HID_ATTACH_DEVICE_EVENT;
+	default:
+		return BPF_HID_ATTACH_INVALID;
+	}
+}
+
+static inline struct hid_bpf_ctx *bpf_hid_allocate_ctx(struct hid_device *hdev,
+						       size_t data_size)
+{
+	struct hid_bpf_ctx *ctx;
+
+	/* ensure data_size is between min and max */
+	data_size = clamp_val(data_size,
+			      HID_BPF_MIN_BUFFER_SIZE,
+			      HID_BPF_MAX_BUFFER_SIZE);
+
+	ctx = kzalloc(sizeof(*ctx) + data_size, GFP_KERNEL);
+	if (!ctx)
+		return ERR_PTR(-ENOMEM);
+
+	ctx->hdev = hdev;
+	ctx->allocated_size = data_size;
+
+	return ctx;
+}
+
+union bpf_attr;
+struct bpf_prog;
+
+#if IS_ENABLED(CONFIG_HID)
+int bpf_hid_prog_query(const union bpf_attr *attr,
+		       union bpf_attr __user *uattr);
+int bpf_hid_link_create(const union bpf_attr *attr,
+			struct bpf_prog *prog);
+#else
+static inline int bpf_hid_prog_query(const union bpf_attr *attr,
+				     union bpf_attr __user *uattr)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline int bpf_hid_link_create(const union bpf_attr *attr,
+				      struct bpf_prog *prog)
+{
+	return -EOPNOTSUPP;
+}
+#endif
+
+static inline bool bpf_hid_link_empty(struct bpf_hid *bpf,
+				      enum bpf_hid_attach_type type)
+{
+	return list_empty(&bpf->links[type]);
+}
+
+struct bpf_hid_hooks {
+	struct hid_device *(*hdev_from_fd)(int fd);
+	int (*link_attach)(struct hid_device *hdev, enum bpf_hid_attach_type type);
+	void (*array_detached)(struct hid_device *hdev, enum bpf_hid_attach_type type);
+};
+
+#ifdef CONFIG_BPF
+int bpf_hid_init(struct hid_device *hdev);
+void bpf_hid_exit(struct hid_device *hdev);
+void bpf_hid_set_hooks(struct bpf_hid_hooks *hooks);
+#else
+static inline int bpf_hid_init(struct hid_device *hdev)
+{
+	return 0;
+}
+
+static inline void bpf_hid_exit(struct hid_device *hdev) {}
+static inline void bpf_hid_set_hooks(struct bpf_hid_hooks *hooks) {}
+#endif
+
+#endif /* _BPF_HID_H */
diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
index 48a91c51c015..1509862aacc4 100644
--- a/include/linux/bpf_types.h
+++ b/include/linux/bpf_types.h
@@ -76,6 +76,10 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_EXT, bpf_extension,
 BPF_PROG_TYPE(BPF_PROG_TYPE_LSM, lsm,
 	       void *, void *)
 #endif /* CONFIG_BPF_LSM */
+#if IS_ENABLED(CONFIG_HID)
+BPF_PROG_TYPE(BPF_PROG_TYPE_HID, hid,
+	      __u32, u32)
+#endif
 #endif
 BPF_PROG_TYPE(BPF_PROG_TYPE_SYSCALL, bpf_syscall,
 	      void *, void *)
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 7487b0586fe6..56f6f4ad45a7 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -15,6 +15,7 @@
 
 
 #include <linux/bitops.h>
+#include <linux/bpf-hid.h>
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/list.h>
@@ -639,6 +640,10 @@ struct hid_device {							/* device report descriptor */
 	struct list_head debug_list;
 	spinlock_t  debug_list_lock;
 	wait_queue_head_t debug_wait;
+
+#ifdef CONFIG_BPF
+	struct bpf_hid bpf;
+#endif
 };
 
 #define to_hid_device(pdev) \
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index afe3d0d7f5f2..5978b92cacd3 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -952,6 +952,7 @@ enum bpf_prog_type {
 	BPF_PROG_TYPE_LSM,
 	BPF_PROG_TYPE_SK_LOOKUP,
 	BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */
+	BPF_PROG_TYPE_HID,
 };
 
 enum bpf_attach_type {
@@ -997,6 +998,7 @@ enum bpf_attach_type {
 	BPF_SK_REUSEPORT_SELECT,
 	BPF_SK_REUSEPORT_SELECT_OR_MIGRATE,
 	BPF_PERF_EVENT,
+	BPF_HID_DEVICE_EVENT,
 	__MAX_BPF_ATTACH_TYPE
 };
 
@@ -1011,6 +1013,7 @@ enum bpf_link_type {
 	BPF_LINK_TYPE_NETNS = 5,
 	BPF_LINK_TYPE_XDP = 6,
 	BPF_LINK_TYPE_PERF_EVENT = 7,
+	BPF_LINK_TYPE_HID = 8,
 
 	MAX_BPF_LINK_TYPE,
 };
@@ -5870,6 +5873,10 @@ struct bpf_link_info {
 		struct {
 			__u32 ifindex;
 		} xdp;
+		struct  {
+			__s32 hidraw_ino;
+			__u32 attach_type;
+		} hid;
 	};
 } __attribute__((aligned(8)));
 
diff --git a/include/uapi/linux/bpf_hid.h b/include/uapi/linux/bpf_hid.h
new file mode 100644
index 000000000000..975ca5bd526f
--- /dev/null
+++ b/include/uapi/linux/bpf_hid.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later WITH Linux-syscall-note */
+
+/*
+ *  HID BPF public headers
+ *
+ *  Copyright (c) 2021 Benjamin Tissoires
+ */
+
+#ifndef _UAPI__LINUX_BPF_HID_H__
+#define _UAPI__LINUX_BPF_HID_H__
+
+#include <linux/types.h>
+
+/*
+ * The first 1024 bytes are available directly in the bpf programs.
+ * To access the rest of the data (if allocated_size is bigger
+ * than 1024, you need to use bpf_hid_ helpers.
+ */
+#define HID_BPF_MIN_BUFFER_SIZE		1024
+#define HID_BPF_MAX_BUFFER_SIZE		16384		/* in sync with HID_MAX_BUFFER_SIZE */
+
+struct hid_device;
+
+enum hid_bpf_event {
+	HID_BPF_UNDEF = 0,
+	HID_BPF_DEVICE_EVENT,		/* when attach type is BPF_HID_DEVICE_EVENT */
+};
+
+struct hid_bpf_ctx {
+	enum hid_bpf_event type;	/* read-only */
+	__u16 allocated_size;		/* the allocated size of data below (RO) */
+	struct hid_device *hdev;	/* read-only */
+
+	__u16 size;			/* used size in data (RW) */
+	__u8 data[];			/* data buffer (RW) */
+};
+
+#endif /* _UAPI__LINUX_BPF_HID_H__ */
+
diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
index c1a9be6a4b9f..8d5619d3d7e5 100644
--- a/kernel/bpf/Makefile
+++ b/kernel/bpf/Makefile
@@ -35,6 +35,9 @@ ifeq ($(CONFIG_BPF_JIT),y)
 obj-$(CONFIG_BPF_SYSCALL) += bpf_struct_ops.o
 obj-${CONFIG_BPF_LSM} += bpf_lsm.o
 endif
+ifneq ($(CONFIG_HID),)
+obj-$(CONFIG_BPF_SYSCALL) += hid.o
+endif
 obj-$(CONFIG_BPF_PRELOAD) += preload/
 
 obj-$(CONFIG_BPF_SYSCALL) += relo_core.o
diff --git a/kernel/bpf/hid.c b/kernel/bpf/hid.c
new file mode 100644
index 000000000000..db7f75a0a812
--- /dev/null
+++ b/kernel/bpf/hid.c
@@ -0,0 +1,437 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * based on kernel/bpf/net-namespace.c
+ */
+
+#include <linux/bpf.h>
+#include <linux/bpf-hid.h>
+#include <linux/filter.h>
+#include <linux/hid.h>
+#include <linux/hidraw.h>
+
+/*
+ * Functions to manage BPF programs attached to hid
+ */
+
+struct bpf_hid_link {
+	struct bpf_link link;
+	enum bpf_attach_type type;
+	enum bpf_hid_attach_type hid_type;
+
+	/* Must be accessed with bpf_hid_mutex held. */
+	struct hid_device *hdev;
+	struct list_head node; /* node in list of links attached to hid */
+};
+
+/* Protects updates to bpf_hid */
+DEFINE_MUTEX(bpf_hid_mutex);
+
+static struct bpf_hid_hooks hid_hooks = {0};
+
+void bpf_hid_set_hooks(struct bpf_hid_hooks *hooks)
+{
+	if (hooks)
+		hid_hooks = *hooks;
+	else
+		memset(&hid_hooks, 0, sizeof(hid_hooks));
+}
+EXPORT_SYMBOL_GPL(bpf_hid_set_hooks);
+
+static const struct bpf_func_proto *
+hid_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
+{
+	switch (func_id) {
+	default:
+		return bpf_base_func_proto(func_id);
+	}
+}
+
+static bool hid_is_valid_access(int off, int size,
+				enum bpf_access_type access_type,
+				const struct bpf_prog *prog,
+				struct bpf_insn_access_aux *info)
+{
+	/* everything not in ctx is prohibited */
+	if (off < 0 || off + size > sizeof(struct hid_bpf_ctx) + HID_BPF_MIN_BUFFER_SIZE)
+		return false;
+
+	switch (off) {
+	/* type, allocated_size, hdev are read-only */
+	case bpf_ctx_range_till(struct hid_bpf_ctx, type, hdev):
+		return access_type == BPF_READ;
+	}
+
+	/* everything else is read/write */
+	return true;
+}
+
+const struct bpf_verifier_ops hid_verifier_ops = {
+	.get_func_proto  = hid_func_proto,
+	.is_valid_access = hid_is_valid_access
+};
+
+/* Must be called with bpf_hid_mutex held. */
+static void bpf_hid_run_array_detach(struct hid_device *hdev,
+				     enum bpf_hid_attach_type type)
+{
+	struct bpf_prog_array *run_array;
+
+	run_array = rcu_replace_pointer(hdev->bpf.run_array[type], NULL,
+					lockdep_is_held(&bpf_hid_mutex));
+	bpf_prog_array_free(run_array);
+
+	if (hid_hooks.array_detached)
+		hid_hooks.array_detached(hdev, type);
+}
+
+static int link_index(struct hid_device *hdev, enum bpf_hid_attach_type type,
+		      struct bpf_hid_link *link)
+{
+	struct bpf_hid_link *pos;
+	int i = 0;
+
+	list_for_each_entry(pos, &hdev->bpf.links[type], node) {
+		if (pos == link)
+			return i;
+		i++;
+	}
+	return -ENOENT;
+}
+
+static int link_count(struct hid_device *hdev, enum bpf_hid_attach_type type)
+{
+	struct list_head *pos;
+	int i = 0;
+
+	list_for_each(pos, &hdev->bpf.links[type])
+		i++;
+	return i;
+}
+
+static void fill_prog_array(struct hid_device *hdev, enum bpf_hid_attach_type type,
+			    struct bpf_prog_array *prog_array)
+{
+	struct bpf_hid_link *pos;
+	unsigned int i = 0;
+
+	list_for_each_entry(pos, &hdev->bpf.links[type], node) {
+		prog_array->items[i].prog = pos->link.prog;
+		i++;
+	}
+}
+
+static void bpf_hid_link_release(struct bpf_link *link)
+{
+	struct bpf_hid_link *hid_link =
+		container_of(link, struct bpf_hid_link, link);
+	enum bpf_hid_attach_type type = hid_link->hid_type;
+	struct bpf_prog_array *old_array, *new_array;
+	struct hid_device *hdev;
+	int cnt, idx;
+
+	mutex_lock(&bpf_hid_mutex);
+
+	hdev = hid_link->hdev;
+	if (!hdev)
+		goto out_unlock;
+
+	/* Remember link position in case of safe delete */
+	idx = link_index(hdev, type, hid_link);
+	list_del(&hid_link->node);
+
+	cnt = link_count(hdev, type);
+	if (!cnt) {
+		bpf_hid_run_array_detach(hdev, type);
+		goto out_unlock;
+	}
+
+	old_array = rcu_dereference_protected(hdev->bpf.run_array[type],
+					      lockdep_is_held(&bpf_hid_mutex));
+	new_array = bpf_prog_array_alloc(cnt, GFP_KERNEL);
+	if (!new_array) {
+		WARN_ON(bpf_prog_array_delete_safe_at(old_array, idx));
+		goto out_unlock;
+	}
+	fill_prog_array(hdev, type, new_array);
+	rcu_assign_pointer(hdev->bpf.run_array[type], new_array);
+	bpf_prog_array_free(old_array);
+
+out_unlock:
+	hid_link->hdev = NULL;
+	mutex_unlock(&bpf_hid_mutex);
+}
+
+static int bpf_hid_link_detach(struct bpf_link *link)
+{
+	bpf_hid_link_release(link);
+	return 0;
+}
+
+static void bpf_hid_link_dealloc(struct bpf_link *link)
+{
+	struct bpf_hid_link *hid_link =
+		container_of(link, struct bpf_hid_link, link);
+
+	kfree(hid_link);
+}
+
+static int bpf_hid_link_update_prog(struct bpf_link *link,
+				    struct bpf_prog *new_prog,
+				    struct bpf_prog *old_prog)
+{
+	struct bpf_hid_link *hid_link =
+		container_of(link, struct bpf_hid_link, link);
+	enum bpf_hid_attach_type type = hid_link->hid_type;
+	struct bpf_prog_array *run_array;
+	struct hid_device *hdev;
+	int idx, ret;
+
+	if (old_prog && old_prog != link->prog)
+		return -EPERM;
+	if (new_prog->type != link->prog->type)
+		return -EINVAL;
+
+	mutex_lock(&bpf_hid_mutex);
+
+	hdev = hid_link->hdev;
+	if (!hdev) {
+		/* hid dying */
+		ret = -ENOLINK;
+		goto out_unlock;
+	}
+
+	run_array = rcu_dereference_protected(hdev->bpf.run_array[type],
+					      lockdep_is_held(&bpf_hid_mutex));
+	idx = link_index(hdev, type, hid_link);
+	ret = bpf_prog_array_update_at(run_array, idx, new_prog);
+	if (ret)
+		goto out_unlock;
+
+	old_prog = xchg(&link->prog, new_prog);
+	bpf_prog_put(old_prog);
+
+out_unlock:
+	mutex_unlock(&bpf_hid_mutex);
+	return ret;
+}
+
+static int bpf_hid_link_fill_info(const struct bpf_link *link,
+				  struct bpf_link_info *info)
+{
+	const struct bpf_hid_link *hid_link =
+		container_of(link, struct bpf_hid_link, link);
+	int hidraw_ino = -1;
+	struct hid_device *hdev;
+	struct hidraw *hidraw;
+
+	mutex_lock(&bpf_hid_mutex);
+	hdev = hid_link->hdev;
+	if (hdev && hdev->hidraw) {
+		hidraw = hdev->hidraw;
+		hidraw_ino = hidraw->minor;
+	}
+	mutex_unlock(&bpf_hid_mutex);
+
+	info->hid.hidraw_ino = hidraw_ino;
+	info->hid.attach_type = hid_link->type;
+	return 0;
+}
+
+static void bpf_hid_link_show_fdinfo(const struct bpf_link *link,
+				     struct seq_file *seq)
+{
+	struct bpf_link_info info = {};
+
+	bpf_hid_link_fill_info(link, &info);
+	seq_printf(seq,
+		   "hidraw_ino:\t%u\n"
+		   "attach_type:\t%u\n",
+		   info.hid.hidraw_ino,
+		   info.hid.attach_type);
+}
+
+static const struct bpf_link_ops bpf_hid_link_ops = {
+	.release = bpf_hid_link_release,
+	.dealloc = bpf_hid_link_dealloc,
+	.detach = bpf_hid_link_detach,
+	.update_prog = bpf_hid_link_update_prog,
+	.fill_link_info = bpf_hid_link_fill_info,
+	.show_fdinfo = bpf_hid_link_show_fdinfo,
+};
+
+/* Must be called with bpf_hid_mutex held. */
+static int __bpf_hid_prog_query(const union bpf_attr *attr,
+				union bpf_attr __user *uattr,
+				  struct hid_device *hdev,
+				  enum bpf_hid_attach_type type)
+{
+	__u32 __user *prog_ids = u64_to_user_ptr(attr->query.prog_ids);
+	struct bpf_prog_array *run_array;
+	u32 prog_cnt = 0, flags = 0;
+
+	run_array = rcu_dereference_protected(hdev->bpf.run_array[type],
+					      lockdep_is_held(&bpf_hid_mutex));
+	if (run_array)
+		prog_cnt = bpf_prog_array_length(run_array);
+
+	if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags)))
+		return -EFAULT;
+	if (copy_to_user(&uattr->query.prog_cnt, &prog_cnt, sizeof(prog_cnt)))
+		return -EFAULT;
+	if (!attr->query.prog_cnt || !prog_ids || !prog_cnt)
+		return 0;
+
+	return bpf_prog_array_copy_to_user(run_array, prog_ids,
+					   attr->query.prog_cnt);
+}
+
+int bpf_hid_prog_query(const union bpf_attr *attr,
+		       union bpf_attr __user *uattr)
+{
+	enum bpf_hid_attach_type type;
+	struct hid_device *hdev;
+	int ret;
+
+	if (attr->query.query_flags || !hid_hooks.hdev_from_fd)
+		return -EINVAL;
+
+	type = to_bpf_hid_attach_type(attr->query.attach_type);
+	if (type < 0)
+		return -EINVAL;
+
+	hdev = hid_hooks.hdev_from_fd(attr->query.target_fd);
+	if (IS_ERR(hdev))
+		return PTR_ERR(hdev);
+
+	mutex_lock(&bpf_hid_mutex);
+	ret = __bpf_hid_prog_query(attr, uattr, hdev, type);
+	mutex_unlock(&bpf_hid_mutex);
+
+	return ret;
+}
+
+static int bpf_hid_max_progs(enum bpf_hid_attach_type type)
+{
+	switch (type) {
+	case BPF_HID_ATTACH_DEVICE_EVENT:
+		return 64;
+	default:
+		return 0;
+	}
+}
+
+static int bpf_hid_link_attach(struct hid_device *hdev, struct bpf_link *link,
+			       enum bpf_hid_attach_type type)
+{
+	struct bpf_hid_link *hid_link =
+		container_of(link, struct bpf_hid_link, link);
+	struct bpf_prog_array *run_array;
+	int cnt, err = 0;
+
+	mutex_lock(&bpf_hid_mutex);
+
+	cnt = link_count(hdev, type);
+	if (cnt >= bpf_hid_max_progs(type)) {
+		err = -E2BIG;
+		goto out_unlock;
+	}
+
+	if (hid_hooks.link_attach) {
+		err = hid_hooks.link_attach(hdev, type);
+		if (err)
+			goto out_unlock;
+	}
+
+	run_array = bpf_prog_array_alloc(cnt + 1, GFP_KERNEL);
+	if (!run_array) {
+		err = -ENOMEM;
+		goto out_unlock;
+	}
+
+	list_add_tail(&hid_link->node, &hdev->bpf.links[type]);
+
+	fill_prog_array(hdev, type, run_array);
+	run_array = rcu_replace_pointer(hdev->bpf.run_array[type], run_array,
+					lockdep_is_held(&bpf_hid_mutex));
+	bpf_prog_array_free(run_array);
+
+out_unlock:
+	mutex_unlock(&bpf_hid_mutex);
+	return err;
+}
+
+int bpf_hid_link_create(const union bpf_attr *attr, struct bpf_prog *prog)
+{
+	enum bpf_hid_attach_type hid_type;
+	struct bpf_link_primer link_primer;
+	struct bpf_hid_link *hid_link;
+	enum bpf_attach_type type;
+	struct hid_device *hdev;
+	int err;
+
+	if (attr->link_create.flags || !hid_hooks.hdev_from_fd)
+		return -EINVAL;
+
+	type = attr->link_create.attach_type;
+	hid_type = to_bpf_hid_attach_type(type);
+	if (hid_type < 0)
+		return -EINVAL;
+
+	hdev = hid_hooks.hdev_from_fd(attr->link_create.target_fd);
+	if (IS_ERR(hdev))
+		return PTR_ERR(hdev);
+
+	hid_link = kzalloc(sizeof(*hid_link), GFP_USER);
+	if (!hid_link)
+		return -ENOMEM;
+
+	bpf_link_init(&hid_link->link, BPF_LINK_TYPE_HID,
+		      &bpf_hid_link_ops, prog);
+	hid_link->hdev = hdev;
+	hid_link->type = type;
+	hid_link->hid_type = hid_type;
+
+	err = bpf_link_prime(&hid_link->link, &link_primer);
+	if (err) {
+		kfree(hid_link);
+		return err;
+	}
+
+	err = bpf_hid_link_attach(hdev, &hid_link->link, hid_type);
+	if (err) {
+		bpf_link_cleanup(&link_primer);
+		return err;
+	}
+
+	return bpf_link_settle(&link_primer);
+}
+
+const struct bpf_prog_ops hid_prog_ops = {
+};
+
+int bpf_hid_init(struct hid_device *hdev)
+{
+	int type;
+
+	for (type = 0; type < MAX_BPF_HID_ATTACH_TYPE; type++)
+		INIT_LIST_HEAD(&hdev->bpf.links[type]);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(bpf_hid_init);
+
+void bpf_hid_exit(struct hid_device *hdev)
+{
+	enum bpf_hid_attach_type type;
+	struct bpf_hid_link *hid_link;
+
+	mutex_lock(&bpf_hid_mutex);
+	for (type = 0; type < MAX_BPF_HID_ATTACH_TYPE; type++) {
+		bpf_hid_run_array_detach(hdev, type);
+		list_for_each_entry(hid_link, &hdev->bpf.links[type], node) {
+			hid_link->hdev = NULL; /* auto-detach link */
+		}
+	}
+	mutex_unlock(&bpf_hid_mutex);
+}
+EXPORT_SYMBOL_GPL(bpf_hid_exit);
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index cc570891322b..a94e78ec3211 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -3,6 +3,7 @@
  */
 #include <linux/bpf.h>
 #include <linux/bpf-cgroup.h>
+#include <linux/bpf-hid.h>
 #include <linux/bpf_trace.h>
 #include <linux/bpf_lirc.h>
 #include <linux/bpf_verifier.h>
@@ -2205,6 +2206,7 @@ static bool is_sys_admin_prog_type(enum bpf_prog_type prog_type)
 {
 	switch (prog_type) {
 	case BPF_PROG_TYPE_LIRC_MODE2:
+	case BPF_PROG_TYPE_HID:
 	case BPF_PROG_TYPE_EXT: /* extends any prog */
 		return true;
 	default:
@@ -3200,6 +3202,8 @@ attach_type_to_prog_type(enum bpf_attach_type attach_type)
 		return BPF_PROG_TYPE_SK_LOOKUP;
 	case BPF_XDP:
 		return BPF_PROG_TYPE_XDP;
+	case BPF_HID_DEVICE_EVENT:
+		return BPF_PROG_TYPE_HID;
 	default:
 		return BPF_PROG_TYPE_UNSPEC;
 	}
@@ -3343,6 +3347,8 @@ static int bpf_prog_query(const union bpf_attr *attr,
 	case BPF_SK_MSG_VERDICT:
 	case BPF_SK_SKB_VERDICT:
 		return sock_map_bpf_prog_query(attr, uattr);
+	case BPF_HID_DEVICE_EVENT:
+		return bpf_hid_prog_query(attr, uattr);
 	default:
 		return -EINVAL;
 	}
@@ -4337,6 +4343,8 @@ static int link_create(union bpf_attr *attr, bpfptr_t uattr)
 		ret = bpf_perf_link_attach(attr, prog);
 		break;
 #endif
+	case BPF_PROG_TYPE_HID:
+		return bpf_hid_link_create(attr, prog);
 	default:
 		ret = -EINVAL;
 	}
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index afe3d0d7f5f2..5978b92cacd3 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -952,6 +952,7 @@ enum bpf_prog_type {
 	BPF_PROG_TYPE_LSM,
 	BPF_PROG_TYPE_SK_LOOKUP,
 	BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */
+	BPF_PROG_TYPE_HID,
 };
 
 enum bpf_attach_type {
@@ -997,6 +998,7 @@ enum bpf_attach_type {
 	BPF_SK_REUSEPORT_SELECT,
 	BPF_SK_REUSEPORT_SELECT_OR_MIGRATE,
 	BPF_PERF_EVENT,
+	BPF_HID_DEVICE_EVENT,
 	__MAX_BPF_ATTACH_TYPE
 };
 
@@ -1011,6 +1013,7 @@ enum bpf_link_type {
 	BPF_LINK_TYPE_NETNS = 5,
 	BPF_LINK_TYPE_XDP = 6,
 	BPF_LINK_TYPE_PERF_EVENT = 7,
+	BPF_LINK_TYPE_HID = 8,
 
 	MAX_BPF_LINK_TYPE,
 };
@@ -5870,6 +5873,10 @@ struct bpf_link_info {
 		struct {
 			__u32 ifindex;
 		} xdp;
+		struct  {
+			__s32 hidraw_ino;
+			__u32 attach_type;
+		} hid;
 	};
 } __attribute__((aligned(8)));
 
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 03/28] HID: hook up with bpf
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
  2022-03-04 17:28 ` [PATCH bpf-next v2 01/28] bpf: add new is_sys_admin_prog_type() helper Benjamin Tissoires
  2022-03-04 17:28 ` [PATCH bpf-next v2 02/28] bpf: introduce hid program type Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-04 18:24   ` Greg KH
                     ` (2 more replies)
  2022-03-04 17:28 ` [PATCH bpf-next v2 04/28] libbpf: add HID program type and API Benjamin Tissoires
                   ` (26 subsequent siblings)
  29 siblings, 3 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

Now that BPF can be compatible with HID, add the capability into HID.
drivers/hid/hid-bpf.c takes care of the glue between bpf and HID, and
hid-core can then inject any incoming event from the device into a BPF
program to filter/analyze it.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

changes in v2:
- split the series by bpf/libbpf/hid/selftests and samples
- addressed review comments from v1
---
 drivers/hid/Makefile   |   1 +
 drivers/hid/hid-bpf.c  | 157 +++++++++++++++++++++++++++++++++++++++++
 drivers/hid/hid-core.c |  21 +++++-
 include/linux/hid.h    |  11 +++
 4 files changed, 187 insertions(+), 3 deletions(-)
 create mode 100644 drivers/hid/hid-bpf.c

diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 6d3e630e81af..08d2d7619937 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -4,6 +4,7 @@
 #
 hid-y			:= hid-core.o hid-input.o hid-quirks.o
 hid-$(CONFIG_DEBUG_FS)		+= hid-debug.o
+hid-$(CONFIG_BPF)		+= hid-bpf.o
 
 obj-$(CONFIG_HID)		+= hid.o
 obj-$(CONFIG_UHID)		+= uhid.o
diff --git a/drivers/hid/hid-bpf.c b/drivers/hid/hid-bpf.c
new file mode 100644
index 000000000000..8120e598de9f
--- /dev/null
+++ b/drivers/hid/hid-bpf.c
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *  BPF in HID support for Linux
+ *
+ *  Copyright (c) 2022 Benjamin Tissoires
+ */
+
+#include <linux/filter.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include <uapi/linux/bpf_hid.h>
+#include <linux/hid.h>
+
+static int __hid_bpf_match_sysfs(struct device *dev, const void *data)
+{
+	struct kernfs_node *kn = dev->kobj.sd;
+	struct kernfs_node *uevent_kn;
+
+	uevent_kn = kernfs_find_and_get_ns(kn, "uevent", NULL);
+
+	return uevent_kn == data;
+}
+
+static struct hid_device *hid_bpf_fd_to_hdev(int fd)
+{
+	struct device *dev;
+	struct hid_device *hdev;
+	struct fd f = fdget(fd);
+	struct inode *inode;
+	struct kernfs_node *node;
+
+	if (!f.file) {
+		hdev = ERR_PTR(-EBADF);
+		goto out;
+	}
+
+	inode = file_inode(f.file);
+	node = inode->i_private;
+
+	dev = bus_find_device(&hid_bus_type, NULL, node, __hid_bpf_match_sysfs);
+
+	if (dev)
+		hdev = to_hid_device(dev);
+	else
+		hdev = ERR_PTR(-EINVAL);
+
+ out:
+	fdput(f);
+	return hdev;
+}
+
+static int hid_bpf_link_attach(struct hid_device *hdev, enum bpf_hid_attach_type type)
+{
+	int err = 0;
+
+	switch (type) {
+	case BPF_HID_ATTACH_DEVICE_EVENT:
+		if (!hdev->bpf.ctx) {
+			hdev->bpf.ctx = bpf_hid_allocate_ctx(hdev, HID_BPF_MAX_BUFFER_SIZE);
+			if (IS_ERR(hdev->bpf.ctx)) {
+				err = PTR_ERR(hdev->bpf.ctx);
+				hdev->bpf.ctx = NULL;
+			}
+		}
+		break;
+	default:
+		/* do nothing */
+	}
+
+	return err;
+}
+
+static void hid_bpf_array_detached(struct hid_device *hdev, enum bpf_hid_attach_type type)
+{
+	switch (type) {
+	case BPF_HID_ATTACH_DEVICE_EVENT:
+		kfree(hdev->bpf.ctx);
+		hdev->bpf.ctx = NULL;
+		break;
+	default:
+		/* do nothing */
+	}
+}
+
+static int hid_bpf_run_progs(struct hid_device *hdev, enum bpf_hid_attach_type type,
+			     struct hid_bpf_ctx *ctx, u8 *data, int size)
+{
+	enum hid_bpf_event event = HID_BPF_UNDEF;
+
+	if (type < 0 || !ctx)
+		return -EINVAL;
+
+	if (size > ctx->allocated_size)
+		return -E2BIG;
+
+	switch (type) {
+	case BPF_HID_ATTACH_DEVICE_EVENT:
+		event = HID_BPF_DEVICE_EVENT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (!hdev->bpf.run_array[type])
+		return 0;
+
+	memset(ctx->data, 0, ctx->allocated_size);
+	ctx->type = event;
+
+	if (size && data) {
+		memcpy(ctx->data, data, size);
+		ctx->size = size;
+	} else {
+		ctx->size = 0;
+	}
+
+	return BPF_PROG_RUN_ARRAY(hdev->bpf.run_array[type], ctx, bpf_prog_run);
+}
+
+u8 *hid_bpf_raw_event(struct hid_device *hdev, u8 *data, int *size)
+{
+	int ret;
+
+	if (bpf_hid_link_empty(&hdev->bpf, BPF_HID_ATTACH_DEVICE_EVENT))
+		return data;
+
+	ret = hid_bpf_run_progs(hdev, BPF_HID_ATTACH_DEVICE_EVENT,
+				hdev->bpf.ctx, data, *size);
+	if (ret)
+		return ERR_PTR(ret);
+
+	if (!hdev->bpf.ctx->size)
+		return ERR_PTR(-EINVAL);
+
+	*size = hdev->bpf.ctx->size;
+
+	return hdev->bpf.ctx->data;
+}
+
+int __init hid_bpf_module_init(void)
+{
+	struct bpf_hid_hooks hooks = {
+		.hdev_from_fd = hid_bpf_fd_to_hdev,
+		.link_attach = hid_bpf_link_attach,
+		.array_detached = hid_bpf_array_detached,
+	};
+
+	bpf_hid_set_hooks(&hooks);
+
+	return 0;
+}
+
+void __exit hid_bpf_module_exit(void)
+{
+	bpf_hid_set_hooks(NULL);
+}
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index f1aed5bbd000..a80bffe6ce4a 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1748,13 +1748,21 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
 	struct hid_driver *hdrv;
 	unsigned int a;
 	u32 rsize, csize = size;
-	u8 *cdata = data;
+	u8 *cdata;
 	int ret = 0;
 
+	data = hid_bpf_raw_event(hid, data, &size);
+	if (IS_ERR(data)) {
+		ret = PTR_ERR(data);
+		goto out;
+	}
+
 	report = hid_get_report(report_enum, data);
 	if (!report)
 		goto out;
 
+	cdata = data;
+
 	if (report_enum->numbered) {
 		cdata++;
 		csize--;
@@ -2528,10 +2536,12 @@ int hid_add_device(struct hid_device *hdev)
 
 	hid_debug_register(hdev, dev_name(&hdev->dev));
 	ret = device_add(&hdev->dev);
-	if (!ret)
+	if (!ret) {
 		hdev->status |= HID_STAT_ADDED;
-	else
+	} else {
 		hid_debug_unregister(hdev);
+		bpf_hid_exit(hdev);
+	}
 
 	return ret;
 }
@@ -2567,6 +2577,7 @@ struct hid_device *hid_allocate_device(void)
 	spin_lock_init(&hdev->debug_list_lock);
 	sema_init(&hdev->driver_input_lock, 1);
 	mutex_init(&hdev->ll_open_lock);
+	bpf_hid_init(hdev);
 
 	return hdev;
 }
@@ -2574,6 +2585,7 @@ EXPORT_SYMBOL_GPL(hid_allocate_device);
 
 static void hid_remove_device(struct hid_device *hdev)
 {
+	bpf_hid_exit(hdev);
 	if (hdev->status & HID_STAT_ADDED) {
 		device_del(&hdev->dev);
 		hid_debug_unregister(hdev);
@@ -2700,6 +2712,8 @@ static int __init hid_init(void)
 
 	hid_debug_init();
 
+	hid_bpf_module_init();
+
 	return 0;
 err_bus:
 	bus_unregister(&hid_bus_type);
@@ -2709,6 +2723,7 @@ static int __init hid_init(void)
 
 static void __exit hid_exit(void)
 {
+	hid_bpf_module_exit();
 	hid_debug_exit();
 	hidraw_exit();
 	bus_unregister(&hid_bus_type);
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 56f6f4ad45a7..8fd79011f461 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -27,6 +27,7 @@
 #include <linux/mutex.h>
 #include <linux/power_supply.h>
 #include <uapi/linux/hid.h>
+#include <uapi/linux/bpf_hid.h>
 
 /*
  * We parse each description item into this structure. Short items data
@@ -1210,4 +1211,14 @@ do {									\
 #define hid_dbg_once(hid, fmt, ...)			\
 	dev_dbg_once(&(hid)->dev, fmt, ##__VA_ARGS__)
 
+#ifdef CONFIG_BPF
+u8 *hid_bpf_raw_event(struct hid_device *hdev, u8 *rd, int *size);
+int hid_bpf_module_init(void);
+void hid_bpf_module_exit(void);
+#else
+static inline u8 *hid_bpf_raw_event(struct hid_device *hdev, u8 *rd, int *size) { return rd; }
+static inline int hid_bpf_module_init(void) { return 0; }
+static inline void hid_bpf_module_exit(void) {}
+#endif
+
 #endif
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 04/28] libbpf: add HID program type and API
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (2 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 03/28] HID: hook up with bpf Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-05  0:31   ` Song Liu
  2022-03-08  1:30   ` Andrii Nakryiko
  2022-03-04 17:28 ` [PATCH bpf-next v2 05/28] selftests/bpf: add tests for the HID-bpf initial implementation Benjamin Tissoires
                   ` (25 subsequent siblings)
  29 siblings, 2 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

HID-bpf program type are needing a new SEC.
To bind a hid-bpf program, we can rely on bpf_program__attach_fd()
so export a new function to the API.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

changes in v2:
- split the series by bpf/libbpf/hid/selftests and samples
---
 tools/lib/bpf/libbpf.c   | 7 +++++++
 tools/lib/bpf/libbpf.h   | 2 ++
 tools/lib/bpf/libbpf.map | 1 +
 3 files changed, 10 insertions(+)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 81bf01d67671..356bbd3ad2c7 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -8680,6 +8680,7 @@ static const struct bpf_sec_def section_defs[] = {
 	SEC_DEF("cgroup/setsockopt",	CGROUP_SOCKOPT, BPF_CGROUP_SETSOCKOPT, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 	SEC_DEF("struct_ops+",		STRUCT_OPS, 0, SEC_NONE),
 	SEC_DEF("sk_lookup",		SK_LOOKUP, BPF_SK_LOOKUP, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
+	SEC_DEF("hid/device_event",	HID, BPF_HID_DEVICE_EVENT, SEC_ATTACHABLE_OPT | SEC_SLOPPY_PFX),
 };
 
 #define MAX_TYPE_NAME_SIZE 32
@@ -10659,6 +10660,12 @@ static struct bpf_link *attach_iter(const struct bpf_program *prog, long cookie)
 	return bpf_program__attach_iter(prog, NULL);
 }
 
+struct bpf_link *
+bpf_program__attach_hid(const struct bpf_program *prog, int hid_fd)
+{
+	return bpf_program__attach_fd(prog, hid_fd, 0, "hid");
+}
+
 struct bpf_link *bpf_program__attach(const struct bpf_program *prog)
 {
 	if (!prog->sec_def || !prog->sec_def->attach_fn)
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index c8d8daad212e..f677ac0a9ede 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -529,6 +529,8 @@ struct bpf_iter_attach_opts {
 LIBBPF_API struct bpf_link *
 bpf_program__attach_iter(const struct bpf_program *prog,
 			 const struct bpf_iter_attach_opts *opts);
+LIBBPF_API struct bpf_link *
+bpf_program__attach_hid(const struct bpf_program *prog, int hid_fd);
 
 /*
  * Libbpf allows callers to adjust BPF programs before being loaded
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
index 47e70c9058d9..fdc6fa743953 100644
--- a/tools/lib/bpf/libbpf.map
+++ b/tools/lib/bpf/libbpf.map
@@ -424,6 +424,7 @@ LIBBPF_0.6.0 {
 LIBBPF_0.7.0 {
 	global:
 		bpf_btf_load;
+		bpf_program__attach_hid;
 		bpf_program__expected_attach_type;
 		bpf_program__log_buf;
 		bpf_program__log_level;
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 05/28] selftests/bpf: add tests for the HID-bpf initial implementation
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (3 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 04/28] libbpf: add HID program type and API Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-05  0:41   ` Song Liu
  2022-03-04 17:28 ` [PATCH bpf-next v2 06/28] samples/bpf: add new hid_mouse example Benjamin Tissoires
                   ` (24 subsequent siblings)
  29 siblings, 1 reply; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

The test is pretty basic:
- create a virtual uhid device that no userspace will like (to not mess
  up the running system)
- attach a BPF prog to it
- open the matching hidraw node
- inject one event and check:
  * that the BPF program can do something on the event stream
  * can modify the event stream

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

changes in v2:
- split the series by bpf/libbpf/hid/selftests and samples
---
 tools/testing/selftests/bpf/prog_tests/hid.c | 335 +++++++++++++++++++
 tools/testing/selftests/bpf/progs/hid.c      |  20 ++
 2 files changed, 355 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/hid.c
 create mode 100644 tools/testing/selftests/bpf/progs/hid.c

diff --git a/tools/testing/selftests/bpf/prog_tests/hid.c b/tools/testing/selftests/bpf/prog_tests/hid.c
new file mode 100644
index 000000000000..ee495d5a8bd5
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/hid.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2022 Red Hat */
+#include <test_progs.h>
+#include <testing_helpers.h>
+#include "hid.skel.h"
+
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <dirent.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <linux/uhid.h>
+
+static unsigned char rdesc[] = {
+	0x06, 0x00, 0xff,	/* Usage Page (Vendor Defined Page 1) */
+	0x09, 0x21,		/* Usage (Vendor Usage 0x21) */
+	0xa1, 0x01,		/* COLLECTION (Application) */
+	0x09, 0x01,			/* Usage (Vendor Usage 0x01) */
+	0xa1, 0x00,			/* COLLECTION (Physical) */
+	0x85, 0x01,				/* REPORT_ID (1) */
+	0x06, 0x00, 0xff,			/* Usage Page (Vendor Defined Page 1) */
+	0x19, 0x01,				/* USAGE_MINIMUM (1) */
+	0x29, 0x03,				/* USAGE_MAXIMUM (3) */
+	0x15, 0x00,				/* LOGICAL_MINIMUM (0) */
+	0x25, 0x01,				/* LOGICAL_MAXIMUM (1) */
+	0x95, 0x03,				/* REPORT_COUNT (3) */
+	0x75, 0x01,				/* REPORT_SIZE (1) */
+	0x81, 0x02,				/* INPUT (Data,Var,Abs) */
+	0x95, 0x01,				/* REPORT_COUNT (1) */
+	0x75, 0x05,				/* REPORT_SIZE (5) */
+	0x81, 0x01,				/* INPUT (Cnst,Var,Abs) */
+	0x05, 0x01,				/* USAGE_PAGE (Generic Desktop) */
+	0x09, 0x30,				/* USAGE (X) */
+	0x09, 0x31,				/* USAGE (Y) */
+	0x15, 0x81,				/* LOGICAL_MINIMUM (-127) */
+	0x25, 0x7f,				/* LOGICAL_MAXIMUM (127) */
+	0x75, 0x10,				/* REPORT_SIZE (16) */
+	0x95, 0x02,				/* REPORT_COUNT (2) */
+	0x81, 0x06,				/* INPUT (Data,Var,Rel) */
+
+	0x06, 0x00, 0xff,			/* Usage Page (Vendor Defined Page 1) */
+	0x19, 0x01,				/* USAGE_MINIMUM (1) */
+	0x29, 0x03,				/* USAGE_MAXIMUM (3) */
+	0x15, 0x00,				/* LOGICAL_MINIMUM (0) */
+	0x25, 0x01,				/* LOGICAL_MAXIMUM (1) */
+	0x95, 0x03,				/* REPORT_COUNT (3) */
+	0x75, 0x01,				/* REPORT_SIZE (1) */
+	0x91, 0x02,				/* Output (Data,Var,Abs) */
+	0x95, 0x01,				/* REPORT_COUNT (1) */
+	0x75, 0x05,				/* REPORT_SIZE (5) */
+	0x91, 0x01,				/* Output (Cnst,Var,Abs) */
+
+	0x06, 0x00, 0xff,			/* Usage Page (Vendor Defined Page 1) */
+	0x19, 0x06,				/* USAGE_MINIMUM (6) */
+	0x29, 0x08,				/* USAGE_MAXIMUM (8) */
+	0x15, 0x00,				/* LOGICAL_MINIMUM (0) */
+	0x25, 0x01,				/* LOGICAL_MAXIMUM (1) */
+	0x95, 0x03,				/* REPORT_COUNT (3) */
+	0x75, 0x01,				/* REPORT_SIZE (1) */
+	0xb1, 0x02,				/* Feature (Data,Var,Abs) */
+	0x95, 0x01,				/* REPORT_COUNT (1) */
+	0x75, 0x05,				/* REPORT_SIZE (5) */
+	0x91, 0x01,				/* Output (Cnst,Var,Abs) */
+
+	0xc0,				/* END_COLLECTION */
+	0xc0,			/* END_COLLECTION */
+};
+
+static int uhid_write(int fd, const struct uhid_event *ev)
+{
+	ssize_t ret;
+
+	ret = write(fd, ev, sizeof(*ev));
+	if (ret < 0) {
+		fprintf(stderr, "Cannot write to uhid: %m\n");
+		return -errno;
+	} else if (ret != sizeof(*ev)) {
+		fprintf(stderr, "Wrong size written to uhid: %zd != %zu\n",
+			ret, sizeof(ev));
+		return -EFAULT;
+	} else {
+		return 0;
+	}
+}
+
+static int create(int fd, int rand_nb)
+{
+	struct uhid_event ev;
+	char buf[25];
+
+	sprintf(buf, "test-uhid-device-%d", rand_nb);
+
+	memset(&ev, 0, sizeof(ev));
+	ev.type = UHID_CREATE;
+	strcpy((char *)ev.u.create.name, buf);
+	ev.u.create.rd_data = rdesc;
+	ev.u.create.rd_size = sizeof(rdesc);
+	ev.u.create.bus = BUS_USB;
+	ev.u.create.vendor = 0x0001;
+	ev.u.create.product = 0x0a37;
+	ev.u.create.version = 0;
+	ev.u.create.country = 0;
+
+	sprintf(buf, "%d", rand_nb);
+	strcpy((char *)ev.u.create.phys, buf);
+
+	return uhid_write(fd, &ev);
+}
+
+static void destroy(int fd)
+{
+	struct uhid_event ev;
+
+	memset(&ev, 0, sizeof(ev));
+	ev.type = UHID_DESTROY;
+
+	uhid_write(fd, &ev);
+}
+
+static int send_event(int fd, u8 *buf, size_t size)
+{
+	struct uhid_event ev;
+
+	if (size > sizeof(ev.u.input.data))
+		return -E2BIG;
+
+	memset(&ev, 0, sizeof(ev));
+	ev.type = UHID_INPUT2;
+	ev.u.input2.size = size;
+
+	memcpy(ev.u.input2.data, buf, size);
+
+	return uhid_write(fd, &ev);
+}
+
+static int setup_uhid(int rand_nb)
+{
+	int fd;
+	const char *path = "/dev/uhid";
+	int ret;
+
+	fd = open(path, O_RDWR | O_CLOEXEC);
+	if (!ASSERT_GE(fd, 0, "open uhid-cdev"))
+		return -EPERM;
+
+	ret = create(fd, rand_nb);
+	if (!ASSERT_OK(ret, "create uhid device")) {
+		close(fd);
+		return -EPERM;
+	}
+
+	return fd;
+}
+
+static int get_sysfs_fd(int rand_nb)
+{
+	const char *workdir = "/sys/devices/virtual/misc/uhid";
+	const char *target = "0003:0001:0A37.*";
+	char uevent[1024];
+	char temp[512];
+	char phys[512];
+	DIR *d;
+	struct dirent *dir;
+	int fd, nread;
+	int found = -1;
+
+	/* it would be nice to be able to use nftw, but the no_alu32 target doesn't support it */
+
+	sprintf(phys, "PHYS=%d", rand_nb);
+
+	d = opendir(workdir);
+	if (d) {
+		while ((dir = readdir(d)) != NULL) {
+			if (fnmatch(target, dir->d_name, 0))
+				continue;
+
+			/* we found the correct VID/PID, now check for phys */
+			sprintf(uevent, "%s/%s/uevent", workdir, dir->d_name);
+			fd = open(uevent, O_RDONLY | O_NONBLOCK);
+			if (fd < 0)
+				continue;
+
+			nread = read(fd, temp, ARRAY_SIZE(temp));
+			if (nread > 0 && (strstr(temp, phys)) != NULL) {
+				found = fd;
+				break;
+			}
+
+			close(fd);
+			fd = 0;
+		}
+		closedir(d);
+	}
+
+	return found;
+}
+
+static int get_hidraw(struct bpf_link *link)
+{
+	struct bpf_link_info info = {0};
+	int prog_id, i;
+
+	/* retry 5 times in case the system is loaded */
+	for (i = 5; i > 0; i--) {
+		usleep(10);
+		prog_id = link_info_prog_id(link, &info);
+		if (!prog_id)
+			continue;
+		if (info.hid.hidraw_ino >= 0)
+			break;
+	}
+
+	if (!prog_id)
+		return -1;
+
+	return info.hid.hidraw_ino;
+}
+
+/*
+ * Attach hid_first_event to the given uhid device,
+ * retrieve and open the matching hidraw node,
+ * inject one event in the uhid device,
+ * check that the program sees it and can change the data
+ */
+static int test_hid_raw_event(struct hid *hid_skel, int uhid_fd, int sysfs_fd)
+{
+	int err, hidraw_ino, hidraw_fd = -1;
+	char hidraw_path[64] = {0};
+	u8 buf[10] = {0};
+	int ret = -1;
+
+	/* check that the program is correctly loaded */
+	ASSERT_EQ(hid_skel->data->callback_check, 52, "callback_check1");
+	ASSERT_EQ(hid_skel->data->callback2_check, 52, "callback2_check1");
+
+	/* attach the first program */
+	hid_skel->links.hid_first_event =
+		bpf_program__attach_hid(hid_skel->progs.hid_first_event, sysfs_fd);
+	if (!ASSERT_OK_PTR(hid_skel->links.hid_first_event,
+			   "attach_hid(hid_first_event)"))
+		return PTR_ERR(hid_skel->links.hid_first_event);
+
+	hidraw_ino = get_hidraw(hid_skel->links.hid_first_event);
+	if (!ASSERT_GE(hidraw_ino, 0, "get_hidraw"))
+		goto cleanup;
+
+	/* open hidraw node to check the other side of the pipe */
+	sprintf(hidraw_path, "/dev/hidraw%d", hidraw_ino);
+	hidraw_fd = open(hidraw_path, O_RDWR | O_NONBLOCK);
+
+	if (!ASSERT_GE(hidraw_fd, 0, "open_hidraw"))
+		goto cleanup;
+
+	/* inject one event */
+	buf[0] = 1;
+	buf[1] = 42;
+	send_event(uhid_fd, buf, 6);
+
+	/* check that hid_first_event() was executed */
+	ASSERT_EQ(hid_skel->data->callback_check, 42, "callback_check1");
+
+	/* read the data from hidraw */
+	memset(buf, 0, sizeof(buf));
+	err = read(hidraw_fd, buf, sizeof(buf));
+	if (!ASSERT_EQ(err, 6, "read_hidraw"))
+		goto cleanup;
+
+	if (!ASSERT_EQ(buf[2], 47, "hid_first_event"))
+		goto cleanup;
+
+	/* inject another event */
+	buf[0] = 1;
+	buf[1] = 47;
+	send_event(uhid_fd, buf, 6);
+
+	/* check that hid_first_event() was executed */
+	ASSERT_EQ(hid_skel->data->callback_check, 47, "callback_check1");
+
+	/* read the data from hidraw */
+	memset(buf, 0, sizeof(buf));
+	err = read(hidraw_fd, buf, sizeof(buf));
+	if (!ASSERT_EQ(err, 6, "read_hidraw"))
+		goto cleanup;
+
+	if (!ASSERT_EQ(buf[2], 52, "hid_first_event"))
+		goto cleanup;
+
+	ret = 0;
+
+cleanup:
+	if (hidraw_fd >= 0)
+		close(hidraw_fd);
+
+	hid__detach(hid_skel);
+
+	return ret;
+}
+
+void serial_test_hid_bpf(void)
+{
+	struct hid *hid_skel = NULL;
+	int err, uhid_fd, sysfs_fd;
+	time_t t;
+	int rand_nb;
+
+	/* initialize random number generator */
+	srand((unsigned int)time(&t));
+
+	rand_nb = rand() % 1024;
+
+	uhid_fd = setup_uhid(rand_nb);
+	if (!ASSERT_GE(uhid_fd, 0, "setup uhid"))
+		return;
+
+	/* give a little bit of time for the device to appear */
+	/* TODO: check on uhid events */
+	usleep(1000);
+
+	/* locate the uevent file of the created device */
+	sysfs_fd = get_sysfs_fd(rand_nb);
+	if (!ASSERT_GE(sysfs_fd, 0, "locate sysfs uhid device"))
+		goto cleanup;
+
+	hid_skel = hid__open_and_load();
+	if (!ASSERT_OK_PTR(hid_skel, "hid_skel_load"))
+		goto cleanup;
+
+	/* start the tests! */
+	err = test_hid_raw_event(hid_skel, uhid_fd, sysfs_fd);
+	ASSERT_OK(err, "hid");
+
+cleanup:
+	hid__destroy(hid_skel);
+	destroy(uhid_fd);
+}
diff --git a/tools/testing/selftests/bpf/progs/hid.c b/tools/testing/selftests/bpf/progs/hid.c
new file mode 100644
index 000000000000..2201dd3b105d
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/hid.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2022 Red hat */
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include <linux/bpf_hid.h>
+
+char _license[] SEC("license") = "GPL";
+
+__u64 callback_check = 52;
+__u64 callback2_check = 52;
+
+SEC("hid/device_event")
+int hid_first_event(struct hid_bpf_ctx *ctx)
+{
+	callback_check = ctx->data[1];
+
+	ctx->data[2] = ctx->data[1] + 5;
+
+	return 0;
+}
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 06/28] samples/bpf: add new hid_mouse example
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (4 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 05/28] selftests/bpf: add tests for the HID-bpf initial implementation Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-04 18:26   ` Greg KH
  2022-03-04 17:28 ` [PATCH bpf-next v2 07/28] bpf/hid: add a new attach type to change the report descriptor Benjamin Tissoires
                   ` (23 subsequent siblings)
  29 siblings, 1 reply; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

Everything should be available in the selftest part of the tree, but
providing an example without uhid and hidraw will be more easy to
follow for users.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

changes in v2:
- split the series by bpf/libbpf/hid/selftests and samples
---
 samples/bpf/.gitignore       |   1 +
 samples/bpf/Makefile         |   4 ++
 samples/bpf/hid_mouse_kern.c |  66 ++++++++++++++++++
 samples/bpf/hid_mouse_user.c | 129 +++++++++++++++++++++++++++++++++++
 4 files changed, 200 insertions(+)
 create mode 100644 samples/bpf/hid_mouse_kern.c
 create mode 100644 samples/bpf/hid_mouse_user.c

diff --git a/samples/bpf/.gitignore b/samples/bpf/.gitignore
index 0e7bfdbff80a..65440bd618b2 100644
--- a/samples/bpf/.gitignore
+++ b/samples/bpf/.gitignore
@@ -2,6 +2,7 @@
 cpustat
 fds_example
 hbm
+hid_mouse
 ibumad
 lathist
 lwt_len_hist
diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index 38638845db9d..84ef458487df 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -60,6 +60,8 @@ tprogs-y += xdp_redirect_map
 tprogs-y += xdp_redirect
 tprogs-y += xdp_monitor
 
+tprogs-y += hid_mouse
+
 # Libbpf dependencies
 LIBBPF_SRC = $(TOOLS_PATH)/lib/bpf
 LIBBPF_OUTPUT = $(abspath $(BPF_SAMPLES_PATH))/libbpf
@@ -124,6 +126,7 @@ xdp_redirect_cpu-objs := xdp_redirect_cpu_user.o $(XDP_SAMPLE)
 xdp_redirect_map-objs := xdp_redirect_map_user.o $(XDP_SAMPLE)
 xdp_redirect-objs := xdp_redirect_user.o $(XDP_SAMPLE)
 xdp_monitor-objs := xdp_monitor_user.o $(XDP_SAMPLE)
+hid_mouse-objs := hid_mouse_user.o
 
 # Tell kbuild to always build the programs
 always-y := $(tprogs-y)
@@ -181,6 +184,7 @@ always-y += ibumad_kern.o
 always-y += hbm_out_kern.o
 always-y += hbm_edt_kern.o
 always-y += xdpsock_kern.o
+always-y += hid_mouse_kern.o
 
 ifeq ($(ARCH), arm)
 # Strip all except -D__LINUX_ARM_ARCH__ option needed to handle linux
diff --git a/samples/bpf/hid_mouse_kern.c b/samples/bpf/hid_mouse_kern.c
new file mode 100644
index 000000000000..c24a12e06b40
--- /dev/null
+++ b/samples/bpf/hid_mouse_kern.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2021 Benjamin Tissoires
+ */
+#include <linux/version.h>
+#include <uapi/linux/bpf.h>
+#include <uapi/linux/bpf_hid.h>
+#include <bpf/bpf_helpers.h>
+
+SEC("hid/device_event")
+int hid_y_event(struct hid_bpf_ctx *ctx)
+{
+	s16 y;
+
+	bpf_printk("event: %02x size: %d", ctx->type, ctx->size);
+	bpf_printk("incoming event: %02x %02x %02x",
+		   ctx->data[0],
+		   ctx->data[1],
+		   ctx->data[2]);
+	bpf_printk("                %02x %02x %02x",
+		   ctx->data[3],
+		   ctx->data[4],
+		   ctx->data[5]);
+	bpf_printk("                %02x %02x %02x",
+		   ctx->data[6],
+		   ctx->data[7],
+		   ctx->data[8]);
+
+	y = ctx->data[3] | (ctx->data[4] << 8);
+
+	y = -y;
+
+	ctx->data[3] = y & 0xFF;
+	ctx->data[4] = (y >> 8) & 0xFF;
+
+	bpf_printk("modified event: %02x %02x %02x",
+		   ctx->data[0],
+		   ctx->data[1],
+		   ctx->data[2]);
+	bpf_printk("                %02x %02x %02x",
+		   ctx->data[3],
+		   ctx->data[4],
+		   ctx->data[5]);
+	bpf_printk("                %02x %02x %02x",
+		   ctx->data[6],
+		   ctx->data[7],
+		   ctx->data[8]);
+
+	return 0;
+}
+
+SEC("hid/device_event")
+int hid_x_event(struct hid_bpf_ctx *ctx)
+{
+	s16 x;
+
+	x = ctx->data[1] | (ctx->data[2] << 8);
+
+	x = -x;
+
+	ctx->data[1] = x & 0xFF;
+	ctx->data[2] = (x >> 8) & 0xFF;
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";
+u32 _version SEC("version") = LINUX_VERSION_CODE;
diff --git a/samples/bpf/hid_mouse_user.c b/samples/bpf/hid_mouse_user.c
new file mode 100644
index 000000000000..d4f37caca2fa
--- /dev/null
+++ b/samples/bpf/hid_mouse_user.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2021 Benjamin Tissoires
+ */
+#include <linux/bpf.h>
+#include <linux/if_link.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <sys/resource.h>
+
+#include "bpf_util.h"
+#include <bpf/bpf.h>
+#include <bpf/libbpf.h>
+
+static char *sysfs_path;
+static int sysfs_fd;
+static int prog_count;
+
+struct prog {
+	int fd;
+	struct bpf_link *link;
+	enum bpf_attach_type type;
+};
+
+static struct prog progs[10];
+
+static void int_exit(int sig)
+{
+	for (prog_count--; prog_count >= 0; prog_count--)
+		bpf_link__destroy(progs[prog_count].link);
+
+	close(sysfs_fd);
+	exit(0);
+}
+
+static void usage(const char *prog)
+{
+	fprintf(stderr,
+		"%s: %s /sys/bus/hid/devices/0BUS:0VID:0PID:00ID/uevent\n\n",
+		__func__, prog);
+}
+
+int main(int argc, char **argv)
+{
+	struct bpf_prog_info info = {};
+	__u32 info_len = sizeof(info);
+	const char *optstr = "";
+	struct bpf_object *obj;
+	struct bpf_program *prog;
+	int opt;
+	char filename[256];
+	int err;
+
+	while ((opt = getopt(argc, argv, optstr)) != -1) {
+		switch (opt) {
+		default:
+			usage(basename(argv[0]));
+			return 1;
+		}
+	}
+
+	if (optind == argc) {
+		usage(basename(argv[0]));
+		return 1;
+	}
+
+	sysfs_path = argv[optind];
+	if (!sysfs_path) {
+		perror("sysfs");
+		return 1;
+	}
+
+	snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
+	obj = bpf_object__open_file(filename, NULL);
+	err = libbpf_get_error(obj);
+	if (err) {
+		fprintf(stderr, "ERROR: opening BPF object file failed\n");
+		obj = NULL;
+		err = 1;
+		goto cleanup;
+	}
+
+	/* load BPF program */
+	err = bpf_object__load(obj);
+	if (err) {
+		fprintf(stderr, "ERROR: loading BPF object file failed\n");
+		goto cleanup;
+	}
+
+	sysfs_fd = open(sysfs_path, O_RDONLY);
+
+	bpf_object__for_each_program(prog, obj) {
+		progs[prog_count].fd = bpf_program__fd(prog);
+		progs[prog_count].type = bpf_program__get_expected_attach_type(prog);
+		progs[prog_count].link = bpf_program__attach_hid(prog, sysfs_fd);
+		if (libbpf_get_error(progs[prog_count].link)) {
+			fprintf(stderr, "bpf_prog_attach: err=%m\n");
+			progs[prog_count].fd = 0;
+			progs[prog_count].link = NULL;
+			goto cleanup;
+		}
+		prog_count++;
+	}
+
+	signal(SIGINT, int_exit);
+	signal(SIGTERM, int_exit);
+
+	err = bpf_obj_get_info_by_fd(progs[0].fd, &info, &info_len);
+	if (err) {
+		printf("can't get prog info - %s\n", strerror(errno));
+		goto cleanup;
+	}
+
+	while (1)
+		;
+
+ cleanup:
+	for (prog_count--; prog_count >= 0; prog_count--)
+		bpf_link__destroy(progs[prog_count].link);
+
+	bpf_object__close(obj);
+	return err;
+}
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 07/28] bpf/hid: add a new attach type to change the report descriptor
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (5 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 06/28] samples/bpf: add new hid_mouse example Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-04 18:32   ` Greg KH
  2022-03-04 17:28 ` [PATCH bpf-next v2 08/28] HID: allow to change the report descriptor from an eBPF program Benjamin Tissoires
                   ` (22 subsequent siblings)
  29 siblings, 1 reply; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

The report descriptor is the dictionary of the HID protocol specific
to the given device.
Changing it is a common habit in the HID world, and making that feature
accessible from eBPF allows to fix devices without having to install a
new kernel.

However, the report descriptor is supposed to be static on a device.
To be able to change it, we need to reconnect the device at the HID
level.
So whenever the report descriptor program type is attached or detached,
we call on a hook on HID to notify it that there is something to be
done.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

changes in v2:
- split the series by bpf/libbpf/hid/selftests and samples
- unsigned long -> __u16 in uapi/linux/bpf_hid.h
---
 include/linux/bpf-hid.h        | 4 ++++
 include/uapi/linux/bpf.h       | 1 +
 include/uapi/linux/bpf_hid.h   | 1 +
 kernel/bpf/hid.c               | 5 +++++
 kernel/bpf/syscall.c           | 2 ++
 tools/include/uapi/linux/bpf.h | 1 +
 6 files changed, 14 insertions(+)

diff --git a/include/linux/bpf-hid.h b/include/linux/bpf-hid.h
index 3cda78051b5f..0c5000b28b20 100644
--- a/include/linux/bpf-hid.h
+++ b/include/linux/bpf-hid.h
@@ -15,6 +15,7 @@ struct hid_device;
 enum bpf_hid_attach_type {
 	BPF_HID_ATTACH_INVALID = -1,
 	BPF_HID_ATTACH_DEVICE_EVENT = 0,
+	BPF_HID_ATTACH_RDESC_FIXUP,
 	MAX_BPF_HID_ATTACH_TYPE
 };
 
@@ -32,6 +33,8 @@ to_bpf_hid_attach_type(enum bpf_attach_type attach_type)
 	switch (attach_type) {
 	case BPF_HID_DEVICE_EVENT:
 		return BPF_HID_ATTACH_DEVICE_EVENT;
+	case BPF_HID_RDESC_FIXUP:
+		return BPF_HID_ATTACH_RDESC_FIXUP;
 	default:
 		return BPF_HID_ATTACH_INVALID;
 	}
@@ -88,6 +91,7 @@ static inline bool bpf_hid_link_empty(struct bpf_hid *bpf,
 struct bpf_hid_hooks {
 	struct hid_device *(*hdev_from_fd)(int fd);
 	int (*link_attach)(struct hid_device *hdev, enum bpf_hid_attach_type type);
+	void (*link_attached)(struct hid_device *hdev, enum bpf_hid_attach_type type);
 	void (*array_detached)(struct hid_device *hdev, enum bpf_hid_attach_type type);
 };
 
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 5978b92cacd3..a7a8d9cfcf24 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -999,6 +999,7 @@ enum bpf_attach_type {
 	BPF_SK_REUSEPORT_SELECT_OR_MIGRATE,
 	BPF_PERF_EVENT,
 	BPF_HID_DEVICE_EVENT,
+	BPF_HID_RDESC_FIXUP,
 	__MAX_BPF_ATTACH_TYPE
 };
 
diff --git a/include/uapi/linux/bpf_hid.h b/include/uapi/linux/bpf_hid.h
index 975ca5bd526f..634f17c0b1cb 100644
--- a/include/uapi/linux/bpf_hid.h
+++ b/include/uapi/linux/bpf_hid.h
@@ -24,6 +24,7 @@ struct hid_device;
 enum hid_bpf_event {
 	HID_BPF_UNDEF = 0,
 	HID_BPF_DEVICE_EVENT,		/* when attach type is BPF_HID_DEVICE_EVENT */
+	HID_BPF_RDESC_FIXUP,		/* ................... BPF_HID_RDESC_FIXUP */
 };
 
 struct hid_bpf_ctx {
diff --git a/kernel/bpf/hid.c b/kernel/bpf/hid.c
index db7f75a0a812..37500313e270 100644
--- a/kernel/bpf/hid.c
+++ b/kernel/bpf/hid.c
@@ -315,6 +315,8 @@ static int bpf_hid_max_progs(enum bpf_hid_attach_type type)
 	switch (type) {
 	case BPF_HID_ATTACH_DEVICE_EVENT:
 		return 64;
+	case BPF_HID_ATTACH_RDESC_FIXUP:
+		return 1;
 	default:
 		return 0;
 	}
@@ -355,6 +357,9 @@ static int bpf_hid_link_attach(struct hid_device *hdev, struct bpf_link *link,
 					lockdep_is_held(&bpf_hid_mutex));
 	bpf_prog_array_free(run_array);
 
+	if (hid_hooks.link_attached)
+		hid_hooks.link_attached(hdev, type);
+
 out_unlock:
 	mutex_unlock(&bpf_hid_mutex);
 	return err;
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index a94e78ec3211..7428a1a512c6 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -3203,6 +3203,7 @@ attach_type_to_prog_type(enum bpf_attach_type attach_type)
 	case BPF_XDP:
 		return BPF_PROG_TYPE_XDP;
 	case BPF_HID_DEVICE_EVENT:
+	case BPF_HID_RDESC_FIXUP:
 		return BPF_PROG_TYPE_HID;
 	default:
 		return BPF_PROG_TYPE_UNSPEC;
@@ -3348,6 +3349,7 @@ static int bpf_prog_query(const union bpf_attr *attr,
 	case BPF_SK_SKB_VERDICT:
 		return sock_map_bpf_prog_query(attr, uattr);
 	case BPF_HID_DEVICE_EVENT:
+	case BPF_HID_RDESC_FIXUP:
 		return bpf_hid_prog_query(attr, uattr);
 	default:
 		return -EINVAL;
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 5978b92cacd3..a7a8d9cfcf24 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -999,6 +999,7 @@ enum bpf_attach_type {
 	BPF_SK_REUSEPORT_SELECT_OR_MIGRATE,
 	BPF_PERF_EVENT,
 	BPF_HID_DEVICE_EVENT,
+	BPF_HID_RDESC_FIXUP,
 	__MAX_BPF_ATTACH_TYPE
 };
 
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 08/28] HID: allow to change the report descriptor from an eBPF program
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (6 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 07/28] bpf/hid: add a new attach type to change the report descriptor Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-04 18:32   ` Greg KH
  2022-03-04 17:28 ` [PATCH bpf-next v2 09/28] libbpf: add new attach type BPF_HID_RDESC_FIXUP Benjamin Tissoires
                   ` (21 subsequent siblings)
  29 siblings, 1 reply; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

Make use of BPF_HID_ATTACH_RDESC_FIXUP so we can trigger an rdesc 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.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

changes in v2:
- split the series by bpf/libbpf/hid/selftests and samples
---
 drivers/hid/hid-bpf.c  | 60 ++++++++++++++++++++++++++++++++++++++++++
 drivers/hid/hid-core.c |  3 ++-
 include/linux/hid.h    |  6 +++++
 3 files changed, 68 insertions(+), 1 deletion(-)

diff --git a/drivers/hid/hid-bpf.c b/drivers/hid/hid-bpf.c
index 8120e598de9f..510e24f4307c 100644
--- a/drivers/hid/hid-bpf.c
+++ b/drivers/hid/hid-bpf.c
@@ -50,6 +50,14 @@ static struct hid_device *hid_bpf_fd_to_hdev(int fd)
 	return hdev;
 }
 
+static int hid_reconnect(struct hid_device *hdev)
+{
+	if (!test_and_set_bit(ffs(HID_STAT_REPROBED), &hdev->status))
+		return device_reprobe(&hdev->dev);
+
+	return 0;
+}
+
 static int hid_bpf_link_attach(struct hid_device *hdev, enum bpf_hid_attach_type type)
 {
 	int err = 0;
@@ -71,6 +79,17 @@ static int hid_bpf_link_attach(struct hid_device *hdev, enum bpf_hid_attach_type
 	return err;
 }
 
+static void hid_bpf_link_attached(struct hid_device *hdev, enum bpf_hid_attach_type type)
+{
+	switch (type) {
+	case BPF_HID_ATTACH_RDESC_FIXUP:
+		hid_reconnect(hdev);
+		break;
+	default:
+		/* do nothing */
+	}
+}
+
 static void hid_bpf_array_detached(struct hid_device *hdev, enum bpf_hid_attach_type type)
 {
 	switch (type) {
@@ -78,6 +97,9 @@ static void hid_bpf_array_detached(struct hid_device *hdev, enum bpf_hid_attach_
 		kfree(hdev->bpf.ctx);
 		hdev->bpf.ctx = NULL;
 		break;
+	case BPF_HID_ATTACH_RDESC_FIXUP:
+		hid_reconnect(hdev);
+		break;
 	default:
 		/* do nothing */
 	}
@@ -98,6 +120,9 @@ static int hid_bpf_run_progs(struct hid_device *hdev, enum bpf_hid_attach_type t
 	case BPF_HID_ATTACH_DEVICE_EVENT:
 		event = HID_BPF_DEVICE_EVENT;
 		break;
+	case BPF_HID_ATTACH_RDESC_FIXUP:
+		event = HID_BPF_RDESC_FIXUP;
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -138,11 +163,46 @@ u8 *hid_bpf_raw_event(struct hid_device *hdev, u8 *data, int *size)
 	return hdev->bpf.ctx->data;
 }
 
+u8 *hid_bpf_report_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size)
+{
+	struct hid_bpf_ctx *ctx = NULL;
+	int ret;
+
+	if (bpf_hid_link_empty(&hdev->bpf, BPF_HID_ATTACH_RDESC_FIXUP))
+		goto ignore_bpf;
+
+	ctx = bpf_hid_allocate_ctx(hdev, HID_MAX_DESCRIPTOR_SIZE);
+	if (IS_ERR(ctx))
+		goto ignore_bpf;
+
+	ret = hid_bpf_run_progs(hdev, BPF_HID_ATTACH_RDESC_FIXUP, ctx, rdesc, *size);
+	if (ret)
+		goto ignore_bpf;
+
+	*size = ctx->size;
+
+	if (!*size) {
+		rdesc = NULL;
+		goto unlock;
+	}
+
+	rdesc = kmemdup(ctx->data, *size, GFP_KERNEL);
+
+ unlock:
+	kfree(ctx);
+	return rdesc;
+
+ ignore_bpf:
+	kfree(ctx);
+	return kmemdup(rdesc, *size, GFP_KERNEL);
+}
+
 int __init hid_bpf_module_init(void)
 {
 	struct bpf_hid_hooks hooks = {
 		.hdev_from_fd = hid_bpf_fd_to_hdev,
 		.link_attach = hid_bpf_link_attach,
+		.link_attached = hid_bpf_link_attached,
 		.array_detached = hid_bpf_array_detached,
 	};
 
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index a80bffe6ce4a..0eb8189faaee 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1213,7 +1213,8 @@ int hid_open_report(struct hid_device *device)
 		return -ENODEV;
 	size = device->dev_rsize;
 
-	buf = kmemdup(start, size, GFP_KERNEL);
+	/* hid_bpf_report_fixup() ensures we work on a copy of rdesc */
+	buf = hid_bpf_report_fixup(device, start, &size);
 	if (buf == NULL)
 		return -ENOMEM;
 
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 8fd79011f461..66d949d10b78 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -1213,10 +1213,16 @@ do {									\
 
 #ifdef CONFIG_BPF
 u8 *hid_bpf_raw_event(struct hid_device *hdev, u8 *rd, int *size);
+u8 *hid_bpf_report_fixup(struct hid_device *hdev, u8 *rdesc, unsigned int *size);
 int hid_bpf_module_init(void);
 void hid_bpf_module_exit(void);
 #else
 static inline u8 *hid_bpf_raw_event(struct hid_device *hdev, u8 *rd, int *size) { return rd; }
+static inline u8 *hid_bpf_report_fixup(struct hid_device *hdev, u8 *rdesc,
+				       unsigned int *size)
+{
+	return kmemdup(rdesc, *size, GFP_KERNEL);
+}
 static inline int hid_bpf_module_init(void) { return 0; }
 static inline void hid_bpf_module_exit(void) {}
 #endif
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 09/28] libbpf: add new attach type BPF_HID_RDESC_FIXUP
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (7 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 08/28] HID: allow to change the report descriptor from an eBPF program Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-04 17:28 ` [PATCH bpf-next v2 10/28] selftests/bpf: add report descriptor fixup tests Benjamin Tissoires
                   ` (20 subsequent siblings)
  29 siblings, 0 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

Adds the SEC definiton for report descriptor fixups.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

changes in v2:
- split the series by bpf/libbpf/hid/selftests and samples
---
 tools/lib/bpf/libbpf.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 356bbd3ad2c7..192ef3901251 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -8681,6 +8681,7 @@ static const struct bpf_sec_def section_defs[] = {
 	SEC_DEF("struct_ops+",		STRUCT_OPS, 0, SEC_NONE),
 	SEC_DEF("sk_lookup",		SK_LOOKUP, BPF_SK_LOOKUP, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 	SEC_DEF("hid/device_event",	HID, BPF_HID_DEVICE_EVENT, SEC_ATTACHABLE_OPT | SEC_SLOPPY_PFX),
+	SEC_DEF("hid/rdesc_fixup",	HID, BPF_HID_RDESC_FIXUP, SEC_ATTACHABLE_OPT | SEC_SLOPPY_PFX),
 };
 
 #define MAX_TYPE_NAME_SIZE 32
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 10/28] selftests/bpf: add report descriptor fixup tests
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (8 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 09/28] libbpf: add new attach type BPF_HID_RDESC_FIXUP Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-04 18:34   ` Greg KH
  2022-03-04 17:28 ` [PATCH bpf-next v2 11/28] samples/bpf: add a report descriptor fixup Benjamin Tissoires
                   ` (19 subsequent siblings)
  29 siblings, 1 reply; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

Simple report descriptor override in HID: replace part of the report
descriptor from a static definition in the bpf kernel program.

Note that this test should be run last because we disconnect/reconnect
the device, meaning that it changes the overall uhid device.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

changes in v2:
- split the series by bpf/libbpf/hid/selftests and samples
---
 tools/testing/selftests/bpf/prog_tests/hid.c | 69 ++++++++++++++++++++
 tools/testing/selftests/bpf/progs/hid.c      | 48 ++++++++++++++
 2 files changed, 117 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/hid.c b/tools/testing/selftests/bpf/prog_tests/hid.c
index ee495d5a8bd5..91543b8078ca 100644
--- a/tools/testing/selftests/bpf/prog_tests/hid.c
+++ b/tools/testing/selftests/bpf/prog_tests/hid.c
@@ -9,6 +9,7 @@
 #include <dirent.h>
 #include <poll.h>
 #include <stdbool.h>
+#include <linux/hidraw.h>
 #include <linux/uhid.h>
 
 static unsigned char rdesc[] = {
@@ -296,6 +297,71 @@ static int test_hid_raw_event(struct hid *hid_skel, int uhid_fd, int sysfs_fd)
 	return ret;
 }
 
+/*
+ * Attach hid_rdesc_fixup to the given uhid device,
+ * retrieve and open the matching hidraw node,
+ * check that the hidraw report descriptor has been updated.
+ */
+static int test_rdesc_fixup(struct hid *hid_skel, int uhid_fd, int sysfs_fd)
+{
+	struct hidraw_report_descriptor rpt_desc = {0};
+	int err, desc_size, hidraw_ino, hidraw_fd = -1;
+	char hidraw_path[64] = {0};
+	int ret = -1;
+
+	/* attach the program */
+	hid_skel->links.hid_rdesc_fixup =
+		bpf_program__attach_hid(hid_skel->progs.hid_rdesc_fixup, sysfs_fd);
+	if (!ASSERT_OK_PTR(hid_skel->links.hid_rdesc_fixup,
+			   "attach_hid(hid_rdesc_fixup)"))
+		return PTR_ERR(hid_skel->links.hid_rdesc_fixup);
+
+	/* give a little bit of time for the device to appear */
+	/* TODO: check on uhid events */
+	usleep(1000);
+
+	hidraw_ino = get_hidraw(hid_skel->links.hid_rdesc_fixup);
+	if (!ASSERT_GE(hidraw_ino, 0, "get_hidraw"))
+		goto cleanup;
+
+	/* open hidraw node to check the other side of the pipe */
+	sprintf(hidraw_path, "/dev/hidraw%d", hidraw_ino);
+	hidraw_fd = open(hidraw_path, O_RDWR | O_NONBLOCK);
+
+	if (!ASSERT_GE(hidraw_fd, 0, "open_hidraw"))
+		goto cleanup;
+
+	/* check that hid_rdesc_fixup() was executed */
+	ASSERT_EQ(hid_skel->data->callback2_check, 0x21, "callback_check2");
+
+	/* read the exposed report descriptor from hidraw */
+	err = ioctl(hidraw_fd, HIDIOCGRDESCSIZE, &desc_size);
+	if (!ASSERT_GE(err, 0, "HIDIOCGRDESCSIZE"))
+		goto cleanup;
+
+	/* ensure the new size of the rdesc is bigger than the old one */
+	if (!ASSERT_GT(desc_size, sizeof(rdesc), "new_rdesc_size"))
+		goto cleanup;
+
+	rpt_desc.size = desc_size;
+	err = ioctl(hidraw_fd, HIDIOCGRDESC, &rpt_desc);
+	if (!ASSERT_GE(err, 0, "HIDIOCGRDESC"))
+		goto cleanup;
+
+	if (!ASSERT_EQ(rpt_desc.value[4], 0x42, "hid_rdesc_fixup"))
+		goto cleanup;
+
+	ret = 0;
+
+cleanup:
+	if (hidraw_fd >= 0)
+		close(hidraw_fd);
+
+	hid__detach(hid_skel);
+
+	return ret;
+}
+
 void serial_test_hid_bpf(void)
 {
 	struct hid *hid_skel = NULL;
@@ -329,6 +395,9 @@ void serial_test_hid_bpf(void)
 	err = test_hid_raw_event(hid_skel, uhid_fd, sysfs_fd);
 	ASSERT_OK(err, "hid");
 
+	err = test_rdesc_fixup(hid_skel, uhid_fd, sysfs_fd);
+	ASSERT_OK(err, "hid_rdesc_fixup");
+
 cleanup:
 	hid__destroy(hid_skel);
 	destroy(uhid_fd);
diff --git a/tools/testing/selftests/bpf/progs/hid.c b/tools/testing/selftests/bpf/progs/hid.c
index 2201dd3b105d..2270448d0d3f 100644
--- a/tools/testing/selftests/bpf/progs/hid.c
+++ b/tools/testing/selftests/bpf/progs/hid.c
@@ -18,3 +18,51 @@ int hid_first_event(struct hid_bpf_ctx *ctx)
 
 	return 0;
 }
+
+static __u8 rdesc[] = {
+	0x05, 0x01,				/* USAGE_PAGE (Generic Desktop) */
+	0x09, 0x32,				/* USAGE (Z) */
+	0x95, 0x01,				/* REPORT_COUNT (1) */
+	0x81, 0x06,				/* INPUT (Data,Var,Rel) */
+
+	0x06, 0x00, 0xff,			/* Usage Page (Vendor Defined Page 1) */
+	0x19, 0x01,				/* USAGE_MINIMUM (1) */
+	0x29, 0x03,				/* USAGE_MAXIMUM (3) */
+	0x15, 0x00,				/* LOGICAL_MINIMUM (0) */
+	0x25, 0x01,				/* LOGICAL_MAXIMUM (1) */
+	0x95, 0x03,				/* REPORT_COUNT (3) */
+	0x75, 0x01,				/* REPORT_SIZE (1) */
+	0x91, 0x02,				/* Output (Data,Var,Abs) */
+	0x95, 0x01,				/* REPORT_COUNT (1) */
+	0x75, 0x05,				/* REPORT_SIZE (5) */
+	0x91, 0x01,				/* Output (Cnst,Var,Abs) */
+
+	0x06, 0x00, 0xff,			/* Usage Page (Vendor Defined Page 1) */
+	0x19, 0x06,				/* USAGE_MINIMUM (6) */
+	0x29, 0x08,				/* USAGE_MAXIMUM (8) */
+	0x15, 0x00,				/* LOGICAL_MINIMUM (0) */
+	0x25, 0x01,				/* LOGICAL_MAXIMUM (1) */
+	0x95, 0x03,				/* REPORT_COUNT (3) */
+	0x75, 0x01,				/* REPORT_SIZE (1) */
+	0xb1, 0x02,				/* Feature (Data,Var,Abs) */
+	0x95, 0x01,				/* REPORT_COUNT (1) */
+	0x75, 0x05,				/* REPORT_SIZE (5) */
+	0x91, 0x01,				/* Output (Cnst,Var,Abs) */
+
+	0xc0,				/* END_COLLECTION */
+	0xc0,			/* END_COLLECTION */
+};
+
+SEC("hid/rdesc_fixup")
+int hid_rdesc_fixup(struct hid_bpf_ctx *ctx)
+{
+	callback2_check = ctx->data[4];
+
+	/* insert rdesc at offset 52 */
+	__builtin_memcpy(&ctx->data[52], rdesc, sizeof(rdesc));
+	ctx->size = sizeof(rdesc) + 52;
+
+	ctx->data[4] = 0x42;
+
+	return 0;
+}
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 11/28] samples/bpf: add a report descriptor fixup
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (9 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 10/28] selftests/bpf: add report descriptor fixup tests Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-04 18:36   ` Greg KH
  2022-03-04 17:28 ` [PATCH bpf-next v2 12/28] bpf/hid: add hid_{get|set}_data helpers Benjamin Tissoires
                   ` (18 subsequent siblings)
  29 siblings, 1 reply; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

the program inverts the definition of X and Y at a given place in the
report descriptor of my mouse.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

changes in v2:
- split the series by bpf/libbpf/hid/selftests and samples
---
 samples/bpf/hid_mouse_kern.c | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/samples/bpf/hid_mouse_kern.c b/samples/bpf/hid_mouse_kern.c
index c24a12e06b40..958820caaf5d 100644
--- a/samples/bpf/hid_mouse_kern.c
+++ b/samples/bpf/hid_mouse_kern.c
@@ -62,5 +62,30 @@ int hid_x_event(struct hid_bpf_ctx *ctx)
 	return 0;
 }
 
+SEC("hid/rdesc_fixup")
+int hid_rdesc_fixup(struct hid_bpf_ctx *ctx)
+{
+	if (ctx->type != HID_BPF_RDESC_FIXUP)
+		return 0;
+
+	bpf_printk("rdesc: %02x %02x %02x",
+		   ctx->data[0],
+		   ctx->data[1],
+		   ctx->data[2]);
+	bpf_printk("       %02x %02x %02x",
+		   ctx->data[3],
+		   ctx->data[4],
+		   ctx->data[5]);
+	bpf_printk("       %02x %02x %02x ...",
+		   ctx->data[6],
+		   ctx->data[7],
+		   ctx->data[8]);
+
+	ctx->data[39] = 0x31;
+	ctx->data[41] = 0x30;
+
+	return 0;
+}
+
 char _license[] SEC("license") = "GPL";
 u32 _version SEC("version") = LINUX_VERSION_CODE;
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 12/28] bpf/hid: add hid_{get|set}_data helpers
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (10 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 11/28] samples/bpf: add a report descriptor fixup Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-04 18:37   ` Greg KH
  2022-03-04 18:41   ` Greg KH
  2022-03-04 17:28 ` [PATCH bpf-next v2 13/28] HID: bpf: implement hid_bpf_get|set_data Benjamin Tissoires
                   ` (17 subsequent siblings)
  29 siblings, 2 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

When we process an incoming HID report, it is common to have to account
for fields that are not aligned in the report. HID is using 2 helpers
hid_field_extract() and implement() to pick up any data at any offset
within the report.

Export those 2 helpers in BPF programs so users can also rely on them.
The second net worth advantage of those helpers is that now we can
fetch data anywhere in the report without knowing at compile time the
location of it. The boundary checks are done in hid-bpf.c, to prevent
a memory leak.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

changes in v2:
- split the patch with libbpf and HID left outside.
---
 include/linux/bpf-hid.h        |  4 +++
 include/uapi/linux/bpf.h       | 32 ++++++++++++++++++++
 kernel/bpf/hid.c               | 53 ++++++++++++++++++++++++++++++++++
 tools/include/uapi/linux/bpf.h | 32 ++++++++++++++++++++
 4 files changed, 121 insertions(+)

diff --git a/include/linux/bpf-hid.h b/include/linux/bpf-hid.h
index 0c5000b28b20..69bb28523ceb 100644
--- a/include/linux/bpf-hid.h
+++ b/include/linux/bpf-hid.h
@@ -93,6 +93,10 @@ struct bpf_hid_hooks {
 	int (*link_attach)(struct hid_device *hdev, enum bpf_hid_attach_type type);
 	void (*link_attached)(struct hid_device *hdev, enum bpf_hid_attach_type type);
 	void (*array_detached)(struct hid_device *hdev, enum bpf_hid_attach_type type);
+	int (*hid_get_data)(struct hid_device *hdev, u8 *buf, size_t buf_size,
+			    u64 offset, u32 n, u8 *data, u64 data_size);
+	int (*hid_set_data)(struct hid_device *hdev, u8 *buf, size_t buf_size,
+			    u64 offset, u32 n, u8 *data, u64 data_size);
 };
 
 #ifdef CONFIG_BPF
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index a7a8d9cfcf24..4845a20e6f96 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -5090,6 +5090,36 @@ union bpf_attr {
  *	Return
  *		0 on success, or a negative error in case of failure. On error
  *		*dst* buffer is zeroed out.
+ *
+ * int bpf_hid_get_data(void *ctx, u64 offset, u32 n, u8 *data, u64 size)
+ *	Description
+ *		Get the data of size n (in bits) at the given offset (bits) in the
+ *		ctx->event.data field and store it into data.
+ *
+ *		if n is less or equal than 32, we can address with bit precision,
+ *		the value in the buffer. However, data must be a pointer to a u32
+ *		and size must be 4.
+ *
+ *		if n is greater than 32, offset and n must be a multiple of 8
+ *		and the result is working with a memcpy internally.
+ *	Return
+ *		The length of data copied into data. On error, a negative value
+ *		is returned.
+ *
+ * int bpf_hid_set_data(void *ctx, u64 offset, u32 n, u8 *data, u64 size)
+ *	Description
+ *		Set the data of size n (in bits) at the given offset (bits) in the
+ *		ctx->event.data field.
+ *
+ *		if n is less or equal than 32, we can address with bit precision,
+ *		the value in the buffer. However, data must be a pointer to a u32
+ *		and size must be 4.
+ *
+ *		if n is greater than 32, offset and n must be a multiple of 8
+ *		and the result is working with a memcpy internally.
+ *	Return
+ *		The length of data copied into ctx->event.data. On error, a negative
+ *		value is returned.
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -5284,6 +5314,8 @@ union bpf_attr {
 	FN(xdp_load_bytes),		\
 	FN(xdp_store_bytes),		\
 	FN(copy_from_user_task),	\
+	FN(hid_get_data),		\
+	FN(hid_set_data),		\
 	/* */
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
diff --git a/kernel/bpf/hid.c b/kernel/bpf/hid.c
index 37500313e270..640e55ba66ec 100644
--- a/kernel/bpf/hid.c
+++ b/kernel/bpf/hid.c
@@ -37,10 +37,63 @@ void bpf_hid_set_hooks(struct bpf_hid_hooks *hooks)
 }
 EXPORT_SYMBOL_GPL(bpf_hid_set_hooks);
 
+BPF_CALL_5(bpf_hid_get_data, void*, ctx, u64, offset, u32, n, void*, data, u64, size)
+{
+	struct hid_bpf_ctx *bpf_ctx = ctx;
+
+	if (!hid_hooks.hid_get_data)
+		return -EOPNOTSUPP;
+
+	return hid_hooks.hid_get_data(bpf_ctx->hdev,
+				      bpf_ctx->data, bpf_ctx->allocated_size,
+				      offset, n,
+				      data, size);
+}
+
+static const struct bpf_func_proto bpf_hid_get_data_proto = {
+	.func      = bpf_hid_get_data,
+	.gpl_only  = true,
+	.ret_type  = RET_INTEGER,
+	.arg1_type = ARG_PTR_TO_CTX,
+	.arg2_type = ARG_ANYTHING,
+	.arg3_type = ARG_ANYTHING,
+	.arg4_type = ARG_PTR_TO_MEM,
+	.arg5_type = ARG_CONST_SIZE_OR_ZERO,
+};
+
+BPF_CALL_5(bpf_hid_set_data, void*, ctx, u64, offset, u32, n, void*, data, u64, size)
+{
+	struct hid_bpf_ctx *bpf_ctx = ctx;
+
+	if (!hid_hooks.hid_set_data)
+		return -EOPNOTSUPP;
+
+	hid_hooks.hid_set_data(bpf_ctx->hdev,
+			       bpf_ctx->data, bpf_ctx->allocated_size,
+			       offset, n,
+			       data, size);
+	return 0;
+}
+
+static const struct bpf_func_proto bpf_hid_set_data_proto = {
+	.func      = bpf_hid_set_data,
+	.gpl_only  = true,
+	.ret_type  = RET_INTEGER,
+	.arg1_type = ARG_PTR_TO_CTX,
+	.arg2_type = ARG_ANYTHING,
+	.arg3_type = ARG_ANYTHING,
+	.arg4_type = ARG_PTR_TO_MEM,
+	.arg5_type = ARG_CONST_SIZE_OR_ZERO,
+};
+
 static const struct bpf_func_proto *
 hid_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 {
 	switch (func_id) {
+	case BPF_FUNC_hid_get_data:
+		return &bpf_hid_get_data_proto;
+	case BPF_FUNC_hid_set_data:
+		return &bpf_hid_set_data_proto;
 	default:
 		return bpf_base_func_proto(func_id);
 	}
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index a7a8d9cfcf24..4845a20e6f96 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -5090,6 +5090,36 @@ union bpf_attr {
  *	Return
  *		0 on success, or a negative error in case of failure. On error
  *		*dst* buffer is zeroed out.
+ *
+ * int bpf_hid_get_data(void *ctx, u64 offset, u32 n, u8 *data, u64 size)
+ *	Description
+ *		Get the data of size n (in bits) at the given offset (bits) in the
+ *		ctx->event.data field and store it into data.
+ *
+ *		if n is less or equal than 32, we can address with bit precision,
+ *		the value in the buffer. However, data must be a pointer to a u32
+ *		and size must be 4.
+ *
+ *		if n is greater than 32, offset and n must be a multiple of 8
+ *		and the result is working with a memcpy internally.
+ *	Return
+ *		The length of data copied into data. On error, a negative value
+ *		is returned.
+ *
+ * int bpf_hid_set_data(void *ctx, u64 offset, u32 n, u8 *data, u64 size)
+ *	Description
+ *		Set the data of size n (in bits) at the given offset (bits) in the
+ *		ctx->event.data field.
+ *
+ *		if n is less or equal than 32, we can address with bit precision,
+ *		the value in the buffer. However, data must be a pointer to a u32
+ *		and size must be 4.
+ *
+ *		if n is greater than 32, offset and n must be a multiple of 8
+ *		and the result is working with a memcpy internally.
+ *	Return
+ *		The length of data copied into ctx->event.data. On error, a negative
+ *		value is returned.
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -5284,6 +5314,8 @@ union bpf_attr {
 	FN(xdp_load_bytes),		\
 	FN(xdp_store_bytes),		\
 	FN(copy_from_user_task),	\
+	FN(hid_get_data),		\
+	FN(hid_set_data),		\
 	/* */
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 13/28] HID: bpf: implement hid_bpf_get|set_data
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (11 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 12/28] bpf/hid: add hid_{get|set}_data helpers Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-04 18:39   ` Greg KH
  2022-03-04 17:28 ` [PATCH bpf-next v2 14/28] selftests/bpf: add tests for hid_{get|set}_data helpers Benjamin Tissoires
                   ` (16 subsequent siblings)
  29 siblings, 1 reply; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

We have 2 cases of usage here:
- either n <= 32: we are addressing individual bits at the given offset

- either n > 32: we are using a memcpy to transmit the data to the caller,
  meaning that we need to be byte-aligned.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

changes in v2:
- split the series by bpf/libbpf/hid/selftests and samples
- allow for n > 32, by relying on memcpy
---
 drivers/hid/hid-bpf.c  | 68 ++++++++++++++++++++++++++++++++++++++++++
 drivers/hid/hid-core.c |  4 +--
 include/linux/hid.h    |  2 ++
 3 files changed, 72 insertions(+), 2 deletions(-)

diff --git a/drivers/hid/hid-bpf.c b/drivers/hid/hid-bpf.c
index 510e24f4307c..8ae247fba5bc 100644
--- a/drivers/hid/hid-bpf.c
+++ b/drivers/hid/hid-bpf.c
@@ -105,6 +105,72 @@ static void hid_bpf_array_detached(struct hid_device *hdev, enum bpf_hid_attach_
 	}
 }
 
+int hid_bpf_get_data(struct hid_device *hdev, u8 *buf, size_t buf_size, u64 offset, u32 n,
+		     u8 *data, u64 data_size)
+{
+	u32 *value = (u32 *)data;
+
+	if (((offset + n) >> 3) >= buf_size)
+		return -E2BIG;
+
+	if (n <= 32) {
+		/* data must be a pointer to a u32 */
+		if (data_size != 4)
+			return -EINVAL;
+
+		*value = hid_field_extract(hdev, buf, offset, n);
+		return 4;
+	}
+
+	/* if n > 32, use memcpy, but ensure we are dealing with full bytes */
+	if ((n | offset) & 0x7)
+		return -EINVAL;
+
+	/* work on bytes now */
+	offset = offset >> 3;
+	n = n >> 3;
+
+	if (n > data_size)
+		return -EINVAL;
+
+	memcpy(data, buf + offset, n);
+
+	return n;
+}
+
+int hid_bpf_set_data(struct hid_device *hdev, u8 *buf, size_t buf_size, u64 offset, u32 n,
+		     u8 *data, u64 data_size)
+{
+	u32 *value = (u32 *)data;
+
+	if (((offset + n) >> 3) >= buf_size)
+		return -E2BIG;
+
+	if (n <= 32) {
+		/* data must be a pointer to a u32 */
+		if (data_size != 4)
+			return -EINVAL;
+
+		implement(hdev, buf, offset, n, *value);
+		return 4;
+	}
+
+	/* if n > 32, use memcpy, but ensure we are dealing with full bytes */
+	if ((n | offset) & 0x7)
+		return -EINVAL;
+
+	/* work on bytes now */
+	offset = offset >> 3;
+	n = n >> 3;
+
+	if (n > data_size)
+		return -EINVAL;
+
+	memcpy(buf + offset, data, n);
+
+	return n;
+}
+
 static int hid_bpf_run_progs(struct hid_device *hdev, enum bpf_hid_attach_type type,
 			     struct hid_bpf_ctx *ctx, u8 *data, int size)
 {
@@ -204,6 +270,8 @@ int __init hid_bpf_module_init(void)
 		.link_attach = hid_bpf_link_attach,
 		.link_attached = hid_bpf_link_attached,
 		.array_detached = hid_bpf_array_detached,
+		.hid_get_data = hid_bpf_get_data,
+		.hid_set_data = hid_bpf_set_data,
 	};
 
 	bpf_hid_set_hooks(&hooks);
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 0eb8189faaee..d3f4499ee4cd 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1416,8 +1416,8 @@ static void __implement(u8 *report, unsigned offset, int n, u32 value)
 	}
 }
 
-static void implement(const struct hid_device *hid, u8 *report,
-		      unsigned offset, unsigned n, u32 value)
+void implement(const struct hid_device *hid, u8 *report, unsigned int offset, unsigned int n,
+	       u32 value)
 {
 	if (unlikely(n > 32)) {
 		hid_warn(hid, "%s() called with n (%d) > 32! (%s)\n",
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 66d949d10b78..7454e844324c 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -944,6 +944,8 @@ bool hid_compare_device_paths(struct hid_device *hdev_a,
 s32 hid_snto32(__u32 value, unsigned n);
 __u32 hid_field_extract(const struct hid_device *hid, __u8 *report,
 		     unsigned offset, unsigned n);
+void implement(const struct hid_device *hid, u8 *report, unsigned int offset, unsigned int n,
+	       u32 value);
 
 #ifdef CONFIG_PM
 int hid_driver_suspend(struct hid_device *hdev, pm_message_t state);
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 14/28] selftests/bpf: add tests for hid_{get|set}_data helpers
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (12 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 13/28] HID: bpf: implement hid_bpf_get|set_data Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-15 16:49   ` Tero Kristo
  2022-03-04 17:28 ` [PATCH bpf-next v2 15/28] bpf/hid: add new BPF type to trigger commands from userspace Benjamin Tissoires
                   ` (15 subsequent siblings)
  29 siblings, 1 reply; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

Simple test added here, with one use of each helper.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

changes in v2:
- split the patch with libbpf left outside.
---
 tools/testing/selftests/bpf/prog_tests/hid.c | 65 ++++++++++++++++++++
 tools/testing/selftests/bpf/progs/hid.c      | 45 ++++++++++++++
 2 files changed, 110 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/hid.c b/tools/testing/selftests/bpf/prog_tests/hid.c
index 91543b8078ca..74426523dd6f 100644
--- a/tools/testing/selftests/bpf/prog_tests/hid.c
+++ b/tools/testing/selftests/bpf/prog_tests/hid.c
@@ -297,6 +297,68 @@ static int test_hid_raw_event(struct hid *hid_skel, int uhid_fd, int sysfs_fd)
 	return ret;
 }
 
+/*
+ * Attach hid_set_get_data to the given uhid device,
+ * retrieve and open the matching hidraw node,
+ * inject one event in the uhid device,
+ * check that the program makes correct use of bpf_hid_{set|get}_data.
+ */
+static int test_hid_set_get_data(struct hid *hid_skel, int uhid_fd, int sysfs_fd)
+{
+	int err, hidraw_ino, hidraw_fd = -1;
+	char hidraw_path[64] = {0};
+	u8 buf[10] = {0};
+	int ret = -1;
+
+	/* attach hid_set_get_data program */
+	hid_skel->links.hid_set_get_data =
+		bpf_program__attach_hid(hid_skel->progs.hid_set_get_data, sysfs_fd);
+	if (!ASSERT_OK_PTR(hid_skel->links.hid_set_get_data,
+			   "attach_hid(hid_set_get_data)"))
+		return PTR_ERR(hid_skel->links.hid_set_get_data);
+
+	hidraw_ino = get_hidraw(hid_skel->links.hid_set_get_data);
+	if (!ASSERT_GE(hidraw_ino, 0, "get_hidraw"))
+		goto cleanup;
+
+	/* open hidraw node to check the other side of the pipe */
+	sprintf(hidraw_path, "/dev/hidraw%d", hidraw_ino);
+	hidraw_fd = open(hidraw_path, O_RDWR | O_NONBLOCK);
+
+	if (!ASSERT_GE(hidraw_fd, 0, "open_hidraw"))
+		goto cleanup;
+
+	/* inject one event */
+	buf[0] = 1;
+	buf[1] = 42;
+	send_event(uhid_fd, buf, 6);
+
+	/* read the data from hidraw */
+	memset(buf, 0, sizeof(buf));
+	err = read(hidraw_fd, buf, sizeof(buf));
+	if (!ASSERT_EQ(err, 6, "read_hidraw"))
+		goto cleanup;
+
+	if (!ASSERT_EQ(buf[2], (42 >> 2), "hid_set_get_data"))
+		goto cleanup;
+
+	if (!ASSERT_EQ(buf[3], 1, "hid_set_get_data"))
+		goto cleanup;
+
+	if (!ASSERT_EQ(buf[4], 42, "hid_set_get_data"))
+		goto cleanup;
+
+	ret = 0;
+
+cleanup:
+	if (hidraw_fd >= 0)
+		close(hidraw_fd);
+
+	hid__detach(hid_skel);
+
+	return ret;
+}
+
 /*
  * Attach hid_rdesc_fixup to the given uhid device,
  * retrieve and open the matching hidraw node,
@@ -395,6 +457,9 @@ void serial_test_hid_bpf(void)
 	err = test_hid_raw_event(hid_skel, uhid_fd, sysfs_fd);
 	ASSERT_OK(err, "hid");
 
+	err = test_hid_set_get_data(hid_skel, uhid_fd, sysfs_fd);
+	ASSERT_OK(err, "hid_set_get_data");
+
 	err = test_rdesc_fixup(hid_skel, uhid_fd, sysfs_fd);
 	ASSERT_OK(err, "hid_rdesc_fixup");
 
diff --git a/tools/testing/selftests/bpf/progs/hid.c b/tools/testing/selftests/bpf/progs/hid.c
index 2270448d0d3f..de6668471940 100644
--- a/tools/testing/selftests/bpf/progs/hid.c
+++ b/tools/testing/selftests/bpf/progs/hid.c
@@ -66,3 +66,48 @@ int hid_rdesc_fixup(struct hid_bpf_ctx *ctx)
 
 	return 0;
 }
+
+SEC("hid/device_event")
+int hid_set_get_data(struct hid_bpf_ctx *ctx)
+{
+	int ret;
+	__u8 *buf;
+
+	buf = bpf_ringbuf_reserve(&ringbuf, 8, 0);
+	if (!buf)
+		return -12; /* -ENOMEM */
+
+	/* first try read/write with n > 32 */
+	ret = bpf_hid_get_data(ctx, 0, 64, buf, 8);
+	if (ret < 0)
+		goto discard;
+
+	/* reinject it */
+	ret = bpf_hid_set_data(ctx, 24, 64, buf, 8);
+	if (ret < 0)
+		goto discard;
+
+	/* extract data at bit offset 10 of size 4 (half a byte) */
+	ret = bpf_hid_get_data(ctx, 10, 4, buf, 8);  /* expected to fail */
+	if (ret > 0) {
+		ret = -1;
+		goto discard;
+	}
+
+	ret = bpf_hid_get_data(ctx, 10, 4, buf, 4);
+	if (ret < 0)
+		goto discard;
+
+	/* reinject it */
+	ret = bpf_hid_set_data(ctx, 16, 4, buf, 4);
+	if (ret < 0)
+		goto discard;
+
+	ret = 0;
+
+ discard:
+
+	bpf_ringbuf_discard(buf, 0);
+
+	return ret;
+}
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 15/28] bpf/hid: add new BPF type to trigger commands from userspace
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (13 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 14/28] selftests/bpf: add tests for hid_{get|set}_data helpers Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-11  0:46   ` Song Liu
  2022-03-04 17:28 ` [PATCH bpf-next v2 16/28] libbpf: add new attach type BPF_HID_USER_EVENT Benjamin Tissoires
                   ` (14 subsequent siblings)
  29 siblings, 1 reply; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

Given that we can not call bpf_hid_raw_request() from within an IRQ,
userspace needs to have a way to communicate with the device when
it needs.

Implement a new type that the caller can run at will without being in
an IRQ context.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

changes in v2:
- split the series by bpf/libbpf/hid/selftests and samples
- unsigned long -> __u16 in uapi/linux/bpf_hid.h
- int -> __32 in uapi/linux/bpf_hid.h
---
 include/linux/bpf-hid.h        |   3 +
 include/uapi/linux/bpf.h       |   1 +
 include/uapi/linux/bpf_hid.h   |  10 +++
 kernel/bpf/hid.c               | 116 +++++++++++++++++++++++++++++++++
 kernel/bpf/syscall.c           |   2 +
 tools/include/uapi/linux/bpf.h |   1 +
 6 files changed, 133 insertions(+)

diff --git a/include/linux/bpf-hid.h b/include/linux/bpf-hid.h
index 69bb28523ceb..4cf2e99109fe 100644
--- a/include/linux/bpf-hid.h
+++ b/include/linux/bpf-hid.h
@@ -16,6 +16,7 @@ enum bpf_hid_attach_type {
 	BPF_HID_ATTACH_INVALID = -1,
 	BPF_HID_ATTACH_DEVICE_EVENT = 0,
 	BPF_HID_ATTACH_RDESC_FIXUP,
+	BPF_HID_ATTACH_USER_EVENT,
 	MAX_BPF_HID_ATTACH_TYPE
 };
 
@@ -35,6 +36,8 @@ to_bpf_hid_attach_type(enum bpf_attach_type attach_type)
 		return BPF_HID_ATTACH_DEVICE_EVENT;
 	case BPF_HID_RDESC_FIXUP:
 		return BPF_HID_ATTACH_RDESC_FIXUP;
+	case BPF_HID_USER_EVENT:
+		return BPF_HID_ATTACH_USER_EVENT;
 	default:
 		return BPF_HID_ATTACH_INVALID;
 	}
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 4845a20e6f96..b3063384d380 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -1000,6 +1000,7 @@ enum bpf_attach_type {
 	BPF_PERF_EVENT,
 	BPF_HID_DEVICE_EVENT,
 	BPF_HID_RDESC_FIXUP,
+	BPF_HID_USER_EVENT,
 	__MAX_BPF_ATTACH_TYPE
 };
 
diff --git a/include/uapi/linux/bpf_hid.h b/include/uapi/linux/bpf_hid.h
index 634f17c0b1cb..14a3c0405345 100644
--- a/include/uapi/linux/bpf_hid.h
+++ b/include/uapi/linux/bpf_hid.h
@@ -25,6 +25,12 @@ enum hid_bpf_event {
 	HID_BPF_UNDEF = 0,
 	HID_BPF_DEVICE_EVENT,		/* when attach type is BPF_HID_DEVICE_EVENT */
 	HID_BPF_RDESC_FIXUP,		/* ................... BPF_HID_RDESC_FIXUP */
+	HID_BPF_USER_EVENT,		/* ................... BPF_HID_USER_EVENT */
+};
+
+/* type is HID_BPF_USER_EVENT */
+struct hid_bpf_ctx_user_event {
+	__s32 retval;
 };
 
 struct hid_bpf_ctx {
@@ -32,6 +38,10 @@ struct hid_bpf_ctx {
 	__u16 allocated_size;		/* the allocated size of data below (RO) */
 	struct hid_device *hdev;	/* read-only */
 
+	union {
+		struct hid_bpf_ctx_user_event user;	/* read-write */
+	} u;
+
 	__u16 size;			/* used size in data (RW) */
 	__u8 data[];			/* data buffer (RW) */
 };
diff --git a/kernel/bpf/hid.c b/kernel/bpf/hid.c
index 640e55ba66ec..de003dbd7d01 100644
--- a/kernel/bpf/hid.c
+++ b/kernel/bpf/hid.c
@@ -370,6 +370,8 @@ static int bpf_hid_max_progs(enum bpf_hid_attach_type type)
 		return 64;
 	case BPF_HID_ATTACH_RDESC_FIXUP:
 		return 1;
+	case BPF_HID_ATTACH_USER_EVENT:
+		return 64;
 	default:
 		return 0;
 	}
@@ -464,7 +466,121 @@ int bpf_hid_link_create(const union bpf_attr *attr, struct bpf_prog *prog)
 	return bpf_link_settle(&link_primer);
 }
 
+static int hid_bpf_prog_test_run(struct bpf_prog *prog,
+				 const union bpf_attr *attr,
+				 union bpf_attr __user *uattr)
+{
+	struct hid_device *hdev = NULL;
+	struct bpf_prog_array *progs;
+	struct hid_bpf_ctx *ctx = NULL;
+	bool valid_prog = false;
+	int i;
+	int target_fd, ret;
+	void __user *data_out = u64_to_user_ptr(attr->test.data_out);
+	void __user *data_in = u64_to_user_ptr(attr->test.data_in);
+	u32 user_size_in = attr->test.data_size_in;
+	u32 user_size_out = attr->test.data_size_out;
+
+	if (!hid_hooks.hdev_from_fd)
+		return -EOPNOTSUPP;
+
+	if (attr->test.ctx_size_in != sizeof(int))
+		return -EINVAL;
+
+	if (user_size_in > HID_BPF_MAX_BUFFER_SIZE)
+		return -E2BIG;
+
+	if (copy_from_user(&target_fd, (void *)attr->test.ctx_in, attr->test.ctx_size_in))
+		return -EFAULT;
+
+	hdev = hid_hooks.hdev_from_fd(target_fd);
+	if (IS_ERR(hdev))
+		return PTR_ERR(hdev);
+
+	ret = mutex_lock_interruptible(&bpf_hid_mutex);
+	if (ret)
+		return ret;
+
+	/* check if the given program is of correct type and registered */
+	progs = rcu_dereference_protected(hdev->bpf.run_array[BPF_HID_ATTACH_USER_EVENT],
+					  lockdep_is_held(&bpf_hid_mutex));
+	if (!progs) {
+		ret = -EFAULT;
+		goto unlock;
+	}
+
+	for (i = 0; i < bpf_prog_array_length(progs); i++) {
+		if (progs->items[i].prog == prog) {
+			valid_prog = true;
+			break;
+		}
+	}
+
+	if (!valid_prog) {
+		ret = -EINVAL;
+		goto unlock;
+	}
+
+	ctx = bpf_hid_allocate_ctx(hdev, max(user_size_in, user_size_out));
+	if (IS_ERR(ctx)) {
+		ret = PTR_ERR(ctx);
+		goto unlock;
+	}
+
+	ctx->type = HID_BPF_USER_EVENT;
+
+	/* copy data_in from userspace */
+	if (user_size_in) {
+		if (user_size_in > ctx->allocated_size) {
+			/* should never happen, given that size is < HID_BPF_MAX_BUFFER_SIZE */
+			ret = -E2BIG;
+			goto unlock;
+		}
+
+		if (copy_from_user(ctx->data, data_in, user_size_in)) {
+			ret = -EFAULT;
+			goto unlock;
+		}
+
+		ctx->size = user_size_in;
+	}
+
+	migrate_disable();
+
+	ret = bpf_prog_run(prog, ctx);
+
+	migrate_enable();
+
+	if (user_size_out && data_out) {
+		user_size_out = min3(user_size_out, (u32)ctx->size, (u32)ctx->allocated_size);
+
+		if (copy_to_user(data_out, ctx->data, user_size_out)) {
+			ret = -EFAULT;
+			goto unlock;
+		}
+
+		if (copy_to_user(&uattr->test.data_size_out,
+				 &user_size_out,
+				 sizeof(user_size_out))) {
+			ret = -EFAULT;
+			goto unlock;
+		}
+	}
+
+	if (copy_to_user(&uattr->test.retval, &ctx->u.user.retval, sizeof(ctx->u.user.retval))) {
+		ret = -EFAULT;
+		goto unlock;
+	}
+
+unlock:
+	kfree(ctx);
+
+	mutex_unlock(&bpf_hid_mutex);
+	return ret;
+}
+
 const struct bpf_prog_ops hid_prog_ops = {
+	.test_run = hid_bpf_prog_test_run,
 };
 
 int bpf_hid_init(struct hid_device *hdev)
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 7428a1a512c6..74d13ec826df 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -3204,6 +3204,7 @@ attach_type_to_prog_type(enum bpf_attach_type attach_type)
 		return BPF_PROG_TYPE_XDP;
 	case BPF_HID_DEVICE_EVENT:
 	case BPF_HID_RDESC_FIXUP:
+	case BPF_HID_USER_EVENT:
 		return BPF_PROG_TYPE_HID;
 	default:
 		return BPF_PROG_TYPE_UNSPEC;
@@ -3350,6 +3351,7 @@ static int bpf_prog_query(const union bpf_attr *attr,
 		return sock_map_bpf_prog_query(attr, uattr);
 	case BPF_HID_DEVICE_EVENT:
 	case BPF_HID_RDESC_FIXUP:
+	case BPF_HID_USER_EVENT:
 		return bpf_hid_prog_query(attr, uattr);
 	default:
 		return -EINVAL;
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 4845a20e6f96..b3063384d380 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -1000,6 +1000,7 @@ enum bpf_attach_type {
 	BPF_PERF_EVENT,
 	BPF_HID_DEVICE_EVENT,
 	BPF_HID_RDESC_FIXUP,
+	BPF_HID_USER_EVENT,
 	__MAX_BPF_ATTACH_TYPE
 };
 
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 16/28] libbpf: add new attach type BPF_HID_USER_EVENT
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (14 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 15/28] bpf/hid: add new BPF type to trigger commands from userspace Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-04 17:28 ` [PATCH bpf-next v2 17/28] selftests/bpf: add test for user call of HID bpf programs Benjamin Tissoires
                   ` (13 subsequent siblings)
  29 siblings, 0 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

Adds the SEC definiton for the user callbacks of HID bpf.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

changes in v2:
- split the series by bpf/libbpf/hid/selftests and samples
---
 tools/lib/bpf/libbpf.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 192ef3901251..d1e305f760bb 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -8682,6 +8682,7 @@ static const struct bpf_sec_def section_defs[] = {
 	SEC_DEF("sk_lookup",		SK_LOOKUP, BPF_SK_LOOKUP, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 	SEC_DEF("hid/device_event",	HID, BPF_HID_DEVICE_EVENT, SEC_ATTACHABLE_OPT | SEC_SLOPPY_PFX),
 	SEC_DEF("hid/rdesc_fixup",	HID, BPF_HID_RDESC_FIXUP, SEC_ATTACHABLE_OPT | SEC_SLOPPY_PFX),
+	SEC_DEF("hid/user_event",	HID, BPF_HID_USER_EVENT, SEC_ATTACHABLE_OPT | SEC_SLOPPY_PFX),
 };
 
 #define MAX_TYPE_NAME_SIZE 32
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 17/28] selftests/bpf: add test for user call of HID bpf programs
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (15 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 16/28] libbpf: add new attach type BPF_HID_USER_EVENT Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-04 17:28 ` [PATCH bpf-next v2 18/28] selftests/bpf: hid: rely on uhid event to know if a test device is ready Benjamin Tissoires
                   ` (12 subsequent siblings)
  29 siblings, 0 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

Add a simple test to see if we can trigger a bpf program of type
"hid/user_event".

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

changes in v2:
- split the series by bpf/libbpf/hid/selftests and samples
---
 tools/testing/selftests/bpf/prog_tests/hid.c | 56 ++++++++++++++++++++
 tools/testing/selftests/bpf/progs/hid.c      | 10 ++++
 2 files changed, 66 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/hid.c b/tools/testing/selftests/bpf/prog_tests/hid.c
index 74426523dd6f..eb4c0d0a4666 100644
--- a/tools/testing/selftests/bpf/prog_tests/hid.c
+++ b/tools/testing/selftests/bpf/prog_tests/hid.c
@@ -359,6 +359,59 @@ static int test_hid_set_get_data(struct hid *hid_skel, int uhid_fd, int sysfs_fd
 	return ret;
 }
 
+/*
+ * Attach hid_user to the given uhid device,
+ * call the bpf program from userspace
+ * check that the program is called and does the expected.
+ */
+static int test_hid_user_call(struct hid *hid_skel, int uhid_fd, int sysfs_fd)
+{
+	int err, prog_fd;
+	u8 buf[10] = {0};
+	int ret = -1;
+
+	LIBBPF_OPTS(bpf_test_run_opts, run_attrs,
+		    .repeat = 1,
+		    .ctx_in = &sysfs_fd,
+		    .ctx_size_in = sizeof(sysfs_fd),
+		    .data_in = buf,
+		    .data_size_in = sizeof(buf),
+		    .data_out = buf,
+		    .data_size_out = sizeof(buf),
+	);
+
+	/* attach hid_user program */
+	hid_skel->links.hid_user = bpf_program__attach_hid(hid_skel->progs.hid_user, sysfs_fd);
+	if (!ASSERT_OK_PTR(hid_skel->links.hid_user,
+			   "attach_hid(hid_user)"))
+		return PTR_ERR(hid_skel->links.hid_user);
+
+	buf[0] = 39;
+
+	prog_fd = bpf_program__fd(hid_skel->progs.hid_user);
+
+	err = bpf_prog_test_run_opts(prog_fd, &run_attrs);
+	if (!ASSERT_EQ(err, 0, "bpf_prog_test_run_xattr"))
+		goto cleanup;
+
+	if (!ASSERT_EQ(run_attrs.retval, 72, "bpf_prog_test_run_opts"))
+		goto cleanup;
+
+	if (!ASSERT_EQ(buf[1], 42, "hid_user_check_in"))
+		goto cleanup;
+
+	if (!ASSERT_EQ(buf[2], 4, "hid_user_check_static_out"))
+		goto cleanup;
+
+	ret = 0;
+
+cleanup:
+
+	hid__detach(hid_skel);
+
+	return ret;
+}
+
 /*
  * Attach hid_rdesc_fixup to the given uhid device,
  * retrieve and open the matching hidraw node,
@@ -460,6 +513,9 @@ void serial_test_hid_bpf(void)
 	err = test_hid_set_get_data(hid_skel, uhid_fd, sysfs_fd);
 	ASSERT_OK(err, "hid_set_get_data");
 
+	err = test_hid_user_call(hid_skel, uhid_fd, sysfs_fd);
+	ASSERT_OK(err, "hid_user");
+
 	err = test_rdesc_fixup(hid_skel, uhid_fd, sysfs_fd);
 	ASSERT_OK(err, "hid_rdesc_fixup");
 
diff --git a/tools/testing/selftests/bpf/progs/hid.c b/tools/testing/selftests/bpf/progs/hid.c
index de6668471940..fabfaf0f2526 100644
--- a/tools/testing/selftests/bpf/progs/hid.c
+++ b/tools/testing/selftests/bpf/progs/hid.c
@@ -111,3 +111,13 @@ int hid_set_get_data(struct hid_bpf_ctx *ctx)
 
 	return ret;
 }
+
+SEC("hid/user_event")
+int hid_user(struct hid_bpf_ctx *ctx)
+{
+	ctx->data[1] = ctx->data[0] + 3;
+	ctx->data[2] = 4;
+	ctx->u.user.retval = 72;
+
+	return 0;
+}
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 18/28] selftests/bpf: hid: rely on uhid event to know if a test device is ready
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (16 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 17/28] selftests/bpf: add test for user call of HID bpf programs Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-04 17:28 ` [PATCH bpf-next v2 19/28] bpf/hid: add bpf_hid_raw_request helper function Benjamin Tissoires
                   ` (11 subsequent siblings)
  29 siblings, 0 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

We need this for 2 reasons:
- first we remove the ugly sleeps
- then when we try to communicate with the device, we need to have another
  thread that handles that communication and simulate a real device

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

changes in v2:
- split the series by bpf/libbpf/hid/selftests and samples
---
 tools/testing/selftests/bpf/prog_tests/hid.c | 126 ++++++++++++++++++-
 1 file changed, 120 insertions(+), 6 deletions(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/hid.c b/tools/testing/selftests/bpf/prog_tests/hid.c
index eb4c0d0a4666..cc01601c1168 100644
--- a/tools/testing/selftests/bpf/prog_tests/hid.c
+++ b/tools/testing/selftests/bpf/prog_tests/hid.c
@@ -67,6 +67,12 @@ static unsigned char rdesc[] = {
 	0xc0,			/* END_COLLECTION */
 };
 
+static pthread_mutex_t uhid_started_mtx = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t uhid_started = PTHREAD_COND_INITIALIZER;
+
+/* no need to protect uhid_stopped, only one thread accesses it */
+static bool uhid_stopped;
+
 static int uhid_write(int fd, const struct uhid_event *ev)
 {
 	ssize_t ret;
@@ -118,6 +124,104 @@ static void destroy(int fd)
 	uhid_write(fd, &ev);
 }
 
+static int event(int fd)
+{
+	struct uhid_event ev;
+	ssize_t ret;
+
+	memset(&ev, 0, sizeof(ev));
+	ret = read(fd, &ev, sizeof(ev));
+	if (ret == 0) {
+		fprintf(stderr, "Read HUP on uhid-cdev\n");
+		return -EFAULT;
+	} else if (ret < 0) {
+		fprintf(stderr, "Cannot read uhid-cdev: %m\n");
+		return -errno;
+	} else if (ret != sizeof(ev)) {
+		fprintf(stderr, "Invalid size read from uhid-dev: %zd != %zu\n",
+			ret, sizeof(ev));
+		return -EFAULT;
+	}
+
+	switch (ev.type) {
+	case UHID_START:
+		pthread_mutex_lock(&uhid_started_mtx);
+		pthread_cond_signal(&uhid_started);
+		pthread_mutex_unlock(&uhid_started_mtx);
+
+		fprintf(stderr, "UHID_START from uhid-dev\n");
+		break;
+	case UHID_STOP:
+		uhid_stopped = true;
+
+		fprintf(stderr, "UHID_STOP from uhid-dev\n");
+		break;
+	case UHID_OPEN:
+		fprintf(stderr, "UHID_OPEN from uhid-dev\n");
+		break;
+	case UHID_CLOSE:
+		fprintf(stderr, "UHID_CLOSE from uhid-dev\n");
+		break;
+	case UHID_OUTPUT:
+		fprintf(stderr, "UHID_OUTPUT from uhid-dev\n");
+		break;
+	case UHID_GET_REPORT:
+		fprintf(stderr, "UHID_GET_REPORT from uhid-dev\n");
+		break;
+	case UHID_SET_REPORT:
+		fprintf(stderr, "UHID_SET_REPORT from uhid-dev\n");
+		break;
+	default:
+		fprintf(stderr, "Invalid event from uhid-dev: %u\n", ev.type);
+	}
+
+	return 0;
+}
+
+static void *read_uhid_events_thread(void *arg)
+{
+	int fd = *(int *)arg;
+	struct pollfd pfds[1];
+	int ret = 0;
+
+	pfds[0].fd = fd;
+	pfds[0].events = POLLIN;
+
+	uhid_stopped = false;
+
+	while (!uhid_stopped) {
+		ret = poll(pfds, 1, 100);
+		if (ret < 0) {
+			fprintf(stderr, "Cannot poll for fds: %m\n");
+			break;
+		}
+		if (pfds[0].revents & POLLIN) {
+			ret = event(fd);
+			if (ret)
+				break;
+		}
+	}
+
+	return (void *)(long)ret;
+}
+
+static int uhid_start_listener(pthread_t *tid, int uhid_fd)
+{
+	int fd = uhid_fd;
+
+	pthread_mutex_lock(&uhid_started_mtx);
+	if (CHECK_FAIL(pthread_create(tid, NULL, read_uhid_events_thread,
+				      (void *)&fd))) {
+		pthread_mutex_unlock(&uhid_started_mtx);
+		close(fd);
+		return -EIO;
+	}
+	pthread_cond_wait(&uhid_started, &uhid_started_mtx);
+	pthread_mutex_unlock(&uhid_started_mtx);
+
+	return 0;
+}
+
 static int send_event(int fd, u8 *buf, size_t size)
 {
 	struct uhid_event ev;
@@ -422,7 +526,9 @@ static int test_rdesc_fixup(struct hid *hid_skel, int uhid_fd, int sysfs_fd)
 	struct hidraw_report_descriptor rpt_desc = {0};
 	int err, desc_size, hidraw_ino, hidraw_fd = -1;
 	char hidraw_path[64] = {0};
+	void *uhid_err;
 	int ret = -1;
+	pthread_t tid;
 
 	/* attach the program */
 	hid_skel->links.hid_rdesc_fixup =
@@ -431,9 +537,8 @@ static int test_rdesc_fixup(struct hid *hid_skel, int uhid_fd, int sysfs_fd)
 			   "attach_hid(hid_rdesc_fixup)"))
 		return PTR_ERR(hid_skel->links.hid_rdesc_fixup);
 
-	/* give a little bit of time for the device to appear */
-	/* TODO: check on uhid events */
-	usleep(1000);
+	err = uhid_start_listener(&tid, uhid_fd);
+	ASSERT_OK(err, "uhid_start_listener");
 
 	hidraw_ino = get_hidraw(hid_skel->links.hid_rdesc_fixup);
 	if (!ASSERT_GE(hidraw_ino, 0, "get_hidraw"))
@@ -474,6 +579,10 @@ static int test_rdesc_fixup(struct hid *hid_skel, int uhid_fd, int sysfs_fd)
 
 	hid__detach(hid_skel);
 
+	pthread_join(tid, &uhid_err);
+	err = (int)(long)uhid_err;
+	CHECK_FAIL(err);
+
 	return ret;
 }
 
@@ -481,7 +590,9 @@ void serial_test_hid_bpf(void)
 {
 	struct hid *hid_skel = NULL;
 	int err, uhid_fd, sysfs_fd;
+	void *uhid_err;
 	time_t t;
+	pthread_t tid;
 	int rand_nb;
 
 	/* initialize random number generator */
@@ -493,9 +604,8 @@ void serial_test_hid_bpf(void)
 	if (!ASSERT_GE(uhid_fd, 0, "setup uhid"))
 		return;
 
-	/* give a little bit of time for the device to appear */
-	/* TODO: check on uhid events */
-	usleep(1000);
+	err = uhid_start_listener(&tid, uhid_fd);
+	ASSERT_OK(err, "uhid_start_listener");
 
 	/* locate the uevent file of the created device */
 	sysfs_fd = get_sysfs_fd(rand_nb);
@@ -522,4 +632,8 @@ void serial_test_hid_bpf(void)
 cleanup:
 	hid__destroy(hid_skel);
 	destroy(uhid_fd);
+
+	pthread_join(tid, &uhid_err);
+	err = (int)(long)uhid_err;
+	CHECK_FAIL(err);
 }
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 19/28] bpf/hid: add bpf_hid_raw_request helper function
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (17 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 18/28] selftests/bpf: hid: rely on uhid event to know if a test device is ready Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-11  0:50   ` Song Liu
  2022-03-04 17:28 ` [PATCH bpf-next v2 20/28] HID: add implementation of bpf_hid_raw_request Benjamin Tissoires
                   ` (10 subsequent siblings)
  29 siblings, 1 reply; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

When we are in a user_event context, we can talk to the device to fetch
or set features/outputs/inputs reports.
Add a bpf helper to do so. This helper is thus only available to
user_events, because calling this function while in IRQ context (any
other BPF type) is forbidden.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

changes in v2:
- split the series by bpf/libbpf/hid/selftests and samples
---
 include/linux/bpf-hid.h        |  2 ++
 include/uapi/linux/bpf.h       |  8 ++++++++
 kernel/bpf/hid.c               | 26 ++++++++++++++++++++++++++
 tools/include/uapi/linux/bpf.h |  8 ++++++++
 4 files changed, 44 insertions(+)

diff --git a/include/linux/bpf-hid.h b/include/linux/bpf-hid.h
index 4cf2e99109fe..bd548f6a4a26 100644
--- a/include/linux/bpf-hid.h
+++ b/include/linux/bpf-hid.h
@@ -100,6 +100,8 @@ struct bpf_hid_hooks {
 			    u64 offset, u32 n, u8 *data, u64 data_size);
 	int (*hid_set_data)(struct hid_device *hdev, u8 *buf, size_t buf_size,
 			    u64 offset, u32 n, u8 *data, u64 data_size);
+	int (*hid_raw_request)(struct hid_device *hdev, u8 *buf, size_t size,
+			       u8 rtype, u8 reqtype);
 };
 
 #ifdef CONFIG_BPF
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index b3063384d380..417cf1c31579 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -5121,6 +5121,13 @@ union bpf_attr {
  *	Return
  *		The length of data copied into ctx->event.data. On error, a negative
  *		value is returned.
+ *
+ * int bpf_hid_raw_request(void *ctx, void *buf, u64 size, u8 rtype, u8 reqtype)
+ *	Description
+ *		communicate with the HID device
+ *	Return
+ *		0 on success.
+ *		negative value on error.
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -5317,6 +5324,7 @@ union bpf_attr {
 	FN(copy_from_user_task),	\
 	FN(hid_get_data),		\
 	FN(hid_set_data),		\
+	FN(hid_raw_request),		\
 	/* */
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
diff --git a/kernel/bpf/hid.c b/kernel/bpf/hid.c
index de003dbd7d01..653d10c0f4e6 100644
--- a/kernel/bpf/hid.c
+++ b/kernel/bpf/hid.c
@@ -86,6 +86,28 @@ static const struct bpf_func_proto bpf_hid_set_data_proto = {
 	.arg5_type = ARG_CONST_SIZE_OR_ZERO,
 };
 
+BPF_CALL_5(bpf_hid_raw_request, void*, ctx, void*, buf, u64, size,
+	   u8, rtype, u8, reqtype)
+{
+	struct hid_bpf_ctx *bpf_ctx = ctx;
+
+	if (!hid_hooks.hid_raw_request)
+		return -EOPNOTSUPP;
+
+	return hid_hooks.hid_raw_request(bpf_ctx->hdev, buf, size, rtype, reqtype);
+}
+
+static const struct bpf_func_proto bpf_hid_raw_request_proto = {
+	.func      = bpf_hid_raw_request,
+	.gpl_only  = true, /* hid_raw_request is EXPORT_SYMBOL_GPL */
+	.ret_type  = RET_INTEGER,
+	.arg1_type = ARG_PTR_TO_CTX,
+	.arg2_type = ARG_PTR_TO_MEM,
+	.arg3_type = ARG_CONST_SIZE_OR_ZERO,
+	.arg4_type = ARG_ANYTHING,
+	.arg5_type = ARG_ANYTHING,
+};
+
 static const struct bpf_func_proto *
 hid_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 {
@@ -94,6 +116,10 @@ hid_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 		return &bpf_hid_get_data_proto;
 	case BPF_FUNC_hid_set_data:
 		return &bpf_hid_set_data_proto;
+	case BPF_FUNC_hid_raw_request:
+		if (prog->expected_attach_type != BPF_HID_DEVICE_EVENT)
+			return &bpf_hid_raw_request_proto;
+		return NULL;
 	default:
 		return bpf_base_func_proto(func_id);
 	}
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index b3063384d380..417cf1c31579 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -5121,6 +5121,13 @@ union bpf_attr {
  *	Return
  *		The length of data copied into ctx->event.data. On error, a negative
  *		value is returned.
+ *
+ * int bpf_hid_raw_request(void *ctx, void *buf, u64 size, u8 rtype, u8 reqtype)
+ *	Description
+ *		communicate with the HID device
+ *	Return
+ *		0 on success.
+ *		negative value on error.
  */
 #define __BPF_FUNC_MAPPER(FN)		\
 	FN(unspec),			\
@@ -5317,6 +5324,7 @@ union bpf_attr {
 	FN(copy_from_user_task),	\
 	FN(hid_get_data),		\
 	FN(hid_set_data),		\
+	FN(hid_raw_request),		\
 	/* */
 
 /* integer value in 'imm' field of BPF_CALL instruction selects which helper
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 20/28] HID: add implementation of bpf_hid_raw_request
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (18 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 19/28] bpf/hid: add bpf_hid_raw_request helper function Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-04 17:28 ` [PATCH bpf-next v2 21/28] selftests/bpf: add tests for bpf_hid_hw_request Benjamin Tissoires
                   ` (9 subsequent siblings)
  29 siblings, 0 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

Hook up BPF to hid_hw_raw_request.
Not much to report here except that we need to export hid_get_report
from hid-core so it gets available in hid-bpf.c

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

changes in v2:
- split the series by bpf/libbpf/hid/selftests and samples
---
 drivers/hid/hid-bpf.c  | 63 ++++++++++++++++++++++++++++++++++++++++++
 drivers/hid/hid-core.c |  3 +-
 include/linux/hid.h    |  1 +
 3 files changed, 65 insertions(+), 2 deletions(-)

diff --git a/drivers/hid/hid-bpf.c b/drivers/hid/hid-bpf.c
index 8ae247fba5bc..b8c0060f3180 100644
--- a/drivers/hid/hid-bpf.c
+++ b/drivers/hid/hid-bpf.c
@@ -171,6 +171,68 @@ int hid_bpf_set_data(struct hid_device *hdev, u8 *buf, size_t buf_size, u64 offs
 	return n;
 }
 
+int hid_bpf_raw_request(struct hid_device *hdev, u8 *buf, size_t size,
+			u8 rtype, u8 reqtype)
+{
+	struct hid_report *report;
+	struct hid_report_enum *report_enum;
+	u8 *dma_data;
+	u32 report_len;
+	int ret;
+
+	/* check arguments */
+	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 (size < 1)
+		return -EINVAL;
+
+	report_enum = hdev->report_enum + rtype;
+	report = hid_get_report(report_enum, buf);
+	if (!report)
+		return -EINVAL;
+
+	report_len = hid_report_len(report);
+
+	if (size > report_len)
+		size = report_len;
+
+	dma_data = kmemdup(buf, size, GFP_KERNEL);
+	if (!dma_data)
+		return -ENOMEM;
+
+	ret = hid_hw_raw_request(hdev,
+				 dma_data[0],
+				 dma_data,
+				 size,
+				 rtype,
+				 reqtype);
+
+	if (ret > 0)
+		memcpy(buf, dma_data, ret);
+
+	kfree(dma_data);
+	return ret;
+}
+
 static int hid_bpf_run_progs(struct hid_device *hdev, enum bpf_hid_attach_type type,
 			     struct hid_bpf_ctx *ctx, u8 *data, int size)
 {
@@ -272,6 +334,7 @@ int __init hid_bpf_module_init(void)
 		.array_detached = hid_bpf_array_detached,
 		.hid_get_data = hid_bpf_get_data,
 		.hid_set_data = hid_bpf_set_data,
+		.hid_raw_request  = hid_bpf_raw_request,
 	};
 
 	bpf_hid_set_hooks(&hooks);
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index d3f4499ee4cd..d0e015986e17 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1686,8 +1686,7 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value)
 }
 EXPORT_SYMBOL_GPL(hid_set_field);
 
-static struct hid_report *hid_get_report(struct hid_report_enum *report_enum,
-		const u8 *data)
+struct hid_report *hid_get_report(struct hid_report_enum *report_enum, const u8 *data)
 {
 	struct hid_report *report;
 	unsigned int n = 0;	/* Normally report number is 0 */
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 7454e844324c..b2698df31e5b 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -946,6 +946,7 @@ __u32 hid_field_extract(const struct hid_device *hid, __u8 *report,
 		     unsigned offset, unsigned n);
 void implement(const struct hid_device *hid, u8 *report, unsigned int offset, unsigned int n,
 	       u32 value);
+struct hid_report *hid_get_report(struct hid_report_enum *report_enum, const u8 *data);
 
 #ifdef CONFIG_PM
 int hid_driver_suspend(struct hid_device *hdev, pm_message_t state);
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 21/28] selftests/bpf: add tests for bpf_hid_hw_request
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (19 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 20/28] HID: add implementation of bpf_hid_raw_request Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-04 17:28 ` [PATCH bpf-next v2 22/28] bpf/verifier: prevent non GPL programs to be loaded against HID Benjamin Tissoires
                   ` (8 subsequent siblings)
  29 siblings, 0 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

Add tests for the newly implemented function.
We test here only the GET_REPORT part because the other calls are pure
HID protocol and won't infer the result of the test of the bpf hook.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

changes in v2:
- split the series by bpf/libbpf/hid/selftests and samples
---
 tools/testing/selftests/bpf/prog_tests/hid.c | 71 +++++++++++++++++++-
 tools/testing/selftests/bpf/progs/hid.c      | 57 ++++++++++++++++
 2 files changed, 127 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/hid.c b/tools/testing/selftests/bpf/prog_tests/hid.c
index cc01601c1168..a2bab6a799a1 100644
--- a/tools/testing/selftests/bpf/prog_tests/hid.c
+++ b/tools/testing/selftests/bpf/prog_tests/hid.c
@@ -67,6 +67,8 @@ static unsigned char rdesc[] = {
 	0xc0,			/* END_COLLECTION */
 };
 
+static u8 feature_data[] = { 1, 2 };
+
 static pthread_mutex_t uhid_started_mtx = PTHREAD_MUTEX_INITIALIZER;
 static pthread_cond_t uhid_started = PTHREAD_COND_INITIALIZER;
 
@@ -126,7 +128,7 @@ static void destroy(int fd)
 
 static int event(int fd)
 {
-	struct uhid_event ev;
+	struct uhid_event ev, answer;
 	ssize_t ret;
 
 	memset(&ev, 0, sizeof(ev));
@@ -143,6 +145,8 @@ static int event(int fd)
 		return -EFAULT;
 	}
 
+	memset(&answer, 0, sizeof(answer));
+
 	switch (ev.type) {
 	case UHID_START:
 		pthread_mutex_lock(&uhid_started_mtx);
@@ -167,6 +171,15 @@ static int event(int fd)
 		break;
 	case UHID_GET_REPORT:
 		fprintf(stderr, "UHID_GET_REPORT from uhid-dev\n");
+
+		answer.type = UHID_GET_REPORT_REPLY;
+		answer.u.get_report_reply.id = ev.u.get_report.id;
+		answer.u.get_report_reply.err = ev.u.get_report.rnum == 1 ? 0 : -EIO;
+		answer.u.get_report_reply.size = sizeof(feature_data);
+		memcpy(answer.u.get_report_reply.data, feature_data, sizeof(feature_data));
+
+		uhid_write(fd, &answer);
+
 		break;
 	case UHID_SET_REPORT:
 		fprintf(stderr, "UHID_SET_REPORT from uhid-dev\n");
@@ -516,6 +529,59 @@ static int test_hid_user_call(struct hid *hid_skel, int uhid_fd, int sysfs_fd)
 	return ret;
 }
 
+/*
+ * Attach hid_user_raw_request to the given uhid device,
+ * call the bpf program from userspace
+ * check that the program is called and does the expected.
+ */
+static int test_hid_user_raw_request_call(struct hid *hid_skel, int uhid_fd, int sysfs_fd)
+{
+	int err, prog_fd;
+	u8 buf[10] = {0};
+	int ret = -1;
+
+	LIBBPF_OPTS(bpf_test_run_opts, run_attrs,
+		    .repeat = 1,
+		    .ctx_in = &sysfs_fd,
+		    .ctx_size_in = sizeof(sysfs_fd),
+		    .data_in = buf,
+		    .data_size_in = sizeof(buf),
+		    .data_out = buf,
+		    .data_size_out = sizeof(buf),
+	);
+
+	/* attach hid_user_raw_request program */
+	hid_skel->links.hid_user_raw_request =
+		bpf_program__attach_hid(hid_skel->progs.hid_user_raw_request, sysfs_fd);
+	if (!ASSERT_OK_PTR(hid_skel->links.hid_user_raw_request,
+			   "attach_hid(hid_user_raw_request)"))
+		return PTR_ERR(hid_skel->links.hid_user_raw_request);
+
+	buf[0] = 2; /* HID_FEATURE_REPORT */
+	buf[1] = 1; /* HID_REQ_GET_REPORT */
+	buf[2] = 1; /* report ID */
+
+	prog_fd = bpf_program__fd(hid_skel->progs.hid_user_raw_request);
+
+	err = bpf_prog_test_run_opts(prog_fd, &run_attrs);
+	if (!ASSERT_EQ(err, 0, "bpf_prog_test_run_xattr"))
+		goto cleanup;
+
+	if (!ASSERT_EQ(run_attrs.retval, 2, "bpf_prog_test_run_xattr_retval"))
+		goto cleanup;
+
+	if (!ASSERT_EQ(buf[3], 2, "hid_user_raw_request_check_in"))
+		goto cleanup;
+
+	ret = 0;
+
+cleanup:
+
+	hid__detach(hid_skel);
+
+	return ret;
+}
+
 /*
  * Attach hid_rdesc_fixup to the given uhid device,
  * retrieve and open the matching hidraw node,
@@ -626,6 +692,9 @@ void serial_test_hid_bpf(void)
 	err = test_hid_user_call(hid_skel, uhid_fd, sysfs_fd);
 	ASSERT_OK(err, "hid_user");
 
+	err = test_hid_user_raw_request_call(hid_skel, uhid_fd, sysfs_fd);
+	ASSERT_OK(err, "hid_user_raw_request");
+
 	err = test_rdesc_fixup(hid_skel, uhid_fd, sysfs_fd);
 	ASSERT_OK(err, "hid_rdesc_fixup");
 
diff --git a/tools/testing/selftests/bpf/progs/hid.c b/tools/testing/selftests/bpf/progs/hid.c
index fabfaf0f2526..ea011ff9e752 100644
--- a/tools/testing/selftests/bpf/progs/hid.c
+++ b/tools/testing/selftests/bpf/progs/hid.c
@@ -9,6 +9,11 @@ char _license[] SEC("license") = "GPL";
 __u64 callback_check = 52;
 __u64 callback2_check = 52;
 
+struct {
+	__uint(type, BPF_MAP_TYPE_RINGBUF);
+	__uint(max_entries, 4096 * 64);
+} ringbuf SEC(".maps");
+
 SEC("hid/device_event")
 int hid_first_event(struct hid_bpf_ctx *ctx)
 {
@@ -121,3 +126,55 @@ int hid_user(struct hid_bpf_ctx *ctx)
 
 	return 0;
 }
+
+SEC("hid/user_event")
+int hid_user_raw_request(struct hid_bpf_ctx *ctx)
+{
+	const unsigned int buflen = 256;
+	const unsigned int _buflen = buflen * sizeof(__u8);
+	__u8 *buf;
+	int ret;
+	__u32 size;
+	__u8 rtype, reqtype;
+
+	buf = bpf_ringbuf_reserve(&ringbuf, _buflen, 0);
+	if (!buf)
+		return -12; /* -ENOMEM */
+
+	__builtin_memcpy(buf, ctx->data, _buflen);
+
+	/*
+	 * build up a custom API for our needs:
+	 * offset 0, size 1: report type
+	 * offset 1, size 1: request type
+	 * offset 2+: data
+	 */
+	rtype = buf[0];
+	reqtype = buf[1];
+	size = ctx->size - 2;
+
+	if (size < _buflen - 2) {
+		ret = bpf_hid_raw_request(ctx,
+					  &buf[2],
+					  size,
+					  rtype,
+					  reqtype);
+		if (ret < 0)
+			goto discard;
+	} else {
+		ret = -7; /* -E2BIG */
+		goto discard;
+	}
+
+	__builtin_memcpy(&ctx->data[2], &buf[2], _buflen - 2);
+
+	ctx->size = ret + 2;
+	ctx->u.user.retval = ret;
+
+	ret = 0;
+
+ discard:
+	bpf_ringbuf_discard(buf, 0);
+
+	return ret;
+}
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 22/28] bpf/verifier: prevent non GPL programs to be loaded against HID
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (20 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 21/28] selftests/bpf: add tests for bpf_hid_hw_request Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-04 17:28 ` [PATCH bpf-next v2 23/28] HID: bpf: compute only the required buffer size for the device Benjamin Tissoires
                   ` (7 subsequent siblings)
  29 siblings, 0 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

This is just to hammer the obvious because I suspect you can not already
load a bpf HID program which is not GPL because all of the useful
functions are GPL only.

Anyway, this ensures that users are not tempted to bypass this requirement
and will allow us to ship tested BPF programs in the kernel without having
to aorry about the license.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

new in v2:
 - Note: I placed this statement in check_attach_btf_id() to be local to
   other similar checks (regarding LSM), however, I have no idea if this
   is the correct place. Please shout at me if it isn't.
---
 include/linux/bpf-hid.h |  8 ++++++++
 kernel/bpf/hid.c        | 12 ++++++++++++
 kernel/bpf/verifier.c   |  7 +++++++
 3 files changed, 27 insertions(+)

diff --git a/include/linux/bpf-hid.h b/include/linux/bpf-hid.h
index bd548f6a4a26..3da1d0ecb9be 100644
--- a/include/linux/bpf-hid.h
+++ b/include/linux/bpf-hid.h
@@ -2,6 +2,7 @@
 #ifndef _BPF_HID_H
 #define _BPF_HID_H
 
+#include <linux/bpf_verifier.h>
 #include <linux/mutex.h>
 #include <uapi/linux/bpf.h>
 #include <uapi/linux/bpf_hid.h>
@@ -71,6 +72,8 @@ int bpf_hid_prog_query(const union bpf_attr *attr,
 		       union bpf_attr __user *uattr);
 int bpf_hid_link_create(const union bpf_attr *attr,
 			struct bpf_prog *prog);
+int bpf_hid_verify_prog(struct bpf_verifier_log *vlog,
+			const struct bpf_prog *prog);
 #else
 static inline int bpf_hid_prog_query(const union bpf_attr *attr,
 				     union bpf_attr __user *uattr)
@@ -83,6 +86,11 @@ static inline int bpf_hid_link_create(const union bpf_attr *attr,
 {
 	return -EOPNOTSUPP;
 }
+static inline int bpf_hid_verify_prog(struct bpf_verifier_log *vlog,
+				      const struct bpf_prog *prog)
+{
+	return -EOPNOTSUPP;
+}
 #endif
 
 static inline bool bpf_hid_link_empty(struct bpf_hid *bpf,
diff --git a/kernel/bpf/hid.c b/kernel/bpf/hid.c
index 653d10c0f4e6..b3dc1cd37a3e 100644
--- a/kernel/bpf/hid.c
+++ b/kernel/bpf/hid.c
@@ -37,6 +37,18 @@ void bpf_hid_set_hooks(struct bpf_hid_hooks *hooks)
 }
 EXPORT_SYMBOL_GPL(bpf_hid_set_hooks);
 
+int bpf_hid_verify_prog(struct bpf_verifier_log *vlog,
+			const struct bpf_prog *prog)
+{
+	if (!prog->gpl_compatible) {
+		bpf_log(vlog,
+			"HID programs must have a GPL compatible license\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 BPF_CALL_5(bpf_hid_get_data, void*, ctx, u64, offset, u32, n, void*, data, u64, size)
 {
 	struct hid_bpf_ctx *bpf_ctx = ctx;
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index a57db4b2803c..afec8fa1d674 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -21,6 +21,7 @@
 #include <linux/perf_event.h>
 #include <linux/ctype.h>
 #include <linux/error-injection.h>
+#include <linux/bpf-hid.h>
 #include <linux/bpf_lsm.h>
 #include <linux/btf_ids.h>
 
@@ -14235,6 +14236,12 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
 	if (prog->type == BPF_PROG_TYPE_STRUCT_OPS)
 		return check_struct_ops_btf_id(env);
 
+	if (prog->type == BPF_PROG_TYPE_HID) {
+		ret = bpf_hid_verify_prog(&env->log, prog);
+		if (ret < 0)
+			return ret;
+	}
+
 	if (prog->type != BPF_PROG_TYPE_TRACING &&
 	    prog->type != BPF_PROG_TYPE_LSM &&
 	    prog->type != BPF_PROG_TYPE_EXT)
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 23/28] HID: bpf: compute only the required buffer size for the device
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (21 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 22/28] bpf/verifier: prevent non GPL programs to be loaded against HID Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-04 17:28 ` [PATCH bpf-next v2 24/28] HID: bpf: only call hid_bpf_raw_event() if a ctx is available Benjamin Tissoires
                   ` (6 subsequent siblings)
  29 siblings, 0 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

There is no point in using 16 kB of memory if the device needs less
for all of its reports (uwhich is usually the case).

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

new in v2
---
 drivers/hid/hid-bpf.c | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/drivers/hid/hid-bpf.c b/drivers/hid/hid-bpf.c
index b8c0060f3180..d56fbad990ed 100644
--- a/drivers/hid/hid-bpf.c
+++ b/drivers/hid/hid-bpf.c
@@ -61,11 +61,24 @@ static int hid_reconnect(struct hid_device *hdev)
 static int hid_bpf_link_attach(struct hid_device *hdev, enum bpf_hid_attach_type type)
 {
 	int err = 0;
+	unsigned int i, j, max_report_len = 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));
+		}
+	}
 
 	switch (type) {
 	case BPF_HID_ATTACH_DEVICE_EVENT:
 		if (!hdev->bpf.ctx) {
-			hdev->bpf.ctx = bpf_hid_allocate_ctx(hdev, HID_BPF_MAX_BUFFER_SIZE);
+			hdev->bpf.ctx = bpf_hid_allocate_ctx(hdev, max_report_len);
 			if (IS_ERR(hdev->bpf.ctx)) {
 				err = PTR_ERR(hdev->bpf.ctx);
 				hdev->bpf.ctx = NULL;
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 24/28] HID: bpf: only call hid_bpf_raw_event() if a ctx is available
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (22 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 23/28] HID: bpf: compute only the required buffer size for the device Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-04 17:28 ` [PATCH bpf-next v2 25/28] bpf/hid: Add a flag to add the program at the beginning of the list Benjamin Tissoires
                   ` (5 subsequent siblings)
  29 siblings, 0 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

the context is allocated the first time a program of type DEVICE_EVENT
is attached to the device. To not add too much jumps in the code for
the general device handling, call hid_bpf_raw_event() only if we know
that a program has been attached once during the life of the device.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

new in v2
---
 drivers/hid/hid-core.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index d0e015986e17..2b49f6064a40 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1751,10 +1751,13 @@ int hid_report_raw_event(struct hid_device *hid, int type, u8 *data, u32 size,
 	u8 *cdata;
 	int ret = 0;
 
-	data = hid_bpf_raw_event(hid, data, &size);
-	if (IS_ERR(data)) {
-		ret = PTR_ERR(data);
-		goto out;
+	/* we pre-test if ctx is available here to cut the calls at the earliest */
+	if (hid->bpf.ctx) {
+		data = hid_bpf_raw_event(hid, data, &size);
+		if (IS_ERR(data)) {
+			ret = PTR_ERR(data);
+			goto out;
+		}
 	}
 
 	report = hid_get_report(report_enum, data);
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 25/28] bpf/hid: Add a flag to add the program at the beginning of the list
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (23 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 24/28] HID: bpf: only call hid_bpf_raw_event() if a ctx is available Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-04 17:28 ` [PATCH bpf-next v2 26/28] libbpf: add handling for BPF_F_INSERT_HEAD in HID programs Benjamin Tissoires
                   ` (4 subsequent siblings)
  29 siblings, 0 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

When tracing the incoming events, if a bpf program is already loaded,
the next bpf program will see the potentially changed data.

Add a flag to BPF_LINK_CREATE that allows to chose the position of the
inserted program: at the beginning or at the end.

This way, we can have a tracing program that compare the raw event from
the device and the transformed stream from all the other bpf programs.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

new in v2
---
 include/uapi/linux/bpf.h | 10 ++++++++++
 kernel/bpf/hid.c         | 11 +++++++----
 2 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 417cf1c31579..23ebe5e96d69 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -1123,6 +1123,16 @@ enum bpf_link_type {
  */
 #define BPF_F_XDP_HAS_FRAGS	(1U << 5)
 
+/* HID flag used in BPF_LINK_CREATE command
+ *
+ * NONE(default): The bpf program will be added at the tail of the list
+ * of existing bpf program for this type.
+ *
+ * BPF_F_INSERT_HEAD: The bpf program will be added at the beginning
+ * of the list of existing bpf program for this type..
+ */
+#define BPF_F_INSERT_HEAD	(1U << 0)
+
 /* When BPF ldimm64's insn[0].src_reg != 0 then this can have
  * the following extensions:
  *
diff --git a/kernel/bpf/hid.c b/kernel/bpf/hid.c
index b3dc1cd37a3e..141eb4169079 100644
--- a/kernel/bpf/hid.c
+++ b/kernel/bpf/hid.c
@@ -416,7 +416,7 @@ static int bpf_hid_max_progs(enum bpf_hid_attach_type type)
 }
 
 static int bpf_hid_link_attach(struct hid_device *hdev, struct bpf_link *link,
-			       enum bpf_hid_attach_type type)
+			       enum bpf_hid_attach_type type, u32 flags)
 {
 	struct bpf_hid_link *hid_link =
 		container_of(link, struct bpf_hid_link, link);
@@ -443,7 +443,10 @@ static int bpf_hid_link_attach(struct hid_device *hdev, struct bpf_link *link,
 		goto out_unlock;
 	}
 
-	list_add_tail(&hid_link->node, &hdev->bpf.links[type]);
+	if (flags & BPF_F_INSERT_HEAD)
+		list_add(&hid_link->node, &hdev->bpf.links[type]);
+	else
+		list_add_tail(&hid_link->node, &hdev->bpf.links[type]);
 
 	fill_prog_array(hdev, type, run_array);
 	run_array = rcu_replace_pointer(hdev->bpf.run_array[type], run_array,
@@ -467,7 +470,7 @@ int bpf_hid_link_create(const union bpf_attr *attr, struct bpf_prog *prog)
 	struct hid_device *hdev;
 	int err;
 
-	if (attr->link_create.flags || !hid_hooks.hdev_from_fd)
+	if ((attr->link_create.flags & ~BPF_F_INSERT_HEAD) || !hid_hooks.hdev_from_fd)
 		return -EINVAL;
 
 	type = attr->link_create.attach_type;
@@ -495,7 +498,7 @@ int bpf_hid_link_create(const union bpf_attr *attr, struct bpf_prog *prog)
 		return err;
 	}
 
-	err = bpf_hid_link_attach(hdev, &hid_link->link, hid_type);
+	err = bpf_hid_link_attach(hdev, &hid_link->link, hid_type, attr->link_create.flags);
 	if (err) {
 		bpf_link_cleanup(&link_primer);
 		return err;
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 26/28] libbpf: add handling for BPF_F_INSERT_HEAD in HID programs
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (24 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 25/28] bpf/hid: Add a flag to add the program at the beginning of the list Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-04 17:28 ` [PATCH bpf-next v2 27/28] selftests/bpf: Add a test for BPF_F_INSERT_HEAD Benjamin Tissoires
                   ` (3 subsequent siblings)
  29 siblings, 0 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

Export the newly created flag to libbpf.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

new in v2
---
 tools/include/uapi/linux/bpf.h | 10 ++++++++++
 tools/lib/bpf/libbpf.c         | 17 +++++++++--------
 tools/lib/bpf/libbpf.h         |  2 +-
 3 files changed, 20 insertions(+), 9 deletions(-)

diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 417cf1c31579..23ebe5e96d69 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -1123,6 +1123,16 @@ enum bpf_link_type {
  */
 #define BPF_F_XDP_HAS_FRAGS	(1U << 5)
 
+/* HID flag used in BPF_LINK_CREATE command
+ *
+ * NONE(default): The bpf program will be added at the tail of the list
+ * of existing bpf program for this type.
+ *
+ * BPF_F_INSERT_HEAD: The bpf program will be added at the beginning
+ * of the list of existing bpf program for this type..
+ */
+#define BPF_F_INSERT_HEAD	(1U << 0)
+
 /* When BPF ldimm64's insn[0].src_reg != 0 then this can have
  * the following extensions:
  *
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index d1e305f760bb..6f0cb6717207 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -10533,10 +10533,11 @@ static struct bpf_link *attach_lsm(const struct bpf_program *prog, long cookie)
 
 static struct bpf_link *
 bpf_program__attach_fd(const struct bpf_program *prog, int target_fd, int btf_id,
-		       const char *target_name)
+		       const char *target_name, __u32 flags)
 {
 	DECLARE_LIBBPF_OPTS(bpf_link_create_opts, opts,
-			    .target_btf_id = btf_id);
+			    .target_btf_id = btf_id,
+			    .flags = flags);
 	enum bpf_attach_type attach_type;
 	char errmsg[STRERR_BUFSIZE];
 	struct bpf_link *link;
@@ -10570,19 +10571,19 @@ bpf_program__attach_fd(const struct bpf_program *prog, int target_fd, int btf_id
 struct bpf_link *
 bpf_program__attach_cgroup(const struct bpf_program *prog, int cgroup_fd)
 {
-	return bpf_program__attach_fd(prog, cgroup_fd, 0, "cgroup");
+	return bpf_program__attach_fd(prog, cgroup_fd, 0, "cgroup", 0);
 }
 
 struct bpf_link *
 bpf_program__attach_netns(const struct bpf_program *prog, int netns_fd)
 {
-	return bpf_program__attach_fd(prog, netns_fd, 0, "netns");
+	return bpf_program__attach_fd(prog, netns_fd, 0, "netns", 0);
 }
 
 struct bpf_link *bpf_program__attach_xdp(const struct bpf_program *prog, int ifindex)
 {
 	/* target_fd/target_ifindex use the same field in LINK_CREATE */
-	return bpf_program__attach_fd(prog, ifindex, 0, "xdp");
+	return bpf_program__attach_fd(prog, ifindex, 0, "xdp", 0);
 }
 
 struct bpf_link *bpf_program__attach_freplace(const struct bpf_program *prog,
@@ -10608,7 +10609,7 @@ struct bpf_link *bpf_program__attach_freplace(const struct bpf_program *prog,
 		if (btf_id < 0)
 			return libbpf_err_ptr(btf_id);
 
-		return bpf_program__attach_fd(prog, target_fd, btf_id, "freplace");
+		return bpf_program__attach_fd(prog, target_fd, btf_id, "freplace", 0);
 	} else {
 		/* no target, so use raw_tracepoint_open for compatibility
 		 * with old kernels
@@ -10663,9 +10664,9 @@ static struct bpf_link *attach_iter(const struct bpf_program *prog, long cookie)
 }
 
 struct bpf_link *
-bpf_program__attach_hid(const struct bpf_program *prog, int hid_fd)
+bpf_program__attach_hid(const struct bpf_program *prog, int hid_fd, __u32 flags)
 {
-	return bpf_program__attach_fd(prog, hid_fd, 0, "hid");
+	return bpf_program__attach_fd(prog, hid_fd, 0, "hid", flags);
 }
 
 struct bpf_link *bpf_program__attach(const struct bpf_program *prog)
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index f677ac0a9ede..65be3e2ec62d 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -530,7 +530,7 @@ LIBBPF_API struct bpf_link *
 bpf_program__attach_iter(const struct bpf_program *prog,
 			 const struct bpf_iter_attach_opts *opts);
 LIBBPF_API struct bpf_link *
-bpf_program__attach_hid(const struct bpf_program *prog, int hid_fd);
+bpf_program__attach_hid(const struct bpf_program *prog, int hid_fd, __u32 flags);
 
 /*
  * Libbpf allows callers to adjust BPF programs before being loaded
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 27/28] selftests/bpf: Add a test for BPF_F_INSERT_HEAD
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (25 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 26/28] libbpf: add handling for BPF_F_INSERT_HEAD in HID programs Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-04 17:28 ` [PATCH bpf-next v2 28/28] samples/bpf: fix bpf_program__attach_hid() api change Benjamin Tissoires
                   ` (2 subsequent siblings)
  29 siblings, 0 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

Insert 3 programs to check that we are doing the correct thing:
'2', '1', '3' are inserted, but '1' is supposed to be executed first.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

new in v2
---
 tools/testing/selftests/bpf/prog_tests/hid.c | 90 ++++++++++++++++++--
 tools/testing/selftests/bpf/progs/hid.c      | 36 ++++++++
 2 files changed, 121 insertions(+), 5 deletions(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/hid.c b/tools/testing/selftests/bpf/prog_tests/hid.c
index a2bab6a799a1..f7fec748a0b9 100644
--- a/tools/testing/selftests/bpf/prog_tests/hid.c
+++ b/tools/testing/selftests/bpf/prog_tests/hid.c
@@ -353,7 +353,7 @@ static int test_hid_raw_event(struct hid *hid_skel, int uhid_fd, int sysfs_fd)
 
 	/* attach the first program */
 	hid_skel->links.hid_first_event =
-		bpf_program__attach_hid(hid_skel->progs.hid_first_event, sysfs_fd);
+		bpf_program__attach_hid(hid_skel->progs.hid_first_event, sysfs_fd, 0);
 	if (!ASSERT_OK_PTR(hid_skel->links.hid_first_event,
 			   "attach_hid(hid_first_event)"))
 		return PTR_ERR(hid_skel->links.hid_first_event);
@@ -429,7 +429,7 @@ static int test_hid_set_get_data(struct hid *hid_skel, int uhid_fd, int sysfs_fd
 
 	/* attach hid_set_get_data program */
 	hid_skel->links.hid_set_get_data =
-		bpf_program__attach_hid(hid_skel->progs.hid_set_get_data, sysfs_fd);
+		bpf_program__attach_hid(hid_skel->progs.hid_set_get_data, sysfs_fd, 0);
 	if (!ASSERT_OK_PTR(hid_skel->links.hid_set_get_data,
 			   "attach_hid(hid_set_get_data)"))
 		return PTR_ERR(hid_skel->links.hid_set_get_data);
@@ -498,7 +498,7 @@ static int test_hid_user_call(struct hid *hid_skel, int uhid_fd, int sysfs_fd)
 	);
 
 	/* attach hid_user program */
-	hid_skel->links.hid_user = bpf_program__attach_hid(hid_skel->progs.hid_user, sysfs_fd);
+	hid_skel->links.hid_user = bpf_program__attach_hid(hid_skel->progs.hid_user, sysfs_fd, 0);
 	if (!ASSERT_OK_PTR(hid_skel->links.hid_user,
 			   "attach_hid(hid_user)"))
 		return PTR_ERR(hid_skel->links.hid_user);
@@ -552,7 +552,7 @@ static int test_hid_user_raw_request_call(struct hid *hid_skel, int uhid_fd, int
 
 	/* attach hid_user_raw_request program */
 	hid_skel->links.hid_user_raw_request =
-		bpf_program__attach_hid(hid_skel->progs.hid_user_raw_request, sysfs_fd);
+		bpf_program__attach_hid(hid_skel->progs.hid_user_raw_request, sysfs_fd, 0);
 	if (!ASSERT_OK_PTR(hid_skel->links.hid_user_raw_request,
 			   "attach_hid(hid_user_raw_request)"))
 		return PTR_ERR(hid_skel->links.hid_user_raw_request);
@@ -582,6 +582,83 @@ static int test_hid_user_raw_request_call(struct hid *hid_skel, int uhid_fd, int
 	return ret;
 }
 
+/*
+ * Attach hid_insert{0,1,2} to the given uhid device,
+ * retrieve and open the matching hidraw node,
+ * inject one event in the uhid device,
+ * check that the programs have been inserted in the correct order.
+ */
+static int test_hid_attach_flags(struct hid *hid_skel, int uhid_fd, int sysfs_fd)
+{
+	int err, hidraw_ino, hidraw_fd = -1;
+	char hidraw_path[64] = {0};
+	u8 buf[10] = {0};
+	int ret = -1;
+
+	/* attach hid_test_insert2 program */
+	hid_skel->links.hid_test_insert2 =
+		bpf_program__attach_hid(hid_skel->progs.hid_test_insert2, sysfs_fd, 0);
+	if (!ASSERT_OK_PTR(hid_skel->links.hid_test_insert2,
+			   "attach_hid(hid_test_insert2)"))
+		return PTR_ERR(hid_skel->links.hid_test_insert2);
+
+	/* then attach hid_test_insert1 program before the previous*/
+	hid_skel->links.hid_test_insert1 =
+		bpf_program__attach_hid(hid_skel->progs.hid_test_insert1,
+					sysfs_fd,
+					BPF_F_INSERT_HEAD);
+	if (!ASSERT_OK_PTR(hid_skel->links.hid_test_insert1,
+			   "attach_hid(hid_test_insert1)"))
+		return PTR_ERR(hid_skel->links.hid_test_insert1);
+
+	/* finally attach hid_test_insert3 at the end */
+	hid_skel->links.hid_test_insert3 =
+		bpf_program__attach_hid(hid_skel->progs.hid_test_insert3, sysfs_fd, 0);
+	if (!ASSERT_OK_PTR(hid_skel->links.hid_test_insert3,
+			   "attach_hid(hid_test_insert3)"))
+		return PTR_ERR(hid_skel->links.hid_test_insert3);
+
+	hidraw_ino = get_hidraw(hid_skel->links.hid_test_insert1);
+	if (!ASSERT_GE(hidraw_ino, 0, "get_hidraw"))
+		goto cleanup;
+
+	/* open hidraw node to check the other side of the pipe */
+	sprintf(hidraw_path, "/dev/hidraw%d", hidraw_ino);
+	hidraw_fd = open(hidraw_path, O_RDWR | O_NONBLOCK);
+
+	if (!ASSERT_GE(hidraw_fd, 0, "open_hidraw"))
+		goto cleanup;
+
+	/* inject one event */
+	buf[0] = 1;
+	send_event(uhid_fd, buf, 6);
+
+	/* read the data from hidraw */
+	memset(buf, 0, sizeof(buf));
+	err = read(hidraw_fd, buf, sizeof(buf));
+	if (!ASSERT_EQ(err, 6, "read_hidraw"))
+		goto cleanup;
+
+	if (!ASSERT_EQ(buf[1], 1, "hid_test_insert1"))
+		goto cleanup;
+
+	if (!ASSERT_EQ(buf[2], 2, "hid_test_insert2"))
+		goto cleanup;
+
+	if (!ASSERT_EQ(buf[3], 3, "hid_test_insert3"))
+		goto cleanup;
+
+	ret = 0;
+
+cleanup:
+	if (hidraw_fd >= 0)
+		close(hidraw_fd);
+
+	hid__detach(hid_skel);
+
+	return ret;
+}
+
 /*
  * Attach hid_rdesc_fixup to the given uhid device,
  * retrieve and open the matching hidraw node,
@@ -598,7 +675,7 @@ static int test_rdesc_fixup(struct hid *hid_skel, int uhid_fd, int sysfs_fd)
 
 	/* attach the program */
 	hid_skel->links.hid_rdesc_fixup =
-		bpf_program__attach_hid(hid_skel->progs.hid_rdesc_fixup, sysfs_fd);
+		bpf_program__attach_hid(hid_skel->progs.hid_rdesc_fixup, sysfs_fd, 0);
 	if (!ASSERT_OK_PTR(hid_skel->links.hid_rdesc_fixup,
 			   "attach_hid(hid_rdesc_fixup)"))
 		return PTR_ERR(hid_skel->links.hid_rdesc_fixup);
@@ -695,6 +772,9 @@ void serial_test_hid_bpf(void)
 	err = test_hid_user_raw_request_call(hid_skel, uhid_fd, sysfs_fd);
 	ASSERT_OK(err, "hid_user_raw_request");
 
+	err = test_hid_attach_flags(hid_skel, uhid_fd, sysfs_fd);
+	ASSERT_OK(err, "hid_user_raw_request");
+
 	err = test_rdesc_fixup(hid_skel, uhid_fd, sysfs_fd);
 	ASSERT_OK(err, "hid_rdesc_fixup");
 
diff --git a/tools/testing/selftests/bpf/progs/hid.c b/tools/testing/selftests/bpf/progs/hid.c
index ea011ff9e752..40161aa8cb6e 100644
--- a/tools/testing/selftests/bpf/progs/hid.c
+++ b/tools/testing/selftests/bpf/progs/hid.c
@@ -178,3 +178,39 @@ int hid_user_raw_request(struct hid_bpf_ctx *ctx)
 
 	return ret;
 }
+
+SEC("hid/device_event")
+int hid_test_insert1(struct hid_bpf_ctx *ctx)
+{
+	/* we need to be run first */
+	if (ctx->data[2] || ctx->data[3])
+		return -1;
+
+	ctx->data[1] = 1;
+
+	return 0;
+}
+
+SEC("hid/device_event")
+int hid_test_insert2(struct hid_bpf_ctx *ctx)
+{
+	/* after insert0 and before insert2 */
+	if (!ctx->data[1] || ctx->data[3])
+		return -1;
+
+	ctx->data[2] = 2;
+
+	return 0;
+}
+
+SEC("hid/device_event")
+int hid_test_insert3(struct hid_bpf_ctx *ctx)
+{
+	/* at the end */
+	if (!ctx->data[1] || !ctx->data[2])
+		return -1;
+
+	ctx->data[3] = 3;
+
+	return 0;
+}
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* [PATCH bpf-next v2 28/28] samples/bpf: fix bpf_program__attach_hid() api change
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (26 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 27/28] selftests/bpf: Add a test for BPF_F_INSERT_HEAD Benjamin Tissoires
@ 2022-03-04 17:28 ` Benjamin Tissoires
  2022-03-05  1:13 ` [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Song Liu
  2022-03-15 17:04 ` Tero Kristo
  29 siblings, 0 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-04 17:28 UTC (permalink / raw)
  To: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer
  Cc: Tero Kristo, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest, Benjamin Tissoires

Add the new flag parameter.

Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

---

new in v2
---
 samples/bpf/hid_mouse_user.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/samples/bpf/hid_mouse_user.c b/samples/bpf/hid_mouse_user.c
index d4f37caca2fa..bfae25e7b659 100644
--- a/samples/bpf/hid_mouse_user.c
+++ b/samples/bpf/hid_mouse_user.c
@@ -98,7 +98,7 @@ int main(int argc, char **argv)
 	bpf_object__for_each_program(prog, obj) {
 		progs[prog_count].fd = bpf_program__fd(prog);
 		progs[prog_count].type = bpf_program__get_expected_attach_type(prog);
-		progs[prog_count].link = bpf_program__attach_hid(prog, sysfs_fd);
+		progs[prog_count].link = bpf_program__attach_hid(prog, sysfs_fd, 0);
 		if (libbpf_get_error(progs[prog_count].link)) {
 			fprintf(stderr, "bpf_prog_attach: err=%m\n");
 			progs[prog_count].fd = 0;
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 02/28] bpf: introduce hid program type
  2022-03-04 17:28 ` [PATCH bpf-next v2 02/28] bpf: introduce hid program type Benjamin Tissoires
@ 2022-03-04 18:21   ` Greg KH
  2022-03-07 17:57     ` Benjamin Tissoires
  2022-03-05  0:02   ` Song Liu
  2022-03-05  0:20   ` Song Liu
  2 siblings, 1 reply; 73+ messages in thread
From: Greg KH @ 2022-03-04 18:21 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, linux-kernel, linux-input, netdev,
	bpf, linux-kselftest

On Fri, Mar 04, 2022 at 06:28:26PM +0100, Benjamin Tissoires wrote:
> HID is a protocol that could benefit from using BPF too.
> 
> This patch implements a net-like use of BPF capability for HID.
> Any incoming report coming from the device can be injected into a series
> of BPF programs that can modify it or even discard it by setting the
> size in the context to 0.
> 
> The kernel/bpf implementation is based on net-namespace.c, with only
> the bpf_link part kept, there is no real points in keeping the
> bpf_prog_{attach|detach} API.
> 
> The implementation here is only focusing on the bpf changes. The HID
> changes that hooks onto this are coming in a separate patch.
> 
> Given that HID can be compiled in as a module, and the functions that
> kernel/bpf/hid.c needs to call in hid.ko are exported in struct hid_hooks.
> 
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> 
> ---
> 
> changes in v2:
> - split the series by bpf/libbpf/hid/selftests and samples
> - unsigned long -> __u16 in uapi/linux/bpf_hid.h
> - change the bpf_ctx to be of variable size, with a min of 1024 bytes
> - make this 1 kB available directly from bpf program, the rest will
>   need a helper
> - add some more doc comments in uapi
> ---
>  include/linux/bpf-hid.h        | 108 ++++++++
>  include/linux/bpf_types.h      |   4 +
>  include/linux/hid.h            |   5 +
>  include/uapi/linux/bpf.h       |   7 +
>  include/uapi/linux/bpf_hid.h   |  39 +++
>  kernel/bpf/Makefile            |   3 +
>  kernel/bpf/hid.c               | 437 +++++++++++++++++++++++++++++++++
>  kernel/bpf/syscall.c           |   8 +
>  tools/include/uapi/linux/bpf.h |   7 +
>  9 files changed, 618 insertions(+)
>  create mode 100644 include/linux/bpf-hid.h
>  create mode 100644 include/uapi/linux/bpf_hid.h
>  create mode 100644 kernel/bpf/hid.c
> 
> diff --git a/include/linux/bpf-hid.h b/include/linux/bpf-hid.h
> new file mode 100644
> index 000000000000..3cda78051b5f
> --- /dev/null
> +++ b/include/linux/bpf-hid.h
> @@ -0,0 +1,108 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _BPF_HID_H
> +#define _BPF_HID_H
> +
> +#include <linux/mutex.h>
> +#include <uapi/linux/bpf.h>
> +#include <uapi/linux/bpf_hid.h>
> +#include <linux/list.h>
> +#include <linux/slab.h>
> +
> +struct bpf_prog;
> +struct bpf_prog_array;
> +struct hid_device;
> +
> +enum bpf_hid_attach_type {
> +	BPF_HID_ATTACH_INVALID = -1,
> +	BPF_HID_ATTACH_DEVICE_EVENT = 0,
> +	MAX_BPF_HID_ATTACH_TYPE
> +};
> +
> +struct bpf_hid {
> +	struct hid_bpf_ctx *ctx;
> +
> +	/* Array of programs to run compiled from links */
> +	struct bpf_prog_array __rcu *run_array[MAX_BPF_HID_ATTACH_TYPE];
> +	struct list_head links[MAX_BPF_HID_ATTACH_TYPE];
> +};
> +
> +static inline enum bpf_hid_attach_type
> +to_bpf_hid_attach_type(enum bpf_attach_type attach_type)
> +{
> +	switch (attach_type) {
> +	case BPF_HID_DEVICE_EVENT:
> +		return BPF_HID_ATTACH_DEVICE_EVENT;
> +	default:
> +		return BPF_HID_ATTACH_INVALID;
> +	}
> +}
> +
> +static inline struct hid_bpf_ctx *bpf_hid_allocate_ctx(struct hid_device *hdev,
> +						       size_t data_size)
> +{
> +	struct hid_bpf_ctx *ctx;
> +
> +	/* ensure data_size is between min and max */
> +	data_size = clamp_val(data_size,
> +			      HID_BPF_MIN_BUFFER_SIZE,
> +			      HID_BPF_MAX_BUFFER_SIZE);

Do you want to return an error if the data size is not within the range?
Otherwise people will just start to use crazy values and you will always
be limiting them?

> +
> +	ctx = kzalloc(sizeof(*ctx) + data_size, GFP_KERNEL);
> +	if (!ctx)
> +		return ERR_PTR(-ENOMEM);
> +
> +	ctx->hdev = hdev;
> +	ctx->allocated_size = data_size;
> +
> +	return ctx;
> +}

And why is this an inline function?  Why not put it in a .c file?

> +
> +union bpf_attr;
> +struct bpf_prog;
> +
> +#if IS_ENABLED(CONFIG_HID)
> +int bpf_hid_prog_query(const union bpf_attr *attr,
> +		       union bpf_attr __user *uattr);
> +int bpf_hid_link_create(const union bpf_attr *attr,
> +			struct bpf_prog *prog);
> +#else
> +static inline int bpf_hid_prog_query(const union bpf_attr *attr,
> +				     union bpf_attr __user *uattr)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
> +static inline int bpf_hid_link_create(const union bpf_attr *attr,
> +				      struct bpf_prog *prog)
> +{
> +	return -EOPNOTSUPP;
> +}
> +#endif
> +
> +static inline bool bpf_hid_link_empty(struct bpf_hid *bpf,
> +				      enum bpf_hid_attach_type type)
> +{
> +	return list_empty(&bpf->links[type]);
> +}
> +
> +struct bpf_hid_hooks {
> +	struct hid_device *(*hdev_from_fd)(int fd);
> +	int (*link_attach)(struct hid_device *hdev, enum bpf_hid_attach_type type);
> +	void (*array_detached)(struct hid_device *hdev, enum bpf_hid_attach_type type);
> +};
> +
> +#ifdef CONFIG_BPF
> +int bpf_hid_init(struct hid_device *hdev);
> +void bpf_hid_exit(struct hid_device *hdev);
> +void bpf_hid_set_hooks(struct bpf_hid_hooks *hooks);
> +#else
> +static inline int bpf_hid_init(struct hid_device *hdev)
> +{
> +	return 0;
> +}
> +
> +static inline void bpf_hid_exit(struct hid_device *hdev) {}
> +static inline void bpf_hid_set_hooks(struct bpf_hid_hooks *hooks) {}
> +#endif
> +
> +#endif /* _BPF_HID_H */
> diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
> index 48a91c51c015..1509862aacc4 100644
> --- a/include/linux/bpf_types.h
> +++ b/include/linux/bpf_types.h
> @@ -76,6 +76,10 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_EXT, bpf_extension,
>  BPF_PROG_TYPE(BPF_PROG_TYPE_LSM, lsm,
>  	       void *, void *)
>  #endif /* CONFIG_BPF_LSM */
> +#if IS_ENABLED(CONFIG_HID)
> +BPF_PROG_TYPE(BPF_PROG_TYPE_HID, hid,
> +	      __u32, u32)

Why the mix of __u32 and u32 here?

> +#endif
>  #endif
>  BPF_PROG_TYPE(BPF_PROG_TYPE_SYSCALL, bpf_syscall,
>  	      void *, void *)
> diff --git a/include/linux/hid.h b/include/linux/hid.h
> index 7487b0586fe6..56f6f4ad45a7 100644
> --- a/include/linux/hid.h
> +++ b/include/linux/hid.h
> @@ -15,6 +15,7 @@
>  
>  
>  #include <linux/bitops.h>
> +#include <linux/bpf-hid.h>
>  #include <linux/types.h>
>  #include <linux/slab.h>
>  #include <linux/list.h>
> @@ -639,6 +640,10 @@ struct hid_device {							/* device report descriptor */
>  	struct list_head debug_list;
>  	spinlock_t  debug_list_lock;
>  	wait_queue_head_t debug_wait;
> +
> +#ifdef CONFIG_BPF
> +	struct bpf_hid bpf;
> +#endif
>  };
>  
>  #define to_hid_device(pdev) \
> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> index afe3d0d7f5f2..5978b92cacd3 100644
> --- a/include/uapi/linux/bpf.h
> +++ b/include/uapi/linux/bpf.h
> @@ -952,6 +952,7 @@ enum bpf_prog_type {
>  	BPF_PROG_TYPE_LSM,
>  	BPF_PROG_TYPE_SK_LOOKUP,
>  	BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */
> +	BPF_PROG_TYPE_HID,
>  };
>  
>  enum bpf_attach_type {
> @@ -997,6 +998,7 @@ enum bpf_attach_type {
>  	BPF_SK_REUSEPORT_SELECT,
>  	BPF_SK_REUSEPORT_SELECT_OR_MIGRATE,
>  	BPF_PERF_EVENT,
> +	BPF_HID_DEVICE_EVENT,
>  	__MAX_BPF_ATTACH_TYPE
>  };
>  
> @@ -1011,6 +1013,7 @@ enum bpf_link_type {
>  	BPF_LINK_TYPE_NETNS = 5,
>  	BPF_LINK_TYPE_XDP = 6,
>  	BPF_LINK_TYPE_PERF_EVENT = 7,
> +	BPF_LINK_TYPE_HID = 8,
>  
>  	MAX_BPF_LINK_TYPE,
>  };
> @@ -5870,6 +5873,10 @@ struct bpf_link_info {
>  		struct {
>  			__u32 ifindex;
>  		} xdp;
> +		struct  {
> +			__s32 hidraw_ino;

"ino"?  We have lots of letters to spell words out :)

> +			__u32 attach_type;
> +		} hid;
>  	};
>  } __attribute__((aligned(8)));
>  
> diff --git a/include/uapi/linux/bpf_hid.h b/include/uapi/linux/bpf_hid.h
> new file mode 100644
> index 000000000000..975ca5bd526f
> --- /dev/null
> +++ b/include/uapi/linux/bpf_hid.h
> @@ -0,0 +1,39 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later WITH Linux-syscall-note */
> +
> +/*
> + *  HID BPF public headers
> + *
> + *  Copyright (c) 2021 Benjamin Tissoires

Did you forget the copyright line on the other .h file above?

> + */
> +
> +#ifndef _UAPI__LINUX_BPF_HID_H__
> +#define _UAPI__LINUX_BPF_HID_H__
> +
> +#include <linux/types.h>
> +
> +/*
> + * The first 1024 bytes are available directly in the bpf programs.
> + * To access the rest of the data (if allocated_size is bigger
> + * than 1024, you need to use bpf_hid_ helpers.
> + */
> +#define HID_BPF_MIN_BUFFER_SIZE		1024
> +#define HID_BPF_MAX_BUFFER_SIZE		16384		/* in sync with HID_MAX_BUFFER_SIZE */

Can't you just use HID_MAX_BUFFER_SIZE?

Anyway, all minor stuff, looks good!

Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>


^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 03/28] HID: hook up with bpf
  2022-03-04 17:28 ` [PATCH bpf-next v2 03/28] HID: hook up with bpf Benjamin Tissoires
@ 2022-03-04 18:24   ` Greg KH
  2022-03-05  0:23   ` Song Liu
  2022-03-15 16:29   ` Tero Kristo
  2 siblings, 0 replies; 73+ messages in thread
From: Greg KH @ 2022-03-04 18:24 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, linux-kernel, linux-input, netdev,
	bpf, linux-kselftest

On Fri, Mar 04, 2022 at 06:28:27PM +0100, Benjamin Tissoires wrote:
> Now that BPF can be compatible with HID, add the capability into HID.
> drivers/hid/hid-bpf.c takes care of the glue between bpf and HID, and
> hid-core can then inject any incoming event from the device into a BPF
> program to filter/analyze it.
> 
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> 

Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 06/28] samples/bpf: add new hid_mouse example
  2022-03-04 17:28 ` [PATCH bpf-next v2 06/28] samples/bpf: add new hid_mouse example Benjamin Tissoires
@ 2022-03-04 18:26   ` Greg KH
  0 siblings, 0 replies; 73+ messages in thread
From: Greg KH @ 2022-03-04 18:26 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, linux-kernel, linux-input, netdev,
	bpf, linux-kselftest

On Fri, Mar 04, 2022 at 06:28:30PM +0100, Benjamin Tissoires wrote:
> Everything should be available in the selftest part of the tree, but
> providing an example without uhid and hidraw will be more easy to
> follow for users.
> 
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> 
> ---
> 
> changes in v2:
> - split the series by bpf/libbpf/hid/selftests and samples
> ---
>  samples/bpf/.gitignore       |   1 +
>  samples/bpf/Makefile         |   4 ++
>  samples/bpf/hid_mouse_kern.c |  66 ++++++++++++++++++
>  samples/bpf/hid_mouse_user.c | 129 +++++++++++++++++++++++++++++++++++
>  4 files changed, 200 insertions(+)
>  create mode 100644 samples/bpf/hid_mouse_kern.c
>  create mode 100644 samples/bpf/hid_mouse_user.c
> 
> diff --git a/samples/bpf/.gitignore b/samples/bpf/.gitignore
> index 0e7bfdbff80a..65440bd618b2 100644
> --- a/samples/bpf/.gitignore
> +++ b/samples/bpf/.gitignore
> @@ -2,6 +2,7 @@
>  cpustat
>  fds_example
>  hbm
> +hid_mouse
>  ibumad
>  lathist
>  lwt_len_hist
> diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
> index 38638845db9d..84ef458487df 100644
> --- a/samples/bpf/Makefile
> +++ b/samples/bpf/Makefile
> @@ -60,6 +60,8 @@ tprogs-y += xdp_redirect_map
>  tprogs-y += xdp_redirect
>  tprogs-y += xdp_monitor
>  
> +tprogs-y += hid_mouse
> +
>  # Libbpf dependencies
>  LIBBPF_SRC = $(TOOLS_PATH)/lib/bpf
>  LIBBPF_OUTPUT = $(abspath $(BPF_SAMPLES_PATH))/libbpf
> @@ -124,6 +126,7 @@ xdp_redirect_cpu-objs := xdp_redirect_cpu_user.o $(XDP_SAMPLE)
>  xdp_redirect_map-objs := xdp_redirect_map_user.o $(XDP_SAMPLE)
>  xdp_redirect-objs := xdp_redirect_user.o $(XDP_SAMPLE)
>  xdp_monitor-objs := xdp_monitor_user.o $(XDP_SAMPLE)
> +hid_mouse-objs := hid_mouse_user.o
>  
>  # Tell kbuild to always build the programs
>  always-y := $(tprogs-y)
> @@ -181,6 +184,7 @@ always-y += ibumad_kern.o
>  always-y += hbm_out_kern.o
>  always-y += hbm_edt_kern.o
>  always-y += xdpsock_kern.o
> +always-y += hid_mouse_kern.o
>  
>  ifeq ($(ARCH), arm)
>  # Strip all except -D__LINUX_ARM_ARCH__ option needed to handle linux
> diff --git a/samples/bpf/hid_mouse_kern.c b/samples/bpf/hid_mouse_kern.c
> new file mode 100644
> index 000000000000..c24a12e06b40
> --- /dev/null
> +++ b/samples/bpf/hid_mouse_kern.c
> @@ -0,0 +1,66 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright (c) 2021 Benjamin Tissoires

It's 2022 now :(

Other than that, looks nice and simple, good work!

Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 07/28] bpf/hid: add a new attach type to change the report descriptor
  2022-03-04 17:28 ` [PATCH bpf-next v2 07/28] bpf/hid: add a new attach type to change the report descriptor Benjamin Tissoires
@ 2022-03-04 18:32   ` Greg KH
  0 siblings, 0 replies; 73+ messages in thread
From: Greg KH @ 2022-03-04 18:32 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, linux-kernel, linux-input, netdev,
	bpf, linux-kselftest

On Fri, Mar 04, 2022 at 06:28:31PM +0100, Benjamin Tissoires wrote:
> The report descriptor is the dictionary of the HID protocol specific
> to the given device.
> Changing it is a common habit in the HID world, and making that feature
> accessible from eBPF allows to fix devices without having to install a
> new kernel.
> 
> However, the report descriptor is supposed to be static on a device.
> To be able to change it, we need to reconnect the device at the HID
> level.
> So whenever the report descriptor program type is attached or detached,
> we call on a hook on HID to notify it that there is something to be
> done.
> 
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 08/28] HID: allow to change the report descriptor from an eBPF program
  2022-03-04 17:28 ` [PATCH bpf-next v2 08/28] HID: allow to change the report descriptor from an eBPF program Benjamin Tissoires
@ 2022-03-04 18:32   ` Greg KH
  0 siblings, 0 replies; 73+ messages in thread
From: Greg KH @ 2022-03-04 18:32 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, linux-kernel, linux-input, netdev,
	bpf, linux-kselftest

On Fri, Mar 04, 2022 at 06:28:32PM +0100, Benjamin Tissoires wrote:
> Make use of BPF_HID_ATTACH_RDESC_FIXUP so we can trigger an rdesc 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.
> 
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> 


Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 10/28] selftests/bpf: add report descriptor fixup tests
  2022-03-04 17:28 ` [PATCH bpf-next v2 10/28] selftests/bpf: add report descriptor fixup tests Benjamin Tissoires
@ 2022-03-04 18:34   ` Greg KH
  0 siblings, 0 replies; 73+ messages in thread
From: Greg KH @ 2022-03-04 18:34 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, linux-kernel, linux-input, netdev,
	bpf, linux-kselftest

On Fri, Mar 04, 2022 at 06:28:34PM +0100, Benjamin Tissoires wrote:
> Simple report descriptor override in HID: replace part of the report
> descriptor from a static definition in the bpf kernel program.
> 
> Note that this test should be run last because we disconnect/reconnect
> the device, meaning that it changes the overall uhid device.

You might want to add that in a comment right before:

> @@ -329,6 +395,9 @@ void serial_test_hid_bpf(void)
>  	err = test_hid_raw_event(hid_skel, uhid_fd, sysfs_fd);
>  	ASSERT_OK(err, "hid");
>  
> +	err = test_rdesc_fixup(hid_skel, uhid_fd, sysfs_fd);
> +	ASSERT_OK(err, "hid_rdesc_fixup");

There, so that you don't add another test after this later on in 5
years.

Anyway, just a nit:

Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 11/28] samples/bpf: add a report descriptor fixup
  2022-03-04 17:28 ` [PATCH bpf-next v2 11/28] samples/bpf: add a report descriptor fixup Benjamin Tissoires
@ 2022-03-04 18:36   ` Greg KH
  0 siblings, 0 replies; 73+ messages in thread
From: Greg KH @ 2022-03-04 18:36 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, linux-kernel, linux-input, netdev,
	bpf, linux-kselftest

On Fri, Mar 04, 2022 at 06:28:35PM +0100, Benjamin Tissoires wrote:
> the program inverts the definition of X and Y at a given place in the
> report descriptor of my mouse.
> 
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> 
> ---
> 
> changes in v2:
> - split the series by bpf/libbpf/hid/selftests and samples
> ---
>  samples/bpf/hid_mouse_kern.c | 25 +++++++++++++++++++++++++
>  1 file changed, 25 insertions(+)
> 
> diff --git a/samples/bpf/hid_mouse_kern.c b/samples/bpf/hid_mouse_kern.c
> index c24a12e06b40..958820caaf5d 100644
> --- a/samples/bpf/hid_mouse_kern.c
> +++ b/samples/bpf/hid_mouse_kern.c
> @@ -62,5 +62,30 @@ int hid_x_event(struct hid_bpf_ctx *ctx)
>  	return 0;
>  }
>  
> +SEC("hid/rdesc_fixup")
> +int hid_rdesc_fixup(struct hid_bpf_ctx *ctx)

No comment here to show the same as you put in the changelog saying what
this function is doing?

Otherwise it's hard for a non-HID developer to know that:

> +
> +	ctx->data[39] = 0x31;
> +	ctx->data[41] = 0x30;

Is flipping things.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 12/28] bpf/hid: add hid_{get|set}_data helpers
  2022-03-04 17:28 ` [PATCH bpf-next v2 12/28] bpf/hid: add hid_{get|set}_data helpers Benjamin Tissoires
@ 2022-03-04 18:37   ` Greg KH
  2022-03-04 18:41   ` Greg KH
  1 sibling, 0 replies; 73+ messages in thread
From: Greg KH @ 2022-03-04 18:37 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, linux-kernel, linux-input, netdev,
	bpf, linux-kselftest

On Fri, Mar 04, 2022 at 06:28:36PM +0100, Benjamin Tissoires wrote:
> When we process an incoming HID report, it is common to have to account
> for fields that are not aligned in the report. HID is using 2 helpers
> hid_field_extract() and implement() to pick up any data at any offset
> within the report.
> 
> Export those 2 helpers in BPF programs so users can also rely on them.
> The second net worth advantage of those helpers is that now we can
> fetch data anywhere in the report without knowing at compile time the
> location of it. The boundary checks are done in hid-bpf.c, to prevent
> a memory leak.
> 
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 13/28] HID: bpf: implement hid_bpf_get|set_data
  2022-03-04 17:28 ` [PATCH bpf-next v2 13/28] HID: bpf: implement hid_bpf_get|set_data Benjamin Tissoires
@ 2022-03-04 18:39   ` Greg KH
  0 siblings, 0 replies; 73+ messages in thread
From: Greg KH @ 2022-03-04 18:39 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, linux-kernel, linux-input, netdev,
	bpf, linux-kselftest

On Fri, Mar 04, 2022 at 06:28:37PM +0100, Benjamin Tissoires wrote:
> We have 2 cases of usage here:
> - either n <= 32: we are addressing individual bits at the given offset
> 
> - either n > 32: we are using a memcpy to transmit the data to the caller,
>   meaning that we need to be byte-aligned.

That is crazy.  Can you split this into a bits/bytes function set to
have 4 of them?  You are using each function in two totally different
ways.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 12/28] bpf/hid: add hid_{get|set}_data helpers
  2022-03-04 17:28 ` [PATCH bpf-next v2 12/28] bpf/hid: add hid_{get|set}_data helpers Benjamin Tissoires
  2022-03-04 18:37   ` Greg KH
@ 2022-03-04 18:41   ` Greg KH
  2022-03-05 10:33     ` Benjamin Tissoires
  1 sibling, 1 reply; 73+ messages in thread
From: Greg KH @ 2022-03-04 18:41 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, linux-kernel, linux-input, netdev,
	bpf, linux-kselftest

On Fri, Mar 04, 2022 at 06:28:36PM +0100, Benjamin Tissoires wrote:
> When we process an incoming HID report, it is common to have to account
> for fields that are not aligned in the report. HID is using 2 helpers
> hid_field_extract() and implement() to pick up any data at any offset
> within the report.
> 
> Export those 2 helpers in BPF programs so users can also rely on them.
> The second net worth advantage of those helpers is that now we can
> fetch data anywhere in the report without knowing at compile time the
> location of it. The boundary checks are done in hid-bpf.c, to prevent
> a memory leak.
> 
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> 
> ---
> 
> changes in v2:
> - split the patch with libbpf and HID left outside.
> ---
>  include/linux/bpf-hid.h        |  4 +++
>  include/uapi/linux/bpf.h       | 32 ++++++++++++++++++++
>  kernel/bpf/hid.c               | 53 ++++++++++++++++++++++++++++++++++
>  tools/include/uapi/linux/bpf.h | 32 ++++++++++++++++++++
>  4 files changed, 121 insertions(+)
> 
> diff --git a/include/linux/bpf-hid.h b/include/linux/bpf-hid.h
> index 0c5000b28b20..69bb28523ceb 100644
> --- a/include/linux/bpf-hid.h
> +++ b/include/linux/bpf-hid.h
> @@ -93,6 +93,10 @@ struct bpf_hid_hooks {
>  	int (*link_attach)(struct hid_device *hdev, enum bpf_hid_attach_type type);
>  	void (*link_attached)(struct hid_device *hdev, enum bpf_hid_attach_type type);
>  	void (*array_detached)(struct hid_device *hdev, enum bpf_hid_attach_type type);
> +	int (*hid_get_data)(struct hid_device *hdev, u8 *buf, size_t buf_size,
> +			    u64 offset, u32 n, u8 *data, u64 data_size);
> +	int (*hid_set_data)(struct hid_device *hdev, u8 *buf, size_t buf_size,
> +			    u64 offset, u32 n, u8 *data, u64 data_size);
>  };
>  
>  #ifdef CONFIG_BPF
> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> index a7a8d9cfcf24..4845a20e6f96 100644
> --- a/include/uapi/linux/bpf.h
> +++ b/include/uapi/linux/bpf.h
> @@ -5090,6 +5090,36 @@ union bpf_attr {
>   *	Return
>   *		0 on success, or a negative error in case of failure. On error
>   *		*dst* buffer is zeroed out.
> + *
> + * int bpf_hid_get_data(void *ctx, u64 offset, u32 n, u8 *data, u64 size)
> + *	Description
> + *		Get the data of size n (in bits) at the given offset (bits) in the
> + *		ctx->event.data field and store it into data.
> + *
> + *		if n is less or equal than 32, we can address with bit precision,
> + *		the value in the buffer. However, data must be a pointer to a u32
> + *		and size must be 4.
> + *
> + *		if n is greater than 32, offset and n must be a multiple of 8
> + *		and the result is working with a memcpy internally.
> + *	Return
> + *		The length of data copied into data. On error, a negative value
> + *		is returned.
> + *
> + * int bpf_hid_set_data(void *ctx, u64 offset, u32 n, u8 *data, u64 size)
> + *	Description
> + *		Set the data of size n (in bits) at the given offset (bits) in the
> + *		ctx->event.data field.
> + *
> + *		if n is less or equal than 32, we can address with bit precision,
> + *		the value in the buffer. However, data must be a pointer to a u32
> + *		and size must be 4.
> + *
> + *		if n is greater than 32, offset and n must be a multiple of 8
> + *		and the result is working with a memcpy internally.
> + *	Return
> + *		The length of data copied into ctx->event.data. On error, a negative
> + *		value is returned.

Wait, nevermind my reviewed-by previously, see my comment about how this
might be split into 4:
	bpf_hid_set_bytes()
	bpf_hid_get_bytes()
	bpf_hid_set_bits()
	bpf_hid_get_bits()

Should be easier to understand and maintain over time, right?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 01/28] bpf: add new is_sys_admin_prog_type() helper
  2022-03-04 17:28 ` [PATCH bpf-next v2 01/28] bpf: add new is_sys_admin_prog_type() helper Benjamin Tissoires
@ 2022-03-04 23:12   ` Song Liu
  2022-03-05 10:07     ` Benjamin Tissoires
  2022-03-09  8:27   ` Sean Young
  1 sibling, 1 reply; 73+ messages in thread
From: Song Liu @ 2022-03-04 23:12 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, open list, open list:HID CORE LAYER,
	Networking, bpf, linux-kselftest, Sean Young

On Fri, Mar 4, 2022 at 9:30 AM Benjamin Tissoires
<benjamin.tissoires@redhat.com> wrote:
>
> LIRC_MODE2 does not really need net_admin capability, but only sys_admin.
>
> Extract a new helper for it, it will be also used for the HID bpf
> implementation.
>
> Cc: Sean Young <sean@mess.org>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
>
> ---
>
> new in v2
> ---
>  kernel/bpf/syscall.c | 14 +++++++++++++-
>  1 file changed, 13 insertions(+), 1 deletion(-)
>
> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> index db402ebc5570..cc570891322b 100644
> --- a/kernel/bpf/syscall.c
> +++ b/kernel/bpf/syscall.c
> @@ -2165,7 +2165,6 @@ static bool is_net_admin_prog_type(enum bpf_prog_type prog_type)
>         case BPF_PROG_TYPE_LWT_SEG6LOCAL:
>         case BPF_PROG_TYPE_SK_SKB:
>         case BPF_PROG_TYPE_SK_MSG:
> -       case BPF_PROG_TYPE_LIRC_MODE2:
>         case BPF_PROG_TYPE_FLOW_DISSECTOR:
>         case BPF_PROG_TYPE_CGROUP_DEVICE:
>         case BPF_PROG_TYPE_CGROUP_SOCK:
> @@ -2202,6 +2201,17 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type)
>         }
>  }
>
> +static bool is_sys_admin_prog_type(enum bpf_prog_type prog_type)
> +{
> +       switch (prog_type) {
> +       case BPF_PROG_TYPE_LIRC_MODE2:
> +       case BPF_PROG_TYPE_EXT: /* extends any prog */
> +               return true;
> +       default:
> +               return false;
> +       }
> +}

I am not sure whether we should do this. This is a behavior change, that may
break some user space. Also, BPF_PROG_TYPE_EXT is checked in
is_perfmon_prog_type(), and this change will make that case useless.

Thanks,
Song

[...]

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 02/28] bpf: introduce hid program type
  2022-03-04 17:28 ` [PATCH bpf-next v2 02/28] bpf: introduce hid program type Benjamin Tissoires
  2022-03-04 18:21   ` Greg KH
@ 2022-03-05  0:02   ` Song Liu
  2022-03-07 18:39     ` Benjamin Tissoires
  2022-03-05  0:20   ` Song Liu
  2 siblings, 1 reply; 73+ messages in thread
From: Song Liu @ 2022-03-05  0:02 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, open list, open list:HID CORE LAYER,
	Networking, bpf, linux-kselftest

On Fri, Mar 4, 2022 at 9:31 AM Benjamin Tissoires
<benjamin.tissoires@redhat.com> wrote:
>
> HID is a protocol that could benefit from using BPF too.

[...]

> +#include <linux/list.h>
> +#include <linux/slab.h>
> +
> +struct bpf_prog;
> +struct bpf_prog_array;
> +struct hid_device;
> +
> +enum bpf_hid_attach_type {
> +       BPF_HID_ATTACH_INVALID = -1,
> +       BPF_HID_ATTACH_DEVICE_EVENT = 0,
> +       MAX_BPF_HID_ATTACH_TYPE

Is it typical to have different BPF programs for different attach types?
Otherwise, (different types may have similar BPF programs), maybe
we can pass type as an argument to the program (shared among
different types)?

[...]

> +struct hid_device;
> +
> +enum hid_bpf_event {
> +       HID_BPF_UNDEF = 0,
> +       HID_BPF_DEVICE_EVENT,           /* when attach type is BPF_HID_DEVICE_EVENT */
> +};
> +
> +struct hid_bpf_ctx {
> +       enum hid_bpf_event type;        /* read-only */
> +       __u16 allocated_size;           /* the allocated size of data below (RO) */

There is a (6-byte?) hole here.

> +       struct hid_device *hdev;        /* read-only */
> +
> +       __u16 size;                     /* used size in data (RW) */
> +       __u8 data[];                    /* data buffer (RW) */
> +};

Do we really need hit_bpf_ctx in uapi? Maybe we can just use it
from vmlinuxh?

[...]

> +
> +static bool hid_is_valid_access(int off, int size,
> +                               enum bpf_access_type access_type,
> +                               const struct bpf_prog *prog,
> +                               struct bpf_insn_access_aux *info)
> +{
> +       /* everything not in ctx is prohibited */
> +       if (off < 0 || off + size > sizeof(struct hid_bpf_ctx) + HID_BPF_MIN_BUFFER_SIZE)
> +               return false;

Mabe add the following here to fail unaligned accesses

        if (off % size != 0)
                return false;
[...]

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 02/28] bpf: introduce hid program type
  2022-03-04 17:28 ` [PATCH bpf-next v2 02/28] bpf: introduce hid program type Benjamin Tissoires
  2022-03-04 18:21   ` Greg KH
  2022-03-05  0:02   ` Song Liu
@ 2022-03-05  0:20   ` Song Liu
  2 siblings, 0 replies; 73+ messages in thread
From: Song Liu @ 2022-03-05  0:20 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, open list, open list:HID CORE LAYER,
	Networking, bpf, linux-kselftest

On Fri, Mar 4, 2022 at 9:31 AM Benjamin Tissoires
<benjamin.tissoires@redhat.com> wrote:
>
[...]
> +#endif
> +
> +static inline bool bpf_hid_link_empty(struct bpf_hid *bpf,
> +                                     enum bpf_hid_attach_type type)
> +{
> +       return list_empty(&bpf->links[type]);
> +}
> +
> +struct bpf_hid_hooks {
> +       struct hid_device *(*hdev_from_fd)(int fd);
> +       int (*link_attach)(struct hid_device *hdev, enum bpf_hid_attach_type type);
> +       void (*array_detached)(struct hid_device *hdev, enum bpf_hid_attach_type type);

shall we call this array_detach()? detached sounds like a function that
checks the status of link/hook.

[...]

> --- /dev/null
> +++ b/kernel/bpf/hid.c
> @@ -0,0 +1,437 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * based on kernel/bpf/net-namespace.c
> + */

I guess we don't need this comment.

> +
> +#include <linux/bpf.h>
> +#include <linux/bpf-hid.h>
> +#include <linux/filter.h>
> +#include <linux/hid.h>
> +#include <linux/hidraw.h>
[...]

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 03/28] HID: hook up with bpf
  2022-03-04 17:28 ` [PATCH bpf-next v2 03/28] HID: hook up with bpf Benjamin Tissoires
  2022-03-04 18:24   ` Greg KH
@ 2022-03-05  0:23   ` Song Liu
  2022-03-15 16:29   ` Tero Kristo
  2 siblings, 0 replies; 73+ messages in thread
From: Song Liu @ 2022-03-05  0:23 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, open list, open list:HID CORE LAYER,
	Networking, bpf, linux-kselftest

On Fri, Mar 4, 2022 at 9:31 AM Benjamin Tissoires
<benjamin.tissoires@redhat.com> wrote:
>
> Now that BPF can be compatible with HID, add the capability into HID.
> drivers/hid/hid-bpf.c takes care of the glue between bpf and HID, and
> hid-core can then inject any incoming event from the device into a BPF
> program to filter/analyze it.
>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
>
[...]

> +
> +static int hid_bpf_link_attach(struct hid_device *hdev, enum bpf_hid_attach_type type)
> +{
> +       int err = 0;
> +
> +       switch (type) {
> +       case BPF_HID_ATTACH_DEVICE_EVENT:
> +               if (!hdev->bpf.ctx) {
> +                       hdev->bpf.ctx = bpf_hid_allocate_ctx(hdev, HID_BPF_MAX_BUFFER_SIZE);
> +                       if (IS_ERR(hdev->bpf.ctx)) {
> +                               err = PTR_ERR(hdev->bpf.ctx);
> +                               hdev->bpf.ctx = NULL;
> +                       }
> +               }
> +               break;
> +       default:
> +               /* do nothing */

Do we need to show warning and/or return EINVAL here?

> +       }
> +
> +       return err;
> +}
> +
> +static void hid_bpf_array_detached(struct hid_device *hdev, enum bpf_hid_attach_type type)
> +{
> +       switch (type) {
> +       case BPF_HID_ATTACH_DEVICE_EVENT:
> +               kfree(hdev->bpf.ctx);
> +               hdev->bpf.ctx = NULL;
> +               break;
> +       default:
> +               /* do nothing */

ditto

[...]

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 04/28] libbpf: add HID program type and API
  2022-03-04 17:28 ` [PATCH bpf-next v2 04/28] libbpf: add HID program type and API Benjamin Tissoires
@ 2022-03-05  0:31   ` Song Liu
  2022-03-08  1:30   ` Andrii Nakryiko
  1 sibling, 0 replies; 73+ messages in thread
From: Song Liu @ 2022-03-05  0:31 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, open list, open list:HID CORE LAYER,
	Networking, bpf, linux-kselftest

On Fri, Mar 4, 2022 at 9:31 AM Benjamin Tissoires
<benjamin.tissoires@redhat.com> wrote:
>
> HID-bpf program type are needing a new SEC.
> To bind a hid-bpf program, we can rely on bpf_program__attach_fd()
> so export a new function to the API.
>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

Acked-by: Song Liu <songliubraving@fb.com>

>
> ---
>
> changes in v2:
> - split the series by bpf/libbpf/hid/selftests and samples
> ---
>  tools/lib/bpf/libbpf.c   | 7 +++++++
>  tools/lib/bpf/libbpf.h   | 2 ++
>  tools/lib/bpf/libbpf.map | 1 +
>  3 files changed, 10 insertions(+)
>
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 81bf01d67671..356bbd3ad2c7 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -8680,6 +8680,7 @@ static const struct bpf_sec_def section_defs[] = {
>         SEC_DEF("cgroup/setsockopt",    CGROUP_SOCKOPT, BPF_CGROUP_SETSOCKOPT, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
>         SEC_DEF("struct_ops+",          STRUCT_OPS, 0, SEC_NONE),
>         SEC_DEF("sk_lookup",            SK_LOOKUP, BPF_SK_LOOKUP, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
> +       SEC_DEF("hid/device_event",     HID, BPF_HID_DEVICE_EVENT, SEC_ATTACHABLE_OPT | SEC_SLOPPY_PFX),
>  };
>
>  #define MAX_TYPE_NAME_SIZE 32
> @@ -10659,6 +10660,12 @@ static struct bpf_link *attach_iter(const struct bpf_program *prog, long cookie)
>         return bpf_program__attach_iter(prog, NULL);
>  }
>
> +struct bpf_link *
> +bpf_program__attach_hid(const struct bpf_program *prog, int hid_fd)
> +{
> +       return bpf_program__attach_fd(prog, hid_fd, 0, "hid");
> +}
> +
>  struct bpf_link *bpf_program__attach(const struct bpf_program *prog)
>  {
>         if (!prog->sec_def || !prog->sec_def->attach_fn)
> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> index c8d8daad212e..f677ac0a9ede 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -529,6 +529,8 @@ struct bpf_iter_attach_opts {
>  LIBBPF_API struct bpf_link *
>  bpf_program__attach_iter(const struct bpf_program *prog,
>                          const struct bpf_iter_attach_opts *opts);
> +LIBBPF_API struct bpf_link *
> +bpf_program__attach_hid(const struct bpf_program *prog, int hid_fd);
>
>  /*
>   * Libbpf allows callers to adjust BPF programs before being loaded
> diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
> index 47e70c9058d9..fdc6fa743953 100644
> --- a/tools/lib/bpf/libbpf.map
> +++ b/tools/lib/bpf/libbpf.map
> @@ -424,6 +424,7 @@ LIBBPF_0.6.0 {
>  LIBBPF_0.7.0 {
>         global:
>                 bpf_btf_load;
> +               bpf_program__attach_hid;
>                 bpf_program__expected_attach_type;
>                 bpf_program__log_buf;
>                 bpf_program__log_level;
> --
> 2.35.1
>

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 05/28] selftests/bpf: add tests for the HID-bpf initial implementation
  2022-03-04 17:28 ` [PATCH bpf-next v2 05/28] selftests/bpf: add tests for the HID-bpf initial implementation Benjamin Tissoires
@ 2022-03-05  0:41   ` Song Liu
  2022-03-05 10:10     ` Benjamin Tissoires
  0 siblings, 1 reply; 73+ messages in thread
From: Song Liu @ 2022-03-05  0:41 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, open list, open list:HID CORE LAYER,
	Networking, bpf, linux-kselftest

On Fri, Mar 4, 2022 at 9:31 AM Benjamin Tissoires
<benjamin.tissoires@redhat.com> wrote:
>
> The test is pretty basic:
> - create a virtual uhid device that no userspace will like (to not mess
>   up the running system)
> - attach a BPF prog to it
> - open the matching hidraw node
> - inject one event and check:
>   * that the BPF program can do something on the event stream
>   * can modify the event stream
>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
>

Does this test run with vm (qemu, etc.)? Maybe we need to update
tools/testing/selftests/bpf/config ?

Thanks,
Song

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (27 preceding siblings ...)
  2022-03-04 17:28 ` [PATCH bpf-next v2 28/28] samples/bpf: fix bpf_program__attach_hid() api change Benjamin Tissoires
@ 2022-03-05  1:13 ` Song Liu
  2022-03-05 10:23   ` Benjamin Tissoires
  2022-03-15 17:04 ` Tero Kristo
  29 siblings, 1 reply; 73+ messages in thread
From: Song Liu @ 2022-03-05  1:13 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, open list, open list:HID CORE LAYER,
	Networking, bpf, linux-kselftest

On Fri, Mar 4, 2022 at 9:29 AM Benjamin Tissoires
<benjamin.tissoires@redhat.com> wrote:
>
> Hi,
>
> This is a followup of my v1 at [0].
>
> The short summary of the previous cover letter and discussions is that
> HID could benefit from BPF for the following use cases:
>
> - simple fixup of report descriptor:
>   benefits are faster development time and testing, with the produced
>   bpf program being shipped in the kernel directly (the shipping part
>   is *not* addressed here).
>
> - Universal Stylus Interface:
>   allows a user-space program to define its own kernel interface
>
> - Surface Dial:
>   somehow similar to the previous one except that userspace can decide
>   to change the shape of the exported device
>
> - firewall:
>   still partly missing there, there is not yet interception of hidraw
>   calls, but it's coming in a followup series, I promise
>
> - tracing:
>   well, tracing.
>
>
> I tried to address as many comments as I could and here is the short log
> of changes:
>
> v2:
> ===
>
> - split the series by subsystem (bpf, HID, libbpf, selftests and
>   samples)
>
> - Added an extra patch at the beginning to not require CAP_NET_ADMIN for
>   BPF_PROG_TYPE_LIRC_MODE2 (please shout if this is wrong)
>
> - made the bpf context attached to HID program of dynamic size:
>   * the first 1 kB will be able to be addressed directly
>   * the rest can be retrieved through bpf_hid_{set|get}_data
>     (note that I am definitivey not happy with that API, because there
>     is part of it in bits and other in bytes. ouch)
>
> - added an extra patch to prevent non GPL HID bpf programs to be loaded
>   of type BPF_PROG_TYPE_HID
>   * same here, not really happy but I don't know where to put that check
>     in verifier.c
>
> - added a new flag BPF_F_INSERT_HEAD for BPF_LINK_CREATE syscall when in
>   used with HID program types.
>   * this flag is used for tracing, to be able to load a program before
>     any others that might already have been inserted and that might
>     change the data stream.
>
> Cheers,
> Benjamin
>

The set looks good so far. I will review the rest later.

[...]

A quick note about how we organize these patches. Maybe we can
merge some of these patches like:

>   bpf: introduce hid program type
>   bpf/hid: add a new attach type to change the report descriptor
>   bpf/hid: add new BPF type to trigger commands from userspace
I guess the three can merge into one.

>   HID: hook up with bpf
>   HID: allow to change the report descriptor from an eBPF program
>   HID: bpf: compute only the required buffer size for the device
>   HID: bpf: only call hid_bpf_raw_event() if a ctx is available
I haven't read through all of them, but I guess they can probably merge
as well.

>   libbpf: add HID program type and API
>   libbpf: add new attach type BPF_HID_RDESC_FIXUP
>   libbpf: add new attach type BPF_HID_USER_EVENT
There 3 can merge, and maybe also the one below
>   libbpf: add handling for BPF_F_INSERT_HEAD in HID programs

>   samples/bpf: add new hid_mouse example
>   samples/bpf: add a report descriptor fixup
>   samples/bpf: fix bpf_program__attach_hid() api change
Maybe it makes sense to merge these 3?

>   bpf/hid: add hid_{get|set}_data helpers
>   HID: bpf: implement hid_bpf_get|set_data
>   bpf/hid: add bpf_hid_raw_request helper function
>   HID: add implementation of bpf_hid_raw_request
We can have 1 or 2 patches for these helpers

>   selftests/bpf: add tests for the HID-bpf initial implementation
>   selftests/bpf: add report descriptor fixup tests
>   selftests/bpf: add tests for hid_{get|set}_data helpers
>   selftests/bpf: add test for user call of HID bpf programs
>   selftests/bpf: hid: rely on uhid event to know if a test device is
>     ready
>   selftests/bpf: add tests for bpf_hid_hw_request
>   selftests/bpf: Add a test for BPF_F_INSERT_HEAD
These selftests could also merge into 1 or 2 patches I guess.

I understand rearranging these patches may take quite some effort.
But I do feel that's a cleaner approach (from someone doesn't know
much about HID). If you really hate it that way, we can discuss...

Thanks,
Song

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 01/28] bpf: add new is_sys_admin_prog_type() helper
  2022-03-04 23:12   ` Song Liu
@ 2022-03-05 10:07     ` Benjamin Tissoires
  2022-03-05 16:58       ` Sean Young
  2022-03-07 18:23       ` Song Liu
  0 siblings, 2 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-05 10:07 UTC (permalink / raw)
  To: Song Liu
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, open list, open list:HID CORE LAYER,
	Networking, bpf, linux-kselftest, Sean Young

On Sat, Mar 5, 2022 at 12:12 AM Song Liu <song@kernel.org> wrote:
>
> On Fri, Mar 4, 2022 at 9:30 AM Benjamin Tissoires
> <benjamin.tissoires@redhat.com> wrote:
> >
> > LIRC_MODE2 does not really need net_admin capability, but only sys_admin.
> >
> > Extract a new helper for it, it will be also used for the HID bpf
> > implementation.
> >
> > Cc: Sean Young <sean@mess.org>
> > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> >
> > ---
> >
> > new in v2
> > ---
> >  kernel/bpf/syscall.c | 14 +++++++++++++-
> >  1 file changed, 13 insertions(+), 1 deletion(-)
> >
> > diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> > index db402ebc5570..cc570891322b 100644
> > --- a/kernel/bpf/syscall.c
> > +++ b/kernel/bpf/syscall.c
> > @@ -2165,7 +2165,6 @@ static bool is_net_admin_prog_type(enum bpf_prog_type prog_type)
> >         case BPF_PROG_TYPE_LWT_SEG6LOCAL:
> >         case BPF_PROG_TYPE_SK_SKB:
> >         case BPF_PROG_TYPE_SK_MSG:
> > -       case BPF_PROG_TYPE_LIRC_MODE2:
> >         case BPF_PROG_TYPE_FLOW_DISSECTOR:
> >         case BPF_PROG_TYPE_CGROUP_DEVICE:
> >         case BPF_PROG_TYPE_CGROUP_SOCK:
> > @@ -2202,6 +2201,17 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type)
> >         }
> >  }
> >
> > +static bool is_sys_admin_prog_type(enum bpf_prog_type prog_type)
> > +{
> > +       switch (prog_type) {
> > +       case BPF_PROG_TYPE_LIRC_MODE2:
> > +       case BPF_PROG_TYPE_EXT: /* extends any prog */
> > +               return true;
> > +       default:
> > +               return false;
> > +       }
> > +}
>
> I am not sure whether we should do this. This is a behavior change, that may
> break some user space. Also, BPF_PROG_TYPE_EXT is checked in
> is_perfmon_prog_type(), and this change will make that case useless.

Sure, I can drop it from v3 and make this function appear for HID only.

Regarding BPF_PROG_TYPE_EXT, it was already in both
is_net_admin_prog_type() and is_perfmon_prog_type(), so I duplicated
it here, but I agree, given that it's already in the first function
there, CPA_SYS_ADMIN is already checked.

Cheers,
Benjamin

>
> Thanks,
> Song
>
> [...]
>


^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 05/28] selftests/bpf: add tests for the HID-bpf initial implementation
  2022-03-05  0:41   ` Song Liu
@ 2022-03-05 10:10     ` Benjamin Tissoires
  0 siblings, 0 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-05 10:10 UTC (permalink / raw)
  To: Song Liu
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, open list, open list:HID CORE LAYER,
	Networking, bpf, linux-kselftest

On Sat, Mar 5, 2022 at 1:41 AM Song Liu <song@kernel.org> wrote:
>
> On Fri, Mar 4, 2022 at 9:31 AM Benjamin Tissoires
> <benjamin.tissoires@redhat.com> wrote:
> >
> > The test is pretty basic:
> > - create a virtual uhid device that no userspace will like (to not mess
> >   up the running system)
> > - attach a BPF prog to it
> > - open the matching hidraw node
> > - inject one event and check:
> >   * that the BPF program can do something on the event stream
> >   * can modify the event stream
> >
> > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> >
>
> Does this test run with vm (qemu, etc.)? Maybe we need to update
> tools/testing/selftests/bpf/config ?

Good point. I'll give it a shot. I was testing on my devel machine so
it was quicker to iterate, but I completely forgot to make a run at
qemu with the scripts in place.

Cheers,
Benjamin

>
> Thanks,
> Song
>


^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices
  2022-03-05  1:13 ` [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Song Liu
@ 2022-03-05 10:23   ` Benjamin Tissoires
  2022-03-07 18:11     ` Song Liu
  0 siblings, 1 reply; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-05 10:23 UTC (permalink / raw)
  To: Song Liu
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, open list, open list:HID CORE LAYER,
	Networking, bpf, linux-kselftest

Hi Song,

Thanks a lot for the review.

I'll comment on the review in more details next week, but I have a
quick question here:

On Sat, Mar 5, 2022 at 2:14 AM Song Liu <song@kernel.org> wrote:
>
> On Fri, Mar 4, 2022 at 9:29 AM Benjamin Tissoires
> <benjamin.tissoires@redhat.com> wrote:
> >
> > Hi,
> >
> > This is a followup of my v1 at [0].
> >
> > The short summary of the previous cover letter and discussions is that
> > HID could benefit from BPF for the following use cases:
> >
> > - simple fixup of report descriptor:
> >   benefits are faster development time and testing, with the produced
> >   bpf program being shipped in the kernel directly (the shipping part
> >   is *not* addressed here).
> >
> > - Universal Stylus Interface:
> >   allows a user-space program to define its own kernel interface
> >
> > - Surface Dial:
> >   somehow similar to the previous one except that userspace can decide
> >   to change the shape of the exported device
> >
> > - firewall:
> >   still partly missing there, there is not yet interception of hidraw
> >   calls, but it's coming in a followup series, I promise
> >
> > - tracing:
> >   well, tracing.
> >
> >
> > I tried to address as many comments as I could and here is the short log
> > of changes:
> >
> > v2:
> > ===
> >
> > - split the series by subsystem (bpf, HID, libbpf, selftests and
> >   samples)
> >
> > - Added an extra patch at the beginning to not require CAP_NET_ADMIN for
> >   BPF_PROG_TYPE_LIRC_MODE2 (please shout if this is wrong)
> >
> > - made the bpf context attached to HID program of dynamic size:
> >   * the first 1 kB will be able to be addressed directly
> >   * the rest can be retrieved through bpf_hid_{set|get}_data
> >     (note that I am definitivey not happy with that API, because there
> >     is part of it in bits and other in bytes. ouch)
> >
> > - added an extra patch to prevent non GPL HID bpf programs to be loaded
> >   of type BPF_PROG_TYPE_HID
> >   * same here, not really happy but I don't know where to put that check
> >     in verifier.c
> >
> > - added a new flag BPF_F_INSERT_HEAD for BPF_LINK_CREATE syscall when in
> >   used with HID program types.
> >   * this flag is used for tracing, to be able to load a program before
> >     any others that might already have been inserted and that might
> >     change the data stream.
> >
> > Cheers,
> > Benjamin
> >
>
> The set looks good so far. I will review the rest later.
>
> [...]
>
> A quick note about how we organize these patches. Maybe we can
> merge some of these patches like:

Just to be sure we are talking about the same thing: you mean squash
the patch together?

>
> >   bpf: introduce hid program type
> >   bpf/hid: add a new attach type to change the report descriptor
> >   bpf/hid: add new BPF type to trigger commands from userspace
> I guess the three can merge into one.
>
> >   HID: hook up with bpf
> >   HID: allow to change the report descriptor from an eBPF program
> >   HID: bpf: compute only the required buffer size for the device
> >   HID: bpf: only call hid_bpf_raw_event() if a ctx is available
> I haven't read through all of them, but I guess they can probably merge
> as well.

There are certainly patches that we could squash together (3 and 4
from this list into the previous ones), but I'd like to keep some sort
of granularity here to not have a patch bomb that gets harder to come
back later.

>
> >   libbpf: add HID program type and API
> >   libbpf: add new attach type BPF_HID_RDESC_FIXUP
> >   libbpf: add new attach type BPF_HID_USER_EVENT
> There 3 can merge, and maybe also the one below
> >   libbpf: add handling for BPF_F_INSERT_HEAD in HID programs

Yeah, the libbpf changes are small enough to not really justify
separate patches.

>
> >   samples/bpf: add new hid_mouse example
> >   samples/bpf: add a report descriptor fixup
> >   samples/bpf: fix bpf_program__attach_hid() api change
> Maybe it makes sense to merge these 3?

Sure, why not.

>
> >   bpf/hid: add hid_{get|set}_data helpers
> >   HID: bpf: implement hid_bpf_get|set_data
> >   bpf/hid: add bpf_hid_raw_request helper function
> >   HID: add implementation of bpf_hid_raw_request
> We can have 1 or 2 patches for these helpers

OK, the patches should be self-contained enough.

>
> >   selftests/bpf: add tests for the HID-bpf initial implementation
> >   selftests/bpf: add report descriptor fixup tests
> >   selftests/bpf: add tests for hid_{get|set}_data helpers
> >   selftests/bpf: add test for user call of HID bpf programs
> >   selftests/bpf: hid: rely on uhid event to know if a test device is
> >     ready
> >   selftests/bpf: add tests for bpf_hid_hw_request
> >   selftests/bpf: Add a test for BPF_F_INSERT_HEAD
> These selftests could also merge into 1 or 2 patches I guess.

I'd still like to link them to the granularity of the bpf changes, so
I can refer a selftest change to a specific commit/functionality
added. But that's just my personal taste, and I can be convinced
otherwise. This should give us maybe 4 patches instead of 7.

>
> I understand rearranging these patches may take quite some effort.
> But I do feel that's a cleaner approach (from someone doesn't know
> much about HID). If you really hate it that way, we can discuss...
>

No worries. I don't mind iterating on the series. IIRC I already
rewrote it twice from scratch, and that's when the selftests I
introduced in the second rewrite were tremendously helpful :) And
honestly I don't think it'll be too much effort to reorder/squash the
patches given that the v2 is *very* granular.

Anyway, I prefer having the reviewers happy so we can have a solid
rock API from day 1 than keeping it obscure for everyone and having to
deal with design issues forever. So if it takes 10 or 20 revisions to
have everybody on the same page, that's fine with me (not that I want
to have that many revisions, just that I won't be afraid of the
bikeshedding we might have at some point).

Cheers,
Benjamin


^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 12/28] bpf/hid: add hid_{get|set}_data helpers
  2022-03-04 18:41   ` Greg KH
@ 2022-03-05 10:33     ` Benjamin Tissoires
  2022-03-05 10:47       ` Greg KH
  0 siblings, 1 reply; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-05 10:33 UTC (permalink / raw)
  To: Greg KH
  Cc: Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, lkml, open list:HID CORE LAYER,
	Networking, bpf, linux-kselftest

On Fri, Mar 4, 2022 at 7:41 PM Greg KH <gregkh@linuxfoundation.org> wrote:
>
> On Fri, Mar 04, 2022 at 06:28:36PM +0100, Benjamin Tissoires wrote:
> > When we process an incoming HID report, it is common to have to account
> > for fields that are not aligned in the report. HID is using 2 helpers
> > hid_field_extract() and implement() to pick up any data at any offset
> > within the report.
> >
> > Export those 2 helpers in BPF programs so users can also rely on them.
> > The second net worth advantage of those helpers is that now we can
> > fetch data anywhere in the report without knowing at compile time the
> > location of it. The boundary checks are done in hid-bpf.c, to prevent
> > a memory leak.
> >
> > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> >
> > ---
> >
> > changes in v2:
> > - split the patch with libbpf and HID left outside.
> > ---
> >  include/linux/bpf-hid.h        |  4 +++
> >  include/uapi/linux/bpf.h       | 32 ++++++++++++++++++++
> >  kernel/bpf/hid.c               | 53 ++++++++++++++++++++++++++++++++++
> >  tools/include/uapi/linux/bpf.h | 32 ++++++++++++++++++++
> >  4 files changed, 121 insertions(+)
> >
> > diff --git a/include/linux/bpf-hid.h b/include/linux/bpf-hid.h
> > index 0c5000b28b20..69bb28523ceb 100644
> > --- a/include/linux/bpf-hid.h
> > +++ b/include/linux/bpf-hid.h
> > @@ -93,6 +93,10 @@ struct bpf_hid_hooks {
> >       int (*link_attach)(struct hid_device *hdev, enum bpf_hid_attach_type type);
> >       void (*link_attached)(struct hid_device *hdev, enum bpf_hid_attach_type type);
> >       void (*array_detached)(struct hid_device *hdev, enum bpf_hid_attach_type type);
> > +     int (*hid_get_data)(struct hid_device *hdev, u8 *buf, size_t buf_size,
> > +                         u64 offset, u32 n, u8 *data, u64 data_size);
> > +     int (*hid_set_data)(struct hid_device *hdev, u8 *buf, size_t buf_size,
> > +                         u64 offset, u32 n, u8 *data, u64 data_size);
> >  };
> >
> >  #ifdef CONFIG_BPF
> > diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> > index a7a8d9cfcf24..4845a20e6f96 100644
> > --- a/include/uapi/linux/bpf.h
> > +++ b/include/uapi/linux/bpf.h
> > @@ -5090,6 +5090,36 @@ union bpf_attr {
> >   *   Return
> >   *           0 on success, or a negative error in case of failure. On error
> >   *           *dst* buffer is zeroed out.
> > + *
> > + * int bpf_hid_get_data(void *ctx, u64 offset, u32 n, u8 *data, u64 size)
> > + *   Description
> > + *           Get the data of size n (in bits) at the given offset (bits) in the
> > + *           ctx->event.data field and store it into data.
> > + *
> > + *           if n is less or equal than 32, we can address with bit precision,
> > + *           the value in the buffer. However, data must be a pointer to a u32
> > + *           and size must be 4.
> > + *
> > + *           if n is greater than 32, offset and n must be a multiple of 8
> > + *           and the result is working with a memcpy internally.
> > + *   Return
> > + *           The length of data copied into data. On error, a negative value
> > + *           is returned.
> > + *
> > + * int bpf_hid_set_data(void *ctx, u64 offset, u32 n, u8 *data, u64 size)
> > + *   Description
> > + *           Set the data of size n (in bits) at the given offset (bits) in the
> > + *           ctx->event.data field.
> > + *
> > + *           if n is less or equal than 32, we can address with bit precision,
> > + *           the value in the buffer. However, data must be a pointer to a u32
> > + *           and size must be 4.
> > + *
> > + *           if n is greater than 32, offset and n must be a multiple of 8
> > + *           and the result is working with a memcpy internally.
> > + *   Return
> > + *           The length of data copied into ctx->event.data. On error, a negative
> > + *           value is returned.
>

Quick answer on this one (before going deeper with the other remarks next week):

> Wait, nevermind my reviewed-by previously, see my comment about how this
> might be split into 4:
>         bpf_hid_set_bytes()
>         bpf_hid_get_bytes()
>         bpf_hid_set_bits()
>         bpf_hid_get_bits()
>
> Should be easier to understand and maintain over time, right?

Yes, definitively. I thought about adding a `bytes` suffix to the
function name for n > 32, but not the `bits` one, meaning the API was
still bunkers in my head.

And as I mentioned in the commit notes, I knew the API proposed in
this patch was not correct, simply because while working on the
selftests it was completely wrong :)

In terms of API usage, does it feel wrong for you to have only a
subset of the array available "for free" and enforce the rest to be
used through these helpers?
That's the point I am not sure but I feel like 1024 (or slightly more)
would be enough for most use cases, and when we are dealing with
bigger data sets the helpers can be justified.

Thanks for the suggestion.

Cheers,
Benjamin

>
> thanks,
>
> greg k-h
>


^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 12/28] bpf/hid: add hid_{get|set}_data helpers
  2022-03-05 10:33     ` Benjamin Tissoires
@ 2022-03-05 10:47       ` Greg KH
  2022-03-11  0:40         ` Song Liu
  0 siblings, 1 reply; 73+ messages in thread
From: Greg KH @ 2022-03-05 10:47 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, lkml, open list:HID CORE LAYER,
	Networking, bpf, linux-kselftest

On Sat, Mar 05, 2022 at 11:33:07AM +0100, Benjamin Tissoires wrote:
> On Fri, Mar 4, 2022 at 7:41 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> >
> > On Fri, Mar 04, 2022 at 06:28:36PM +0100, Benjamin Tissoires wrote:
> > > When we process an incoming HID report, it is common to have to account
> > > for fields that are not aligned in the report. HID is using 2 helpers
> > > hid_field_extract() and implement() to pick up any data at any offset
> > > within the report.
> > >
> > > Export those 2 helpers in BPF programs so users can also rely on them.
> > > The second net worth advantage of those helpers is that now we can
> > > fetch data anywhere in the report without knowing at compile time the
> > > location of it. The boundary checks are done in hid-bpf.c, to prevent
> > > a memory leak.
> > >
> > > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> > >
> > > ---
> > >
> > > changes in v2:
> > > - split the patch with libbpf and HID left outside.
> > > ---
> > >  include/linux/bpf-hid.h        |  4 +++
> > >  include/uapi/linux/bpf.h       | 32 ++++++++++++++++++++
> > >  kernel/bpf/hid.c               | 53 ++++++++++++++++++++++++++++++++++
> > >  tools/include/uapi/linux/bpf.h | 32 ++++++++++++++++++++
> > >  4 files changed, 121 insertions(+)
> > >
> > > diff --git a/include/linux/bpf-hid.h b/include/linux/bpf-hid.h
> > > index 0c5000b28b20..69bb28523ceb 100644
> > > --- a/include/linux/bpf-hid.h
> > > +++ b/include/linux/bpf-hid.h
> > > @@ -93,6 +93,10 @@ struct bpf_hid_hooks {
> > >       int (*link_attach)(struct hid_device *hdev, enum bpf_hid_attach_type type);
> > >       void (*link_attached)(struct hid_device *hdev, enum bpf_hid_attach_type type);
> > >       void (*array_detached)(struct hid_device *hdev, enum bpf_hid_attach_type type);
> > > +     int (*hid_get_data)(struct hid_device *hdev, u8 *buf, size_t buf_size,
> > > +                         u64 offset, u32 n, u8 *data, u64 data_size);
> > > +     int (*hid_set_data)(struct hid_device *hdev, u8 *buf, size_t buf_size,
> > > +                         u64 offset, u32 n, u8 *data, u64 data_size);
> > >  };
> > >
> > >  #ifdef CONFIG_BPF
> > > diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> > > index a7a8d9cfcf24..4845a20e6f96 100644
> > > --- a/include/uapi/linux/bpf.h
> > > +++ b/include/uapi/linux/bpf.h
> > > @@ -5090,6 +5090,36 @@ union bpf_attr {
> > >   *   Return
> > >   *           0 on success, or a negative error in case of failure. On error
> > >   *           *dst* buffer is zeroed out.
> > > + *
> > > + * int bpf_hid_get_data(void *ctx, u64 offset, u32 n, u8 *data, u64 size)
> > > + *   Description
> > > + *           Get the data of size n (in bits) at the given offset (bits) in the
> > > + *           ctx->event.data field and store it into data.
> > > + *
> > > + *           if n is less or equal than 32, we can address with bit precision,
> > > + *           the value in the buffer. However, data must be a pointer to a u32
> > > + *           and size must be 4.
> > > + *
> > > + *           if n is greater than 32, offset and n must be a multiple of 8
> > > + *           and the result is working with a memcpy internally.
> > > + *   Return
> > > + *           The length of data copied into data. On error, a negative value
> > > + *           is returned.
> > > + *
> > > + * int bpf_hid_set_data(void *ctx, u64 offset, u32 n, u8 *data, u64 size)
> > > + *   Description
> > > + *           Set the data of size n (in bits) at the given offset (bits) in the
> > > + *           ctx->event.data field.
> > > + *
> > > + *           if n is less or equal than 32, we can address with bit precision,
> > > + *           the value in the buffer. However, data must be a pointer to a u32
> > > + *           and size must be 4.
> > > + *
> > > + *           if n is greater than 32, offset and n must be a multiple of 8
> > > + *           and the result is working with a memcpy internally.
> > > + *   Return
> > > + *           The length of data copied into ctx->event.data. On error, a negative
> > > + *           value is returned.
> >
> 
> Quick answer on this one (before going deeper with the other remarks next week):
> 
> > Wait, nevermind my reviewed-by previously, see my comment about how this
> > might be split into 4:
> >         bpf_hid_set_bytes()
> >         bpf_hid_get_bytes()
> >         bpf_hid_set_bits()
> >         bpf_hid_get_bits()
> >
> > Should be easier to understand and maintain over time, right?
> 
> Yes, definitively. I thought about adding a `bytes` suffix to the
> function name for n > 32, but not the `bits` one, meaning the API was
> still bunkers in my head.
> 
> And as I mentioned in the commit notes, I knew the API proposed in
> this patch was not correct, simply because while working on the
> selftests it was completely wrong :)
> 
> In terms of API usage, does it feel wrong for you to have only a
> subset of the array available "for free" and enforce the rest to be
> used through these helpers?

It does feel "odd" but:

> That's the point I am not sure but I feel like 1024 (or slightly more)
> would be enough for most use cases, and when we are dealing with
> bigger data sets the helpers can be justified.

How often are hid reports that big?  If providing access to the first
1024 or so bytes for free would handle the huge majority of fixup cases,
and only if you have crazy devices with large reports would you have to
use the functions, it might be fine.

The problem is, only time will tell, and then, it's too late to change
the api :)

So I agree with this for now, stick with what you have (if you split
this into 4 functions), and let's see what happens.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 01/28] bpf: add new is_sys_admin_prog_type() helper
  2022-03-05 10:07     ` Benjamin Tissoires
@ 2022-03-05 16:58       ` Sean Young
  2022-03-07 18:26         ` Song Liu
  2022-03-07 18:23       ` Song Liu
  1 sibling, 1 reply; 73+ messages in thread
From: Sean Young @ 2022-03-05 16:58 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Song Liu, Greg KH, Jiri Kosina, Alexei Starovoitov,
	Daniel Borkmann, Andrii Nakryiko, Martin KaFai Lau, Song Liu,
	Yonghong Song, John Fastabend, KP Singh, Shuah Khan,
	Dave Marchevsky, Joe Stringer, Tero Kristo, open list,
	open list:HID CORE LAYER, Networking, bpf, linux-kselftest

On Sat, Mar 05, 2022 at 11:07:04AM +0100, Benjamin Tissoires wrote:
> On Sat, Mar 5, 2022 at 12:12 AM Song Liu <song@kernel.org> wrote:
> >
> > On Fri, Mar 4, 2022 at 9:30 AM Benjamin Tissoires
> > <benjamin.tissoires@redhat.com> wrote:
> > >
> > > LIRC_MODE2 does not really need net_admin capability, but only sys_admin.
> > >
> > > Extract a new helper for it, it will be also used for the HID bpf
> > > implementation.
> > >
> > > Cc: Sean Young <sean@mess.org>
> > > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> > >
> > > ---
> > >
> > > new in v2
> > > ---
> > >  kernel/bpf/syscall.c | 14 +++++++++++++-
> > >  1 file changed, 13 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> > > index db402ebc5570..cc570891322b 100644
> > > --- a/kernel/bpf/syscall.c
> > > +++ b/kernel/bpf/syscall.c
> > > @@ -2165,7 +2165,6 @@ static bool is_net_admin_prog_type(enum bpf_prog_type prog_type)
> > >         case BPF_PROG_TYPE_LWT_SEG6LOCAL:
> > >         case BPF_PROG_TYPE_SK_SKB:
> > >         case BPF_PROG_TYPE_SK_MSG:
> > > -       case BPF_PROG_TYPE_LIRC_MODE2:
> > >         case BPF_PROG_TYPE_FLOW_DISSECTOR:
> > >         case BPF_PROG_TYPE_CGROUP_DEVICE:
> > >         case BPF_PROG_TYPE_CGROUP_SOCK:
> > > @@ -2202,6 +2201,17 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type)
> > >         }
> > >  }
> > >
> > > +static bool is_sys_admin_prog_type(enum bpf_prog_type prog_type)
> > > +{
> > > +       switch (prog_type) {
> > > +       case BPF_PROG_TYPE_LIRC_MODE2:
> > > +       case BPF_PROG_TYPE_EXT: /* extends any prog */
> > > +               return true;
> > > +       default:
> > > +               return false;
> > > +       }
> > > +}
> >
> > I am not sure whether we should do this. This is a behavior change, that may
> > break some user space. Also, BPF_PROG_TYPE_EXT is checked in
> > is_perfmon_prog_type(), and this change will make that case useless.
> 
> Sure, I can drop it from v3 and make this function appear for HID only.

For BPF_PROG_TYPE_LIRC_MODE2, I don't think this change will break userspace.
This is called from ir-keytable(1) which is called from udev. It should have
all the necessary permissions.

In addition, the vast majority IR decoders are non-bpf. bpf ir decoders have
very few users at the moment.

I am working on completely new userspace tooling which will make extensive
use of bpf ir decoding with full lircd and IRP compatibility, but this is not
finished yet (see https://github.com/seanyoung/cir).

Thanks

Sean

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 02/28] bpf: introduce hid program type
  2022-03-04 18:21   ` Greg KH
@ 2022-03-07 17:57     ` Benjamin Tissoires
  0 siblings, 0 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-07 17:57 UTC (permalink / raw)
  To: Greg KH
  Cc: Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, lkml, open list:HID CORE LAYER,
	Networking, bpf, linux-kselftest

On Fri, Mar 4, 2022 at 7:21 PM Greg KH <gregkh@linuxfoundation.org> wrote:
>
> On Fri, Mar 04, 2022 at 06:28:26PM +0100, Benjamin Tissoires wrote:
> > HID is a protocol that could benefit from using BPF too.
> >
> > This patch implements a net-like use of BPF capability for HID.
> > Any incoming report coming from the device can be injected into a series
> > of BPF programs that can modify it or even discard it by setting the
> > size in the context to 0.
> >
> > The kernel/bpf implementation is based on net-namespace.c, with only
> > the bpf_link part kept, there is no real points in keeping the
> > bpf_prog_{attach|detach} API.
> >
> > The implementation here is only focusing on the bpf changes. The HID
> > changes that hooks onto this are coming in a separate patch.
> >
> > Given that HID can be compiled in as a module, and the functions that
> > kernel/bpf/hid.c needs to call in hid.ko are exported in struct hid_hooks.
> >
> > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> >
> > ---
> >
> > changes in v2:
> > - split the series by bpf/libbpf/hid/selftests and samples
> > - unsigned long -> __u16 in uapi/linux/bpf_hid.h
> > - change the bpf_ctx to be of variable size, with a min of 1024 bytes
> > - make this 1 kB available directly from bpf program, the rest will
> >   need a helper
> > - add some more doc comments in uapi
> > ---
> >  include/linux/bpf-hid.h        | 108 ++++++++
> >  include/linux/bpf_types.h      |   4 +
> >  include/linux/hid.h            |   5 +
> >  include/uapi/linux/bpf.h       |   7 +
> >  include/uapi/linux/bpf_hid.h   |  39 +++
> >  kernel/bpf/Makefile            |   3 +
> >  kernel/bpf/hid.c               | 437 +++++++++++++++++++++++++++++++++
> >  kernel/bpf/syscall.c           |   8 +
> >  tools/include/uapi/linux/bpf.h |   7 +
> >  9 files changed, 618 insertions(+)
> >  create mode 100644 include/linux/bpf-hid.h
> >  create mode 100644 include/uapi/linux/bpf_hid.h
> >  create mode 100644 kernel/bpf/hid.c
> >
> > diff --git a/include/linux/bpf-hid.h b/include/linux/bpf-hid.h
> > new file mode 100644
> > index 000000000000..3cda78051b5f
> > --- /dev/null
> > +++ b/include/linux/bpf-hid.h
> > @@ -0,0 +1,108 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +#ifndef _BPF_HID_H
> > +#define _BPF_HID_H
> > +
> > +#include <linux/mutex.h>
> > +#include <uapi/linux/bpf.h>
> > +#include <uapi/linux/bpf_hid.h>
> > +#include <linux/list.h>
> > +#include <linux/slab.h>
> > +
> > +struct bpf_prog;
> > +struct bpf_prog_array;
> > +struct hid_device;
> > +
> > +enum bpf_hid_attach_type {
> > +     BPF_HID_ATTACH_INVALID = -1,
> > +     BPF_HID_ATTACH_DEVICE_EVENT = 0,
> > +     MAX_BPF_HID_ATTACH_TYPE
> > +};
> > +
> > +struct bpf_hid {
> > +     struct hid_bpf_ctx *ctx;
> > +
> > +     /* Array of programs to run compiled from links */
> > +     struct bpf_prog_array __rcu *run_array[MAX_BPF_HID_ATTACH_TYPE];
> > +     struct list_head links[MAX_BPF_HID_ATTACH_TYPE];
> > +};
> > +
> > +static inline enum bpf_hid_attach_type
> > +to_bpf_hid_attach_type(enum bpf_attach_type attach_type)
> > +{
> > +     switch (attach_type) {
> > +     case BPF_HID_DEVICE_EVENT:
> > +             return BPF_HID_ATTACH_DEVICE_EVENT;
> > +     default:
> > +             return BPF_HID_ATTACH_INVALID;
> > +     }
> > +}
> > +
> > +static inline struct hid_bpf_ctx *bpf_hid_allocate_ctx(struct hid_device *hdev,
> > +                                                    size_t data_size)
> > +{
> > +     struct hid_bpf_ctx *ctx;
> > +
> > +     /* ensure data_size is between min and max */
> > +     data_size = clamp_val(data_size,
> > +                           HID_BPF_MIN_BUFFER_SIZE,
> > +                           HID_BPF_MAX_BUFFER_SIZE);
>
> Do you want to return an error if the data size is not within the range?

That was not something I was counting on.
Though I am thinking of not necessarily clamping this value in the end
because I might have found a way to not do the initial memcpy when
running a prog, and so not having to limit the size of the data.

> Otherwise people will just start to use crazy values and you will always
> be limiting them?

The users of this helper are really limited to drivers/hid/hid_pbf.c
and kernel/bpf/hid.c. And they are known in advance and there must be
only one user per attach type.
The only thing where the data might explode is when in used with
SEC(hid/device_event), because we statically allocate one bpf_ctx
based on the device report descriptor.
But if the required size is bigger than HID_BPF_MAX_BUFFER_SIZE, the
device will not probe or at least already logs something in the dmesg
that we are using a too big buffer.

>
> > +
> > +     ctx = kzalloc(sizeof(*ctx) + data_size, GFP_KERNEL);
> > +     if (!ctx)
> > +             return ERR_PTR(-ENOMEM);
> > +
> > +     ctx->hdev = hdev;
> > +     ctx->allocated_size = data_size;
> > +
> > +     return ctx;
> > +}
>
> And why is this an inline function?  Why not put it in a .c file?

The problem I have here is that the hid module can be loaded as an
external module. So I can not directly use that helper from hid.ko
from kernel/bpf/hid.c (I need it there once for the
SEC(hid/user_event) bprogram attach type).

So the solution would be to have the code in the c part of
kernel/bpf/hid.c and export the function as GPL, but I wanted to have
the minimum of knowledge of HID-BPF internals in that file. So I ended
up using an inline so I can reuse it independently in kernel/bpf/hid.c
and drivers/hid/hid-bpf.c.

>
> > +
> > +union bpf_attr;
> > +struct bpf_prog;
> > +
> > +#if IS_ENABLED(CONFIG_HID)
> > +int bpf_hid_prog_query(const union bpf_attr *attr,
> > +                    union bpf_attr __user *uattr);
> > +int bpf_hid_link_create(const union bpf_attr *attr,
> > +                     struct bpf_prog *prog);
> > +#else
> > +static inline int bpf_hid_prog_query(const union bpf_attr *attr,
> > +                                  union bpf_attr __user *uattr)
> > +{
> > +     return -EOPNOTSUPP;
> > +}
> > +
> > +static inline int bpf_hid_link_create(const union bpf_attr *attr,
> > +                                   struct bpf_prog *prog)
> > +{
> > +     return -EOPNOTSUPP;
> > +}
> > +#endif
> > +
> > +static inline bool bpf_hid_link_empty(struct bpf_hid *bpf,
> > +                                   enum bpf_hid_attach_type type)
> > +{
> > +     return list_empty(&bpf->links[type]);
> > +}
> > +
> > +struct bpf_hid_hooks {
> > +     struct hid_device *(*hdev_from_fd)(int fd);
> > +     int (*link_attach)(struct hid_device *hdev, enum bpf_hid_attach_type type);
> > +     void (*array_detached)(struct hid_device *hdev, enum bpf_hid_attach_type type);
> > +};
> > +
> > +#ifdef CONFIG_BPF
> > +int bpf_hid_init(struct hid_device *hdev);
> > +void bpf_hid_exit(struct hid_device *hdev);
> > +void bpf_hid_set_hooks(struct bpf_hid_hooks *hooks);
> > +#else
> > +static inline int bpf_hid_init(struct hid_device *hdev)
> > +{
> > +     return 0;
> > +}
> > +
> > +static inline void bpf_hid_exit(struct hid_device *hdev) {}
> > +static inline void bpf_hid_set_hooks(struct bpf_hid_hooks *hooks) {}
> > +#endif
> > +
> > +#endif /* _BPF_HID_H */
> > diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h
> > index 48a91c51c015..1509862aacc4 100644
> > --- a/include/linux/bpf_types.h
> > +++ b/include/linux/bpf_types.h
> > @@ -76,6 +76,10 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_EXT, bpf_extension,
> >  BPF_PROG_TYPE(BPF_PROG_TYPE_LSM, lsm,
> >              void *, void *)
> >  #endif /* CONFIG_BPF_LSM */
> > +#if IS_ENABLED(CONFIG_HID)
> > +BPF_PROG_TYPE(BPF_PROG_TYPE_HID, hid,
> > +           __u32, u32)
>
> Why the mix of __u32 and u32 here?

This is actually valid. I tracked it down to kernel/bpf/btf.c with:

static union {
          struct bpf_ctx_convert {
  #define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type) \
          prog_ctx_type _id##_prog; \
          kern_ctx_type _id##_kern;
  #include <linux/bpf_types.h>
  #undef BPF_PROG_TYPE
          } *__t;
          /* 't' is written once under lock. Read many times. */
          const struct btf_type *t;
} bpf_ctx_convert;

So prog_ctx_type represents a user API, while kern_ctx_type
represents the kernel counterpart.

That being said, this is plain wrong, because I am not using u32 as
bpf context, but a properly defined struct :o)

So I probably need to amend this to be either "void *, void *)" or
something better (I'll ask Song in my reply to him).


>
> > +#endif
> >  #endif
> >  BPF_PROG_TYPE(BPF_PROG_TYPE_SYSCALL, bpf_syscall,
> >             void *, void *)
> > diff --git a/include/linux/hid.h b/include/linux/hid.h
> > index 7487b0586fe6..56f6f4ad45a7 100644
> > --- a/include/linux/hid.h
> > +++ b/include/linux/hid.h
> > @@ -15,6 +15,7 @@
> >
> >
> >  #include <linux/bitops.h>
> > +#include <linux/bpf-hid.h>
> >  #include <linux/types.h>
> >  #include <linux/slab.h>
> >  #include <linux/list.h>
> > @@ -639,6 +640,10 @@ struct hid_device {                                                      /* device report descriptor */
> >       struct list_head debug_list;
> >       spinlock_t  debug_list_lock;
> >       wait_queue_head_t debug_wait;
> > +
> > +#ifdef CONFIG_BPF
> > +     struct bpf_hid bpf;
> > +#endif
> >  };
> >
> >  #define to_hid_device(pdev) \
> > diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> > index afe3d0d7f5f2..5978b92cacd3 100644
> > --- a/include/uapi/linux/bpf.h
> > +++ b/include/uapi/linux/bpf.h
> > @@ -952,6 +952,7 @@ enum bpf_prog_type {
> >       BPF_PROG_TYPE_LSM,
> >       BPF_PROG_TYPE_SK_LOOKUP,
> >       BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */
> > +     BPF_PROG_TYPE_HID,
> >  };
> >
> >  enum bpf_attach_type {
> > @@ -997,6 +998,7 @@ enum bpf_attach_type {
> >       BPF_SK_REUSEPORT_SELECT,
> >       BPF_SK_REUSEPORT_SELECT_OR_MIGRATE,
> >       BPF_PERF_EVENT,
> > +     BPF_HID_DEVICE_EVENT,
> >       __MAX_BPF_ATTACH_TYPE
> >  };
> >
> > @@ -1011,6 +1013,7 @@ enum bpf_link_type {
> >       BPF_LINK_TYPE_NETNS = 5,
> >       BPF_LINK_TYPE_XDP = 6,
> >       BPF_LINK_TYPE_PERF_EVENT = 7,
> > +     BPF_LINK_TYPE_HID = 8,
> >
> >       MAX_BPF_LINK_TYPE,
> >  };
> > @@ -5870,6 +5873,10 @@ struct bpf_link_info {
> >               struct {
> >                       __u32 ifindex;
> >               } xdp;
> > +             struct  {
> > +                     __s32 hidraw_ino;
>
> "ino"?  We have lots of letters to spell words out :)

no comments... :)

>
> > +                     __u32 attach_type;
> > +             } hid;
> >       };
> >  } __attribute__((aligned(8)));
> >
> > diff --git a/include/uapi/linux/bpf_hid.h b/include/uapi/linux/bpf_hid.h
> > new file mode 100644
> > index 000000000000..975ca5bd526f
> > --- /dev/null
> > +++ b/include/uapi/linux/bpf_hid.h
> > @@ -0,0 +1,39 @@
> > +/* SPDX-License-Identifier: GPL-2.0-or-later WITH Linux-syscall-note */
> > +
> > +/*
> > + *  HID BPF public headers
> > + *
> > + *  Copyright (c) 2021 Benjamin Tissoires
>
> Did you forget the copyright line on the other .h file above?

oops

>
> > + */
> > +
> > +#ifndef _UAPI__LINUX_BPF_HID_H__
> > +#define _UAPI__LINUX_BPF_HID_H__
> > +
> > +#include <linux/types.h>
> > +
> > +/*
> > + * The first 1024 bytes are available directly in the bpf programs.
> > + * To access the rest of the data (if allocated_size is bigger
> > + * than 1024, you need to use bpf_hid_ helpers.
> > + */
> > +#define HID_BPF_MIN_BUFFER_SIZE              1024
> > +#define HID_BPF_MAX_BUFFER_SIZE              16384           /* in sync with HID_MAX_BUFFER_SIZE */
>
> Can't you just use HID_MAX_BUFFER_SIZE?

Call me dumb, but curiously I could not get my code to compile there.
If I include linux/hid.h, things are getting messy and either the
tools or the kernel itself was not compiling properly (couldn't really
remember what was failing exactly, sorry).

>
> Anyway, all minor stuff, looks good!

Thanks. Not sure I'll keep the bpf_ctx the same after further
thoughts, but I appreciate the review :)

Cheers,
Benjamin

>
> Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
>


^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices
  2022-03-05 10:23   ` Benjamin Tissoires
@ 2022-03-07 18:11     ` Song Liu
  2022-03-08 13:37       ` Benjamin Tissoires
  0 siblings, 1 reply; 73+ messages in thread
From: Song Liu @ 2022-03-07 18:11 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, open list, open list:HID CORE LAYER,
	Networking, bpf, linux-kselftest

On Sat, Mar 5, 2022 at 2:23 AM Benjamin Tissoires
<benjamin.tissoires@redhat.com> wrote:
> > >
> >
> > The set looks good so far. I will review the rest later.
> >
> > [...]
> >
> > A quick note about how we organize these patches. Maybe we can
> > merge some of these patches like:
>
> Just to be sure we are talking about the same thing: you mean squash
> the patch together?

Right, squash some patches together.

>
> >
> > >   bpf: introduce hid program type
> > >   bpf/hid: add a new attach type to change the report descriptor
> > >   bpf/hid: add new BPF type to trigger commands from userspace
> > I guess the three can merge into one.
> >
> > >   HID: hook up with bpf
> > >   HID: allow to change the report descriptor from an eBPF program
> > >   HID: bpf: compute only the required buffer size for the device
> > >   HID: bpf: only call hid_bpf_raw_event() if a ctx is available
> > I haven't read through all of them, but I guess they can probably merge
> > as well.
>
> There are certainly patches that we could squash together (3 and 4
> from this list into the previous ones), but I'd like to keep some sort
> of granularity here to not have a patch bomb that gets harder to come
> back later.

Totally agreed with the granularity of patches. I am not a big fan of patch
bombs either. :)

I guess the problem I have with the current version is that I don't have a
big picture of the design while reading through relatively big patches. A
overview with the following information in the cover letter would be really
help here:
  1. How different types of programs are triggered (IRQ, user input, etc.);
  2. What are the operations and/or outcomes of these programs;
  3. How would programs of different types (or attach types) interact
   with each other (via bpf maps? chaining?)
  4. What's the new uapi;
  5. New helpers and other logistics

Sometimes, I find the changes to uapi are the key for me to understand the
patches, and I would like to see one or two patches with all the UAPI
changes (i.e. bpf_hid_attach_type). However, that may or may not apply to
this set due to granularity concerns.

Does this make sense?

Thanks,
Song




>
> >
> > >   libbpf: add HID program type and API
> > >   libbpf: add new attach type BPF_HID_RDESC_FIXUP
> > >   libbpf: add new attach type BPF_HID_USER_EVENT
> > There 3 can merge, and maybe also the one below
> > >   libbpf: add handling for BPF_F_INSERT_HEAD in HID programs
>
> Yeah, the libbpf changes are small enough to not really justify
> separate patches.
>
> >
> > >   samples/bpf: add new hid_mouse example
> > >   samples/bpf: add a report descriptor fixup
> > >   samples/bpf: fix bpf_program__attach_hid() api change
> > Maybe it makes sense to merge these 3?
>
> Sure, why not.
>
> >
> > >   bpf/hid: add hid_{get|set}_data helpers
> > >   HID: bpf: implement hid_bpf_get|set_data
> > >   bpf/hid: add bpf_hid_raw_request helper function
> > >   HID: add implementation of bpf_hid_raw_request
> > We can have 1 or 2 patches for these helpers
>
> OK, the patches should be self-contained enough.
>
> >
> > >   selftests/bpf: add tests for the HID-bpf initial implementation
> > >   selftests/bpf: add report descriptor fixup tests
> > >   selftests/bpf: add tests for hid_{get|set}_data helpers
> > >   selftests/bpf: add test for user call of HID bpf programs
> > >   selftests/bpf: hid: rely on uhid event to know if a test device is
> > >     ready
> > >   selftests/bpf: add tests for bpf_hid_hw_request
> > >   selftests/bpf: Add a test for BPF_F_INSERT_HEAD
> > These selftests could also merge into 1 or 2 patches I guess.
>
> I'd still like to link them to the granularity of the bpf changes, so
> I can refer a selftest change to a specific commit/functionality
> added. But that's just my personal taste, and I can be convinced
> otherwise. This should give us maybe 4 patches instead of 7.
>
> >
> > I understand rearranging these patches may take quite some effort.
> > But I do feel that's a cleaner approach (from someone doesn't know
> > much about HID). If you really hate it that way, we can discuss...
> >
>
> No worries. I don't mind iterating on the series. IIRC I already
> rewrote it twice from scratch, and that's when the selftests I
> introduced in the second rewrite were tremendously helpful :) And
> honestly I don't think it'll be too much effort to reorder/squash the
> patches given that the v2 is *very* granular.
>
> Anyway, I prefer having the reviewers happy so we can have a solid
> rock API from day 1 than keeping it obscure for everyone and having to
> deal with design issues forever. So if it takes 10 or 20 revisions to
> have everybody on the same page, that's fine with me (not that I want
> to have that many revisions, just that I won't be afraid of the
> bikeshedding we might have at some point).
>
> Cheers,
> Benjamin
>

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 01/28] bpf: add new is_sys_admin_prog_type() helper
  2022-03-05 10:07     ` Benjamin Tissoires
  2022-03-05 16:58       ` Sean Young
@ 2022-03-07 18:23       ` Song Liu
  1 sibling, 0 replies; 73+ messages in thread
From: Song Liu @ 2022-03-07 18:23 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, open list, open list:HID CORE LAYER,
	Networking, bpf, linux-kselftest, Sean Young

On Sat, Mar 5, 2022 at 2:07 AM Benjamin Tissoires
<benjamin.tissoires@redhat.com> wrote:
>
> On Sat, Mar 5, 2022 at 12:12 AM Song Liu <song@kernel.org> wrote:
> >
> > On Fri, Mar 4, 2022 at 9:30 AM Benjamin Tissoires
> > <benjamin.tissoires@redhat.com> wrote:
> > >
> > > LIRC_MODE2 does not really need net_admin capability, but only sys_admin.
> > >
> > > Extract a new helper for it, it will be also used for the HID bpf
> > > implementation.
> > >
> > > Cc: Sean Young <sean@mess.org>
> > > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> > >
> > > ---
> > >
> > > new in v2
> > > ---
> > >  kernel/bpf/syscall.c | 14 +++++++++++++-
> > >  1 file changed, 13 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> > > index db402ebc5570..cc570891322b 100644
> > > --- a/kernel/bpf/syscall.c
> > > +++ b/kernel/bpf/syscall.c
> > > @@ -2165,7 +2165,6 @@ static bool is_net_admin_prog_type(enum bpf_prog_type prog_type)
> > >         case BPF_PROG_TYPE_LWT_SEG6LOCAL:
> > >         case BPF_PROG_TYPE_SK_SKB:
> > >         case BPF_PROG_TYPE_SK_MSG:
> > > -       case BPF_PROG_TYPE_LIRC_MODE2:
> > >         case BPF_PROG_TYPE_FLOW_DISSECTOR:
> > >         case BPF_PROG_TYPE_CGROUP_DEVICE:
> > >         case BPF_PROG_TYPE_CGROUP_SOCK:
> > > @@ -2202,6 +2201,17 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type)
> > >         }
> > >  }
> > >
> > > +static bool is_sys_admin_prog_type(enum bpf_prog_type prog_type)
> > > +{
> > > +       switch (prog_type) {
> > > +       case BPF_PROG_TYPE_LIRC_MODE2:
> > > +       case BPF_PROG_TYPE_EXT: /* extends any prog */
> > > +               return true;
> > > +       default:
> > > +               return false;
> > > +       }
> > > +}
> >
> > I am not sure whether we should do this. This is a behavior change, that may
> > break some user space. Also, BPF_PROG_TYPE_EXT is checked in
> > is_perfmon_prog_type(), and this change will make that case useless.
>
> Sure, I can drop it from v3 and make this function appear for HID only.
>
> Regarding BPF_PROG_TYPE_EXT, it was already in both
> is_net_admin_prog_type() and is_perfmon_prog_type(), so I duplicated
> it here, but I agree, given that it's already in the first function
> there, CPA_SYS_ADMIN is already checked.

I think with current code, a user with CAP_BPF, CAP_NET_ADMIN, and
CAP_PERFMON (but not CAP_SYS_ADMIN) can load programs of type
BPF_PROG_TYPE_EXT. But after the patch, the same user will not be
able to do it. Did I misread it? It is not a common case though.

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 01/28] bpf: add new is_sys_admin_prog_type() helper
  2022-03-05 16:58       ` Sean Young
@ 2022-03-07 18:26         ` Song Liu
  0 siblings, 0 replies; 73+ messages in thread
From: Song Liu @ 2022-03-07 18:26 UTC (permalink / raw)
  To: Sean Young
  Cc: Benjamin Tissoires, Greg KH, Jiri Kosina, Alexei Starovoitov,
	Daniel Borkmann, Andrii Nakryiko, Martin KaFai Lau, Song Liu,
	Yonghong Song, John Fastabend, KP Singh, Shuah Khan,
	Dave Marchevsky, Joe Stringer, Tero Kristo, open list,
	open list:HID CORE LAYER, Networking, bpf, linux-kselftest

On Sat, Mar 5, 2022 at 8:58 AM Sean Young <sean@mess.org> wrote:
>
> On Sat, Mar 05, 2022 at 11:07:04AM +0100, Benjamin Tissoires wrote:
> > On Sat, Mar 5, 2022 at 12:12 AM Song Liu <song@kernel.org> wrote:
> > >
> > > On Fri, Mar 4, 2022 at 9:30 AM Benjamin Tissoires
> > > <benjamin.tissoires@redhat.com> wrote:
> > > >
> > > > LIRC_MODE2 does not really need net_admin capability, but only sys_admin.
> > > >
> > > > Extract a new helper for it, it will be also used for the HID bpf
> > > > implementation.
> > > >
> > > > Cc: Sean Young <sean@mess.org>
> > > > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> > > >
> > > > ---
> > > >
> > > > new in v2
> > > > ---
> > > >  kernel/bpf/syscall.c | 14 +++++++++++++-
> > > >  1 file changed, 13 insertions(+), 1 deletion(-)
> > > >
> > > > diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> > > > index db402ebc5570..cc570891322b 100644
> > > > --- a/kernel/bpf/syscall.c
> > > > +++ b/kernel/bpf/syscall.c
> > > > @@ -2165,7 +2165,6 @@ static bool is_net_admin_prog_type(enum bpf_prog_type prog_type)
> > > >         case BPF_PROG_TYPE_LWT_SEG6LOCAL:
> > > >         case BPF_PROG_TYPE_SK_SKB:
> > > >         case BPF_PROG_TYPE_SK_MSG:
> > > > -       case BPF_PROG_TYPE_LIRC_MODE2:
> > > >         case BPF_PROG_TYPE_FLOW_DISSECTOR:
> > > >         case BPF_PROG_TYPE_CGROUP_DEVICE:
> > > >         case BPF_PROG_TYPE_CGROUP_SOCK:
> > > > @@ -2202,6 +2201,17 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type)
> > > >         }
> > > >  }
> > > >
> > > > +static bool is_sys_admin_prog_type(enum bpf_prog_type prog_type)
> > > > +{
> > > > +       switch (prog_type) {
> > > > +       case BPF_PROG_TYPE_LIRC_MODE2:
> > > > +       case BPF_PROG_TYPE_EXT: /* extends any prog */
> > > > +               return true;
> > > > +       default:
> > > > +               return false;
> > > > +       }
> > > > +}
> > >
> > > I am not sure whether we should do this. This is a behavior change, that may
> > > break some user space. Also, BPF_PROG_TYPE_EXT is checked in
> > > is_perfmon_prog_type(), and this change will make that case useless.
> >
> > Sure, I can drop it from v3 and make this function appear for HID only.
>
> For BPF_PROG_TYPE_LIRC_MODE2, I don't think this change will break userspace.
> This is called from ir-keytable(1) which is called from udev. It should have
> all the necessary permissions.
>
> In addition, the vast majority IR decoders are non-bpf. bpf ir decoders have
> very few users at the moment.
>
> I am working on completely new userspace tooling which will make extensive
> use of bpf ir decoding with full lircd and IRP compatibility, but this is not
> finished yet (see https://github.com/seanyoung/cir).

Thanks for these information. I guess change for BPF_PROG_TYPE_LIRC_MODE2
is ok then. Would you mind ack or review this change (either current version or
a later version)?

Thanks,
Song

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 02/28] bpf: introduce hid program type
  2022-03-05  0:02   ` Song Liu
@ 2022-03-07 18:39     ` Benjamin Tissoires
  2022-03-08  0:56       ` Song Liu
  0 siblings, 1 reply; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-07 18:39 UTC (permalink / raw)
  To: Song Liu
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, open list, open list:HID CORE LAYER,
	Networking, bpf, linux-kselftest

On Sat, Mar 5, 2022 at 1:03 AM Song Liu <song@kernel.org> wrote:
>
> On Fri, Mar 4, 2022 at 9:31 AM Benjamin Tissoires
> <benjamin.tissoires@redhat.com> wrote:
> >
> > HID is a protocol that could benefit from using BPF too.
>
> [...]
>
> > +#include <linux/list.h>
> > +#include <linux/slab.h>
> > +
> > +struct bpf_prog;
> > +struct bpf_prog_array;
> > +struct hid_device;
> > +
> > +enum bpf_hid_attach_type {
> > +       BPF_HID_ATTACH_INVALID = -1,
> > +       BPF_HID_ATTACH_DEVICE_EVENT = 0,
> > +       MAX_BPF_HID_ATTACH_TYPE
>
> Is it typical to have different BPF programs for different attach types?
> Otherwise, (different types may have similar BPF programs), maybe
> we can pass type as an argument to the program (shared among
> different types)?

Not quite sure I am entirely following you, but I consider the various
attach types to be quite different and thus you can not really reuse
the same BPF program with 2 different attach types.

In my view, we have 4 attach types:
- BPF_HID_ATTACH_DEVICE_EVENT: called whenever we receive an IRQ from
the given device (so this is net-like event stream)
- BPF_HID_ATTACH_RDESC_FIXUP: there can be only one of this type, and
this is called to change the device capabilities. So you can not reuse
the other programs for this one
- BPF_HID_ATTACH_USER_EVENT: called explicitly by the userspace
process owning the program. There we can use functions that are
sleeping (we are not in IRQ context), so this is also fundamentally
different from the 3 others.
- BPF_HID_ATTACH_DRIVER_EVENT: whenever the driver gets called into,
we get a bpf program run. This can be suspend/resume, or even specific
request to the device (change a feature on the device or get its
current state). Again, IMO fundamentally different from the others.

So I'm open to any suggestions, but if we can keep the userspace API
being defined with different SEC in libbpf, that would be the best.

>
> [...]
>
> > +struct hid_device;
> > +
> > +enum hid_bpf_event {
> > +       HID_BPF_UNDEF = 0,
> > +       HID_BPF_DEVICE_EVENT,           /* when attach type is BPF_HID_DEVICE_EVENT */
> > +};
> > +
> > +struct hid_bpf_ctx {
> > +       enum hid_bpf_event type;        /* read-only */
> > +       __u16 allocated_size;           /* the allocated size of data below (RO) */
>
> There is a (6-byte?) hole here.
>
> > +       struct hid_device *hdev;        /* read-only */
> > +
> > +       __u16 size;                     /* used size in data (RW) */
> > +       __u8 data[];                    /* data buffer (RW) */
> > +};
>
> Do we really need hit_bpf_ctx in uapi? Maybe we can just use it
> from vmlinuxh?

I had a thought at this context today, and I think I am getting to the
limit of what I understand.

My first worry is that the way I wrote it there, with a variable data
field length is that this is not forward compatible. Unless BTF and
CORE are making magic, this will bite me in the long run IMO.

But then, you are talking about not using uapi, and I am starting to
wonder: am I doing the things correctly?

To solve my first issue (and the weird API I had to introduce in the
bpf_hid_get/set_data), I came up to the following:
instead of exporting the data directly in the context, I could create
a helper bpf_hid_get_data_buf(ctx, const uint size) that returns a
RET_PTR_TO_ALLOC_MEM_OR_NULL in the same way bpf_ringbuf_reserve()
does.

This way, I can directly access the fields within the bpf program
without having to worry about the size.

But now, I am wondering whether the uapi I defined here is correct in
the way CORE works.

My goal is to have HID-BPF programs to be CORE compatible, and not
have to recompile them depending on the underlying kernel.

I can not understand right now if I need to add some other BTF helpers
in the same way the access to struct xdp_md and struct xdp_buff are
converted between one and other, or if defining a forward compatible
struct hid_bpf_ctx is enough.
As far as I understand, .convert_ctx_access allows to export a stable
uapi to the bpf prog users with the verifier doing the conversion
between the structs for me. But is this really required for all the
BPF programs if we want them to be CORE?

Also, I am starting to wonder if I should not hide fields in the
context to the users. The .data field could be a pointer and only
accessed through the helper I mentioned above. This would be forward
compatible, and also allows to use whatever available memory in the
kernel to be forwarded to the BPF program. This way I can skip the
memcpy part and work directly with the incoming dma data buffer from
the IRQ.

But is it best practice to do such a thing?

Cheers,
Benjamin

>
> [...]
>
> > +
> > +static bool hid_is_valid_access(int off, int size,
> > +                               enum bpf_access_type access_type,
> > +                               const struct bpf_prog *prog,
> > +                               struct bpf_insn_access_aux *info)
> > +{
> > +       /* everything not in ctx is prohibited */
> > +       if (off < 0 || off + size > sizeof(struct hid_bpf_ctx) + HID_BPF_MIN_BUFFER_SIZE)
> > +               return false;
>
> Mabe add the following here to fail unaligned accesses
>
>         if (off % size != 0)
>                 return false;
> [...]
>


^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 02/28] bpf: introduce hid program type
  2022-03-07 18:39     ` Benjamin Tissoires
@ 2022-03-08  0:56       ` Song Liu
  2022-03-08  9:20         ` Benjamin Tissoires
  0 siblings, 1 reply; 73+ messages in thread
From: Song Liu @ 2022-03-08  0:56 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, open list, open list:HID CORE LAYER,
	Networking, bpf, linux-kselftest

On Mon, Mar 7, 2022 at 10:39 AM Benjamin Tissoires
<benjamin.tissoires@redhat.com> wrote:
>
> On Sat, Mar 5, 2022 at 1:03 AM Song Liu <song@kernel.org> wrote:
> >
> > On Fri, Mar 4, 2022 at 9:31 AM Benjamin Tissoires
> > <benjamin.tissoires@redhat.com> wrote:
> > >
> > > HID is a protocol that could benefit from using BPF too.
> >
> > [...]
> >
> > > +#include <linux/list.h>
> > > +#include <linux/slab.h>
> > > +
> > > +struct bpf_prog;
> > > +struct bpf_prog_array;
> > > +struct hid_device;
> > > +
> > > +enum bpf_hid_attach_type {
> > > +       BPF_HID_ATTACH_INVALID = -1,
> > > +       BPF_HID_ATTACH_DEVICE_EVENT = 0,
> > > +       MAX_BPF_HID_ATTACH_TYPE
> >
> > Is it typical to have different BPF programs for different attach types?
> > Otherwise, (different types may have similar BPF programs), maybe
> > we can pass type as an argument to the program (shared among
> > different types)?
>
> Not quite sure I am entirely following you, but I consider the various
> attach types to be quite different and thus you can not really reuse
> the same BPF program with 2 different attach types.
>
> In my view, we have 4 attach types:
> - BPF_HID_ATTACH_DEVICE_EVENT: called whenever we receive an IRQ from
> the given device (so this is net-like event stream)
> - BPF_HID_ATTACH_RDESC_FIXUP: there can be only one of this type, and
> this is called to change the device capabilities. So you can not reuse
> the other programs for this one
> - BPF_HID_ATTACH_USER_EVENT: called explicitly by the userspace
> process owning the program. There we can use functions that are
> sleeping (we are not in IRQ context), so this is also fundamentally
> different from the 3 others.
> - BPF_HID_ATTACH_DRIVER_EVENT: whenever the driver gets called into,
> we get a bpf program run. This can be suspend/resume, or even specific
> request to the device (change a feature on the device or get its
> current state). Again, IMO fundamentally different from the others.
>
> So I'm open to any suggestions, but if we can keep the userspace API
> being defined with different SEC in libbpf, that would be the best.

Thanks for this information. Different attach_types sound right for the use
case.

>
> >
> > [...]
> >
> > > +struct hid_device;
> > > +
> > > +enum hid_bpf_event {
> > > +       HID_BPF_UNDEF = 0,
> > > +       HID_BPF_DEVICE_EVENT,           /* when attach type is BPF_HID_DEVICE_EVENT */
> > > +};
> > > +
> > > +struct hid_bpf_ctx {
> > > +       enum hid_bpf_event type;        /* read-only */
> > > +       __u16 allocated_size;           /* the allocated size of data below (RO) */
> >
> > There is a (6-byte?) hole here.
> >
> > > +       struct hid_device *hdev;        /* read-only */
> > > +
> > > +       __u16 size;                     /* used size in data (RW) */
> > > +       __u8 data[];                    /* data buffer (RW) */
> > > +};
> >
> > Do we really need hit_bpf_ctx in uapi? Maybe we can just use it
> > from vmlinuxh?
>
> I had a thought at this context today, and I think I am getting to the
> limit of what I understand.
>
> My first worry is that the way I wrote it there, with a variable data
> field length is that this is not forward compatible. Unless BTF and
> CORE are making magic, this will bite me in the long run IMO.
>
> But then, you are talking about not using uapi, and I am starting to
> wonder: am I doing the things correctly?
>
> To solve my first issue (and the weird API I had to introduce in the
> bpf_hid_get/set_data), I came up to the following:
> instead of exporting the data directly in the context, I could create
> a helper bpf_hid_get_data_buf(ctx, const uint size) that returns a
> RET_PTR_TO_ALLOC_MEM_OR_NULL in the same way bpf_ringbuf_reserve()
> does.
>
> This way, I can directly access the fields within the bpf program
> without having to worry about the size.
>
> But now, I am wondering whether the uapi I defined here is correct in
> the way CORE works.
>
> My goal is to have HID-BPF programs to be CORE compatible, and not
> have to recompile them depending on the underlying kernel.
>
> I can not understand right now if I need to add some other BTF helpers
> in the same way the access to struct xdp_md and struct xdp_buff are
> converted between one and other, or if defining a forward compatible
> struct hid_bpf_ctx is enough.
> As far as I understand, .convert_ctx_access allows to export a stable
> uapi to the bpf prog users with the verifier doing the conversion
> between the structs for me. But is this really required for all the
> BPF programs if we want them to be CORE?
>
> Also, I am starting to wonder if I should not hide fields in the
> context to the users. The .data field could be a pointer and only
> accessed through the helper I mentioned above. This would be forward
> compatible, and also allows to use whatever available memory in the
> kernel to be forwarded to the BPF program. This way I can skip the
> memcpy part and work directly with the incoming dma data buffer from
> the IRQ.
>
> But is it best practice to do such a thing?

I think .convert_ctx_access is the way to go if we want to access the data
buffer without memcpy. I am not sure how much work is needed to make
it compatible with CORE though.

To make sure I understand the case, do we want something like

bpf_prog(struct hid_bpf_ctx *ctx)
{
    /* makes sure n < ctx->size */
    x = ctx->data[n]; /* read data */
    ctx->data[n] = <something>; /* write data */
    ctx->size = <something <= n>; /* change data size */
}

We also need it to be CORE, so that we may modify hid_bpf_ctx by
inserting more members to it before data.

Is this accurate?

Song

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 04/28] libbpf: add HID program type and API
  2022-03-04 17:28 ` [PATCH bpf-next v2 04/28] libbpf: add HID program type and API Benjamin Tissoires
  2022-03-05  0:31   ` Song Liu
@ 2022-03-08  1:30   ` Andrii Nakryiko
  2022-03-08  1:38     ` Song Liu
  1 sibling, 1 reply; 73+ messages in thread
From: Andrii Nakryiko @ 2022-03-08  1:30 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, open list, linux-input, Networking,
	bpf, open list:KERNEL SELFTEST FRAMEWORK

On Fri, Mar 4, 2022 at 9:31 AM Benjamin Tissoires
<benjamin.tissoires@redhat.com> wrote:
>
> HID-bpf program type are needing a new SEC.
> To bind a hid-bpf program, we can rely on bpf_program__attach_fd()
> so export a new function to the API.
>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
>
> ---
>
> changes in v2:
> - split the series by bpf/libbpf/hid/selftests and samples
> ---
>  tools/lib/bpf/libbpf.c   | 7 +++++++
>  tools/lib/bpf/libbpf.h   | 2 ++
>  tools/lib/bpf/libbpf.map | 1 +
>  3 files changed, 10 insertions(+)
>
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 81bf01d67671..356bbd3ad2c7 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -8680,6 +8680,7 @@ static const struct bpf_sec_def section_defs[] = {
>         SEC_DEF("cgroup/setsockopt",    CGROUP_SOCKOPT, BPF_CGROUP_SETSOCKOPT, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
>         SEC_DEF("struct_ops+",          STRUCT_OPS, 0, SEC_NONE),
>         SEC_DEF("sk_lookup",            SK_LOOKUP, BPF_SK_LOOKUP, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
> +       SEC_DEF("hid/device_event",     HID, BPF_HID_DEVICE_EVENT, SEC_ATTACHABLE_OPT | SEC_SLOPPY_PFX),

no SEC_SLOPPY_PFX for any new program type, please


>  };
>
>  #define MAX_TYPE_NAME_SIZE 32
> @@ -10659,6 +10660,12 @@ static struct bpf_link *attach_iter(const struct bpf_program *prog, long cookie)
>         return bpf_program__attach_iter(prog, NULL);
>  }
>
> +struct bpf_link *
> +bpf_program__attach_hid(const struct bpf_program *prog, int hid_fd)
> +{
> +       return bpf_program__attach_fd(prog, hid_fd, 0, "hid");
> +}
> +
>  struct bpf_link *bpf_program__attach(const struct bpf_program *prog)
>  {
>         if (!prog->sec_def || !prog->sec_def->attach_fn)
> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> index c8d8daad212e..f677ac0a9ede 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -529,6 +529,8 @@ struct bpf_iter_attach_opts {
>  LIBBPF_API struct bpf_link *
>  bpf_program__attach_iter(const struct bpf_program *prog,
>                          const struct bpf_iter_attach_opts *opts);
> +LIBBPF_API struct bpf_link *
> +bpf_program__attach_hid(const struct bpf_program *prog, int hid_fd);
>
>  /*
>   * Libbpf allows callers to adjust BPF programs before being loaded
> diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
> index 47e70c9058d9..fdc6fa743953 100644
> --- a/tools/lib/bpf/libbpf.map
> +++ b/tools/lib/bpf/libbpf.map
> @@ -424,6 +424,7 @@ LIBBPF_0.6.0 {
>  LIBBPF_0.7.0 {
>         global:
>                 bpf_btf_load;
> +               bpf_program__attach_hid;

should go into 0.8.0

>                 bpf_program__expected_attach_type;
>                 bpf_program__log_buf;
>                 bpf_program__log_level;
> --
> 2.35.1
>

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 04/28] libbpf: add HID program type and API
  2022-03-08  1:30   ` Andrii Nakryiko
@ 2022-03-08  1:38     ` Song Liu
  2022-03-08  5:52       ` Andrii Nakryiko
  0 siblings, 1 reply; 73+ messages in thread
From: Song Liu @ 2022-03-08  1:38 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Benjamin Tissoires, Greg KH, Jiri Kosina, Alexei Starovoitov,
	Daniel Borkmann, Andrii Nakryiko, Martin Lau, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, open list, linux-input, Networking,
	bpf, open list:KERNEL SELFTEST FRAMEWORK



> On Mar 7, 2022, at 5:30 PM, Andrii Nakryiko <andrii.nakryiko@gmail.com> wrote:
> 
> On Fri, Mar 4, 2022 at 9:31 AM Benjamin Tissoires
> <benjamin.tissoires@redhat.com> wrote:
>> 
>> HID-bpf program type are needing a new SEC.
>> To bind a hid-bpf program, we can rely on bpf_program__attach_fd()
>> so export a new function to the API.
>> 
>> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
>> 
>> ---
>> 
>> changes in v2:
>> - split the series by bpf/libbpf/hid/selftests and samples
>> ---
>> tools/lib/bpf/libbpf.c   | 7 +++++++
>> tools/lib/bpf/libbpf.h   | 2 ++
>> tools/lib/bpf/libbpf.map | 1 +
>> 3 files changed, 10 insertions(+)
>> 
>> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
>> index 81bf01d67671..356bbd3ad2c7 100644
>> --- a/tools/lib/bpf/libbpf.c
>> +++ b/tools/lib/bpf/libbpf.c
>> @@ -8680,6 +8680,7 @@ static const struct bpf_sec_def section_defs[] = {
>>        SEC_DEF("cgroup/setsockopt",    CGROUP_SOCKOPT, BPF_CGROUP_SETSOCKOPT, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
>>        SEC_DEF("struct_ops+",          STRUCT_OPS, 0, SEC_NONE),
>>        SEC_DEF("sk_lookup",            SK_LOOKUP, BPF_SK_LOOKUP, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
>> +       SEC_DEF("hid/device_event",     HID, BPF_HID_DEVICE_EVENT, SEC_ATTACHABLE_OPT | SEC_SLOPPY_PFX),
> 
> no SEC_SLOPPY_PFX for any new program type, please
> 
> 
>> };
>> 
>> #define MAX_TYPE_NAME_SIZE 32
>> @@ -10659,6 +10660,12 @@ static struct bpf_link *attach_iter(const struct bpf_program *prog, long cookie)
>>        return bpf_program__attach_iter(prog, NULL);
>> }
>> 
>> +struct bpf_link *
>> +bpf_program__attach_hid(const struct bpf_program *prog, int hid_fd)
>> +{
>> +       return bpf_program__attach_fd(prog, hid_fd, 0, "hid");
>> +}
>> +
>> struct bpf_link *bpf_program__attach(const struct bpf_program *prog)
>> {
>>        if (!prog->sec_def || !prog->sec_def->attach_fn)
>> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
>> index c8d8daad212e..f677ac0a9ede 100644
>> --- a/tools/lib/bpf/libbpf.h
>> +++ b/tools/lib/bpf/libbpf.h
>> @@ -529,6 +529,8 @@ struct bpf_iter_attach_opts {
>> LIBBPF_API struct bpf_link *
>> bpf_program__attach_iter(const struct bpf_program *prog,
>>                         const struct bpf_iter_attach_opts *opts);
>> +LIBBPF_API struct bpf_link *
>> +bpf_program__attach_hid(const struct bpf_program *prog, int hid_fd);
>> 
>> /*
>>  * Libbpf allows callers to adjust BPF programs before being loaded
>> diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
>> index 47e70c9058d9..fdc6fa743953 100644
>> --- a/tools/lib/bpf/libbpf.map
>> +++ b/tools/lib/bpf/libbpf.map
>> @@ -424,6 +424,7 @@ LIBBPF_0.6.0 {
>> LIBBPF_0.7.0 {
>>        global:
>>                bpf_btf_load;
>> +               bpf_program__attach_hid;
> 
> should go into 0.8.0

Ah, I missed this one. 

btw, bpf_xdp_attach and buddies should also go into 0.8.0, no? 

> 
>>                bpf_program__expected_attach_type;
>>                bpf_program__log_buf;
>>                bpf_program__log_level;
>> --
>> 2.35.1
>> 


^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 04/28] libbpf: add HID program type and API
  2022-03-08  1:38     ` Song Liu
@ 2022-03-08  5:52       ` Andrii Nakryiko
  0 siblings, 0 replies; 73+ messages in thread
From: Andrii Nakryiko @ 2022-03-08  5:52 UTC (permalink / raw)
  To: Song Liu
  Cc: Benjamin Tissoires, Greg KH, Jiri Kosina, Alexei Starovoitov,
	Daniel Borkmann, Andrii Nakryiko, Martin Lau, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, open list, linux-input, Networking,
	bpf, open list:KERNEL SELFTEST FRAMEWORK

On Mon, Mar 7, 2022 at 5:38 PM Song Liu <songliubraving@fb.com> wrote:
>
>
>
> > On Mar 7, 2022, at 5:30 PM, Andrii Nakryiko <andrii.nakryiko@gmail.com> wrote:
> >
> > On Fri, Mar 4, 2022 at 9:31 AM Benjamin Tissoires
> > <benjamin.tissoires@redhat.com> wrote:
> >>
> >> HID-bpf program type are needing a new SEC.
> >> To bind a hid-bpf program, we can rely on bpf_program__attach_fd()
> >> so export a new function to the API.
> >>
> >> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> >>
> >> ---
> >>
> >> changes in v2:
> >> - split the series by bpf/libbpf/hid/selftests and samples
> >> ---
> >> tools/lib/bpf/libbpf.c   | 7 +++++++
> >> tools/lib/bpf/libbpf.h   | 2 ++
> >> tools/lib/bpf/libbpf.map | 1 +
> >> 3 files changed, 10 insertions(+)
> >>
> >> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> >> index 81bf01d67671..356bbd3ad2c7 100644
> >> --- a/tools/lib/bpf/libbpf.c
> >> +++ b/tools/lib/bpf/libbpf.c
> >> @@ -8680,6 +8680,7 @@ static const struct bpf_sec_def section_defs[] = {
> >>        SEC_DEF("cgroup/setsockopt",    CGROUP_SOCKOPT, BPF_CGROUP_SETSOCKOPT, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
> >>        SEC_DEF("struct_ops+",          STRUCT_OPS, 0, SEC_NONE),
> >>        SEC_DEF("sk_lookup",            SK_LOOKUP, BPF_SK_LOOKUP, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
> >> +       SEC_DEF("hid/device_event",     HID, BPF_HID_DEVICE_EVENT, SEC_ATTACHABLE_OPT | SEC_SLOPPY_PFX),
> >
> > no SEC_SLOPPY_PFX for any new program type, please
> >
> >
> >> };
> >>
> >> #define MAX_TYPE_NAME_SIZE 32
> >> @@ -10659,6 +10660,12 @@ static struct bpf_link *attach_iter(const struct bpf_program *prog, long cookie)
> >>        return bpf_program__attach_iter(prog, NULL);
> >> }
> >>
> >> +struct bpf_link *
> >> +bpf_program__attach_hid(const struct bpf_program *prog, int hid_fd)
> >> +{
> >> +       return bpf_program__attach_fd(prog, hid_fd, 0, "hid");
> >> +}
> >> +
> >> struct bpf_link *bpf_program__attach(const struct bpf_program *prog)
> >> {
> >>        if (!prog->sec_def || !prog->sec_def->attach_fn)
> >> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> >> index c8d8daad212e..f677ac0a9ede 100644
> >> --- a/tools/lib/bpf/libbpf.h
> >> +++ b/tools/lib/bpf/libbpf.h
> >> @@ -529,6 +529,8 @@ struct bpf_iter_attach_opts {
> >> LIBBPF_API struct bpf_link *
> >> bpf_program__attach_iter(const struct bpf_program *prog,
> >>                         const struct bpf_iter_attach_opts *opts);
> >> +LIBBPF_API struct bpf_link *
> >> +bpf_program__attach_hid(const struct bpf_program *prog, int hid_fd);
> >>
> >> /*
> >>  * Libbpf allows callers to adjust BPF programs before being loaded
> >> diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map
> >> index 47e70c9058d9..fdc6fa743953 100644
> >> --- a/tools/lib/bpf/libbpf.map
> >> +++ b/tools/lib/bpf/libbpf.map
> >> @@ -424,6 +424,7 @@ LIBBPF_0.6.0 {
> >> LIBBPF_0.7.0 {
> >>        global:
> >>                bpf_btf_load;
> >> +               bpf_program__attach_hid;
> >
> > should go into 0.8.0
>
> Ah, I missed this one.
>
> btw, bpf_xdp_attach and buddies should also go into 0.8.0, no?

not really, they were released in libbpf v0.7, it's just any new
incoming API that should go into 0.8.0

>
> >
> >>                bpf_program__expected_attach_type;
> >>                bpf_program__log_buf;
> >>                bpf_program__log_level;
> >> --
> >> 2.35.1
> >>
>

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 02/28] bpf: introduce hid program type
  2022-03-08  0:56       ` Song Liu
@ 2022-03-08  9:20         ` Benjamin Tissoires
  2022-03-11 17:16           ` Benjamin Tissoires
  0 siblings, 1 reply; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-08  9:20 UTC (permalink / raw)
  To: Song Liu
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, open list, open list:HID CORE LAYER,
	Networking, bpf, linux-kselftest

On Tue, Mar 8, 2022 at 1:57 AM Song Liu <song@kernel.org> wrote:
>
> On Mon, Mar 7, 2022 at 10:39 AM Benjamin Tissoires
> <benjamin.tissoires@redhat.com> wrote:
> >
> > On Sat, Mar 5, 2022 at 1:03 AM Song Liu <song@kernel.org> wrote:
> > >
> > > On Fri, Mar 4, 2022 at 9:31 AM Benjamin Tissoires
> > > <benjamin.tissoires@redhat.com> wrote:
> > > >
> > > > HID is a protocol that could benefit from using BPF too.
> > >
> > > [...]
> > >
> > > > +#include <linux/list.h>
> > > > +#include <linux/slab.h>
> > > > +
> > > > +struct bpf_prog;
> > > > +struct bpf_prog_array;
> > > > +struct hid_device;
> > > > +
> > > > +enum bpf_hid_attach_type {
> > > > +       BPF_HID_ATTACH_INVALID = -1,
> > > > +       BPF_HID_ATTACH_DEVICE_EVENT = 0,
> > > > +       MAX_BPF_HID_ATTACH_TYPE
> > >
> > > Is it typical to have different BPF programs for different attach types?
> > > Otherwise, (different types may have similar BPF programs), maybe
> > > we can pass type as an argument to the program (shared among
> > > different types)?
> >
> > Not quite sure I am entirely following you, but I consider the various
> > attach types to be quite different and thus you can not really reuse
> > the same BPF program with 2 different attach types.
> >
> > In my view, we have 4 attach types:
> > - BPF_HID_ATTACH_DEVICE_EVENT: called whenever we receive an IRQ from
> > the given device (so this is net-like event stream)
> > - BPF_HID_ATTACH_RDESC_FIXUP: there can be only one of this type, and
> > this is called to change the device capabilities. So you can not reuse
> > the other programs for this one
> > - BPF_HID_ATTACH_USER_EVENT: called explicitly by the userspace
> > process owning the program. There we can use functions that are
> > sleeping (we are not in IRQ context), so this is also fundamentally
> > different from the 3 others.
> > - BPF_HID_ATTACH_DRIVER_EVENT: whenever the driver gets called into,
> > we get a bpf program run. This can be suspend/resume, or even specific
> > request to the device (change a feature on the device or get its
> > current state). Again, IMO fundamentally different from the others.
> >
> > So I'm open to any suggestions, but if we can keep the userspace API
> > being defined with different SEC in libbpf, that would be the best.
>
> Thanks for this information. Different attach_types sound right for the use
> case.
>
> >
> > >
> > > [...]
> > >
> > > > +struct hid_device;
> > > > +
> > > > +enum hid_bpf_event {
> > > > +       HID_BPF_UNDEF = 0,
> > > > +       HID_BPF_DEVICE_EVENT,           /* when attach type is BPF_HID_DEVICE_EVENT */
> > > > +};
> > > > +
> > > > +struct hid_bpf_ctx {
> > > > +       enum hid_bpf_event type;        /* read-only */
> > > > +       __u16 allocated_size;           /* the allocated size of data below (RO) */
> > >
> > > There is a (6-byte?) hole here.
> > >
> > > > +       struct hid_device *hdev;        /* read-only */
> > > > +
> > > > +       __u16 size;                     /* used size in data (RW) */
> > > > +       __u8 data[];                    /* data buffer (RW) */
> > > > +};
> > >
> > > Do we really need hit_bpf_ctx in uapi? Maybe we can just use it
> > > from vmlinuxh?
> >
> > I had a thought at this context today, and I think I am getting to the
> > limit of what I understand.
> >
> > My first worry is that the way I wrote it there, with a variable data
> > field length is that this is not forward compatible. Unless BTF and
> > CORE are making magic, this will bite me in the long run IMO.
> >
> > But then, you are talking about not using uapi, and I am starting to
> > wonder: am I doing the things correctly?
> >
> > To solve my first issue (and the weird API I had to introduce in the
> > bpf_hid_get/set_data), I came up to the following:
> > instead of exporting the data directly in the context, I could create
> > a helper bpf_hid_get_data_buf(ctx, const uint size) that returns a
> > RET_PTR_TO_ALLOC_MEM_OR_NULL in the same way bpf_ringbuf_reserve()
> > does.
> >
> > This way, I can directly access the fields within the bpf program
> > without having to worry about the size.
> >
> > But now, I am wondering whether the uapi I defined here is correct in
> > the way CORE works.
> >
> > My goal is to have HID-BPF programs to be CORE compatible, and not
> > have to recompile them depending on the underlying kernel.
> >
> > I can not understand right now if I need to add some other BTF helpers
> > in the same way the access to struct xdp_md and struct xdp_buff are
> > converted between one and other, or if defining a forward compatible
> > struct hid_bpf_ctx is enough.
> > As far as I understand, .convert_ctx_access allows to export a stable
> > uapi to the bpf prog users with the verifier doing the conversion
> > between the structs for me. But is this really required for all the
> > BPF programs if we want them to be CORE?
> >
> > Also, I am starting to wonder if I should not hide fields in the
> > context to the users. The .data field could be a pointer and only
> > accessed through the helper I mentioned above. This would be forward
> > compatible, and also allows to use whatever available memory in the
> > kernel to be forwarded to the BPF program. This way I can skip the
> > memcpy part and work directly with the incoming dma data buffer from
> > the IRQ.
> >
> > But is it best practice to do such a thing?
>
> I think .convert_ctx_access is the way to go if we want to access the data
> buffer without memcpy. I am not sure how much work is needed to make
> it compatible with CORE though.
>
> To make sure I understand the case, do we want something like
>
> bpf_prog(struct hid_bpf_ctx *ctx)
> {
>     /* makes sure n < ctx->size */
>     x = ctx->data[n]; /* read data */
>     ctx->data[n] = <something>; /* write data */
>     ctx->size = <something <= n>; /* change data size */
> }
>
> We also need it to be CORE, so that we may modify hid_bpf_ctx by
> inserting more members to it before data.
>
> Is this accurate?
>

Yes, you pretty much summed it all (except maybe that we might want to
have allocated_size in addition to size so we can also grow the value
of .size within the allocated limit).

All in all, what I want for HID bpf programs is to be able to read and
write an array of bytes, and change its size within an allocated
kernel limit.

This will apply to every HID bpf attach type, to the exception of some
BPF_HID_ATTACH_DRIVER_EVENT when we are receiving a suspend/resume
notification. Though in the suspend/resume case we won't have the data
array available, so it won't matter much.

I want the HID bpf programs to be CORE, but if you tell me that it
would matter only if we need to reshuffle hid_bpf_ctx, I would be fine
simply put a comment "new fields must be added at the end" like some
other definitions of contexts are doing.

Besides that, I currently do not want to allow access to the content
of struct hid_device (or any other kernel struct) in HID BPF programs.
That might be of interest at some point for debugging, but with just
the array capability I should be able to achieve all of my use cases.

Cheers,
Benjamin


^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices
  2022-03-07 18:11     ` Song Liu
@ 2022-03-08 13:37       ` Benjamin Tissoires
  0 siblings, 0 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-08 13:37 UTC (permalink / raw)
  To: Song Liu
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, open list, open list:HID CORE LAYER,
	Networking, bpf, linux-kselftest

On Mon, Mar 7, 2022 at 7:12 PM Song Liu <song@kernel.org> wrote:
>
> On Sat, Mar 5, 2022 at 2:23 AM Benjamin Tissoires
> <benjamin.tissoires@redhat.com> wrote:
> > > >
> > >
> > > The set looks good so far. I will review the rest later.
> > >
> > > [...]
> > >
> > > A quick note about how we organize these patches. Maybe we can
> > > merge some of these patches like:
> >
> > Just to be sure we are talking about the same thing: you mean squash
> > the patch together?
>
> Right, squash some patches together.
>
> >
> > >
> > > >   bpf: introduce hid program type
> > > >   bpf/hid: add a new attach type to change the report descriptor
> > > >   bpf/hid: add new BPF type to trigger commands from userspace
> > > I guess the three can merge into one.
> > >
> > > >   HID: hook up with bpf
> > > >   HID: allow to change the report descriptor from an eBPF program
> > > >   HID: bpf: compute only the required buffer size for the device
> > > >   HID: bpf: only call hid_bpf_raw_event() if a ctx is available
> > > I haven't read through all of them, but I guess they can probably merge
> > > as well.
> >
> > There are certainly patches that we could squash together (3 and 4
> > from this list into the previous ones), but I'd like to keep some sort
> > of granularity here to not have a patch bomb that gets harder to come
> > back later.
>
> Totally agreed with the granularity of patches. I am not a big fan of patch
> bombs either. :)
>
> I guess the problem I have with the current version is that I don't have a
> big picture of the design while reading through relatively big patches. A
> overview with the following information in the cover letter would be really
> help here:
>   1. How different types of programs are triggered (IRQ, user input, etc.);
>   2. What are the operations and/or outcomes of these programs;
>   3. How would programs of different types (or attach types) interact
>    with each other (via bpf maps? chaining?)
>   4. What's the new uapi;
>   5. New helpers and other logistics
>
> Sometimes, I find the changes to uapi are the key for me to understand the
> patches, and I would like to see one or two patches with all the UAPI
> changes (i.e. bpf_hid_attach_type). However, that may or may not apply to
> this set due to granularity concerns.
>
> Does this make sense?
>

It definitely does. And as I read that, I realized that if I manage to
get such a clear depiction of what HID-BPF is, it would certainly be a
good idea to paste that in a file into the Documentation directory as
well :)

I think you have a slightly better picture now with the exchanges we
are having on the individual patches, but I'll try to come out with
that description in the cover letter for v3.

Cheers,
Benjamin


^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 01/28] bpf: add new is_sys_admin_prog_type() helper
  2022-03-04 17:28 ` [PATCH bpf-next v2 01/28] bpf: add new is_sys_admin_prog_type() helper Benjamin Tissoires
  2022-03-04 23:12   ` Song Liu
@ 2022-03-09  8:27   ` Sean Young
  1 sibling, 0 replies; 73+ messages in thread
From: Sean Young @ 2022-03-09  8:27 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, linux-kernel, linux-input, netdev,
	bpf, linux-kselftest

On Fri, Mar 04, 2022 at 06:28:25PM +0100, Benjamin Tissoires wrote:
> LIRC_MODE2 does not really need net_admin capability, but only sys_admin.
> 
> Extract a new helper for it, it will be also used for the HID bpf
> implementation.
> 
> Cc: Sean Young <sean@mess.org>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>

For BPF_PROG_TYPE_LIRC_MODE2, I don't think this change will break userspace.
This is called from ir-keytable(1) which is called from udev. It should have
all the necessary permissions.

In addition, the vast majority IR decoders are non-bpf. bpf ir decoders have
very few users at the moment.

Acked-by: Sean Young <sean@mess.org>


Sean

> 
> ---
> 
> new in v2
> ---
>  kernel/bpf/syscall.c | 14 +++++++++++++-
>  1 file changed, 13 insertions(+), 1 deletion(-)
> 
> diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
> index db402ebc5570..cc570891322b 100644
> --- a/kernel/bpf/syscall.c
> +++ b/kernel/bpf/syscall.c
> @@ -2165,7 +2165,6 @@ static bool is_net_admin_prog_type(enum bpf_prog_type prog_type)
>  	case BPF_PROG_TYPE_LWT_SEG6LOCAL:
>  	case BPF_PROG_TYPE_SK_SKB:
>  	case BPF_PROG_TYPE_SK_MSG:
> -	case BPF_PROG_TYPE_LIRC_MODE2:
>  	case BPF_PROG_TYPE_FLOW_DISSECTOR:
>  	case BPF_PROG_TYPE_CGROUP_DEVICE:
>  	case BPF_PROG_TYPE_CGROUP_SOCK:
> @@ -2202,6 +2201,17 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type)
>  	}
>  }
>  
> +static bool is_sys_admin_prog_type(enum bpf_prog_type prog_type)
> +{
> +	switch (prog_type) {
> +	case BPF_PROG_TYPE_LIRC_MODE2:
> +	case BPF_PROG_TYPE_EXT: /* extends any prog */
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
>  /* last field in 'union bpf_attr' used by this command */
>  #define	BPF_PROG_LOAD_LAST_FIELD core_relo_rec_size
>  
> @@ -2252,6 +2262,8 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr)
>  		return -EPERM;
>  	if (is_perfmon_prog_type(type) && !perfmon_capable())
>  		return -EPERM;
> +	if (is_sys_admin_prog_type(type) && !capable(CAP_SYS_ADMIN))
> +		return -EPERM;
>  
>  	/* attach_prog_fd/attach_btf_obj_fd can specify fd of either bpf_prog
>  	 * or btf, we need to check which one it is
> -- 
> 2.35.1

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 12/28] bpf/hid: add hid_{get|set}_data helpers
  2022-03-05 10:47       ` Greg KH
@ 2022-03-11  0:40         ` Song Liu
  2022-03-11 15:08           ` Benjamin Tissoires
  0 siblings, 1 reply; 73+ messages in thread
From: Song Liu @ 2022-03-11  0:40 UTC (permalink / raw)
  To: Greg KH
  Cc: Benjamin Tissoires, Jiri Kosina, Alexei Starovoitov,
	Daniel Borkmann, Andrii Nakryiko, Martin KaFai Lau, Song Liu,
	Yonghong Song, John Fastabend, KP Singh, Shuah Khan,
	Dave Marchevsky, Joe Stringer, Tero Kristo, lkml,
	open list:HID CORE LAYER, Networking, bpf, linux-kselftest

On Sat, Mar 5, 2022 at 2:47 AM Greg KH <gregkh@linuxfoundation.org> wrote:
>
> On Sat, Mar 05, 2022 at 11:33:07AM +0100, Benjamin Tissoires wrote:
> > On Fri, Mar 4, 2022 at 7:41 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> > >
> > > On Fri, Mar 04, 2022 at 06:28:36PM +0100, Benjamin Tissoires wrote:
> > > > When we process an incoming HID report, it is common to have to account
> > > > for fields that are not aligned in the report. HID is using 2 helpers
> > > > hid_field_extract() and implement() to pick up any data at any offset
> > > > within the report.
> > > >
> > > > Export those 2 helpers in BPF programs so users can also rely on them.
> > > > The second net worth advantage of those helpers is that now we can
> > > > fetch data anywhere in the report without knowing at compile time the
> > > > location of it. The boundary checks are done in hid-bpf.c, to prevent
> > > > a memory leak.
> > > >
> > > > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> > > >
> > > > ---
> > > >
> > > > changes in v2:
> > > > - split the patch with libbpf and HID left outside.
> > > > ---
> > > >  include/linux/bpf-hid.h        |  4 +++
> > > >  include/uapi/linux/bpf.h       | 32 ++++++++++++++++++++
> > > >  kernel/bpf/hid.c               | 53 ++++++++++++++++++++++++++++++++++
> > > >  tools/include/uapi/linux/bpf.h | 32 ++++++++++++++++++++
> > > >  4 files changed, 121 insertions(+)
> > > >
> > > > diff --git a/include/linux/bpf-hid.h b/include/linux/bpf-hid.h
> > > > index 0c5000b28b20..69bb28523ceb 100644
> > > > --- a/include/linux/bpf-hid.h
> > > > +++ b/include/linux/bpf-hid.h
> > > > @@ -93,6 +93,10 @@ struct bpf_hid_hooks {
> > > >       int (*link_attach)(struct hid_device *hdev, enum bpf_hid_attach_type type);
> > > >       void (*link_attached)(struct hid_device *hdev, enum bpf_hid_attach_type type);
> > > >       void (*array_detached)(struct hid_device *hdev, enum bpf_hid_attach_type type);
> > > > +     int (*hid_get_data)(struct hid_device *hdev, u8 *buf, size_t buf_size,
> > > > +                         u64 offset, u32 n, u8 *data, u64 data_size);
> > > > +     int (*hid_set_data)(struct hid_device *hdev, u8 *buf, size_t buf_size,
> > > > +                         u64 offset, u32 n, u8 *data, u64 data_size);
> > > >  };
> > > >
> > > >  #ifdef CONFIG_BPF
> > > > diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> > > > index a7a8d9cfcf24..4845a20e6f96 100644
> > > > --- a/include/uapi/linux/bpf.h
> > > > +++ b/include/uapi/linux/bpf.h
> > > > @@ -5090,6 +5090,36 @@ union bpf_attr {
> > > >   *   Return
> > > >   *           0 on success, or a negative error in case of failure. On error
> > > >   *           *dst* buffer is zeroed out.
> > > > + *
> > > > + * int bpf_hid_get_data(void *ctx, u64 offset, u32 n, u8 *data, u64 size)
> > > > + *   Description
> > > > + *           Get the data of size n (in bits) at the given offset (bits) in the
> > > > + *           ctx->event.data field and store it into data.
> > > > + *
> > > > + *           if n is less or equal than 32, we can address with bit precision,
> > > > + *           the value in the buffer. However, data must be a pointer to a u32
> > > > + *           and size must be 4.
> > > > + *
> > > > + *           if n is greater than 32, offset and n must be a multiple of 8
> > > > + *           and the result is working with a memcpy internally.
> > > > + *   Return
> > > > + *           The length of data copied into data. On error, a negative value
> > > > + *           is returned.
> > > > + *
> > > > + * int bpf_hid_set_data(void *ctx, u64 offset, u32 n, u8 *data, u64 size)
> > > > + *   Description
> > > > + *           Set the data of size n (in bits) at the given offset (bits) in the
> > > > + *           ctx->event.data field.
> > > > + *
> > > > + *           if n is less or equal than 32, we can address with bit precision,
> > > > + *           the value in the buffer. However, data must be a pointer to a u32
> > > > + *           and size must be 4.
> > > > + *
> > > > + *           if n is greater than 32, offset and n must be a multiple of 8
> > > > + *           and the result is working with a memcpy internally.
> > > > + *   Return
> > > > + *           The length of data copied into ctx->event.data. On error, a negative
> > > > + *           value is returned.
> > >
> >
> > Quick answer on this one (before going deeper with the other remarks next week):
> >
> > > Wait, nevermind my reviewed-by previously, see my comment about how this
> > > might be split into 4:
> > >         bpf_hid_set_bytes()
> > >         bpf_hid_get_bytes()
> > >         bpf_hid_set_bits()
> > >         bpf_hid_get_bits()
> > >
> > > Should be easier to understand and maintain over time, right?
> >
> > Yes, definitively. I thought about adding a `bytes` suffix to the
> > function name for n > 32, but not the `bits` one, meaning the API was
> > still bunkers in my head.

Do we really need per-bit access? I was under the impression that only
one BPF program is working on a ctx/buffer at a time, so we can just do
read-modify-write at byte level, no?

Thanks,
Song

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 15/28] bpf/hid: add new BPF type to trigger commands from userspace
  2022-03-04 17:28 ` [PATCH bpf-next v2 15/28] bpf/hid: add new BPF type to trigger commands from userspace Benjamin Tissoires
@ 2022-03-11  0:46   ` Song Liu
  0 siblings, 0 replies; 73+ messages in thread
From: Song Liu @ 2022-03-11  0:46 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, open list, open list:HID CORE LAYER,
	Networking, bpf, linux-kselftest

On Fri, Mar 4, 2022 at 9:33 AM Benjamin Tissoires
<benjamin.tissoires@redhat.com> wrote:
>
> Given that we can not call bpf_hid_raw_request() from within an IRQ,
> userspace needs to have a way to communicate with the device when
> it needs.
>
> Implement a new type that the caller can run at will without being in
> an IRQ context.
>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
>
> ---
[...]

> +       if (user_size_out && data_out) {
> +               user_size_out = min3(user_size_out, (u32)ctx->size, (u32)ctx->allocated_size);
> +
> +               if (copy_to_user(data_out, ctx->data, user_size_out)) {
> +                       ret = -EFAULT;
> +                       goto unlock;
> +               }
> +
> +               if (copy_to_user(&uattr->test.data_size_out,
> +                                &user_size_out,
> +                                sizeof(user_size_out))) {
> +                       ret = -EFAULT;
> +                       goto unlock;
> +               }
> +       }
> +
> +       if (copy_to_user(&uattr->test.retval, &ctx->u.user.retval, sizeof(ctx->u.user.retval))) {
> +               ret = -EFAULT;
> +               goto unlock;

nit: this goto is not really needed.

> +       }
> +
> +unlock:
> +       kfree(ctx);
> +
> +       mutex_unlock(&bpf_hid_mutex);
> +       return ret;
> +}
[...]

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 19/28] bpf/hid: add bpf_hid_raw_request helper function
  2022-03-04 17:28 ` [PATCH bpf-next v2 19/28] bpf/hid: add bpf_hid_raw_request helper function Benjamin Tissoires
@ 2022-03-11  0:50   ` Song Liu
  0 siblings, 0 replies; 73+ messages in thread
From: Song Liu @ 2022-03-11  0:50 UTC (permalink / raw)
  To: Benjamin Tissoires
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, open list, open list:HID CORE LAYER,
	Networking, bpf, linux-kselftest

On Fri, Mar 4, 2022 at 9:35 AM Benjamin Tissoires
<benjamin.tissoires@redhat.com> wrote:
>
> When we are in a user_event context, we can talk to the device to fetch
> or set features/outputs/inputs reports.
> Add a bpf helper to do so. This helper is thus only available to
> user_events, because calling this function while in IRQ context (any
> other BPF type) is forbidden.
>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
>
> ---
>
> changes in v2:
> - split the series by bpf/libbpf/hid/selftests and samples
> ---
>  include/linux/bpf-hid.h        |  2 ++
>  include/uapi/linux/bpf.h       |  8 ++++++++
>  kernel/bpf/hid.c               | 26 ++++++++++++++++++++++++++
>  tools/include/uapi/linux/bpf.h |  8 ++++++++
>  4 files changed, 44 insertions(+)
>
> diff --git a/include/linux/bpf-hid.h b/include/linux/bpf-hid.h
> index 4cf2e99109fe..bd548f6a4a26 100644
> --- a/include/linux/bpf-hid.h
> +++ b/include/linux/bpf-hid.h
> @@ -100,6 +100,8 @@ struct bpf_hid_hooks {
>                             u64 offset, u32 n, u8 *data, u64 data_size);
>         int (*hid_set_data)(struct hid_device *hdev, u8 *buf, size_t buf_size,
>                             u64 offset, u32 n, u8 *data, u64 data_size);
> +       int (*hid_raw_request)(struct hid_device *hdev, u8 *buf, size_t size,
> +                              u8 rtype, u8 reqtype);
>  };
>
>  #ifdef CONFIG_BPF
> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> index b3063384d380..417cf1c31579 100644
> --- a/include/uapi/linux/bpf.h
> +++ b/include/uapi/linux/bpf.h
> @@ -5121,6 +5121,13 @@ union bpf_attr {
>   *     Return
>   *             The length of data copied into ctx->event.data. On error, a negative
>   *             value is returned.
> + *
> + * int bpf_hid_raw_request(void *ctx, void *buf, u64 size, u8 rtype, u8 reqtype)
> + *     Description
> + *             communicate with the HID device

I think we need more description here, e.g. what are rtype and reqtype here?


> + *     Return
> + *             0 on success.
> + *             negative value on error.
>   */
>  #define __BPF_FUNC_MAPPER(FN)          \
>         FN(unspec),                     \
> @@ -5317,6 +5324,7 @@ union bpf_attr {
>         FN(copy_from_user_task),        \
>         FN(hid_get_data),               \
>         FN(hid_set_data),               \
> +       FN(hid_raw_request),            \
>         /* */
[...]

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 12/28] bpf/hid: add hid_{get|set}_data helpers
  2022-03-11  0:40         ` Song Liu
@ 2022-03-11 15:08           ` Benjamin Tissoires
  0 siblings, 0 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-11 15:08 UTC (permalink / raw)
  To: Song Liu
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, lkml, open list:HID CORE LAYER,
	Networking, bpf, linux-kselftest

On Fri, Mar 11, 2022 at 1:41 AM Song Liu <song@kernel.org> wrote:
>
> On Sat, Mar 5, 2022 at 2:47 AM Greg KH <gregkh@linuxfoundation.org> wrote:
> >
> > On Sat, Mar 05, 2022 at 11:33:07AM +0100, Benjamin Tissoires wrote:
> > > On Fri, Mar 4, 2022 at 7:41 PM Greg KH <gregkh@linuxfoundation.org> wrote:
> > > >
> > > > On Fri, Mar 04, 2022 at 06:28:36PM +0100, Benjamin Tissoires wrote:
> > > > > When we process an incoming HID report, it is common to have to account
> > > > > for fields that are not aligned in the report. HID is using 2 helpers
> > > > > hid_field_extract() and implement() to pick up any data at any offset
> > > > > within the report.
> > > > >
> > > > > Export those 2 helpers in BPF programs so users can also rely on them.
> > > > > The second net worth advantage of those helpers is that now we can
> > > > > fetch data anywhere in the report without knowing at compile time the
> > > > > location of it. The boundary checks are done in hid-bpf.c, to prevent
> > > > > a memory leak.
> > > > >
> > > > > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> > > > >
> > > > > ---
> > > > >
> > > > > changes in v2:
> > > > > - split the patch with libbpf and HID left outside.
> > > > > ---
> > > > >  include/linux/bpf-hid.h        |  4 +++
> > > > >  include/uapi/linux/bpf.h       | 32 ++++++++++++++++++++
> > > > >  kernel/bpf/hid.c               | 53 ++++++++++++++++++++++++++++++++++
> > > > >  tools/include/uapi/linux/bpf.h | 32 ++++++++++++++++++++
> > > > >  4 files changed, 121 insertions(+)
> > > > >
> > > > > diff --git a/include/linux/bpf-hid.h b/include/linux/bpf-hid.h
> > > > > index 0c5000b28b20..69bb28523ceb 100644
> > > > > --- a/include/linux/bpf-hid.h
> > > > > +++ b/include/linux/bpf-hid.h
> > > > > @@ -93,6 +93,10 @@ struct bpf_hid_hooks {
> > > > >       int (*link_attach)(struct hid_device *hdev, enum bpf_hid_attach_type type);
> > > > >       void (*link_attached)(struct hid_device *hdev, enum bpf_hid_attach_type type);
> > > > >       void (*array_detached)(struct hid_device *hdev, enum bpf_hid_attach_type type);
> > > > > +     int (*hid_get_data)(struct hid_device *hdev, u8 *buf, size_t buf_size,
> > > > > +                         u64 offset, u32 n, u8 *data, u64 data_size);
> > > > > +     int (*hid_set_data)(struct hid_device *hdev, u8 *buf, size_t buf_size,
> > > > > +                         u64 offset, u32 n, u8 *data, u64 data_size);
> > > > >  };
> > > > >
> > > > >  #ifdef CONFIG_BPF
> > > > > diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> > > > > index a7a8d9cfcf24..4845a20e6f96 100644
> > > > > --- a/include/uapi/linux/bpf.h
> > > > > +++ b/include/uapi/linux/bpf.h
> > > > > @@ -5090,6 +5090,36 @@ union bpf_attr {
> > > > >   *   Return
> > > > >   *           0 on success, or a negative error in case of failure. On error
> > > > >   *           *dst* buffer is zeroed out.
> > > > > + *
> > > > > + * int bpf_hid_get_data(void *ctx, u64 offset, u32 n, u8 *data, u64 size)
> > > > > + *   Description
> > > > > + *           Get the data of size n (in bits) at the given offset (bits) in the
> > > > > + *           ctx->event.data field and store it into data.
> > > > > + *
> > > > > + *           if n is less or equal than 32, we can address with bit precision,
> > > > > + *           the value in the buffer. However, data must be a pointer to a u32
> > > > > + *           and size must be 4.
> > > > > + *
> > > > > + *           if n is greater than 32, offset and n must be a multiple of 8
> > > > > + *           and the result is working with a memcpy internally.
> > > > > + *   Return
> > > > > + *           The length of data copied into data. On error, a negative value
> > > > > + *           is returned.
> > > > > + *
> > > > > + * int bpf_hid_set_data(void *ctx, u64 offset, u32 n, u8 *data, u64 size)
> > > > > + *   Description
> > > > > + *           Set the data of size n (in bits) at the given offset (bits) in the
> > > > > + *           ctx->event.data field.
> > > > > + *
> > > > > + *           if n is less or equal than 32, we can address with bit precision,
> > > > > + *           the value in the buffer. However, data must be a pointer to a u32
> > > > > + *           and size must be 4.
> > > > > + *
> > > > > + *           if n is greater than 32, offset and n must be a multiple of 8
> > > > > + *           and the result is working with a memcpy internally.
> > > > > + *   Return
> > > > > + *           The length of data copied into ctx->event.data. On error, a negative
> > > > > + *           value is returned.
> > > >
> > >
> > > Quick answer on this one (before going deeper with the other remarks next week):
> > >
> > > > Wait, nevermind my reviewed-by previously, see my comment about how this
> > > > might be split into 4:
> > > >         bpf_hid_set_bytes()
> > > >         bpf_hid_get_bytes()
> > > >         bpf_hid_set_bits()
> > > >         bpf_hid_get_bits()
> > > >
> > > > Should be easier to understand and maintain over time, right?
> > >
> > > Yes, definitively. I thought about adding a `bytes` suffix to the
> > > function name for n > 32, but not the `bits` one, meaning the API was
> > > still bunkers in my head.
>
> Do we really need per-bit access? I was under the impression that only
> one BPF program is working on a ctx/buffer at a time, so we can just do
> read-modify-write at byte level, no?
>

Yes, we really need per-bit access, and yes only one BPF program is
working on a ctx/buffer at a time.

The per-bit access is a HID requirement and a much more convenient way
of accessing data in the buffer. Well, there is another advantage too
that I'll add later.

Basically, in the HID world, HW makers are trying to 'compact' the
reports their device is sending to a minimum value.

For instance, when you have a 3 buttons + wheel mouse you may need:
3 bits of information for the 3 buttons
4 bits for the wheel
16 bits for X
16 bits for Y.

This usually translates almost verbatim in the report (we can add one
bit of padding between buttons and wheel), which means that accessing
the wheel data requires the user to access the offset 4 (bits) of size
4 bits in the report.

Some HW vendors are not even bothering aligning the data, so this can
be messy from time to time with just plain byte access.
All in all, the HID report descriptor gives you that information, and
internally, the HID stack stores the offset in bits and the sizes in
bits to access them without too much trouble.

The second advantage I have with these 2 accessors is that it allows
me to not know statically the offset and size values. Because the
helper in the kernel checks them for me, I can use registers values
that are unknown to the verifier and can basically have:

```
__u64 offsetX = 0;
__u64 offsetY = 0;
__u32 sizeX = 0;
__u32 sizeY = 0;

SEC("hid/device_event")
int invert_xy(struct hid_bpf_ctx *ctx)
{
  __u16 x, y;

  if (sizeX == 16) {
    x = bpf_hid_get_bits(ctx, offsetX, sizeX);
    bpf_hid_set_bits(ctx, offsetX, -x);
  }
  if (sizeY == 16) {
    y = bpf_hid_get_bits(ctx, offsetY, sizeY);
    bpf_hid_set_bits(ctx, offsetY, -y);
  }
  return 0;
}
```

Then, I have my userspace program parse the report descriptor, set the
correct values for size{X|Y} and offset{X|Y} and I can have this
program compiled once and redistributed many times.

Cheers,
Benjamin


^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 02/28] bpf: introduce hid program type
  2022-03-08  9:20         ` Benjamin Tissoires
@ 2022-03-11 17:16           ` Benjamin Tissoires
  0 siblings, 0 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-11 17:16 UTC (permalink / raw)
  To: Song Liu
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, Tero Kristo, open list, open list:HID CORE LAYER,
	Networking, bpf, linux-kselftest

On Tue, Mar 8, 2022 at 10:20 AM Benjamin Tissoires
<benjamin.tissoires@redhat.com> wrote:
>
> On Tue, Mar 8, 2022 at 1:57 AM Song Liu <song@kernel.org> wrote:
> >
> > On Mon, Mar 7, 2022 at 10:39 AM Benjamin Tissoires
> > <benjamin.tissoires@redhat.com> wrote:
> > >
> > > On Sat, Mar 5, 2022 at 1:03 AM Song Liu <song@kernel.org> wrote:
> > > >
> > > > On Fri, Mar 4, 2022 at 9:31 AM Benjamin Tissoires
> > > > <benjamin.tissoires@redhat.com> wrote:
> > > > >
[...]
> > > > > +struct hid_bpf_ctx {
> > > > > +       enum hid_bpf_event type;        /* read-only */
> > > > > +       __u16 allocated_size;           /* the allocated size of data below (RO) */
> > > >
> > > > There is a (6-byte?) hole here.
> > > >
> > > > > +       struct hid_device *hdev;        /* read-only */
> > > > > +
> > > > > +       __u16 size;                     /* used size in data (RW) */
> > > > > +       __u8 data[];                    /* data buffer (RW) */
> > > > > +};
> > > >
> > > > Do we really need hit_bpf_ctx in uapi? Maybe we can just use it
> > > > from vmlinuxh?
> > >
> > > I had a thought at this context today, and I think I am getting to the
> > > limit of what I understand.
> > >
> > > My first worry is that the way I wrote it there, with a variable data
> > > field length is that this is not forward compatible. Unless BTF and
> > > CORE are making magic, this will bite me in the long run IMO.
> > >
> > > But then, you are talking about not using uapi, and I am starting to
> > > wonder: am I doing the things correctly?
> > >
> > > To solve my first issue (and the weird API I had to introduce in the
> > > bpf_hid_get/set_data), I came up to the following:
> > > instead of exporting the data directly in the context, I could create
> > > a helper bpf_hid_get_data_buf(ctx, const uint size) that returns a
> > > RET_PTR_TO_ALLOC_MEM_OR_NULL in the same way bpf_ringbuf_reserve()
> > > does.
> > >
> > > This way, I can directly access the fields within the bpf program
> > > without having to worry about the size.
> > >
> > > But now, I am wondering whether the uapi I defined here is correct in
> > > the way CORE works.
> > >
> > > My goal is to have HID-BPF programs to be CORE compatible, and not
> > > have to recompile them depending on the underlying kernel.
> > >
> > > I can not understand right now if I need to add some other BTF helpers
> > > in the same way the access to struct xdp_md and struct xdp_buff are
> > > converted between one and other, or if defining a forward compatible
> > > struct hid_bpf_ctx is enough.
> > > As far as I understand, .convert_ctx_access allows to export a stable
> > > uapi to the bpf prog users with the verifier doing the conversion
> > > between the structs for me. But is this really required for all the
> > > BPF programs if we want them to be CORE?
> > >
> > > Also, I am starting to wonder if I should not hide fields in the
> > > context to the users. The .data field could be a pointer and only
> > > accessed through the helper I mentioned above. This would be forward
> > > compatible, and also allows to use whatever available memory in the
> > > kernel to be forwarded to the BPF program. This way I can skip the
> > > memcpy part and work directly with the incoming dma data buffer from
> > > the IRQ.
> > >
> > > But is it best practice to do such a thing?
> >
> > I think .convert_ctx_access is the way to go if we want to access the data
> > buffer without memcpy. I am not sure how much work is needed to make
> > it compatible with CORE though.

I spent the week working on that, and I am really amazed at how smart
and simple the .convert_ctx_access is working.

With that in place, I can hide the internal fields and export
"virtual" public fields that are remapped on the fly by the kernel at
load time :)

> >
> > To make sure I understand the case, do we want something like
> >
> > bpf_prog(struct hid_bpf_ctx *ctx)
> > {
> >     /* makes sure n < ctx->size */
> >     x = ctx->data[n]; /* read data */
> >     ctx->data[n] = <something>; /* write data */
> >     ctx->size = <something <= n>; /* change data size */
> > }
> >
> > We also need it to be CORE, so that we may modify hid_bpf_ctx by
> > inserting more members to it before data.
> >
> > Is this accurate?
> >

I have been trying to implement that, based on the PTR_TO_PACKET implementation.
It works, but is kind of verbose while using it because we need to
teach the verifier about the size of the arrays.

For instance, I have the following:

int bpf_prog(struct hid_bpf_ctx *ctx)
{
  /* we need to store a pointer on the stack to store its accessible size */
  __u8 *data = ctx->data;

  if (data + 3 > ctx->data_end)
    return 0; /* EPERM, bounds check */

  data[1] = data[0] + 3;

  return 0;
}

This is OK, but it gets worse if I want to access a random offset:

__s64 offset = 0;
int bpf_prog(struct hid_bpf_ctx *ctx)
{
  __u8 *data = ctx->data;
  __u16 *x;

  /* first assign the size of data */
  if (data + 4 > ctx->data_end)
    return 0; /* EPERM, bounds check */

  /* teach the verifier the range of offset (needs to be a s64) */
  if (offset >= 0 && offset < 2) {
    x = (__u16 *)&data[offset];

    /* now store the size of x in its register */
    if (x + 2 > ctx->data_end)
      return 0;

    /* finally we can read/write the data */
    *x += 1;
  }

  return 0;
}

OTOH, I managed to define a simpler helper that allows me to return a
pointer to the internal data:

BPF_CALL_3(bpf_hid_get_data, struct hid_bpf_ctx_kern*, ctx, u64,
offset, u64, size)
  {
          if (!size)
                  return (unsigned long)0;

          if (offset + size > ctx->data_end - ctx->data)
                  return (unsigned long)0;

          return (unsigned long)(ctx->data + offset);
  }

  static const struct bpf_func_proto bpf_hid_get_data_proto = {
          .func      = bpf_hid_get_data,
          .gpl_only  = true,
          .ret_type  = RET_PTR_TO_ALLOC_MEM_OR_NULL,
          .arg1_type = ARG_PTR_TO_CTX,
          .arg2_type = ARG_ANYTHING,
          .arg3_type = ARG_CONST_ALLOC_SIZE_OR_ZERO,
  };

 Which makes the previous bpf code into:

__u64 offset = 0;
int bpf_prog(struct hid_bpf_ctx *ctx)
{
  __u16 *x = bpf_hid_get_data(ctx, offset, 2);

  if (!x)
    return 0; /* EPERM, bounds check */

  *x += 1;

  return 0;
}

The advantage of both of those solutions is that they are removing the
need for bpf_hid_{get|set}_bytes().

The second solution is much simpler in terms of usage, but it doesn't
feel "right" to have to reimplement the wheel when we should have
direct array accesses.
OTOH, the first solution with the packet implementation is bulky and
requiring users to do that for every single access of the data is IMO
way too much...

Any thoughts?

Cheers,
Benjamin


^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 03/28] HID: hook up with bpf
  2022-03-04 17:28 ` [PATCH bpf-next v2 03/28] HID: hook up with bpf Benjamin Tissoires
  2022-03-04 18:24   ` Greg KH
  2022-03-05  0:23   ` Song Liu
@ 2022-03-15 16:29   ` Tero Kristo
  2 siblings, 0 replies; 73+ messages in thread
From: Tero Kristo @ 2022-03-15 16:29 UTC (permalink / raw)
  To: Benjamin Tissoires, Greg KH, Jiri Kosina, Alexei Starovoitov,
	Daniel Borkmann, Andrii Nakryiko, Martin KaFai Lau, Song Liu,
	Yonghong Song, John Fastabend, KP Singh, Shuah Khan,
	Dave Marchevsky, Joe Stringer
  Cc: linux-kernel, linux-input, netdev, bpf, linux-kselftest

Hi Benjamin,

On 04/03/2022 19:28, Benjamin Tissoires wrote:
> Now that BPF can be compatible with HID, add the capability into HID.
> drivers/hid/hid-bpf.c takes care of the glue between bpf and HID, and
> hid-core can then inject any incoming event from the device into a BPF
> program to filter/analyze it.
>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
>
> ---
>
> changes in v2:
> - split the series by bpf/libbpf/hid/selftests and samples
> - addressed review comments from v1
> ---
>   drivers/hid/Makefile   |   1 +
>   drivers/hid/hid-bpf.c  | 157 +++++++++++++++++++++++++++++++++++++++++
>   drivers/hid/hid-core.c |  21 +++++-
>   include/linux/hid.h    |  11 +++
>   4 files changed, 187 insertions(+), 3 deletions(-)
>   create mode 100644 drivers/hid/hid-bpf.c
>
> diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
> index 6d3e630e81af..08d2d7619937 100644
> --- a/drivers/hid/Makefile
> +++ b/drivers/hid/Makefile
> @@ -4,6 +4,7 @@
>   #
>   hid-y			:= hid-core.o hid-input.o hid-quirks.o
>   hid-$(CONFIG_DEBUG_FS)		+= hid-debug.o
> +hid-$(CONFIG_BPF)		+= hid-bpf.o
>   
>   obj-$(CONFIG_HID)		+= hid.o
>   obj-$(CONFIG_UHID)		+= uhid.o
> diff --git a/drivers/hid/hid-bpf.c b/drivers/hid/hid-bpf.c
> new file mode 100644
> index 000000000000..8120e598de9f
> --- /dev/null
> +++ b/drivers/hid/hid-bpf.c
> @@ -0,0 +1,157 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + *  BPF in HID support for Linux
> + *
> + *  Copyright (c) 2022 Benjamin Tissoires
> + */
> +
> +#include <linux/filter.h>
> +#include <linux/mutex.h>
> +#include <linux/slab.h>
> +
> +#include <uapi/linux/bpf_hid.h>
> +#include <linux/hid.h>
> +
> +static int __hid_bpf_match_sysfs(struct device *dev, const void *data)
> +{
> +	struct kernfs_node *kn = dev->kobj.sd;
> +	struct kernfs_node *uevent_kn;
> +
> +	uevent_kn = kernfs_find_and_get_ns(kn, "uevent", NULL);
> +
> +	return uevent_kn == data;
> +}
> +
> +static struct hid_device *hid_bpf_fd_to_hdev(int fd)
> +{
> +	struct device *dev;
> +	struct hid_device *hdev;
> +	struct fd f = fdget(fd);
> +	struct inode *inode;
> +	struct kernfs_node *node;
> +
> +	if (!f.file) {
> +		hdev = ERR_PTR(-EBADF);
> +		goto out;
> +	}
> +
> +	inode = file_inode(f.file);
> +	node = inode->i_private;
> +
> +	dev = bus_find_device(&hid_bus_type, NULL, node, __hid_bpf_match_sysfs);
> +
> +	if (dev)
> +		hdev = to_hid_device(dev);
> +	else
> +		hdev = ERR_PTR(-EINVAL);
> +
> + out:
> +	fdput(f);
> +	return hdev;
> +}
> +
> +static int hid_bpf_link_attach(struct hid_device *hdev, enum bpf_hid_attach_type type)
> +{
> +	int err = 0;
> +
> +	switch (type) {
> +	case BPF_HID_ATTACH_DEVICE_EVENT:
> +		if (!hdev->bpf.ctx) {
> +			hdev->bpf.ctx = bpf_hid_allocate_ctx(hdev, HID_BPF_MAX_BUFFER_SIZE);
> +			if (IS_ERR(hdev->bpf.ctx)) {
> +				err = PTR_ERR(hdev->bpf.ctx);
> +				hdev->bpf.ctx = NULL;
> +			}
> +		}
> +		break;
> +	default:
> +		/* do nothing */

These cause following error:


   CC      drivers/hid/hid-bpf.o
drivers/hid/hid-bpf.c: In function ‘hid_bpf_link_attach’:
drivers/hid/hid-bpf.c:88:2: error: label at end of compound statement
    88 |  default:
       |  ^~~~~~~
drivers/hid/hid-bpf.c: In function ‘hid_bpf_link_attached’:
drivers/hid/hid-bpf.c:101:2: error: label at end of compound statement
   101 |  default:
       |  ^~~~~~~
drivers/hid/hid-bpf.c: In function ‘hid_bpf_array_detached’:
drivers/hid/hid-bpf.c:116:2: error: label at end of compound statement
   116 |  default:
       |  ^~~~~~~
make[2]: *** [scripts/Makefile.build:288: drivers/hid/hid-bpf.o] Error 1
make[1]: *** [scripts/Makefile.build:550: drivers/hid] Error 2
make[1]: *** Waiting for unfinished jobs....
make: *** [Makefile:1831: drivers] Error 2

To fix that, you need to add a break statement at end:

default:

     /* do nothing */

     break;

Same for couple of other occurrences in the file.

-Tero



^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 14/28] selftests/bpf: add tests for hid_{get|set}_data helpers
  2022-03-04 17:28 ` [PATCH bpf-next v2 14/28] selftests/bpf: add tests for hid_{get|set}_data helpers Benjamin Tissoires
@ 2022-03-15 16:49   ` Tero Kristo
  2022-03-15 17:02     ` Benjamin Tissoires
  0 siblings, 1 reply; 73+ messages in thread
From: Tero Kristo @ 2022-03-15 16:49 UTC (permalink / raw)
  To: Benjamin Tissoires, Greg KH, Jiri Kosina, Alexei Starovoitov,
	Daniel Borkmann, Andrii Nakryiko, Martin KaFai Lau, Song Liu,
	Yonghong Song, John Fastabend, KP Singh, Shuah Khan,
	Dave Marchevsky, Joe Stringer
  Cc: linux-kernel, linux-input, netdev, bpf, linux-kselftest

Hi Benjamin,

On 04/03/2022 19:28, Benjamin Tissoires wrote:
> Simple test added here, with one use of each helper.
>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
>
> ---
>
> changes in v2:
> - split the patch with libbpf left outside.
> ---
>   tools/testing/selftests/bpf/prog_tests/hid.c | 65 ++++++++++++++++++++
>   tools/testing/selftests/bpf/progs/hid.c      | 45 ++++++++++++++
>   2 files changed, 110 insertions(+)
>
> diff --git a/tools/testing/selftests/bpf/prog_tests/hid.c b/tools/testing/selftests/bpf/prog_tests/hid.c
> index 91543b8078ca..74426523dd6f 100644
> --- a/tools/testing/selftests/bpf/prog_tests/hid.c
> +++ b/tools/testing/selftests/bpf/prog_tests/hid.c
> @@ -297,6 +297,68 @@ static int test_hid_raw_event(struct hid *hid_skel, int uhid_fd, int sysfs_fd)
>   	return ret;
>   }
>   
> +/*
> + * Attach hid_set_get_data to the given uhid device,
> + * retrieve and open the matching hidraw node,
> + * inject one event in the uhid device,
> + * check that the program makes correct use of bpf_hid_{set|get}_data.
> + */
> +static int test_hid_set_get_data(struct hid *hid_skel, int uhid_fd, int sysfs_fd)
> +{
> +	int err, hidraw_ino, hidraw_fd = -1;
> +	char hidraw_path[64] = {0};
> +	u8 buf[10] = {0};
> +	int ret = -1;
> +
> +	/* attach hid_set_get_data program */
> +	hid_skel->links.hid_set_get_data =
> +		bpf_program__attach_hid(hid_skel->progs.hid_set_get_data, sysfs_fd);
> +	if (!ASSERT_OK_PTR(hid_skel->links.hid_set_get_data,
> +			   "attach_hid(hid_set_get_data)"))
> +		return PTR_ERR(hid_skel->links.hid_set_get_data);
> +
> +	hidraw_ino = get_hidraw(hid_skel->links.hid_set_get_data);
> +	if (!ASSERT_GE(hidraw_ino, 0, "get_hidraw"))
> +		goto cleanup;
> +
> +	/* open hidraw node to check the other side of the pipe */
> +	sprintf(hidraw_path, "/dev/hidraw%d", hidraw_ino);
> +	hidraw_fd = open(hidraw_path, O_RDWR | O_NONBLOCK);
> +
> +	if (!ASSERT_GE(hidraw_fd, 0, "open_hidraw"))
> +		goto cleanup;
> +
> +	/* inject one event */
> +	buf[0] = 1;
> +	buf[1] = 42;
> +	send_event(uhid_fd, buf, 6);
> +
> +	/* read the data from hidraw */
> +	memset(buf, 0, sizeof(buf));
> +	err = read(hidraw_fd, buf, sizeof(buf));
> +	if (!ASSERT_EQ(err, 6, "read_hidraw"))
> +		goto cleanup;
> +
> +	if (!ASSERT_EQ(buf[2], (42 >> 2), "hid_set_get_data"))
> +		goto cleanup;
> +
> +	if (!ASSERT_EQ(buf[3], 1, "hid_set_get_data"))
> +		goto cleanup;
> +
> +	if (!ASSERT_EQ(buf[4], 42, "hid_set_get_data"))
> +		goto cleanup;
> +
> +	ret = 0;
> +
> +cleanup:
> +	if (hidraw_fd >= 0)
> +		close(hidraw_fd);
> +
> +	hid__detach(hid_skel);
> +
> +	return ret;
> +}
> +
>   /*
>    * Attach hid_rdesc_fixup to the given uhid device,
>    * retrieve and open the matching hidraw node,
> @@ -395,6 +457,9 @@ void serial_test_hid_bpf(void)
>   	err = test_hid_raw_event(hid_skel, uhid_fd, sysfs_fd);
>   	ASSERT_OK(err, "hid");
>   
> +	err = test_hid_set_get_data(hid_skel, uhid_fd, sysfs_fd);
> +	ASSERT_OK(err, "hid_set_get_data");
> +
>   	err = test_rdesc_fixup(hid_skel, uhid_fd, sysfs_fd);
>   	ASSERT_OK(err, "hid_rdesc_fixup");
>   
> diff --git a/tools/testing/selftests/bpf/progs/hid.c b/tools/testing/selftests/bpf/progs/hid.c
> index 2270448d0d3f..de6668471940 100644
> --- a/tools/testing/selftests/bpf/progs/hid.c
> +++ b/tools/testing/selftests/bpf/progs/hid.c
> @@ -66,3 +66,48 @@ int hid_rdesc_fixup(struct hid_bpf_ctx *ctx)
>   
>   	return 0;
>   }
> +
> +SEC("hid/device_event")
> +int hid_set_get_data(struct hid_bpf_ctx *ctx)
> +{
> +	int ret;
> +	__u8 *buf;
> +
> +	buf = bpf_ringbuf_reserve(&ringbuf, 8, 0);

Ordering of patches is probably wrong, it seems the ringbuf is defined 
in patch #21 but used here.

Also, this usage of ringbuf leads into running out of available memory 
in the buffer if used for long time, it is not evident from the test 
case written here but I spent a couple of hours debugging my own BPF 
program that used ringbuf in similar way as what is done here. Basically 
the producer idx is increased with the bpf_ringbuf_reserve / discard, 
but the consumer index is not if you don't have a consumer in place.

I ended up using a global statically allocated buffer for the purpose 
for now.

-Tero


> +	if (!buf)
> +		return -12; /* -ENOMEM */
> +
> +	/* first try read/write with n > 32 */
> +	ret = bpf_hid_get_data(ctx, 0, 64, buf, 8);
> +	if (ret < 0)
> +		goto discard;
> +
> +	/* reinject it */
> +	ret = bpf_hid_set_data(ctx, 24, 64, buf, 8);
> +	if (ret < 0)
> +		goto discard;
> +
> +	/* extract data at bit offset 10 of size 4 (half a byte) */
> +	ret = bpf_hid_get_data(ctx, 10, 4, buf, 8);  /* expected to fail */
> +	if (ret > 0) {
> +		ret = -1;
> +		goto discard;
> +	}
> +
> +	ret = bpf_hid_get_data(ctx, 10, 4, buf, 4);
> +	if (ret < 0)
> +		goto discard;
> +
> +	/* reinject it */
> +	ret = bpf_hid_set_data(ctx, 16, 4, buf, 4);
> +	if (ret < 0)
> +		goto discard;
> +
> +	ret = 0;
> +
> + discard:
> +
> +	bpf_ringbuf_discard(buf, 0);
> +
> +	return ret;
> +}

^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 14/28] selftests/bpf: add tests for hid_{get|set}_data helpers
  2022-03-15 16:49   ` Tero Kristo
@ 2022-03-15 17:02     ` Benjamin Tissoires
  0 siblings, 0 replies; 73+ messages in thread
From: Benjamin Tissoires @ 2022-03-15 17:02 UTC (permalink / raw)
  To: Tero Kristo
  Cc: Greg KH, Jiri Kosina, Alexei Starovoitov, Daniel Borkmann,
	Andrii Nakryiko, Martin KaFai Lau, Song Liu, Yonghong Song,
	John Fastabend, KP Singh, Shuah Khan, Dave Marchevsky,
	Joe Stringer, linux-kernel, linux-input, netdev, bpf,
	linux-kselftest

On Tue, Mar 15, 2022 at 5:51 PM Tero Kristo <tero.kristo@linux.intel.com> wrote:
>
> Hi Benjamin,
>
> On 04/03/2022 19:28, Benjamin Tissoires wrote:
> > Simple test added here, with one use of each helper.
> >
> > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> >
> > ---
> >
> > changes in v2:
> > - split the patch with libbpf left outside.
> > ---
> >   tools/testing/selftests/bpf/prog_tests/hid.c | 65 ++++++++++++++++++++
> >   tools/testing/selftests/bpf/progs/hid.c      | 45 ++++++++++++++
> >   2 files changed, 110 insertions(+)
> >
> > diff --git a/tools/testing/selftests/bpf/prog_tests/hid.c b/tools/testing/selftests/bpf/prog_tests/hid.c
> > index 91543b8078ca..74426523dd6f 100644
> > --- a/tools/testing/selftests/bpf/prog_tests/hid.c
> > +++ b/tools/testing/selftests/bpf/prog_tests/hid.c
> > @@ -297,6 +297,68 @@ static int test_hid_raw_event(struct hid *hid_skel, int uhid_fd, int sysfs_fd)
> >       return ret;
> >   }
> >
> > +/*
> > + * Attach hid_set_get_data to the given uhid device,
> > + * retrieve and open the matching hidraw node,
> > + * inject one event in the uhid device,
> > + * check that the program makes correct use of bpf_hid_{set|get}_data.
> > + */
> > +static int test_hid_set_get_data(struct hid *hid_skel, int uhid_fd, int sysfs_fd)
> > +{
> > +     int err, hidraw_ino, hidraw_fd = -1;
> > +     char hidraw_path[64] = {0};
> > +     u8 buf[10] = {0};
> > +     int ret = -1;
> > +
> > +     /* attach hid_set_get_data program */
> > +     hid_skel->links.hid_set_get_data =
> > +             bpf_program__attach_hid(hid_skel->progs.hid_set_get_data, sysfs_fd);
> > +     if (!ASSERT_OK_PTR(hid_skel->links.hid_set_get_data,
> > +                        "attach_hid(hid_set_get_data)"))
> > +             return PTR_ERR(hid_skel->links.hid_set_get_data);
> > +
> > +     hidraw_ino = get_hidraw(hid_skel->links.hid_set_get_data);
> > +     if (!ASSERT_GE(hidraw_ino, 0, "get_hidraw"))
> > +             goto cleanup;
> > +
> > +     /* open hidraw node to check the other side of the pipe */
> > +     sprintf(hidraw_path, "/dev/hidraw%d", hidraw_ino);
> > +     hidraw_fd = open(hidraw_path, O_RDWR | O_NONBLOCK);
> > +
> > +     if (!ASSERT_GE(hidraw_fd, 0, "open_hidraw"))
> > +             goto cleanup;
> > +
> > +     /* inject one event */
> > +     buf[0] = 1;
> > +     buf[1] = 42;
> > +     send_event(uhid_fd, buf, 6);
> > +
> > +     /* read the data from hidraw */
> > +     memset(buf, 0, sizeof(buf));
> > +     err = read(hidraw_fd, buf, sizeof(buf));
> > +     if (!ASSERT_EQ(err, 6, "read_hidraw"))
> > +             goto cleanup;
> > +
> > +     if (!ASSERT_EQ(buf[2], (42 >> 2), "hid_set_get_data"))
> > +             goto cleanup;
> > +
> > +     if (!ASSERT_EQ(buf[3], 1, "hid_set_get_data"))
> > +             goto cleanup;
> > +
> > +     if (!ASSERT_EQ(buf[4], 42, "hid_set_get_data"))
> > +             goto cleanup;
> > +
> > +     ret = 0;
> > +
> > +cleanup:
> > +     if (hidraw_fd >= 0)
> > +             close(hidraw_fd);
> > +
> > +     hid__detach(hid_skel);
> > +
> > +     return ret;
> > +}
> > +
> >   /*
> >    * Attach hid_rdesc_fixup to the given uhid device,
> >    * retrieve and open the matching hidraw node,
> > @@ -395,6 +457,9 @@ void serial_test_hid_bpf(void)
> >       err = test_hid_raw_event(hid_skel, uhid_fd, sysfs_fd);
> >       ASSERT_OK(err, "hid");
> >
> > +     err = test_hid_set_get_data(hid_skel, uhid_fd, sysfs_fd);
> > +     ASSERT_OK(err, "hid_set_get_data");
> > +
> >       err = test_rdesc_fixup(hid_skel, uhid_fd, sysfs_fd);
> >       ASSERT_OK(err, "hid_rdesc_fixup");
> >
> > diff --git a/tools/testing/selftests/bpf/progs/hid.c b/tools/testing/selftests/bpf/progs/hid.c
> > index 2270448d0d3f..de6668471940 100644
> > --- a/tools/testing/selftests/bpf/progs/hid.c
> > +++ b/tools/testing/selftests/bpf/progs/hid.c
> > @@ -66,3 +66,48 @@ int hid_rdesc_fixup(struct hid_bpf_ctx *ctx)
> >
> >       return 0;
> >   }
> > +
> > +SEC("hid/device_event")
> > +int hid_set_get_data(struct hid_bpf_ctx *ctx)
> > +{
> > +     int ret;
> > +     __u8 *buf;
> > +
> > +     buf = bpf_ringbuf_reserve(&ringbuf, 8, 0);
>
> Ordering of patches is probably wrong, it seems the ringbuf is defined
> in patch #21 but used here.
>
> Also, this usage of ringbuf leads into running out of available memory
> in the buffer if used for long time, it is not evident from the test
> case written here but I spent a couple of hours debugging my own BPF
> program that used ringbuf in similar way as what is done here. Basically
> the producer idx is increased with the bpf_ringbuf_reserve / discard,
> but the consumer index is not if you don't have a consumer in place.

Oh, that's good to know.

FWIW, on v3, we won't have to use a ringbuf at all. This is a new API
break (sorry for that), but I think this will be better in the long
run.
I have chosen to use the hid_bpf_get_data() from [0]. The net
advantage is that we get a pointer to the internal buffer that can be
of any size, and we can even re-inject that same pointer into
bpf_hid_raw_request() without having to use another buffer.

I'm currently working on the documentation part and small fixes now
that the big rewrite with that implementation is now done.

Cheers,
Benjamin

>
> I ended up using a global statically allocated buffer for the purpose
> for now.
>
> -Tero
>

[0] https://lore.kernel.org/linux-input/CAO-hwJJqP5iivQZOu0LTYa1D5OuM_aVi=LH27Udc_VYkbFsrww@mail.gmail.com/


^ permalink raw reply	[flat|nested] 73+ messages in thread

* Re: [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices
  2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
                   ` (28 preceding siblings ...)
  2022-03-05  1:13 ` [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Song Liu
@ 2022-03-15 17:04 ` Tero Kristo
  29 siblings, 0 replies; 73+ messages in thread
From: Tero Kristo @ 2022-03-15 17:04 UTC (permalink / raw)
  To: Benjamin Tissoires, Greg KH, Jiri Kosina, Alexei Starovoitov,
	Daniel Borkmann, Andrii Nakryiko, Martin KaFai Lau, Song Liu,
	Yonghong Song, John Fastabend, KP Singh, Shuah Khan,
	Dave Marchevsky, Joe Stringer
  Cc: linux-kernel, linux-input, netdev, bpf, linux-kselftest

Hi Benjamin,

On 04/03/2022 19:28, Benjamin Tissoires wrote:
> Hi,
>
> This is a followup of my v1 at [0].
>
> The short summary of the previous cover letter and discussions is that
> HID could benefit from BPF for the following use cases:
>
> - simple fixup of report descriptor:
>    benefits are faster development time and testing, with the produced
>    bpf program being shipped in the kernel directly (the shipping part
>    is *not* addressed here).
>
> - Universal Stylus Interface:
>    allows a user-space program to define its own kernel interface
>
> - Surface Dial:
>    somehow similar to the previous one except that userspace can decide
>    to change the shape of the exported device
>
> - firewall:
>    still partly missing there, there is not yet interception of hidraw
>    calls, but it's coming in a followup series, I promise
>
> - tracing:
>    well, tracing.
>
>
> I tried to address as many comments as I could and here is the short log
> of changes:
>
> v2:
> ===
>
> - split the series by subsystem (bpf, HID, libbpf, selftests and
>    samples)
>
> - Added an extra patch at the beginning to not require CAP_NET_ADMIN for
>    BPF_PROG_TYPE_LIRC_MODE2 (please shout if this is wrong)
>
> - made the bpf context attached to HID program of dynamic size:
>    * the first 1 kB will be able to be addressed directly
>    * the rest can be retrieved through bpf_hid_{set|get}_data
>      (note that I am definitivey not happy with that API, because there
>      is part of it in bits and other in bytes. ouch)
>
> - added an extra patch to prevent non GPL HID bpf programs to be loaded
>    of type BPF_PROG_TYPE_HID
>    * same here, not really happy but I don't know where to put that check
>      in verifier.c
>
> - added a new flag BPF_F_INSERT_HEAD for BPF_LINK_CREATE syscall when in
>    used with HID program types.
>    * this flag is used for tracing, to be able to load a program before
>      any others that might already have been inserted and that might
>      change the data stream.
>
> Cheers,
> Benjamin

I posted a couple of comments to the series, but other than that for the 
whole series you can use:

Reviewed-by: Tero Kristo <tero.kristo@linux.intel.com>

Tested-by: Tero Kristo <tero.kristo@linux.intel.com>

I did test this with my USI-BPF program + userspace code, they work with 
few minor updates compared to previous version.

-Tero

>
>
>
> [0] https://lore.kernel.org/linux-input/20220224110828.2168231-1-benjamin.tissoires@redhat.com/T/#t
>
>
> Benjamin Tissoires (28):
>    bpf: add new is_sys_admin_prog_type() helper
>    bpf: introduce hid program type
>    HID: hook up with bpf
>    libbpf: add HID program type and API
>    selftests/bpf: add tests for the HID-bpf initial implementation
>    samples/bpf: add new hid_mouse example
>    bpf/hid: add a new attach type to change the report descriptor
>    HID: allow to change the report descriptor from an eBPF program
>    libbpf: add new attach type BPF_HID_RDESC_FIXUP
>    selftests/bpf: add report descriptor fixup tests
>    samples/bpf: add a report descriptor fixup
>    bpf/hid: add hid_{get|set}_data helpers
>    HID: bpf: implement hid_bpf_get|set_data
>    selftests/bpf: add tests for hid_{get|set}_data helpers
>    bpf/hid: add new BPF type to trigger commands from userspace
>    libbpf: add new attach type BPF_HID_USER_EVENT
>    selftests/bpf: add test for user call of HID bpf programs
>    selftests/bpf: hid: rely on uhid event to know if a test device is
>      ready
>    bpf/hid: add bpf_hid_raw_request helper function
>    HID: add implementation of bpf_hid_raw_request
>    selftests/bpf: add tests for bpf_hid_hw_request
>    bpf/verifier: prevent non GPL programs to be loaded against HID
>    HID: bpf: compute only the required buffer size for the device
>    HID: bpf: only call hid_bpf_raw_event() if a ctx is available
>    bpf/hid: Add a flag to add the program at the beginning of the list
>    libbpf: add handling for BPF_F_INSERT_HEAD in HID programs
>    selftests/bpf: Add a test for BPF_F_INSERT_HEAD
>    samples/bpf: fix bpf_program__attach_hid() api change
>
>   drivers/hid/Makefile                         |   1 +
>   drivers/hid/hid-bpf.c                        | 361 +++++++++
>   drivers/hid/hid-core.c                       |  34 +-
>   include/linux/bpf-hid.h                      | 129 +++
>   include/linux/bpf_types.h                    |   4 +
>   include/linux/hid.h                          |  25 +
>   include/uapi/linux/bpf.h                     |  59 ++
>   include/uapi/linux/bpf_hid.h                 |  50 ++
>   kernel/bpf/Makefile                          |   3 +
>   kernel/bpf/hid.c                             | 652 +++++++++++++++
>   kernel/bpf/syscall.c                         |  26 +-
>   kernel/bpf/verifier.c                        |   7 +
>   samples/bpf/.gitignore                       |   1 +
>   samples/bpf/Makefile                         |   4 +
>   samples/bpf/hid_mouse_kern.c                 |  91 +++
>   samples/bpf/hid_mouse_user.c                 | 129 +++
>   tools/include/uapi/linux/bpf.h               |  59 ++
>   tools/lib/bpf/libbpf.c                       |  22 +-
>   tools/lib/bpf/libbpf.h                       |   2 +
>   tools/lib/bpf/libbpf.map                     |   1 +
>   tools/testing/selftests/bpf/prog_tests/hid.c | 788 +++++++++++++++++++
>   tools/testing/selftests/bpf/progs/hid.c      | 216 +++++
>   22 files changed, 2649 insertions(+), 15 deletions(-)
>   create mode 100644 drivers/hid/hid-bpf.c
>   create mode 100644 include/linux/bpf-hid.h
>   create mode 100644 include/uapi/linux/bpf_hid.h
>   create mode 100644 kernel/bpf/hid.c
>   create mode 100644 samples/bpf/hid_mouse_kern.c
>   create mode 100644 samples/bpf/hid_mouse_user.c
>   create mode 100644 tools/testing/selftests/bpf/prog_tests/hid.c
>   create mode 100644 tools/testing/selftests/bpf/progs/hid.c
>

^ permalink raw reply	[flat|nested] 73+ messages in thread

end of thread, other threads:[~2022-03-15 17:07 UTC | newest]

Thread overview: 73+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-03-04 17:28 [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Benjamin Tissoires
2022-03-04 17:28 ` [PATCH bpf-next v2 01/28] bpf: add new is_sys_admin_prog_type() helper Benjamin Tissoires
2022-03-04 23:12   ` Song Liu
2022-03-05 10:07     ` Benjamin Tissoires
2022-03-05 16:58       ` Sean Young
2022-03-07 18:26         ` Song Liu
2022-03-07 18:23       ` Song Liu
2022-03-09  8:27   ` Sean Young
2022-03-04 17:28 ` [PATCH bpf-next v2 02/28] bpf: introduce hid program type Benjamin Tissoires
2022-03-04 18:21   ` Greg KH
2022-03-07 17:57     ` Benjamin Tissoires
2022-03-05  0:02   ` Song Liu
2022-03-07 18:39     ` Benjamin Tissoires
2022-03-08  0:56       ` Song Liu
2022-03-08  9:20         ` Benjamin Tissoires
2022-03-11 17:16           ` Benjamin Tissoires
2022-03-05  0:20   ` Song Liu
2022-03-04 17:28 ` [PATCH bpf-next v2 03/28] HID: hook up with bpf Benjamin Tissoires
2022-03-04 18:24   ` Greg KH
2022-03-05  0:23   ` Song Liu
2022-03-15 16:29   ` Tero Kristo
2022-03-04 17:28 ` [PATCH bpf-next v2 04/28] libbpf: add HID program type and API Benjamin Tissoires
2022-03-05  0:31   ` Song Liu
2022-03-08  1:30   ` Andrii Nakryiko
2022-03-08  1:38     ` Song Liu
2022-03-08  5:52       ` Andrii Nakryiko
2022-03-04 17:28 ` [PATCH bpf-next v2 05/28] selftests/bpf: add tests for the HID-bpf initial implementation Benjamin Tissoires
2022-03-05  0:41   ` Song Liu
2022-03-05 10:10     ` Benjamin Tissoires
2022-03-04 17:28 ` [PATCH bpf-next v2 06/28] samples/bpf: add new hid_mouse example Benjamin Tissoires
2022-03-04 18:26   ` Greg KH
2022-03-04 17:28 ` [PATCH bpf-next v2 07/28] bpf/hid: add a new attach type to change the report descriptor Benjamin Tissoires
2022-03-04 18:32   ` Greg KH
2022-03-04 17:28 ` [PATCH bpf-next v2 08/28] HID: allow to change the report descriptor from an eBPF program Benjamin Tissoires
2022-03-04 18:32   ` Greg KH
2022-03-04 17:28 ` [PATCH bpf-next v2 09/28] libbpf: add new attach type BPF_HID_RDESC_FIXUP Benjamin Tissoires
2022-03-04 17:28 ` [PATCH bpf-next v2 10/28] selftests/bpf: add report descriptor fixup tests Benjamin Tissoires
2022-03-04 18:34   ` Greg KH
2022-03-04 17:28 ` [PATCH bpf-next v2 11/28] samples/bpf: add a report descriptor fixup Benjamin Tissoires
2022-03-04 18:36   ` Greg KH
2022-03-04 17:28 ` [PATCH bpf-next v2 12/28] bpf/hid: add hid_{get|set}_data helpers Benjamin Tissoires
2022-03-04 18:37   ` Greg KH
2022-03-04 18:41   ` Greg KH
2022-03-05 10:33     ` Benjamin Tissoires
2022-03-05 10:47       ` Greg KH
2022-03-11  0:40         ` Song Liu
2022-03-11 15:08           ` Benjamin Tissoires
2022-03-04 17:28 ` [PATCH bpf-next v2 13/28] HID: bpf: implement hid_bpf_get|set_data Benjamin Tissoires
2022-03-04 18:39   ` Greg KH
2022-03-04 17:28 ` [PATCH bpf-next v2 14/28] selftests/bpf: add tests for hid_{get|set}_data helpers Benjamin Tissoires
2022-03-15 16:49   ` Tero Kristo
2022-03-15 17:02     ` Benjamin Tissoires
2022-03-04 17:28 ` [PATCH bpf-next v2 15/28] bpf/hid: add new BPF type to trigger commands from userspace Benjamin Tissoires
2022-03-11  0:46   ` Song Liu
2022-03-04 17:28 ` [PATCH bpf-next v2 16/28] libbpf: add new attach type BPF_HID_USER_EVENT Benjamin Tissoires
2022-03-04 17:28 ` [PATCH bpf-next v2 17/28] selftests/bpf: add test for user call of HID bpf programs Benjamin Tissoires
2022-03-04 17:28 ` [PATCH bpf-next v2 18/28] selftests/bpf: hid: rely on uhid event to know if a test device is ready Benjamin Tissoires
2022-03-04 17:28 ` [PATCH bpf-next v2 19/28] bpf/hid: add bpf_hid_raw_request helper function Benjamin Tissoires
2022-03-11  0:50   ` Song Liu
2022-03-04 17:28 ` [PATCH bpf-next v2 20/28] HID: add implementation of bpf_hid_raw_request Benjamin Tissoires
2022-03-04 17:28 ` [PATCH bpf-next v2 21/28] selftests/bpf: add tests for bpf_hid_hw_request Benjamin Tissoires
2022-03-04 17:28 ` [PATCH bpf-next v2 22/28] bpf/verifier: prevent non GPL programs to be loaded against HID Benjamin Tissoires
2022-03-04 17:28 ` [PATCH bpf-next v2 23/28] HID: bpf: compute only the required buffer size for the device Benjamin Tissoires
2022-03-04 17:28 ` [PATCH bpf-next v2 24/28] HID: bpf: only call hid_bpf_raw_event() if a ctx is available Benjamin Tissoires
2022-03-04 17:28 ` [PATCH bpf-next v2 25/28] bpf/hid: Add a flag to add the program at the beginning of the list Benjamin Tissoires
2022-03-04 17:28 ` [PATCH bpf-next v2 26/28] libbpf: add handling for BPF_F_INSERT_HEAD in HID programs Benjamin Tissoires
2022-03-04 17:28 ` [PATCH bpf-next v2 27/28] selftests/bpf: Add a test for BPF_F_INSERT_HEAD Benjamin Tissoires
2022-03-04 17:28 ` [PATCH bpf-next v2 28/28] samples/bpf: fix bpf_program__attach_hid() api change Benjamin Tissoires
2022-03-05  1:13 ` [PATCH bpf-next v2 00/28] Introduce eBPF support for HID devices Song Liu
2022-03-05 10:23   ` Benjamin Tissoires
2022-03-07 18:11     ` Song Liu
2022-03-08 13:37       ` Benjamin Tissoires
2022-03-15 17:04 ` Tero Kristo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).