| .. SPDX-License-Identifier: GPL-2.0 |
| |
| ================================== |
| Tracefs ring-buffer memory mapping |
| ================================== |
| |
| :Author: Vincent Donnefort <vdonnefort@google.com> |
| |
| Overview |
| ======== |
| Tracefs ring-buffer memory map provides an efficient method to stream data |
| as no memory copy is necessary. The application mapping the ring-buffer becomes |
| then a consumer for that ring-buffer, in a similar fashion to trace_pipe. |
| |
| Memory mapping setup |
| ==================== |
| The mapping works with a mmap() of the trace_pipe_raw interface. |
| |
| The first system page of the mapping contains ring-buffer statistics and |
| description. It is referred to as the meta-page. One of the most important |
| fields of the meta-page is the reader. It contains the sub-buffer ID which can |
| be safely read by the mapper (see ring-buffer-design.rst). |
| |
| The meta-page is followed by all the sub-buffers, ordered by ascending ID. It is |
| therefore effortless to know where the reader starts in the mapping: |
| |
| .. code-block:: c |
| |
| reader_id = meta->reader->id; |
| reader_offset = meta->meta_page_size + reader_id * meta->subbuf_size; |
| |
| When the application is done with the current reader, it can get a new one using |
| the trace_pipe_raw ioctl() TRACE_MMAP_IOCTL_GET_READER. This ioctl also updates |
| the meta-page fields. |
| |
| Limitations |
| =========== |
| When a mapping is in place on a Tracefs ring-buffer, it is not possible to |
| either resize it (either by increasing the entire size of the ring-buffer or |
| each subbuf). It is also not possible to use snapshot and causes splice to copy |
| the ring buffer data instead of using the copyless swap from the ring buffer. |
| |
| Concurrent readers (either another application mapping that ring-buffer or the |
| kernel with trace_pipe) are allowed but not recommended. They will compete for |
| the ring-buffer and the output is unpredictable, just like concurrent readers on |
| trace_pipe would be. |
| |
| Example |
| ======= |
| |
| .. code-block:: c |
| |
| #include <fcntl.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| |
| #include <linux/trace_mmap.h> |
| |
| #include <sys/mman.h> |
| #include <sys/ioctl.h> |
| |
| #define TRACE_PIPE_RAW "/sys/kernel/tracing/per_cpu/cpu0/trace_pipe_raw" |
| |
| int main(void) |
| { |
| int page_size = getpagesize(), fd, reader_id; |
| unsigned long meta_len, data_len; |
| struct trace_buffer_meta *meta; |
| void *map, *reader, *data; |
| |
| fd = open(TRACE_PIPE_RAW, O_RDONLY | O_NONBLOCK); |
| if (fd < 0) |
| exit(EXIT_FAILURE); |
| |
| map = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0); |
| if (map == MAP_FAILED) |
| exit(EXIT_FAILURE); |
| |
| meta = (struct trace_buffer_meta *)map; |
| meta_len = meta->meta_page_size; |
| |
| printf("entries: %llu\n", meta->entries); |
| printf("overrun: %llu\n", meta->overrun); |
| printf("read: %llu\n", meta->read); |
| printf("nr_subbufs: %u\n", meta->nr_subbufs); |
| |
| data_len = meta->subbuf_size * meta->nr_subbufs; |
| data = mmap(NULL, data_len, PROT_READ, MAP_SHARED, fd, meta_len); |
| if (data == MAP_FAILED) |
| exit(EXIT_FAILURE); |
| |
| if (ioctl(fd, TRACE_MMAP_IOCTL_GET_READER) < 0) |
| exit(EXIT_FAILURE); |
| |
| reader_id = meta->reader.id; |
| reader = data + meta->subbuf_size * reader_id; |
| |
| printf("Current reader address: %p\n", reader); |
| |
| munmap(data, data_len); |
| munmap(meta, meta_len); |
| close (fd); |
| |
| return 0; |
| } |