All of lore.kernel.org
 help / color / mirror / Atom feed
From: Benjamin Tissoires <bentiss@kernel.org>
To: Alexei Starovoitov <ast@kernel.org>,
	 Daniel Borkmann <daniel@iogearbox.net>,
	 John Fastabend <john.fastabend@gmail.com>,
	 Andrii Nakryiko <andrii@kernel.org>,
	 Martin KaFai Lau <martin.lau@linux.dev>,
	 Eduard Zingerman <eddyz87@gmail.com>, Song Liu <song@kernel.org>,
	 Yonghong Song <yonghong.song@linux.dev>,
	KP Singh <kpsingh@kernel.org>,
	 Stanislav Fomichev <sdf@google.com>, Hao Luo <haoluo@google.com>,
	 Jiri Olsa <jolsa@kernel.org>, Jiri Kosina <jikos@kernel.org>,
	 Benjamin Tissoires <benjamin.tissoires@redhat.com>,
	 Jonathan Corbet <corbet@lwn.net>, Shuah Khan <shuah@kernel.org>
Cc: bpf@vger.kernel.org, linux-kernel@vger.kernel.org,
	 linux-input@vger.kernel.org, linux-doc@vger.kernel.org,
	 linux-kselftest@vger.kernel.org,
	Benjamin Tissoires <bentiss@kernel.org>
Subject: [PATCH RFC bpf-next v3 15/16] selftests/hid: add test for bpf_timer
Date: Wed, 21 Feb 2024 17:25:31 +0100	[thread overview]
Message-ID: <20240221-hid-bpf-sleepable-v3-15-1fb378ca6301@kernel.org> (raw)
In-Reply-To: <20240221-hid-bpf-sleepable-v3-0-1fb378ca6301@kernel.org>

This test checks that we can actually delay a workload in a sleepable
context through bpf_timer.

When an event is injected, we push it on a map of type queue and schedule
a work.
When that work kicks in, it pulls the event from the queue, and wakes
up userspace through a ring buffer.

The use of the ring buffer is there to not have sleeps in userspace
because we have no guarantees of the timing of when those jobs will be
called.

Signed-off-by: Benjamin Tissoires <bentiss@kernel.org>

---

changes in v3:
- amended for the new API changes in v3

new in v2
---
 tools/testing/selftests/hid/hid_bpf.c              |  83 +++++++++++
 tools/testing/selftests/hid/progs/hid.c            | 152 +++++++++++++++++++++
 .../testing/selftests/hid/progs/hid_bpf_helpers.h  |   2 +
 3 files changed, 237 insertions(+)

diff --git a/tools/testing/selftests/hid/hid_bpf.c b/tools/testing/selftests/hid/hid_bpf.c
index f825623e3edc..c16efb43dd91 100644
--- a/tools/testing/selftests/hid/hid_bpf.c
+++ b/tools/testing/selftests/hid/hid_bpf.c
@@ -875,6 +875,89 @@ TEST_F(hid_bpf, test_hid_user_raw_request_call)
 	ASSERT_EQ(args.data[1], 2);
 }
 
