int ret;
struct lttng_ht_iter iter;
+ if (relayd == NULL) {
+ return;
+ }
+
DBG("Consumer destroy and close relayd socket pair");
iter.iter.node = &relayd->node.node;
ret = lttng_ht_del(consumer_data.relayd_ht, &iter);
- assert(!ret);
+ if (ret != 0) {
+ /* We assume the relayd was already destroyed */
+ return;
+ }
/* Close all sockets */
pthread_mutex_lock(&relayd->ctrl_sock_mutex);
if (relayd != NULL) {
uatomic_dec(&relayd->refcount);
assert(uatomic_read(&relayd->refcount) >= 0);
- if (uatomic_read(&relayd->refcount) == 0) {
- /* Refcount of the relayd struct is 0, destroy it */
+
+ ret = relayd_send_close_stream(&relayd->control_sock,
+ stream->relayd_stream_id,
+ stream->next_net_seq_num - 1);
+ if (ret < 0) {
+ DBG("Unable to close stream on the relayd. Continuing");
+ /*
+ * Continue here. There is nothing we can do for the relayd.
+ * Chances are that the relayd has closed the socket so we just
+ * continue cleaning up.
+ */
+ }
+
+ /* Both conditions are met, we destroy the relayd. */
+ if (uatomic_read(&relayd->refcount) == 0 &&
+ uatomic_read(&relayd->destroy_flag)) {
consumer_destroy_relayd(relayd);
}
}
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);
pthread_mutex_init(&obj->ctrl_sock_mutex, NULL);
*
* Return destination file descriptor or negative value on error.
*/
-int consumer_handle_stream_before_relayd(struct lttng_consumer_stream *stream,
- size_t data_size)
+static int write_relayd_stream_header(struct lttng_consumer_stream *stream,
+ size_t data_size, struct consumer_relayd_sock_pair *relayd)
{
int outfd = -1, ret;
- struct consumer_relayd_sock_pair *relayd;
struct lttcomm_relayd_data_hdr data_hdr;
/* Safety net */
assert(stream);
+ assert(relayd);
/* Reset data header */
memset(&data_hdr, 0, sizeof(data_hdr));
- rcu_read_lock();
- /* Get relayd reference of the stream. */
- relayd = consumer_find_relayd(stream->net_seq_idx);
- if (relayd == NULL) {
- /* Stream is either local or corrupted */
- goto error;
- }
-
- DBG("Consumer found relayd socks with index %d", stream->net_seq_idx);
if (stream->metadata_flag) {
/* Caller MUST acquire the relayd control socket lock */
ret = relayd_send_metadata(&relayd->control_sock, data_size);
/* Set header with stream information */
data_hdr.stream_id = htobe64(stream->relayd_stream_id);
data_hdr.data_size = htobe32(data_size);
+ data_hdr.net_seq_num = htobe64(stream->next_net_seq_num++);
/* Other fields are zeroed previously */
ret = relayd_send_data_hdr(&relayd->data_sock, &data_hdr,
}
error:
- rcu_read_unlock();
return outfd;
}
}
/*
- * Mmap the ring buffer, read it and write the data to the tracefile.
+ * Write the metadata stream id on the specified file descriptor.
+ */
+static int write_relayd_metadata_id(int fd,
+ struct lttng_consumer_stream *stream,
+ struct consumer_relayd_sock_pair *relayd)
+{
+ int ret;
+ uint64_t metadata_id;
+
+ metadata_id = htobe64(stream->relayd_stream_id);
+ do {
+ ret = write(fd, (void *) &metadata_id,
+ sizeof(stream->relayd_stream_id));
+ } while (ret < 0 && errno == EINTR);
+ if (ret < 0) {
+ PERROR("write metadata stream id");
+ goto end;
+ }
+ DBG("Metadata stream id %zu written before data",
+ stream->relayd_stream_id);
+
+end:
+ return ret;
+}
+
+/*
+ * Mmap the ring buffer, read it and write the data to the tracefile. This is a
+ * core function for writing trace buffers to either the local filesystem or
+ * the network.
+ *
+ * Careful review MUST be put if any changes occur!
*
* Returns the number of bytes written
*/
struct lttng_consumer_local_data *ctx,
struct lttng_consumer_stream *stream, unsigned long len)
{
+ unsigned long mmap_offset;
+ ssize_t ret = 0, written = 0;
+ off_t orig_offset = stream->out_fd_offset;
+ /* Default is on the disk */
+ int outfd = stream->out_fd;
+ struct consumer_relayd_sock_pair *relayd = NULL;
+
+ /* RCU lock for the relayd pointer */
+ rcu_read_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);
+ if (relayd == NULL) {
+ goto end;
+ }
+ }
+
+ /* get the offset inside the fd to mmap */
switch (consumer_data.type) {
case LTTNG_CONSUMER_KERNEL:
- return lttng_kconsumer_on_read_subbuffer_mmap(ctx, stream, len);
+ ret = kernctl_get_mmap_read_offset(stream->wait_fd, &mmap_offset);
+ break;
case LTTNG_CONSUMER32_UST:
case LTTNG_CONSUMER64_UST:
- return lttng_ustconsumer_on_read_subbuffer_mmap(ctx, stream, len);
+ ret = lttng_ustctl_get_mmap_read_offset(stream->chan->handle,
+ stream->buf, &mmap_offset);
+ break;
default:
ERR("Unknown consumer_data type");
assert(0);
}
+ if (ret != 0) {
+ errno = -ret;
+ PERROR("tracer ctl get_mmap_read_offset");
+ written = ret;
+ goto end;
+ }
- return 0;
+ /* Handle stream on the relayd if the output is on the network */
+ if (relayd) {
+ unsigned long netlen = len;
+
+ /*
+ * Lock the control socket for the complete duration of the function
+ * since from this point on we will use the socket.
+ */
+ if (stream->metadata_flag) {
+ /* Metadata requires the control socket. */
+ pthread_mutex_lock(&relayd->ctrl_sock_mutex);
+ netlen += sizeof(stream->relayd_stream_id);
+ }
+
+ ret = write_relayd_stream_header(stream, netlen, relayd);
+ if (ret >= 0) {
+ /* Use the returned socket. */
+ outfd = ret;
+
+ /* Write metadata stream id before payload */
+ if (stream->metadata_flag) {
+ ret = write_relayd_metadata_id(outfd, stream, relayd);
+ if (ret < 0) {
+ written = ret;
+ goto end;
+ }
+ }
+ }
+ /* Else, use the default set before which is the filesystem. */
+ }
+
+ while (len > 0) {
+ do {
+ ret = write(outfd, stream->mmap_base + mmap_offset, len);
+ } while (ret < 0 && errno == EINTR);
+ if (ret < 0) {
+ PERROR("Error in file write");
+ if (written == 0) {
+ written = ret;
+ }
+ goto end;
+ } else if (ret > len) {
+ PERROR("Error in file write (ret %ld > len %lu)", ret, len);
+ written += ret;
+ goto end;
+ } else {
+ len -= ret;
+ mmap_offset += ret;
+ }
+ DBG("Consumer mmap write() ret %ld (len %lu)", ret, len);
+
+ /* This call is useless on a socket so better save a syscall. */
+ if (!relayd) {
+ /* This won't block, but will start writeout asynchronously */
+ lttng_sync_file_range(outfd, stream->out_fd_offset, ret,
+ SYNC_FILE_RANGE_WRITE);
+ stream->out_fd_offset += ret;
+ }
+ written += ret;
+ }
+ lttng_consumer_sync_trace_file(stream, orig_offset);
+
+end:
+ /* Unlock only if ctrl socket used */
+ if (relayd && stream->metadata_flag) {
+ pthread_mutex_unlock(&relayd->ctrl_sock_mutex);
+ }
+
+ rcu_read_unlock();
+ return written;
}
/*
struct lttng_consumer_local_data *ctx,
struct lttng_consumer_stream *stream, unsigned long len)
{
+ ssize_t ret = 0, written = 0, ret_splice = 0;
+ loff_t offset = 0;
+ off_t orig_offset = stream->out_fd_offset;
+ int fd = stream->wait_fd;
+ /* Default is on the disk */
+ int outfd = stream->out_fd;
+ struct consumer_relayd_sock_pair *relayd = NULL;
+
switch (consumer_data.type) {
case LTTNG_CONSUMER_KERNEL:
- return lttng_kconsumer_on_read_subbuffer_splice(ctx, stream, len);
+ break;
case LTTNG_CONSUMER32_UST:
case LTTNG_CONSUMER64_UST:
+ /* Not supported for user space tracing */
return -ENOSYS;
default:
ERR("Unknown consumer_data type");
assert(0);
- return -ENOSYS;
}
+ /* RCU lock for the relayd pointer */
+ rcu_read_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);
+ if (relayd == NULL) {
+ goto end;
+ }
+ }
+
+ /* Write metadata stream id before payload */
+ if (stream->metadata_flag && relayd) {
+ /*
+ * Lock the control socket for the complete duration of the function
+ * since from this point on we will use the socket.
+ */
+ pthread_mutex_lock(&relayd->ctrl_sock_mutex);
+
+ ret = write_relayd_metadata_id(ctx->consumer_thread_pipe[1],
+ stream, relayd);
+ if (ret < 0) {
+ written = ret;
+ goto end;
+ }
+ }
+
+ while (len > 0) {
+ DBG("splice chan to pipe offset %lu of len %lu (fd : %d)",
+ (unsigned long)offset, len, fd);
+ ret_splice = splice(fd, &offset, ctx->consumer_thread_pipe[1], NULL, len,
+ SPLICE_F_MOVE | SPLICE_F_MORE);
+ DBG("splice chan to pipe, ret %zd", ret_splice);
+ if (ret_splice < 0) {
+ PERROR("Error in relay splice");
+ if (written == 0) {
+ written = ret_splice;
+ }
+ ret = errno;
+ goto splice_error;
+ }
+
+ /* Handle stream on the relayd if the output is on the network */
+ if (relayd) {
+ if (stream->metadata_flag) {
+ /* Update counter to fit the spliced data */
+ ret_splice += sizeof(stream->relayd_stream_id);
+ len += sizeof(stream->relayd_stream_id);
+ /*
+ * We do this so the return value can match the len passed as
+ * argument to this function.
+ */
+ written -= sizeof(stream->relayd_stream_id);
+ }
+
+ ret = write_relayd_stream_header(stream, ret_splice, relayd);
+ if (ret >= 0) {
+ /* Use the returned socket. */
+ outfd = ret;
+ } else {
+ ERR("Remote relayd disconnected. Stopping");
+ goto end;
+ }
+ }
+
+ /* Splice data out */
+ ret_splice = splice(ctx->consumer_thread_pipe[0], NULL, outfd, NULL,
+ ret_splice, SPLICE_F_MOVE | SPLICE_F_MORE);
+ DBG("Kernel consumer splice pipe to file, ret %zd", ret_splice);
+ if (ret_splice < 0) {
+ PERROR("Error in file splice");
+ if (written == 0) {
+ written = ret_splice;
+ }
+ ret = errno;
+ goto splice_error;
+ } else if (ret_splice > len) {
+ errno = EINVAL;
+ PERROR("Wrote more data than requested %zd (len: %lu)",
+ ret_splice, len);
+ written += ret_splice;
+ ret = errno;
+ goto splice_error;
+ }
+ len -= ret_splice;
+
+ /* This call is useless on a socket so better save a syscall. */
+ if (!relayd) {
+ /* This won't block, but will start writeout asynchronously */
+ lttng_sync_file_range(outfd, stream->out_fd_offset, ret_splice,
+ SYNC_FILE_RANGE_WRITE);
+ stream->out_fd_offset += ret_splice;
+ }
+ written += ret_splice;
+ }
+ lttng_consumer_sync_trace_file(stream, orig_offset);
+
+ ret = ret_splice;
+
+ goto end;
+
+splice_error:
+ /* send the appropriate error description to sessiond */
+ switch (ret) {
+ case EBADF:
+ lttng_consumer_send_error(ctx, CONSUMERD_SPLICE_EBADF);
+ break;
+ case EINVAL:
+ lttng_consumer_send_error(ctx, CONSUMERD_SPLICE_EINVAL);
+ break;
+ case ENOMEM:
+ lttng_consumer_send_error(ctx, CONSUMERD_SPLICE_ENOMEM);
+ break;
+ case ESPIPE:
+ lttng_consumer_send_error(ctx, CONSUMERD_SPLICE_ESPIPE);
+ break;
+ }
+
+end:
+ if (relayd && stream->metadata_flag) {
+ pthread_mutex_unlock(&relayd->ctrl_sock_mutex);
+ }
+
+ rcu_read_unlock();
+ return written;
}
/*