File Index
Files and directories
Sign in
FILE

mmap.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/perf_events/mmap.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2#define _GNU_SOURCE
  3
  4#include <dirent.h>
  5#include <sched.h>
  6#include <stdbool.h>
  7#include <stdio.h>
  8#include <unistd.h>
  9
 10#include <sys/ioctl.h>
 11#include <sys/mman.h>
 12#include <sys/syscall.h>
 13#include <sys/types.h>
 14
 15#include <linux/perf_event.h>
 16
 17#include "kselftest_harness.h"
 18
 19#define RB_SIZE		0x3000
 20#define AUX_SIZE	0x10000
 21#define AUX_OFFS	0x4000
 22
 23#define HOLE_SIZE	0x1000
 24
 25/* Reserve space for rb, aux with space for shrink-beyond-vma testing. */
 26#define REGION_SIZE	(2 * RB_SIZE + 2 * AUX_SIZE)
 27#define REGION_AUX_OFFS (2 * RB_SIZE)
 28
 29#define MAP_BASE	1
 30#define MAP_AUX		2
 31
 32#define EVENT_SRC_DIR	"/sys/bus/event_source/devices"
 33
 34FIXTURE(perf_mmap)
 35{
 36	int		fd;
 37	void		*ptr;
 38	void		*region;
 39};
 40
 41FIXTURE_VARIANT(perf_mmap)
 42{
 43	bool		aux;
 44	unsigned long	ptr_size;
 45};
 46
 47FIXTURE_VARIANT_ADD(perf_mmap, rb)
 48{
 49	.aux = false,
 50	.ptr_size = RB_SIZE,
 51};
 52
 53FIXTURE_VARIANT_ADD(perf_mmap, aux)
 54{
 55	.aux = true,
 56	.ptr_size = AUX_SIZE,
 57};
 58
 59static bool read_event_type(struct dirent *dent, __u32 *type)
 60{
 61	char typefn[512];
 62	FILE *fp;
 63	int res;
 64
 65	snprintf(typefn, sizeof(typefn), "%s/%s/type", EVENT_SRC_DIR, dent->d_name);
 66	fp = fopen(typefn, "r");
 67	if (!fp)
 68		return false;
 69
 70	res = fscanf(fp, "%u", type);
 71	fclose(fp);
 72	return res > 0;
 73}
 74
 75FIXTURE_SETUP(perf_mmap)
 76{
 77	struct perf_event_attr attr = {
 78		.size		= sizeof(attr),
 79		.disabled	= 1,
 80		.exclude_kernel	= 1,
 81		.exclude_hv	= 1,
 82	};
 83	struct perf_event_attr attr_ok = {};
 84	unsigned int eacces = 0, map = 0;
 85	struct perf_event_mmap_page *rb;
 86	struct dirent *dent;
 87	void *aux, *region;
 88	DIR *dir;
 89
 90	self->ptr = NULL;
 91
 92	dir = opendir(EVENT_SRC_DIR);
 93	if (!dir)
 94		SKIP(return, "perf not available.");
 95
 96	region = mmap(NULL, REGION_SIZE, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
 97	ASSERT_NE(region, MAP_FAILED);
 98	self->region = region;
 99
100	// Try to find a suitable event on this system
101	while ((dent = readdir(dir))) {
102		int fd;
103
104		if (!read_event_type(dent, &attr.type))
105			continue;
106
107		fd = syscall(SYS_perf_event_open, &attr, 0, -1, -1, 0);
108		if (fd < 0) {
109			if (errno == EACCES)
110				eacces++;
111			continue;
112		}
113
114		// Check whether the event supports mmap()
115		rb = mmap(region, RB_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0);
116		if (rb == MAP_FAILED) {
117			close(fd);
118			continue;
119		}
120
121		if (!map) {
122			// Save the event in case that no AUX capable event is found
123			attr_ok = attr;
124			map = MAP_BASE;
125		}
126
127		if (!variant->aux)
128			continue;
129
130		rb->aux_offset = AUX_OFFS;
131		rb->aux_size = AUX_SIZE;
132
133		// Check whether it supports a AUX buffer
134		aux = mmap(region + REGION_AUX_OFFS, AUX_SIZE, PROT_READ | PROT_WRITE,
135			   MAP_SHARED | MAP_FIXED, fd, AUX_OFFS);
136		if (aux == MAP_FAILED) {
137			munmap(rb, RB_SIZE);
138			close(fd);
139			continue;
140		}
141
142		attr_ok = attr;
143		map = MAP_AUX;
144		munmap(aux, AUX_SIZE);
145		munmap(rb, RB_SIZE);
146		close(fd);
147		break;
148	}
149	closedir(dir);
150
151	if (!map) {
152		if (!eacces)
153			SKIP(return, "No mappable perf event found.");
154		else
155			SKIP(return, "No permissions for perf_event_open()");
156	}
157
158	self->fd = syscall(SYS_perf_event_open, &attr_ok, 0, -1, -1, 0);
159	ASSERT_NE(self->fd, -1);
160
161	rb = mmap(region, RB_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, self->fd, 0);
162	ASSERT_NE(rb, MAP_FAILED);
163
164	if (!variant->aux) {
165		self->ptr = rb;
166		return;
167	}
168
169	if (map != MAP_AUX)
170		SKIP(return, "No AUX event found.");
171
172	rb->aux_offset = AUX_OFFS;
173	rb->aux_size = AUX_SIZE;
174	aux = mmap(region + REGION_AUX_OFFS, AUX_SIZE, PROT_READ | PROT_WRITE,
175		   MAP_SHARED | MAP_FIXED, self->fd, AUX_OFFS);
176	ASSERT_NE(aux, MAP_FAILED);
177	self->ptr = aux;
178}
179
180FIXTURE_TEARDOWN(perf_mmap)
181{
182	ASSERT_EQ(munmap(self->region, REGION_SIZE), 0);
183	if (self->fd != -1)
184		ASSERT_EQ(close(self->fd), 0);
185}
186
187TEST_F(perf_mmap, remap)
188{
189	void *tmp, *ptr = self->ptr;
190	unsigned long size = variant->ptr_size;
191
192	// Test the invalid remaps
193	ASSERT_EQ(mremap(ptr, size, HOLE_SIZE, MREMAP_MAYMOVE), MAP_FAILED);
194	ASSERT_EQ(mremap(ptr + HOLE_SIZE, size, HOLE_SIZE, MREMAP_MAYMOVE), MAP_FAILED);
195	ASSERT_EQ(mremap(ptr + size - HOLE_SIZE, HOLE_SIZE, size, MREMAP_MAYMOVE), MAP_FAILED);
196	// Shrink the end of the mapping such that we only unmap past end of the VMA,
197	// which should succeed and poke a hole into the PROT_NONE region
198	ASSERT_NE(mremap(ptr + size - HOLE_SIZE, size, HOLE_SIZE, MREMAP_MAYMOVE), MAP_FAILED);
199
200	// Remap the whole buffer to a new address
201	tmp = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
202	ASSERT_NE(tmp, MAP_FAILED);
203
204	// Try splitting offset 1 hole size into VMA, this should fail
205	ASSERT_EQ(mremap(ptr + HOLE_SIZE, size - HOLE_SIZE, size - HOLE_SIZE,
206			 MREMAP_MAYMOVE | MREMAP_FIXED, tmp), MAP_FAILED);
207	// Remapping the whole thing should succeed fine
208	ptr = mremap(ptr, size, size, MREMAP_MAYMOVE | MREMAP_FIXED, tmp);
209	ASSERT_EQ(ptr, tmp);
210	ASSERT_EQ(munmap(tmp, size), 0);
211}
212
213TEST_F(perf_mmap, unmap)
214{
215	unsigned long size = variant->ptr_size;
216
217	// Try to poke holes into the mappings
218	ASSERT_NE(munmap(self->ptr, HOLE_SIZE), 0);
219	ASSERT_NE(munmap(self->ptr + HOLE_SIZE, HOLE_SIZE), 0);
220	ASSERT_NE(munmap(self->ptr + size - HOLE_SIZE, HOLE_SIZE), 0);
221}
222
223TEST_F(perf_mmap, map)
224{
225	unsigned long size = variant->ptr_size;
226
227	// Try to poke holes into the mappings by mapping anonymous memory over it
228	ASSERT_EQ(mmap(self->ptr, HOLE_SIZE, PROT_READ | PROT_WRITE,
229		       MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0), MAP_FAILED);
230	ASSERT_EQ(mmap(self->ptr + HOLE_SIZE, HOLE_SIZE, PROT_READ | PROT_WRITE,
231		       MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0), MAP_FAILED);
232	ASSERT_EQ(mmap(self->ptr + size - HOLE_SIZE, HOLE_SIZE, PROT_READ | PROT_WRITE,
233		       MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0), MAP_FAILED);
234}
235
236TEST_HARNESS_MAIN
~/ Read-only file preview.