Add the notify UST application scheme
authorDavid Goulet <david.goulet@polymtl.ca>
Thu, 1 Sep 2011 17:58:40 +0000 (13:58 -0400)
committerDavid Goulet <david.goulet@polymtl.ca>
Thu, 1 Sep 2011 18:02:56 +0000 (14:02 -0400)
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 <david.goulet@polymtl.ca>
ltt-sessiond/Makefile.am
ltt-sessiond/futex.c
ltt-sessiond/futex.h
ltt-sessiond/ltt-sessiond.h
ltt-sessiond/main.c
ltt-sessiond/shm.c [new file with mode: 0644]
ltt-sessiond/shm.h [new file with mode: 0644]
lttng/commands/enable_channels.c

index 0a1f1571224c48da4dcde5b2c4bd47d534d788a9..d19fe3caa7298d03e9eb511610f8f7e56d535c69 100644 (file)
@@ -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 \
index 0c333fce68e5fef0d2d881ef2b19435ccea6c48b..de3f94e6e6a0bb6dcbc25505721ad033c398a1c1 100644 (file)
@@ -18,6 +18,7 @@
  */
 
 #define _GNU_SOURCE
+#include <limits.h>
 #include <sys/syscall.h>
 #include <unistd.h>
 #include <urcu.h>
  * 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.
  */
index ebda1e06c2a8611f883c45015bf0535af58db1d6..a056ec22b6e28d813c8deeb1c8a87af924c08673 100644 (file)
@@ -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);
index cc6a6ba155ced9e26b6cec7b5f9f4e24a54bb968..63ef6d57dd3a3c038ee09758ba864e6fb28cb74b 100644 (file)
@@ -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;
index a3335292bc0760746a701f0426d79a6a011dd34a..85ac06be9e32c2967417513b7902d2b226b303ba 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
- * Copyright (C) 2011 - Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *                      Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/mman.h>
 #include <sys/mount.h>
 #include <sys/resource.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/wait.h>
+#include <urcu/futex.h>
 #include <unistd.h>
 
 #include <ltt-kconsumerd.h>
@@ -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 (file)
index 0000000..9bc96ad
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C)  2011 - David Goulet <david.goulet@polymtl.ca>
+ *                       Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * 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 <fcntl.h>
+#include <limits.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <urcu.h>
+
+#include <lttngerr.h>
+
+#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 (file)
index 0000000..2d301bb
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2011 - David Goulet <david.goulet@polymtl.ca>
+ *                      Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
+ *
+ * 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 */
index ba5b7b45a5219370a9fd86aa9e9dc4d966145702..d8ad2a77b67a548df98ac10c6077e5f46481642e 100644 (file)
@@ -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)
 {
This page took 0.052585 seconds and 4 git commands to generate.