Linux Audio

Check our new training course

Loading...
  1// SPDX-License-Identifier: LGPL-2.1
  2/*
  3 * rseq.c
  4 *
  5 * Copyright (C) 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
  6 *
  7 * This library is free software; you can redistribute it and/or
  8 * modify it under the terms of the GNU Lesser General Public
  9 * License as published by the Free Software Foundation; only
 10 * version 2.1 of the License.
 11 *
 12 * This library is distributed in the hope that it will be useful,
 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 15 * Lesser General Public License for more details.
 16 */
 17
 18#define _GNU_SOURCE
 19#include <errno.h>
 20#include <sched.h>
 21#include <stdio.h>
 22#include <stdlib.h>
 23#include <string.h>
 24#include <unistd.h>
 25#include <syscall.h>
 26#include <assert.h>
 27#include <signal.h>
 28#include <limits.h>
 29
 30#include "rseq.h"
 31
 32#define ARRAY_SIZE(arr)	(sizeof(arr) / sizeof((arr)[0]))
 33
 34__thread volatile struct rseq __rseq_abi = {
 35	.cpu_id = RSEQ_CPU_ID_UNINITIALIZED,
 36};
 37
 38/*
 39 * Shared with other libraries. This library may take rseq ownership if it is
 40 * still 0 when executing the library constructor. Set to 1 by library
 41 * constructor when handling rseq. Set to 0 in destructor if handling rseq.
 42 */
 43int __rseq_handled;
 44
 45/* Whether this library have ownership of rseq registration. */
 46static int rseq_ownership;
 47
 48static __thread volatile uint32_t __rseq_refcount;
 49
 50static void signal_off_save(sigset_t *oldset)
 51{
 52	sigset_t set;
 53	int ret;
 54
 55	sigfillset(&set);
 56	ret = pthread_sigmask(SIG_BLOCK, &set, oldset);
 57	if (ret)
 58		abort();
 59}
 60
 61static void signal_restore(sigset_t oldset)
 62{
 63	int ret;
 64
 65	ret = pthread_sigmask(SIG_SETMASK, &oldset, NULL);
 66	if (ret)
 67		abort();
 68}
 69
 70static int sys_rseq(volatile struct rseq *rseq_abi, uint32_t rseq_len,
 71		    int flags, uint32_t sig)
 72{
 73	return syscall(__NR_rseq, rseq_abi, rseq_len, flags, sig);
 74}
 75
 76int rseq_register_current_thread(void)
 77{
 78	int rc, ret = 0;
 79	sigset_t oldset;
 80
 81	if (!rseq_ownership)
 82		return 0;
 83	signal_off_save(&oldset);
 84	if (__rseq_refcount == UINT_MAX) {
 85		ret = -1;
 86		goto end;
 87	}
 88	if (__rseq_refcount++)
 89		goto end;
 90	rc = sys_rseq(&__rseq_abi, sizeof(struct rseq), 0, RSEQ_SIG);
 91	if (!rc) {
 92		assert(rseq_current_cpu_raw() >= 0);
 93		goto end;
 94	}
 95	if (errno != EBUSY)
 96		__rseq_abi.cpu_id = RSEQ_CPU_ID_REGISTRATION_FAILED;
 97	ret = -1;
 98	__rseq_refcount--;
 99end:
100	signal_restore(oldset);
101	return ret;
102}
103
104int rseq_unregister_current_thread(void)
105{
106	int rc, ret = 0;
107	sigset_t oldset;
108
109	if (!rseq_ownership)
110		return 0;
111	signal_off_save(&oldset);
112	if (!__rseq_refcount) {
113		ret = -1;
114		goto end;
115	}
116	if (--__rseq_refcount)
117		goto end;
118	rc = sys_rseq(&__rseq_abi, sizeof(struct rseq),
119		      RSEQ_FLAG_UNREGISTER, RSEQ_SIG);
120	if (!rc)
121		goto end;
122	__rseq_refcount = 1;
123	ret = -1;
124end:
125	signal_restore(oldset);
126	return ret;
127}
128
129int32_t rseq_fallback_current_cpu(void)
130{
131	int32_t cpu;
132
133	cpu = sched_getcpu();
134	if (cpu < 0) {
135		perror("sched_getcpu()");
136		abort();
137	}
138	return cpu;
139}
140
141void __attribute__((constructor)) rseq_init(void)
142{
143	/* Check whether rseq is handled by another library. */
144	if (__rseq_handled)
145		return;
146	__rseq_handled = 1;
147	rseq_ownership = 1;
148}
149
150void __attribute__((destructor)) rseq_fini(void)
151{
152	if (!rseq_ownership)
153		return;
154	__rseq_handled = 0;
155	rseq_ownership = 0;
156}