blob: f8bf17e1f8ad74a03222e4fca833663ed67c45ee [file] [log] [blame]
// SPDX-License-Identifier: MIT
// Copyright © 2023 Simon Ser
#include <fcntl.h>
#include <poll.h>
#include <stdint.h>
#include <sys/eventfd.h>
#include "drm.h"
#include "igt.h"
#include "igt_syncobj.h"
#include "sw_sync.h"
/**
* TEST: syncobj eventfd
* Description: Tests for the drm sync object eventfd API
* Category: Core
* Mega feature: General Core features
* Sub-category: DRM
* Functionality: semaphore
* Feature: synchronization
* Test category: GEM_Legacy
*/
IGT_TEST_DESCRIPTION("Tests for the drm sync object eventfd API");
static bool
has_syncobj_eventfd(int fd)
{
uint64_t value;
int ret;
if (drmGetCap(fd, DRM_CAP_SYNCOBJ_TIMELINE, &value))
return false;
if (!value)
return false;
/* Try waiting with invalid syncobj should fail with ENOENT */
ret = __syncobj_eventfd(fd, 0, 0, 0, -1);
return ret == -ENOENT;
}
static int
syncobj_attach_sw_sync(int fd, uint32_t handle, uint64_t point)
{
int timeline, fence;
uint32_t syncobj;
timeline = sw_sync_timeline_create();
fence = sw_sync_timeline_create_fence(timeline, 1);
if (point == 0) {
syncobj_import_sync_file(fd, handle, fence);
} else {
syncobj = syncobj_create(fd, 0);
syncobj_import_sync_file(fd, syncobj, fence);
syncobj_binary_to_timeline(fd, handle, point, syncobj);
syncobj_destroy(fd, syncobj);
}
close(fence);
return timeline;
}
static int
ev_fd_read(int ev_fd)
{
uint64_t ev_fd_value;
int ret;
ret = read(ev_fd, &ev_fd_value, sizeof(ev_fd_value));
if (ret == -1)
return -errno;
igt_assert_eq(ret, sizeof(ev_fd_value));
return 0;
}
static void
ev_fd_poll_in(int ev_fd, bool avail)
{
struct pollfd pollfd;
int ret;
int timeout_ms;
/* Wait 5s if we're expecting data, 10ms otherwise */
timeout_ms = avail ? 5000 : 10;
pollfd.fd = ev_fd;
pollfd.events = POLLIN;
pollfd.revents = 0;
ret = poll(&pollfd, 1, timeout_ms);
if (avail) {
igt_assert(ret >= 0);
igt_assert(pollfd.revents & POLLIN);
} else {
igt_assert_eq(ret, 0);
}
}
static void
ev_fd_assert_unsignaled(int ev_fd)
{
/* Poll the eventfd to give the kernel time to signal it, error out if
* that happens */
ev_fd_poll_in(ev_fd, false);
igt_assert_eq(ev_fd_read(ev_fd), -EAGAIN);
}
static void
ev_fd_assert_signaled(int ev_fd)
{
ev_fd_poll_in(ev_fd, true);
igt_assert_eq(ev_fd_read(ev_fd), 0);
}
static const char test_bad_flags_desc[] =
"Verifies that passing bad flags is rejected";
static void
test_bad_flags(int fd)
{
uint32_t flags;
uint32_t syncobj;
int ev_fd;
syncobj = syncobj_create(fd, DRM_SYNCOBJ_CREATE_SIGNALED);
flags = 0xdeadbeef;
ev_fd = eventfd(0, EFD_NONBLOCK);
igt_assert_eq(__syncobj_eventfd(fd, syncobj, 0, flags, ev_fd), -EINVAL);
close(ev_fd);
syncobj_destroy(fd, syncobj);
}
static const char test_illegal_handle_desc[] =
"Verifies that passing an invalid syncobj handle is rejected";
static void
test_illegal_handle(int fd)
{
int ev_fd;
ev_fd = eventfd(0, EFD_NONBLOCK);
igt_assert_eq(__syncobj_eventfd(fd, 0, 0, 0, ev_fd), -ENOENT);
close(ev_fd);
}
static const char test_illegal_eventfd_desc[] =
"Verifies that passing an invalid eventfd is rejected";
static void
test_illegal_eventfd(int fd)
{
int dev_null;
uint32_t syncobj;
syncobj = syncobj_create(fd, DRM_SYNCOBJ_CREATE_SIGNALED);
dev_null = open("/dev/null", O_RDWR);
igt_assert(dev_null >= 0);
igt_assert_eq(__syncobj_eventfd(fd, syncobj, 0, 0, dev_null), -EINVAL);
close(dev_null);
syncobj_destroy(fd, syncobj);
}
static const char test_bad_pad_desc[] =
"Verifies that passing a non-zero padding is rejected";
static void
test_bad_pad(int fd)
{
struct drm_syncobj_eventfd args;
int ret;
args.handle = syncobj_create(fd, DRM_SYNCOBJ_CREATE_SIGNALED);
args.flags = 0;
args.point = 0;
args.fd = eventfd(0, EFD_NONBLOCK);
args.pad = 0xdeadbeef;
ret = drmIoctl(fd, DRM_IOCTL_SYNCOBJ_EVENTFD, &args);
igt_assert(ret == -1 && errno == EINVAL);
}
static const char test_wait_desc[] =
"Verifies waiting an already-materialized fence";
static void
test_wait(int fd, bool use_timeline)
{
uint32_t syncobj;
int timeline, ev_fd_wait, ev_fd_avail;
uint64_t point = use_timeline ? 1 : 0;
syncobj = syncobj_create(fd, 0);
timeline = syncobj_attach_sw_sync(fd, syncobj, point);
ev_fd_wait = eventfd(0, EFD_NONBLOCK);
ev_fd_avail = eventfd(0, EFD_NONBLOCK);
syncobj_eventfd(fd, syncobj, point, 0, ev_fd_wait);
syncobj_eventfd(fd, syncobj, point, DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE,
ev_fd_avail);
ev_fd_assert_unsignaled(ev_fd_wait);
ev_fd_assert_signaled(ev_fd_avail);
sw_sync_timeline_inc(timeline, 1);
ev_fd_assert_signaled(ev_fd_wait);
close(ev_fd_wait);
close(ev_fd_avail);
close(timeline);
syncobj_destroy(fd, syncobj);
}
static const char test_wait_before_signal_desc[] =
"Verifies waiting a fence not yet materialized";
static void
test_wait_before_signal(int fd, bool use_timeline)
{
uint32_t syncobj;
int timeline, ev_fd_wait, ev_fd_avail;
uint64_t point = use_timeline ? 1 : 0;
syncobj = syncobj_create(fd, 0);
ev_fd_wait = eventfd(0, EFD_NONBLOCK);
ev_fd_avail = eventfd(0, EFD_NONBLOCK);
syncobj_eventfd(fd, syncobj, point, 0, ev_fd_wait);
syncobj_eventfd(fd, syncobj, point, DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE,
ev_fd_avail);
ev_fd_assert_unsignaled(ev_fd_wait);
ev_fd_assert_unsignaled(ev_fd_avail);
timeline = syncobj_attach_sw_sync(fd, syncobj, point);
ev_fd_assert_unsignaled(ev_fd_wait);
ev_fd_assert_signaled(ev_fd_avail);
sw_sync_timeline_inc(timeline, 1);
ev_fd_assert_signaled(ev_fd_wait);
close(ev_fd_wait);
close(ev_fd_avail);
close(timeline);
syncobj_destroy(fd, syncobj);
}
static const char test_wait_signaled_desc[] =
"Verifies waiting an already-signaled fence";
static void
test_wait_signaled(int fd, bool use_timeline)
{
uint32_t syncobj;
int timeline, ev_fd_wait, ev_fd_avail;
uint64_t point = use_timeline ? 1 : 0;
syncobj = syncobj_create(fd, 0);
ev_fd_wait = eventfd(0, EFD_NONBLOCK);
ev_fd_avail = eventfd(0, EFD_NONBLOCK);
timeline = syncobj_attach_sw_sync(fd, syncobj, point);
sw_sync_timeline_inc(timeline, 1);
syncobj_eventfd(fd, syncobj, point, 0, ev_fd_wait);
syncobj_eventfd(fd, syncobj, point, DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE,
ev_fd_avail);
ev_fd_assert_signaled(ev_fd_wait);
ev_fd_assert_signaled(ev_fd_avail);
close(ev_fd_wait);
close(ev_fd_avail);
close(timeline);
syncobj_destroy(fd, syncobj);
}
igt_main
{
int fd = -1, i;
igt_fixture {
fd = drm_open_driver(DRIVER_ANY);
igt_require(has_syncobj_eventfd(fd));
igt_require_sw_sync();
}
igt_describe(test_bad_flags_desc);
igt_subtest("invalid-bad-flags")
test_bad_flags(fd);
igt_describe(test_illegal_handle_desc);
igt_subtest("invalid-illegal-handle")
test_illegal_handle(fd);
igt_describe(test_illegal_eventfd_desc);
igt_subtest("invalid-illegal-eventfd")
test_illegal_eventfd(fd);
igt_describe(test_bad_pad_desc);
igt_subtest("invalid-bad-pad")
test_bad_pad(fd);
for (i = 0; i < 2; i++) {
bool use_timeline = i == 1;
const char *kind = use_timeline ? "timeline" : "binary";
igt_describe(test_wait_desc);
igt_subtest_f("%s-wait", kind)
test_wait(fd, use_timeline);
igt_describe(test_wait_before_signal_desc);
igt_subtest_f("%s-wait-before-signal", kind)
test_wait_before_signal(fd, use_timeline);
igt_describe(test_wait_signaled_desc);
igt_subtest_f("%s-wait-signaled", kind)
test_wait_signaled(fd, use_timeline);
}
igt_fixture {
drm_close_driver(fd);
}
}