blob: a96f911aa2e91f55eb8dac04f2183fbabb1034a9 [file] [log] [blame]
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sync/sync.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
/* These are not exported from the library because they are for testing only. */
int sw_sync_fence_create(int fd, const char *name, unsigned value);
int sw_sync_timeline_create(void);
int sw_sync_timeline_inc(int fd, unsigned count);
#define CHK(errn) do { int ret = fc; if (ret < 0) return ret; } while (0)
#define ERROR(msg, en) do {\
fprintf(stderr, "ERROR : %s. : %d:%s\n", (msg), (en), strerror(en)); \
} while (0)
bool verbose = false;
static int
test_swsync_exists(void)
{
int ret;
int newsync = 0;
struct stat buf = { 0 };
ret = stat("/dev/sw_sync", &buf);
if (ret < 0 && errno == ENOENT) {
ret = stat("/sys/kernel/debug/sync/sw_sync", &buf);
if (ret < 0 && errno == ENOENT) {
ret = -errno;
ERROR("sw_sync interface does not exist", -ret);
goto fail;
}
newsync = 1;
}
if (ret < 0 && errno == EACCES) {
ret = -errno;
ERROR("No permission to access sw_sync interface", -ret);
goto fail;
}
if (ret < 0) {
ret = -errno;
ERROR("Stat failed", -ret);
goto fail;
}
/* Old sync is a character device. New is a regular file in sysfs. */
if ((!newsync && (buf.st_mode & S_IFCHR))
|| (newsync && (buf.st_mode & S_IFREG))) {
ret = 0;
}
fail:
return ret;
}
static int
get_timeline(void)
{
int tl = sw_sync_timeline_create();
if (tl < 0) {
tl = -errno;
ERROR("Cannot create timeline", -tl);
}
return tl;
}
static int
get_fence(int tl, int value)
{
int fc = sw_sync_fence_create(tl, "simple fence", value);
if (fc < 0) {
fc = -errno;
ERROR("Failed to create fence", -fc);
}
return fc;
}
static int
fence_check(int fc)
{
int ret;
ret = sync_wait(fc, 0);
if (ret < 0) {
if (errno == ETIME)
return 0;
if (errno == EINVAL)
return 1; /* timeline destroyed */
ret = -errno;
ERROR("Failed fence wait", -ret);
return ret;
}
return 1;
}
/* Return 0 when timeout, 1 when passing, < 0 for error
*/
static int
timeline_inc(int tl, int delta)
{
int ret;
ret = sw_sync_timeline_inc(tl, delta);
if (ret < 0) {
ret = -errno;
ERROR("Failed to increment timeline", -ret);
}
return ret;
}
/* Test single fence. */
static int
test_simple(void)
{
int tl;
int fc;
int ret;
tl = get_timeline();
if (tl < 0) {
return tl;
}
fc = get_fence(tl, 1);
if (fc < 0) {
ret = fc;
goto fail;
}
ret = fence_check(fc);
if (ret < 0) {
goto fail_fence;
} else if (ret == 1) {
ret = -1;
ERROR("Fence already signaled even though we did not signal it", 0);
goto fail_fence;
}
ret = timeline_inc(tl, 1);
if (ret < 0) {
goto fail_fence;
}
ret = fence_check(fc);
if (ret < 0) {
goto fail_fence;
} else if (ret == 0) {
ret = -1;
ERROR("Fence is not signaled even though it should be", 0);
goto fail_fence;
}
ret = 0;
fail_fence:
close(fc);
fail:
close(tl);
return ret;
}
#define NUM_FENCES 10
/* Test multiple fences signaled with one timeline inc. */
static int
test_multiple(void)
{
int i;
int tl;
int fc[NUM_FENCES];
int ret;
int value = 1;
tl = get_timeline();
if (tl < 0) {
return tl;
}
for (i = 0; i < NUM_FENCES; i++) {
fc[i] = get_fence(tl, value++);
if (fc[i] < 0) {
ret = fc[i];
goto fail;
}
}
for (i = 0; i < NUM_FENCES; i++) {
ret = fence_check(fc[i]);
if (ret < 0) {
goto fail_fence;
} else if (ret == 1) {
ret = -1;
ERROR("Fence already signaled even though we did not signal it", 0);
goto fail_fence;
}
}
ret = timeline_inc(tl, NUM_FENCES);
if (ret < 0) {
goto fail_fence;
}
for (i = 0; i < NUM_FENCES; i++) {
ret = fence_check(fc[i]);
if (ret < 0) {
goto fail_fence;
} else if (ret == 0) {
ret = -1;
ERROR("Fence is not signaled even though it should be", 0);
goto fail_fence;
}
}
ret = 0;
fail_fence:
for (i = 0; i < NUM_FENCES; i++)
close(fc[i]);
fail:
close(tl);
return ret;
}
#undef NUM_FENCES
/* Test destruction of timeline. It should signal/set error condition on all
* fences in the timeline. */
static int
test_timeline_destroy(void)
{
int tl;
int fc;
int ret;
tl = get_timeline();
if (tl < 0) {
return tl;
}
fc = get_fence(tl, 1);
if (fc < 0) {
ret = fc;
goto fail;
}
ret = fence_check(fc);
if (ret < 0) {
goto fail_fence;
} else if (ret == 1) {
ret = -1;
ERROR("Fence already signaled even though we did not signal it", 0);
goto fail_fence;
}
close(tl);
ret = fence_check(fc);
if (ret < 0) {
goto fail_fence;
} else if (ret == 0) {
ret = -1;
ERROR("Fence is not signaled even though it should be", 0);
goto fail_fence;
}
ret = 0;
fail_fence:
close(fc);
fail:
return ret;
}
static const struct { const char *name; int (*func)(void); } cases[] = {
{ "swsync_exists", test_swsync_exists },
{ "simple", test_simple },
{ "multiple", test_multiple },
{ "timeline_destroy", test_timeline_destroy },
{ NULL, NULL }
};
static const struct option longopts[] = {
{ "test_name", required_argument, NULL, 't' },
{ "verbose", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
{ 0, 0, 0, 0 },
};
static void print_help(const char *argv0)
{
unsigned int u;
printf("usage: %s -t <test_name> [-v]\n", argv0);
printf("A valid name test is one the following:\n");
for (u = 0; cases[u].name; u++)
printf("%s\n", cases[u].name);
printf("all\n");
}
int
run_synctest(const char *name)
{
unsigned int u;
int ret = 0;
for (u = 0; cases[u].name; u++) {
if (strcmp(cases[u].name, name) && strcmp(name, "all"))
continue;
if (verbose)
printf("Running subtest %s\n", cases[u].name);
ret = cases[u].func();
if (verbose)
printf("Subtest %s %s\n", cases[u].name, (ret < 0) ? "failed" : "passed");
if (ret < 0)
break;
}
return ret;
}
int
main(int ARGC, char *ARGV[])
{
int c;
char *name = strdup("all");
while ((c = getopt_long(ARGC, ARGV, "t:vh", longopts, NULL)) != -1) {
switch (c) {
case 't':
if (name) {
free(name);
name = NULL;
}
name = strdup(optarg);
break;
case 'v':
verbose = true;
break;
case 'h':
goto print;
default:
goto print;
}
}
if (!name)
goto print;
int ret = run_synctest(name);
if (ret == 0)
printf("[ PASSED ] synctest.%s\n", name);
else if (ret < 0)
printf("[ FAILED ] synctest.%s\n", name);
free(name);
if (ret > 0) {
printf("Unexpected test result.\n");
goto print;
}
return ret;
print:
print_help(ARGV[0]);
return -EINVAL;
}