175961e55SVincent Donnefort // SPDX-License-Identifier: GPL-2.0
275961e55SVincent Donnefort /*
375961e55SVincent Donnefort * Ring-buffer memory mapping tests
475961e55SVincent Donnefort *
575961e55SVincent Donnefort * Copyright (c) 2024 Vincent Donnefort <[email protected]>
675961e55SVincent Donnefort */
775961e55SVincent Donnefort #include <fcntl.h>
875961e55SVincent Donnefort #include <sched.h>
975961e55SVincent Donnefort #include <stdbool.h>
1075961e55SVincent Donnefort #include <stdio.h>
1175961e55SVincent Donnefort #include <stdlib.h>
1275961e55SVincent Donnefort #include <unistd.h>
1375961e55SVincent Donnefort
1475961e55SVincent Donnefort #include <linux/trace_mmap.h>
1575961e55SVincent Donnefort
1675961e55SVincent Donnefort #include <sys/mman.h>
1775961e55SVincent Donnefort #include <sys/ioctl.h>
1875961e55SVincent Donnefort
1975961e55SVincent Donnefort #include "../user_events/user_events_selftests.h" /* share tracefs setup */
2075961e55SVincent Donnefort #include "../kselftest_harness.h"
2175961e55SVincent Donnefort
2275961e55SVincent Donnefort #define TRACEFS_ROOT "/sys/kernel/tracing"
2375961e55SVincent Donnefort
__tracefs_write(const char * path,const char * value)2475961e55SVincent Donnefort static int __tracefs_write(const char *path, const char *value)
2575961e55SVincent Donnefort {
2675961e55SVincent Donnefort int fd, ret;
2775961e55SVincent Donnefort
2875961e55SVincent Donnefort fd = open(path, O_WRONLY | O_TRUNC);
2975961e55SVincent Donnefort if (fd < 0)
3075961e55SVincent Donnefort return fd;
3175961e55SVincent Donnefort
3275961e55SVincent Donnefort ret = write(fd, value, strlen(value));
3375961e55SVincent Donnefort
3475961e55SVincent Donnefort close(fd);
3575961e55SVincent Donnefort
3675961e55SVincent Donnefort return ret == -1 ? -errno : 0;
3775961e55SVincent Donnefort }
3875961e55SVincent Donnefort
__tracefs_write_int(const char * path,int value)3975961e55SVincent Donnefort static int __tracefs_write_int(const char *path, int value)
4075961e55SVincent Donnefort {
4175961e55SVincent Donnefort char *str;
4275961e55SVincent Donnefort int ret;
4375961e55SVincent Donnefort
4475961e55SVincent Donnefort if (asprintf(&str, "%d", value) < 0)
4575961e55SVincent Donnefort return -1;
4675961e55SVincent Donnefort
4775961e55SVincent Donnefort ret = __tracefs_write(path, str);
4875961e55SVincent Donnefort
4975961e55SVincent Donnefort free(str);
5075961e55SVincent Donnefort
5175961e55SVincent Donnefort return ret;
5275961e55SVincent Donnefort }
5375961e55SVincent Donnefort
5475961e55SVincent Donnefort #define tracefs_write_int(path, value) \
5575961e55SVincent Donnefort ASSERT_EQ(__tracefs_write_int((path), (value)), 0)
5675961e55SVincent Donnefort
5775961e55SVincent Donnefort #define tracefs_write(path, value) \
5875961e55SVincent Donnefort ASSERT_EQ(__tracefs_write((path), (value)), 0)
5975961e55SVincent Donnefort
tracefs_reset(void)6075961e55SVincent Donnefort static int tracefs_reset(void)
6175961e55SVincent Donnefort {
6275961e55SVincent Donnefort if (__tracefs_write_int(TRACEFS_ROOT"/tracing_on", 0))
6375961e55SVincent Donnefort return -1;
6475961e55SVincent Donnefort if (__tracefs_write(TRACEFS_ROOT"/trace", ""))
6575961e55SVincent Donnefort return -1;
6675961e55SVincent Donnefort if (__tracefs_write(TRACEFS_ROOT"/set_event", ""))
6775961e55SVincent Donnefort return -1;
6875961e55SVincent Donnefort if (__tracefs_write(TRACEFS_ROOT"/current_tracer", "nop"))
6975961e55SVincent Donnefort return -1;
7075961e55SVincent Donnefort
7175961e55SVincent Donnefort return 0;
7275961e55SVincent Donnefort }
7375961e55SVincent Donnefort
7475961e55SVincent Donnefort struct tracefs_cpu_map_desc {
7575961e55SVincent Donnefort struct trace_buffer_meta *meta;
7675961e55SVincent Donnefort int cpu_fd;
7775961e55SVincent Donnefort };
7875961e55SVincent Donnefort
tracefs_cpu_map(struct tracefs_cpu_map_desc * desc,int cpu)7975961e55SVincent Donnefort int tracefs_cpu_map(struct tracefs_cpu_map_desc *desc, int cpu)
8075961e55SVincent Donnefort {
8175961e55SVincent Donnefort int page_size = getpagesize();
8275961e55SVincent Donnefort char *cpu_path;
8375961e55SVincent Donnefort void *map;
8475961e55SVincent Donnefort
8575961e55SVincent Donnefort if (asprintf(&cpu_path,
8675961e55SVincent Donnefort TRACEFS_ROOT"/per_cpu/cpu%d/trace_pipe_raw",
8775961e55SVincent Donnefort cpu) < 0)
8875961e55SVincent Donnefort return -ENOMEM;
8975961e55SVincent Donnefort
9075961e55SVincent Donnefort desc->cpu_fd = open(cpu_path, O_RDONLY | O_NONBLOCK);
9175961e55SVincent Donnefort free(cpu_path);
9275961e55SVincent Donnefort if (desc->cpu_fd < 0)
9375961e55SVincent Donnefort return -ENODEV;
9475961e55SVincent Donnefort
9575d7ff9aSVincent Donnefort again:
9675961e55SVincent Donnefort map = mmap(NULL, page_size, PROT_READ, MAP_SHARED, desc->cpu_fd, 0);
9775961e55SVincent Donnefort if (map == MAP_FAILED)
9875961e55SVincent Donnefort return -errno;
9975961e55SVincent Donnefort
10075961e55SVincent Donnefort desc->meta = (struct trace_buffer_meta *)map;
10175961e55SVincent Donnefort
10275d7ff9aSVincent Donnefort /* the meta-page is bigger than the original mapping */
10375d7ff9aSVincent Donnefort if (page_size < desc->meta->meta_struct_len) {
10475d7ff9aSVincent Donnefort int meta_page_size = desc->meta->meta_page_size;
10575d7ff9aSVincent Donnefort
10675d7ff9aSVincent Donnefort munmap(desc->meta, page_size);
10775d7ff9aSVincent Donnefort page_size = meta_page_size;
10875d7ff9aSVincent Donnefort goto again;
10975d7ff9aSVincent Donnefort }
11075d7ff9aSVincent Donnefort
11175961e55SVincent Donnefort return 0;
11275961e55SVincent Donnefort }
11375961e55SVincent Donnefort
tracefs_cpu_unmap(struct tracefs_cpu_map_desc * desc)11475961e55SVincent Donnefort void tracefs_cpu_unmap(struct tracefs_cpu_map_desc *desc)
11575961e55SVincent Donnefort {
11675961e55SVincent Donnefort munmap(desc->meta, desc->meta->meta_page_size);
11775961e55SVincent Donnefort close(desc->cpu_fd);
11875961e55SVincent Donnefort }
11975961e55SVincent Donnefort
FIXTURE(map)12075961e55SVincent Donnefort FIXTURE(map) {
12175961e55SVincent Donnefort struct tracefs_cpu_map_desc map_desc;
12275961e55SVincent Donnefort bool umount;
12375961e55SVincent Donnefort };
12475961e55SVincent Donnefort
FIXTURE_VARIANT(map)12575961e55SVincent Donnefort FIXTURE_VARIANT(map) {
12675961e55SVincent Donnefort int subbuf_size;
12775961e55SVincent Donnefort };
12875961e55SVincent Donnefort
FIXTURE_VARIANT_ADD(map,subbuf_size_4k)12975961e55SVincent Donnefort FIXTURE_VARIANT_ADD(map, subbuf_size_4k) {
13075961e55SVincent Donnefort .subbuf_size = 4,
13175961e55SVincent Donnefort };
13275961e55SVincent Donnefort
FIXTURE_VARIANT_ADD(map,subbuf_size_8k)13375961e55SVincent Donnefort FIXTURE_VARIANT_ADD(map, subbuf_size_8k) {
13475961e55SVincent Donnefort .subbuf_size = 8,
13575961e55SVincent Donnefort };
13675961e55SVincent Donnefort
FIXTURE_SETUP(map)13775961e55SVincent Donnefort FIXTURE_SETUP(map)
13875961e55SVincent Donnefort {
13975961e55SVincent Donnefort int cpu = sched_getcpu();
14075961e55SVincent Donnefort cpu_set_t cpu_mask;
14175961e55SVincent Donnefort bool fail, umount;
14275961e55SVincent Donnefort char *message;
14375961e55SVincent Donnefort
14475961e55SVincent Donnefort if (getuid() != 0)
14575961e55SVincent Donnefort SKIP(return, "Skipping: %s", "Please run the test as root");
14675961e55SVincent Donnefort
14775961e55SVincent Donnefort if (!tracefs_enabled(&message, &fail, &umount)) {
14875961e55SVincent Donnefort if (fail) {
14975961e55SVincent Donnefort TH_LOG("Tracefs setup failed: %s", message);
15075961e55SVincent Donnefort ASSERT_FALSE(fail);
15175961e55SVincent Donnefort }
15275961e55SVincent Donnefort SKIP(return, "Skipping: %s", message);
15375961e55SVincent Donnefort }
15475961e55SVincent Donnefort
15575961e55SVincent Donnefort self->umount = umount;
15675961e55SVincent Donnefort
15775961e55SVincent Donnefort ASSERT_GE(cpu, 0);
15875961e55SVincent Donnefort
15975961e55SVincent Donnefort ASSERT_EQ(tracefs_reset(), 0);
16075961e55SVincent Donnefort
16175961e55SVincent Donnefort tracefs_write_int(TRACEFS_ROOT"/buffer_subbuf_size_kb", variant->subbuf_size);
16275961e55SVincent Donnefort
16375961e55SVincent Donnefort ASSERT_EQ(tracefs_cpu_map(&self->map_desc, cpu), 0);
16475961e55SVincent Donnefort
16575961e55SVincent Donnefort /*
16675961e55SVincent Donnefort * Ensure generated events will be found on this very same ring-buffer.
16775961e55SVincent Donnefort */
16875961e55SVincent Donnefort CPU_ZERO(&cpu_mask);
16975961e55SVincent Donnefort CPU_SET(cpu, &cpu_mask);
17075961e55SVincent Donnefort ASSERT_EQ(sched_setaffinity(0, sizeof(cpu_mask), &cpu_mask), 0);
17175961e55SVincent Donnefort }
17275961e55SVincent Donnefort
FIXTURE_TEARDOWN(map)17375961e55SVincent Donnefort FIXTURE_TEARDOWN(map)
17475961e55SVincent Donnefort {
17575961e55SVincent Donnefort tracefs_reset();
17675961e55SVincent Donnefort
17775961e55SVincent Donnefort if (self->umount)
17875961e55SVincent Donnefort tracefs_unmount();
17975961e55SVincent Donnefort
18075961e55SVincent Donnefort tracefs_cpu_unmap(&self->map_desc);
18175961e55SVincent Donnefort }
18275961e55SVincent Donnefort
TEST_F(map,meta_page_check)18375961e55SVincent Donnefort TEST_F(map, meta_page_check)
18475961e55SVincent Donnefort {
18575961e55SVincent Donnefort struct tracefs_cpu_map_desc *desc = &self->map_desc;
18675961e55SVincent Donnefort int cnt = 0;
18775961e55SVincent Donnefort
18875961e55SVincent Donnefort ASSERT_EQ(desc->meta->entries, 0);
18975961e55SVincent Donnefort ASSERT_EQ(desc->meta->overrun, 0);
19075961e55SVincent Donnefort ASSERT_EQ(desc->meta->read, 0);
19175961e55SVincent Donnefort
19275961e55SVincent Donnefort ASSERT_EQ(desc->meta->reader.id, 0);
19375961e55SVincent Donnefort ASSERT_EQ(desc->meta->reader.read, 0);
19475961e55SVincent Donnefort
19575961e55SVincent Donnefort ASSERT_EQ(ioctl(desc->cpu_fd, TRACE_MMAP_IOCTL_GET_READER), 0);
19675961e55SVincent Donnefort ASSERT_EQ(desc->meta->reader.id, 0);
19775961e55SVincent Donnefort
19875961e55SVincent Donnefort tracefs_write_int(TRACEFS_ROOT"/tracing_on", 1);
19975961e55SVincent Donnefort for (int i = 0; i < 16; i++)
20075961e55SVincent Donnefort tracefs_write_int(TRACEFS_ROOT"/trace_marker", i);
20175961e55SVincent Donnefort again:
20275961e55SVincent Donnefort ASSERT_EQ(ioctl(desc->cpu_fd, TRACE_MMAP_IOCTL_GET_READER), 0);
20375961e55SVincent Donnefort
20475961e55SVincent Donnefort ASSERT_EQ(desc->meta->entries, 16);
20575961e55SVincent Donnefort ASSERT_EQ(desc->meta->overrun, 0);
20675961e55SVincent Donnefort ASSERT_EQ(desc->meta->read, 16);
20775961e55SVincent Donnefort
20875961e55SVincent Donnefort ASSERT_EQ(desc->meta->reader.id, 1);
20975961e55SVincent Donnefort
21075961e55SVincent Donnefort if (!(cnt++))
21175961e55SVincent Donnefort goto again;
21275961e55SVincent Donnefort }
21375961e55SVincent Donnefort
TEST_F(map,data_mmap)21475961e55SVincent Donnefort TEST_F(map, data_mmap)
21575961e55SVincent Donnefort {
21675961e55SVincent Donnefort struct tracefs_cpu_map_desc *desc = &self->map_desc;
21775961e55SVincent Donnefort unsigned long meta_len, data_len;
21875961e55SVincent Donnefort void *data;
21975961e55SVincent Donnefort
22075961e55SVincent Donnefort meta_len = desc->meta->meta_page_size;
22175961e55SVincent Donnefort data_len = desc->meta->subbuf_size * desc->meta->nr_subbufs;
22275961e55SVincent Donnefort
22375961e55SVincent Donnefort /* Map all the available subbufs */
22475961e55SVincent Donnefort data = mmap(NULL, data_len, PROT_READ, MAP_SHARED,
22575961e55SVincent Donnefort desc->cpu_fd, meta_len);
22675961e55SVincent Donnefort ASSERT_NE(data, MAP_FAILED);
22775961e55SVincent Donnefort munmap(data, data_len);
22875961e55SVincent Donnefort
22975961e55SVincent Donnefort /* Map all the available subbufs - 1 */
23075961e55SVincent Donnefort data_len -= desc->meta->subbuf_size;
23175961e55SVincent Donnefort data = mmap(NULL, data_len, PROT_READ, MAP_SHARED,
23275961e55SVincent Donnefort desc->cpu_fd, meta_len);
23375961e55SVincent Donnefort ASSERT_NE(data, MAP_FAILED);
23475961e55SVincent Donnefort munmap(data, data_len);
23575961e55SVincent Donnefort
236*b6f9cd83SVincent Donnefort /* Offset within ring-buffer bounds, mapping size overflow */
23775961e55SVincent Donnefort meta_len += desc->meta->subbuf_size * 2;
23875961e55SVincent Donnefort data = mmap(NULL, data_len, PROT_READ, MAP_SHARED,
23975961e55SVincent Donnefort desc->cpu_fd, meta_len);
24075961e55SVincent Donnefort ASSERT_EQ(data, MAP_FAILED);
241eb2dcde9SVincent Donnefort
242*b6f9cd83SVincent Donnefort /* Offset outside ring-buffer bounds */
243*b6f9cd83SVincent Donnefort data_len = desc->meta->subbuf_size * desc->meta->nr_subbufs;
244*b6f9cd83SVincent Donnefort data = mmap(NULL, data_len, PROT_READ, MAP_SHARED,
245*b6f9cd83SVincent Donnefort desc->cpu_fd, data_len + (desc->meta->subbuf_size * 2));
246*b6f9cd83SVincent Donnefort ASSERT_EQ(data, MAP_FAILED);
247*b6f9cd83SVincent Donnefort
248eb2dcde9SVincent Donnefort /* Verify meta-page padding */
249eb2dcde9SVincent Donnefort if (desc->meta->meta_page_size > getpagesize()) {
250eb2dcde9SVincent Donnefort data_len = desc->meta->meta_page_size;
251eb2dcde9SVincent Donnefort data = mmap(NULL, data_len,
252eb2dcde9SVincent Donnefort PROT_READ, MAP_SHARED, desc->cpu_fd, 0);
253eb2dcde9SVincent Donnefort ASSERT_NE(data, MAP_FAILED);
254eb2dcde9SVincent Donnefort
25521ff365bSVincent Donnefort for (int i = desc->meta->meta_struct_len;
25621ff365bSVincent Donnefort i < desc->meta->meta_page_size; i += sizeof(int))
25721ff365bSVincent Donnefort ASSERT_EQ(*(int *)(data + i), 0);
25821ff365bSVincent Donnefort
259eb2dcde9SVincent Donnefort munmap(data, data_len);
260eb2dcde9SVincent Donnefort }
26175961e55SVincent Donnefort }
26275961e55SVincent Donnefort
FIXTURE(snapshot)26375961e55SVincent Donnefort FIXTURE(snapshot) {
26475961e55SVincent Donnefort bool umount;
26575961e55SVincent Donnefort };
26675961e55SVincent Donnefort
FIXTURE_SETUP(snapshot)26775961e55SVincent Donnefort FIXTURE_SETUP(snapshot)
26875961e55SVincent Donnefort {
26975961e55SVincent Donnefort bool fail, umount;
27075961e55SVincent Donnefort struct stat sb;
27175961e55SVincent Donnefort char *message;
27275961e55SVincent Donnefort
27375961e55SVincent Donnefort if (getuid() != 0)
27475961e55SVincent Donnefort SKIP(return, "Skipping: %s", "Please run the test as root");
27575961e55SVincent Donnefort
27675961e55SVincent Donnefort if (stat(TRACEFS_ROOT"/snapshot", &sb))
27775961e55SVincent Donnefort SKIP(return, "Skipping: %s", "snapshot not available");
27875961e55SVincent Donnefort
27975961e55SVincent Donnefort if (!tracefs_enabled(&message, &fail, &umount)) {
28075961e55SVincent Donnefort if (fail) {
28175961e55SVincent Donnefort TH_LOG("Tracefs setup failed: %s", message);
28275961e55SVincent Donnefort ASSERT_FALSE(fail);
28375961e55SVincent Donnefort }
28475961e55SVincent Donnefort SKIP(return, "Skipping: %s", message);
28575961e55SVincent Donnefort }
28675961e55SVincent Donnefort
28775961e55SVincent Donnefort self->umount = umount;
28875961e55SVincent Donnefort }
28975961e55SVincent Donnefort
FIXTURE_TEARDOWN(snapshot)29075961e55SVincent Donnefort FIXTURE_TEARDOWN(snapshot)
29175961e55SVincent Donnefort {
29275961e55SVincent Donnefort __tracefs_write(TRACEFS_ROOT"/events/sched/sched_switch/trigger",
29375961e55SVincent Donnefort "!snapshot");
29475961e55SVincent Donnefort tracefs_reset();
29575961e55SVincent Donnefort
29675961e55SVincent Donnefort if (self->umount)
29775961e55SVincent Donnefort tracefs_unmount();
29875961e55SVincent Donnefort }
29975961e55SVincent Donnefort
TEST_F(snapshot,excludes_map)30075961e55SVincent Donnefort TEST_F(snapshot, excludes_map)
30175961e55SVincent Donnefort {
30275961e55SVincent Donnefort struct tracefs_cpu_map_desc map_desc;
30375961e55SVincent Donnefort int cpu = sched_getcpu();
30475961e55SVincent Donnefort
30575961e55SVincent Donnefort ASSERT_GE(cpu, 0);
30675961e55SVincent Donnefort tracefs_write(TRACEFS_ROOT"/events/sched/sched_switch/trigger",
30775961e55SVincent Donnefort "snapshot");
30875961e55SVincent Donnefort ASSERT_EQ(tracefs_cpu_map(&map_desc, cpu), -EBUSY);
30975961e55SVincent Donnefort }
31075961e55SVincent Donnefort
TEST_F(snapshot,excluded_by_map)31175961e55SVincent Donnefort TEST_F(snapshot, excluded_by_map)
31275961e55SVincent Donnefort {
31375961e55SVincent Donnefort struct tracefs_cpu_map_desc map_desc;
31475961e55SVincent Donnefort int cpu = sched_getcpu();
31575961e55SVincent Donnefort
31675961e55SVincent Donnefort ASSERT_EQ(tracefs_cpu_map(&map_desc, cpu), 0);
31775961e55SVincent Donnefort
31875961e55SVincent Donnefort ASSERT_EQ(__tracefs_write(TRACEFS_ROOT"/events/sched/sched_switch/trigger",
31975961e55SVincent Donnefort "snapshot"), -EBUSY);
32075961e55SVincent Donnefort ASSERT_EQ(__tracefs_write(TRACEFS_ROOT"/snapshot",
32175961e55SVincent Donnefort "1"), -EBUSY);
32275961e55SVincent Donnefort }
32375961e55SVincent Donnefort
32475961e55SVincent Donnefort TEST_HARNESS_MAIN
325