blob: 44d1597f1ba52a3d1da818745faa28829844b043 [file] [log] [blame]
// SPDX-License-Identifier: LGPL-2.1
/*
* Copyright (C) 2023 Google Inc, Steven Rostedt <rostedt@goodmis.org>
*/
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <asm/types.h>
#include "tracefs-local.h"
/**
* struct trace_buffer_meta - Ring-buffer Meta-page description
* @meta_page_size: Size of this meta-page.
* @meta_struct_len: Size of this structure.
* @subbuf_size: Size of each sub-buffer.
* @nr_subbufs: Number of subbfs in the ring-buffer, including the reader.
* @reader.lost_events: Number of events lost at the time of the reader swap.
* @reader.id: subbuf ID of the current reader. ID range [0 : @nr_subbufs - 1]
* @reader.read: Number of bytes read on the reader subbuf.
* @flags: Placeholder for now, 0 until new features are supported.
* @entries: Number of entries in the ring-buffer.
* @overrun: Number of entries lost in the ring-buffer.
* @read: Number of entries that have been read.
* @Reserved1: Internal use only.
* @Reserved2: Internal use only.
*/
struct trace_buffer_meta {
__u32 meta_page_size;
__u32 meta_struct_len;
__u32 subbuf_size;
__u32 nr_subbufs;
struct {
__u64 lost_events;
__u32 id;
__u32 read;
} reader;
__u64 flags;
__u64 entries;
__u64 overrun;
__u64 read;
__u64 Reserved1;
__u64 Reserved2;
};
#define TRACE_MMAP_IOCTL_GET_READER _IO('R', 0x20)
struct trace_mmap {
struct trace_buffer_meta *map;
struct kbuffer *kbuf;
void *data;
int *data_pages;
int fd;
int last_idx;
int last_read;
int meta_len;
int data_len;
};
/**
* trace_mmap - try to mmap the ring buffer
* @fd: The file descriptor to the trace_pipe_raw file
* @kbuf: The kbuffer to load the subbuffer to
*
* Will try to mmap the ring buffer if it is supported, and
* if not, will return NULL, otherwise it returns a descriptor
* to handle the mapping.
*/
__hidden void *trace_mmap(int fd, struct kbuffer *kbuf)
{
struct trace_mmap *tmap;
int page_size;
void *meta;
void *data;
page_size = getpagesize();
meta = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0);
if (meta == MAP_FAILED)
return NULL;
tmap = calloc(1, sizeof(*tmap));
if (!tmap) {
munmap(meta, page_size);
return NULL;
}
tmap->kbuf = kbuffer_dup(kbuf);
if (!tmap->kbuf) {
munmap(meta, page_size);
free(tmap);
}
kbuf = tmap->kbuf;
tmap->fd = fd;
tmap->map = meta;
tmap->meta_len = tmap->map->meta_page_size;
if (tmap->meta_len > page_size) {
munmap(meta, page_size);
meta = mmap(NULL, tmap->meta_len, PROT_READ, MAP_SHARED, fd, 0);
if (meta == MAP_FAILED) {
kbuffer_free(kbuf);
free(tmap);
return NULL;
}
tmap->map = meta;
}
tmap->data_pages = meta + tmap->meta_len;
tmap->data_len = tmap->map->subbuf_size * tmap->map->nr_subbufs;
tmap->data = mmap(NULL, tmap->data_len, PROT_READ, MAP_SHARED,
fd, tmap->meta_len);
if (tmap->data == MAP_FAILED) {
munmap(meta, tmap->meta_len);
kbuffer_free(kbuf);
free(tmap);
return NULL;
}
tmap->last_idx = tmap->map->reader.id;
data = tmap->data + tmap->map->subbuf_size * tmap->last_idx;
kbuffer_load_subbuffer(kbuf, data);
/*
* The page could have left over data on it that was already
* consumed. Move the "read" forward in that case.
*/
if (tmap->map->reader.read) {
int size = kbuffer_start_of_data(kbuf) + tmap->map->reader.read;
char tmpbuf[size];
kbuffer_read_buffer(kbuf, tmpbuf, size);
}
return tmap;
}
__hidden void trace_unmap(void *mapping)
{
struct trace_mmap *tmap = mapping;
if (!tmap)
return;
munmap(tmap->data, tmap->data_len);
munmap(tmap->map, tmap->meta_len);
kbuffer_free(tmap->kbuf);
free(tmap);
}
static int get_reader(struct trace_mmap *tmap)
{
return ioctl(tmap->fd, TRACE_MMAP_IOCTL_GET_READER);
}
__hidden int trace_mmap_load_subbuf(void *mapping, struct kbuffer *kbuf)
{
struct trace_mmap *tmap = mapping;
void *data;
int id;
if (!tmap)
return -1;
id = tmap->map->reader.id;
data = tmap->data + tmap->map->subbuf_size * id;
/*
* If kbuf doesn't point to the current sub-buffer
* just load it and return.
*/
if (data != kbuffer_subbuffer(kbuf)) {
kbuffer_load_subbuffer(kbuf, data);
/* Move the read pointer forward if need be */
if (kbuffer_curr_index(tmap->kbuf)) {
int size = kbuffer_curr_offset(tmap->kbuf);
char tmpbuf[size];
kbuffer_read_buffer(kbuf, tmpbuf, size);
}
return 1;
}
/*
* Perhaps the reader page had a write that added
* more data.
*/
kbuffer_refresh(kbuf);
/* Are there still events to read? */
if (kbuffer_curr_size(kbuf)) {
/* If current is greater than what was read, refresh */
if (kbuffer_curr_offset(kbuf) + kbuffer_curr_size(kbuf) >
tmap->map->reader.read) {
if (get_reader(tmap) < 0)
return -1;
}
return 1;
}
/* See if a new page is ready? */
if (get_reader(tmap) < 0)
return -1;
id = tmap->map->reader.id;
data = tmap->data + tmap->map->subbuf_size * id;
/*
* If the sub-buffer hasn't changed, then there's no more
* events to read.
*/
if (data == kbuffer_subbuffer(kbuf))
return 0;
kbuffer_load_subbuffer(kbuf, data);
return 1;
}
__hidden int trace_mmap_read(void *mapping, void *buffer)
{
struct trace_mmap *tmap = mapping;
struct kbuffer *kbuf;
int ret;
if (!tmap)
return -1;
kbuf = tmap->kbuf;
ret = trace_mmap_load_subbuf(mapping, kbuf);
/* Return for error or no more events */
if (ret <= 0)
return ret;
/* Update the buffer */
ret = kbuffer_read_buffer(kbuf, buffer, tmap->map->subbuf_size);
if (ret <= 0)
return ret;
/* This needs to include the size of the meta data too */
return ret + kbuffer_start_of_data(kbuf);
}