319 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Ring-buffer memory mapping tests4 *5 * Copyright (c) 2024 Vincent Donnefort <vdonnefort@google.com>6 */7#include <fcntl.h>8#include <sched.h>9#include <stdbool.h>10#include <stdio.h>11#include <stdlib.h>12#include <unistd.h>13 14#include <linux/trace_mmap.h>15 16#include <sys/mman.h>17#include <sys/ioctl.h>18 19#include "../user_events/user_events_selftests.h" /* share tracefs setup */20#include "../kselftest_harness.h"21 22#define TRACEFS_ROOT "/sys/kernel/tracing"23 24static int __tracefs_write(const char *path, const char *value)25{26 int fd, ret;27 28 fd = open(path, O_WRONLY | O_TRUNC);29 if (fd < 0)30 return fd;31 32 ret = write(fd, value, strlen(value));33 34 close(fd);35 36 return ret == -1 ? -errno : 0;37}38 39static int __tracefs_write_int(const char *path, int value)40{41 char *str;42 int ret;43 44 if (asprintf(&str, "%d", value) < 0)45 return -1;46 47 ret = __tracefs_write(path, str);48 49 free(str);50 51 return ret;52}53 54#define tracefs_write_int(path, value) \55 ASSERT_EQ(__tracefs_write_int((path), (value)), 0)56 57#define tracefs_write(path, value) \58 ASSERT_EQ(__tracefs_write((path), (value)), 0)59 60static int tracefs_reset(void)61{62 if (__tracefs_write_int(TRACEFS_ROOT"/tracing_on", 0))63 return -1;64 if (__tracefs_write(TRACEFS_ROOT"/trace", ""))65 return -1;66 if (__tracefs_write(TRACEFS_ROOT"/set_event", ""))67 return -1;68 if (__tracefs_write(TRACEFS_ROOT"/current_tracer", "nop"))69 return -1;70 71 return 0;72}73 74struct tracefs_cpu_map_desc {75 struct trace_buffer_meta *meta;76 int cpu_fd;77};78 79int tracefs_cpu_map(struct tracefs_cpu_map_desc *desc, int cpu)80{81 int page_size = getpagesize();82 char *cpu_path;83 void *map;84 85 if (asprintf(&cpu_path,86 TRACEFS_ROOT"/per_cpu/cpu%d/trace_pipe_raw",87 cpu) < 0)88 return -ENOMEM;89 90 desc->cpu_fd = open(cpu_path, O_RDONLY | O_NONBLOCK);91 free(cpu_path);92 if (desc->cpu_fd < 0)93 return -ENODEV;94 95again:96 map = mmap(NULL, page_size, PROT_READ, MAP_SHARED, desc->cpu_fd, 0);97 if (map == MAP_FAILED)98 return -errno;99 100 desc->meta = (struct trace_buffer_meta *)map;101 102 /* the meta-page is bigger than the original mapping */103 if (page_size < desc->meta->meta_struct_len) {104 int meta_page_size = desc->meta->meta_page_size;105 106 munmap(desc->meta, page_size);107 page_size = meta_page_size;108 goto again;109 }110 111 return 0;112}113 114void tracefs_cpu_unmap(struct tracefs_cpu_map_desc *desc)115{116 munmap(desc->meta, desc->meta->meta_page_size);117 close(desc->cpu_fd);118}119 120FIXTURE(map) {121 struct tracefs_cpu_map_desc map_desc;122 bool umount;123};124 125FIXTURE_VARIANT(map) {126 int subbuf_size;127};128 129FIXTURE_VARIANT_ADD(map, subbuf_size_4k) {130 .subbuf_size = 4,131};132 133FIXTURE_VARIANT_ADD(map, subbuf_size_8k) {134 .subbuf_size = 8,135};136 137FIXTURE_SETUP(map)138{139 int cpu = sched_getcpu();140 cpu_set_t cpu_mask;141 bool fail, umount;142 char *message;143 144 if (getuid() != 0)145 SKIP(return, "Skipping: %s", "Please run the test as root");146 147 if (!tracefs_enabled(&message, &fail, &umount)) {148 if (fail) {149 TH_LOG("Tracefs setup failed: %s", message);150 ASSERT_FALSE(fail);151 }152 SKIP(return, "Skipping: %s", message);153 }154 155 self->umount = umount;156 157 ASSERT_GE(cpu, 0);158 159 ASSERT_EQ(tracefs_reset(), 0);160 161 tracefs_write_int(TRACEFS_ROOT"/buffer_subbuf_size_kb", variant->subbuf_size);162 163 ASSERT_EQ(tracefs_cpu_map(&self->map_desc, cpu), 0);164 165 /*166 * Ensure generated events will be found on this very same ring-buffer.167 */168 CPU_ZERO(&cpu_mask);169 CPU_SET(cpu, &cpu_mask);170 ASSERT_EQ(sched_setaffinity(0, sizeof(cpu_mask), &cpu_mask), 0);171}172 173FIXTURE_TEARDOWN(map)174{175 tracefs_reset();176 177 if (self->umount)178 tracefs_unmount();179 180 tracefs_cpu_unmap(&self->map_desc);181}182 183TEST_F(map, meta_page_check)184{185 struct tracefs_cpu_map_desc *desc = &self->map_desc;186 int cnt = 0;187 188 ASSERT_EQ(desc->meta->entries, 0);189 ASSERT_EQ(desc->meta->overrun, 0);190 ASSERT_EQ(desc->meta->read, 0);191 192 ASSERT_EQ(desc->meta->reader.id, 0);193 ASSERT_EQ(desc->meta->reader.read, 0);194 195 ASSERT_EQ(ioctl(desc->cpu_fd, TRACE_MMAP_IOCTL_GET_READER), 0);196 ASSERT_EQ(desc->meta->reader.id, 0);197 198 tracefs_write_int(TRACEFS_ROOT"/tracing_on", 1);199 for (int i = 0; i < 16; i++)200 tracefs_write_int(TRACEFS_ROOT"/trace_marker", i);201again:202 ASSERT_EQ(ioctl(desc->cpu_fd, TRACE_MMAP_IOCTL_GET_READER), 0);203 204 ASSERT_EQ(desc->meta->entries, 16);205 ASSERT_EQ(desc->meta->overrun, 0);206 ASSERT_EQ(desc->meta->read, 16);207 208 ASSERT_EQ(desc->meta->reader.id, 1);209 210 if (!(cnt++))211 goto again;212}213 214TEST_F(map, data_mmap)215{216 struct tracefs_cpu_map_desc *desc = &self->map_desc;217 unsigned long meta_len, data_len;218 void *data;219 220 meta_len = desc->meta->meta_page_size;221 data_len = desc->meta->subbuf_size * desc->meta->nr_subbufs;222 223 /* Map all the available subbufs */224 data = mmap(NULL, data_len, PROT_READ, MAP_SHARED,225 desc->cpu_fd, meta_len);226 ASSERT_NE(data, MAP_FAILED);227 munmap(data, data_len);228 229 /* Map all the available subbufs - 1 */230 data_len -= desc->meta->subbuf_size;231 data = mmap(NULL, data_len, PROT_READ, MAP_SHARED,232 desc->cpu_fd, meta_len);233 ASSERT_NE(data, MAP_FAILED);234 munmap(data, data_len);235 236 /* Overflow the available subbufs by 1 */237 meta_len += desc->meta->subbuf_size * 2;238 data = mmap(NULL, data_len, PROT_READ, MAP_SHARED,239 desc->cpu_fd, meta_len);240 ASSERT_EQ(data, MAP_FAILED);241 242 /* Verify meta-page padding */243 if (desc->meta->meta_page_size > getpagesize()) {244 data_len = desc->meta->meta_page_size;245 data = mmap(NULL, data_len,246 PROT_READ, MAP_SHARED, desc->cpu_fd, 0);247 ASSERT_NE(data, MAP_FAILED);248 249 for (int i = desc->meta->meta_struct_len;250 i < desc->meta->meta_page_size; i += sizeof(int))251 ASSERT_EQ(*(int *)(data + i), 0);252 253 munmap(data, data_len);254 }255}256 257FIXTURE(snapshot) {258 bool umount;259};260 261FIXTURE_SETUP(snapshot)262{263 bool fail, umount;264 struct stat sb;265 char *message;266 267 if (getuid() != 0)268 SKIP(return, "Skipping: %s", "Please run the test as root");269 270 if (stat(TRACEFS_ROOT"/snapshot", &sb))271 SKIP(return, "Skipping: %s", "snapshot not available");272 273 if (!tracefs_enabled(&message, &fail, &umount)) {274 if (fail) {275 TH_LOG("Tracefs setup failed: %s", message);276 ASSERT_FALSE(fail);277 }278 SKIP(return, "Skipping: %s", message);279 }280 281 self->umount = umount;282}283 284FIXTURE_TEARDOWN(snapshot)285{286 __tracefs_write(TRACEFS_ROOT"/events/sched/sched_switch/trigger",287 "!snapshot");288 tracefs_reset();289 290 if (self->umount)291 tracefs_unmount();292}293 294TEST_F(snapshot, excludes_map)295{296 struct tracefs_cpu_map_desc map_desc;297 int cpu = sched_getcpu();298 299 ASSERT_GE(cpu, 0);300 tracefs_write(TRACEFS_ROOT"/events/sched/sched_switch/trigger",301 "snapshot");302 ASSERT_EQ(tracefs_cpu_map(&map_desc, cpu), -EBUSY);303}304 305TEST_F(snapshot, excluded_by_map)306{307 struct tracefs_cpu_map_desc map_desc;308 int cpu = sched_getcpu();309 310 ASSERT_EQ(tracefs_cpu_map(&map_desc, cpu), 0);311 312 ASSERT_EQ(__tracefs_write(TRACEFS_ROOT"/events/sched/sched_switch/trigger",313 "snapshot"), -EBUSY);314 ASSERT_EQ(__tracefs_write(TRACEFS_ROOT"/snapshot",315 "1"), -EBUSY);316}317 318TEST_HARNESS_MAIN319