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