904 lines
24 KiB
C
904 lines
24 KiB
C
/*
|
|
* Copyright © 2016 Collabora, Ltd.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the next
|
|
* paragraph) shall be included in all copies or substantial portions of the
|
|
* Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
* IN THE SOFTWARE.
|
|
*
|
|
* Authors:
|
|
* Robert Foss <robert.foss@collabora.com>
|
|
*/
|
|
|
|
#include <pthread.h>
|
|
#include <semaphore.h>
|
|
#include <stdatomic.h>
|
|
#include <stdint.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include "igt.h"
|
|
#include "igt_aux.h"
|
|
#include "igt_primes.h"
|
|
|
|
#include "sw_sync.h"
|
|
|
|
|
|
IGT_TEST_DESCRIPTION("Test SW Sync Framework");
|
|
|
|
typedef struct {
|
|
int timeline;
|
|
uint32_t thread_id;
|
|
_Atomic(uint32_t) *counter;
|
|
sem_t *sem;
|
|
} data_t;
|
|
|
|
static void test_alloc_timeline(void)
|
|
{
|
|
int timeline;
|
|
|
|
timeline = sw_sync_timeline_create();
|
|
close(timeline);
|
|
}
|
|
|
|
static void test_alloc_fence(void)
|
|
{
|
|
int in_fence;
|
|
int timeline;
|
|
|
|
timeline = sw_sync_timeline_create();
|
|
in_fence = sw_sync_timeline_create_fence(timeline, 0);
|
|
|
|
close(in_fence);
|
|
close(timeline);
|
|
}
|
|
|
|
static void test_alloc_fence_invalid_timeline(void)
|
|
{
|
|
igt_assert_f(__sw_sync_timeline_create_fence(-1, 0) < 0,
|
|
"Did not fail to create fence on invalid timeline\n");
|
|
}
|
|
|
|
static void test_timeline_closed(void)
|
|
{
|
|
int fence;
|
|
int timeline;
|
|
|
|
timeline = sw_sync_timeline_create();
|
|
fence = sw_sync_timeline_create_fence(timeline, 1);
|
|
|
|
close(timeline);
|
|
igt_assert_f(sync_fence_wait(fence, 0) == 0,
|
|
"Failure waiting on unsignaled fence on closed timeline\n");
|
|
igt_assert_f(sync_fence_status(fence) == -ENOENT,
|
|
"Failure in marking up an unsignaled fence on closed timeline\n");
|
|
}
|
|
|
|
static void test_timeline_closed_signaled(void)
|
|
{
|
|
int fence;
|
|
int timeline;
|
|
|
|
timeline = sw_sync_timeline_create();
|
|
fence = sw_sync_timeline_create_fence(timeline, 1);
|
|
|
|
sw_sync_timeline_inc(timeline, 1);
|
|
close(timeline);
|
|
igt_assert_f(sync_fence_wait(fence, 0) == 0,
|
|
"Failure waiting on signaled fence for closed timeline\n");
|
|
}
|
|
|
|
static void test_alloc_merge_fence(void)
|
|
{
|
|
int in_fence[2];
|
|
int fence_merge;
|
|
int timeline[2];
|
|
|
|
timeline[0] = sw_sync_timeline_create();
|
|
timeline[1] = sw_sync_timeline_create();
|
|
|
|
in_fence[0] = sw_sync_timeline_create_fence(timeline[0], 1);
|
|
in_fence[1] = sw_sync_timeline_create_fence(timeline[1], 1);
|
|
fence_merge = sync_fence_merge(in_fence[1], in_fence[0]);
|
|
|
|
close(in_fence[0]);
|
|
close(in_fence[1]);
|
|
close(fence_merge);
|
|
close(timeline[0]);
|
|
close(timeline[1]);
|
|
}
|
|
|
|
static void test_sync_busy(void)
|
|
{
|
|
int fence;
|
|
int timeline;
|
|
int seqno;
|
|
|
|
timeline = sw_sync_timeline_create();
|
|
fence = sw_sync_timeline_create_fence(timeline, 5);
|
|
|
|
/* Make sure that fence has not been signaled yet */
|
|
igt_assert_f(sync_fence_wait(fence, 0) == -ETIME,
|
|
"Fence signaled early (timeline value 0, fence seqno 5)\n");
|
|
|
|
/* Advance timeline from 0 -> 1 */
|
|
sw_sync_timeline_inc(timeline, 1);
|
|
|
|
/* Make sure that fence has not been signaled yet */
|
|
igt_assert_f(sync_fence_wait(fence, 0) == -ETIME,
|
|
"Fence signaled early (timeline value 1, fence seqno 5)\n");
|
|
|
|
/* Advance timeline from 1 -> 5: signaling the fence (seqno 5)*/
|
|
sw_sync_timeline_inc(timeline, 4);
|
|
igt_assert_f(sync_fence_wait(fence, 0) == 0,
|
|
"Fence not signaled (timeline value 5, fence seqno 5)\n");
|
|
|
|
/* Go even further, and confirm wait still succeeds */
|
|
sw_sync_timeline_inc(timeline, 5);
|
|
igt_assert_f(sync_fence_wait(fence, 0) == 0,
|
|
"Fence not signaled (timeline value 10, fence seqno 5)\n");
|
|
|
|
seqno = 10;
|
|
for_each_prime_number(prime, 100) {
|
|
int fence_prime;
|
|
seqno += prime;
|
|
|
|
fence_prime = sw_sync_timeline_create_fence(timeline, seqno);
|
|
sw_sync_timeline_inc(timeline, prime);
|
|
|
|
igt_assert_f(sync_fence_wait(fence_prime, 0) == 0,
|
|
"Fence not signaled during test of prime timeline increments\n");
|
|
close(fence_prime);
|
|
}
|
|
|
|
close(fence);
|
|
close(timeline);
|
|
}
|
|
|
|
static void test_sync_busy_fork_unixsocket(void)
|
|
{
|
|
int fence;
|
|
int timeline;
|
|
int sv[2];
|
|
|
|
igt_require(socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) == 0);
|
|
|
|
timeline = sw_sync_timeline_create();
|
|
fence = sw_sync_timeline_create_fence(timeline, 1);
|
|
|
|
igt_fork(child, 1) {
|
|
/* Child process */
|
|
int socket = sv[1];
|
|
int socket_timeline;
|
|
struct msghdr msg = {0};
|
|
struct cmsghdr *cmsg;
|
|
unsigned char *data;
|
|
char m_buffer[256];
|
|
char c_buffer[256];
|
|
struct iovec io = { .iov_base = m_buffer, .iov_len = sizeof(m_buffer) };
|
|
close(sv[0]);
|
|
|
|
msg.msg_iov = &io;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_control = c_buffer;
|
|
msg.msg_controllen = sizeof(c_buffer);
|
|
|
|
igt_assert(recvmsg(socket, &msg, 0) > 0);
|
|
|
|
cmsg = CMSG_FIRSTHDR(&msg);
|
|
data = CMSG_DATA(cmsg);
|
|
socket_timeline = *((int *) data);
|
|
|
|
/* Advance timeline from 0 -> 1 */
|
|
sw_sync_timeline_inc(socket_timeline, 1);
|
|
}
|
|
|
|
{
|
|
/* Parent process */
|
|
int socket = sv[0];
|
|
struct cmsghdr *cmsg;
|
|
struct iovec io = { .iov_base = (char *)"ABC", .iov_len = 3 };
|
|
struct msghdr msg = { 0 };
|
|
char buf[CMSG_SPACE(sizeof(timeline))];
|
|
memset(buf, '\0', sizeof(buf));
|
|
close(sv[1]);
|
|
|
|
msg.msg_iov = &io;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_control = buf;
|
|
msg.msg_controllen = sizeof(buf);
|
|
|
|
cmsg = CMSG_FIRSTHDR(&msg);
|
|
cmsg->cmsg_level = SOL_SOCKET;
|
|
cmsg->cmsg_type = SCM_RIGHTS;
|
|
cmsg->cmsg_len = CMSG_LEN(sizeof(timeline));
|
|
|
|
*((int *) CMSG_DATA(cmsg)) = timeline;
|
|
msg.msg_controllen = cmsg->cmsg_len;
|
|
|
|
igt_assert_f(sync_fence_wait(fence, 0) == -ETIME,
|
|
"Fence signaled (it should not have been signalled yet)\n");
|
|
|
|
igt_assert(sendmsg(socket, &msg, 0) > 0);
|
|
|
|
igt_assert_f(sync_fence_wait(fence, 2*1000) == 0,
|
|
"Fence not signaled (timeline value 1 fence seqno 1)\n");
|
|
}
|
|
|
|
igt_waitchildren();
|
|
|
|
close(fence);
|
|
close(timeline);
|
|
}
|
|
|
|
static void test_sync_busy_fork(void)
|
|
{
|
|
int timeline = sw_sync_timeline_create();
|
|
int fence = sw_sync_timeline_create_fence(timeline, 1);
|
|
|
|
igt_assert_f(sync_fence_wait(fence, 0) == -ETIME,
|
|
"Fence signaled (it should not have been signalled yet)\n");
|
|
|
|
igt_fork(child, 1) {
|
|
usleep(1*1000*1000);
|
|
/* Advance timeline from 0 -> 1 */
|
|
sw_sync_timeline_inc(timeline, 1);
|
|
}
|
|
|
|
igt_assert_f(sync_fence_wait(fence, 2*1000) == 0,
|
|
"Fence not signaled (timeline value 1 fence seqno 1)\n");
|
|
|
|
igt_waitchildren();
|
|
|
|
close(fence);
|
|
close(timeline);
|
|
}
|
|
|
|
static void test_sync_merge_invalid(void)
|
|
{
|
|
int in_fence;
|
|
int fence_invalid;
|
|
int fence_merge;
|
|
int timeline;
|
|
char tmppath[] = "/tmp/igt-XXXXXX";
|
|
int skip = 0;
|
|
|
|
timeline = sw_sync_timeline_create();
|
|
in_fence = sw_sync_timeline_create_fence(timeline, 1);
|
|
|
|
fence_invalid = -1;
|
|
fence_merge = sync_fence_merge(in_fence, fence_invalid);
|
|
igt_assert_f(fence_merge < 0, "Verify invalid fd (-1) handling");
|
|
|
|
fence_invalid = drm_open_driver(DRIVER_ANY);
|
|
fence_merge = sync_fence_merge(in_fence, fence_invalid);
|
|
igt_assert_f(fence_merge < 0, "Verify invalid fd (device fd) handling");
|
|
|
|
fence_invalid = mkstemp(tmppath);
|
|
if (fence_invalid == -1) {
|
|
skip = 1;
|
|
goto out;
|
|
}
|
|
unlink(tmppath);
|
|
fence_invalid = drm_open_driver(DRIVER_ANY);
|
|
fence_merge = sync_fence_merge(in_fence, fence_invalid);
|
|
close(fence_invalid);
|
|
igt_assert_f(fence_merge < 0, "Verify invalid fd (file fd) handling");
|
|
|
|
out:
|
|
close(in_fence);
|
|
close(fence_merge);
|
|
close(timeline);
|
|
igt_require(skip == 0);
|
|
}
|
|
|
|
static void test_sync_merge(void)
|
|
{
|
|
int in_fence[3];
|
|
int fence_merge;
|
|
int timeline;
|
|
int active, signaled;
|
|
|
|
timeline = sw_sync_timeline_create();
|
|
in_fence[0] = sw_sync_timeline_create_fence(timeline, 1);
|
|
in_fence[1] = sw_sync_timeline_create_fence(timeline, 2);
|
|
in_fence[2] = sw_sync_timeline_create_fence(timeline, 3);
|
|
|
|
fence_merge = sync_fence_merge(in_fence[0], in_fence[1]);
|
|
fence_merge = sync_fence_merge(in_fence[2], fence_merge);
|
|
|
|
/* confirm all fences have one active point (even d) */
|
|
active = sync_fence_count_status(in_fence[0],
|
|
SW_SYNC_FENCE_STATUS_ACTIVE);
|
|
igt_assert_f(active == 1, "in_fence[0] has too many active fences\n");
|
|
active = sync_fence_count_status(in_fence[1],
|
|
SW_SYNC_FENCE_STATUS_ACTIVE);
|
|
igt_assert_f(active == 1, "in_fence[1] has too many active fences\n");
|
|
active = sync_fence_count_status(in_fence[2],
|
|
SW_SYNC_FENCE_STATUS_ACTIVE);
|
|
igt_assert_f(active == 1, "in_fence[2] has too many active fences\n");
|
|
active = sync_fence_count_status(fence_merge,
|
|
SW_SYNC_FENCE_STATUS_ACTIVE);
|
|
igt_assert_f(active == 1, "fence_merge has too many active fences\n");
|
|
|
|
/* confirm that fence_merge is not signaled until the max of fence 0,1,2 */
|
|
sw_sync_timeline_inc(timeline, 1);
|
|
signaled = sync_fence_count_status(in_fence[0],
|
|
SW_SYNC_FENCE_STATUS_SIGNALED);
|
|
active = sync_fence_count_status(fence_merge,
|
|
SW_SYNC_FENCE_STATUS_ACTIVE);
|
|
igt_assert_f(signaled == 1, "in_fence[0] did not signal\n");
|
|
igt_assert_f(active == 1, "fence_merge signaled too early\n");
|
|
|
|
sw_sync_timeline_inc(timeline, 1);
|
|
signaled = sync_fence_count_status(in_fence[1],
|
|
SW_SYNC_FENCE_STATUS_SIGNALED);
|
|
active = sync_fence_count_status(fence_merge,
|
|
SW_SYNC_FENCE_STATUS_ACTIVE);
|
|
igt_assert_f(signaled == 1, "in_fence[1] did not signal\n");
|
|
igt_assert_f(active == 1, "fence_merge signaled too early\n");
|
|
|
|
sw_sync_timeline_inc(timeline, 1);
|
|
signaled = sync_fence_count_status(in_fence[2],
|
|
SW_SYNC_FENCE_STATUS_SIGNALED);
|
|
igt_assert_f(signaled == 1, "in_fence[2] did not signal\n");
|
|
signaled = sync_fence_count_status(fence_merge,
|
|
SW_SYNC_FENCE_STATUS_SIGNALED);
|
|
active = sync_fence_count_status(fence_merge,
|
|
SW_SYNC_FENCE_STATUS_ACTIVE);
|
|
igt_assert_f(active == 0 && signaled == 1,
|
|
"fence_merge did not signal\n");
|
|
|
|
close(in_fence[0]);
|
|
close(in_fence[1]);
|
|
close(in_fence[2]);
|
|
close(fence_merge);
|
|
close(timeline);
|
|
}
|
|
|
|
static void test_sync_merge_same(void)
|
|
{
|
|
int in_fence[2];
|
|
int timeline;
|
|
int signaled;
|
|
|
|
timeline = sw_sync_timeline_create();
|
|
in_fence[0] = sw_sync_timeline_create_fence(timeline, 1);
|
|
in_fence[1] = sync_fence_merge(in_fence[0], in_fence[0]);
|
|
|
|
signaled = sync_fence_count_status(in_fence[0],
|
|
SW_SYNC_FENCE_STATUS_SIGNALED);
|
|
igt_assert_f(signaled == 0, "Fence signaled too early\n");
|
|
|
|
sw_sync_timeline_inc(timeline, 1);
|
|
signaled = sync_fence_count_status(in_fence[0],
|
|
SW_SYNC_FENCE_STATUS_SIGNALED);
|
|
igt_assert_f(signaled == 1, "Fence did not signal\n");
|
|
|
|
close(in_fence[0]);
|
|
close(in_fence[1]);
|
|
close(timeline);
|
|
}
|
|
|
|
static void test_sync_multi_timeline_wait(void)
|
|
{
|
|
int timeline[3];
|
|
int in_fence[3];
|
|
int fence_merge;
|
|
int active, signaled;
|
|
|
|
timeline[0] = sw_sync_timeline_create();
|
|
timeline[1] = sw_sync_timeline_create();
|
|
timeline[2] = sw_sync_timeline_create();
|
|
|
|
in_fence[0] = sw_sync_timeline_create_fence(timeline[0], 5);
|
|
in_fence[1] = sw_sync_timeline_create_fence(timeline[1], 5);
|
|
in_fence[2] = sw_sync_timeline_create_fence(timeline[2], 5);
|
|
|
|
fence_merge = sync_fence_merge(in_fence[0], in_fence[1]);
|
|
fence_merge = sync_fence_merge(in_fence[2], fence_merge);
|
|
|
|
/* Confirm fence isn't signaled */
|
|
active = sync_fence_count_status(fence_merge,
|
|
SW_SYNC_FENCE_STATUS_ACTIVE);
|
|
igt_assert_f(active == 3, "Fence signaled too early\n");
|
|
|
|
igt_assert_f(sync_fence_wait(fence_merge, 0) == -ETIME,
|
|
"Failure waiting on fence until timeout\n");
|
|
|
|
sw_sync_timeline_inc(timeline[0], 5);
|
|
active = sync_fence_count_status(fence_merge,
|
|
SW_SYNC_FENCE_STATUS_ACTIVE);
|
|
signaled = sync_fence_count_status(fence_merge,
|
|
SW_SYNC_FENCE_STATUS_SIGNALED);
|
|
igt_assert_f(active == 2 && signaled == 1,
|
|
"Fence did not signal properly\n");
|
|
|
|
sw_sync_timeline_inc(timeline[1], 5);
|
|
active = sync_fence_count_status(fence_merge,
|
|
SW_SYNC_FENCE_STATUS_ACTIVE);
|
|
signaled = sync_fence_count_status(fence_merge,
|
|
SW_SYNC_FENCE_STATUS_SIGNALED);
|
|
igt_assert_f(active == 1 && signaled == 2,
|
|
"Fence did not signal properly\n");
|
|
|
|
sw_sync_timeline_inc(timeline[2], 5);
|
|
active = sync_fence_count_status(fence_merge,
|
|
SW_SYNC_FENCE_STATUS_ACTIVE);
|
|
signaled = sync_fence_count_status(fence_merge,
|
|
SW_SYNC_FENCE_STATUS_SIGNALED);
|
|
igt_assert_f(active == 0 && signaled == 3,
|
|
"Fence did not signal properly\n");
|
|
|
|
/* confirm you can successfully wait */
|
|
igt_assert_f(sync_fence_wait(fence_merge, 100) == 0,
|
|
"Failure waiting on signaled fence\n");
|
|
|
|
close(in_fence[0]);
|
|
close(in_fence[1]);
|
|
close(in_fence[2]);
|
|
close(fence_merge);
|
|
close(timeline[0]);
|
|
close(timeline[1]);
|
|
close(timeline[2]);
|
|
}
|
|
|
|
#define MULTI_CONSUMER_THREADS 8
|
|
#define MULTI_CONSUMER_ITERATIONS (1 << 14)
|
|
static void * test_sync_multi_consumer_thread(void *arg)
|
|
{
|
|
data_t *data = arg;
|
|
int thread_id = data->thread_id;
|
|
int timeline = data->timeline;
|
|
int i;
|
|
|
|
for (i = 0; i < MULTI_CONSUMER_ITERATIONS; i++) {
|
|
int next_point = i * MULTI_CONSUMER_THREADS + thread_id;
|
|
int fence = sw_sync_timeline_create_fence(timeline, next_point);
|
|
|
|
if (sync_fence_wait(fence, 1000) < 0)
|
|
return (void *) 1;
|
|
|
|
if (READ_ONCE(*data->counter) != next_point)
|
|
return (void *) 1;
|
|
|
|
sem_post(data->sem);
|
|
close(fence);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void test_sync_multi_consumer(void)
|
|
{
|
|
|
|
data_t data_arr[MULTI_CONSUMER_THREADS];
|
|
pthread_t thread_arr[MULTI_CONSUMER_THREADS];
|
|
sem_t sem;
|
|
int timeline;
|
|
_Atomic(uint32_t) counter = 0;
|
|
uintptr_t thread_ret = 0;
|
|
data_t data;
|
|
int i, ret;
|
|
|
|
sem_init(&sem, 0, 0);
|
|
timeline = sw_sync_timeline_create();
|
|
|
|
data.counter = &counter;
|
|
data.timeline = timeline;
|
|
data.sem = &sem;
|
|
|
|
/* Start sync threads. */
|
|
for (i = 0; i < MULTI_CONSUMER_THREADS; i++)
|
|
{
|
|
data_arr[i] = data;
|
|
data_arr[i].thread_id = i;
|
|
ret = pthread_create(&thread_arr[i], NULL,
|
|
test_sync_multi_consumer_thread,
|
|
(void *) &(data_arr[i]));
|
|
igt_assert_eq(ret, 0);
|
|
}
|
|
|
|
/* Produce 'content'. */
|
|
for (i = 0; i < MULTI_CONSUMER_THREADS * MULTI_CONSUMER_ITERATIONS; i++)
|
|
{
|
|
sem_wait(&sem);
|
|
|
|
atomic_fetch_add(&counter, 1);
|
|
sw_sync_timeline_inc(timeline, 1);
|
|
}
|
|
|
|
/* Wait for threads to complete. */
|
|
for (i = 0; i < MULTI_CONSUMER_THREADS; i++)
|
|
{
|
|
uintptr_t local_thread_ret;
|
|
pthread_join(thread_arr[i], (void **)&local_thread_ret);
|
|
thread_ret |= local_thread_ret;
|
|
}
|
|
|
|
close(timeline);
|
|
sem_destroy(&sem);
|
|
|
|
igt_assert_eq(counter,
|
|
MULTI_CONSUMER_THREADS * MULTI_CONSUMER_ITERATIONS);
|
|
|
|
igt_assert_f(thread_ret == 0, "A sync thread reported failure.\n");
|
|
}
|
|
|
|
#define MULTI_CONSUMER_PRODUCER_THREADS 8
|
|
#define MULTI_CONSUMER_PRODUCER_ITERATIONS (1 << 14)
|
|
static void * test_sync_multi_consumer_producer_thread(void *arg)
|
|
{
|
|
data_t *data = arg;
|
|
int thread_id = data->thread_id;
|
|
int timeline = data->timeline;
|
|
int i;
|
|
|
|
for (i = 0; i < MULTI_CONSUMER_PRODUCER_ITERATIONS; i++) {
|
|
int next_point = i * MULTI_CONSUMER_PRODUCER_THREADS + thread_id;
|
|
int fence = sw_sync_timeline_create_fence(timeline, next_point);
|
|
|
|
if (sync_fence_wait(fence, 1000) < 0)
|
|
return (void *) 1;
|
|
|
|
if (atomic_fetch_add(data->counter, 1) != next_point)
|
|
return (void *) 1;
|
|
|
|
/* Kick off the next thread. */
|
|
sw_sync_timeline_inc(timeline, 1);
|
|
|
|
close(fence);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void test_sync_multi_consumer_producer(void)
|
|
{
|
|
data_t data_arr[MULTI_CONSUMER_PRODUCER_THREADS];
|
|
pthread_t thread_arr[MULTI_CONSUMER_PRODUCER_THREADS];
|
|
int timeline;
|
|
_Atomic(uint32_t) counter = 0;
|
|
uintptr_t thread_ret = 0;
|
|
data_t data;
|
|
int i, ret;
|
|
|
|
timeline = sw_sync_timeline_create();
|
|
|
|
data.counter = &counter;
|
|
data.timeline = timeline;
|
|
|
|
/* Start consumer threads. */
|
|
for (i = 0; i < MULTI_CONSUMER_PRODUCER_THREADS; i++)
|
|
{
|
|
data_arr[i] = data;
|
|
data_arr[i].thread_id = i;
|
|
ret = pthread_create(&thread_arr[i], NULL,
|
|
test_sync_multi_consumer_producer_thread,
|
|
(void *) &(data_arr[i]));
|
|
igt_assert_eq(ret, 0);
|
|
}
|
|
|
|
/* Wait for threads to complete. */
|
|
for (i = 0; i < MULTI_CONSUMER_PRODUCER_THREADS; i++)
|
|
{
|
|
uintptr_t local_thread_ret;
|
|
pthread_join(thread_arr[i], (void **)&local_thread_ret);
|
|
thread_ret |= local_thread_ret;
|
|
}
|
|
|
|
close(timeline);
|
|
|
|
igt_assert_eq(counter,
|
|
MULTI_CONSUMER_PRODUCER_THREADS *
|
|
MULTI_CONSUMER_PRODUCER_ITERATIONS);
|
|
|
|
igt_assert_f(thread_ret == 0, "A sync thread reported failure.\n");
|
|
}
|
|
|
|
static int test_mspc_wait_on_fence(int fence)
|
|
{
|
|
int error, active;
|
|
|
|
do {
|
|
error = sync_fence_count_status(fence,
|
|
SW_SYNC_FENCE_STATUS_ERROR);
|
|
igt_assert_f(error == 0, "Error occurred on fence\n");
|
|
active = sync_fence_count_status(fence,
|
|
SW_SYNC_FENCE_STATUS_ACTIVE);
|
|
} while (active);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct {
|
|
int iterations;
|
|
int threads;
|
|
int counter;
|
|
int cons_timeline;
|
|
int *prod_timeline;
|
|
pthread_mutex_t lock;
|
|
} test_mpsc_data;
|
|
|
|
static void *mpsc_producer_thread(void *d)
|
|
{
|
|
int id = (long)d;
|
|
int fence, i;
|
|
int *prod_timeline = test_mpsc_data.prod_timeline;
|
|
int cons_timeline = test_mpsc_data.cons_timeline;
|
|
int iterations = test_mpsc_data.iterations;
|
|
|
|
for (i = 0; i < iterations; i++) {
|
|
fence = sw_sync_timeline_create_fence(cons_timeline, i);
|
|
|
|
/* Wait for the consumer to finish. Use alternate
|
|
* means of waiting on the fence
|
|
*/
|
|
if ((iterations + id) % 8 != 0) {
|
|
igt_assert_f(sync_fence_wait(fence, -1) == 0,
|
|
"Failure waiting on fence\n");
|
|
} else {
|
|
igt_assert_f(test_mspc_wait_on_fence(fence) == 0,
|
|
"Failure waiting on fence\n");
|
|
}
|
|
|
|
/* Every producer increments the counter, the consumer
|
|
* checks and erases it
|
|
*/
|
|
pthread_mutex_lock(&test_mpsc_data.lock);
|
|
test_mpsc_data.counter++;
|
|
pthread_mutex_unlock(&test_mpsc_data.lock);
|
|
|
|
sw_sync_timeline_inc(prod_timeline[id], 1);
|
|
close(fence);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int mpsc_consumer_thread(void)
|
|
{
|
|
int fence, merged, tmp, it, i;
|
|
int *prod_timeline = test_mpsc_data.prod_timeline;
|
|
int cons_timeline = test_mpsc_data.cons_timeline;
|
|
int iterations = test_mpsc_data.iterations;
|
|
int n = test_mpsc_data.threads;
|
|
|
|
for (it = 1; it <= iterations; it++) {
|
|
fence = sw_sync_timeline_create_fence(prod_timeline[0], it);
|
|
for (i = 1; i < n; i++) {
|
|
tmp = sw_sync_timeline_create_fence(prod_timeline[i], it);
|
|
merged = sync_fence_merge(tmp, fence);
|
|
close(tmp);
|
|
close(fence);
|
|
fence = merged;
|
|
}
|
|
|
|
/* Make sure we see an increment from every producer thread.
|
|
* Vary the means by which we wait.
|
|
*/
|
|
if (iterations % 8 != 0) {
|
|
igt_assert_f(sync_fence_wait(fence, -1) == 0,
|
|
"Producers did not increment as expected\n");
|
|
} else {
|
|
igt_assert_f(test_mspc_wait_on_fence(fence) == 0,
|
|
"Failure waiting on fence\n");
|
|
}
|
|
|
|
igt_assert_f(test_mpsc_data.counter == n * it,
|
|
"Counter value mismatch\n");
|
|
|
|
/* Release the producer threads */
|
|
sw_sync_timeline_inc(cons_timeline, 1);
|
|
close(fence);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* IMPORTANT NOTE: if you see this test failing on your system, it may be
|
|
* due to a shortage of file descriptors. Please ensure your system has
|
|
* a sensible limit for this test to finish correctly.
|
|
*/
|
|
static void test_sync_multi_producer_single_consumer(void)
|
|
{
|
|
int iterations = 1 << 12;
|
|
int n = 5;
|
|
int prod_timeline[n];
|
|
int cons_timeline;
|
|
pthread_t threads[n];
|
|
long i;
|
|
|
|
cons_timeline = sw_sync_timeline_create();
|
|
for (i = 0; i < n; i++)
|
|
prod_timeline[i] = sw_sync_timeline_create();
|
|
|
|
test_mpsc_data.prod_timeline = prod_timeline;
|
|
test_mpsc_data.cons_timeline = cons_timeline;
|
|
test_mpsc_data.iterations = iterations;
|
|
test_mpsc_data.threads = n;
|
|
test_mpsc_data.counter = 0;
|
|
pthread_mutex_init(&test_mpsc_data.lock, NULL);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
pthread_create(&threads[i], NULL, (void * (*)(void *))
|
|
mpsc_producer_thread,
|
|
(void *)i);
|
|
}
|
|
|
|
mpsc_consumer_thread();
|
|
|
|
for (i = 0; i < n; i++)
|
|
pthread_join(threads[i], NULL);
|
|
}
|
|
|
|
static void test_sync_expired_merge(void)
|
|
{
|
|
int iterations = 1 << 20;
|
|
int timeline;
|
|
int i;
|
|
int fence_expired, fence_merged;
|
|
|
|
timeline = sw_sync_timeline_create();
|
|
|
|
sw_sync_timeline_inc(timeline, 100);
|
|
fence_expired = sw_sync_timeline_create_fence(timeline, 1);
|
|
igt_assert_f(sync_fence_wait(fence_expired, 0) == 0,
|
|
"Failure waiting for expired fence\n");
|
|
|
|
fence_merged = sync_fence_merge(fence_expired, fence_expired);
|
|
close(fence_merged);
|
|
|
|
for (i = 0; i < iterations; i++) {
|
|
int fence = sync_fence_merge(fence_expired, fence_expired);
|
|
|
|
igt_assert_f(sync_fence_wait(fence, -1) == 0,
|
|
"Failure waiting on fence\n");
|
|
close(fence);
|
|
}
|
|
|
|
close(fence_expired);
|
|
}
|
|
|
|
static void test_sync_random_merge(void)
|
|
{
|
|
int i, size;
|
|
const int nbr_timeline = 32;
|
|
const int nbr_merge = 1024;
|
|
int fence_map[nbr_timeline];
|
|
int timeline_arr[nbr_timeline];
|
|
int fence, tmpfence, merged;
|
|
int timeline, timeline_offset, sync_pt;
|
|
|
|
srand(time(NULL));
|
|
|
|
for (i = 0; i < nbr_timeline; i++) {
|
|
timeline_arr[i] = sw_sync_timeline_create();
|
|
fence_map[i] = -1;
|
|
}
|
|
|
|
sync_pt = rand();
|
|
fence = sw_sync_timeline_create_fence(timeline_arr[0], sync_pt);
|
|
|
|
fence_map[0] = sync_pt;
|
|
|
|
/* Randomly create syncpoints out of a fixed set of timelines,
|
|
* and merge them together.
|
|
*/
|
|
for (i = 0; i < nbr_merge; i++) {
|
|
/* Generate syncpoint. */
|
|
timeline_offset = rand() % nbr_timeline;
|
|
timeline = timeline_arr[timeline_offset];
|
|
sync_pt = rand();
|
|
|
|
/* Keep track of the latest sync_pt in each timeline. */
|
|
if (fence_map[timeline_offset] == -1)
|
|
fence_map[timeline_offset] = sync_pt;
|
|
else if (fence_map[timeline_offset] < sync_pt)
|
|
fence_map[timeline_offset] = sync_pt;
|
|
|
|
/* Merge. */
|
|
tmpfence = sw_sync_timeline_create_fence(timeline, sync_pt);
|
|
merged = sync_fence_merge(tmpfence, fence);
|
|
close(tmpfence);
|
|
close(fence);
|
|
fence = merged;
|
|
}
|
|
|
|
size = 0;
|
|
for (i = 0; i < nbr_timeline; i++)
|
|
if (fence_map[i] != -1)
|
|
size++;
|
|
|
|
/* Trigger the merged fence. */
|
|
for (i = 0; i < nbr_timeline; i++) {
|
|
if (fence_map[i] != -1) {
|
|
igt_assert_f(sync_fence_wait(fence, 0) == -ETIME,
|
|
"Failure waiting on fence until timeout\n");
|
|
/* Increment the timeline to the last sync_pt */
|
|
sw_sync_timeline_inc(timeline_arr[i], fence_map[i]);
|
|
}
|
|
}
|
|
|
|
/* Check that the fence is triggered. */
|
|
igt_assert_f(sync_fence_wait(fence, 1) == 0,
|
|
"Failure triggering fence\n");
|
|
|
|
close(fence);
|
|
for (i = 0; i < nbr_timeline; i++)
|
|
close(timeline_arr[i]);
|
|
}
|
|
|
|
igt_main
|
|
{
|
|
igt_fixture
|
|
igt_require_sw_sync();
|
|
|
|
igt_subtest("alloc_timeline")
|
|
test_alloc_timeline();
|
|
|
|
igt_subtest("alloc_fence")
|
|
test_alloc_fence();
|
|
|
|
igt_subtest("alloc_fence_invalid_timeline")
|
|
test_alloc_fence_invalid_timeline();
|
|
|
|
igt_subtest("timeline_closed")
|
|
test_timeline_closed();
|
|
|
|
igt_subtest("timeline_closed_signaled")
|
|
test_timeline_closed_signaled();
|
|
|
|
igt_subtest("alloc_merge_fence")
|
|
test_alloc_merge_fence();
|
|
|
|
igt_subtest("sync_busy")
|
|
test_sync_busy();
|
|
|
|
igt_subtest("sync_busy_fork")
|
|
test_sync_busy_fork();
|
|
|
|
igt_subtest("sync_busy_fork_unixsocket")
|
|
test_sync_busy_fork_unixsocket();
|
|
|
|
igt_subtest("sync_merge_invalid")
|
|
test_sync_merge_invalid();
|
|
|
|
igt_subtest("sync_merge")
|
|
test_sync_merge();
|
|
|
|
igt_subtest("sync_merge_same")
|
|
test_sync_merge_same();
|
|
|
|
igt_subtest("sync_multi_timeline_wait")
|
|
test_sync_multi_timeline_wait();
|
|
|
|
igt_subtest("sync_multi_consumer")
|
|
test_sync_multi_consumer();
|
|
|
|
igt_subtest("sync_multi_consumer_producer")
|
|
test_sync_multi_consumer_producer();
|
|
|
|
igt_subtest("sync_multi_producer_single_consumer")
|
|
test_sync_multi_producer_single_consumer();
|
|
|
|
igt_subtest("sync_expired_merge")
|
|
test_sync_expired_merge();
|
|
|
|
igt_subtest("sync_random_merge")
|
|
test_sync_random_merge();
|
|
}
|