diff options
Diffstat (limited to 'kernel/tests/lib/tst_checkpoint.c')
-rw-r--r-- | kernel/tests/lib/tst_checkpoint.c | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/kernel/tests/lib/tst_checkpoint.c b/kernel/tests/lib/tst_checkpoint.c new file mode 100644 index 0000000..5e5b114 --- /dev/null +++ b/kernel/tests/lib/tst_checkpoint.c @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2015 Cyril Hrubis <chrubis@suse.cz> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <stdint.h> +#include <limits.h> +#include <errno.h> +#include <sys/syscall.h> +#include <linux/futex.h> + +#include "test.h" +#include "safe_macros.h" +#include "lapi/futex.h" + +#define DEFAULT_MSEC_TIMEOUT 10000 + +futex_t *tst_futexes; +unsigned int tst_max_futexes; + +void tst_checkpoint_init(const char *file, const int lineno, + void (*cleanup_fn)(void)) +{ + int fd; + unsigned int page_size; + + if (tst_futexes) { + tst_brkm(TBROK, cleanup_fn, + "%s: %d checkpoints already initialized", + file, lineno); + return; + } + + /* + * The parent test process is responsible for creating the temporary + * directory and therefore must pass non-zero cleanup (to remove the + * directory if something went wrong). + * + * We cannot do this check unconditionally because if we need to init + * the checkpoint from a binary that was started by exec() the + * tst_tmpdir_created() will return false because the tmpdir was + * created by parent. In this case we expect the subprogram can call + * the init as a first function with NULL as cleanup function. + */ + if (cleanup_fn && !tst_tmpdir_created()) { + tst_brkm(TBROK, cleanup_fn, + "%s:%d You have to create test temporary directory " + "first (call tst_tmpdir())", file, lineno); + return; + } + + page_size = getpagesize(); + + fd = SAFE_OPEN(cleanup_fn, "checkpoint_futex_base_file", + O_RDWR | O_CREAT, 0666); + + SAFE_FTRUNCATE(cleanup_fn, fd, page_size); + + tst_futexes = SAFE_MMAP(cleanup_fn, NULL, page_size, + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + + tst_max_futexes = page_size / sizeof(uint32_t); + + SAFE_CLOSE(cleanup_fn, fd); +} + +int tst_checkpoint_wait(unsigned int id, unsigned int msec_timeout) +{ + struct timespec timeout; + int ret; + + if (id >= tst_max_futexes) { + errno = EOVERFLOW; + return -1; + } + + timeout.tv_sec = msec_timeout/1000; + timeout.tv_nsec = (msec_timeout%1000) * 1000000; + + do { + ret = syscall(SYS_futex, &tst_futexes[id], FUTEX_WAIT, + tst_futexes[id], &timeout); + } while (ret == -1 && errno == EINTR); + + return ret; +} + +int tst_checkpoint_wake(unsigned int id, unsigned int nr_wake, + unsigned int msec_timeout) +{ + unsigned int msecs = 0, waked = 0; + + if (id >= tst_max_futexes) { + errno = EOVERFLOW; + return -1; + } + + for (;;) { + waked += syscall(SYS_futex, &tst_futexes[id], FUTEX_WAKE, + INT_MAX, NULL); + + if (waked == nr_wake) + break; + + usleep(1000); + msecs++; + + if (msecs >= msec_timeout) { + errno = ETIMEDOUT; + return -1; + } + } + + return 0; +} + +void tst_safe_checkpoint_wait(const char *file, const int lineno, + void (*cleanup_fn)(void), unsigned int id, + unsigned int msec_timeout) +{ + int ret; + + if (!msec_timeout) + msec_timeout = DEFAULT_MSEC_TIMEOUT; + + ret = tst_checkpoint_wait(id, msec_timeout); + + if (ret) { + tst_brkm(TBROK | TERRNO, cleanup_fn, + "%s:%d: tst_checkpoint_wait(%u, %i)", + file, lineno, id, msec_timeout); + } +} + +void tst_safe_checkpoint_wake(const char *file, const int lineno, + void (*cleanup_fn)(void), unsigned int id, + unsigned int nr_wake) +{ + int ret = tst_checkpoint_wake(id, nr_wake, DEFAULT_MSEC_TIMEOUT); + + if (ret) { + tst_brkm(TBROK | TERRNO, cleanup_fn, + "%s:%d: tst_checkpoint_wake(%u, %u, %i)", + file, lineno, id, nr_wake, DEFAULT_MSEC_TIMEOUT); + } +} |