X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=src%2Fcommon%2Fconsumer.c;h=f4cfa82ce8dcc1179843a806a4d969f432b5391e;hp=d245ed7eecb385cb72b76841a14944810f9a72c1;hb=c5b6f4f08fe8d1abff74c7f6ad3630b7dcf0669d;hpb=8994307fa7ccf9b61cc0157f2c5d34e248c56641 diff --git a/src/common/consumer.c b/src/common/consumer.c index d245ed7ee..f4cfa82ce 100644 --- a/src/common/consumer.c +++ b/src/common/consumer.c @@ -47,9 +47,6 @@ struct lttng_consumer_global_data consumer_data = { .type = LTTNG_CONSUMER_UNKNOWN, }; -/* timeout parameter, to control the polling thread grace period. */ -int consumer_poll_timeout = -1; - /* * 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. @@ -59,15 +56,12 @@ int consumer_poll_timeout = -1; volatile int consumer_quit; /* - * The following two hash tables are visible by all threads which are separated - * in different source files. - * * Global hash table containing respectively metadata and data streams. The * stream element in this ht should only be updated by the metadata poll thread * for the metadata and the data poll thread for the data. */ -struct lttng_ht *metadata_ht; -struct lttng_ht *data_ht; +static struct lttng_ht *metadata_ht; +static struct lttng_ht *data_ht; /* * Notify a thread pipe to poll back again. This usually means that some global @@ -133,6 +127,12 @@ void consumer_steal_stream_key(int key, struct lttng_ht *ht) rcu_read_unlock(); } +/* + * Return a channel object for the given key. + * + * RCU read side lock MUST be acquired before calling this function and + * protects the channel ptr. + */ static struct lttng_consumer_channel *consumer_find_channel(int key) { struct lttng_ht_iter iter; @@ -144,8 +144,6 @@ static struct lttng_consumer_channel *consumer_find_channel(int key) return NULL; } - rcu_read_lock(); - lttng_ht_lookup(consumer_data.channel_ht, (void *)((unsigned long) key), &iter); node = lttng_ht_iter_get_node_ulong(&iter); @@ -153,8 +151,6 @@ static struct lttng_consumer_channel *consumer_find_channel(int key) channel = caa_container_of(node, struct lttng_consumer_channel, node); } - rcu_read_unlock(); - return channel; } @@ -238,6 +234,27 @@ static void destroy_relayd(struct consumer_relayd_sock_pair *relayd) call_rcu(&relayd->node.head, consumer_rcu_free_relayd); } +/* + * Iterate over the relayd hash table and destroy each element. Finally, + * destroy the whole hash table. + */ +static void cleanup_relayd_ht(void) +{ + struct lttng_ht_iter iter; + struct consumer_relayd_sock_pair *relayd; + + rcu_read_lock(); + + cds_lfht_for_each_entry(consumer_data.relayd_ht->ht, &iter.iter, relayd, + node.node) { + destroy_relayd(relayd); + } + + lttng_ht_destroy(consumer_data.relayd_ht); + + rcu_read_unlock(); +} + /* * Update the end point status of all streams having the given network sequence * index (relayd index). @@ -288,6 +305,8 @@ static void cleanup_relayd(struct consumer_relayd_sock_pair *relayd, assert(relayd); + DBG("Cleaning up relayd sockets"); + /* Save the net sequence index before destroying the object */ netidx = relayd->net_seq_idx; @@ -352,8 +371,8 @@ void consumer_del_stream(struct lttng_consumer_stream *stream, goto free_stream; } - pthread_mutex_lock(&stream->lock); pthread_mutex_lock(&consumer_data.lock); + pthread_mutex_lock(&stream->lock); switch (consumer_data.type) { case LTTNG_CONSUMER_KERNEL: @@ -445,8 +464,8 @@ void consumer_del_stream(struct lttng_consumer_stream *stream, end: consumer_data.need_update = 1; - pthread_mutex_unlock(&consumer_data.lock); pthread_mutex_unlock(&stream->lock); + pthread_mutex_unlock(&consumer_data.lock); if (free_chan) { consumer_del_channel(free_chan); @@ -479,6 +498,8 @@ struct lttng_consumer_stream *consumer_allocate_stream( goto end; } + rcu_read_lock(); + /* * Get stream's channel reference. Needed when adding the stream to the * global hash table. @@ -535,9 +556,12 @@ struct lttng_consumer_stream *consumer_allocate_stream( stream->path_name, stream->key, stream->shm_fd, stream->wait_fd, (unsigned long long) stream->mmap_len, stream->out_fd, stream->net_seq_idx, stream->session_id); + + rcu_read_unlock(); return stream; error: + rcu_read_unlock(); free(stream); end: return NULL; @@ -558,6 +582,7 @@ static int consumer_add_stream(struct lttng_consumer_stream *stream, DBG3("Adding consumer stream %d", stream->key); pthread_mutex_lock(&consumer_data.lock); + pthread_mutex_lock(&stream->lock); rcu_read_lock(); /* Steal stream identifier to avoid having streams with the same key */ @@ -597,6 +622,7 @@ static int consumer_add_stream(struct lttng_consumer_stream *stream, consumer_data.need_update = 1; rcu_read_unlock(); + pthread_mutex_unlock(&stream->lock); pthread_mutex_unlock(&consumer_data.lock); return ret; @@ -759,6 +785,8 @@ void consumer_del_channel(struct lttng_consumer_channel *channel) int ret; struct lttng_ht_iter iter; + DBG("Consumer delete channel key %d", channel->key); + pthread_mutex_lock(&consumer_data.lock); switch (consumer_data.type) { @@ -912,7 +940,7 @@ static int consumer_update_poll_array( * changed where this function will be called back again. */ if (stream->state != LTTNG_CONSUMER_ACTIVE_STREAM || - stream->endpoint_status) { + stream->endpoint_status == CONSUMER_ENDPOINT_INACTIVE) { continue; } DBG("Active FD %d", stream->wait_fd); @@ -996,8 +1024,8 @@ int lttng_consumer_send_error( } /* - * Close all the tracefiles and stream fds, should be called when all instances - * are destroyed. + * Close all the tracefiles and stream fds and MUST be called when all + * instances are destroyed i.e. when all threads were joined and are ended. */ void lttng_consumer_cleanup(void) { @@ -1016,6 +1044,15 @@ void lttng_consumer_cleanup(void) rcu_read_unlock(); lttng_ht_destroy(consumer_data.channel_ht); + + cleanup_relayd_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 + * it. + */ + lttng_ht_destroy(consumer_data.stream_list_ht); } /* @@ -1262,6 +1299,8 @@ end: * core function for writing trace buffers to either the local filesystem or * the network. * + * It must be called with the stream lock held. + * * Careful review MUST be put if any changes occur! * * Returns the number of bytes written @@ -1282,8 +1321,6 @@ ssize_t lttng_consumer_on_read_subbuffer_mmap( /* RCU lock for the relayd pointer */ rcu_read_lock(); - pthread_mutex_lock(&stream->lock); - /* Flag that the current stream if set for network streaming. */ if (stream->net_seq_idx != -1) { relayd = consumer_find_relayd(stream->net_seq_idx); @@ -1408,7 +1445,6 @@ end: if (relayd && stream->metadata_flag) { pthread_mutex_unlock(&relayd->ctrl_sock_mutex); } - pthread_mutex_unlock(&stream->lock); rcu_read_unlock(); return written; @@ -1417,6 +1453,8 @@ end: /* * Splice the data from the ring buffer to the tracefile. * + * It must be called with the stream lock held. + * * Returns the number of bytes spliced. */ ssize_t lttng_consumer_on_read_subbuffer_splice( @@ -1449,8 +1487,6 @@ ssize_t lttng_consumer_on_read_subbuffer_splice( /* RCU lock for the relayd pointer */ rcu_read_lock(); - pthread_mutex_lock(&stream->lock); - /* Flag that the current stream if set for network streaming. */ if (stream->net_seq_idx != -1) { relayd = consumer_find_relayd(stream->net_seq_idx); @@ -1557,7 +1593,7 @@ ssize_t lttng_consumer_on_read_subbuffer_splice( written = ret_splice; } /* Socket operation failed. We consider the relayd dead */ - if (errno == EBADF) { + if (errno == EBADF || errno == EPIPE) { WARN("Remote relayd disconnected. Stopping"); relayd_hang_up = 1; goto write_error; @@ -1618,7 +1654,6 @@ end: if (relayd && stream->metadata_flag) { pthread_mutex_unlock(&relayd->ctrl_sock_mutex); } - pthread_mutex_unlock(&stream->lock); rcu_read_unlock(); return written; @@ -1692,7 +1727,6 @@ int lttng_consumer_recv_cmd(struct lttng_consumer_local_data *ctx, */ static void destroy_data_stream_ht(struct lttng_ht *ht) { - int ret; struct lttng_ht_iter iter; struct lttng_consumer_stream *stream; @@ -1702,10 +1736,11 @@ static void destroy_data_stream_ht(struct lttng_ht *ht) rcu_read_lock(); cds_lfht_for_each_entry(ht->ht, &iter.iter, stream, node.node) { - ret = lttng_ht_del(ht, &iter); - assert(!ret); - - call_rcu(&stream->node.head, consumer_free_stream); + /* + * Ignore return value since we are currently cleaning up so any error + * can't be handled. + */ + (void) consumer_del_stream(stream, ht); } rcu_read_unlock(); @@ -1719,7 +1754,6 @@ static void destroy_data_stream_ht(struct lttng_ht *ht) */ static void destroy_stream_ht(struct lttng_ht *ht) { - int ret; struct lttng_ht_iter iter; struct lttng_consumer_stream *stream; @@ -1729,10 +1763,11 @@ static void destroy_stream_ht(struct lttng_ht *ht) rcu_read_lock(); cds_lfht_for_each_entry(ht->ht, &iter.iter, stream, node.node) { - ret = lttng_ht_del(ht, &iter); - assert(!ret); - - call_rcu(&stream->node.head, consumer_free_stream); + /* + * Ignore return value since we are currently cleaning up so any error + * can't be handled. + */ + (void) consumer_del_metadata_stream(stream, ht); } rcu_read_unlock(); @@ -1764,9 +1799,9 @@ void consumer_del_metadata_stream(struct lttng_consumer_stream *stream, goto free_stream; } + pthread_mutex_lock(&consumer_data.lock); pthread_mutex_lock(&stream->lock); - pthread_mutex_lock(&consumer_data.lock); switch (consumer_data.type) { case LTTNG_CONSUMER_KERNEL: if (stream->mmap_base != NULL) { @@ -1856,8 +1891,8 @@ void consumer_del_metadata_stream(struct lttng_consumer_stream *stream, } end: - pthread_mutex_unlock(&consumer_data.lock); pthread_mutex_unlock(&stream->lock); + pthread_mutex_unlock(&consumer_data.lock); if (free_chan) { consumer_del_channel(free_chan); @@ -1876,6 +1911,8 @@ static int consumer_add_metadata_stream(struct lttng_consumer_stream *stream, { int ret = 0; struct consumer_relayd_sock_pair *relayd; + struct lttng_ht_iter iter; + struct lttng_ht_node_ulong *node; assert(stream); assert(ht); @@ -1883,6 +1920,7 @@ static int consumer_add_metadata_stream(struct lttng_consumer_stream *stream, DBG3("Adding metadata stream %d to hash table", stream->wait_fd); pthread_mutex_lock(&consumer_data.lock); + pthread_mutex_lock(&stream->lock); /* * From here, refcounts are updated so be _careful_ when returning an error @@ -1890,6 +1928,15 @@ static int consumer_add_metadata_stream(struct lttng_consumer_stream *stream, */ rcu_read_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->wait_fd), &iter); + node = lttng_ht_iter_get_node_ulong(&iter); + assert(!node); + /* Find relayd and, if one is found, increment refcount. */ relayd = consumer_find_relayd(stream->net_seq_idx); if (relayd != NULL) { @@ -1910,9 +1957,6 @@ static int consumer_add_metadata_stream(struct lttng_consumer_stream *stream, uatomic_dec(&stream->chan->nb_init_streams); } - /* Steal stream identifier to avoid having streams with the same key */ - consumer_steal_stream_key(stream->key, ht); - lttng_ht_add_unique_ulong(ht, &stream->node); /* @@ -1924,6 +1968,7 @@ static int consumer_add_metadata_stream(struct lttng_consumer_stream *stream, rcu_read_unlock(); + pthread_mutex_unlock(&stream->lock); pthread_mutex_unlock(&consumer_data.lock); return ret; } @@ -1941,7 +1986,7 @@ static void validate_endpoint_status_data_stream(void) rcu_read_lock(); cds_lfht_for_each_entry(data_ht->ht, &iter.iter, stream, node.node) { /* Validate delete flag of the stream */ - if (!stream->endpoint_status) { + if (stream->endpoint_status == CONSUMER_ENDPOINT_ACTIVE) { continue; } /* Delete it right now */ @@ -1966,7 +2011,7 @@ static void validate_endpoint_status_metadata_stream( rcu_read_lock(); cds_lfht_for_each_entry(metadata_ht->ht, &iter.iter, stream, node.node) { /* Validate delete flag of the stream */ - if (!stream->endpoint_status) { + if (stream->endpoint_status == CONSUMER_ENDPOINT_ACTIVE) { continue; } /* @@ -1998,6 +2043,12 @@ void *consumer_thread_metadata_poll(void *data) rcu_register_thread(); + metadata_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); + if (!metadata_ht) { + /* ENOMEM at this point. Better to bail out. */ + goto error; + } + DBG("Thread metadata poll started"); /* Size is set to 1 for the consumer_metadata pipe */ @@ -2055,7 +2106,10 @@ restart: * since their might be data to consume. */ lttng_poll_del(&events, ctx->consumer_metadata_pipe[0]); - close(ctx->consumer_metadata_pipe[0]); + ret = close(ctx->consumer_metadata_pipe[0]); + if (ret < 0) { + PERROR("close metadata pipe"); + } continue; } else if (revents & LPOLLIN) { do { @@ -2161,9 +2215,7 @@ end: DBG("Metadata poll thread exiting"); lttng_poll_clean(&events); - if (metadata_ht) { - destroy_stream_ht(metadata_ht); - } + destroy_stream_ht(metadata_ht); rcu_unregister_thread(); return NULL; @@ -2188,6 +2240,7 @@ void *consumer_thread_data_poll(void *data) data_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); if (data_ht == NULL) { + /* ENOMEM at this point. Better to bail out. */ goto end; } @@ -2248,7 +2301,7 @@ void *consumer_thread_data_poll(void *data) /* poll on the array of fds */ restart: DBG("polling on %d fd", nb_fd + 1); - num_rdy = poll(pollfd, nb_fd + 1, consumer_poll_timeout); + num_rdy = poll(pollfd, nb_fd + 1, -1); DBG("poll num_rdy : %d", num_rdy); if (num_rdy == -1) { /* @@ -2307,6 +2360,9 @@ void *consumer_thread_data_poll(void *data) /* Take care of high priority channels first. */ for (i = 0; i < nb_fd; i++) { + if (local_stream[i] == NULL) { + continue; + } if (pollfd[i].revents & POLLPRI) { DBG("Urgent read on fd %d", pollfd[i].fd); high_prio = 1; @@ -2315,6 +2371,7 @@ void *consumer_thread_data_poll(void *data) if (len < 0 && len != -EAGAIN && len != -ENODATA) { /* Clean the stream and free it. */ consumer_del_stream(local_stream[i], data_ht); + local_stream[i] = NULL; } else if (len > 0) { local_stream[i]->data_read = 1; } @@ -2331,6 +2388,9 @@ void *consumer_thread_data_poll(void *data) /* Take care of low priority channels. */ for (i = 0; i < nb_fd; i++) { + if (local_stream[i] == NULL) { + continue; + } if ((pollfd[i].revents & POLLIN) || local_stream[i]->hangup_flush_done) { DBG("Normal read on fd %d", pollfd[i].fd); @@ -2339,6 +2399,7 @@ void *consumer_thread_data_poll(void *data) if (len < 0 && len != -EAGAIN && len != -ENODATA) { /* Clean the stream and free it. */ consumer_del_stream(local_stream[i], data_ht); + local_stream[i] = NULL; } else if (len > 0) { local_stream[i]->data_read = 1; } @@ -2347,12 +2408,15 @@ void *consumer_thread_data_poll(void *data) /* Handle hangup and errors */ for (i = 0; i < nb_fd; i++) { + if (local_stream[i] == NULL) { + continue; + } if (!local_stream[i]->hangup_flush_done && (pollfd[i].revents & (POLLHUP | POLLERR | POLLNVAL)) && (consumer_data.type == LTTNG_CONSUMER32_UST || consumer_data.type == LTTNG_CONSUMER64_UST)) { DBG("fd %d is hup|err|nval. Attempting flush and read.", - pollfd[i].fd); + pollfd[i].fd); lttng_ustconsumer_on_stream_hangup(local_stream[i]); /* Attempt read again, for the data we just flushed. */ local_stream[i]->data_read = 1; @@ -2366,22 +2430,27 @@ void *consumer_thread_data_poll(void *data) DBG("Polling fd %d tells it has hung up.", pollfd[i].fd); if (!local_stream[i]->data_read) { consumer_del_stream(local_stream[i], data_ht); + local_stream[i] = NULL; num_hup++; } } else if (pollfd[i].revents & POLLERR) { ERR("Error returned in polling fd %d.", pollfd[i].fd); if (!local_stream[i]->data_read) { consumer_del_stream(local_stream[i], data_ht); + local_stream[i] = NULL; num_hup++; } } else if (pollfd[i].revents & POLLNVAL) { ERR("Polling fd %d tells fd is not open.", pollfd[i].fd); if (!local_stream[i]->data_read) { consumer_del_stream(local_stream[i], data_ht); + local_stream[i] = NULL; num_hup++; } } - local_stream[i]->data_read = 0; + if (local_stream[i] != NULL) { + local_stream[i]->data_read = 0; + } } } end: @@ -2403,12 +2472,13 @@ end: * only tracked fd in the poll set. The thread will take care of closing * the read side. */ - close(ctx->consumer_metadata_pipe[1]); - - if (data_ht) { - destroy_data_stream_ht(data_ht); + ret = close(ctx->consumer_metadata_pipe[1]); + if (ret < 0) { + PERROR("close data pipe"); } + destroy_data_stream_ht(data_ht); + rcu_unregister_thread(); return NULL; } @@ -2419,7 +2489,7 @@ end: */ void *consumer_thread_sessiond_poll(void *data) { - int sock, client_socket, ret; + int sock = -1, client_socket, ret; /* * structure to poll for incoming data on communication socket avoids * making blocking sockets. @@ -2479,6 +2549,13 @@ void *consumer_thread_sessiond_poll(void *data) goto end; } + /* This socket is not useful anymore. */ + ret = close(client_socket); + if (ret < 0) { + PERROR("close client_socket"); + } + client_socket = -1; + /* update the polling structure to poll on the established socket */ consumer_sockpoll[1].fd = sock; consumer_sockpoll[1].events = POLLIN | POLLPRI; @@ -2516,19 +2593,26 @@ end: */ consumer_quit = 1; - /* - * 2s of grace period, if no polling events occur during - * this period, the polling thread will exit even if there - * are still open FDs (should not happen, but safety mechanism). - */ - consumer_poll_timeout = LTTNG_CONSUMER_POLL_TIMEOUT; - /* * Notify the data poll thread to poll back again and test the * consumer_quit state that we just set so to quit gracefully. */ notify_thread_pipe(ctx->consumer_data_pipe[1]); + /* Cleaning up possibly open sockets. */ + if (sock >= 0) { + ret = close(sock); + if (ret < 0) { + PERROR("close sock sessiond poll"); + } + } + if (client_socket >= 0) { + ret = close(sock); + if (ret < 0) { + PERROR("close client_socket sessiond poll"); + } + } + rcu_unregister_thread(); return NULL; } @@ -2536,17 +2620,27 @@ end: ssize_t lttng_consumer_read_subbuffer(struct lttng_consumer_stream *stream, struct lttng_consumer_local_data *ctx) { + ssize_t ret; + + pthread_mutex_lock(&stream->lock); + switch (consumer_data.type) { case LTTNG_CONSUMER_KERNEL: - return lttng_kconsumer_read_subbuffer(stream, ctx); + ret = lttng_kconsumer_read_subbuffer(stream, ctx); + break; case LTTNG_CONSUMER32_UST: case LTTNG_CONSUMER64_UST: - return lttng_ustconsumer_read_subbuffer(stream, ctx); + ret = lttng_ustconsumer_read_subbuffer(stream, ctx); + break; default: ERR("Unknown consumer_data type"); assert(0); - return -ENOSYS; + ret = -ENOSYS; + break; } + + pthread_mutex_unlock(&stream->lock); + return ret; } int lttng_consumer_on_recv_stream(struct lttng_consumer_stream *stream) @@ -2572,11 +2666,6 @@ 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); - - metadata_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); - assert(metadata_ht); - data_ht = lttng_ht_new(0, LTTNG_HT_TYPE_ULONG); - assert(data_ht); } /* @@ -2589,11 +2678,19 @@ 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) { - int fd, ret = -1; + int fd = -1, ret = -1; + enum lttng_error_code ret_code = LTTNG_OK; struct consumer_relayd_sock_pair *relayd; DBG("Consumer adding relayd socket (idx: %d)", net_seq_idx); + /* First send a status message before receiving the fds. */ + ret = consumer_send_status_msg(sock, ret_code); + if (ret < 0) { + /* Somehow, the session daemon is not responding anymore. */ + goto error; + } + /* Get relayd reference if exists. */ relayd = consumer_find_relayd(net_seq_idx); if (relayd == NULL) { @@ -2616,6 +2713,14 @@ int consumer_add_relayd_socket(int net_seq_idx, int sock_type, if (ret != sizeof(fd)) { lttng_consumer_send_error(ctx, LTTCOMM_CONSUMERD_ERROR_RECV_FD); ret = -1; + fd = -1; /* Just in case it gets set with an invalid value. */ + goto error; + } + + /* We have the fds without error. Send status back. */ + ret = consumer_send_status_msg(sock, ret_code); + if (ret < 0) { + /* Somehow, the session daemon is not responding anymore. */ goto error; } @@ -2625,27 +2730,46 @@ int consumer_add_relayd_socket(int net_seq_idx, int sock_type, /* Copy received lttcomm socket */ lttcomm_copy_sock(&relayd->control_sock, relayd_sock); ret = lttcomm_create_sock(&relayd->control_sock); + /* Immediately try to close the created socket if valid. */ + if (relayd->control_sock.fd >= 0) { + if (close(relayd->control_sock.fd)) { + PERROR("close relayd control socket"); + } + } + /* Handle create_sock error. */ if (ret < 0) { goto error; } - /* Close the created socket fd which is useless */ - close(relayd->control_sock.fd); - /* Assign new file descriptor */ relayd->control_sock.fd = fd; + + /* + * Create a session on the relayd and store the returned id. No need to + * grab the socket lock since the relayd object is not yet visible. + */ + ret = relayd_create_session(&relayd->control_sock, + &relayd->session_id); + if (ret < 0) { + goto error; + } + break; case LTTNG_STREAM_DATA: /* Copy received lttcomm socket */ lttcomm_copy_sock(&relayd->data_sock, relayd_sock); ret = lttcomm_create_sock(&relayd->data_sock); + /* Immediately try to close the created socket if valid. */ + if (relayd->data_sock.fd >= 0) { + if (close(relayd->data_sock.fd)) { + PERROR("close relayd data socket"); + } + } + /* Handle create_sock error. */ if (ret < 0) { goto error; } - /* Close the created socket fd which is useless */ - close(relayd->data_sock.fd); - /* Assign new file descriptor */ relayd->data_sock.fd = fd; break; @@ -2665,9 +2789,43 @@ int consumer_add_relayd_socket(int net_seq_idx, int sock_type, add_relayd(relayd); /* All good! */ - ret = 0; + return 0; error: + /* Close received socket if valid. */ + if (fd >= 0) { + if (close(fd)) { + PERROR("close received socket"); + } + } + return ret; +} + +/* + * Try to lock the stream mutex. + * + * On success, 1 is returned else 0 indicating that the mutex is NOT lock. + */ +static int stream_try_lock(struct lttng_consumer_stream *stream) +{ + int ret; + + assert(stream); + + /* + * Try to lock the stream mutex. On failure, we know that the stream is + * being used else where hence there is data still being extracted. + */ + ret = pthread_mutex_trylock(&stream->lock); + if (ret) { + /* For both EBUSY and EINVAL error, the mutex is NOT locked. */ + ret = 0; + goto end; + } + + ret = 1; + +end: return ret; } @@ -2675,29 +2833,29 @@ error: * Check if for a given session id there is still data needed to be extract * from the buffers. * - * Return 1 if data is in fact available to be read or else 0. + * Return 1 if data is pending or else 0 meaning ready to be read. */ -int consumer_data_available(uint64_t id) +int consumer_data_pending(uint64_t id) { int ret; struct lttng_ht_iter iter; struct lttng_ht *ht; struct lttng_consumer_stream *stream; struct consumer_relayd_sock_pair *relayd; - int (*data_available)(struct lttng_consumer_stream *); + int (*data_pending)(struct lttng_consumer_stream *); - DBG("Consumer data available command on session id %" PRIu64, id); + DBG("Consumer data pending command on session id %" PRIu64, id); rcu_read_lock(); pthread_mutex_lock(&consumer_data.lock); switch (consumer_data.type) { case LTTNG_CONSUMER_KERNEL: - data_available = lttng_kconsumer_data_available; + data_pending = lttng_kconsumer_data_pending; break; case LTTNG_CONSUMER32_UST: case LTTNG_CONSUMER64_UST: - data_available = lttng_ustconsumer_data_available; + data_pending = lttng_ustconsumer_data_pending; break; default: ERR("Unknown consumer data type"); @@ -2708,33 +2866,59 @@ int consumer_data_available(uint64_t id) ht = consumer_data.stream_list_ht; cds_lfht_for_each_entry_duplicate(ht->ht, - ht->hash_fct((void *)((unsigned long) id), 0x42UL), + ht->hash_fct((void *)((unsigned long) id), lttng_ht_seed), ht->match_fct, (void *)((unsigned long) id), &iter.iter, stream, node_session_id.node) { - /* Check the stream for data. */ - ret = data_available(stream); - if (ret == 0) { - goto data_not_available; + /* If this call fails, the stream is being used hence data pending. */ + ret = stream_try_lock(stream); + if (!ret) { + goto data_not_pending; } + /* + * A removed node from the hash table indicates that the stream has + * been deleted thus having a guarantee that the buffers are closed + * on the consumer side. However, data can still be transmitted + * over the network so don't skip the relayd check. + */ + ret = cds_lfht_is_node_deleted(&stream->node.node); + if (!ret) { + /* Check the stream if there is data in the buffers. */ + ret = data_pending(stream); + if (ret == 1) { + pthread_mutex_unlock(&stream->lock); + goto data_not_pending; + } + } + + /* Relayd check */ if (stream->net_seq_idx != -1) { relayd = consumer_find_relayd(stream->net_seq_idx); - assert(relayd); + if (!relayd) { + /* + * At this point, if the relayd object is not available for the + * given stream, it is because the relayd is being cleaned up + * so every stream associated with it (for a session id value) + * are or will be marked for deletion hence no data pending. + */ + pthread_mutex_unlock(&stream->lock); + goto data_not_pending; + } - pthread_mutex_lock(&stream->lock); pthread_mutex_lock(&relayd->ctrl_sock_mutex); if (stream->metadata_flag) { ret = relayd_quiescent_control(&relayd->control_sock); } else { - ret = relayd_data_available(&relayd->control_sock, + ret = relayd_data_pending(&relayd->control_sock, stream->relayd_stream_id, stream->next_net_seq_num); } pthread_mutex_unlock(&relayd->ctrl_sock_mutex); - pthread_mutex_unlock(&stream->lock); - if (ret == 0) { - goto data_not_available; + if (ret == 1) { + pthread_mutex_unlock(&stream->lock); + goto data_not_pending; } } + pthread_mutex_unlock(&stream->lock); } /* @@ -2747,11 +2931,25 @@ int consumer_data_available(uint64_t id) /* Data is available to be read by a viewer. */ pthread_mutex_unlock(&consumer_data.lock); rcu_read_unlock(); - return 1; + return 0; -data_not_available: +data_not_pending: /* Data is still being extracted from buffers. */ pthread_mutex_unlock(&consumer_data.lock); rcu_read_unlock(); - return 0; + return 1; +} + +/* + * Send a ret code status message to the sessiond daemon. + * + * Return the sendmsg() return value. + */ +int consumer_send_status_msg(int sock, int ret_code) +{ + struct lttcomm_consumer_status_msg msg; + + msg.ret_code = ret_code; + + return lttcomm_send_unix_sock(sock, &msg, sizeof(msg)); }