All of lore.kernel.org
 help / color / mirror / Atom feed
* [libgpiod][PATCH 0/2] fix potential discarding of events by read events
@ 2020-09-12  2:22 Kent Gibson
  2020-09-12  2:22 ` [libgpiod][PATCH 1/2] tests: event: reading test coverage extended to cover reading a subset of available events Kent Gibson
  2020-09-12  2:22 ` [libgpiod][PATCH 2/2] core: fix reading " Kent Gibson
  0 siblings, 2 replies; 4+ messages in thread
From: Kent Gibson @ 2020-09-12  2:22 UTC (permalink / raw)
  To: linux-gpio, bgolaszewski; +Cc: Kent Gibson

This patch set addresses a bug where reading events can quietly discard
events.  The problem occurs any time a request made is for a number of
events that differs from the number available at the time - a number
that userspace is unaware of.

The problem is due to the read multiple always reading up to 16 events
from the kernel - independent of how many events have been requested.

The problem applies to reading both single and multiple events, as the
single event read implementation wraps the multiple.

The first patch extends test coverage to highlight the problem.
The second fixes the bug.

Kent Gibson (2):
  tests: event: reading test coverage extended to cover reading a subset
    of available events
  core: fix reading subset of available events

 lib/core.c          |   5 +-
 tests/tests-event.c | 144 ++++++++++++++++++++++++++++++++++++++------
 2 files changed, 128 insertions(+), 21 deletions(-)


base-commit: fc61e740fcbe3c6594295766759888c96c45bd29
-- 
2.28.0


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

* [libgpiod][PATCH 1/2] tests: event: reading test coverage extended to cover reading a subset of available events
  2020-09-12  2:22 [libgpiod][PATCH 0/2] fix potential discarding of events by read events Kent Gibson
