X-Git-Url: http://git.liburcu.org/?a=blobdiff_plain;f=libust%2Ftracectl.c;h=0cff6fda9f035e8a009232268530e8e39e206ab4;hb=83aa16929ed1e60fabe1bda4824e7c4942ff42ec;hp=f720244530c9f60b77f5978875f712a5ea69658a;hpb=1d471d9fd21c9ef20cd7b0ea54a71168f0ad1b6e;p=ust.git diff --git a/libust/tracectl.c b/libust/tracectl.c index f720244..0cff6fd 100644 --- a/libust/tracectl.c +++ b/libust/tracectl.c @@ -63,6 +63,16 @@ static char receive_buffer[USTCOMM_BUFFER_SIZE]; static char send_buffer[USTCOMM_BUFFER_SIZE]; static int epoll_fd; + +/* + * Listener thread data vs fork() protection mechanism. Ensures that no listener + * thread mutexes and data structures are being concurrently modified or held by + * other threads when fork() is executed. + */ +static pthread_mutex_t listener_thread_data_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* Mutex protecting listen_sock. Nests inside listener_thread_data_mutex. */ +static pthread_mutex_t listen_sock_mutex = PTHREAD_MUTEX_INITIALIZER; static struct ustcomm_sock *listen_sock; extern struct chan_info_struct chan_infos[]; @@ -533,9 +543,19 @@ unlock_traces: return retval; } +static void release_listener_mutex(void *ptr) +{ + pthread_mutex_unlock(&listener_thread_data_mutex); +} + static void listener_cleanup(void *ptr) { - ustcomm_del_named_sock(listen_sock, 0); + pthread_mutex_lock(&listen_sock_mutex); + if (listen_sock) { + ustcomm_del_named_sock(listen_sock, 0); + listen_sock = NULL; + } + pthread_mutex_unlock(&listen_sock_mutex); } static void force_subbuf_switch() @@ -937,7 +957,7 @@ static void process_client_cmd(struct ustcomm_header *recv_header, print_markers(fp); fclose(fp); - reply_header->size = size; + reply_header->size = size + 1; /* Include final \0 */ result = ustcomm_send(sock, reply_header, ptr); @@ -963,7 +983,7 @@ static void process_client_cmd(struct ustcomm_header *recv_header, print_trace_events(fp); fclose(fp); - reply_header->size = size; + reply_header->size = size + 1; /* Include final \0 */ result = ustcomm_send(sock, reply_header, ptr); @@ -1080,6 +1100,8 @@ void *listener_main(void *p) } for (i = 0; i < nfds; i++) { + pthread_mutex_lock(&listener_thread_data_mutex); + pthread_cleanup_push(release_listener_mutex, NULL); epoll_sock = (struct ustcomm_sock *)events[i].data.ptr; if (epoll_sock == listen_sock) { addr_size = sizeof(struct sockaddr); @@ -1108,6 +1130,7 @@ void *listener_main(void *p) epoll_sock->fd); } } + pthread_cleanup_pop(1); /* release listener mutex */ } } @@ -1578,9 +1601,6 @@ static void ust_fork(void) /* Get the pid of the new process */ processpid = getpid(); - /* break lock if necessary */ - ltt_unlock_traces(); - /* * FIXME: This could be prettier, we loop over the list twice and * following good locking practice should lock around the loop @@ -1608,8 +1628,11 @@ static void ust_fork(void) ltt_trace_destroy(trace->trace_name, 1); } - /* Clean up the listener socket and epoll, keeping the scoket file */ - ustcomm_del_named_sock(listen_sock, 1); + /* Clean up the listener socket and epoll, keeping the socket file */ + if (listen_sock) { + ustcomm_del_named_sock(listen_sock, 1); + listen_sock = NULL; + } close(epoll_fd); /* Re-start the launch sequence */ @@ -1664,6 +1687,16 @@ void ust_before_fork(ust_fork_info_t *fork_info) PERROR("sigprocmask"); return; } + + /* + * Take the fork lock to make sure we are not in the middle of + * something in the listener thread. + */ + pthread_mutex_lock(&listener_thread_data_mutex); + /* + * Hold listen_sock_mutex to protect from listen_sock teardown. + */ + pthread_mutex_lock(&listen_sock_mutex); } /* Don't call this function directly in a traced program */ @@ -1671,6 +1704,9 @@ static void ust_after_fork_common(ust_fork_info_t *fork_info) { int result; + pthread_mutex_unlock(&listen_sock_mutex); + pthread_mutex_unlock(&listener_thread_data_mutex); + /* Restore signals */ result = sigprocmask(SIG_SETMASK, &fork_info->orig_sigs, NULL); if (result == -1) { @@ -1681,7 +1717,7 @@ static void ust_after_fork_common(ust_fork_info_t *fork_info) void ust_after_fork_parent(ust_fork_info_t *fork_info) { - /* Reenable signals */ + /* Release mutexes and reenable signals */ ust_after_fork_common(fork_info); } @@ -1690,7 +1726,15 @@ void ust_after_fork_child(ust_fork_info_t *fork_info) /* First sanitize the child */ ust_fork(); - /* Then reenable interrupts */ + /* Then release mutexes and reenable signals */ ust_after_fork_common(fork_info); + + /* + * Make sure we clean up the urcu-bp thread list in the child by running + * the garbage collection before any pthread_create can be called. + * Failure to do so could lead to a deadlock caused by reuse of a thread + * ID before urcu-bp garbage collection is performed. + */ + synchronize_rcu(); }