diff --git a/tools/testing/selftests/.gitignore b/tools/testing/selftests/.gitignore new file mode 100644 index 0000000..f0600d2 --- /dev/null +++ b/tools/testing/selftests/.gitignore @@ -0,0 +1 @@ +kselftest diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index f770dba..3044d43 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -6,6 +6,7 @@ TARGETS += exec TARGETS += firmware TARGETS += ftrace TARGETS += futex +TARGETS += gpio TARGETS += ipc TARGETS += kcmp TARGETS += lib @@ -23,6 +24,7 @@ TARGETS += seccomp TARGETS += sigaltstack TARGETS += size TARGETS += static_keys +TARGETS += sync TARGETS += sysctl ifneq (1, $(quicktest)) TARGETS += timers diff --git a/tools/testing/selftests/drivers/gpu/i915.sh b/tools/testing/selftests/drivers/gpu/i915.sh new file mode 100755 index 0000000..d407f0f --- /dev/null +++ b/tools/testing/selftests/drivers/gpu/i915.sh @@ -0,0 +1,14 @@ +#!/bin/sh +# Runs hardware independent tests for i915 (drivers/gpu/drm/i915) + +if ! /sbin/modprobe -q -r i915; then + echo "drivers/gpu/i915: [SKIP]" + exit 77 +fi + +if /sbin/modprobe -q i915 mock_selftests=-1; then + echo "drivers/gpu/i915: ok" +else + echo "drivers/gpu/i915: [FAIL]" + exit 1 +fi diff --git a/tools/testing/selftests/gpio/Makefile b/tools/testing/selftests/gpio/Makefile new file mode 100644 index 0000000..205e4d1 --- /dev/null +++ b/tools/testing/selftests/gpio/Makefile @@ -0,0 +1,23 @@ + +TEST_PROGS := gpio-mockup.sh +TEST_FILES := gpio-mockup-sysfs.sh $(BINARIES) +BINARIES := gpio-mockup-chardev + +include ../lib.mk + +all: $(BINARIES) + +clean: + $(RM) $(BINARIES) + +CFLAGS += -O2 -g -std=gnu99 -Wall -I../../../../usr/include/ +LDLIBS += -lmount -I/usr/include/libmount + +$(BINARIES): ../../../gpio/gpio-utils.o ../../../../usr/include/linux/gpio.h + +../../../gpio/gpio-utils.o: + make ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C ../../../gpio + +../../../../usr/include/linux/gpio.h: + make -C ../../../.. headers_install INSTALL_HDR_PATH=$(shell pwd)/../../../../usr/ + diff --git a/tools/testing/selftests/gpio/gpio-mockup-chardev.c b/tools/testing/selftests/gpio/gpio-mockup-chardev.c new file mode 100644 index 0000000..667e916 --- /dev/null +++ b/tools/testing/selftests/gpio/gpio-mockup-chardev.c @@ -0,0 +1,324 @@ +/* + * GPIO chardev test helper + * + * Copyright (C) 2016 Bamvor Jian Zhang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../../gpio/gpio-utils.h" + +#define CONSUMER "gpio-selftest" +#define GC_NUM 10 +enum direction { + OUT, + IN +}; + +static int get_debugfs(char **path) +{ + struct libmnt_context *cxt; + struct libmnt_table *tb; + struct libmnt_iter *itr = NULL; + struct libmnt_fs *fs; + int found = 0; + + cxt = mnt_new_context(); + if (!cxt) + err(EXIT_FAILURE, "libmount context allocation failed"); + + itr = mnt_new_iter(MNT_ITER_FORWARD); + if (!itr) + err(EXIT_FAILURE, "failed to initialize libmount iterator"); + + if (mnt_context_get_mtab(cxt, &tb)) + err(EXIT_FAILURE, "failed to read mtab"); + + while (mnt_table_next_fs(tb, itr, &fs) == 0) { + const char *type = mnt_fs_get_fstype(fs); + + if (!strcmp(type, "debugfs")) { + found = 1; + break; + } + } + if (found) + asprintf(path, "%s/gpio", mnt_fs_get_target(fs)); + + mnt_free_iter(itr); + mnt_free_context(cxt); + + if (!found) + return -1; + + return 0; +} + +static int gpio_debugfs_get(const char *consumer, int *dir, int *value) +{ + char *debugfs; + FILE *f; + char *line = NULL; + size_t len = 0; + char *cur; + int found = 0; + + if (get_debugfs(&debugfs) != 0) + err(EXIT_FAILURE, "debugfs is not mounted"); + + f = fopen(debugfs, "r"); + if (!f) + err(EXIT_FAILURE, "read from gpio debugfs failed"); + + /* + * gpio-2 ( |gpio-selftest ) in lo + */ + while (getline(&line, &len, f) != -1) { + cur = strstr(line, consumer); + if (cur == NULL) + continue; + + cur = strchr(line, ')'); + if (!cur) + continue; + + cur += 2; + if (!strncmp(cur, "out", 3)) { + *dir = OUT; + cur += 4; + } else if (!strncmp(cur, "in", 2)) { + *dir = IN; + cur += 4; + } + + if (!strncmp(cur, "hi", 2)) + *value = 1; + else if (!strncmp(cur, "lo", 2)) + *value = 0; + + found = 1; + break; + } + free(debugfs); + fclose(f); + free(line); + + if (!found) + return -1; + + return 0; +} + +static struct gpiochip_info *list_gpiochip(const char *gpiochip_name, int *ret) +{ + struct gpiochip_info *cinfo; + struct gpiochip_info *current; + const struct dirent *ent; + DIR *dp; + char *chrdev_name; + int fd; + int i = 0; + + cinfo = calloc(sizeof(struct gpiochip_info) * 4, GC_NUM + 1); + if (!cinfo) + err(EXIT_FAILURE, "gpiochip_info allocation failed"); + + current = cinfo; + dp = opendir("/dev"); + if (!dp) { + *ret = -errno; + goto error_out; + } else { + *ret = 0; + } + + while (ent = readdir(dp), ent) { + if (check_prefix(ent->d_name, "gpiochip")) { + *ret = asprintf(&chrdev_name, "/dev/%s", ent->d_name); + if (*ret < 0) + goto error_out; + + fd = open(chrdev_name, 0); + if (fd == -1) { + *ret = -errno; + fprintf(stderr, "Failed to open %s\n", + chrdev_name); + goto error_close_dir; + } + *ret = ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, current); + if (*ret == -1) { + perror("Failed to issue CHIPINFO IOCTL\n"); + goto error_close_dir; + } + close(fd); + if (strcmp(current->label, gpiochip_name) == 0 + || check_prefix(current->label, gpiochip_name)) { + *ret = 0; + current++; + i++; + } + } + } + + if ((!*ret && i == 0) || *ret < 0) { + free(cinfo); + cinfo = NULL; + } + if (!*ret && i > 0) { + cinfo = realloc(cinfo, sizeof(struct gpiochip_info) * 4 * i); + *ret = i; + } + +error_close_dir: + closedir(dp); +error_out: + if (*ret < 0) + err(EXIT_FAILURE, "list gpiochip failed: %s", strerror(*ret)); + + return cinfo; +} + +int gpio_pin_test(struct gpiochip_info *cinfo, int line, int flag, int value) +{ + struct gpiohandle_data data; + unsigned int lines[] = {line}; + int fd; + int debugfs_dir = IN; + int debugfs_value = 0; + int ret; + + data.values[0] = value; + ret = gpiotools_request_linehandle(cinfo->name, lines, 1, flag, &data, + CONSUMER); + if (ret < 0) + goto fail_out; + else + fd = ret; + + ret = gpio_debugfs_get(CONSUMER, &debugfs_dir, &debugfs_value); + if (ret) { + ret = -EINVAL; + goto fail_out; + } + if (flag & GPIOHANDLE_REQUEST_INPUT) { + if (debugfs_dir != IN) { + errno = -EINVAL; + ret = -errno; + } + } else if (flag & GPIOHANDLE_REQUEST_OUTPUT) { + if (flag & GPIOHANDLE_REQUEST_ACTIVE_LOW) + debugfs_value = !debugfs_value; + + if (!(debugfs_dir == OUT && value == debugfs_value)) + errno = -EINVAL; + ret = -errno; + + } + gpiotools_release_linehandle(fd); + +fail_out: + if (ret) + err(EXIT_FAILURE, "gpio<%s> line<%d> test flag<0x%x> value<%d>", + cinfo->name, line, flag, value); + + return ret; +} + +void gpio_pin_tests(struct gpiochip_info *cinfo, unsigned int line) +{ + printf("line<%d>", line); + gpio_pin_test(cinfo, line, GPIOHANDLE_REQUEST_OUTPUT, 0); + printf("."); + gpio_pin_test(cinfo, line, GPIOHANDLE_REQUEST_OUTPUT, 1); + printf("."); + gpio_pin_test(cinfo, line, + GPIOHANDLE_REQUEST_OUTPUT | GPIOHANDLE_REQUEST_ACTIVE_LOW, + 0); + printf("."); + gpio_pin_test(cinfo, line, + GPIOHANDLE_REQUEST_OUTPUT | GPIOHANDLE_REQUEST_ACTIVE_LOW, + 1); + printf("."); + gpio_pin_test(cinfo, line, GPIOHANDLE_REQUEST_INPUT, 0); + printf("."); +} + +/* + * ./gpio-mockup-chardev gpio_chip_name_prefix is_valid_gpio_chip + * Return 0 if successful or exit with EXIT_FAILURE if test failed. + * gpio_chip_name_prefix: The prefix of gpiochip you want to test. E.g. + * gpio-mockup + * is_valid_gpio_chip: Whether the gpio_chip is valid. 1 means valid, + * 0 means invalid which could not be found by + * list_gpiochip. + */ +int main(int argc, char *argv[]) +{ + char *prefix; + int valid; + struct gpiochip_info *cinfo; + struct gpiochip_info *current; + int i; + int ret; + + if (argc < 3) { + printf("Usage: %s prefix is_valid", argv[0]); + exit(EXIT_FAILURE); + } + + prefix = argv[1]; + valid = strcmp(argv[2], "true") == 0 ? 1 : 0; + + printf("Test gpiochip %s: ", prefix); + cinfo = list_gpiochip(prefix, &ret); + if (!cinfo) { + if (!valid && ret == 0) { + printf("Invalid test successful\n"); + ret = 0; + goto out; + } else { + ret = -EINVAL; + goto out; + } + } else if (cinfo && !valid) { + ret = -EINVAL; + goto out; + } + current = cinfo; + for (i = 0; i < ret; i++) { + gpio_pin_tests(current, 0); + gpio_pin_tests(current, current->lines - 1); + gpio_pin_tests(current, random() % current->lines); + current++; + } + ret = 0; + printf("successful\n"); + +out: + if (ret) + fprintf(stderr, "gpio<%s> test failed\n", prefix); + + if (cinfo) + free(cinfo); + + if (ret) + exit(EXIT_FAILURE); + + return ret; +} diff --git a/tools/testing/selftests/gpio/gpio-mockup-sysfs.sh b/tools/testing/selftests/gpio/gpio-mockup-sysfs.sh new file mode 100755 index 0000000..085d7a3 --- /dev/null +++ b/tools/testing/selftests/gpio/gpio-mockup-sysfs.sh @@ -0,0 +1,134 @@ + +is_consistent() +{ + val= + + active_low_sysfs=`cat $GPIO_SYSFS/gpio$nr/active_low` + val_sysfs=`cat $GPIO_SYSFS/gpio$nr/value` + dir_sysfs=`cat $GPIO_SYSFS/gpio$nr/direction` + + gpio_this_debugfs=`cat $GPIO_DEBUGFS |grep "gpio-$nr" | sed "s/(.*)//g"` + dir_debugfs=`echo $gpio_this_debugfs | awk '{print $2}'` + val_debugfs=`echo $gpio_this_debugfs | awk '{print $3}'` + if [ $val_debugfs = "lo" ]; then + val=0 + elif [ $val_debugfs = "hi" ]; then + val=1 + fi + + if [ $active_low_sysfs = "1" ]; then + if [ $val = "0" ]; then + val="1" + else + val="0" + fi + fi + + if [ $val_sysfs = $val ] && [ $dir_sysfs = $dir_debugfs ]; then + echo -n "." + else + echo "test fail, exit" + die + fi +} + +test_pin_logic() +{ + nr=$1 + direction=$2 + active_low=$3 + value=$4 + + echo $direction > $GPIO_SYSFS/gpio$nr/direction + echo $active_low > $GPIO_SYSFS/gpio$nr/active_low + if [ $direction = "out" ]; then + echo $value > $GPIO_SYSFS/gpio$nr/value + fi + is_consistent $nr +} + +test_one_pin() +{ + nr=$1 + + echo -n "test pin<$nr>" + + echo $nr > $GPIO_SYSFS/export 2>/dev/null + + if [ X$? != X0 ]; then + echo "test GPIO pin $nr failed" + die + fi + + #"Checking if the sysfs is consistent with debugfs: " + is_consistent $nr + + #"Checking the logic of active_low: " + test_pin_logic $nr out 1 1 + test_pin_logic $nr out 1 0 + test_pin_logic $nr out 0 1 + test_pin_logic $nr out 0 0 + + #"Checking the logic of direction: " + test_pin_logic $nr in 1 1 + test_pin_logic $nr out 1 0 + test_pin_logic $nr low 0 1 + test_pin_logic $nr high 0 0 + + echo $nr > $GPIO_SYSFS/unexport + + echo "successful" +} + +test_one_pin_fail() +{ + nr=$1 + + echo $nr > $GPIO_SYSFS/export 2>/dev/null + + if [ X$? != X0 ]; then + echo "test invalid pin $nr successful" + else + echo "test invalid pin $nr failed" + echo $nr > $GPIO_SYSFS/unexport 2>/dev/null + die + fi +} + +list_chip() +{ + echo `ls -d $GPIO_DRV_SYSFS/gpiochip* 2>/dev/null` +} + +test_chip() +{ + chip=$1 + name=`basename $chip` + base=`cat $chip/base` + ngpio=`cat $chip/ngpio` + printf "%-10s %-5s %-5s\n" $name $base $ngpio + if [ $ngpio = "0" ]; then + echo "number of gpio is zero is not allowed". + fi + test_one_pin $base + test_one_pin $(($base + $ngpio - 1)) + test_one_pin $((( RANDOM % $ngpio ) + $base )) +} + +test_chips_sysfs() +{ + gpiochip=`list_chip $module` + if [ X"$gpiochip" = X ]; then + if [ X"$valid" = Xfalse ]; then + echo "successful" + else + echo "fail" + die + fi + else + for chip in $gpiochip; do + test_chip $chip + done + fi +} + diff --git a/tools/testing/selftests/gpio/gpio-mockup.sh b/tools/testing/selftests/gpio/gpio-mockup.sh new file mode 100755 index 0000000..b183439 --- /dev/null +++ b/tools/testing/selftests/gpio/gpio-mockup.sh @@ -0,0 +1,201 @@ +#!/bin/bash + +#exit status +#1: run as non-root user +#2: sysfs/debugfs not mount +#3: insert module fail when gpio-mockup is a module. +#4: other reason. + +SYSFS= +GPIO_SYSFS= +GPIO_DRV_SYSFS= +DEBUGFS= +GPIO_DEBUGFS= +dev_type= +module= + +usage() +{ + echo "Usage:" + echo "$0 [-f] [-m name] [-t type]" + echo "-f: full test. It maybe conflict with existence gpio device." + echo "-m: module name, default name is gpio-mockup. It could also test" + echo " other gpio device." + echo "-t: interface type: chardev(char device) and sysfs(being" + echo " deprecated). The first one is default" + echo "" + echo "$0 -h" + echo "This usage" +} + +prerequisite() +{ + msg="skip all tests:" + if [ $UID != 0 ]; then + echo $msg must be run as root >&2 + exit 1 + fi + SYSFS=`mount -t sysfs | head -1 | awk '{ print $3 }'` + if [ ! -d "$SYSFS" ]; then + echo $msg sysfs is not mounted >&2 + exit 2 + fi + GPIO_SYSFS=`echo $SYSFS/class/gpio` + GPIO_DRV_SYSFS=`echo $SYSFS/devices/platform/$module/gpio` + DEBUGFS=`mount -t debugfs | head -1 | awk '{ print $3 }'` + if [ ! -d "$DEBUGFS" ]; then + echo $msg debugfs is not mounted >&2 + exit 2 + fi + GPIO_DEBUGFS=`echo $DEBUGFS/gpio` + source gpio-mockup-sysfs.sh +} + +try_insert_module() +{ + if [ -d "$GPIO_DRV_SYSFS" ]; then + echo "$GPIO_DRV_SYSFS exist. Skip insert module" + else + modprobe -q $module $1 + if [ X$? != X0 ]; then + echo $msg insmod $module failed >&2 + exit 3 + fi + fi +} + +remove_module() +{ + modprobe -r -q $module +} + +die() +{ + remove_module + exit 4 +} + +test_chips() +{ + if [ X$dev_type = Xsysfs ]; then + echo "WARNING: sysfs ABI of gpio is going to deprecated." + test_chips_sysfs $* + else + $BASE/gpio-mockup-chardev $* + fi +} + +gpio_test() +{ + param=$1 + valid=$2 + + if [ X"$param" = X ]; then + die + fi + try_insert_module "gpio_mockup_ranges=$param" + echo -n "GPIO $module test with ranges: <" + echo "$param>: " + printf "%-10s %s\n" $param + test_chips $module $valid + remove_module +} + +BASE=`dirname $0` + +dev_type= +TEMP=`getopt -o fhm:t: -n '$0' -- "$@"` + +if [ "$?" != "0" ]; then + echo "Parameter process failed, Terminating..." >&2 + exit 1 +fi + +# Note the quotes around `$TEMP': they are essential! +eval set -- "$TEMP" + +while true; do + case $1 in + -f) + full_test=true + shift + ;; + -h) + usage + exit + ;; + -m) + module=$2 + shift 2 + ;; + -t) + dev_type=$2 + shift 2 + ;; + --) + shift + break + ;; + *) + echo "Internal error!" + exit 1 + ;; + esac +done + +if [ X"$module" = X ]; then + module="gpio-mockup" +fi + +if [ X$dev_type != Xsysfs ]; then + dev_type="chardev" +fi + +prerequisite + +echo "1. Test dynamic allocation of gpio successful means insert gpiochip and" +echo " manipulate gpio pin successful" +gpio_test "-1,32" true +gpio_test "-1,32,-1,32" true +gpio_test "-1,32,-1,32,-1,32" true +if [ X$full_test = Xtrue ]; then + gpio_test "-1,32,32,64" true + gpio_test "-1,32,40,64,-1,5" true + gpio_test "-1,32,32,64,-1,32" true + gpio_test "0,32,32,64,-1,32,-1,32" true + gpio_test "-1,32,-1,32,0,32,32,64" true + echo "2. Do basic test: successful means insert gpiochip and" + echo " manipulate gpio pin successful" + gpio_test "0,32" true + gpio_test "0,32,32,64" true + gpio_test "0,32,40,64,64,96" true +fi +echo "3. Error test: successful means insert gpiochip failed" +echo "3.1 Test number of gpio overflow" +#Currently: The max number of gpio(1024) is defined in arm architecture. +gpio_test "-1,32,-1,1024" false +if [ X$full_test = Xtrue ]; then + echo "3.2 Test zero line of gpio" + gpio_test "0,0" false + echo "3.3 Test range overlap" + echo "3.3.1 Test corner case" + gpio_test "0,32,0,1" false + gpio_test "0,32,32,64,32,40" false + gpio_test "0,32,35,64,35,45" false + gpio_test "0,32,31,32" false + gpio_test "0,32,32,64,36,37" false + gpio_test "0,32,35,64,34,36" false + echo "3.3.2 Test inserting invalid second gpiochip" + gpio_test "0,32,30,35" false + gpio_test "0,32,1,5" false + gpio_test "10,32,9,14" false + gpio_test "10,32,30,35" false + echo "3.3.3 Test others" + gpio_test "0,32,40,56,39,45" false + gpio_test "0,32,40,56,30,33" false + gpio_test "0,32,40,56,30,41" false + gpio_test "0,32,40,56,20,21" false +fi + +echo GPIO test PASS + diff --git a/tools/testing/selftests/nsfs/.gitignore b/tools/testing/selftests/nsfs/.gitignore new file mode 100644 index 0000000..2ab2c82 --- /dev/null +++ b/tools/testing/selftests/nsfs/.gitignore @@ -0,0 +1,2 @@ +owner +pidns diff --git a/tools/testing/selftests/sigaltstack/.gitignore b/tools/testing/selftests/sigaltstack/.gitignore new file mode 100644 index 0000000..35897b0 --- /dev/null +++ b/tools/testing/selftests/sigaltstack/.gitignore @@ -0,0 +1 @@ +sas diff --git a/tools/testing/selftests/sync/.gitignore b/tools/testing/selftests/sync/.gitignore new file mode 100644 index 0000000..f5091e7 --- /dev/null +++ b/tools/testing/selftests/sync/.gitignore @@ -0,0 +1 @@ +sync_test diff --git a/tools/testing/selftests/sync/Makefile b/tools/testing/selftests/sync/Makefile new file mode 100644 index 0000000..87ac400 --- /dev/null +++ b/tools/testing/selftests/sync/Makefile @@ -0,0 +1,24 @@ +CFLAGS += -O2 -g -std=gnu89 -pthread -Wall -Wextra +CFLAGS += -I../../../../usr/include/ +LDFLAGS += -pthread + +TEST_PROGS = sync_test + +all: $(TEST_PROGS) + +include ../lib.mk + +OBJS = sync_test.o sync.o + +TESTS += sync_alloc.o +TESTS += sync_fence.o +TESTS += sync_merge.o +TESTS += sync_wait.o +TESTS += sync_stress_parallelism.o +TESTS += sync_stress_consumer.o +TESTS += sync_stress_merge.o + +sync_test: $(OBJS) $(TESTS) + +clean: + $(RM) sync_test $(OBJS) $(TESTS) diff --git a/tools/testing/selftests/sync/sw_sync.h b/tools/testing/selftests/sync/sw_sync.h new file mode 100644 index 0000000..e2cfc6ba --- /dev/null +++ b/tools/testing/selftests/sync/sw_sync.h @@ -0,0 +1,46 @@ +/* + * sw_sync abstraction + * + * Copyright 2015-2016 Collabora Ltd. + * + * Based on the implementation from the Android Open Source Project, + * + * Copyright 2013 Google, Inc + * + * 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 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. + */ + +#ifndef SELFTESTS_SW_SYNC_H +#define SELFTESTS_SW_SYNC_H + +/* + * sw_sync is mainly intended for testing and should not be compiled into + * production kernels + */ + +int sw_sync_timeline_create(void); +int sw_sync_timeline_is_valid(int fd); +int sw_sync_timeline_inc(int fd, unsigned int count); +void sw_sync_timeline_destroy(int fd); + +int sw_sync_fence_create(int fd, const char *name, unsigned int value); +int sw_sync_fence_is_valid(int fd); +void sw_sync_fence_destroy(int fd); + +#endif diff --git a/tools/testing/selftests/sync/sync.c b/tools/testing/selftests/sync/sync.c new file mode 100644 index 0000000..f3d599f --- /dev/null +++ b/tools/testing/selftests/sync/sync.c @@ -0,0 +1,221 @@ +/* + * sync / sw_sync abstraction + * Copyright 2015-2016 Collabora Ltd. + * + * Based on the implementation from the Android Open Source Project, + * + * Copyright 2012 Google, Inc + * + * 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 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. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "sync.h" +#include "sw_sync.h" + +#include + + +/* SW_SYNC ioctls */ +struct sw_sync_create_fence_data { + __u32 value; + char name[32]; + __s32 fence; +}; + +#define SW_SYNC_IOC_MAGIC 'W' +#define SW_SYNC_IOC_CREATE_FENCE _IOWR(SW_SYNC_IOC_MAGIC, 0,\ + struct sw_sync_create_fence_data) +#define SW_SYNC_IOC_INC _IOW(SW_SYNC_IOC_MAGIC, 1, __u32) + + +int sync_wait(int fd, int timeout) +{ + struct pollfd fds; + + fds.fd = fd; + fds.events = POLLIN | POLLERR; + + return poll(&fds, 1, timeout); +} + +int sync_merge(const char *name, int fd1, int fd2) +{ + struct sync_merge_data data = {}; + int err; + + data.fd2 = fd2; + strncpy(data.name, name, sizeof(data.name) - 1); + data.name[sizeof(data.name) - 1] = '\0'; + + err = ioctl(fd1, SYNC_IOC_MERGE, &data); + if (err < 0) + return err; + + return data.fence; +} + +static struct sync_file_info *sync_file_info(int fd) +{ + struct sync_file_info *info; + struct sync_fence_info *fence_info; + int err, num_fences; + + info = calloc(1, sizeof(*info)); + if (info == NULL) + return NULL; + + err = ioctl(fd, SYNC_IOC_FILE_INFO, info); + if (err < 0) { + free(info); + return NULL; + } + + num_fences = info->num_fences; + + if (num_fences) { + info->flags = 0; + info->num_fences = num_fences; + + fence_info = calloc(num_fences, sizeof(*fence_info)); + if (!fence_info) { + free(info); + return NULL; + } + + info->sync_fence_info = (uint64_t)fence_info; + + err = ioctl(fd, SYNC_IOC_FILE_INFO, info); + if (err < 0) { + free(fence_info); + free(info); + return NULL; + } + } + + return info; +} + +static void sync_file_info_free(struct sync_file_info *info) +{ + free((void *)info->sync_fence_info); + free(info); +} + +int sync_fence_size(int fd) +{ + int count; + struct sync_file_info *info = sync_file_info(fd); + + if (!info) + return 0; + + count = info->num_fences; + + sync_file_info_free(info); + + return count; +} + +int sync_fence_count_with_status(int fd, int status) +{ + unsigned int i, count = 0; + struct sync_fence_info *fence_info = NULL; + struct sync_file_info *info = sync_file_info(fd); + + if (!info) + return -1; + + fence_info = (struct sync_fence_info *)info->sync_fence_info; + for (i = 0 ; i < info->num_fences ; i++) { + if (fence_info[i].status == status) + count++; + } + + sync_file_info_free(info); + + return count; +} + +int sw_sync_timeline_create(void) +{ + return open("/sys/kernel/debug/sync/sw_sync", O_RDWR); +} + +int sw_sync_timeline_inc(int fd, unsigned int count) +{ + __u32 arg = count; + + return ioctl(fd, SW_SYNC_IOC_INC, &arg); +} + +int sw_sync_timeline_is_valid(int fd) +{ + int status; + + if (fd == -1) + return 0; + + status = fcntl(fd, F_GETFD, 0); + return (status >= 0); +} + +void sw_sync_timeline_destroy(int fd) +{ + if (sw_sync_timeline_is_valid(fd)) + close(fd); +} + +int sw_sync_fence_create(int fd, const char *name, unsigned int value) +{ + struct sw_sync_create_fence_data data = {}; + int err; + + data.value = value; + strncpy(data.name, name, sizeof(data.name) - 1); + data.name[sizeof(data.name) - 1] = '\0'; + + err = ioctl(fd, SW_SYNC_IOC_CREATE_FENCE, &data); + if (err < 0) + return err; + + return data.fence; +} + +int sw_sync_fence_is_valid(int fd) +{ + /* Same code! */ + return sw_sync_timeline_is_valid(fd); +} + +void sw_sync_fence_destroy(int fd) +{ + if (sw_sync_fence_is_valid(fd)) + close(fd); +} diff --git a/tools/testing/selftests/sync/sync.h b/tools/testing/selftests/sync/sync.h new file mode 100644 index 0000000..fb71561 --- /dev/null +++ b/tools/testing/selftests/sync/sync.h @@ -0,0 +1,40 @@ +/* + * sync abstraction + * Copyright 2015-2016 Collabora Ltd. + * + * Based on the implementation from the Android Open Source Project, + * + * Copyright 2012 Google, Inc + * + * 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 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. + */ + +#ifndef SELFTESTS_SYNC_H +#define SELFTESTS_SYNC_H + +#define FENCE_STATUS_ERROR (-1) +#define FENCE_STATUS_ACTIVE (0) +#define FENCE_STATUS_SIGNALED (1) + +int sync_wait(int fd, int timeout); +int sync_merge(const char *name, int fd1, int fd2); +int sync_fence_size(int fd); +int sync_fence_count_with_status(int fd, int status); + +#endif diff --git a/tools/testing/selftests/sync/sync_alloc.c b/tools/testing/selftests/sync/sync_alloc.c new file mode 100644 index 0000000..66a28af --- /dev/null +++ b/tools/testing/selftests/sync/sync_alloc.c @@ -0,0 +1,74 @@ +/* + * sync allocation tests + * Copyright 2015-2016 Collabora Ltd. + * + * Based on the implementation from the Android Open Source Project, + * + * Copyright 2012 Google, Inc + * + * 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 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. + */ + +#include "sync.h" +#include "sw_sync.h" +#include "synctest.h" + +int test_alloc_timeline(void) +{ + int timeline, valid; + + timeline = sw_sync_timeline_create(); + valid = sw_sync_timeline_is_valid(timeline); + ASSERT(valid, "Failure allocating timeline\n"); + + sw_sync_timeline_destroy(timeline); + return 0; +} + +int test_alloc_fence(void) +{ + int timeline, fence, valid; + + timeline = sw_sync_timeline_create(); + valid = sw_sync_timeline_is_valid(timeline); + ASSERT(valid, "Failure allocating timeline\n"); + + fence = sw_sync_fence_create(timeline, "allocFence", 1); + valid = sw_sync_fence_is_valid(fence); + ASSERT(valid, "Failure allocating fence\n"); + + sw_sync_fence_destroy(fence); + sw_sync_timeline_destroy(timeline); + return 0; +} + +int test_alloc_fence_negative(void) +{ + int fence, timeline; + + timeline = sw_sync_timeline_create(); + ASSERT(timeline > 0, "Failure allocating timeline\n"); + + fence = sw_sync_fence_create(-1, "fence", 1); + ASSERT(fence < 0, "Success allocating negative fence\n"); + + sw_sync_fence_destroy(fence); + sw_sync_timeline_destroy(timeline); + return 0; +} diff --git a/tools/testing/selftests/sync/sync_fence.c b/tools/testing/selftests/sync/sync_fence.c new file mode 100644 index 0000000..13f1752 --- /dev/null +++ b/tools/testing/selftests/sync/sync_fence.c @@ -0,0 +1,132 @@ +/* + * sync fence tests with one timeline + * Copyright 2015-2016 Collabora Ltd. + * + * Based on the implementation from the Android Open Source Project, + * + * Copyright 2012 Google, Inc + * + * 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 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. + */ + +#include "sync.h" +#include "sw_sync.h" +#include "synctest.h" + +int test_fence_one_timeline_wait(void) +{ + int fence, valid, ret; + int timeline = sw_sync_timeline_create(); + + valid = sw_sync_timeline_is_valid(timeline); + ASSERT(valid, "Failure allocating timeline\n"); + + fence = sw_sync_fence_create(timeline, "allocFence", 5); + valid = sw_sync_fence_is_valid(fence); + ASSERT(valid, "Failure allocating fence\n"); + + /* Wait on fence until timeout */ + ret = sync_wait(fence, 0); + ASSERT(ret == 0, "Failure waiting on fence until timeout\n"); + + /* Advance timeline from 0 -> 1 */ + ret = sw_sync_timeline_inc(timeline, 1); + ASSERT(ret == 0, "Failure advancing timeline\n"); + + /* Wait on fence until timeout */ + ret = sync_wait(fence, 0); + ASSERT(ret == 0, "Failure waiting on fence until timeout\n"); + + /* Signal the fence */ + ret = sw_sync_timeline_inc(timeline, 4); + ASSERT(ret == 0, "Failure signaling the fence\n"); + + /* Wait successfully */ + ret = sync_wait(fence, 0); + ASSERT(ret > 0, "Failure waiting on fence\n"); + + /* Go even further, and confirm wait still succeeds */ + ret = sw_sync_timeline_inc(timeline, 10); + ASSERT(ret == 0, "Failure going further\n"); + ret = sync_wait(fence, 0); + ASSERT(ret > 0, "Failure waiting ahead\n"); + + sw_sync_fence_destroy(fence); + sw_sync_timeline_destroy(timeline); + + return 0; +} + +int test_fence_one_timeline_merge(void) +{ + int a, b, c, d, valid; + int timeline = sw_sync_timeline_create(); + + /* create fence a,b,c and then merge them all into fence d */ + a = sw_sync_fence_create(timeline, "allocFence", 1); + b = sw_sync_fence_create(timeline, "allocFence", 2); + c = sw_sync_fence_create(timeline, "allocFence", 3); + + valid = sw_sync_fence_is_valid(a) && + sw_sync_fence_is_valid(b) && + sw_sync_fence_is_valid(c); + ASSERT(valid, "Failure allocating fences\n"); + + d = sync_merge("mergeFence", b, a); + d = sync_merge("mergeFence", c, d); + valid = sw_sync_fence_is_valid(d); + ASSERT(valid, "Failure merging fences\n"); + + /* confirm all fences have one active point (even d) */ + ASSERT(sync_fence_count_with_status(a, FENCE_STATUS_ACTIVE) == 1, + "a has too many active fences!\n"); + ASSERT(sync_fence_count_with_status(a, FENCE_STATUS_ACTIVE) == 1, + "b has too many active fences!\n"); + ASSERT(sync_fence_count_with_status(a, FENCE_STATUS_ACTIVE) == 1, + "c has too many active fences!\n"); + ASSERT(sync_fence_count_with_status(a, FENCE_STATUS_ACTIVE) == 1, + "d has too many active fences!\n"); + + /* confirm that d is not signaled until the max of a,b,c */ + sw_sync_timeline_inc(timeline, 1); + ASSERT(sync_fence_count_with_status(a, FENCE_STATUS_SIGNALED) == 1, + "a did not signal!\n"); + ASSERT(sync_fence_count_with_status(d, FENCE_STATUS_ACTIVE) == 1, + "d signaled too early!\n"); + + sw_sync_timeline_inc(timeline, 1); + ASSERT(sync_fence_count_with_status(b, FENCE_STATUS_SIGNALED) == 1, + "b did not signal!\n"); + ASSERT(sync_fence_count_with_status(d, FENCE_STATUS_ACTIVE) == 1, + "d signaled too early!\n"); + + sw_sync_timeline_inc(timeline, 1); + ASSERT(sync_fence_count_with_status(c, FENCE_STATUS_SIGNALED) == 1, + "c did not signal!\n"); + ASSERT(sync_fence_count_with_status(d, FENCE_STATUS_ACTIVE) == 0 && + sync_fence_count_with_status(d, FENCE_STATUS_SIGNALED) == 1, + "d did not signal!\n"); + + sw_sync_fence_destroy(d); + sw_sync_fence_destroy(c); + sw_sync_fence_destroy(b); + sw_sync_fence_destroy(a); + sw_sync_timeline_destroy(timeline); + return 0; +} diff --git a/tools/testing/selftests/sync/sync_merge.c b/tools/testing/selftests/sync/sync_merge.c new file mode 100644 index 0000000..8914d43 --- /dev/null +++ b/tools/testing/selftests/sync/sync_merge.c @@ -0,0 +1,60 @@ +/* + * sync fence merge tests + * Copyright 2015-2016 Collabora Ltd. + * + * Based on the implementation from the Android Open Source Project, + * + * Copyright 2012 Google, Inc + * + * 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 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. + */ + +#include "sync.h" +#include "sw_sync.h" +#include "synctest.h" + +int test_fence_merge_same_fence(void) +{ + int fence, valid, merged; + int timeline = sw_sync_timeline_create(); + + valid = sw_sync_timeline_is_valid(timeline); + ASSERT(valid, "Failure allocating timeline\n"); + + fence = sw_sync_fence_create(timeline, "allocFence", 5); + valid = sw_sync_fence_is_valid(fence); + ASSERT(valid, "Failure allocating fence\n"); + + merged = sync_merge("mergeFence", fence, fence); + valid = sw_sync_fence_is_valid(fence); + ASSERT(valid, "Failure merging fence\n"); + + ASSERT(sync_fence_count_with_status(merged, FENCE_STATUS_SIGNALED) == 0, + "fence signaled too early!\n"); + + sw_sync_timeline_inc(timeline, 5); + ASSERT(sync_fence_count_with_status(merged, FENCE_STATUS_SIGNALED) == 1, + "fence did not signal!\n"); + + sw_sync_fence_destroy(merged); + sw_sync_fence_destroy(fence); + sw_sync_timeline_destroy(timeline); + + return 0; +} diff --git a/tools/testing/selftests/sync/sync_stress_consumer.c b/tools/testing/selftests/sync/sync_stress_consumer.c new file mode 100644 index 0000000..d9eff8d --- /dev/null +++ b/tools/testing/selftests/sync/sync_stress_consumer.c @@ -0,0 +1,185 @@ +/* + * sync stress test: producer/consumer + * Copyright 2015-2016 Collabora Ltd. + * + * Based on the implementation from the Android Open Source Project, + * + * Copyright 2012 Google, Inc + * + * 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 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. + */ + +#include + +#include "sync.h" +#include "sw_sync.h" +#include "synctest.h" + +/* 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. + */ + +/* Returns 1 on error, 0 on success */ +static int busy_wait_on_fence(int fence) +{ + int error, active; + + do { + error = sync_fence_count_with_status(fence, FENCE_STATUS_ERROR); + ASSERT(error == 0, "Error occurred on fence\n"); + active = sync_fence_count_with_status(fence, + FENCE_STATUS_ACTIVE); + } while (active); + + return 0; +} + +static struct { + int iterations; + int threads; + int counter; + int consumer_timeline; + int *producer_timelines; + pthread_mutex_t lock; +} test_data_mpsc; + +static int mpsc_producer_thread(void *d) +{ + int id = (long)d; + int fence, valid, i; + int *producer_timelines = test_data_mpsc.producer_timelines; + int consumer_timeline = test_data_mpsc.consumer_timeline; + int iterations = test_data_mpsc.iterations; + + for (i = 0; i < iterations; i++) { + fence = sw_sync_fence_create(consumer_timeline, "fence", i); + valid = sw_sync_fence_is_valid(fence); + ASSERT(valid, "Failure creating fence\n"); + + /* + * Wait for the consumer to finish. Use alternate + * means of waiting on the fence + */ + + if ((iterations + id) % 8 != 0) { + ASSERT(sync_wait(fence, -1) > 0, + "Failure waiting on fence\n"); + } else { + ASSERT(busy_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_data_mpsc.lock); + test_data_mpsc.counter++; + pthread_mutex_unlock(&test_data_mpsc.lock); + + ASSERT(sw_sync_timeline_inc(producer_timelines[id], 1) == 0, + "Error advancing producer timeline\n"); + + sw_sync_fence_destroy(fence); + } + + return 0; +} + +static int mpcs_consumer_thread(void) +{ + int fence, merged, tmp, valid, it, i; + int *producer_timelines = test_data_mpsc.producer_timelines; + int consumer_timeline = test_data_mpsc.consumer_timeline; + int iterations = test_data_mpsc.iterations; + int n = test_data_mpsc.threads; + + for (it = 1; it <= iterations; it++) { + fence = sw_sync_fence_create(producer_timelines[0], "name", it); + for (i = 1; i < n; i++) { + tmp = sw_sync_fence_create(producer_timelines[i], + "name", it); + merged = sync_merge("name", tmp, fence); + sw_sync_fence_destroy(tmp); + sw_sync_fence_destroy(fence); + fence = merged; + } + + valid = sw_sync_fence_is_valid(fence); + ASSERT(valid, "Failure merging fences\n"); + + /* + * Make sure we see an increment from every producer thread. + * Vary the means by which we wait. + */ + if (iterations % 8 != 0) { + ASSERT(sync_wait(fence, -1) > 0, + "Producers did not increment as expected\n"); + } else { + ASSERT(busy_wait_on_fence(fence) == 0, + "Producers did not increment as expected\n"); + } + + ASSERT(test_data_mpsc.counter == n * it, + "Counter value mismatch!\n"); + + /* Release the producer threads */ + ASSERT(sw_sync_timeline_inc(consumer_timeline, 1) == 0, + "Failure releasing producer threads\n"); + + sw_sync_fence_destroy(fence); + } + + return 0; +} + +int test_consumer_stress_multi_producer_single_consumer(void) +{ + int iterations = 1 << 12; + int n = 5; + long i, ret; + int producer_timelines[n]; + int consumer_timeline; + pthread_t threads[n]; + + consumer_timeline = sw_sync_timeline_create(); + for (i = 0; i < n; i++) + producer_timelines[i] = sw_sync_timeline_create(); + + test_data_mpsc.producer_timelines = producer_timelines; + test_data_mpsc.consumer_timeline = consumer_timeline; + test_data_mpsc.iterations = iterations; + test_data_mpsc.threads = n; + test_data_mpsc.counter = 0; + pthread_mutex_init(&test_data_mpsc.lock, NULL); + + for (i = 0; i < n; i++) { + pthread_create(&threads[i], NULL, (void * (*)(void *)) + mpsc_producer_thread, (void *)i); + } + + /* Consumer thread runs here */ + ret = mpcs_consumer_thread(); + + for (i = 0; i < n; i++) + pthread_join(threads[i], NULL); + + return ret; +} diff --git a/tools/testing/selftests/sync/sync_stress_merge.c b/tools/testing/selftests/sync/sync_stress_merge.c new file mode 100644 index 0000000..99e83ef --- /dev/null +++ b/tools/testing/selftests/sync/sync_stress_merge.c @@ -0,0 +1,115 @@ +/* + * sync stress test: merging + * Copyright 2015-2016 Collabora Ltd. + * + * Based on the implementation from the Android Open Source Project, + * + * Copyright 2012 Google, Inc + * + * 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 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. + */ + +#include +#include +#include + +#include "sync.h" +#include "sw_sync.h" +#include "synctest.h" + +int test_merge_stress_random_merge(void) +{ + int i, size, ret; + int timeline_count = 32; + int merge_count = 1024 * 32; + int timelines[timeline_count]; + int fence_map[timeline_count]; + int fence, tmpfence, merged, valid; + int timeline, timeline_offset, sync_point; + + srand(time(NULL)); + + for (i = 0; i < timeline_count; i++) + timelines[i] = sw_sync_timeline_create(); + + fence = sw_sync_fence_create(timelines[0], "fence", 0); + valid = sw_sync_fence_is_valid(fence); + ASSERT(valid, "Failure creating fence\n"); + + memset(fence_map, -1, sizeof(fence_map)); + fence_map[0] = 0; + + /* + * Randomly create sync_points out of a fixed set of timelines, + * and merge them together + */ + for (i = 0; i < merge_count; i++) { + /* Generate sync_point. */ + timeline_offset = rand() % timeline_count; + timeline = timelines[timeline_offset]; + sync_point = rand(); + + /* Keep track of the latest sync_point in each timeline. */ + if (fence_map[timeline_offset] == -1) + fence_map[timeline_offset] = sync_point; + else if (fence_map[timeline_offset] < sync_point) + fence_map[timeline_offset] = sync_point; + + /* Merge */ + tmpfence = sw_sync_fence_create(timeline, "fence", sync_point); + merged = sync_merge("merge", tmpfence, fence); + sw_sync_fence_destroy(tmpfence); + sw_sync_fence_destroy(fence); + fence = merged; + + valid = sw_sync_fence_is_valid(merged); + ASSERT(valid, "Failure creating fence i\n"); + } + + size = 0; + for (i = 0; i < timeline_count; i++) + if (fence_map[i] != -1) + size++; + + /* Confirm our map matches the fence. */ + ASSERT(sync_fence_size(fence) == size, + "Quantity of elements not matching\n"); + + /* Trigger the merged fence */ + for (i = 0; i < timeline_count; i++) { + if (fence_map[i] != -1) { + ret = sync_wait(fence, 0); + ASSERT(ret == 0, + "Failure waiting on fence until timeout\n"); + /* Increment the timeline to the last sync_point */ + sw_sync_timeline_inc(timelines[i], fence_map[i]); + } + } + + /* Check that the fence is triggered. */ + ret = sync_wait(fence, 0); + ASSERT(ret > 0, "Failure triggering fence\n"); + + sw_sync_fence_destroy(fence); + + for (i = 0; i < timeline_count; i++) + sw_sync_timeline_destroy(timelines[i]); + + return 0; +} diff --git a/tools/testing/selftests/sync/sync_stress_parallelism.c b/tools/testing/selftests/sync/sync_stress_parallelism.c new file mode 100644 index 0000000..e6c9be67 --- /dev/null +++ b/tools/testing/selftests/sync/sync_stress_parallelism.c @@ -0,0 +1,111 @@ +/* + * sync stress test: parallelism + * Copyright 2015-2016 Collabora Ltd. + * + * Based on the implementation from the Android Open Source Project, + * + * Copyright 2012 Google, Inc + * + * 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 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. + */ + +#include + +#include "sync.h" +#include "sw_sync.h" +#include "synctest.h" + +static struct { + int iterations; + int timeline; + int counter; +} test_data_two_threads; + +static int test_stress_two_threads_shared_timeline_thread(void *d) +{ + int thread_id = (long)d; + int timeline = test_data_two_threads.timeline; + int iterations = test_data_two_threads.iterations; + int fence, valid, ret, i; + + for (i = 0; i < iterations; i++) { + fence = sw_sync_fence_create(timeline, "fence", + i * 2 + thread_id); + valid = sw_sync_fence_is_valid(fence); + ASSERT(valid, "Failure allocating fence\n"); + + /* Wait on the prior thread to complete */ + ret = sync_wait(fence, -1); + ASSERT(ret > 0, "Problem occurred on prior thread\n"); + + /* + * Confirm the previous thread's writes are visible + * and then increment + */ + ASSERT(test_data_two_threads.counter == i * 2 + thread_id, + "Counter got damaged!\n"); + test_data_two_threads.counter++; + + /* Kick off the other thread */ + ret = sw_sync_timeline_inc(timeline, 1); + ASSERT(ret == 0, "Advancing timeline failed\n"); + + sw_sync_fence_destroy(fence); + } + + return 0; +} + +int test_stress_two_threads_shared_timeline(void) +{ + pthread_t a, b; + int valid; + int timeline = sw_sync_timeline_create(); + + valid = sw_sync_timeline_is_valid(timeline); + ASSERT(valid, "Failure allocating timeline\n"); + + test_data_two_threads.iterations = 1 << 16; + test_data_two_threads.counter = 0; + test_data_two_threads.timeline = timeline; + + /* + * Use a single timeline to synchronize two threads + * hammmering on the same counter. + */ + + pthread_create(&a, NULL, (void *(*)(void *)) + test_stress_two_threads_shared_timeline_thread, + (void *)0); + pthread_create(&b, NULL, (void *(*)(void *)) + test_stress_two_threads_shared_timeline_thread, + (void *)1); + + pthread_join(a, NULL); + pthread_join(b, NULL); + + /* make sure the threads did not trample on one another */ + ASSERT(test_data_two_threads.counter == + test_data_two_threads.iterations * 2, + "Counter has unexpected value\n"); + + sw_sync_timeline_destroy(timeline); + + return 0; +} diff --git a/tools/testing/selftests/sync/sync_test.c b/tools/testing/selftests/sync/sync_test.c new file mode 100644 index 0000000..9ea08d9 --- /dev/null +++ b/tools/testing/selftests/sync/sync_test.c @@ -0,0 +1,79 @@ +/* + * sync test runner + * Copyright 2015-2016 Collabora Ltd. + * + * Based on the implementation from the Android Open Source Project, + * + * Copyright 2012 Google, Inc + * + * 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 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. + */ + +#include +#include +#include +#include +#include + +#include "synctest.h" + +static int run_test(int (*test)(void), char *name) +{ + int result; + pid_t childpid; + + fflush(stdout); + childpid = fork(); + + if (childpid) { + waitpid(childpid, &result, 0); + if (WIFEXITED(result)) + return WEXITSTATUS(result); + return 1; + } + + printf("[RUN]\tExecuting %s\n", name); + exit(test()); +} + +int main(void) +{ + int err = 0; + + printf("[RUN]\tTesting sync framework\n"); + + err += RUN_TEST(test_alloc_timeline); + err += RUN_TEST(test_alloc_fence); + err += RUN_TEST(test_alloc_fence_negative); + + err += RUN_TEST(test_fence_one_timeline_wait); + err += RUN_TEST(test_fence_one_timeline_merge); + err += RUN_TEST(test_fence_merge_same_fence); + err += RUN_TEST(test_fence_multi_timeline_wait); + err += RUN_TEST(test_stress_two_threads_shared_timeline); + err += RUN_TEST(test_consumer_stress_multi_producer_single_consumer); + err += RUN_TEST(test_merge_stress_random_merge); + + if (err) + printf("[FAIL]\tsync errors: %d\n", err); + else + printf("[OK]\tsync\n"); + + return !!err; +} diff --git a/tools/testing/selftests/sync/sync_wait.c b/tools/testing/selftests/sync/sync_wait.c new file mode 100644 index 0000000..d69b752 --- /dev/null +++ b/tools/testing/selftests/sync/sync_wait.c @@ -0,0 +1,91 @@ +/* + * sync fence wait tests + * Copyright 2015-2016 Collabora Ltd. + * + * Based on the implementation from the Android Open Source Project, + * + * Copyright 2012 Google, Inc + * + * 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 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. + */ + +#include "sync.h" +#include "sw_sync.h" +#include "synctest.h" + +int test_fence_multi_timeline_wait(void) +{ + int timelineA, timelineB, timelineC; + int fenceA, fenceB, fenceC, merged; + int valid, active, signaled, ret; + + timelineA = sw_sync_timeline_create(); + timelineB = sw_sync_timeline_create(); + timelineC = sw_sync_timeline_create(); + + fenceA = sw_sync_fence_create(timelineA, "fenceA", 5); + fenceB = sw_sync_fence_create(timelineB, "fenceB", 5); + fenceC = sw_sync_fence_create(timelineC, "fenceC", 5); + + merged = sync_merge("mergeFence", fenceB, fenceA); + merged = sync_merge("mergeFence", fenceC, merged); + + valid = sw_sync_fence_is_valid(merged); + ASSERT(valid, "Failure merging fence from various timelines\n"); + + /* Confirm fence isn't signaled */ + active = sync_fence_count_with_status(merged, FENCE_STATUS_ACTIVE); + ASSERT(active == 3, "Fence signaled too early!\n"); + + ret = sync_wait(merged, 0); + ASSERT(ret == 0, + "Failure waiting on fence until timeout\n"); + + ret = sw_sync_timeline_inc(timelineA, 5); + active = sync_fence_count_with_status(merged, FENCE_STATUS_ACTIVE); + signaled = sync_fence_count_with_status(merged, FENCE_STATUS_SIGNALED); + ASSERT(active == 2 && signaled == 1, + "Fence did not signal properly!\n"); + + ret = sw_sync_timeline_inc(timelineB, 5); + active = sync_fence_count_with_status(merged, FENCE_STATUS_ACTIVE); + signaled = sync_fence_count_with_status(merged, FENCE_STATUS_SIGNALED); + ASSERT(active == 1 && signaled == 2, + "Fence did not signal properly!\n"); + + ret = sw_sync_timeline_inc(timelineC, 5); + active = sync_fence_count_with_status(merged, FENCE_STATUS_ACTIVE); + signaled = sync_fence_count_with_status(merged, FENCE_STATUS_SIGNALED); + ASSERT(active == 0 && signaled == 3, + "Fence did not signal properly!\n"); + + /* confirm you can successfully wait */ + ret = sync_wait(merged, 100); + ASSERT(ret > 0, "Failure waiting on signaled fence\n"); + + sw_sync_fence_destroy(merged); + sw_sync_fence_destroy(fenceC); + sw_sync_fence_destroy(fenceB); + sw_sync_fence_destroy(fenceA); + sw_sync_timeline_destroy(timelineC); + sw_sync_timeline_destroy(timelineB); + sw_sync_timeline_destroy(timelineA); + + return 0; +} diff --git a/tools/testing/selftests/sync/synctest.h b/tools/testing/selftests/sync/synctest.h new file mode 100644 index 0000000..e7d1d57 --- /dev/null +++ b/tools/testing/selftests/sync/synctest.h @@ -0,0 +1,66 @@ +/* + * sync tests + * Copyright 2015-2016 Collabora Ltd. + * + * Based on the implementation from the Android Open Source Project, + * + * Copyright 2012 Google, Inc + * + * 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 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. + */ + +#ifndef SELFTESTS_SYNCTEST_H +#define SELFTESTS_SYNCTEST_H + +#include + +#define ASSERT(cond, msg) do { \ + if (!(cond)) { \ + printf("[ERROR]\t%s", (msg)); \ + return 1; \ + } \ +} while (0) + +#define RUN_TEST(x) run_test((x), #x) + +/* Allocation tests */ +int test_alloc_timeline(void); +int test_alloc_fence(void); +int test_alloc_fence_negative(void); + +/* Fence tests with one timeline */ +int test_fence_one_timeline_wait(void); +int test_fence_one_timeline_merge(void); + +/* Fence merge tests */ +int test_fence_merge_same_fence(void); + +/* Fence wait tests */ +int test_fence_multi_timeline_wait(void); + +/* Stress test - parallelism */ +int test_stress_two_threads_shared_timeline(void); + +/* Stress test - consumer */ +int test_consumer_stress_multi_producer_single_consumer(void); + +/* Stress test - merging */ +int test_merge_stress_random_merge(void); + +#endif diff --git a/tools/testing/selftests/timers/.gitignore b/tools/testing/selftests/timers/.gitignore index 68f3fc7..cc98662 100644 --- a/tools/testing/selftests/timers/.gitignore +++ b/tools/testing/selftests/timers/.gitignore @@ -17,3 +17,4 @@ skew_consistency threadtest valid-adjtimex adjtick +set-tz