*/
#define _LGPL_SOURCE
+#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/mman.h>
#include "compat.h"
#include "../libringbuffer/tlsfixup.h"
#include "lttng-ust-baddr.h"
+#include "clock.h"
+#include "../libringbuffer/getcpu.h"
/*
* Has lttng ust comm constructor been called ?
* 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
*/
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 quilt.
+ * Return 0 on success, -1 if should quit.
* The lock is taken in both cases.
+ * Signal-safe.
*/
int ust_lock(void)
{
- pthread_mutex_lock(&ust_mutex);
+ 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 {
* 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)
{
- pthread_mutex_lock(&ust_mutex);
+ 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)
{
- pthread_mutex_unlock(&ust_mutex);
+ 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));
+ }
}
/*
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;
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);
}
}
* 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
*/
init_usterr();
init_tracepoint();
+ lttng_ust_clock_init();
+ lttng_ust_getcpu_init();
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_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();
if (ret == -1) {
PERROR("sigprocmask");
}
+
+ pthread_mutex_lock(&ust_fork_mutex);
+
ust_lock_nocheck();
rcu_bp_before_fork();
}
DBG("process %d", getpid());
ust_unlock();
+
+ pthread_mutex_unlock(&ust_fork_mutex);
+
/* Restore signals */
ret = sigprocmask(SIG_SETMASK, restore_sigset, NULL);
if (ret == -1) {