1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci *  Copyright (c) International Business Machines Corp., 2004
4f08c3bdfSopenharmony_ci *  Copyright (c) Linux Test Project, 2013-2016
5f08c3bdfSopenharmony_ci */
6f08c3bdfSopenharmony_ci
7f08c3bdfSopenharmony_ci/*
8f08c3bdfSopenharmony_ci * This is a test for the madvise(2) system call. It is intended
9f08c3bdfSopenharmony_ci * to provide a complete exposure of the system call. It tests
10f08c3bdfSopenharmony_ci * madvise(2) for all error conditions to occur correctly.
11f08c3bdfSopenharmony_ci *
12f08c3bdfSopenharmony_ci * (A) Test Case for EINVAL
13f08c3bdfSopenharmony_ci *  1. start is not page-aligned
14f08c3bdfSopenharmony_ci *  2. advice is not a valid value
15f08c3bdfSopenharmony_ci *  3. application is attempting to release
16f08c3bdfSopenharmony_ci *     locked or shared pages (with MADV_DONTNEED)
17f08c3bdfSopenharmony_ci *  4. MADV_MERGEABLE or MADV_UNMERGEABLE was specified in advice,
18f08c3bdfSopenharmony_ci *     but the kernel was not configured with CONFIG_KSM.
19f08c3bdfSopenharmony_ci *  8|9. The MADV_FREE & MADV_WIPEONFORK operation can be applied
20f08c3bdfSopenharmony_ci *  	only to private anonymous pages.
21f08c3bdfSopenharmony_ci *
22f08c3bdfSopenharmony_ci * (B) Test Case for ENOMEM
23f08c3bdfSopenharmony_ci *  5|6. addresses in the specified range are not currently mapped
24f08c3bdfSopenharmony_ci *     or are outside the address space of the process
25f08c3bdfSopenharmony_ci *  b. Not enough memory - paging in failed
26f08c3bdfSopenharmony_ci *
27f08c3bdfSopenharmony_ci * (C) Test Case for EBADF
28f08c3bdfSopenharmony_ci *  7. the map exists,
29f08c3bdfSopenharmony_ci *     but the area maps something that isn't a file.
30f08c3bdfSopenharmony_ci */
31f08c3bdfSopenharmony_ci
32f08c3bdfSopenharmony_ci#include <sys/types.h>
33f08c3bdfSopenharmony_ci#include <sys/resource.h>
34f08c3bdfSopenharmony_ci#include <sys/stat.h>
35f08c3bdfSopenharmony_ci#include <sys/time.h>
36f08c3bdfSopenharmony_ci#include <errno.h>
37f08c3bdfSopenharmony_ci#include <fcntl.h>
38f08c3bdfSopenharmony_ci#include <stdio.h>
39f08c3bdfSopenharmony_ci#include <stdlib.h>
40f08c3bdfSopenharmony_ci#include <string.h>
41f08c3bdfSopenharmony_ci#include <unistd.h>
42f08c3bdfSopenharmony_ci
43f08c3bdfSopenharmony_ci#include "tst_test.h"
44f08c3bdfSopenharmony_ci#include "lapi/mmap.h"
45f08c3bdfSopenharmony_ci
46f08c3bdfSopenharmony_ci#define MAP_SIZE (4 * 1024)
47f08c3bdfSopenharmony_ci#define TEST_FILE "testfile"
48f08c3bdfSopenharmony_ci#define STR "abcdefghijklmnopqrstuvwxyz12345\n"
49f08c3bdfSopenharmony_ci#define KSM_SYS_DIR	"/sys/kernel/mm/ksm"
50f08c3bdfSopenharmony_ci
51f08c3bdfSopenharmony_cistatic struct stat st;
52f08c3bdfSopenharmony_cistatic long pagesize;
53f08c3bdfSopenharmony_cistatic char *file1;
54f08c3bdfSopenharmony_cistatic char *file2;
55f08c3bdfSopenharmony_cistatic char *file3;
56f08c3bdfSopenharmony_cistatic char *shared_anon;
57f08c3bdfSopenharmony_cistatic char *ptr_addr;
58f08c3bdfSopenharmony_cistatic char *tmp_addr;
59f08c3bdfSopenharmony_cistatic char *nonalign;
60f08c3bdfSopenharmony_ci
61f08c3bdfSopenharmony_cistatic struct tcase {
62f08c3bdfSopenharmony_ci	int advice;
63f08c3bdfSopenharmony_ci	char *name;
64f08c3bdfSopenharmony_ci	char **addr;
65f08c3bdfSopenharmony_ci	int exp_errno;
66f08c3bdfSopenharmony_ci	int skip;
67f08c3bdfSopenharmony_ci} tcases[] = {
68f08c3bdfSopenharmony_ci	{MADV_NORMAL,      "MADV_NORMAL",      &nonalign, EINVAL, 0},
69f08c3bdfSopenharmony_ci	{1212,             "MADV_NORMAL",      &file1,    EINVAL, 0},
70f08c3bdfSopenharmony_ci	{MADV_REMOVE,      "MADV_REMOVE",      &file1,    EINVAL, 0},
71f08c3bdfSopenharmony_ci	{MADV_DONTNEED,    "MADV_DONTNEED",    &file1,    EINVAL, 1},
72f08c3bdfSopenharmony_ci	{MADV_MERGEABLE,   "MADV_MERGEABLE",   &file1,    EINVAL, 0},
73f08c3bdfSopenharmony_ci	{MADV_UNMERGEABLE, "MADV_UNMERGEABLE", &file1,    EINVAL, 0},
74f08c3bdfSopenharmony_ci	{MADV_NORMAL,      "MADV_NORMAL",      &file2,    ENOMEM, 0},
75f08c3bdfSopenharmony_ci	{MADV_WILLNEED,    "MADV_WILLNEED",    &file2,    ENOMEM, 0},
76f08c3bdfSopenharmony_ci	{MADV_WILLNEED,    "MADV_WILLNEED",    &tmp_addr,  EBADF, 0},
77f08c3bdfSopenharmony_ci	{MADV_FREE,        "MADV_FREE",        &file1,    EINVAL, 0},
78f08c3bdfSopenharmony_ci	{MADV_WIPEONFORK,  "MADV_WIPEONFORK",  &file1,    EINVAL, 0},
79f08c3bdfSopenharmony_ci	{MADV_WIPEONFORK,  "MADV_WIPEONFORK shared_anon", &shared_anon, EINVAL, 0},
80f08c3bdfSopenharmony_ci	{MADV_WIPEONFORK,  "MADV_WIPEONFORK private file backed", &file3, EINVAL, 0},
81f08c3bdfSopenharmony_ci};
82f08c3bdfSopenharmony_ci
83f08c3bdfSopenharmony_cistatic void tcases_filter(void)
84f08c3bdfSopenharmony_ci{
85f08c3bdfSopenharmony_ci	unsigned int i;
86f08c3bdfSopenharmony_ci
87f08c3bdfSopenharmony_ci	for (i = 0; i < ARRAY_SIZE(tcases); i++) {
88f08c3bdfSopenharmony_ci		struct tcase *tc = &tcases[i];
89f08c3bdfSopenharmony_ci
90f08c3bdfSopenharmony_ci		switch (tc->advice) {
91f08c3bdfSopenharmony_ci		case MADV_DONTNEED:
92f08c3bdfSopenharmony_ci#if !defined(UCLINUX)
93f08c3bdfSopenharmony_ci			if (mlock(file1, st.st_size) < 0)
94f08c3bdfSopenharmony_ci				tst_brk(TBROK | TERRNO, "mlock failed");
95f08c3bdfSopenharmony_ci			tc->skip = 0;
96f08c3bdfSopenharmony_ci#endif /* if !defined(UCLINUX) */
97f08c3bdfSopenharmony_ci		break;
98f08c3bdfSopenharmony_ci		case MADV_MERGEABLE:
99f08c3bdfSopenharmony_ci		case MADV_UNMERGEABLE:
100f08c3bdfSopenharmony_ci			/* kernel configured with CONFIG_KSM,
101f08c3bdfSopenharmony_ci			 * skip EINVAL test for MADV_MERGEABLE. */
102f08c3bdfSopenharmony_ci			if (access(KSM_SYS_DIR, F_OK) == 0)
103f08c3bdfSopenharmony_ci				tc->skip = 1;
104f08c3bdfSopenharmony_ci		break;
105f08c3bdfSopenharmony_ci		case MADV_WILLNEED:
106f08c3bdfSopenharmony_ci			/* In kernel commit 1998cc0, madvise(MADV_WILLNEED) to
107f08c3bdfSopenharmony_ci			 * anon mem doesn't return -EBADF now, as now we support
108f08c3bdfSopenharmony_ci			 * swap prefretch. */
109f08c3bdfSopenharmony_ci			if (tc->exp_errno == EBADF)
110f08c3bdfSopenharmony_ci				tc->skip = 1;
111f08c3bdfSopenharmony_ci		break;
112f08c3bdfSopenharmony_ci		case MADV_FREE:
113f08c3bdfSopenharmony_ci			if ((tst_kvercmp(4, 5, 0)) < 0)
114f08c3bdfSopenharmony_ci				tc->skip = 1;
115f08c3bdfSopenharmony_ci		break;
116f08c3bdfSopenharmony_ci		case MADV_WIPEONFORK:
117f08c3bdfSopenharmony_ci			if ((tst_kvercmp(4, 14, 0)) < 0)
118f08c3bdfSopenharmony_ci				tc->skip = 1;
119f08c3bdfSopenharmony_ci		break;
120f08c3bdfSopenharmony_ci		default:
121f08c3bdfSopenharmony_ci		break;
122f08c3bdfSopenharmony_ci		}
123f08c3bdfSopenharmony_ci	}
124f08c3bdfSopenharmony_ci}
125f08c3bdfSopenharmony_ci
126f08c3bdfSopenharmony_cistatic void setup(void)
127f08c3bdfSopenharmony_ci{
128f08c3bdfSopenharmony_ci	int i, fd;
129f08c3bdfSopenharmony_ci
130f08c3bdfSopenharmony_ci	fd = SAFE_OPEN(TEST_FILE, O_RDWR | O_CREAT, 0664);
131f08c3bdfSopenharmony_ci
132f08c3bdfSopenharmony_ci	pagesize = getpagesize();
133f08c3bdfSopenharmony_ci
134f08c3bdfSopenharmony_ci	/* Writing 16 pages of random data into this file */
135f08c3bdfSopenharmony_ci	for (i = 0; i < (pagesize / 2); i++)
136f08c3bdfSopenharmony_ci		SAFE_WRITE(SAFE_WRITE_ALL, fd, STR, sizeof(STR) - 1);
137f08c3bdfSopenharmony_ci
138f08c3bdfSopenharmony_ci	SAFE_FSTAT(fd, &st);
139f08c3bdfSopenharmony_ci
140f08c3bdfSopenharmony_ci	file1 = SAFE_MMAP(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
141f08c3bdfSopenharmony_ci	file2 = SAFE_MMAP(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
142f08c3bdfSopenharmony_ci	file3 = SAFE_MMAP(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
143f08c3bdfSopenharmony_ci	shared_anon = SAFE_MMAP(0, MAP_SIZE, PROT_READ, MAP_SHARED |
144f08c3bdfSopenharmony_ci			MAP_ANONYMOUS, -1, 0);
145f08c3bdfSopenharmony_ci
146f08c3bdfSopenharmony_ci	nonalign = file1 + 100;
147f08c3bdfSopenharmony_ci
148f08c3bdfSopenharmony_ci	ptr_addr = SAFE_MALLOC(st.st_size);
149f08c3bdfSopenharmony_ci	tmp_addr = (void*)LTP_ALIGN((long)ptr_addr, pagesize);
150f08c3bdfSopenharmony_ci
151f08c3bdfSopenharmony_ci	/* unmap as last step to avoid subsequent mmap(s) pick same address */
152f08c3bdfSopenharmony_ci	SAFE_MUNMAP(file2 + st.st_size - pagesize, pagesize);
153f08c3bdfSopenharmony_ci	SAFE_CLOSE(fd);
154f08c3bdfSopenharmony_ci
155f08c3bdfSopenharmony_ci	tcases_filter();
156f08c3bdfSopenharmony_ci}
157f08c3bdfSopenharmony_ci
158f08c3bdfSopenharmony_ci
159f08c3bdfSopenharmony_cistatic void advice_test(unsigned int i)
160f08c3bdfSopenharmony_ci{
161f08c3bdfSopenharmony_ci	struct tcase *tc = &tcases[i];
162f08c3bdfSopenharmony_ci
163f08c3bdfSopenharmony_ci	if (tc->skip == 1) {
164f08c3bdfSopenharmony_ci		tst_res(TCONF, "%s is not supported", tc->name);
165f08c3bdfSopenharmony_ci		return;
166f08c3bdfSopenharmony_ci	}
167f08c3bdfSopenharmony_ci
168f08c3bdfSopenharmony_ci	TEST(madvise(*(tc->addr), st.st_size, tc->advice));
169f08c3bdfSopenharmony_ci	if (TST_RET == -1) {
170f08c3bdfSopenharmony_ci		if (TST_ERR == tc->exp_errno) {
171f08c3bdfSopenharmony_ci			tst_res(TPASS | TTERRNO, "%s failed as expected", tc->name);
172f08c3bdfSopenharmony_ci		} else {
173f08c3bdfSopenharmony_ci			tst_res(TFAIL | TTERRNO,
174f08c3bdfSopenharmony_ci					"%s failed unexpectedly; expected - %d : %s",
175f08c3bdfSopenharmony_ci					tc->name, tc->exp_errno,
176f08c3bdfSopenharmony_ci					tst_strerrno(TFAIL | TTERRNO));
177f08c3bdfSopenharmony_ci		}
178f08c3bdfSopenharmony_ci	} else {
179f08c3bdfSopenharmony_ci		tst_res(TFAIL, "madvise succeeded unexpectedly");
180f08c3bdfSopenharmony_ci	}
181f08c3bdfSopenharmony_ci}
182f08c3bdfSopenharmony_ci
183f08c3bdfSopenharmony_cistatic void cleanup(void)
184f08c3bdfSopenharmony_ci{
185f08c3bdfSopenharmony_ci	free(ptr_addr);
186f08c3bdfSopenharmony_ci	SAFE_MUNMAP(file1, st.st_size);
187f08c3bdfSopenharmony_ci	SAFE_MUNMAP(file2, st.st_size - pagesize);
188f08c3bdfSopenharmony_ci	SAFE_MUNMAP(file3, st.st_size);
189f08c3bdfSopenharmony_ci	SAFE_MUNMAP(shared_anon, MAP_SIZE);
190f08c3bdfSopenharmony_ci}
191f08c3bdfSopenharmony_ci
192f08c3bdfSopenharmony_cistatic struct tst_test test = {
193f08c3bdfSopenharmony_ci	.tcnt = ARRAY_SIZE(tcases),
194f08c3bdfSopenharmony_ci	.test = advice_test,
195f08c3bdfSopenharmony_ci	.needs_tmpdir = 1,
196f08c3bdfSopenharmony_ci	.needs_root = 1,
197f08c3bdfSopenharmony_ci	.setup = setup,
198f08c3bdfSopenharmony_ci	.cleanup = cleanup,
199f08c3bdfSopenharmony_ci};
200