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
 12struct vm_boundaries {
 13	unsigned long start;
 14	unsigned long end;
 15};
 16
 17static int get_vm_area(unsigned long addr, struct vm_boundaries *area)
 18{
 19	FILE *file;
 20	int ret = 1;
 21	char line[1024] = {0};
 22	char *end_addr;
 23	char *stop;
 24	unsigned long start;
 25	unsigned long end;
 26
 27	if (!area)
 28		return ret;
 29
 30	file = fopen("/proc/self/maps", "r");
 31	if (!file) {
 32		perror("fopen");
 33		return ret;
 34	}
 35
 36	memset(area, 0, sizeof(struct vm_boundaries));
 37
 38	while(fgets(line, 1024, file)) {
 39		end_addr = strchr(line, '-');
 40		if (!end_addr) {
 41			printf("cannot parse /proc/self/maps\n");
 42			goto out;
 43		}
 44		*end_addr = '\0';
 45		end_addr++;
 46		stop = strchr(end_addr, ' ');
 47		if (!stop) {
 48			printf("cannot parse /proc/self/maps\n");
 49			goto out;
 50		}
 51		stop = '\0';
 52
 53		sscanf(line, "%lx", &start);
 54		sscanf(end_addr, "%lx", &end);
 55
 56		if (start <= addr && end > addr) {
 57			area->start = start;
 58			area->end = end;
 59			ret = 0;
 60			goto out;
 61		}
 62	}
 63out:
 64	fclose(file);
 65	return ret;
 66}
 67
 68static uint64_t get_pageflags(unsigned long addr)
 69{
 70	FILE *file;
 71	uint64_t pfn;
 72	unsigned long offset;
 73
 74	file = fopen("/proc/self/pagemap", "r");
 75	if (!file) {
 76		perror("fopen pagemap");
 77		_exit(1);
 78	}
 79
 80	offset = addr / getpagesize() * sizeof(pfn);
 81
 82	if (fseek(file, offset, SEEK_SET)) {
 83		perror("fseek pagemap");
 84		_exit(1);
 85	}
 86
 87	if (fread(&pfn, sizeof(pfn), 1, file) != 1) {
 88		perror("fread pagemap");
 89		_exit(1);
 90	}
 91
 92	fclose(file);
 93	return pfn;
 94}
 95
 96static uint64_t get_kpageflags(unsigned long pfn)
 97{
 98	uint64_t flags;
 99	FILE *file;
100
101	file = fopen("/proc/kpageflags", "r");
102	if (!file) {
103		perror("fopen kpageflags");
104		_exit(1);
105	}
106
107	if (fseek(file, pfn * sizeof(flags), SEEK_SET)) {
108		perror("fseek kpageflags");
109		_exit(1);
110	}
111
112	if (fread(&flags, sizeof(flags), 1, file) != 1) {
113		perror("fread kpageflags");
114		_exit(1);
115	}
116
117	fclose(file);
118	return flags;
119}
120
121#define VMFLAGS "VmFlags:"
122
123static bool is_vmflag_set(unsigned long addr, const char *vmflag)
124{
125	char *line = NULL;
126	char *flags;
127	size_t size = 0;
128	bool ret = false;
129	FILE *smaps;
130
131	smaps = seek_to_smaps_entry(addr);
132	if (!smaps) {
133		printf("Unable to parse /proc/self/smaps\n");
134		goto out;
135	}
136
137	while (getline(&line, &size, smaps) > 0) {
138		if (!strstr(line, VMFLAGS)) {
139			free(line);
140			line = NULL;
141			size = 0;
142			continue;
143		}
144
145		flags = line + strlen(VMFLAGS);
146		ret = (strstr(flags, vmflag) != NULL);
147		goto out;
148	}
149
150out:
151	free(line);
152	fclose(smaps);
153	return ret;
154}
155
156#define SIZE "Size:"
157#define RSS  "Rss:"
158#define LOCKED "lo"
159
160static bool is_vma_lock_on_fault(unsigned long addr)
161{
162	bool ret = false;
163	bool locked;
164	FILE *smaps = NULL;
165	unsigned long vma_size, vma_rss;
166	char *line = NULL;
167	char *value;
168	size_t size = 0;
169
170	locked = is_vmflag_set(addr, LOCKED);
171	if (!locked)
172		goto out;
173
174	smaps = seek_to_smaps_entry(addr);
175	if (!smaps) {
176		printf("Unable to parse /proc/self/smaps\n");
177		goto out;
178	}
179
180	while (getline(&line, &size, smaps) > 0) {
181		if (!strstr(line, SIZE)) {
182			free(line);
183			line = NULL;
184			size = 0;
185			continue;
186		}
187
188		value = line + strlen(SIZE);
189		if (sscanf(value, "%lu kB", &vma_size) < 1) {
190			printf("Unable to parse smaps entry for Size\n");
191			goto out;
192		}
193		break;
194	}
195
196	while (getline(&line, &size, smaps) > 0) {
197		if (!strstr(line, RSS)) {
198			free(line);
199			line = NULL;
200			size = 0;
201			continue;
202		}
203
204		value = line + strlen(RSS);
205		if (sscanf(value, "%lu kB", &vma_rss) < 1) {
206			printf("Unable to parse smaps entry for Rss\n");
207			goto out;
208		}
209		break;
210	}
211
212	ret = locked && (vma_rss < vma_size);
213out:
214	free(line);
215	if (smaps)
216		fclose(smaps);
217	return ret;
218}
219
220#define PRESENT_BIT     0x8000000000000000ULL
221#define PFN_MASK        0x007FFFFFFFFFFFFFULL
222#define UNEVICTABLE_BIT (1UL << 18)
223
224static int lock_check(char *map)
225{
226	unsigned long page_size = getpagesize();
227	uint64_t page1_flags, page2_flags;
228
229	page1_flags = get_pageflags((unsigned long)map);
230	page2_flags = get_pageflags((unsigned long)map + page_size);
231
232	/* Both pages should be present */
233	if (((page1_flags & PRESENT_BIT) == 0) ||
234	    ((page2_flags & PRESENT_BIT) == 0)) {
235		printf("Failed to make both pages present\n");
236		return 1;
237	}
238
239	page1_flags = get_kpageflags(page1_flags & PFN_MASK);
240	page2_flags = get_kpageflags(page2_flags & PFN_MASK);
241
242	/* Both pages should be unevictable */
243	if (((page1_flags & UNEVICTABLE_BIT) == 0) ||
244	    ((page2_flags & UNEVICTABLE_BIT) == 0)) {
245		printf("Failed to make both pages unevictable\n");
246		return 1;
247	}
248
249	if (!is_vmflag_set((unsigned long)map, LOCKED)) {
250		printf("VMA flag %s is missing on page 1\n", LOCKED);
251		return 1;
252	}
253
254	if (!is_vmflag_set((unsigned long)map + page_size, LOCKED)) {
255		printf("VMA flag %s is missing on page 2\n", LOCKED);
256		return 1;
257	}
258
259	return 0;
260}
261
262static int unlock_lock_check(char *map)
263{
264	unsigned long page_size = getpagesize();
265	uint64_t page1_flags, page2_flags;
266
267	page1_flags = get_pageflags((unsigned long)map);
268	page2_flags = get_pageflags((unsigned long)map + page_size);
269	page1_flags = get_kpageflags(page1_flags & PFN_MASK);
270	page2_flags = get_kpageflags(page2_flags & PFN_MASK);
271
272	if ((page1_flags & UNEVICTABLE_BIT) || (page2_flags & UNEVICTABLE_BIT)) {
273		printf("A page is still marked unevictable after unlock\n");
274		return 1;
275	}
276
277	if (is_vmflag_set((unsigned long)map, LOCKED)) {
278		printf("VMA flag %s is present on page 1 after unlock\n", LOCKED);
279		return 1;
280	}
281
282	if (is_vmflag_set((unsigned long)map + page_size, LOCKED)) {
283		printf("VMA flag %s is present on page 2 after unlock\n", LOCKED);
284		return 1;
285	}
286
287	return 0;
288}
289
290static int test_mlock_lock()
291{
292	char *map;
293	int ret = 1;
294	unsigned long page_size = getpagesize();
295
296	map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
297		   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
298	if (map == MAP_FAILED) {
299		perror("test_mlock_locked mmap");
300		goto out;
301	}
302
303	if (mlock2_(map, 2 * page_size, 0)) {
304		if (errno == ENOSYS) {
305			printf("Cannot call new mlock family, skipping test\n");
306			_exit(0);
307		}
308		perror("mlock2(0)");
309		goto unmap;
310	}
311
312	if (lock_check(map))
313		goto unmap;
314
315	/* Now unlock and recheck attributes */
316	if (munlock(map, 2 * page_size)) {
317		perror("munlock()");
318		goto unmap;
319	}
320
321	ret = unlock_lock_check(map);
322
323unmap:
324	munmap(map, 2 * page_size);
325out:
326	return ret;
327}
328
329static int onfault_check(char *map)
330{
331	unsigned long page_size = getpagesize();
332	uint64_t page1_flags, page2_flags;
333
334	page1_flags = get_pageflags((unsigned long)map);
335	page2_flags = get_pageflags((unsigned long)map + page_size);
336
337	/* Neither page should be present */
338	if ((page1_flags & PRESENT_BIT) || (page2_flags & PRESENT_BIT)) {
339		printf("Pages were made present by MLOCK_ONFAULT\n");
340		return 1;
341	}
342
343	*map = 'a';
344	page1_flags = get_pageflags((unsigned long)map);
345	page2_flags = get_pageflags((unsigned long)map + page_size);
346
347	/* Only page 1 should be present */
348	if ((page1_flags & PRESENT_BIT) == 0) {
349		printf("Page 1 is not present after fault\n");
350		return 1;
351	} else if (page2_flags & PRESENT_BIT) {
352		printf("Page 2 was made present\n");
353		return 1;
354	}
355
356	page1_flags = get_kpageflags(page1_flags & PFN_MASK);
357
358	/* Page 1 should be unevictable */
359	if ((page1_flags & UNEVICTABLE_BIT) == 0) {
360		printf("Failed to make faulted page unevictable\n");
361		return 1;
362	}
363
364	if (!is_vma_lock_on_fault((unsigned long)map)) {
365		printf("VMA is not marked for lock on fault\n");
366		return 1;
367	}
368
369	if (!is_vma_lock_on_fault((unsigned long)map + page_size)) {
370		printf("VMA is not marked for lock on fault\n");
371		return 1;
372	}
373
374	return 0;
375}
376
377static int unlock_onfault_check(char *map)
378{
379	unsigned long page_size = getpagesize();
380	uint64_t page1_flags;
381
382	page1_flags = get_pageflags((unsigned long)map);
383	page1_flags = get_kpageflags(page1_flags & PFN_MASK);
384
385	if (page1_flags & UNEVICTABLE_BIT) {
386		printf("Page 1 is still marked unevictable after unlock\n");
387		return 1;
388	}
389
390	if (is_vma_lock_on_fault((unsigned long)map) ||
391	    is_vma_lock_on_fault((unsigned long)map + page_size)) {
392		printf("VMA is still lock on fault after unlock\n");
393		return 1;
394	}
395
396	return 0;
397}
398
399static int test_mlock_onfault()
400{
401	char *map;
402	int ret = 1;
403	unsigned long page_size = getpagesize();
404
405	map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
406		   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
407	if (map == MAP_FAILED) {
408		perror("test_mlock_locked mmap");
409		goto out;
410	}
411
412	if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) {
413		if (errno == ENOSYS) {
414			printf("Cannot call new mlock family, skipping test\n");
415			_exit(0);
416		}
417		perror("mlock2(MLOCK_ONFAULT)");
418		goto unmap;
419	}
420
421	if (onfault_check(map))
422		goto unmap;
423
424	/* Now unlock and recheck attributes */
425	if (munlock(map, 2 * page_size)) {
426		if (errno == ENOSYS) {
427			printf("Cannot call new mlock family, skipping test\n");
428			_exit(0);
429		}
430		perror("munlock()");
431		goto unmap;
432	}
433
434	ret = unlock_onfault_check(map);
435unmap:
436	munmap(map, 2 * page_size);
437out:
438	return ret;
439}
440
441static int test_lock_onfault_of_present()
442{
443	char *map;
444	int ret = 1;
445	unsigned long page_size = getpagesize();
446	uint64_t page1_flags, page2_flags;
447
448	map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
449		   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
450	if (map == MAP_FAILED) {
451		perror("test_mlock_locked mmap");
452		goto out;
453	}
454
455	*map = 'a';
456
457	if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) {
458		if (errno == ENOSYS) {
459			printf("Cannot call new mlock family, skipping test\n");
460			_exit(0);
461		}
462		perror("mlock2(MLOCK_ONFAULT)");
463		goto unmap;
464	}
465
466	page1_flags = get_pageflags((unsigned long)map);
467	page2_flags = get_pageflags((unsigned long)map + page_size);
468	page1_flags = get_kpageflags(page1_flags & PFN_MASK);
469	page2_flags = get_kpageflags(page2_flags & PFN_MASK);
470
471	/* Page 1 should be unevictable */
472	if ((page1_flags & UNEVICTABLE_BIT) == 0) {
473		printf("Failed to make present page unevictable\n");
474		goto unmap;
475	}
476
477	if (!is_vma_lock_on_fault((unsigned long)map) ||
478	    !is_vma_lock_on_fault((unsigned long)map + page_size)) {
479		printf("VMA with present pages is not marked lock on fault\n");
480		goto unmap;
481	}
482	ret = 0;
483unmap:
484	munmap(map, 2 * page_size);
485out:
486	return ret;
487}
488
489static int test_munlockall()
490{
491	char *map;
492	int ret = 1;
493	unsigned long page_size = getpagesize();
494
495	map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
496		   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
497
498	if (map == MAP_FAILED) {
499		perror("test_munlockall mmap");
500		goto out;
501	}
502
503	if (mlockall(MCL_CURRENT)) {
504		perror("mlockall(MCL_CURRENT)");
505		goto out;
506	}
507
508	if (lock_check(map))
509		goto unmap;
510
511	if (munlockall()) {
512		perror("munlockall()");
513		goto unmap;
514	}
515
516	if (unlock_lock_check(map))
517		goto unmap;
518
519	munmap(map, 2 * page_size);
520
521	map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
522		   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
523
524	if (map == MAP_FAILED) {
525		perror("test_munlockall second mmap");
526		goto out;
527	}
528
529	if (mlockall(MCL_CURRENT | MCL_ONFAULT)) {
530		perror("mlockall(MCL_CURRENT | MCL_ONFAULT)");
531		goto unmap;
532	}
533
534	if (onfault_check(map))
535		goto unmap;
536
537	if (munlockall()) {
538		perror("munlockall()");
539		goto unmap;
540	}
541
542	if (unlock_onfault_check(map))
543		goto unmap;
544
545	if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
546		perror("mlockall(MCL_CURRENT | MCL_FUTURE)");
547		goto out;
548	}
549
550	if (lock_check(map))
551		goto unmap;
552
553	if (munlockall()) {
554		perror("munlockall()");
555		goto unmap;
556	}
557
558	ret = unlock_lock_check(map);
559
560unmap:
561	munmap(map, 2 * page_size);
562out:
563	munlockall();
564	return ret;
565}
566
567static int test_vma_management(bool call_mlock)
568{
569	int ret = 1;
570	void *map;
571	unsigned long page_size = getpagesize();
572	struct vm_boundaries page1;
573	struct vm_boundaries page2;
574	struct vm_boundaries page3;
575
576	map = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE,
577		   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
578	if (map == MAP_FAILED) {
579		perror("mmap()");
580		return ret;
581	}
582
583	if (call_mlock && mlock2_(map, 3 * page_size, MLOCK_ONFAULT)) {
584		if (errno == ENOSYS) {
585			printf("Cannot call new mlock family, skipping test\n");
586			_exit(0);
587		}
588		perror("mlock(ONFAULT)\n");
589		goto out;
590	}
591
592	if (get_vm_area((unsigned long)map, &page1) ||
593	    get_vm_area((unsigned long)map + page_size, &page2) ||
594	    get_vm_area((unsigned long)map + page_size * 2, &page3)) {
595		printf("couldn't find mapping in /proc/self/maps\n");
596		goto out;
597	}
598
599	/*
600	 * Before we unlock a portion, we need to that all three pages are in
601	 * the same VMA.  If they are not we abort this test (Note that this is
602	 * not a failure)
603	 */
604	if (page1.start != page2.start || page2.start != page3.start) {
605		printf("VMAs are not merged to start, aborting test\n");
606		ret = 0;
607		goto out;
608	}
609
610	if (munlock(map + page_size, page_size)) {
611		perror("munlock()");
612		goto out;
613	}
614
615	if (get_vm_area((unsigned long)map, &page1) ||
616	    get_vm_area((unsigned long)map + page_size, &page2) ||
617	    get_vm_area((unsigned long)map + page_size * 2, &page3)) {
618		printf("couldn't find mapping in /proc/self/maps\n");
619		goto out;
620	}
621
622	/* All three VMAs should be different */
623	if (page1.start == page2.start || page2.start == page3.start) {
624		printf("failed to split VMA for munlock\n");
625		goto out;
626	}
627
628	/* Now unlock the first and third page and check the VMAs again */
629	if (munlock(map, page_size * 3)) {
630		perror("munlock()");
631		goto out;
632	}
633
634	if (get_vm_area((unsigned long)map, &page1) ||
635	    get_vm_area((unsigned long)map + page_size, &page2) ||
636	    get_vm_area((unsigned long)map + page_size * 2, &page3)) {
637		printf("couldn't find mapping in /proc/self/maps\n");
638		goto out;
639	}
640
641	/* Now all three VMAs should be the same */
642	if (page1.start != page2.start || page2.start != page3.start) {
643		printf("failed to merge VMAs after munlock\n");
644		goto out;
645	}
646
647	ret = 0;
648out:
649	munmap(map, 3 * page_size);
650	return ret;
651}
652
653static int test_mlockall(int (test_function)(bool call_mlock))
654{
655	int ret = 1;
656
657	if (mlockall(MCL_CURRENT | MCL_ONFAULT | MCL_FUTURE)) {
658		perror("mlockall");
659		return ret;
660	}
661
662	ret = test_function(false);
663	munlockall();
664	return ret;
665}
666
667int main(int argc, char **argv)
668{
669	int ret = 0;
670	ret += test_mlock_lock();
671	ret += test_mlock_onfault();
672	ret += test_munlockall();
673	ret += test_lock_onfault_of_present();
674	ret += test_vma_management(true);
675	ret += test_mlockall(test_vma_management);
676	return ret;
677}