Home | History | Annotate | Download | only in rseq
      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