@ 2020-09-12  2:22 ` Kent Gibson
  2020-09-12  3:54   ` Kent Gibson
  2020-09-12  2:22 ` [libgpiod][PATCH 2/2] core: fix reading " Kent Gibson
  1 sibling, 1 reply; 4+ messages in thread
From: Kent Gibson @ 2020-09-12  2:22 UTC (permalink / raw)
  To: linux-gpio, bgolaszewski; +Cc: Kent Gibson

Add tests for gpiod_line_event_read(), including reading multiple
entries from the kernel event kfifo, and extend the existing
read_multiple_event tests to read a subset of the available events as
well as all the available events.

Signed-off-by: Kent Gibson <warthog618@gmail.com>
---

I have removed the usleep()s between setting the pulls on the mockup as
I don't believe they are necessary.
Setting the pulls results in serial writes to the debugfs, which the
kernel should be able to deal with.
Any queuing in the kernel that results should come out in the wash.

 tests/tests-event.c | 144 ++++++++++++++++++++++++++++++++++++++------
 1 file changed, 124 insertions(+), 20 deletions(-)

diff --git a/tests/tests-event.c b/tests/tests-event.c
index e323060..c1f73d4 100644
--- a/tests/tests-event.c
+++ b/tests/tests-event.c
@@ -663,13 +663,74 @@ GPIOD_TEST_CASE(invalid_fd, 0, { 8 })
 	g_assert_cmpint(errno, ==, EINVAL);
 }
 
+GPIOD_TEST_CASE(read_event, 0, { 8 })
+{
+	g_autoptr(gpiod_chip_struct) chip = NULL;
+	struct timespec ts = { 1, 0 };
+	struct gpiod_line_event ev;
+	struct gpiod_line *line;
+	gint ret;
+	unsigned int i;
+
+	chip = gpiod_chip_open(gpiod_test_chip_path(0));
+	g_assert_nonnull(chip);
+	gpiod_test_return_if_failed();
+
+	line = gpiod_chip_get_line(chip, 7);
+	g_assert_nonnull(line);
+	gpiod_test_return_if_failed();
+
+	ret = gpiod_line_request_both_edges_events_flags(line,
+		GPIOD_TEST_CONSUMER, GPIOD_LINE_REQUEST_FLAG_BIAS_PULL_UP);
+	g_assert_cmpint(ret, ==, 0);
+
+	/* generate multiple events */
+	for (i = 0; i < 3; i++)
+		gpiod_test_chip_set_pull(0, 7, i & 1);
+
+	/* read them individually... */
+	ret = gpiod_line_event_wait(line, &ts);
+	g_assert_cmpint(ret, ==, 1);
+	if (!ret)
+		return;
+
+	ret = gpiod_line_event_read(line, &ev);
+	g_assert_cmpint(ret, ==, 0);
+
+	g_assert_cmpint(ev.event_type, ==, GPIOD_LINE_EVENT_FALLING_EDGE);
+
+	ret = gpiod_line_event_wait(line, &ts);
+	g_assert_cmpint(ret, ==, 1);
+	if (!ret)
+		return;
+
+	ret = gpiod_line_event_read(line, &ev);
+	g_assert_cmpint(ret, ==, 0);
+
+	g_assert_cmpint(ev.event_type, ==, GPIOD_LINE_EVENT_RISING_EDGE);
+
+	ret = gpiod_line_event_wait(line, &ts);
+	g_assert_cmpint(ret, ==, 1);
+	if (!ret)
+		return;
+
+	ret = gpiod_line_event_read(line, &ev);
+	g_assert_cmpint(ret, ==, 0);
+
+	g_assert_cmpint(ev.event_type, ==, GPIOD_LINE_EVENT_FALLING_EDGE);
+
+	ret = gpiod_line_event_wait(line, &ts);
+	g_assert_cmpint(ret, ==, 0);
+}
+
 GPIOD_TEST_CASE(read_multiple_events, 0, { 8 })
 {
 	g_autoptr(gpiod_chip_struct) chip = NULL;
-	struct gpiod_line_event events[3];
+	struct gpiod_line_event events[5];
 	struct timespec ts = { 1, 0 };
 	struct gpiod_line *line;
 	gint ret;
+	unsigned int i;
 
 	chip = gpiod_chip_open(gpiod_test_chip_path(0));
 	g_assert_nonnull(chip);
@@ -682,22 +743,16 @@ GPIOD_TEST_CASE(read_multiple_events, 0, { 8 })
 	ret = gpiod_line_request_both_edges_events(line, GPIOD_TEST_CONSUMER);
 	g_assert_cmpint(ret, ==, 0);
 
-	gpiod_test_chip_set_pull(0, 4, 1);
-	/*
-	 * We sleep for a short period of time here and in other test cases
-	 * for multiple events to let the kernel service each simulated
-	 * interrupt. Otherwise we'd risk triggering an interrupt while the
-	 * previous one is still being handled.
-	 */
-	usleep(10000);
-	gpiod_test_chip_set_pull(0, 4, 0);
-	usleep(10000);
-	gpiod_test_chip_set_pull(0, 4, 1);
-	usleep(10000);
+	/* generate multiple events */
+	for (i = 0; i < 7; i++)
+		gpiod_test_chip_set_pull(0, 4, !(i & 1));
 
 	ret = gpiod_line_event_wait(line, &ts);
 	g_assert_cmpint(ret, ==, 1);
+	if (!ret)
+		return;
 
+	/* read a chunk */
 	ret = gpiod_line_event_read_multiple(line, events, 3);
 	g_assert_cmpint(ret, ==, 3);
 
@@ -707,15 +762,40 @@ GPIOD_TEST_CASE(read_multiple_events, 0, { 8 })
 			GPIOD_LINE_EVENT_FALLING_EDGE);
 	g_assert_cmpint(events[2].event_type, ==,
 			GPIOD_LINE_EVENT_RISING_EDGE);
+
+	ret = gpiod_line_event_wait(line, &ts);
+	g_assert_cmpint(ret, ==, 1);
+	if (!ret)
+		return;
+
+	/*
+	 * read the remainder
+	 * - note the attempt to read more than are available
+	 */
+	ret = gpiod_line_event_read_multiple(line, events, 5);
+	g_assert_cmpint(ret, ==, 4);
+
+	g_assert_cmpint(events[0].event_type, ==,
+			GPIOD_LINE_EVENT_FALLING_EDGE);
+	g_assert_cmpint(events[1].event_type, ==,
+			GPIOD_LINE_EVENT_RISING_EDGE);
+	g_assert_cmpint(events[2].event_type, ==,
+			GPIOD_LINE_EVENT_FALLING_EDGE);
+	g_assert_cmpint(events[3].event_type, ==,
+			GPIOD_LINE_EVENT_RISING_EDGE);
+
+	ret = gpiod_line_event_wait(line, &ts);
+	g_assert_cmpint(ret, ==, 0);
 }
 
 GPIOD_TEST_CASE(read_multiple_events_fd, 0, { 8 })
 {
 	g_autoptr(gpiod_chip_struct) chip = NULL;
-	struct gpiod_line_event events[3];
+	struct gpiod_line_event events[5];
 	struct timespec ts = { 1, 0 };
 	struct gpiod_line *line;
 	gint ret, fd;
+	unsigned int i;
 
 	chip = gpiod_chip_open(gpiod_test_chip_path(0));
 	g_assert_nonnull(chip);
@@ -728,19 +808,19 @@ GPIOD_TEST_CASE(read_multiple_events_fd, 0, { 8 })
 	ret = gpiod_line_request_both_edges_events(line, GPIOD_TEST_CONSUMER);
 	g_assert_cmpint(ret, ==, 0);
 
-	gpiod_test_chip_set_pull(0, 4, 1);
-	usleep(10000);
-	gpiod_test_chip_set_pull(0, 4, 0);
-	usleep(10000);
-	gpiod_test_chip_set_pull(0, 4, 1);
-	usleep(10000);
+	/* generate multiple events */
+	for (i = 0; i < 7; i++)
+		gpiod_test_chip_set_pull(0, 4, !(i & 1));
 
 	ret = gpiod_line_event_wait(line, &ts);
 	g_assert_cmpint(ret, ==, 1);
+	if (!ret)
+		return;
 
 	fd = gpiod_line_event_get_fd(line);
 	g_assert_cmpint(fd, >=, 0);
 
+	/* read a chunk */
 	ret = gpiod_line_event_read_fd_multiple(fd, events, 3);
 	g_assert_cmpint(ret, ==, 3);
 
@@ -750,4 +830,28 @@ GPIOD_TEST_CASE(read_multiple_events_fd, 0, { 8 })
 			GPIOD_LINE_EVENT_FALLING_EDGE);
 	g_assert_cmpint(events[2].event_type, ==,
 			GPIOD_LINE_EVENT_RISING_EDGE);
+
+	ret = gpiod_line_event_wait(line, &ts);
+	g_assert_cmpint(ret, ==, 1);
+	if (!ret)
+		return;
+
+	/*
+	 * read the remainder
+	 * - note the attempt to read more than are available
+	 */
+	ret = gpiod_line_event_read_fd_multiple(fd, events, 5);
+	g_assert_cmpint(ret, ==, 4);
+
+	g_assert_cmpint(events[0].event_type, ==,
+			GPIOD_LINE_EVENT_FALLING_EDGE);
+	g_assert_cmpint(events[1].event_type, ==,
+			GPIOD_LINE_EVENT_RISING_EDGE);
+	g_assert_cmpint(events[2].event_type, ==,
+			GPIOD_LINE_EVENT_FALLING_EDGE);
+	g_assert_cmpint(events[3].event_type, ==,
+			GPIOD_LINE_EVENT_RISING_EDGE);
+
+	ret = gpiod_line_event_wait(line, &ts);
+	g_assert_cmpint(ret, ==, 0);
 }
-- 
2.28.0


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

* [libgpiod][PATCH 2/2] core: fix reading subset of available events
  2020-09-12  2:22 [libgpiod][PATCH 0/2] fix potential discarding of events by read events Kent Gibson
  2020-09-12  2:22 ` [libgpiod][PATCH 1/2] tests: event: reading test coverage extended to cover reading a subset of available events Kent Gibson