+static __u8 workload_data;
+
+static int handle_event(void *ctx, void *data, size_t data_sz)
+{
+	const __u8 *e = data;
+
+	workload_data = *e;
+
+	return 0;
+}
+
+TEST_F(hid_bpf, test_hid_schedule_work_defer_events_2)
+{
+	struct hid_hw_request_syscall_args args = {
+		.retval = -1,
+		.size = 10,
+	};
+	DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattrs,
+			    .ctx_in = &args,
+			    .ctx_size_in = sizeof(args),
+	);
+	const struct test_program progs[] = {
+		{ .name = "hid_defer_bpf_timer" },
+	};
+	struct ring_buffer *rb = NULL;
+	__u8 buf[10] = {0};
+	int prog_fd, err;
+
+	LOAD_PROGRAMS(progs);
+
+	/* Set up ring buffer polling */
+	rb = ring_buffer__new(bpf_map__fd(self->skel->maps.rb), handle_event, NULL, NULL);
+	ASSERT_OK_PTR(rb) TH_LOG("Failed to create ring buffer");
+	ASSERT_EQ(workload_data, 0);
+
+	args.hid = self->hid_id;
+	prog_fd = bpf_program__fd(self->skel->progs.hid_setup_timer);
+
+	err = bpf_prog_test_run_opts(prog_fd, &tattrs);
+
+	ASSERT_OK(err) TH_LOG("error while calling bpf_prog_test_run_opts");
+
+	/* check that there is no data to read from hidraw */
+	memset(buf, 0, sizeof(buf));
+	err = read(self->hidraw_fd, buf, sizeof(buf));
+	ASSERT_EQ(err, -1) TH_LOG("read_hidraw");
+
+	/* inject one event */
+	buf[0] = 1;
+	buf[1] = 47;
+	buf[2] = 50;
+	uhid_send_event(_metadata, self->uhid_fd, buf, 6);
+
+	err = ring_buffer__poll(rb, 100 /* timeout, ms */);
+	ASSERT_EQ(err, 1) TH_LOG("error while calling ring_buffer__poll");
+	ASSERT_EQ(workload_data, 3);
+
+	/* read the data from hidraw */
+	memset(buf, 0, sizeof(buf));
+	err = read(self->hidraw_fd, buf, sizeof(buf));
+	ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
+	ASSERT_EQ(buf[0], 2);
+	ASSERT_EQ(buf[1], 3);
+	ASSERT_EQ(buf[2], 4) TH_LOG("leftovers_from_previous_test");
+
+	err = ring_buffer__poll(rb, 100 /* timeout, ms */);
+	ASSERT_EQ(err, 1) TH_LOG("error while calling ring_buffer__poll");
+	ASSERT_EQ(workload_data, 4);
+
+	/* read the data from hidraw */
+	memset(buf, 0, sizeof(buf));
+	err = read(self->hidraw_fd, buf, sizeof(buf));
+	ASSERT_EQ(err, 6) TH_LOG("read_hidraw");
+	ASSERT_EQ(buf[0], 2);
+	ASSERT_EQ(buf[1], 4);
+	ASSERT_EQ(buf[2], 6);
+
+	/* read the data from hidraw */
+	memset(buf, 0, sizeof(buf));
+	err = read(self->hidraw_fd, buf, sizeof(buf));
+	ASSERT_EQ(err, -1) TH_LOG("read_hidraw");
+}
+
 /*
  * Attach hid_insert{0,1,2} to the given uhid device,
  * retrieve and open the matching hidraw node,
diff --git a/tools/testing/selftests/hid/progs/hid.c b/tools/testing/selftests/hid/progs/hid.c
index f67d35def142..7afcc77bcc29 100644
--- a/tools/testing/selftests/hid/progs/hid.c
+++ b/tools/testing/selftests/hid/progs/hid.c
@@ -250,3 +250,155 @@ int BPF_PROG(hid_test_insert3, struct hid_bpf_ctx *hid_ctx)
 
 	return 0;
 }
+
+struct test_report {
+	__u8 data[6];
+};
+
+struct {
+	__uint(type, BPF_MAP_TYPE_QUEUE);
+	__uint(max_entries, 8);
+	__type(value, struct test_report);
+} queue SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_RINGBUF);
+	__uint(max_entries, 8);
+} rb SEC(".maps");
+
+struct elem {
+	struct bpf_timer t;
+};
+
+struct {
+	__uint(type, BPF_MAP_TYPE_HASH);
+	__uint(max_entries, 1024);
+	__type(key, u32);
+	__type(value, struct elem);
+} timer_map SEC(".maps");
+
+/* callback for timer_map timers */
+
+static int timer_cb1(void *map, int *key, struct bpf_timer *timer)
+{
+	struct hid_bpf_ctx *hid_ctx;
+	struct test_report buf;
+	__u8 *rb_elem;
+	int err;
+	int i, ret = 0;
+
+	/* do not pop the event, it'll be done in hid_offload_test() when
+	 * notifying user space, this also allows to retry sending it
+	 * if hid_bpf_input_report fails
+	 */
+	if (bpf_map_peek_elem(&queue, &buf))
+		return 0;
+
+	hid_ctx = hid_bpf_allocate_context(*key);
+	if (!hid_ctx)
+		return 0; /* EPERM check */
+
+	buf.data[0] = 2;
+
+	/* re-inject the modified event into the HID stack */
+	err = hid_bpf_input_report(hid_ctx, HID_INPUT_REPORT, buf.data, sizeof(buf.data));
+	if (err == -16 /* -EBUSY */) {
+		/*
+		 * This happens when we schedule the work with a 0 delay:
+		 * the thread immediately starts but the current input
+		 * processing hasn't finished yet. So the semaphore is
+		 * already taken, and hid_input_report returns -EBUSY
+		 */
+		/* schedule another attempt */
+		bpf_timer_start(timer, 0, BPF_F_TIMER_SLEEPABLE);
+
+		goto out;
+	}
+
+	if (bpf_map_pop_elem(&queue, &buf))
+		goto out;
+
+	rb_elem = bpf_ringbuf_reserve(&rb, sizeof(*rb_elem), 0);
+	if (!rb_elem)
+		goto out;
+
+	*rb_elem = buf.data[1];
+
+	bpf_ringbuf_submit(rb_elem, 0);
+
+	/* call ourself once again until there is no more events in the queue */
+	bpf_timer_start(timer, 0, BPF_F_TIMER_SLEEPABLE);
+
+ out:
+	hid_bpf_release_context(hid_ctx);
+	return 0;
+}
+
+#define CLOCK_MONOTONIC		1
+
+SEC("?fmod_ret/hid_bpf_device_event")
+int BPF_PROG(hid_defer_bpf_timer, struct hid_bpf_ctx *hctx)
+{
+	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 4 /* size */);
+	struct test_report buf = {
+		.data = {2, 3, 4, 5, 6, 7},
+	};
+	struct bpf_timer *timer;
+	int key = hctx->hid->id;
+	struct elem init = {};
+
+	if (!data)
+		return 0; /* EPERM check */
+
+	/* Only schedule a delayed work when reportID is 1, otherwise
+	 * simply forward it to hidraw
+	 */
+	if (data[0] != 1)
+		return 0;
+
+	bpf_map_push_elem(&queue, &buf, BPF_ANY);
+	buf.data[0] = 2;
+	buf.data[1] = 4;
+	buf.data[2] = 6;
+	bpf_map_push_elem(&queue, &buf, BPF_ANY);
+
+	timer = bpf_map_lookup_elem(&timer_map, &key);
+	if (!timer)
+		return 3;
+
+	bpf_timer_set_sleepable_cb(timer, timer_cb1);
+
+	if (bpf_timer_start(timer, 0, BPF_F_TIMER_SLEEPABLE) != 0)
+		return 2;
+
+	return -1; /* discard the event */
+}
+
+SEC("syscall")
+int hid_setup_timer(struct hid_hw_request_syscall_args *args)
+{
+	struct hid_bpf_ctx *ctx;
+	struct bpf_timer *timer;
+	struct elem init = {};
+	int key = args->hid;
+	int i, ret = 0;
+
+	ctx = hid_bpf_allocate_context(args->hid);
+	if (!ctx)
+		return -1; /* EPERM check */
+
+	bpf_map_update_elem(&timer_map, &key, &init, 0);
+
+	timer = bpf_map_lookup_elem(&timer_map, &key);
+	if (!timer) {
+		hid_bpf_release_context(ctx);
+		return 1;
+	}
+
+	bpf_timer_init(timer, &timer_map, CLOCK_MONOTONIC);
+
+	hid_bpf_release_context(ctx);
+
+	return 0;
+}
+
diff --git a/tools/testing/selftests/hid/progs/hid_bpf_helpers.h b/tools/testing/selftests/hid/progs/hid_bpf_helpers.h
index 9cd56821d0f1..8235a28e7dee 100644
--- a/tools/testing/selftests/hid/progs/hid_bpf_helpers.h
+++ b/tools/testing/selftests/hid/progs/hid_bpf_helpers.h
@@ -100,5 +100,7 @@ extern int hid_bpf_input_report(struct hid_bpf_ctx *ctx,
 				enum hid_report_type type,
 				__u8 *data,
 				size_t buf__sz) __ksym;
