summaryrefslogtreecommitdiff
path: root/tests/drm_read.c
diff options
context:
space:
mode:
authorChris Wilson <chris@chris-wilson.co.uk>2019-02-27 09:10:14 +0000
committerChris Wilson <chris@chris-wilson.co.uk>2019-02-27 23:24:07 +0000
commit21e45c99dc2e0439bb48703a559814de002f4ff2 (patch)
treef512ea5b9247a4094a240b138ea7d86ac2a52e5e /tests/drm_read.c
parent017d8461ae509b2d8ef5f585de8ad908081d28a0 (diff)
igt/drm_read: Exercise waking up the next waiter
Try to hit a bug in the kernel whereby a short reader does not wakeup the next waiter (on the same fd) leading to forever blocking. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk> Cc: Daniel Vetter <daniel.vetter@ffwll.ch> Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Diffstat (limited to 'tests/drm_read.c')
-rw-r--r--tests/drm_read.c87
1 files changed, 87 insertions, 0 deletions
diff --git a/tests/drm_read.c b/tests/drm_read.c
index 309f389f..cfb1c04d 100644
--- a/tests/drm_read.c
+++ b/tests/drm_read.c
@@ -43,6 +43,7 @@
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/poll.h>
+#include <pthread.h>
#include "drm.h"
IGT_TEST_DESCRIPTION("Call read(drm) and see if it behaves.");
@@ -166,6 +167,89 @@ static void test_short_buffer(int in, int nonblock, enum pipe pipe)
teardown(fd);
}
+struct short_buffer_wakeup {
+ pthread_mutex_t mutex;
+ pthread_cond_t send, recv;
+ int counter;
+ int done;
+ int fd;
+};
+
+static void *thread_short_buffer_wakeup(void *arg)
+{
+ struct short_buffer_wakeup *w = arg;
+ char buffer; /* events are typically 32 bytes */
+
+ while (!w->done) {
+ /* Short read, does not consume the event. */
+ igt_assert_eq(read(w->fd, &buffer, sizeof(buffer)), 0);
+
+ pthread_mutex_lock(&w->mutex);
+ if (!--w->counter)
+ pthread_cond_signal(&w->send);
+ pthread_cond_wait(&w->recv, &w->mutex);
+ pthread_mutex_unlock(&w->mutex);
+ }
+
+ return NULL;
+}
+
+static void test_short_buffer_wakeup(int in, enum pipe pipe)
+{
+ const int nt = sysconf(_SC_NPROCESSORS_ONLN) + 1;
+ struct short_buffer_wakeup w = {
+ .fd = setup(in, 0),
+ };
+ pthread_t t[nt];
+ char buffer[1024]; /* events are typically 32 bytes */
+
+ pthread_mutex_init(&w.mutex, NULL);
+ pthread_cond_init(&w.send, NULL);
+ pthread_cond_init(&w.recv, NULL);
+
+ for (int n = 0; n < nt; n++)
+ pthread_create(&t[n], NULL, thread_short_buffer_wakeup, &w);
+
+ igt_until_timeout(30) {
+ struct timespec tv;
+ int err = 0;
+
+ pthread_mutex_lock(&w.mutex);
+ w.counter = nt;
+ pthread_cond_broadcast(&w.recv);
+ pthread_mutex_unlock(&w.mutex);
+
+ /* Give each thread a chance to sleep in drm_read() */
+ pthread_yield();
+
+ /* One event should wake all threads as none consume */
+ generate_event(w.fd, pipe);
+
+ clock_gettime(CLOCK_REALTIME, &tv);
+ tv.tv_sec += 5; /* Let's be very generous to the scheduler */
+
+ pthread_mutex_lock(&w.mutex);
+ while (w.counter && !err)
+ err = pthread_cond_timedwait(&w.send, &w.mutex, &tv);
+ pthread_mutex_unlock(&w.mutex);
+
+ igt_assert_f(err == 0,
+ "Timed out waiting for drm_read() to wakeup on an event\n");
+
+ /* No thread should consume the event */
+ igt_assert(read(w.fd, buffer, sizeof(buffer)) > 0);
+ }
+ pthread_mutex_lock(&w.mutex);
+ w.done = true;
+ pthread_cond_broadcast(&w.recv);
+ pthread_mutex_unlock(&w.mutex);
+
+ for (int n = 0; n < nt; n++)
+ pthread_join(t[n], NULL);
+
+ close(w.fd);
+}
+
igt_main
{
int fd;
@@ -218,4 +302,7 @@ igt_main
igt_subtest("short-buffer-nonblock")
test_short_buffer(fd, 1, pipe);
+
+ igt_subtest("short-buffer-wakeup")
+ test_short_buffer_wakeup(fd, pipe);
}