1 // SPDX-License-Identifier: LGPL-2.1 2 /* 3 * rseq.c 4 * 5 * Copyright (C) 2016 Mathieu Desnoyers <mathieu.desnoyers (at) 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 29 #include "rseq.h" 30 31 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 32 33 __attribute__((tls_model("initial-exec"))) __thread 34 volatile struct rseq __rseq_abi = { 35 .cpu_id = RSEQ_CPU_ID_UNINITIALIZED, 36 }; 37 38 static __attribute__((tls_model("initial-exec"))) __thread 39 volatile int refcount; 40 41 static void signal_off_save(sigset_t *oldset) 42 { 43 sigset_t set; 44 int ret; 45 46 sigfillset(&set); 47 ret = pthread_sigmask(SIG_BLOCK, &set, oldset); 48 if (ret) 49 abort(); 50 } 51 52 static void signal_restore(sigset_t oldset) 53 { 54 int ret; 55 56 ret = pthread_sigmask(SIG_SETMASK, &oldset, NULL); 57 if (ret) 58 abort(); 59 } 60 61 static int sys_rseq(volatile struct rseq *rseq_abi, uint32_t rseq_len, 62 int flags, uint32_t sig) 63 { 64 return syscall(__NR_rseq, rseq_abi, rseq_len, flags, sig); 65 } 66 67 int rseq_register_current_thread(void) 68 { 69 int rc, ret = 0; 70 sigset_t oldset; 71 72 signal_off_save(&oldset); 73 if (refcount++) 74 goto end; 75 rc = sys_rseq(&__rseq_abi, sizeof(struct rseq), 0, RSEQ_SIG); 76 if (!rc) { 77 assert(rseq_current_cpu_raw() >= 0); 78 goto end; 79 } 80 if (errno != EBUSY) 81 __rseq_abi.cpu_id = -2; 82 ret = -1; 83 refcount--; 84 end: 85 signal_restore(oldset); 86 return ret; 87 } 88 89 int rseq_unregister_current_thread(void) 90 { 91 int rc, ret = 0; 92 sigset_t oldset; 93 94 signal_off_save(&oldset); 95 if (--refcount) 96 goto end; 97 rc = sys_rseq(&__rseq_abi, sizeof(struct rseq), 98 RSEQ_FLAG_UNREGISTER, RSEQ_SIG); 99 if (!rc) 100 goto end; 101 ret = -1; 102 end: 103 signal_restore(oldset); 104 return ret; 105 } 106 107 int32_t rseq_fallback_current_cpu(void) 108 { 109 int32_t cpu; 110 111 cpu = sched_getcpu(); 112 if (cpu < 0) { 113 perror("sched_getcpu()"); 114 abort(); 115 } 116 return cpu; 117 } 118