Linux Audio

Check our new training course

Loading...
v6.2
  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}
v4.17
  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}