+extern int bpf_timer_set_sleepable_cb(struct bpf_timer *timer,
+		int (callback_fn)(void *map, int *key, struct bpf_timer *timer)) __ksym;
 
 #endif /* __HID_BPF_HELPERS_H */

-- 
2.43.0


  parent reply	other threads:[~2024-02-21 16:26 UTC|newest]

Thread overview: 40+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-02-21 16:25 [PATCH RFC bpf-next v3 00/16] sleepable bpf_timer (was: allow HID-BPF to do device IOs) Benjamin Tissoires
2024-02-21 16:25 ` [PATCH RFC bpf-next v3 01/16] bpf/verifier: allow more maps in sleepable bpf programs Benjamin Tissoires
2024-02-21 16:25 ` [PATCH RFC bpf-next v3 02/16] bpf/verifier: introduce in_sleepable() helper Benjamin Tissoires
2024-02-23  1:56   ` Alexei Starovoitov
2024-02-23 19:46     ` Benjamin Tissoires
2024-02-21 16:25 ` [PATCH RFC bpf-next v3 03/16] bpf/verifier: add is_async_callback_calling_insn() helper Benjamin Tissoires
2024-02-21 16:25 ` [PATCH RFC bpf-next v3 04/16] bpf/helpers: introduce sleepable bpf_timers Benjamin Tissoires
2024-02-22  8:05   ` Benjamin Tissoires
2024-02-22 11:50   ` Toke Høiland-Jørgensen
2024-02-22 20:47   ` Alexei Starovoitov
2024-02-22 22:40   ` Eduard Zingerman
2024-02-27 14:27     ` Benjamin Tissoires
2024-02-21 16:25 ` [PATCH RFC bpf-next v3 05/16] bpf/verifier: add bpf_timer as a kfunc capable type Benjamin Tissoires
2024-02-23  0:22   ` Eduard Zingerman
2024-02-23  0:26     ` Eduard Zingerman
2024-02-23 14:54   ` Eduard Zingerman
2024-02-21 16:25 ` [PATCH RFC bpf-next v3 06/16] bpf/helpers: introduce bpf_timer_set_sleepable_cb() kfunc Benjamin Tissoires
2024-02-22 20:53   ` Alexei Starovoitov
2024-02-23 15:10   ` Eduard Zingerman
2024-02-21 16:25 ` [PATCH RFC bpf-next v3 07/16] bpf/helpers: mark the callback of bpf_timer_set_sleepable_cb() as sleepable Benjamin Tissoires
2024-02-23 15:35   ` Eduard Zingerman
2024-02-21 16:25 ` [PATCH RFC bpf-next v3 08/16] bpf/verifier: do_misc_fixups for is_bpf_timer_set_sleepable_cb_kfunc Benjamin Tissoires
2024-02-23 16:00   ` Eduard Zingerman
2024-02-27 16:18     ` Benjamin Tissoires
2024-02-27 16:36       ` Eduard Zingerman
2024-02-27 16:51         ` Benjamin Tissoires
2024-02-28  1:49           ` Alexei Starovoitov
2024-02-28 11:01             ` Benjamin Tissoires
2024-02-21 16:25 ` [PATCH RFC bpf-next v3 09/16] HID: bpf/dispatch: regroup kfuncs definitions Benjamin Tissoires
2024-02-22 20:17   ` Eduard Zingerman
2024-02-23 19:44     ` Benjamin Tissoires
2024-02-21 16:25 ` [PATCH RFC bpf-next v3 10/16] HID: bpf: export hid_hw_output_report as a BPF kfunc Benjamin Tissoires
2024-02-21 16:25 ` [PATCH RFC bpf-next v3 11/16] selftests/hid: Add test for hid_bpf_hw_output_report Benjamin Tissoires
2024-02-21 16:25 ` [PATCH RFC bpf-next v3 12/16] HID: bpf: allow to inject HID event from BPF Benjamin Tissoires
2024-02-21 16:25 ` [PATCH RFC bpf-next v3 13/16] selftests/hid: add tests for hid_bpf_input_report Benjamin Tissoires
2024-02-21 16:25 ` [PATCH RFC bpf-next v3 14/16] HID: bpf: allow to use bpf_timer_set_sleepable_cb() in tracing callbacks Benjamin Tissoires
2024-02-21 16:25 ` Benjamin Tissoires [this message]
2024-02-21 16:25 ` [PATCH RFC bpf-next v3 16/16] selftests/hid: add KASAN to the VM tests Benjamin Tissoires
2024-02-23 16:19 ` [PATCH RFC bpf-next v3 00/16] sleepable bpf_timer (was: allow HID-BPF to do device IOs) Eduard Zingerman
2024-02-23 19:42   ` Benjamin Tissoires

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20240221-hid-bpf-sleepable-v3-15-1fb378ca6301@kernel.org \
    --to=bentiss@kernel.org \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=benjamin.tissoires@redhat.com \
    --cc=bpf@vger.kernel.org \
    --cc=corbet@lwn.net \
    --cc=daniel@iogearbox.net \
    --cc=eddyz87@gmail.com \
    --cc=haoluo@google.com \
    --cc=jikos@kernel.org \
    --cc=john.fastabend@gmail.com \
    --cc=jolsa@kernel.org \
    --cc=kpsingh@kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=martin.lau@linux.dev \
    --cc=sdf@google.com \
    --cc=shuah@kernel.org \
    --cc=song@kernel.org \
    --cc=yonghong.song@linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.