Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  1// SPDX-License-Identifier: GPL-2.0
  2#define _GNU_SOURCE
  3#include <sys/mman.h>
  4#include <stdint.h>
  5#include <unistd.h>
  6#include <string.h>
  7#include <sys/time.h>
  8#include <sys/resource.h>
  9#include <stdbool.h>
 10#include "mlock2.h"
 11
 12#include "../kselftest.h"
 13
 14struct vm_boundaries {
 15	unsigned long start;
 16	unsigned long end;
 17};
 18
 19static int get_vm_area(unsigned long addr, struct vm_boundaries *area)
 20{
 21	FILE *file;
 22	int ret = 1;
 23	char line[1024] = {0};
 24	char *end_addr;
 25	char *stop;
 26	unsigned long start;
 27	unsigned long end;
 28
 29	if (!area)
 30		return ret;
 31
 32	file = fopen("/proc/self/maps", "r");
 33	if (!file) {
 34		perror("fopen");
 35		return ret;
 36	}
 37
 38	memset(area, 0, sizeof(struct vm_boundaries));
 39
 40	while(fgets(line, 1024, file)) {
 41		end_addr = strchr(line, '-');
 42		if (!end_addr) {
 43			printf("cannot parse /proc/self/maps\n");
 44			goto out;
 45		}
 46		*end_addr = '\0';
 47		end_addr++;
 48		stop = strchr(end_addr, ' ');
 49		if (!stop) {
 50			printf("cannot parse /proc/self/maps\n");
 51			goto out;
 52		}
 53		stop = '\0';
 54
 55		sscanf(line, "%lx", &start);
 56		sscanf(end_addr, "%lx", &end);
 57
 58		if (start <= addr && end > addr) {
 59			area->start = start;
 60			area->end = end;
 61			ret = 0;
 62			goto out;
 63		}
 64	}
 65out:
 66	fclose(file);
 67	return ret;
 68}
 69
 70#define VMFLAGS "VmFlags:"
 71
 72static bool is_vmflag_set(unsigned long addr, const char *vmflag)
 73{
 74	char *line = NULL;
 75	char *flags;
 76	size_t size = 0;
 77	bool ret = false;
 78	FILE *smaps;
 79
 80	smaps = seek_to_smaps_entry(addr);
 81	if (!smaps) {
 82		printf("Unable to parse /proc/self/smaps\n");
 83		goto out;
 84	}
 85
 86	while (getline(&line, &size, smaps) > 0) {
 87		if (!strstr(line, VMFLAGS)) {
 88			free(line);
 89			line = NULL;
 90			size = 0;
 91			continue;
 92		}
 93
 94		flags = line + strlen(VMFLAGS);
 95		ret = (strstr(flags, vmflag) != NULL);
 96		goto out;
 97	}
 98
 99out:
100	free(line);
101	fclose(smaps);
102	return ret;
103}
104
105#define SIZE "Size:"
106#define RSS  "Rss:"
107#define LOCKED "lo"
108
109static unsigned long get_value_for_name(unsigned long addr, const char *name)
110{
111	char *line = NULL;
112	size_t size = 0;
113	char *value_ptr;
114	FILE *smaps = NULL;
115	unsigned long value = -1UL;
116
117	smaps = seek_to_smaps_entry(addr);
118	if (!smaps) {
119		printf("Unable to parse /proc/self/smaps\n");
120		goto out;
121	}
122
123	while (getline(&line, &size, smaps) > 0) {
124		if (!strstr(line, name)) {
125			free(line);
126			line = NULL;
127			size = 0;
128			continue;
129		}
130
131		value_ptr = line + strlen(name);
132		if (sscanf(value_ptr, "%lu kB", &value) < 1) {
133			printf("Unable to parse smaps entry for Size\n");
134			goto out;
135		}
136		break;
137	}
138
139out:
140	if (smaps)
141		fclose(smaps);
142	free(line);
143	return value;
144}
145
146static bool is_vma_lock_on_fault(unsigned long addr)
147{
148	bool locked;
149	unsigned long vma_size, vma_rss;
150
151	locked = is_vmflag_set(addr, LOCKED);
152	if (!locked)
153		return false;
154
155	vma_size = get_value_for_name(addr, SIZE);
156	vma_rss = get_value_for_name(addr, RSS);
157
158	/* only one page is faulted in */
159	return (vma_rss < vma_size);
160}
161
162#define PRESENT_BIT     0x8000000000000000ULL
163#define PFN_MASK        0x007FFFFFFFFFFFFFULL
164#define UNEVICTABLE_BIT (1UL << 18)
165
166static int lock_check(unsigned long addr)
167{
168	bool locked;
169	unsigned long vma_size, vma_rss;
170
171	locked = is_vmflag_set(addr, LOCKED);
172	if (!locked)
173		return false;
174
175	vma_size = get_value_for_name(addr, SIZE);
176	vma_rss = get_value_for_name(addr, RSS);
177
178	return (vma_rss == vma_size);
179}
180
181static int unlock_lock_check(char *map)
182{
183	if (is_vmflag_set((unsigned long)map, LOCKED)) {
184		printf("VMA flag %s is present on page 1 after unlock\n", LOCKED);
185		return 1;
186	}
187
188	return 0;
189}
190
191static int test_mlock_lock()
192{
193	char *map;
194	int ret = 1;
195	unsigned long page_size = getpagesize();
196
197	map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
198		   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
199	if (map == MAP_FAILED) {
200		perror("test_mlock_locked mmap");
201		goto out;
202	}
203
204	if (mlock2_(map, 2 * page_size, 0)) {
205		if (errno == ENOSYS) {
206			printf("Cannot call new mlock family, skipping test\n");
207			_exit(KSFT_SKIP);
208		}
209		perror("mlock2(0)");
210		goto unmap;
211	}
212
213	if (!lock_check((unsigned long)map))
214		goto unmap;
215
216	/* Now unlock and recheck attributes */
217	if (munlock(map, 2 * page_size)) {
218		perror("munlock()");
219		goto unmap;
220	}
221
222	ret = unlock_lock_check(map);
223
224unmap:
225	munmap(map, 2 * page_size);
226out:
227	return ret;
228}
229
230static int onfault_check(char *map)
231{
232	*map = 'a';
233	if (!is_vma_lock_on_fault((unsigned long)map)) {
234		printf("VMA is not marked for lock on fault\n");
235		return 1;
236	}
237
238	return 0;
239}
240
241static int unlock_onfault_check(char *map)
242{
243	unsigned long page_size = getpagesize();
244
245	if (is_vma_lock_on_fault((unsigned long)map) ||
246	    is_vma_lock_on_fault((unsigned long)map + page_size)) {
247		printf("VMA is still lock on fault after unlock\n");
248		return 1;
249	}
250
251	return 0;
252}
253
254static int test_mlock_onfault()
255{
256	char *map;
257	int ret = 1;
258	unsigned long page_size = getpagesize();
259
260	map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
261		   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
262	if (map == MAP_FAILED) {
263		perror("test_mlock_locked mmap");
264		goto out;
265	}
266
267	if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) {
268		if (errno == ENOSYS) {
269			printf("Cannot call new mlock family, skipping test\n");
270			_exit(KSFT_SKIP);
271		}
272		perror("mlock2(MLOCK_ONFAULT)");
273		goto unmap;
274	}
275
276	if (onfault_check(map))
277		goto unmap;
278
279	/* Now unlock and recheck attributes */
280	if (munlock(map, 2 * page_size)) {
281		if (errno == ENOSYS) {
282			printf("Cannot call new mlock family, skipping test\n");
283			_exit(KSFT_SKIP);
284		}
285		perror("munlock()");
286		goto unmap;
287	}
288
289	ret = unlock_onfault_check(map);
290unmap:
291	munmap(map, 2 * page_size);
292out:
293	return ret;
294}
295
296static int test_lock_onfault_of_present()
297{
298	char *map;
299	int ret = 1;
300	unsigned long page_size = getpagesize();
301
302	map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
303		   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
304	if (map == MAP_FAILED) {
305		perror("test_mlock_locked mmap");
306		goto out;
307	}
308
309	*map = 'a';
310
311	if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) {
312		if (errno == ENOSYS) {
313			printf("Cannot call new mlock family, skipping test\n");
314			_exit(KSFT_SKIP);
315		}
316		perror("mlock2(MLOCK_ONFAULT)");
317		goto unmap;
318	}
319
320	if (!is_vma_lock_on_fault((unsigned long)map) ||
321	    !is_vma_lock_on_fault((unsigned long)map + page_size)) {
322		printf("VMA with present pages is not marked lock on fault\n");
323		goto unmap;
324	}
325	ret = 0;
326unmap:
327	munmap(map, 2 * page_size);
328out:
329	return ret;
330}
331
332static int test_munlockall()
333{
334	char *map;
335	int ret = 1;
336	unsigned long page_size = getpagesize();
337
338	map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
339		   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
340
341	if (map == MAP_FAILED) {
342		perror("test_munlockall mmap");
343		goto out;
344	}
345
346	if (mlockall(MCL_CURRENT)) {
347		perror("mlockall(MCL_CURRENT)");
348		goto out;
349	}
350
351	if (!lock_check((unsigned long)map))
352		goto unmap;
353
354	if (munlockall()) {
355		perror("munlockall()");
356		goto unmap;
357	}
358
359	if (unlock_lock_check(map))
360		goto unmap;
361
362	munmap(map, 2 * page_size);
363
364	map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
365		   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
366
367	if (map == MAP_FAILED) {
368		perror("test_munlockall second mmap");
369		goto out;
370	}
371
372	if (mlockall(MCL_CURRENT | MCL_ONFAULT)) {
373		perror("mlockall(MCL_CURRENT | MCL_ONFAULT)");
374		goto unmap;
375	}
376
377	if (onfault_check(map))
378		goto unmap;
379
380	if (munlockall()) {
381		perror("munlockall()");
382		goto unmap;
383	}
384
385	if (unlock_onfault_check(map))
386		goto unmap;
387
388	if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
389		perror("mlockall(MCL_CURRENT | MCL_FUTURE)");
390		goto out;
391	}
392
393	if (!lock_check((unsigned long)map))
394		goto unmap;
395
396	if (munlockall()) {
397		perror("munlockall()");
398		goto unmap;
399	}
400
401	ret = unlock_lock_check(map);
402
403unmap:
404	munmap(map, 2 * page_size);
405out:
406	munlockall();
407	return ret;
408}
409
410static int test_vma_management(bool call_mlock)
411{
412	int ret = 1;
413	void *map;
414	unsigned long page_size = getpagesize();
415	struct vm_boundaries page1;
416	struct vm_boundaries page2;
417	struct vm_boundaries page3;
418
419	map = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE,
420		   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
421	if (map == MAP_FAILED) {
422		perror("mmap()");
423		return ret;
424	}
425
426	if (call_mlock && mlock2_(map, 3 * page_size, MLOCK_ONFAULT)) {
427		if (errno == ENOSYS) {
428			printf("Cannot call new mlock family, skipping test\n");
429			_exit(KSFT_SKIP);
430		}
431		perror("mlock(ONFAULT)\n");
432		goto out;
433	}
434
435	if (get_vm_area((unsigned long)map, &page1) ||
436	    get_vm_area((unsigned long)map + page_size, &page2) ||
437	    get_vm_area((unsigned long)map + page_size * 2, &page3)) {
438		printf("couldn't find mapping in /proc/self/maps\n");
439		goto out;
440	}
441
442	/*
443	 * Before we unlock a portion, we need to that all three pages are in
444	 * the same VMA.  If they are not we abort this test (Note that this is
445	 * not a failure)
446	 */
447	if (page1.start != page2.start || page2.start != page3.start) {
448		printf("VMAs are not merged to start, aborting test\n");
449		ret = 0;
450		goto out;
451	}
452
453	if (munlock(map + page_size, page_size)) {
454		perror("munlock()");
455		goto out;
456	}
457
458	if (get_vm_area((unsigned long)map, &page1) ||
459	    get_vm_area((unsigned long)map + page_size, &page2) ||
460	    get_vm_area((unsigned long)map + page_size * 2, &page3)) {
461		printf("couldn't find mapping in /proc/self/maps\n");
462		goto out;
463	}
464
465	/* All three VMAs should be different */
466	if (page1.start == page2.start || page2.start == page3.start) {
467		printf("failed to split VMA for munlock\n");
468		goto out;
469	}
470
471	/* Now unlock the first and third page and check the VMAs again */
472	if (munlock(map, page_size * 3)) {
473		perror("munlock()");
474		goto out;
475	}
476
477	if (get_vm_area((unsigned long)map, &page1) ||
478	    get_vm_area((unsigned long)map + page_size, &page2) ||
479	    get_vm_area((unsigned long)map + page_size * 2, &page3)) {
480		printf("couldn't find mapping in /proc/self/maps\n");
481		goto out;
482	}
483
484	/* Now all three VMAs should be the same */
485	if (page1.start != page2.start || page2.start != page3.start) {
486		printf("failed to merge VMAs after munlock\n");
487		goto out;
488	}
489
490	ret = 0;
491out:
492	munmap(map, 3 * page_size);
493	return ret;
494}
495
496static int test_mlockall(int (test_function)(bool call_mlock))
497{
498	int ret = 1;
499
500	if (mlockall(MCL_CURRENT | MCL_ONFAULT | MCL_FUTURE)) {
501		perror("mlockall");
502		return ret;
503	}
504
505	ret = test_function(false);
506	munlockall();
507	return ret;
508}
509
510int main(int argc, char **argv)
511{
512	int ret = 0;
513	ret += test_mlock_lock();
514	ret += test_mlock_onfault();
515	ret += test_munlockall();
516	ret += test_lock_onfault_of_present();
517	ret += test_vma_management(true);
518	ret += test_mlockall(test_vma_management);
519	return ret;
520}