@ 2020-09-12  2:22 ` Kent Gibson
  1 sibling, 0 replies; 4+ messages in thread
From: Kent Gibson @ 2020-09-12  2:22 UTC (permalink / raw)
  To: linux-gpio, bgolaszewski; +Cc: Kent Gibson

Only read the requested number of events from the kernel rather than
reading up to 16 and quietly discarding any surplus.

The previous behavour is particularly bad for reading single events as
userspace must read the events as quickly as they arrive, effectively
negating the presence of the kernel event kfifo.

Fixes: 44921ecc9a00 (core: provide functions for reading multiple
line events at once)

Signed-off-by: Kent Gibson <warthog618@gmail.com>
---
 lib/core.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/lib/core.c b/lib/core.c
index ad76051..b964272 100644
--- a/lib/core.c
+++ b/lib/core.c
@@ -1090,7 +1090,10 @@ int gpiod_line_event_read_fd_multiple(int fd, struct gpiod_line_event *events,
 
 	memset(evdata, 0, sizeof(evdata));
 
-	rd = read(fd, evdata, sizeof(evdata));
+	if (num_events > 16)
+		num_events = 16;
+
+	rd = read(fd, evdata, num_events * sizeof(*evdata));
 	if (rd < 0) {
 		return -1;
 	} else if ((unsigned int)rd < sizeof(*evdata)) {
-- 
2.28.0


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

* Re: [libgpiod][PATCH 1/2] tests: event: reading test coverage extended to cover reading a subset of available events
  2020-09-12  2:22 ` [libgpiod][PATCH 1/2] tests: event: reading test coverage extended to cover reading a subset of available events Kent Gibson
