diff options
Diffstat (limited to 'tools/testing/selftests/rseq/rseq.c')
| -rw-r--r-- | tools/testing/selftests/rseq/rseq.c | 117 | 
1 files changed, 117 insertions, 0 deletions
diff --git a/tools/testing/selftests/rseq/rseq.c b/tools/testing/selftests/rseq/rseq.c new file mode 100644 index 000000000000..4847e97ed049 --- /dev/null +++ b/tools/testing/selftests/rseq/rseq.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: LGPL-2.1 +/* + * rseq.c + * + * Copyright (C) 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; only + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#define _GNU_SOURCE +#include <errno.h> +#include <sched.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <syscall.h> +#include <assert.h> +#include <signal.h> + +#include "rseq.h" + +#define ARRAY_SIZE(arr)	(sizeof(arr) / sizeof((arr)[0])) + +__attribute__((tls_model("initial-exec"))) __thread +volatile struct rseq __rseq_abi = { +	.cpu_id = RSEQ_CPU_ID_UNINITIALIZED, +}; + +static __attribute__((tls_model("initial-exec"))) __thread +volatile int refcount; + +static void signal_off_save(sigset_t *oldset) +{ +	sigset_t set; +	int ret; + +	sigfillset(&set); +	ret = pthread_sigmask(SIG_BLOCK, &set, oldset); +	if (ret) +		abort(); +} + +static void signal_restore(sigset_t oldset) +{ +	int ret; + +	ret = pthread_sigmask(SIG_SETMASK, &oldset, NULL); +	if (ret) +		abort(); +} + +static int sys_rseq(volatile struct rseq *rseq_abi, uint32_t rseq_len, +		    int flags, uint32_t sig) +{ +	return syscall(__NR_rseq, rseq_abi, rseq_len, flags, sig); +} + +int rseq_register_current_thread(void) +{ +	int rc, ret = 0; +	sigset_t oldset; + +	signal_off_save(&oldset); +	if (refcount++) +		goto end; +	rc = sys_rseq(&__rseq_abi, sizeof(struct rseq), 0, RSEQ_SIG); +	if (!rc) { +		assert(rseq_current_cpu_raw() >= 0); +		goto end; +	} +	if (errno != EBUSY) +		__rseq_abi.cpu_id = -2; +	ret = -1; +	refcount--; +end: +	signal_restore(oldset); +	return ret; +} + +int rseq_unregister_current_thread(void) +{ +	int rc, ret = 0; +	sigset_t oldset; + +	signal_off_save(&oldset); +	if (--refcount) +		goto end; +	rc = sys_rseq(&__rseq_abi, sizeof(struct rseq), +		      RSEQ_FLAG_UNREGISTER, RSEQ_SIG); +	if (!rc) +		goto end; +	ret = -1; +end: +	signal_restore(oldset); +	return ret; +} + +int32_t rseq_fallback_current_cpu(void) +{ +	int32_t cpu; + +	cpu = sched_getcpu(); +	if (cpu < 0) { +		perror("sched_getcpu()"); +		abort(); +	} +	return cpu; +}  | 
