From 0fdd1e2c4a9f725daed3f91dd4f5609ae54ebe93 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Thu, 1 Sep 2011 13:58:40 -0400 Subject: [PATCH] Add the notify UST application scheme This commit introduce the use of a wait shm futex that is used to notify applications that the session daemon is running and ready to accept registrations. See shm.c/.h for those calls and the new futex_wait_update() in futex.c. This scheme is also found in lttng-ust 2.0 and mainly taken from there. Also, enable-channel of the lttng client was modified to accept UST PID domain. It's the first phase for testing UST features of the liblttngctl. Signed-off-by: David Goulet --- ltt-sessiond/Makefile.am | 3 +- ltt-sessiond/futex.c | 20 ++++ ltt-sessiond/futex.h | 1 + ltt-sessiond/ltt-sessiond.h | 3 + ltt-sessiond/main.c | 153 +++++++++++++----------- ltt-sessiond/shm.c | 195 +++++++++++++++++++++++++++++++ ltt-sessiond/shm.h | 24 ++++ lttng/commands/enable_channels.c | 52 ++++----- 8 files changed, 353 insertions(+), 98 deletions(-) create mode 100644 ltt-sessiond/shm.c create mode 100644 ltt-sessiond/shm.h diff --git a/ltt-sessiond/Makefile.am b/ltt-sessiond/Makefile.am index 0a1f15712..d19fe3caa 100644 --- a/ltt-sessiond/Makefile.am +++ b/ltt-sessiond/Makefile.am @@ -14,11 +14,12 @@ ltt_sessiond_SOURCES = utils.c utils.h \ kernel-ctl.c kernel-ctl.h \ context.c context.h \ futex.c futex.h \ + shm.c shm.h \ session.c session.h \ ltt-sessiond.h main.c # link on liblttngctl for check if sessiond is already alive. -ltt_sessiond_LDADD = \ +ltt_sessiond_LDADD = -lrt \ $(top_builddir)/liblttng-sessiond-comm/liblttng-sessiond-comm.la \ $(top_builddir)/libkernelctl/libkernelctl.la \ $(top_builddir)/libustctl/libustctl.la \ diff --git a/ltt-sessiond/futex.c b/ltt-sessiond/futex.c index 0c333fce6..de3f94e6e 100644 --- a/ltt-sessiond/futex.c +++ b/ltt-sessiond/futex.c @@ -18,6 +18,7 @@ */ #define _GNU_SOURCE +#include #include #include #include @@ -43,6 +44,25 @@ * Ref: git://git.lttng.org/userspace-rcu.git */ +/* + * Update futex according to active or not. This scheme is used to wake every + * libust waiting on the shared memory map futex hence the INT_MAX used in the + * futex() call. If active, we set the value and wake everyone else we indicate + * that we are gone (cleanup() case). + */ +void futex_wait_update(int32_t *futex, int active) +{ + if (active) { + uatomic_set(futex, 1); + futex_async(futex, FUTEX_WAKE, + INT_MAX, NULL, NULL, 0); + } else { + uatomic_set(futex, 0); + } + + DBG("Futex wait update active %d", active); +} + /* * Prepare futex. */ diff --git a/ltt-sessiond/futex.h b/ltt-sessiond/futex.h index ebda1e06c..a056ec22b 100644 --- a/ltt-sessiond/futex.h +++ b/ltt-sessiond/futex.h @@ -19,6 +19,7 @@ #ifndef _LTT_FUTEX_H #define _LTT_FUTEX_H +void futex_wait_update(int32_t *futex, int active); void futex_nto1_prepare(int32_t *futex); void futex_nto1_wait(int32_t *futex); void futex_nto1_wake(int32_t *futex); diff --git a/ltt-sessiond/ltt-sessiond.h b/ltt-sessiond/ltt-sessiond.h index cc6a6ba15..63ef6d57d 100644 --- a/ltt-sessiond/ltt-sessiond.h +++ b/ltt-sessiond/ltt-sessiond.h @@ -29,6 +29,9 @@ #define DEFAULT_GLOBAL_APPS_PIPE DEFAULT_UST_SOCK_DIR "/global" #define DEFAULT_TRACE_OUTPUT DEFAULT_HOME_DIR "/lttng" +#define DEFAULT_GLOBAL_APPS_WAIT_SHM_PATH "/lttng-ust-apps-wait" +#define DEFAULT_HOME_APPS_WAIT_SHM_PATH "/lttng-ust-apps-wait-%u" + struct module_param { const char *name; int required; diff --git a/ltt-sessiond/main.c b/ltt-sessiond/main.c index a3335292b..85ac06be9 100644 --- a/ltt-sessiond/main.c +++ b/ltt-sessiond/main.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2011 - David Goulet - * Copyright (C) 2011 - Mathieu Desnoyers + * Mathieu Desnoyers * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -29,11 +29,14 @@ #include #include #include +#include #include #include #include #include #include +#include +#include #include #include @@ -45,6 +48,7 @@ #include "futex.h" #include "kernel-ctl.h" #include "ltt-sessiond.h" +#include "shm.h" #include "traceable-app.h" #include "ust-ctl.h" #include "utils.h" @@ -75,6 +79,7 @@ static char apps_unix_sock_path[PATH_MAX]; /* Global application Unix socket static char client_unix_sock_path[PATH_MAX]; /* Global client Unix socket path */ static char kconsumerd_err_unix_sock_path[PATH_MAX]; /* kconsumerd error Unix socket path */ static char kconsumerd_cmd_unix_sock_path[PATH_MAX]; /* kconsumerd command Unix socket path */ +static char wait_shm_path[PATH_MAX]; /* global wait shm path for UST */ /* Sockets and FDs */ static int client_sock; @@ -107,8 +112,6 @@ static sem_t kconsumerd_sem; static pthread_mutex_t kconsumerd_pid_mutex; /* Mutex to control kconsumerd pid assignation */ -static int modprobe_remove_kernel_modules(void); - /* * UST registration command queue. This queue is tied with a futex and uses a N * wakers / 1 waiter implemented and detailed in futex.c/.h @@ -130,6 +133,44 @@ static struct ust_cmd_queue ust_cmd_queue; */ static struct ltt_session_list *session_list_ptr; +/* + * Remove modules in reverse load order. + */ +static int modprobe_remove_kernel_modules(void) +{ + int ret = 0, i; + char modprobe[256]; + + for (i = ARRAY_SIZE(kernel_modules_list) - 1; i >= 0; i--) { + ret = snprintf(modprobe, sizeof(modprobe), + "/sbin/modprobe --remove --quiet %s", + kernel_modules_list[i].name); + if (ret < 0) { + perror("snprintf modprobe --remove"); + goto error; + } + modprobe[sizeof(modprobe) - 1] = '\0'; + ret = system(modprobe); + if (ret == -1) { + ERR("Unable to launch modprobe --remove for module %s", + kernel_modules_list[i].name); + } else if (kernel_modules_list[i].required + && WEXITSTATUS(ret) != 0) { + ERR("Unable to remove module %s", + kernel_modules_list[i].name); + } else { + DBG("Modprobe removal successful %s", + kernel_modules_list[i].name); + } + } + +error: + return ret; +} + +/* + * Return group ID of the tracing group or -1 if not found. + */ static gid_t allowed_group(void) { struct group *grp; @@ -405,37 +446,30 @@ error: return ret; } -#ifdef DISABLED /* - * Notify apps by writing 42 to a named pipe using name. Every applications - * waiting for a ltt-sessiond will be notified and re-register automatically to - * the session daemon. - * - * Return open or write error value. + * Notify UST applications using the shm mmap futex. */ -static int notify_apps(const char *name) +static int notify_ust_apps(int active) { - int fd; - int ret = -1; + char *wait_shm_mmap; - DBG("Notify the global application pipe"); + DBG("Notifying applications of session daemon state: %d", active); - /* Try opening the global pipe */ - fd = open(name, O_WRONLY); - if (fd < 0) { + /* See shm.c for this call implying mmap, shm and futex calls */ + wait_shm_mmap = shm_ust_get_mmap(wait_shm_path, is_root); + if (wait_shm_mmap == NULL) { goto error; } - /* Notify by writing on the pipe */ - ret = write(fd, "42", 2); - if (ret < 0) { - perror("write"); - } + /* Wake waiting process */ + futex_wait_update((int32_t *) wait_shm_mmap, active); + + /* Apps notified successfully */ + return 0; error: - return ret; + return -1; } -#endif /* DISABLED */ /* * Setup the outgoing data buffer for the response (llm) by allocating the @@ -1067,7 +1101,12 @@ static void *thread_registration_apps(void *data) pollfd[1].events = POLLIN; /* Notify all applications to register */ - //notify_apps(default_global_apps_pipe); + ret = notify_ust_apps(1); + if (ret < 0) { + ERR("Failed to notify applications or create the wait shared memory.\n" + "Execution continues but there might be problem for already running\n" + "applications that wishes to register."); + } while (1) { DBG("Accepting application registration"); @@ -1138,15 +1177,16 @@ static void *thread_registration_apps(void *data) } error: - DBG("Register apps thread dying"); - if (apps_sock) { - close(apps_sock); - } - if (sock) { - close(sock); - } + DBG("UST Registration thread dying"); + + /* Notify that the registration thread is gone */ + notify_ust_apps(0); + + close(apps_sock); + close(sock); unlink(apps_unix_sock_path); + return NULL; } @@ -1320,42 +1360,6 @@ error: return ret; } -/* - * modprobe_remove_kernel_modules - * Remove modules in reverse load order. - */ -static int modprobe_remove_kernel_modules(void) -{ - int ret = 0, i; - char modprobe[256]; - - for (i = ARRAY_SIZE(kernel_modules_list) - 1; i >= 0; i--) { - ret = snprintf(modprobe, sizeof(modprobe), - "/sbin/modprobe --remove --quiet %s", - kernel_modules_list[i].name); - if (ret < 0) { - perror("snprintf modprobe --remove"); - goto error; - } - modprobe[sizeof(modprobe) - 1] = '\0'; - ret = system(modprobe); - if (ret == -1) { - ERR("Unable to launch modprobe --remove for module %s", - kernel_modules_list[i].name); - } else if (kernel_modules_list[i].required - && WEXITSTATUS(ret) != 0) { - ERR("Unable to remove module %s", - kernel_modules_list[i].name); - } else { - DBG("Modprobe removal successful %s", - kernel_modules_list[i].name); - } - } - -error: - return ret; -} - /* * mount_debugfs */ @@ -1788,6 +1792,8 @@ static int process_client_msg(struct command_ctx *cmd_ctx) } } break; + case LTTNG_DOMAIN_UST_PID: + break; default: break; } @@ -1999,6 +2005,7 @@ static int process_client_msg(struct command_ctx *cmd_ctx) kernel_wait_quiescent(kernel_tracer_fd); break; case LTTNG_DOMAIN_UST_PID: + break; default: ret = LTTCOMM_NOT_IMPLEMENTED; @@ -3134,6 +3141,12 @@ int main(int argc, char **argv) snprintf(client_unix_sock_path, PATH_MAX, DEFAULT_GLOBAL_CLIENT_UNIX_SOCK); } + + /* Set global SHM for ust */ + if (strlen(wait_shm_path) == 0) { + snprintf(wait_shm_path, PATH_MAX, + DEFAULT_GLOBAL_APPS_WAIT_SHM_PATH); + } } else { home_path = get_home_dir(); if (home_path == NULL) { @@ -3153,6 +3166,12 @@ int main(int argc, char **argv) snprintf(client_unix_sock_path, PATH_MAX, DEFAULT_HOME_CLIENT_UNIX_SOCK, home_path); } + + /* Set global SHM for ust */ + if (strlen(wait_shm_path) == 0) { + snprintf(wait_shm_path, PATH_MAX, + DEFAULT_HOME_APPS_WAIT_SHM_PATH, geteuid()); + } } DBG("Client socket path %s", client_unix_sock_path); diff --git a/ltt-sessiond/shm.c b/ltt-sessiond/shm.c new file mode 100644 index 000000000..9bc96ad2d --- /dev/null +++ b/ltt-sessiond/shm.c @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2011 - David Goulet + * Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; only version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "shm.h" + +/* + * Using fork to set umask in the child process (not multi-thread safe). We + * deal with the shm_open vs ftruncate race (happening when the sessiond owns + * the shm and does not let everybody modify it, to ensure safety against + * shm_unlink) by simply letting the mmap fail and retrying after a few + * seconds. For global shm, everybody has rw access to it until the sessiond + * starts. + */ +static int get_wait_shm(char *shm_path, size_t mmap_size, int global) +{ + int wait_shm_fd, ret; + pid_t pid; + mode_t mode; + + /* Default permissions */ + mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; + + /* Change owner of the shm path */ + if (global) { + ret = chown(shm_path, 0, 0); + if (ret < 0) { + if (errno != ENOENT) { + perror("chown wait shm"); + goto error; + } + } + + /* + * If global session daemon, any application can register so the shm + * needs to be set in read-only mode for others. + */ + mode |= S_IROTH; + } else { + ret = chown(shm_path, getuid(), getgid()); + if (ret < 0) { + if (errno != ENOENT) { + perror("chown wait shm"); + goto error; + } + } + } + + /* + * Set permissions to the shm even if we did not create the shm. + */ + ret = chmod(shm_path, mode); + if (ret < 0) { + if (errno != ENOENT) { + perror("chmod wait shm"); + goto error; + } + } + + /* + * If the open failed because the file did not exist, try creating it + * ourself. + */ + pid = fork(); + if (pid > 0) { + int status; + /* + * Parent: wait for child to return, in which case the shared memory + * map will have been created. + */ + pid = wait(&status); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + goto error; + } + + /* + * Try to open read-only again after creation. + */ + wait_shm_fd = shm_open(shm_path, O_RDWR, 0); + if (wait_shm_fd < 0) { + /* + * Real-only open did not work. It's a failure that prohibits using + * shm. + */ + ERR("Error opening shm %s", shm_path); + goto error; + } + goto end; + } else if (pid == 0) { + /* + * We're alone in a child process, so we can modify the process-wide + * umask. + */ + umask(~mode); + + /* + * Try creating shm (or get rw access). We don't do an exclusive open, + * because we allow other processes to create+ftruncate it + * concurrently. + */ + wait_shm_fd = shm_open(shm_path, O_RDWR | O_CREAT, mode); + if (wait_shm_fd >= 0) { + ret = ftruncate(wait_shm_fd, mmap_size); + if (ret < 0) { + perror("ftruncate wait shm"); + exit(EXIT_FAILURE); + } + + ret = fchmod(wait_shm_fd, mode); + if (ret < 0) { + perror("fchmod"); + exit(EXIT_FAILURE); + } + exit(EXIT_SUCCESS); + } + ERR("Error opening shm %s", shm_path); + exit(EXIT_FAILURE); + } else { + return -1; + } + +end: + DBG("Got the wait shm fd %d", wait_shm_fd); + + return wait_shm_fd; + +error: + DBG("Failing to get the wait shm fd"); + + return -1; +} + +/* + * Return the wait shm mmap for UST application notification. The global + * variable is used to indicate if the the session daemon is global + * (root:tracing) or running with an unprivileged user. + * + * This returned value is used by futex_wait_update() in futex.c to WAKE all + * waiters which are UST application waiting for a session daemon. + */ +char *shm_ust_get_mmap(char *shm_path, int global) +{ + size_t mmap_size = sysconf(_SC_PAGE_SIZE); + int wait_shm_fd, ret; + char *wait_shm_mmap; + + wait_shm_fd = get_wait_shm(shm_path, mmap_size, global); + if (wait_shm_fd < 0) { + goto error; + } + + wait_shm_mmap = mmap(NULL, mmap_size, PROT_WRITE | PROT_READ, + MAP_SHARED, wait_shm_fd, 0); + /* close shm fd immediately after taking the mmap reference */ + ret = close(wait_shm_fd); + if (ret) { + perror("Error closing fd"); + } + + if (wait_shm_mmap == MAP_FAILED) { + DBG("mmap error (can be caused by race with ust)."); + goto error; + } + + return wait_shm_mmap; + +error: + return NULL; +} diff --git a/ltt-sessiond/shm.h b/ltt-sessiond/shm.h new file mode 100644 index 000000000..2d301bbac --- /dev/null +++ b/ltt-sessiond/shm.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2011 - David Goulet + * Mathieu Desnoyers + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; only version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef _LTT_SHM_H +#define _LTT_SHM_H + +char *shm_ust_get_mmap(char *shm_path, int global); + +#endif /* _LTT_SHM_H */ diff --git a/lttng/commands/enable_channels.c b/lttng/commands/enable_channels.c index ba5b7b45a..d8ad2a77b 100644 --- a/lttng/commands/enable_channels.c +++ b/lttng/commands/enable_channels.c @@ -104,9 +104,7 @@ static void usage(FILE *ofp) } /* - * enable_channel - * - * Adding channel using the lttng API. + * Adding channel using the lttng API. */ static int enable_channel(char *session_name) { @@ -116,43 +114,39 @@ static int enable_channel(char *session_name) if (opt_kernel) { dom.type = LTTNG_DOMAIN_KERNEL; + } else if (opt_pid != 0) { + dom.type = LTTNG_DOMAIN_UST_PID; + dom.attr.pid = opt_pid; + DBG("PID %d set to lttng handle", opt_pid); + } else { + ret = CMD_NOT_IMPLEMENTED; + goto error; } handle = lttng_create_handle(session_name, &dom); if (handle == NULL) { ret = -1; goto error; + } else { + ERR("Please specify a tracer (--kernel or --userspace)"); + goto error; } - /* Strip event list */ + /* Strip channel list (format: chan1,chan2,...) */ channel_name = strtok(opt_channels, ","); while (channel_name != NULL) { - /* Kernel tracer action */ - if (opt_kernel) { - DBG("Enabling kernel channel %s", channel_name); + /* Copy channel name and normalize it */ + strncpy(chan.name, channel_name, NAME_MAX); + chan.name[NAME_MAX - 1] = '\0'; - /* Copy channel name and normalize it */ - strncpy(chan.name, channel_name, NAME_MAX); - chan.name[NAME_MAX - 1] = '\0'; + DBG("Enabling channel %s", channel_name); - ret = lttng_enable_channel(handle, &chan); - if (ret < 0) { - goto error; - } else { - MSG("Kernel channel enabled %s", channel_name); - } - } else if (opt_userspace) { /* User-space tracer action */ - /* - * TODO: Waiting on lttng UST 2.0 - */ - if (opt_pid_all) { - } else if (opt_pid != 0) { - } - ret = CMD_NOT_IMPLEMENTED; + ret = lttng_enable_channel(handle, &chan); + if (ret < 0) { goto error; } else { - ERR("Please specify a tracer (--kernel or --userspace)"); - goto error; + MSG("Channel enabled %s for session %s", + channel_name, session_name); } /* Next event */ @@ -166,9 +160,7 @@ error: } /* - * init_channel_config - * - * Default value for channel configuration. + * Default value for channel configuration. */ static void init_channel_config(void) { @@ -194,7 +186,7 @@ static void init_channel_config(void) } /* - * Add channel to trace session + * Add channel to trace session */ int cmd_enable_channels(int argc, const char **argv) { -- 2.34.1