X-Git-Url: http://git.liburcu.org/?a=blobdiff_plain;f=liblttng-ust%2Flttng-ust-comm.c;h=378ca21cfc71f354a282fe4a9a521cf77319ef02;hb=3df53fae30ba42a0ce6f61054fe38b15f13ceeb6;hp=b99bf00edec2df760df075f0ad77597a3e1a54d1;hpb=394598c1632131141b04ae80cb5e0d40004b95f0;p=lttng-ust.git diff --git a/liblttng-ust/lttng-ust-comm.c b/liblttng-ust/lttng-ust-comm.c index b99bf00e..378ca21c 100644 --- a/liblttng-ust/lttng-ust-comm.c +++ b/liblttng-ust/lttng-ust-comm.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include @@ -63,11 +62,119 @@ static int initialized; * The ust_lock/ust_unlock lock is used as a communication thread mutex. * Held when handling a command, also held by fork() to deal with * removal of threads, and by exit path. + * + * The UST lock is the centralized mutex across UST tracing control and + * probe registration. + * + * ust_exit_mutex must never nest in ust_mutex. + * + * ust_fork_mutex must never nest in ust_mutex. + * + * ust_mutex_nest is a per-thread nesting counter, allowing the perf + * counter lazy initialization called by events within the statedump, + * which traces while the ust_mutex is held. + * + * ust_lock nests within the dynamic loader lock (within glibc) because + * it is taken within the library constructor. */ +static pthread_mutex_t ust_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* Allow nesting the ust_mutex within the same thread. */ +static DEFINE_URCU_TLS(int, ust_mutex_nest); + +/* + * ust_exit_mutex protects thread_active variable wrt thread exit. It + * cannot be done by ust_mutex because pthread_cancel(), which takes an + * internal libc lock, cannot nest within ust_mutex. + * + * It never nests within a ust_mutex. + */ +static pthread_mutex_t ust_exit_mutex = PTHREAD_MUTEX_INITIALIZER; + +/* + * ust_fork_mutex protects base address statedump tracing against forks. It + * prevents the dynamic loader lock to be taken (by base address statedump + * tracing) while a fork is happening, thus preventing deadlock issues with + * the dynamic loader lock. + */ +static pthread_mutex_t ust_fork_mutex = PTHREAD_MUTEX_INITIALIZER; /* Should the ust comm thread quit ? */ static int lttng_ust_comm_should_quit; +/* + * Return 0 on success, -1 if should quit. + * The lock is taken in both cases. + * Signal-safe. + */ +int ust_lock(void) +{ + sigset_t sig_all_blocked, orig_mask; + int ret; + + sigfillset(&sig_all_blocked); + ret = pthread_sigmask(SIG_SETMASK, &sig_all_blocked, &orig_mask); + if (ret) { + ERR("pthread_sigmask: %s", strerror(ret)); + } + if (!URCU_TLS(ust_mutex_nest)++) + pthread_mutex_lock(&ust_mutex); + ret = pthread_sigmask(SIG_SETMASK, &orig_mask, NULL); + if (ret) { + ERR("pthread_sigmask: %s", strerror(ret)); + } + if (lttng_ust_comm_should_quit) { + return -1; + } else { + return 0; + } +} + +/* + * ust_lock_nocheck() can be used in constructors/destructors, because + * they are already nested within the dynamic loader lock, and therefore + * have exclusive access against execution of liblttng-ust destructor. + * Signal-safe. + */ +void ust_lock_nocheck(void) +{ + sigset_t sig_all_blocked, orig_mask; + int ret; + + sigfillset(&sig_all_blocked); + ret = pthread_sigmask(SIG_SETMASK, &sig_all_blocked, &orig_mask); + if (ret) { + ERR("pthread_sigmask: %s", strerror(ret)); + } + if (!URCU_TLS(ust_mutex_nest)++) + pthread_mutex_lock(&ust_mutex); + ret = pthread_sigmask(SIG_SETMASK, &orig_mask, NULL); + if (ret) { + ERR("pthread_sigmask: %s", strerror(ret)); + } +} + +/* + * Signal-safe. + */ +void ust_unlock(void) +{ + sigset_t sig_all_blocked, orig_mask; + int ret; + + sigfillset(&sig_all_blocked); + ret = pthread_sigmask(SIG_SETMASK, &sig_all_blocked, &orig_mask); + if (ret) { + ERR("pthread_sigmask: %s", strerror(ret)); + } + if (!--URCU_TLS(ust_mutex_nest)) + pthread_mutex_unlock(&ust_mutex); + ret = pthread_sigmask(SIG_SETMASK, &orig_mask, NULL); + if (ret) { + ERR("pthread_sigmask: %s", strerror(ret)); + } +} + /* * Wait for either of these before continuing to the main * program: @@ -108,7 +215,8 @@ struct sock_info { char wait_shm_path[PATH_MAX]; char *wait_shm_mmap; - struct lttng_session *session_enabled; + /* Keep track of lazy state dump not performed yet. */ + int statedump_pending; }; /* Socket from app (connect) to session daemon (listen) for communication */ @@ -126,7 +234,7 @@ struct sock_info global_apps = { .wait_shm_path = "/" LTTNG_UST_WAIT_FILENAME, - .session_enabled = NULL, + .statedump_pending = 0, }; /* TODO: allow global_apps_sock_path override */ @@ -141,7 +249,7 @@ struct sock_info local_apps = { .socket = -1, .notify_socket = -1, - .session_enabled = NULL, + .statedump_pending = 0, }; static int wait_poll_fallback; @@ -221,6 +329,22 @@ void lttng_fixup_nest_count_tls(void) asm volatile ("" : : "m" (URCU_TLS(lttng_ust_nest_count))); } +static +void lttng_fixup_ust_mutex_nest_tls(void) +{ + asm volatile ("" : : "m" (URCU_TLS(ust_mutex_nest))); +} + +/* + * Fixup urcu bp TLS. + */ +static +void lttng_fixup_urcu_bp_tls(void) +{ + rcu_read_lock(); + rcu_read_unlock(); +} + int lttng_get_notify_socket(void *owner) { struct sock_info *info = owner; @@ -387,6 +511,30 @@ int handle_register_done(struct sock_info *sock_info) return 0; } +/* + * Only execute pending statedump after the constructor semaphore has + * been posted by each listener thread. This means statedump will only + * be performed after the "registration done" command is received from + * each session daemon the application is connected to. + * + * This ensures we don't run into deadlock issues with the dynamic + * loader mutex, which is held while the constructor is called and + * waiting on the constructor semaphore. All operations requiring this + * dynamic loader lock need to be postponed using this mechanism. + */ +static +void handle_pending_statedump(struct sock_info *sock_info) +{ + int ctor_passed = sock_info->constructor_sem_posted; + + if (ctor_passed && sock_info->statedump_pending) { + sock_info->statedump_pending = 0; + pthread_mutex_lock(&ust_fork_mutex); + lttng_handle_pending_statedump(sock_info); + pthread_mutex_unlock(&ust_fork_mutex); + } +} + static int handle_message(struct sock_info *sock_info, int sock, struct ustcomm_ust_msg *lum) @@ -397,11 +545,9 @@ int handle_message(struct sock_info *sock_info, union ust_args args; ssize_t len; - ust_lock(); - memset(&lur, 0, sizeof(lur)); - if (lttng_ust_comm_should_quit) { + if (ust_lock()) { ret = -LTTNG_UST_ERR_EXITING; goto end; } @@ -705,6 +851,14 @@ end: error: ust_unlock(); + + /* + * Performed delayed statedump operations outside of the UST + * lock. We need to take the dynamic loader lock before we take + * the UST lock internally within handle_pending_statedump(). + */ + handle_pending_statedump(sock_info); + return ret; } @@ -747,9 +901,14 @@ void cleanup_sock_info(struct sock_info *sock_info, int exiting) sock_info->notify_socket = -1; } if (sock_info->wait_shm_mmap) { - ret = munmap(sock_info->wait_shm_mmap, sysconf(_SC_PAGE_SIZE)); - if (ret) { - ERR("Error unmapping wait shm"); + long page_size; + + page_size = sysconf(_SC_PAGE_SIZE); + if (page_size > 0) { + ret = munmap(sock_info->wait_shm_mmap, page_size); + if (ret) { + ERR("Error unmapping wait shm"); + } } sock_info->wait_shm_mmap = NULL; } @@ -922,15 +1081,20 @@ error_close: static char *get_map_shm(struct sock_info *sock_info) { - size_t mmap_size = sysconf(_SC_PAGE_SIZE); + long page_size; int wait_shm_fd, ret; char *wait_shm_mmap; - wait_shm_fd = get_wait_shm(sock_info, mmap_size); + page_size = sysconf(_SC_PAGE_SIZE); + if (page_size < 0) { + goto error; + } + + wait_shm_fd = get_wait_shm(sock_info, page_size); if (wait_shm_fd < 0) { goto error; } - wait_shm_mmap = mmap(NULL, mmap_size, PROT_READ, + wait_shm_mmap = mmap(NULL, page_size, PROT_READ, MAP_SHARED, wait_shm_fd, 0); /* close shm fd immediately after taking the mmap reference */ ret = close(wait_shm_fd); @@ -952,8 +1116,7 @@ void wait_for_sessiond(struct sock_info *sock_info) { int ret; - ust_lock(); - if (lttng_ust_comm_should_quit) { + if (ust_lock()) { goto quit; } if (wait_poll_fallback) { @@ -1058,9 +1221,7 @@ restart: DBG("Info: sessiond not accepting connections to %s apps socket", sock_info->name); prev_connect_failed = 1; - ust_lock(); - - if (lttng_ust_comm_should_quit) { + if (ust_lock()) { goto quit; } @@ -1075,9 +1236,7 @@ restart: } sock_info->socket = ret; - ust_lock(); - - if (lttng_ust_comm_should_quit) { + if (ust_lock()) { goto quit; } @@ -1118,9 +1277,7 @@ restart: DBG("Info: sessiond not accepting connections to %s apps socket", sock_info->name); prev_connect_failed = 1; - ust_lock(); - - if (lttng_ust_comm_should_quit) { + if (ust_lock()) { goto quit; } @@ -1157,9 +1314,7 @@ restart: WARN("Unsupported timeout value %ld", timeout); } - ust_lock(); - - if (lttng_ust_comm_should_quit) { + if (ust_lock()) { goto quit; } @@ -1190,8 +1345,7 @@ restart: switch (len) { case 0: /* orderly shutdown */ DBG("%s lttng-sessiond has performed an orderly shutdown", sock_info->name); - ust_lock(); - if (lttng_ust_comm_should_quit) { + if (ust_lock()) { goto quit; } /* @@ -1213,14 +1367,6 @@ restart: ret = handle_message(sock_info, sock, &lum); if (ret) { ERR("Error handling message for %s socket", sock_info->name); - } else { - struct lttng_session *session; - - session = sock_info->session_enabled; - if (session) { - sock_info->session_enabled = NULL; - lttng_ust_baddr_statedump(session); - } } continue; default: @@ -1238,8 +1384,7 @@ restart: } end: - ust_lock(); - if (lttng_ust_comm_should_quit) { + if (ust_lock()) { goto quit; } /* Cleanup socket handles before trying to reconnect */ @@ -1248,11 +1393,22 @@ end: goto restart; /* try to reconnect */ quit: - sock_info->thread_active = 0; ust_unlock(); + + pthread_mutex_lock(&ust_exit_mutex); + sock_info->thread_active = 0; + pthread_mutex_unlock(&ust_exit_mutex); return NULL; } +/* + * Weak symbol to call when the ust malloc wrapper is not loaded. + */ +__attribute__((weak)) +void lttng_ust_malloc_wrapper_init(void) +{ +} + /* * sessiond monitoring thread: monitor presence of global and per-user * sessiond by polling the application common named pipe. @@ -1273,10 +1429,12 @@ void __attribute__((constructor)) lttng_ust_init(void) * to be the dynamic linker mutex) and ust_lock, taken within * the ust lock. */ + lttng_fixup_urcu_bp_tls(); lttng_fixup_ringbuffer_tls(); lttng_fixup_vtid_tls(); lttng_fixup_nest_count_tls(); lttng_fixup_procname_tls(); + lttng_fixup_ust_mutex_nest_tls(); /* * We want precise control over the order in which we construct @@ -1286,12 +1444,18 @@ void __attribute__((constructor)) lttng_ust_init(void) */ init_usterr(); init_tracepoint(); + lttng_ust_baddr_statedump_init(); lttng_ring_buffer_metadata_client_init(); lttng_ring_buffer_client_overwrite_init(); lttng_ring_buffer_client_overwrite_rt_init(); lttng_ring_buffer_client_discard_init(); lttng_ring_buffer_client_discard_rt_init(); + lttng_perf_counter_init(); lttng_context_init(); + /* + * Invoke ust malloc wrapper init before starting other threads. + */ + lttng_ust_malloc_wrapper_init(); timeout_mode = get_constructor_timeout(&constructor_timeout); @@ -1323,24 +1487,24 @@ void __attribute__((constructor)) lttng_ust_init(void) ERR("pthread_attr_setdetachstate: %s", strerror(ret)); } - ust_lock(); + pthread_mutex_lock(&ust_exit_mutex); ret = pthread_create(&global_apps.ust_listener, &thread_attr, ust_listener_thread, &global_apps); if (ret) { ERR("pthread_create global: %s", strerror(ret)); } global_apps.thread_active = 1; - ust_unlock(); + pthread_mutex_unlock(&ust_exit_mutex); if (local_apps.allowed) { - ust_lock(); + pthread_mutex_lock(&ust_exit_mutex); ret = pthread_create(&local_apps.ust_listener, &thread_attr, ust_listener_thread, &local_apps); if (ret) { ERR("pthread_create local: %s", strerror(ret)); } local_apps.thread_active = 1; - ust_unlock(); + pthread_mutex_unlock(&ust_exit_mutex); } else { handle_register_done(&local_apps); } @@ -1395,11 +1559,13 @@ void lttng_ust_cleanup(int exiting) lttng_ust_abi_exit(); lttng_ust_events_exit(); lttng_context_exit(); + lttng_perf_counter_exit(); lttng_ring_buffer_client_discard_rt_exit(); lttng_ring_buffer_client_discard_exit(); lttng_ring_buffer_client_overwrite_rt_exit(); lttng_ring_buffer_client_overwrite_exit(); lttng_ring_buffer_metadata_client_exit(); + lttng_ust_baddr_statedump_destroy(); exit_tracepoint(); if (!exiting) { /* Reinitialize values for fork */ @@ -1424,9 +1590,11 @@ void __attribute__((destructor)) lttng_ust_exit(void) * mutexes to ensure it is not in a mutex critical section when * pthread_cancel is later called. */ - ust_lock(); + ust_lock_nocheck(); lttng_ust_comm_should_quit = 1; + ust_unlock(); + pthread_mutex_lock(&ust_exit_mutex); /* cancel threads */ if (global_apps.thread_active) { ret = pthread_cancel(global_apps.ust_listener); @@ -1446,7 +1614,7 @@ void __attribute__((destructor)) lttng_ust_exit(void) local_apps.thread_active = 0; } } - ust_unlock(); + pthread_mutex_unlock(&ust_exit_mutex); /* * Do NOT join threads: use of sys_futex makes it impossible to @@ -1485,7 +1653,10 @@ void ust_before_fork(sigset_t *save_sigset) if (ret == -1) { PERROR("sigprocmask"); } - ust_lock(); + + pthread_mutex_lock(&ust_fork_mutex); + + ust_lock_nocheck(); rcu_bp_before_fork(); } @@ -1495,6 +1666,9 @@ static void ust_after_fork_common(sigset_t *restore_sigset) DBG("process %d", getpid()); ust_unlock(); + + pthread_mutex_unlock(&ust_fork_mutex); + /* Restore signals */ ret = sigprocmask(SIG_SETMASK, restore_sigset, NULL); if (ret == -1) { @@ -1535,9 +1709,8 @@ void ust_after_fork_child(sigset_t *restore_sigset) lttng_ust_init(); } -void lttng_ust_sockinfo_session_enabled(void *owner, - struct lttng_session *session_enabled) +void lttng_ust_sockinfo_session_enabled(void *owner) { struct sock_info *sock_info = owner; - sock_info->session_enabled = session_enabled; + sock_info->statedump_pending = 1; }