#include <sys/types.h>
#include <unistd.h>
#include <inttypes.h>
+#include <signal.h>
#include <common/common.h>
#include <common/utils.h>
.type = LTTNG_CONSUMER_UNKNOWN,
};
+enum consumer_channel_action {
+ CONSUMER_CHANNEL_ADD,
+ CONSUMER_CHANNEL_QUIT,
+};
+
+struct consumer_channel_msg {
+ enum consumer_channel_action action;
+ struct lttng_consumer_channel *chan;
+};
+
/*
* Flag to inform the polling thread to quit when all fd hung up. Updated by
* the consumer_thread_receive_fds when it notices that all fds has hung up.
} while (ret < 0 && errno == EINTR);
}
+static void notify_channel_pipe(struct lttng_consumer_local_data *ctx,
+ struct lttng_consumer_channel *chan,
+ enum consumer_channel_action action)
+{
+ struct consumer_channel_msg msg;
+ int ret;
+
+ msg.action = action;
+ msg.chan = chan;
+ do {
+ ret = write(ctx->consumer_channel_pipe[1], &msg, sizeof(msg));
+ } while (ret < 0 && errno == EINTR);
+}
+
+static int read_channel_pipe(struct lttng_consumer_local_data *ctx,
+ struct lttng_consumer_channel **chan,
+ enum consumer_channel_action *action)
+{
+ struct consumer_channel_msg msg;
+ int ret;
+
+ do {
+ ret = read(ctx->consumer_channel_pipe[0], &msg, sizeof(msg));
+ } while (ret < 0 && errno == EINTR);
+ if (ret > 0) {
+ *action = msg.action;
+ *chan = msg.chan;
+ }
+ return ret;
+}
+
/*
* Find a stream. The consumer_data.lock must be locked during this
* call.
*/
-static struct lttng_consumer_stream *find_stream(int key,
+static struct lttng_consumer_stream *find_stream(uint64_t key,
struct lttng_ht *ht)
{
struct lttng_ht_iter iter;
- struct lttng_ht_node_ulong *node;
+ struct lttng_ht_node_u64 *node;
struct lttng_consumer_stream *stream = NULL;
assert(ht);
- /* Negative keys are lookup failures */
- if (key < 0) {
+ /* -1ULL keys are lookup failures */
+ if (key == (uint64_t) -1ULL) {
return NULL;
}
rcu_read_lock();
- lttng_ht_lookup(ht, (void *)((unsigned long) key), &iter);
- node = lttng_ht_iter_get_node_ulong(&iter);
+ lttng_ht_lookup(ht, &key, &iter);
+ node = lttng_ht_iter_get_node_u64(&iter);
if (node != NULL) {
stream = caa_container_of(node, struct lttng_consumer_stream, node);
}
rcu_read_lock();
stream = find_stream(key, ht);
if (stream) {
- stream->key = -1;
+ stream->key = -1ULL;
/*
* We don't want the lookup to match, but we still need
* to iterate on this stream when iterating over the hash table. Just
* change the node key.
*/
- stream->node.key = -1;
+ stream->node.key = -1ULL;
}
rcu_read_unlock();
}
* RCU read side lock MUST be acquired before calling this function and
* protects the channel ptr.
*/
-struct lttng_consumer_channel *consumer_find_channel(unsigned long key)
+struct lttng_consumer_channel *consumer_find_channel(uint64_t key)
{
struct lttng_ht_iter iter;
- struct lttng_ht_node_ulong *node;
+ struct lttng_ht_node_u64 *node;
struct lttng_consumer_channel *channel = NULL;
- /* Negative keys are lookup failures */
- if (key < 0) {
+ /* -1ULL keys are lookup failures */
+ if (key == (uint64_t) -1ULL) {
return NULL;
}
- lttng_ht_lookup(consumer_data.channel_ht, (void *) key, &iter);
- node = lttng_ht_iter_get_node_ulong(&iter);
+ lttng_ht_lookup(consumer_data.channel_ht, &key, &iter);
+ node = lttng_ht_iter_get_node_u64(&iter);
if (node != NULL) {
channel = caa_container_of(node, struct lttng_consumer_channel, node);
}
static void free_stream_rcu(struct rcu_head *head)
{
- struct lttng_ht_node_ulong *node =
- caa_container_of(head, struct lttng_ht_node_ulong, head);
+ struct lttng_ht_node_u64 *node =
+ caa_container_of(head, struct lttng_ht_node_u64, head);
struct lttng_consumer_stream *stream =
caa_container_of(node, struct lttng_consumer_stream, node);
static void free_channel_rcu(struct rcu_head *head)
{
- struct lttng_ht_node_ulong *node =
- caa_container_of(head, struct lttng_ht_node_ulong, head);
+ struct lttng_ht_node_u64 *node =
+ caa_container_of(head, struct lttng_ht_node_u64, head);
struct lttng_consumer_channel *channel =
caa_container_of(node, struct lttng_consumer_channel, node);
*/
static void free_relayd_rcu(struct rcu_head *head)
{
- struct lttng_ht_node_ulong *node =
- caa_container_of(head, struct lttng_ht_node_ulong, head);
+ struct lttng_ht_node_u64 *node =
+ caa_container_of(head, struct lttng_ht_node_u64, head);
struct consumer_relayd_sock_pair *relayd =
caa_container_of(node, struct consumer_relayd_sock_pair, node);
int ret;
struct lttng_ht_iter iter;
- DBG("Consumer delete channel key %d", channel->key);
+ DBG("Consumer delete channel key %" PRIu64, channel->key);
pthread_mutex_lock(&consumer_data.lock);
ret = lttng_ht_del(ht, &iter);
assert(!ret);
- /* Remove node session id from the consumer_data stream ht */
+ iter.iter.node = &stream->node_channel_id.node;
+ ret = lttng_ht_del(consumer_data.stream_per_chan_id_ht, &iter);
+ assert(!ret);
+
iter.iter.node = &stream->node_session_id.node;
ret = lttng_ht_del(consumer_data.stream_list_ht, &iter);
assert(!ret);
call_rcu(&stream->node.head, free_stream_rcu);
}
-struct lttng_consumer_stream *consumer_allocate_stream(int channel_key,
- int stream_key,
+struct lttng_consumer_stream *consumer_allocate_stream(uint64_t channel_key,
+ uint64_t stream_key,
enum lttng_consumer_stream_state state,
const char *channel_name,
uid_t uid,
}
/* Key is always the wait_fd for streams. */
- lttng_ht_node_init_ulong(&stream->node, stream->key);
+ lttng_ht_node_init_u64(&stream->node, stream->key);
+
+ /* Init node per channel id key */
+ lttng_ht_node_init_u64(&stream->node_channel_id, channel_key);
/* Init session id node with the stream session id */
- lttng_ht_node_init_ulong(&stream->node_session_id, stream->session_id);
+ lttng_ht_node_init_u64(&stream->node_session_id, stream->session_id);
- DBG3("Allocated stream %s (key %d, relayd_id %d, session_id %" PRIu64,
- stream->name, stream->key, stream->net_seq_idx, stream->session_id);
+ DBG3("Allocated stream %s (key %" PRIu64 ", chan_key %" PRIu64 " relayd_id %" PRIu64 ", session_id %" PRIu64,
+ stream->name, stream->key, channel_key, stream->net_seq_idx, stream->session_id);
rcu_read_unlock();
return stream;
assert(stream);
assert(ht);
- DBG3("Adding consumer stream %d", stream->key);
+ DBG3("Adding consumer stream %" PRIu64, stream->key);
pthread_mutex_lock(&consumer_data.lock);
pthread_mutex_lock(&stream->lock);
/* Steal stream identifier to avoid having streams with the same key */
steal_stream_key(stream->key, ht);
- lttng_ht_add_unique_ulong(ht, &stream->node);
+ lttng_ht_add_unique_u64(ht, &stream->node);
+
+ lttng_ht_add_u64(consumer_data.stream_per_chan_id_ht,
+ &stream->node_channel_id);
/*
* Add stream to the stream_list_ht of the consumer data. No need to steal
* the key since the HT does not use it and we allow to add redundant keys
* into this table.
*/
- lttng_ht_add_ulong(consumer_data.stream_list_ht, &stream->node_session_id);
+ lttng_ht_add_u64(consumer_data.stream_list_ht, &stream->node_session_id);
/* Check and cleanup relayd */
relayd = consumer_find_relayd(stream->net_seq_idx);
static int add_relayd(struct consumer_relayd_sock_pair *relayd)
{
int ret = 0;
- struct lttng_ht_node_ulong *node;
+ struct lttng_ht_node_u64 *node;
struct lttng_ht_iter iter;
assert(relayd);
lttng_ht_lookup(consumer_data.relayd_ht,
- (void *)((unsigned long) relayd->net_seq_idx), &iter);
- node = lttng_ht_iter_get_node_ulong(&iter);
+ &relayd->net_seq_idx, &iter);
+ node = lttng_ht_iter_get_node_u64(&iter);
if (node != NULL) {
goto end;
}
- lttng_ht_add_unique_ulong(consumer_data.relayd_ht, &relayd->node);
+ lttng_ht_add_unique_u64(consumer_data.relayd_ht, &relayd->node);
end:
return ret;
obj->net_seq_idx = net_seq_idx;
obj->refcount = 0;
obj->destroy_flag = 0;
- lttng_ht_node_init_ulong(&obj->node, obj->net_seq_idx);
+ lttng_ht_node_init_u64(&obj->node, obj->net_seq_idx);
pthread_mutex_init(&obj->ctrl_sock_mutex, NULL);
error:
* RCU read-side lock must be held across this call and while using the
* returned object.
*/
-struct consumer_relayd_sock_pair *consumer_find_relayd(int key)
+struct consumer_relayd_sock_pair *consumer_find_relayd(uint64_t key)
{
struct lttng_ht_iter iter;
- struct lttng_ht_node_ulong *node;
+ struct lttng_ht_node_u64 *node;
struct consumer_relayd_sock_pair *relayd = NULL;
/* Negative keys are lookup failures */
- if (key < 0) {
+ if (key == (uint64_t) -1ULL) {
goto error;
}
- lttng_ht_lookup(consumer_data.relayd_ht, (void *)((unsigned long) key),
+ lttng_ht_lookup(consumer_data.relayd_ht, &key,
&iter);
- node = lttng_ht_iter_get_node_ulong(&iter);
+ node = lttng_ht_iter_get_node_u64(&iter);
if (node != NULL) {
relayd = caa_container_of(node, struct consumer_relayd_sock_pair, node);
}
}
/* Metadata are always sent on the control socket. */
- outfd = relayd->control_sock.fd;
+ outfd = relayd->control_sock.sock.fd;
} else {
/* Set header with stream information */
data_hdr.stream_id = htobe64(stream->relayd_stream_id);
++stream->next_net_seq_num;
/* Set to go on data socket */
- outfd = relayd->data_sock.fd;
+ outfd = relayd->data_sock.sock.fd;
}
error:
*
* On error, return NULL.
*/
-struct lttng_consumer_channel *consumer_allocate_channel(unsigned long key,
+struct lttng_consumer_channel *consumer_allocate_channel(uint64_t key,
uint64_t session_id,
const char *pathname,
const char *name,
uid_t uid,
gid_t gid,
int relayd_id,
- enum lttng_event_output output)
+ enum lttng_event_output output,
+ uint64_t tracefile_size,
+ uint64_t tracefile_count)
{
struct lttng_consumer_channel *channel;
channel->gid = gid;
channel->relayd_id = relayd_id;
channel->output = output;
+ channel->tracefile_size = tracefile_size;
+ channel->tracefile_count = tracefile_count;
strncpy(channel->pathname, pathname, sizeof(channel->pathname));
channel->pathname[sizeof(channel->pathname) - 1] = '\0';
strncpy(channel->name, name, sizeof(channel->name));
channel->name[sizeof(channel->name) - 1] = '\0';
- lttng_ht_node_init_ulong(&channel->node, channel->key);
+ lttng_ht_node_init_u64(&channel->node, channel->key);
+
+ channel->wait_fd = -1;
+
CDS_INIT_LIST_HEAD(&channel->streams.head);
- DBG("Allocated channel (key %d)", channel->key)
+ DBG("Allocated channel (key %" PRIu64 ")", channel->key)
end:
return channel;
/*
* Add a channel to the global list protected by a mutex.
*/
-int consumer_add_channel(struct lttng_consumer_channel *channel)
+int consumer_add_channel(struct lttng_consumer_channel *channel,
+ struct lttng_consumer_local_data *ctx)
{
int ret = 0;
- struct lttng_ht_node_ulong *node;
+ struct lttng_ht_node_u64 *node;
struct lttng_ht_iter iter;
pthread_mutex_lock(&consumer_data.lock);
rcu_read_lock();
- lttng_ht_lookup(consumer_data.channel_ht,
- (void *)((unsigned long) channel->key), &iter);
- node = lttng_ht_iter_get_node_ulong(&iter);
+ lttng_ht_lookup(consumer_data.channel_ht, &channel->key, &iter);
+ node = lttng_ht_iter_get_node_u64(&iter);
if (node != NULL) {
/* Channel already exist. Ignore the insertion */
- ERR("Consumer add channel key %d already exists!", channel->key);
+ ERR("Consumer add channel key %" PRIu64 " already exists!",
+ channel->key);
ret = -1;
goto end;
}
- lttng_ht_add_unique_ulong(consumer_data.channel_ht, &channel->node);
+ lttng_ht_add_unique_u64(consumer_data.channel_ht, &channel->node);
end:
rcu_read_unlock();
pthread_mutex_unlock(&consumer_data.lock);
+ if (!ret && channel->wait_fd != -1 &&
+ channel->metadata_stream == NULL) {
+ notify_channel_pipe(ctx, channel, CONSUMER_CHANNEL_ADD);
+ }
return ret;
}
stream->endpoint_status == CONSUMER_ENDPOINT_INACTIVE) {
continue;
}
- DBG("Active FD %d", stream->wait_fd);
+ /*
+ * This clobbers way too much the debug output. Uncomment that if you
+ * need it for debugging purposes.
+ *
+ * DBG("Active FD %d", stream->wait_fd);
+ */
(*pollfd)[i].fd = stream->wait_fd;
(*pollfd)[i].events = POLLIN | POLLPRI;
local_stream[i] = stream;
cleanup_relayd_ht();
+ lttng_ht_destroy(consumer_data.stream_per_chan_id_ht);
+
/*
* This HT contains streams that are freed by either the metadata thread or
* the data thread so we do *nothing* on the hash table and simply destroy
int (*recv_stream)(struct lttng_consumer_stream *stream),
int (*update_stream)(int stream_key, uint32_t state))
{
- int ret, i;
+ int ret;
struct lttng_consumer_local_data *ctx;
assert(consumer_data.type == LTTNG_CONSUMER_UNKNOWN ||
}
ctx->consumer_error_socket = -1;
+ ctx->consumer_metadata_socket = -1;
/* assign the callbacks */
ctx->on_buffer_ready = buffer_ready;
ctx->on_recv_channel = recv_channel;
goto error_thread_pipe;
}
+ ret = pipe(ctx->consumer_channel_pipe);
+ if (ret < 0) {
+ PERROR("Error creating channel pipe");
+ goto error_channel_pipe;
+ }
+
ret = utils_create_pipe(ctx->consumer_metadata_pipe);
if (ret < 0) {
goto error_metadata_pipe;
error_splice_pipe:
utils_close_pipe(ctx->consumer_metadata_pipe);
error_metadata_pipe:
+ utils_close_pipe(ctx->consumer_channel_pipe);
+error_channel_pipe:
utils_close_pipe(ctx->consumer_thread_pipe);
error_thread_pipe:
- for (i = 0; i < 2; i++) {
- int err;
-
- err = close(ctx->consumer_should_quit[i]);
- if (err) {
- PERROR("close");
- }
- }
+ utils_close_pipe(ctx->consumer_should_quit);
error_poll_fcntl:
error_quit_pipe:
- for (i = 0; i < 2; i++) {
- int err;
-
- err = close(ctx->consumer_data_pipe[i]);
- if (err) {
- PERROR("close");
- }
- }
+ utils_close_pipe(ctx->consumer_data_pipe);
error_poll_pipe:
free(ctx);
error:
if (ret) {
PERROR("close");
}
- ret = close(ctx->consumer_thread_pipe[0]);
- if (ret) {
- PERROR("close");
- }
- ret = close(ctx->consumer_thread_pipe[1]);
- if (ret) {
- PERROR("close");
- }
- ret = close(ctx->consumer_data_pipe[0]);
- if (ret) {
- PERROR("close");
- }
- ret = close(ctx->consumer_data_pipe[1]);
- if (ret) {
- PERROR("close");
- }
- ret = close(ctx->consumer_should_quit[0]);
- if (ret) {
- PERROR("close");
- }
- ret = close(ctx->consumer_should_quit[1]);
+ ret = close(ctx->consumer_metadata_socket);
if (ret) {
PERROR("close");
}
+ utils_close_pipe(ctx->consumer_thread_pipe);
+ utils_close_pipe(ctx->consumer_channel_pipe);
+ utils_close_pipe(ctx->consumer_data_pipe);
+ utils_close_pipe(ctx->consumer_should_quit);
utils_close_pipe(ctx->consumer_splice_metadata_pipe);
unlink(ctx->consumer_command_sock_path);
goto end;
}
ret = lttng_ustctl_get_mmap_read_offset(stream, &mmap_offset);
+
break;
default:
ERR("Unknown consumer_data type");
} else {
/* No streaming, we have to set the len with the full padding */
len += padding;
+
+ /*
+ * Check if we need to change the tracefile before writing the packet.
+ */
+ if (stream->chan->tracefile_size > 0 &&
+ (stream->tracefile_size_current + len) >
+ stream->chan->tracefile_size) {
+ ret = utils_rotate_stream_file(stream->chan->pathname,
+ stream->name, stream->chan->tracefile_size,
+ stream->chan->tracefile_count, stream->uid, stream->gid,
+ stream->out_fd, &(stream->tracefile_count_current));
+ if (ret < 0) {
+ ERR("Rotating output file");
+ goto end;
+ }
+ outfd = stream->out_fd = ret;
+ }
+ stream->tracefile_size_current += len;
}
while (len > 0) {
} else {
/* No streaming, we have to set the len with the full padding */
len += padding;
+
+ /*
+ * Check if we need to change the tracefile before writing the packet.
+ */
+ if (stream->chan->tracefile_size > 0 &&
+ (stream->tracefile_size_current + len) >
+ stream->chan->tracefile_size) {
+ ret = utils_rotate_stream_file(stream->chan->pathname,
+ stream->name, stream->chan->tracefile_size,
+ stream->chan->tracefile_count, stream->uid, stream->gid,
+ stream->out_fd, &(stream->tracefile_count_current));
+ if (ret < 0) {
+ ERR("Rotating output file");
+ goto end;
+ }
+ outfd = stream->out_fd = ret;
+ }
+ stream->tracefile_size_current += len;
}
while (len > 0) {
assert(0);
return -ENOSYS;
}
-
}
/*
lttng_ht_destroy(ht);
}
+void lttng_consumer_close_metadata(void)
+{
+ switch (consumer_data.type) {
+ case LTTNG_CONSUMER_KERNEL:
+ /*
+ * The Kernel consumer has a different metadata scheme so we don't
+ * close anything because the stream will be closed by the session
+ * daemon.
+ */
+ break;
+ case LTTNG_CONSUMER32_UST:
+ case LTTNG_CONSUMER64_UST:
+ /*
+ * Close all metadata streams. The metadata hash table is passed and
+ * this call iterates over it by closing all wakeup fd. This is safe
+ * because at this point we are sure that the metadata producer is
+ * either dead or blocked.
+ */
+ lttng_ustconsumer_close_metadata(metadata_ht);
+ break;
+ default:
+ ERR("Unknown consumer_data type");
+ assert(0);
+ }
+}
+
/*
* Clean up a metadata stream and free its memory.
*/
ret = lttng_ht_del(ht, &iter);
assert(!ret);
- /* Remove node session id from the consumer_data stream ht */
+ iter.iter.node = &stream->node_channel_id.node;
+ ret = lttng_ht_del(consumer_data.stream_per_chan_id_ht, &iter);
+ assert(!ret);
+
iter.iter.node = &stream->node_session_id.node;
ret = lttng_ht_del(consumer_data.stream_list_ht, &iter);
assert(!ret);
int ret = 0;
struct consumer_relayd_sock_pair *relayd;
struct lttng_ht_iter iter;
- struct lttng_ht_node_ulong *node;
+ struct lttng_ht_node_u64 *node;
assert(stream);
assert(ht);
- DBG3("Adding metadata stream %d to hash table", stream->key);
+ DBG3("Adding metadata stream %" PRIu64 " to hash table", stream->key);
pthread_mutex_lock(&consumer_data.lock);
pthread_mutex_lock(&stream->lock);
* Lookup the stream just to make sure it does not exist in our internal
* state. This should NEVER happen.
*/
- lttng_ht_lookup(ht, (void *)((unsigned long) stream->key), &iter);
- node = lttng_ht_iter_get_node_ulong(&iter);
+ lttng_ht_lookup(ht, &stream->key, &iter);
+ node = lttng_ht_iter_get_node_u64(&iter);
assert(!node);
/* Find relayd and, if one is found, increment refcount. */
uatomic_dec(&stream->chan->nb_init_stream_left);
}
- lttng_ht_add_unique_ulong(ht, &stream->node);
+ lttng_ht_add_unique_u64(ht, &stream->node);
+
+ lttng_ht_add_unique_u64(consumer_data.stream_per_chan_id_ht,
+ &stream->node_channel_id);
/*
* Add stream to the stream_list_ht of the consumer data. No need to steal
* the key since the HT does not use it and we allow to add redundant keys
* into this table.
*/
- lttng_ht_add_ulong(consumer_data.stream_list_ht, &stream->node_session_id);
+ lttng_ht_add_u64(consumer_data.stream_list_ht, &stream->node_session_id);
rcu_read_unlock();
uint32_t revents, nb_fd;
struct lttng_consumer_stream *stream = NULL;
struct lttng_ht_iter iter;
- struct lttng_ht_node_ulong *node;
+ struct lttng_ht_node_u64 *node;
struct lttng_poll_event events;
struct lttng_consumer_local_data *ctx = data;
ssize_t len;
rcu_register_thread();
- metadata_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
+ metadata_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
if (!metadata_ht) {
/* ENOMEM at this point. Better to bail out. */
- goto error;
+ goto end_ht;
}
DBG("Thread metadata poll started");
ret = lttng_poll_create(&events, 2, LTTNG_CLOEXEC);
if (ret < 0) {
ERR("Poll set creation failed");
- goto end;
+ goto end_poll;
}
ret = lttng_poll_add(&events, ctx->consumer_metadata_pipe[0], LPOLLIN);
}
rcu_read_lock();
- lttng_ht_lookup(metadata_ht, (void *)((unsigned long) pollfd),
- &iter);
- node = lttng_ht_iter_get_node_ulong(&iter);
+ {
+ uint64_t tmp_id = (uint64_t) pollfd;
+
+ lttng_ht_lookup(metadata_ht, &tmp_id, &iter);
+ }
+ node = lttng_ht_iter_get_node_u64(&iter);
assert(node);
stream = caa_container_of(node, struct lttng_consumer_stream,
error:
end:
DBG("Metadata poll thread exiting");
- lttng_poll_clean(&events);
+ lttng_poll_clean(&events);
+end_poll:
destroy_stream_ht(metadata_ht);
-
+end_ht:
rcu_unregister_thread();
return NULL;
}
rcu_register_thread();
- data_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
+ data_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
if (data_ht == NULL) {
/* ENOMEM at this point. Better to bail out. */
goto end;
ret = add_stream(new_stream, data_ht);
if (ret) {
- ERR("Consumer add stream %d failed. Continuing",
+ ERR("Consumer add stream %" PRIu64 " failed. Continuing",
new_stream->key);
/*
* At this point, if the add_stream fails, it is not in the
return NULL;
}
+/*
+ * Close wake-up end of each stream belonging to the channel. This will
+ * allow the poll() on the stream read-side to detect when the
+ * write-side (application) finally closes them.
+ */
+static
+void consumer_close_channel_streams(struct lttng_consumer_channel *channel)
+{
+ struct lttng_ht *ht;
+ struct lttng_consumer_stream *stream;
+ struct lttng_ht_iter iter;
+
+ ht = consumer_data.stream_per_chan_id_ht;
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry_duplicate(ht->ht,
+ ht->hash_fct(&channel->key, lttng_ht_seed),
+ ht->match_fct, &channel->key,
+ &iter.iter, stream, node_channel_id.node) {
+ switch (consumer_data.type) {
+ case LTTNG_CONSUMER_KERNEL:
+ break;
+ case LTTNG_CONSUMER32_UST:
+ case LTTNG_CONSUMER64_UST:
+ /*
+ * Note: a mutex is taken internally within
+ * liblttng-ust-ctl to protect timer wakeup_fd
+ * use from concurrent close.
+ */
+ lttng_ustconsumer_close_stream_wakeup(stream);
+ break;
+ default:
+ ERR("Unknown consumer_data type");
+ assert(0);
+ }
+ }
+ rcu_read_unlock();
+}
+
+static void destroy_channel_ht(struct lttng_ht *ht)
+{
+ struct lttng_ht_iter iter;
+ struct lttng_consumer_channel *channel;
+ int ret;
+
+ if (ht == NULL) {
+ return;
+ }
+
+ rcu_read_lock();
+ cds_lfht_for_each_entry(ht->ht, &iter.iter, channel, wait_fd_node.node) {
+ ret = lttng_ht_del(ht, &iter);
+ assert(ret != 0);
+ }
+ rcu_read_unlock();
+
+ lttng_ht_destroy(ht);
+}
+
+/*
+ * This thread polls the channel fds to detect when they are being
+ * closed. It closes all related streams if the channel is detected as
+ * closed. It is currently only used as a shim layer for UST because the
+ * consumerd needs to keep the per-stream wakeup end of pipes open for
+ * periodical flush.
+ */
+void *consumer_thread_channel_poll(void *data)
+{
+ int ret, i, pollfd;
+ uint32_t revents, nb_fd;
+ struct lttng_consumer_channel *chan = NULL;
+ struct lttng_ht_iter iter;
+ struct lttng_ht_node_u64 *node;
+ struct lttng_poll_event events;
+ struct lttng_consumer_local_data *ctx = data;
+ struct lttng_ht *channel_ht;
+
+ rcu_register_thread();
+
+ channel_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
+ if (!channel_ht) {
+ /* ENOMEM at this point. Better to bail out. */
+ goto end_ht;
+ }
+
+ DBG("Thread channel poll started");
+
+ /* Size is set to 1 for the consumer_channel pipe */
+ ret = lttng_poll_create(&events, 2, LTTNG_CLOEXEC);
+ if (ret < 0) {
+ ERR("Poll set creation failed");
+ goto end_poll;
+ }
+
+ ret = lttng_poll_add(&events, ctx->consumer_channel_pipe[0], LPOLLIN);
+ if (ret < 0) {
+ goto end;
+ }
+
+ /* Main loop */
+ DBG("Channel main loop started");
+
+ while (1) {
+ /* Only the channel pipe is set */
+ if (LTTNG_POLL_GETNB(&events) == 0 && consumer_quit == 1) {
+ goto end;
+ }
+
+restart:
+ DBG("Channel poll wait with %d fd(s)", LTTNG_POLL_GETNB(&events));
+ ret = lttng_poll_wait(&events, -1);
+ DBG("Channel event catched in thread");
+ if (ret < 0) {
+ if (errno == EINTR) {
+ ERR("Poll EINTR catched");
+ goto restart;
+ }
+ goto end;
+ }
+
+ nb_fd = ret;
+
+ /* From here, the event is a channel wait fd */
+ for (i = 0; i < nb_fd; i++) {
+ revents = LTTNG_POLL_GETEV(&events, i);
+ pollfd = LTTNG_POLL_GETFD(&events, i);
+
+ /* Just don't waste time if no returned events for the fd */
+ if (!revents) {
+ continue;
+ }
+ if (pollfd == ctx->consumer_channel_pipe[0]) {
+ if (revents & (LPOLLERR | LPOLLHUP)) {
+ DBG("Channel thread pipe hung up");
+ /*
+ * Remove the pipe from the poll set and continue the loop
+ * since their might be data to consume.
+ */
+ lttng_poll_del(&events, ctx->consumer_channel_pipe[0]);
+ continue;
+ } else if (revents & LPOLLIN) {
+ enum consumer_channel_action action;
+
+ ret = read_channel_pipe(ctx, &chan, &action);
+ if (ret <= 0) {
+ ERR("Error reading channel pipe");
+ continue;
+ }
+
+ switch (action) {
+ case CONSUMER_CHANNEL_ADD:
+ DBG("Adding channel %d to poll set",
+ chan->wait_fd);
+
+ lttng_ht_node_init_u64(&chan->wait_fd_node,
+ chan->wait_fd);
+ lttng_ht_add_unique_u64(channel_ht,
+ &chan->wait_fd_node);
+ /* Add channel to the global poll events list */
+ lttng_poll_add(&events, chan->wait_fd,
+ LPOLLIN | LPOLLPRI);
+ break;
+ case CONSUMER_CHANNEL_QUIT:
+ /*
+ * Remove the pipe from the poll set and continue the loop
+ * since their might be data to consume.
+ */
+ lttng_poll_del(&events, ctx->consumer_channel_pipe[0]);
+ continue;
+ default:
+ ERR("Unknown action");
+ break;
+ }
+ }
+
+ /* Handle other stream */
+ continue;
+ }
+
+ rcu_read_lock();
+ {
+ uint64_t tmp_id = (uint64_t) pollfd;
+
+ lttng_ht_lookup(channel_ht, &tmp_id, &iter);
+ }
+ node = lttng_ht_iter_get_node_u64(&iter);
+ assert(node);
+
+ chan = caa_container_of(node, struct lttng_consumer_channel,
+ wait_fd_node);
+
+ /* Check for error event */
+ if (revents & (LPOLLERR | LPOLLHUP)) {
+ DBG("Channel fd %d is hup|err.", pollfd);
+
+ lttng_poll_del(&events, chan->wait_fd);
+ ret = lttng_ht_del(channel_ht, &iter);
+ assert(ret == 0);
+ consumer_close_channel_streams(chan);
+ }
+
+ /* Release RCU lock for the channel looked up */
+ rcu_read_unlock();
+ }
+ }
+
+end:
+ lttng_poll_clean(&events);
+end_poll:
+ destroy_channel_ht(channel_ht);
+end_ht:
+ DBG("Channel poll thread exiting");
+ rcu_unregister_thread();
+ return NULL;
+}
+
+static int set_metadata_socket(struct lttng_consumer_local_data *ctx,
+ struct pollfd *sockpoll, int client_socket)
+{
+ int ret;
+
+ assert(ctx);
+ assert(sockpoll);
+
+ if (lttng_consumer_poll_socket(sockpoll) < 0) {
+ ret = -1;
+ goto error;
+ }
+ DBG("Metadata connection on client_socket");
+
+ /* Blocking call, waiting for transmission */
+ ctx->consumer_metadata_socket = lttcomm_accept_unix_sock(client_socket);
+ if (ctx->consumer_metadata_socket < 0) {
+ WARN("On accept metadata");
+ ret = -1;
+ goto error;
+ }
+ ret = 0;
+
+error:
+ return ret;
+}
+
/*
* This thread listens on the consumerd socket and receives the file
* descriptors from the session daemon.
goto end;
}
+ /*
+ * Setup metadata socket which is the second socket connection on the
+ * command unix socket.
+ */
+ ret = set_metadata_socket(ctx, consumer_sockpoll, client_socket);
+ if (ret < 0) {
+ goto end;
+ }
+
/* This socket is not useful anymore. */
ret = close(client_socket);
if (ret < 0) {
end:
DBG("Consumer thread sessiond poll exiting");
+ /*
+ * Close metadata streams since the producer is the session daemon which
+ * just died.
+ *
+ * NOTE: for now, this only applies to the UST tracer.
+ */
+ lttng_consumer_close_metadata();
+
/*
* when all fds have hung up, the polling thread
* can exit cleanly
*/
notify_thread_pipe(ctx->consumer_data_pipe[1]);
+ notify_channel_pipe(ctx, NULL, CONSUMER_CHANNEL_QUIT);
+
/* Cleaning up possibly open sockets. */
if (sock >= 0) {
ret = close(sock);
*/
void lttng_consumer_init(void)
{
- consumer_data.channel_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
- consumer_data.relayd_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
- consumer_data.stream_list_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG);
+ consumer_data.channel_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
+ consumer_data.relayd_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
+ consumer_data.stream_list_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
+ consumer_data.stream_per_chan_id_ht = lttng_ht_new(0, LTTNG_HT_TYPE_U64);
}
/*
*/
int consumer_add_relayd_socket(int net_seq_idx, int sock_type,
struct lttng_consumer_local_data *ctx, int sock,
- struct pollfd *consumer_sockpoll, struct lttcomm_sock *relayd_sock,
- unsigned int sessiond_id)
+ struct pollfd *consumer_sockpoll,
+ struct lttcomm_relayd_sock *relayd_sock, unsigned int sessiond_id)
{
int fd = -1, ret = -1, relayd_created = 0;
enum lttng_error_code ret_code = LTTNG_OK;
struct consumer_relayd_sock_pair *relayd;
+ assert(ctx);
+ assert(relayd_sock);
+
DBG("Consumer adding relayd socket (idx: %d)", net_seq_idx);
/* First send a status message before receiving the fds. */
switch (sock_type) {
case LTTNG_STREAM_CONTROL:
/* Copy received lttcomm socket */
- lttcomm_copy_sock(&relayd->control_sock, relayd_sock);
- ret = lttcomm_create_sock(&relayd->control_sock);
+ lttcomm_copy_sock(&relayd->control_sock.sock, &relayd_sock->sock);
+ ret = lttcomm_create_sock(&relayd->control_sock.sock);
/* Immediately try to close the created socket if valid. */
- if (relayd->control_sock.fd >= 0) {
- if (close(relayd->control_sock.fd)) {
+ if (relayd->control_sock.sock.fd >= 0) {
+ if (close(relayd->control_sock.sock.fd)) {
PERROR("close relayd control socket");
}
}
}
/* Assign new file descriptor */
- relayd->control_sock.fd = fd;
+ relayd->control_sock.sock.fd = fd;
+ /* Assign version values. */
+ relayd->control_sock.major = relayd_sock->major;
+ relayd->control_sock.minor = relayd_sock->minor;
/*
* Create a session on the relayd and store the returned id. Lock the
break;
case LTTNG_STREAM_DATA:
/* Copy received lttcomm socket */
- lttcomm_copy_sock(&relayd->data_sock, relayd_sock);
- ret = lttcomm_create_sock(&relayd->data_sock);
+ lttcomm_copy_sock(&relayd->data_sock.sock, &relayd_sock->sock);
+ ret = lttcomm_create_sock(&relayd->data_sock.sock);
/* Immediately try to close the created socket if valid. */
- if (relayd->data_sock.fd >= 0) {
- if (close(relayd->data_sock.fd)) {
+ if (relayd->data_sock.sock.fd >= 0) {
+ if (close(relayd->data_sock.sock.fd)) {
PERROR("close relayd data socket");
}
}
}
/* Assign new file descriptor */
- relayd->data_sock.fd = fd;
+ relayd->data_sock.sock.fd = fd;
+ /* Assign version values. */
+ relayd->data_sock.major = relayd_sock->major;
+ relayd->data_sock.minor = relayd_sock->minor;
break;
default:
ERR("Unknown relayd socket type (%d)", sock_type);
goto error;
}
- DBG("Consumer %s socket created successfully with net idx %d (fd: %d)",
+ DBG("Consumer %s socket created successfully with net idx %" PRIu64 " (fd: %d)",
sock_type == LTTNG_STREAM_CONTROL ? "control" : "data",
relayd->net_seq_idx, fd);
}
cds_lfht_for_each_entry_duplicate(ht->ht,
- ht->hash_fct((void *)((unsigned long) id), lttng_ht_seed),
- ht->match_fct, (void *)((unsigned long) id),
+ ht->hash_fct(&id, lttng_ht_seed),
+ ht->match_fct, &id,
&iter.iter, stream, node_session_id.node) {
/* If this call fails, the stream is being used hence data pending. */
ret = stream_try_lock(stream);