Commit | Line | Data |
---|---|---|
744d0b8b | 1 | // SPDX-License-Identifier: LGPL-2.1-only |
784b0012 MD |
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 | */ | |
2cbca301 | 17 | #ifndef _GNU_SOURCE |
784b0012 | 18 | #define _GNU_SOURCE |
2cbca301 | 19 | #endif |
784b0012 MD |
20 | #include <errno.h> |
21 | #include <sched.h> | |
22 | #include <stdio.h> | |
23 | #include <stdlib.h> | |
24 | #include <string.h> | |
25 | #include <unistd.h> | |
26 | #include <syscall.h> | |
27 | #include <assert.h> | |
28 | #include <signal.h> | |
0ceae74a | 29 | #include <limits.h> |
784b0012 MD |
30 | |
31 | #include <rseq/rseq.h> | |
32 | ||
33 | #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) | |
34 | ||
1f7e39d3 MD |
35 | __attribute__((weak)) __thread |
36 | volatile struct rseq __rseq_abi = { | |
784b0012 MD |
37 | .cpu_id = RSEQ_CPU_ID_UNINITIALIZED, |
38 | }; | |
39 | ||
1f7e39d3 MD |
40 | __attribute__((weak)) __thread |
41 | volatile uint32_t __rseq_refcount; | |
784b0012 | 42 | |
52e82b87 MD |
43 | static int sys_rseq(volatile struct rseq *rseq_abi, uint32_t rseq_len, |
44 | int flags, uint32_t sig) | |
45 | { | |
46 | return syscall(__NR_rseq, rseq_abi, rseq_len, flags, sig); | |
47 | } | |
48 | ||
49 | int rseq_available(void) | |
50 | { | |
51 | int rc; | |
52 | ||
a5fd0584 | 53 | rc = sys_rseq(NULL, 0, 0, 0); |
52e82b87 MD |
54 | if (rc != -1) |
55 | abort(); | |
56 | switch (errno) { | |
57 | case ENOSYS: | |
58 | return 0; | |
59 | case EINVAL: | |
60 | return 1; | |
61 | default: | |
62 | abort(); | |
63 | } | |
64 | } | |
65 | ||
784b0012 MD |
66 | static void signal_off_save(sigset_t *oldset) |
67 | { | |
68 | sigset_t set; | |
69 | int ret; | |
70 | ||
71 | sigfillset(&set); | |
72 | ret = pthread_sigmask(SIG_BLOCK, &set, oldset); | |
73 | if (ret) | |
74 | abort(); | |
75 | } | |
76 | ||
77 | static void signal_restore(sigset_t oldset) | |
78 | { | |
79 | int ret; | |
80 | ||
81 | ret = pthread_sigmask(SIG_SETMASK, &oldset, NULL); | |
82 | if (ret) | |
83 | abort(); | |
84 | } | |
85 | ||
784b0012 MD |
86 | int rseq_register_current_thread(void) |
87 | { | |
88 | int rc, ret = 0; | |
89 | sigset_t oldset; | |
90 | ||
91 | signal_off_save(&oldset); | |
1f7e39d3 | 92 | if (__rseq_refcount == UINT_MAX) { |
0ceae74a MD |
93 | ret = -1; |
94 | goto end; | |
95 | } | |
1f7e39d3 | 96 | if (__rseq_refcount++) |
784b0012 MD |
97 | goto end; |
98 | rc = sys_rseq(&__rseq_abi, sizeof(struct rseq), 0, RSEQ_SIG); | |
99 | if (!rc) { | |
100 | assert(rseq_current_cpu_raw() >= 0); | |
101 | goto end; | |
102 | } | |
103 | if (errno != EBUSY) | |
104 | __rseq_abi.cpu_id = RSEQ_CPU_ID_REGISTRATION_FAILED; | |
105 | ret = -1; | |
1f7e39d3 | 106 | __rseq_refcount--; |
784b0012 MD |
107 | end: |
108 | signal_restore(oldset); | |
109 | return ret; | |
110 | } | |
111 | ||
112 | int rseq_unregister_current_thread(void) | |
113 | { | |
114 | int rc, ret = 0; | |
115 | sigset_t oldset; | |
116 | ||
117 | signal_off_save(&oldset); | |
1f7e39d3 | 118 | if (!__rseq_refcount) { |
0ceae74a MD |
119 | ret = -1; |
120 | goto end; | |
121 | } | |
1f7e39d3 | 122 | if (--__rseq_refcount) |
784b0012 MD |
123 | goto end; |
124 | rc = sys_rseq(&__rseq_abi, sizeof(struct rseq), | |
125 | RSEQ_FLAG_UNREGISTER, RSEQ_SIG); | |
126 | if (!rc) | |
127 | goto end; | |
128 | ret = -1; | |
129 | end: | |
130 | signal_restore(oldset); | |
131 | return ret; | |
132 | } | |
133 | ||
134 | int32_t rseq_fallback_current_cpu(void) | |
135 | { | |
136 | int32_t cpu; | |
137 | ||
138 | cpu = sched_getcpu(); | |
139 | if (cpu < 0) { | |
140 | perror("sched_getcpu()"); | |
141 | abort(); | |
142 | } | |
143 | return cpu; | |
144 | } |