X-Git-Url: http://git.liburcu.org/?a=blobdiff_plain;f=src%2Fbin%2Flttng-sessiond%2Fmain.c;h=f0765f07ca408147863bad83abc2dc1cecd3810c;hb=aacbfa47ad16d5778726b678c095b824d75d388b;hp=80ca3d7c04e42704a325bb8b2aedfee1db0d7837;hpb=5b49a21f8fcf7a179a86c76669c405b82b6439fb;p=lttng-tools.git diff --git a/src/bin/lttng-sessiond/main.c b/src/bin/lttng-sessiond/main.c index 80ca3d7c0..f0765f07c 100644 --- a/src/bin/lttng-sessiond/main.c +++ b/src/bin/lttng-sessiond/main.c @@ -309,12 +309,38 @@ struct load_session_thread_data *load_info; struct lttng_ht *agent_apps_ht_by_sock = NULL; /* - * Whether sessiond is ready for commands/health check requests. - * NR_LTTNG_SESSIOND_READY must match the number of calls to - * sessiond_notify_ready(). + * The initialization of the session daemon is done in multiple phases. + * + * While all threads are launched near-simultaneously, only some of them + * are needed to ensure the session daemon can start to respond to client + * requests. + * + * There are two important guarantees that we wish to offer with respect + * to the initialisation of the session daemon: + * - When the daemonize/background launcher process exits, the sessiond + * is fully able to respond to client requests, + * - Auto-loaded sessions are visible to clients. + * + * In order to achieve this, a number of support threads have to be launched + * to allow the "client" thread to function properly. Moreover, since the + * "load session" thread needs the client thread, we must provide a way + * for the "load session" thread to know that the "client" thread is up + * and running. + * + * Hence, the support threads decrement the lttng_sessiond_ready counter + * while the "client" threads waits for it to reach 0. Once the "client" thread + * unblocks, it posts the message_thread_ready semaphore which allows the + * "load session" thread to progress. + * + * This implies that the "load session" thread is the last to be initialized + * and will explicitly call sessiond_signal_parents(), which signals the parents + * that the session daemon is fully initialized. + * + * The two (2) support threads are: + * - agent_thread + * - health_thread */ -#define NR_LTTNG_SESSIOND_READY 3 -int lttng_sessiond_ready = NR_LTTNG_SESSIOND_READY; +int lttng_sessiond_ready = 2; int sessiond_check_thread_quit_pipe(int fd, uint32_t events) { @@ -323,28 +349,36 @@ int sessiond_check_thread_quit_pipe(int fd, uint32_t events) /* Notify parents that we are ready for cmd and health check */ LTTNG_HIDDEN -void sessiond_notify_ready(void) +void sessiond_signal_parents(void) { - if (uatomic_sub_return(<tng_sessiond_ready, 1) == 0) { - /* - * Notify parent pid that we are ready to accept command - * for client side. This ppid is the one from the - * external process that spawned us. - */ - if (opt_sig_parent) { - kill(ppid, SIGUSR1); - } + /* + * Notify parent pid that we are ready to accept command + * for client side. This ppid is the one from the + * external process that spawned us. + */ + if (opt_sig_parent) { + kill(ppid, SIGUSR1); + } - /* - * Notify the parent of the fork() process that we are - * ready. - */ - if (opt_daemon || opt_background) { - kill(child_ppid, SIGUSR1); - } + /* + * Notify the parent of the fork() process that we are + * ready. + */ + if (opt_daemon || opt_background) { + kill(child_ppid, SIGUSR1); } } +LTTNG_HIDDEN +void sessiond_notify_ready(void) +{ + /* + * The _return variant is used since the implied memory barriers are + * required. + */ + (void) uatomic_sub_return(<tng_sessiond_ready, 1); +} + static void setup_consumerd_path(void) { @@ -2623,7 +2657,7 @@ static pid_t spawn_consumerd(struct consumer_data *consumer_data) break; } default: - PERROR("unknown consumer type"); + ERR("unknown consumer type"); exit(EXIT_FAILURE); } if (errno != 0) { @@ -2716,7 +2750,6 @@ static int init_kernel_tracer(void) kernel_tracer_fd = open(module_proc_lttng, O_RDWR); if (kernel_tracer_fd < 0) { DBG("Failed to open %s", module_proc_lttng); - ret = -1; goto error_open; } @@ -4376,13 +4409,50 @@ static void *thread_manage_clients(void *data) goto error; } - sessiond_notify_ready(); ret = sem_post(&load_info->message_thread_ready); if (ret) { PERROR("sem_post message_thread_ready"); goto error; } + /* + * Wait until all support threads are initialized before accepting + * commands. + */ + while (uatomic_read(<tng_sessiond_ready) != 0) { + fd_set read_fds; + struct timeval timeout; + + FD_ZERO(&read_fds); + FD_SET(thread_quit_pipe[0], &read_fds); + memset(&timeout, 0, sizeof(timeout)); + timeout.tv_usec = 1000; + + /* + * If a support thread failed to launch, it may signal that + * we must exit and the sessiond would never be marked as + * "ready". + * + * The timeout is set to 1ms, which serves as a way to + * pace down this check. + */ + ret = select(thread_quit_pipe[0] + 1, &read_fds, NULL, NULL, + &timeout); + if (ret > 0 || (ret < 0 && errno != EINTR)) { + goto exit; + } + } + /* + * This barrier is paired with the one in sessiond_notify_ready() to + * ensure that loads accessing data initialized by the other threads, + * on which this thread was waiting, are not performed before this point. + * + * Note that this could be a 'read' memory barrier, but a full barrier + * is used in case the code changes. The performance implications of + * this choice are minimal since this is a slow path. + */ + cmm_smp_mb(); + /* This testpoint is after we signal readiness to the parent. */ if (testpoint(sessiond_thread_manage_clients)) { goto error; @@ -4908,10 +4978,10 @@ static int set_option(int opt, const char *arg, const char *optname) "-l, --load"); } else { free(opt_load_session_path); - opt_load_session_path = strdup(arg); + opt_load_session_path = utils_expand_path(arg); if (!opt_load_session_path) { - PERROR("strdup"); - ret = -ENOMEM; + ERR("Failed to expand load path %s", arg); + ret = -EINVAL; } } } else if (string_match(optname, "kmod-probes")) { @@ -5526,6 +5596,44 @@ error: return ret; } +static int set_clock_plugin_env(void) +{ + int ret = 0; + const char *original_env_value; + char *full_path = NULL; + char *new_env_value = NULL; + + original_env_value = getenv("LTTNG_UST_CLOCK_PLUGIN"); + if (!original_env_value) { + goto end; + } + + full_path = utils_expand_path(original_env_value); + if (!full_path) { + ERR("Failed to expand LTTNG_UST_CLOCK_PLUGIN path \"%s\"", + original_env_value); + ret = -1; + goto end; + } + ret = asprintf(&new_env_value, "LTTNG_UST_CLOCK_PLUGIN=%s", + full_path); + free(full_path); + if (ret < 0) { + PERROR("asprintf"); + goto end; + } + + DBG("Updating environment: %s", new_env_value); + ret = putenv(new_env_value); + if (ret) { + free(new_env_value); + PERROR("putenv of LTTNG_UST_CLOCK_PLUGIN"); + goto end; + } +end: + return ret; +} + /* * main */ @@ -5567,6 +5675,12 @@ int main(int argc, char **argv) goto exit_options; } + ret = set_clock_plugin_env(); + if (ret) { + retval = -1; + goto exit_options; + } + /* Daemonize */ if (opt_daemon || opt_background) { int i;