| 1 | #include <sys/membarrier.h> |
| 2 | #include <semaphore.h> |
| 3 | #include <signal.h> |
| 4 | #include <string.h> |
| 5 | #include "pthread_impl.h" |
| 6 | #include "syscall.h" |
| 7 | |
| 8 | static void dummy_0(void) |
| 9 | { |
| 10 | } |
| 11 | |
| 12 | weak_alias(dummy_0, __tl_lock); |
| 13 | weak_alias(dummy_0, __tl_unlock); |
| 14 | |
| 15 | static sem_t barrier_sem; |
| 16 | |
| 17 | static void bcast_barrier(int s) |
| 18 | { |
| 19 | 	sem_post(&barrier_sem); |
| 20 | } |
| 21 | |
| 22 | int __membarrier(int cmd, int flags) |
| 23 | { |
| 24 | 	int r = __syscall(SYS_membarrier, cmd, flags); |
| 25 | 	/* Emulate the private expedited command, which is needed by the |
| 26 | 	 * dynamic linker for installation of dynamic TLS, for older |
| 27 | 	 * kernels that lack the syscall. Unlike the syscall, this only |
| 28 | 	 * synchronizes with threads of the process, not other processes |
| 29 | 	 * sharing the VM, but such sharing is not a supported usage |
| 30 | 	 * anyway. */ |
| 31 | 	if (r && cmd == MEMBARRIER_CMD_PRIVATE_EXPEDITED && !flags) { |
| 32 | 		pthread_t self=__pthread_self(), td; |
| 33 | 		sigset_t set; |
| 34 | 		__block_app_sigs(&set); |
| 35 | 		__tl_lock(); |
| 36 | 		sem_init(&barrier_sem, 0, 0); |
| 37 | 		struct sigaction sa = { |
| 38 | 			.sa_flags = SA_RESTART | SA_ONSTACK, |
| 39 | 			.sa_handler = bcast_barrier |
| 40 | 		}; |
| 41 | 		memset(&sa.sa_mask, -1, sizeof sa.sa_mask); |
| 42 | 		if (!__libc_sigaction(SIGSYNCCALL, &sa, 0)) { |
| 43 | 			for (td=self->next; td!=self; td=td->next) |
| 44 | 				__syscall(SYS_tkill, td->tid, SIGSYNCCALL); |
| 45 | 			for (td=self->next; td!=self; td=td->next) |
| 46 | 				sem_wait(&barrier_sem); |
| 47 | 			r = 0; |
| 48 | 			sa.sa_handler = SIG_IGN; |
| 49 | 			__libc_sigaction(SIGSYNCCALL, &sa, 0); |
| 50 | 		} |
| 51 | 		sem_destroy(&barrier_sem); |
| 52 | 		__tl_unlock(); |
| 53 | 		__restore_sigs(&set); |
| 54 | 	} |
| 55 | 	return __syscall_ret(r); |
| 56 | } |
| 57 | |
| 58 | void __membarrier_init(void) |
| 59 | { |
| 60 | 	/* If membarrier is linked, attempt to pre-register to be able to use |
| 61 | 	 * the private expedited command before the process becomes multi- |
| 62 | 	 * threaded, since registering later has bad, potentially unbounded |
| 63 | 	 * latency. This syscall should be essentially free, and it's arguably |
| 64 | 	 * a mistake in the API design that registration was even required. |
| 65 | 	 * For other commands, registration may impose some cost, so it's left |
| 66 | 	 * to the application to do so if desired. Unfortunately this means |
| 67 | 	 * library code initialized after the process becomes multi-threaded |
| 68 | 	 * cannot use these features without accepting registration latency. */ |
| 69 | 	__syscall(SYS_membarrier, MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, 0); |
| 70 | } |
| 71 | |
| 72 | weak_alias(__membarrier, membarrier); |