Add cpu-opv helpers
[librseq.git] / src / rseq.c
CommitLineData
784b0012
MD
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
29#include <rseq/rseq.h>
30
31#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
32
33/*
34 * linux/rseq.h defines struct rseq as aligned on 32 bytes. The kernel ABI
35 * size is 20 bytes. For support of multiple rseq users within a process,
36 * user-space defines an extra 4 bytes field as a reference count, for a
37 * total of 24 bytes.
38 */
39struct libc_rseq {
40 /* kernel-userspace ABI. */
41 __u32 cpu_id_start;
42 __u32 cpu_id;
43 __u64 rseq_cs;
44 __u32 flags;
45 /* user-space ABI. */
46 __u32 refcount;
47} __attribute__((aligned(4 * sizeof(__u64))));
48
49__attribute__((visibility("hidden"))) __thread
50volatile struct libc_rseq __lib_rseq_abi = {
51 .cpu_id = RSEQ_CPU_ID_UNINITIALIZED,
52};
53
54extern __attribute__((weak, alias("__lib_rseq_abi"))) __thread
55volatile struct rseq __rseq_abi;
56
57static void signal_off_save(sigset_t *oldset)
58{
59 sigset_t set;
60 int ret;
61
62 sigfillset(&set);
63 ret = pthread_sigmask(SIG_BLOCK, &set, oldset);
64 if (ret)
65 abort();
66}
67
68static void signal_restore(sigset_t oldset)
69{
70 int ret;
71
72 ret = pthread_sigmask(SIG_SETMASK, &oldset, NULL);
73 if (ret)
74 abort();
75}
76
77static int sys_rseq(volatile struct rseq *rseq_abi, uint32_t rseq_len,
78 int flags, uint32_t sig)
79{
80 return syscall(__NR_rseq, rseq_abi, rseq_len, flags, sig);
81}
82
83int rseq_register_current_thread(void)
84{
85 int rc, ret = 0;
86 sigset_t oldset;
87
88 signal_off_save(&oldset);
89 if (__lib_rseq_abi.refcount++)
90 goto end;
91 rc = sys_rseq(&__rseq_abi, sizeof(struct rseq), 0, RSEQ_SIG);
92 if (!rc) {
93 assert(rseq_current_cpu_raw() >= 0);
94 goto end;
95 }
96 if (errno != EBUSY)
97 __rseq_abi.cpu_id = RSEQ_CPU_ID_REGISTRATION_FAILED;
98 ret = -1;
99 __lib_rseq_abi.refcount--;
100end:
101 signal_restore(oldset);
102 return ret;
103}
104
105int rseq_unregister_current_thread(void)
106{
107 int rc, ret = 0;
108 sigset_t oldset;
109
110 signal_off_save(&oldset);
111 if (--__lib_rseq_abi.refcount)
112 goto end;
113 rc = sys_rseq(&__rseq_abi, sizeof(struct rseq),
114 RSEQ_FLAG_UNREGISTER, RSEQ_SIG);
115 if (!rc)
116 goto end;
117 ret = -1;
118end:
119 signal_restore(oldset);
120 return ret;
121}
122
123int32_t rseq_fallback_current_cpu(void)
124{
125 int32_t cpu;
126
127 cpu = sched_getcpu();
128 if (cpu < 0) {
129 perror("sched_getcpu()");
130 abort();
131 }
132 return cpu;
133}
This page took 0.027358 seconds and 4 git commands to generate.