X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=libringbuffer%2Frseq.c;fp=libringbuffer%2Frseq.c;h=0be7d10bd7236195ba9ce5204bae1a0a51fa93fd;hb=26cc635c6066aea8d0c4109c63da574f0448f4fa;hp=219f416eabf3c5c14f7f1aab0ec0dcf3a2d2be22;hpb=38bfb073581f6dcf089d0e4b763ed40530f28f2e;p=lttng-ust.git diff --git a/libringbuffer/rseq.c b/libringbuffer/rseq.c index 219f416e..0be7d10b 100644 --- a/libringbuffer/rseq.c +++ b/libringbuffer/rseq.c @@ -31,10 +31,24 @@ __attribute__((weak)) __thread volatile struct rseq __rseq_abi = { .u.e.cpu_id = -1, }; +/* Own state, not shared with other libs. */ +static __thread int rseq_registered; + +static pthread_key_t rseq_key; + + +#ifdef __NR_rseq static int sys_rseq(volatile struct rseq *rseq_abi, int flags) { return syscall(__NR_rseq, rseq_abi, flags); } +#else +static int sys_rseq(volatile struct rseq *rseq_abi, int flags) +{ + errno = ENOSYS; + return -1; +} +#endif static void signal_off_save(sigset_t *oldset) { @@ -56,29 +70,82 @@ static void signal_restore(sigset_t oldset) abort(); } +int rseq_unregister_current_thread(void) +{ + sigset_t oldset; + int rc, ret = 0; + + signal_off_save(&oldset); + if (rseq_registered) { + rc = sys_rseq(NULL, 0); + if (rc) { + fprintf(stderr, "Error: sys_rseq(...) failed(%d): %s\n", + errno, strerror(errno)); + ret = -1; + goto end; + } + rseq_registered = 0; + } +end: + signal_restore(oldset); + return ret; +} + +static void destroy_rseq_key(void *key) +{ + if (rseq_unregister_current_thread()) + abort(); +} + int rseq_register_current_thread(void) { - int rc; + sigset_t oldset; + int rc, ret = 0; - rc = sys_rseq(&__rseq_abi, 0); - if (rc) { - fprintf(stderr, "Error: sys_rseq(...) failed(%d): %s\n", - errno, strerror(errno)); - return -1; + signal_off_save(&oldset); + if (caa_likely(!rseq_registered)) { + rc = sys_rseq(&__rseq_abi, 0); + if (rc) { + fprintf(stderr, "Error: sys_rseq(...) failed(%d): %s\n", + errno, strerror(errno)); + __rseq_abi.u.e.cpu_id = -2; + ret = -1; + goto end; + } + rseq_registered = 1; + assert(rseq_current_cpu_raw() >= 0); + /* + * Register destroy notifier. Pointer needs to + * be non-NULL. + */ + if (pthread_setspecific(rseq_key, (void *)0x1)) + abort(); } - assert(rseq_current_cpu() >= 0); - return 0; +end: + signal_restore(oldset); + return ret; } -int rseq_unregister_current_thread(void) +void rseq_init(void) { - int rc; + int ret; - rc = sys_rseq(NULL, 0); - if (rc) { - fprintf(stderr, "Error: sys_rseq(...) failed(%d): %s\n", - errno, strerror(errno)); - return -1; + ret = pthread_key_create(&rseq_key, destroy_rseq_key); + if (ret) { + errno = -ret; + perror("pthread_key_create"); + abort(); + } +} + +void rseq_destroy(void) +{ + int ret; + + ret = pthread_key_delete(rseq_key); + if (ret) { + errno = -ret; + perror("pthread_key_delete"); + abort(); } - return 0; }