| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- // SPDX-License-Identifier: GPL-2.0-only
- #define _GNU_SOURCE
- #include <dirent.h>
- #include <sched.h>
- #include <stdbool.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <sys/ioctl.h>
- #include <sys/mman.h>
- #include <sys/syscall.h>
- #include <sys/types.h>
- #include <linux/perf_event.h>
- #include "kselftest_harness.h"
- #define RB_SIZE 0x3000
- #define AUX_SIZE 0x10000
- #define AUX_OFFS 0x4000
- #define HOLE_SIZE 0x1000
- /* Reserve space for rb, aux with space for shrink-beyond-vma testing. */
- #define REGION_SIZE (2 * RB_SIZE + 2 * AUX_SIZE)
- #define REGION_AUX_OFFS (2 * RB_SIZE)
- #define MAP_BASE 1
- #define MAP_AUX 2
- #define EVENT_SRC_DIR "/sys/bus/event_source/devices"
- FIXTURE(perf_mmap)
- {
- int fd;
- void *ptr;
- void *region;
- };
- FIXTURE_VARIANT(perf_mmap)
- {
- bool aux;
- unsigned long ptr_size;
- };
- FIXTURE_VARIANT_ADD(perf_mmap, rb)
- {
- .aux = false,
- .ptr_size = RB_SIZE,
- };
- FIXTURE_VARIANT_ADD(perf_mmap, aux)
- {
- .aux = true,
- .ptr_size = AUX_SIZE,
- };
- static bool read_event_type(struct dirent *dent, __u32 *type)
- {
- char typefn[512];
- FILE *fp;
- int res;
- snprintf(typefn, sizeof(typefn), "%s/%s/type", EVENT_SRC_DIR, dent->d_name);
- fp = fopen(typefn, "r");
- if (!fp)
- return false;
- res = fscanf(fp, "%u", type);
- fclose(fp);
- return res > 0;
- }
- FIXTURE_SETUP(perf_mmap)
- {
- struct perf_event_attr attr = {
- .size = sizeof(attr),
- .disabled = 1,
- .exclude_kernel = 1,
- .exclude_hv = 1,
- };
- struct perf_event_attr attr_ok = {};
- unsigned int eacces = 0, map = 0;
- struct perf_event_mmap_page *rb;
- struct dirent *dent;
- void *aux, *region;
- DIR *dir;
- self->ptr = NULL;
- dir = opendir(EVENT_SRC_DIR);
- if (!dir)
- SKIP(return, "perf not available.");
- region = mmap(NULL, REGION_SIZE, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
- ASSERT_NE(region, MAP_FAILED);
- self->region = region;
- // Try to find a suitable event on this system
- while ((dent = readdir(dir))) {
- int fd;
- if (!read_event_type(dent, &attr.type))
- continue;
- fd = syscall(SYS_perf_event_open, &attr, 0, -1, -1, 0);
- if (fd < 0) {
- if (errno == EACCES)
- eacces++;
- continue;
- }
- // Check whether the event supports mmap()
- rb = mmap(region, RB_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0);
- if (rb == MAP_FAILED) {
- close(fd);
- continue;
- }
- if (!map) {
- // Save the event in case that no AUX capable event is found
- attr_ok = attr;
- map = MAP_BASE;
- }
- if (!variant->aux)
- continue;
- rb->aux_offset = AUX_OFFS;
- rb->aux_size = AUX_SIZE;
- // Check whether it supports a AUX buffer
- aux = mmap(region + REGION_AUX_OFFS, AUX_SIZE, PROT_READ | PROT_WRITE,
- MAP_SHARED | MAP_FIXED, fd, AUX_OFFS);
- if (aux == MAP_FAILED) {
- munmap(rb, RB_SIZE);
- close(fd);
- continue;
- }
- attr_ok = attr;
- map = MAP_AUX;
- munmap(aux, AUX_SIZE);
- munmap(rb, RB_SIZE);
- close(fd);
- break;
- }
- closedir(dir);
- if (!map) {
- if (!eacces)
- SKIP(return, "No mappable perf event found.");
- else
- SKIP(return, "No permissions for perf_event_open()");
- }
- self->fd = syscall(SYS_perf_event_open, &attr_ok, 0, -1, -1, 0);
- ASSERT_NE(self->fd, -1);
- rb = mmap(region, RB_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, self->fd, 0);
- ASSERT_NE(rb, MAP_FAILED);
- if (!variant->aux) {
- self->ptr = rb;
- return;
- }
- if (map != MAP_AUX)
- SKIP(return, "No AUX event found.");
- rb->aux_offset = AUX_OFFS;
- rb->aux_size = AUX_SIZE;
- aux = mmap(region + REGION_AUX_OFFS, AUX_SIZE, PROT_READ | PROT_WRITE,
- MAP_SHARED | MAP_FIXED, self->fd, AUX_OFFS);
- ASSERT_NE(aux, MAP_FAILED);
- self->ptr = aux;
- }
- FIXTURE_TEARDOWN(perf_mmap)
- {
- ASSERT_EQ(munmap(self->region, REGION_SIZE), 0);
- if (self->fd != -1)
- ASSERT_EQ(close(self->fd), 0);
- }
- TEST_F(perf_mmap, remap)
- {
- void *tmp, *ptr = self->ptr;
- unsigned long size = variant->ptr_size;
- // Test the invalid remaps
- ASSERT_EQ(mremap(ptr, size, HOLE_SIZE, MREMAP_MAYMOVE), MAP_FAILED);
- ASSERT_EQ(mremap(ptr + HOLE_SIZE, size, HOLE_SIZE, MREMAP_MAYMOVE), MAP_FAILED);
- ASSERT_EQ(mremap(ptr + size - HOLE_SIZE, HOLE_SIZE, size, MREMAP_MAYMOVE), MAP_FAILED);
- // Shrink the end of the mapping such that we only unmap past end of the VMA,
- // which should succeed and poke a hole into the PROT_NONE region
- ASSERT_NE(mremap(ptr + size - HOLE_SIZE, size, HOLE_SIZE, MREMAP_MAYMOVE), MAP_FAILED);
- // Remap the whole buffer to a new address
- tmp = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
- ASSERT_NE(tmp, MAP_FAILED);
- // Try splitting offset 1 hole size into VMA, this should fail
- ASSERT_EQ(mremap(ptr + HOLE_SIZE, size - HOLE_SIZE, size - HOLE_SIZE,
- MREMAP_MAYMOVE | MREMAP_FIXED, tmp), MAP_FAILED);
- // Remapping the whole thing should succeed fine
- ptr = mremap(ptr, size, size, MREMAP_MAYMOVE | MREMAP_FIXED, tmp);
- ASSERT_EQ(ptr, tmp);
- ASSERT_EQ(munmap(tmp, size), 0);
- }
- TEST_F(perf_mmap, unmap)
- {
- unsigned long size = variant->ptr_size;
- // Try to poke holes into the mappings
- ASSERT_NE(munmap(self->ptr, HOLE_SIZE), 0);
- ASSERT_NE(munmap(self->ptr + HOLE_SIZE, HOLE_SIZE), 0);
- ASSERT_NE(munmap(self->ptr + size - HOLE_SIZE, HOLE_SIZE), 0);
- }
- TEST_F(perf_mmap, map)
- {
- unsigned long size = variant->ptr_size;
- // Try to poke holes into the mappings by mapping anonymous memory over it
- ASSERT_EQ(mmap(self->ptr, HOLE_SIZE, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0), MAP_FAILED);
- ASSERT_EQ(mmap(self->ptr + HOLE_SIZE, HOLE_SIZE, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0), MAP_FAILED);
- ASSERT_EQ(mmap(self->ptr + size - HOLE_SIZE, HOLE_SIZE, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0), MAP_FAILED);
- }
- TEST_HARNESS_MAIN
|