@ 2020-09-12  3:54   ` Kent Gibson
  0 siblings, 0 replies; 4+ messages in thread
From: Kent Gibson @ 2020-09-12  3:54 UTC (permalink / raw)
  To: linux-gpio, bgolaszewski

On Sat, Sep 12, 2020 at 10:22:47AM +0800, Kent Gibson wrote:
> Add tests for gpiod_line_event_read(), including reading multiple
> entries from the kernel event kfifo, and extend the existing
> read_multiple_event tests to read a subset of the available events as
> well as all the available events.
> 
> Signed-off-by: Kent Gibson <warthog618@gmail.com>
> ---
> 
> I have removed the usleep()s between setting the pulls on the mockup as
> I don't believe they are necessary.
> Setting the pulls results in serial writes to the debugfs, which the
> kernel should be able to deal with.
> Any queuing in the kernel that results should come out in the wash.
> 

I'm obviously missing something in the path from gpio-mockup to
gpiolib, as the tests fail in slower emulated environments :-(.
I'll put out a v2 with the usleep()s restored once I finish testing
in a more diverse set of environments.

Sorry for rushing this one out.

Cheers,
Kent.


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

end of thread, other threads:[~2020-09-12  3:54 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-12  2:22 [libgpiod][PATCH 0/2] fix potential discarding of events by read events Kent Gibson
2020-09-12  2:22 ` [libgpiod][PATCH 1/2] tests: event: reading test coverage extended to cover reading a subset of available events Kent Gibson
2020-09-12  3:54   ` Kent Gibson
2020-09-12  2:22 ` [libgpiod][PATCH 2/2] core: fix reading " Kent Gibson

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.