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