LTTNG_HEALTH_APP_REG,
LTTNG_HEALTH_KERNEL,
LTTNG_HEALTH_CONSUMER,
+ LTTNG_HEALTH_HT_CLEANUP,
LTTNG_HEALTH_ALL,
};
health.c health.h \
cmd.c cmd.h \
buffer-registry.c buffer-registry.h \
- testpoint.h
+ testpoint.h ht-cleanup.c
if HAVE_LIBLTTNG_UST_CTL
lttng_sessiond_SOURCES += trace-ust.c ust-registry.c ust-app.c \
#include "fd-limit.h"
#include "ust-consumer.h"
#include "ust-ctl.h"
+#include "utils.h"
/*
* Set in main.c during initialization process of the daemon. This contains
}
rcu_read_unlock();
- lttng_ht_destroy(regp->channels);
+ ht_cleanup_push(regp->channels);
switch (domain) {
case LTTNG_DOMAIN_UST:
void buffer_reg_destroy_registries(void)
{
DBG3("Buffer registry destroy all registry");
- lttng_ht_destroy(buffer_registry_uid);
- lttng_ht_destroy(buffer_registry_pid);
+ ht_cleanup_push(buffer_registry_uid);
+ ht_cleanup_push(buffer_registry_pid);
}
#include "consumer.h"
#include "health.h"
#include "ust-app.h"
+#include "utils.h"
/*
* Receive a reply command status message from the consumer. Consumer socket
rcu_read_unlock();
/* Finally destroy HT */
- lttng_ht_destroy(obj->socks);
+ ht_cleanup_push(obj->socks);
}
free(obj);
HEALTH_TYPE_APP_REG = 2,
HEALTH_TYPE_KERNEL = 3,
HEALTH_TYPE_CONSUMER = 4,
+ HEALTH_TYPE_HT_CLEANUP = 5,
HEALTH_NUM_TYPE,
};
--- /dev/null
+/*
+ * Copyright (C) 2013 - 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, version 2 only,
+ * as published by the Free Software Foundation.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include <assert.h>
+
+#include <common/hashtable/hashtable.h>
+#include <common/common.h>
+#include <common/utils.h>
+
+#include "lttng-sessiond.h"
+#include "health.h"
+
+void *thread_ht_cleanup(void *data)
+{
+ int ret, i, pollfd, err = -1;
+ uint32_t revents, nb_fd;
+ struct lttng_poll_event events;
+
+ DBG("[ht-thread] startup.");
+
+ rcu_register_thread();
+ rcu_thread_online();
+
+ health_register(HEALTH_TYPE_HT_CLEANUP);
+
+ health_code_update();
+
+ ret = sessiond_set_thread_pollset(&events, 2);
+ if (ret < 0) {
+ goto error_poll_create;
+ }
+
+ /* Add pipe to the pollset. */
+ ret = lttng_poll_add(&events, ht_cleanup_pipe[0], LPOLLIN | LPOLLERR);
+ if (ret < 0) {
+ goto error;
+ }
+
+ health_code_update();
+
+ while (1) {
+ DBG3("[ht-thread] Polling on %d fds.",
+ LTTNG_POLL_GETNB(&events));
+
+ /* Inifinite blocking call, waiting for transmission */
+restart:
+ health_poll_entry();
+ ret = lttng_poll_wait(&events, -1);
+ health_poll_exit();
+ if (ret < 0) {
+ /*
+ * Restart interrupted system call.
+ */
+ if (errno == EINTR) {
+ goto restart;
+ }
+ goto error;
+ }
+
+ nb_fd = ret;
+
+ for (i = 0; i < nb_fd; i++) {
+ struct lttng_ht *ht;
+
+ health_code_update();
+
+ /* Fetch once the poll data */
+ revents = LTTNG_POLL_GETEV(&events, i);
+ pollfd = LTTNG_POLL_GETFD(&events, i);
+
+ /* Thread quit pipe has been closed. Killing thread. */
+ ret = sessiond_check_thread_quit_pipe(pollfd, revents);
+ if (ret) {
+ err = 0;
+ goto exit;
+ }
+ assert(pollfd == ht_cleanup_pipe[0]);
+
+ if (revents & (LPOLLERR | LPOLLHUP | LPOLLRDHUP)) {
+ ERR("ht cleanup pipe error");
+ goto error;
+ } else if (!(revents & LPOLLIN)) {
+ /* No POLLIN and not a catched error, stop the thread. */
+ ERR("ht cleanup failed. revent: %u", revents);
+ goto error;
+ }
+
+ do {
+ /* Get socket from dispatch thread. */
+ ret = read(ht_cleanup_pipe[0], &ht, sizeof(ht));
+ } while (ret < 0 && errno == EINTR);
+ if (ret < 0 || ret < sizeof(ht)) {
+ PERROR("ht cleanup notify pipe");
+ goto error;
+ }
+ health_code_update();
+ /*
+ * The whole point of this thread is to call
+ * lttng_ht_destroy from a context that is NOT:
+ * 1) a read-side RCU lock,
+ * 2) a call_rcu thread.
+ */
+ lttng_ht_destroy(ht);
+
+ health_code_update();
+ }
+ }
+
+exit:
+error:
+ lttng_poll_clean(&events);
+error_poll_create:
+ utils_close_pipe(ht_cleanup_pipe);
+ ht_cleanup_pipe[0] = ht_cleanup_pipe[1] = -1;
+ DBG("[ust-thread] cleanup complete.");
+ if (err) {
+ health_error();
+ ERR("Health error occurred in %s", __func__);
+ }
+ health_unregister();
+ rcu_thread_offline();
+ rcu_unregister_thread();
+ return NULL;
+}
*/
extern int apps_cmd_notify_pipe[2];
+/*
+ * Used to notify that a hash table needs to be destroyed by dedicated
+ * thread. Required by design because we don't want to move destroy
+ * paths outside of large RCU read-side lock paths, and destroy cannot
+ * be called by call_rcu thread, because it may hang (waiting for
+ * call_rcu completion).
+ */
+extern int ht_cleanup_pipe[2];
+
/*
* Populated when the daemon starts with the current page size of the system.
*/
int sessiond_set_thread_pollset(struct lttng_poll_event *events, size_t size);
int sessiond_check_thread_quit_pipe(int fd, uint32_t events);
+void *thread_ht_cleanup(void *data);
+
#endif /* _LTT_SESSIOND_H */
static pthread_t kernel_thread;
static pthread_t dispatch_thread;
static pthread_t health_thread;
+static pthread_t ht_cleanup_thread;
/*
* UST registration command queue. This queue is tied with a futex and uses a N
case LTTNG_HEALTH_CONSUMER:
reply.ret_code = check_consumer_health();
break;
+ case LTTNG_HEALTH_HT_CLEANUP:
+ reply.ret_code = health_check_state(HEALTH_TYPE_HT_CLEANUP);
+ break;
case LTTNG_HEALTH_ALL:
reply.ret_code =
health_check_state(HEALTH_TYPE_APP_MANAGE) &&
health_check_state(HEALTH_TYPE_APP_REG) &&
health_check_state(HEALTH_TYPE_CMD) &&
health_check_state(HEALTH_TYPE_KERNEL) &&
- check_consumer_health();
+ check_consumer_health() &&
+ health_check_state(HEALTH_TYPE_HT_CLEANUP);
break;
default:
reply.ret_code = LTTNG_ERR_UND;
}
}
+ /* Setup the thread ht_cleanup communication pipe. */
+ if (utils_create_pipe_cloexec(ht_cleanup_pipe) < 0) {
+ goto exit;
+ }
+
/* Setup the thread apps communication pipe. */
if ((ret = utils_create_pipe_cloexec(apps_cmd_pipe)) < 0) {
goto exit;
write_pidfile();
+ /* Create thread to manage the client socket */
+ ret = pthread_create(&ht_cleanup_thread, NULL,
+ thread_ht_cleanup, (void *) NULL);
+ if (ret != 0) {
+ PERROR("pthread_create ht_cleanup");
+ goto exit_ht_cleanup;
+ }
+
/* Create thread to manage the client socket */
ret = pthread_create(&health_thread, NULL,
thread_manage_health, (void *) NULL);
}
exit_health:
+ ret = pthread_join(ht_cleanup_thread, &status);
+ if (ret != 0) {
+ PERROR("pthread_join ht cleanup thread");
+ goto error; /* join error, exit without cleanup */
+ }
+exit_ht_cleanup:
exit:
/*
* cleanup() is called when no other thread is running.
#include "buffer-registry.h"
#include "trace-ust.h"
+#include "utils.h"
/*
* Match function for the events hash table lookup.
return lus;
error_consumer:
- lttng_ht_destroy(lus->domain_global.channels);
+ ht_cleanup_push(lus->domain_global.channels);
free(lus);
error:
return NULL;
}
rcu_read_unlock();
- lttng_ht_destroy(ht);
+ ht_cleanup_push(ht);
}
/*
}
rcu_read_unlock();
- lttng_ht_destroy(events);
+ ht_cleanup_push(events);
}
/*
}
rcu_read_unlock();
- lttng_ht_destroy(channels);
+ ht_cleanup_push(channels);
}
/*
#include "ust-app.h"
#include "ust-consumer.h"
#include "ust-ctl.h"
+#include "utils.h"
/* Next available channel key. */
static unsigned long next_channel_key;
/*
* We need to execute ht_destroy outside of RCU read-side critical
- * section, so we postpone its execution using call_rcu. It is simpler
- * than to change the semantic of the many callers of
- * delete_ust_app_channel().
+ * section and outside of call_rcu thread, so we postpone its execution
+ * using ht_cleanup_push. It is simpler than to change the semantic of
+ * the many callers of delete_ust_app_session().
*/
static
void delete_ust_app_channel_rcu(struct rcu_head *head)
struct ust_app_channel *ua_chan =
caa_container_of(head, struct ust_app_channel, rcu_head);
- lttng_ht_destroy(ua_chan->ctx);
- lttng_ht_destroy(ua_chan->events);
+ ht_cleanup_push(ua_chan->ctx);
+ ht_cleanup_push(ua_chan->events);
free(ua_chan);
}
/*
* We need to execute ht_destroy outside of RCU read-side critical
- * section, so we postpone its execution using call_rcu. It is simpler
- * than to change the semantic of the many callers of
- * delete_ust_app_session().
+ * section and outside of call_rcu thread, so we postpone its execution
+ * using ht_cleanup_push. It is simpler than to change the semantic of
+ * the many callers of delete_ust_app_session().
*/
static
void delete_ust_app_session_rcu(struct rcu_head *head)
struct ust_app_session *ua_sess =
caa_container_of(head, struct ust_app_session, rcu_head);
- lttng_ht_destroy(ua_sess->channels);
+ ht_cleanup_push(ua_sess->channels);
free(ua_sess);
}
rcu_read_unlock();
}
- lttng_ht_destroy(app->sessions);
- lttng_ht_destroy(app->ust_objd);
+ ht_cleanup_push(app->sessions);
+ ht_cleanup_push(app->ust_objd);
/*
* Wait until we have deleted the application from the sock hash table
rcu_read_unlock();
/* Destroy is done only when the ht is empty */
- lttng_ht_destroy(ust_app_ht);
- lttng_ht_destroy(ust_app_ht_by_sock);
- lttng_ht_destroy(ust_app_ht_by_notify_sock);
+ ht_cleanup_push(ust_app_ht);
+ ht_cleanup_push(ust_app_ht_by_sock);
+ ht_cleanup_push(ust_app_ht_by_notify_sock);
}
/*
#include <lttng/lttng.h>
#include "ust-registry.h"
+#include "utils.h"
/*
* Hash table match function for event in the registry.
/*
* We need to execute ht_destroy outside of RCU read-side critical
- * section, so we postpone its execution using call_rcu. It is simpler
- * than to change the semantic of the many callers of
- * destroy_channel().
+ * section and outside of call_rcu thread, so we postpone its execution
+ * using ht_cleanup_push. It is simpler than to change the semantic of
+ * the many callers of delete_ust_app_session().
*/
static
void destroy_channel_rcu(struct rcu_head *head)
caa_container_of(head, struct ust_registry_channel, rcu_head);
if (chan->ht) {
- lttng_ht_destroy(chan->ht);
+ ht_cleanup_push(chan->ht);
}
free(chan);
}
rcu_read_unlock();
if (reg->channels) {
- lttng_ht_destroy(reg->channels);
+ ht_cleanup_push(reg->channels);
}
free(reg->metadata);
}
#include <common/error.h>
#include "utils.h"
+#include "lttng-sessiond.h"
+
+int ht_cleanup_pipe[2] = { -1, -1 };
/*
* Write to writable pipe used to notify a thread.
{
return ((const char *) getenv("HOME"));
}
+
+void ht_cleanup_push(struct lttng_ht *ht)
+{
+ int ret;
+ int fd = ht_cleanup_pipe[1];
+
+ if (fd < 0)
+ return;
+ do {
+ ret = write(fd, &ht, sizeof(ht));
+ } while (ret < 0 && errno == EINTR);
+ if (ret < 0 || ret != sizeof(ht)) {
+ PERROR("write ht cleanup pipe %d", fd);
+ if (ret < 0) {
+ ret = -errno;
+ }
+ goto error;
+ }
+
+ /* All good. Don't send back the write positive ret value. */
+ ret = 0;
+error:
+ assert(!ret);
+}
#ifndef _LTT_UTILS_H
#define _LTT_UTILS_H
+struct lttng_ht;
+
const char *get_home_dir(void);
int notify_thread_pipe(int wpipe);
+void ht_cleanup_push(struct lttng_ht *ht);
#endif /* _LTT_UTILS_H */
# Session unit test
SESSIONS=$(top_srcdir)/src/bin/lttng-sessiond/session.o \
$(top_srcdir)/src/bin/lttng-sessiond/consumer.o \
+ $(top_srcdir)/src/bin/lttng-sessiond/utils.o \
$(top_srcdir)/src/bin/lttng-sessiond/health.o \
$(top_srcdir)/src/common/uri.o \
$(top_srcdir)/src/common/utils.o \
if HAVE_LIBLTTNG_UST_CTL
UST_DATA_TRACE=$(top_srcdir)/src/bin/lttng-sessiond/trace-ust.o \
$(top_srcdir)/src/bin/lttng-sessiond/consumer.o \
+ $(top_srcdir)/src/bin/lttng-sessiond/utils.o \
$(top_srcdir)/src/bin/lttng-sessiond/buffer-registry.o \
$(top_srcdir)/src/bin/lttng-sessiond/ust-registry.o \
$(top_srcdir)/src/bin/lttng-sessiond/ust-metadata.o \
KERN_DATA_TRACE=$(top_srcdir)/src/bin/lttng-sessiond/trace-kernel.o \
$(top_srcdir)/src/bin/lttng-sessiond/consumer.o \
$(top_srcdir)/src/bin/lttng-sessiond/health.o \
+ $(top_srcdir)/src/bin/lttng-sessiond/utils.o \
$(top_srcdir)/src/common/uri.o \
$(top_srcdir)/src/common/utils.o