1 /*
2 * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <gtest/gtest.h>
17 #include <fcntl.h>
18 #include <cinttypes>
19 #include <climits>
20 #include <cstdio>
21 #include <cstdlib>
22 #include <unistd.h>
23 #include <iomanip>
24 #include <iostream>
25 #include <string>
26 #include <vector>
27 #include <sys/mman.h>
28 #include <sys/utsname.h>
29 #include <string>
30 #include <memory.h>
31 #include "securec.h"
32
33 #define PAGE_SIZE 4096
34 #define NR_HUGEPAGES 10U
35 #define NR_OVERCOMMIT_HUGEPAGES 20U
36 #define HUGETLB_SHIFT 21
37 #define OVERCOMMIT_RATIO 1000000000
38 #define KB_SHIFT 10U
39 #define NUM 1024
40 using namespace testing::ext;
41 using namespace std;
42
43 class MmapVApiTest : public testing::Test {
44 public:
45 static void SetUpTestCase();
46 static void TearDownTestCase();
47 void SetUp();
48 void TearDown();
49 private:
50 };
SetUp()51 void MmapVApiTest::SetUp()
52 {
53 }
TearDown()54 void MmapVApiTest::TearDown()
55 {
56 }
SetUpTestCase()57 void MmapVApiTest::SetUpTestCase()
58 {
59 }
TearDownTestCase()60 void MmapVApiTest::TearDownTestCase()
61 {
62 }
63
64
65 enum {
66 OVERCOMMIT_GUESS,
67 OVERCOMMIT_NEVER,
68 };
69
70 static int g_overcommitPolicy;
71 static int g_overcommitRatio;
72
SetProc(string entry, int input)73 static void SetProc(string entry, int input)
74 {
75 FILE *fp = fopen(entry.c_str(), "w");
76 if (fp != nullptr) {
77 (void)fprintf(fp, "%d", input);
78 (void)fclose(fp);
79 }
80 }
81
FetchProc(string entry, int *output)82 static void FetchProc(string entry, int *output)
83 {
84 char buffer[NUM];
85 FILE *fp = fopen(entry.c_str(), "r");
86 if (fp != nullptr) {
87 int ret = snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, "%d", *output);
88 if (ret == 0) {
89 if (fputs(buffer, fp) == EOF) {
90 std::cout << "Error fputs .\n" << std::endl;
91 }
92 }
93 }
94 }
95
SetOvercommitMemory(int input)96 static void SetOvercommitMemory(int input)
97 {
98 SetProc("/proc/sys/vm/overcommit_memory", input);
99 }
100
FetchOvercommitMemory(int *output)101 static void FetchOvercommitMemory(int *output)
102 {
103 FetchProc("/proc/sys/vm/overcommit_memory", output);
104 }
105
SetOvercommitRatio(int input)106 static void SetOvercommitRatio(int input)
107 {
108 SetProc("/proc/sys/vm/overcommit_ratio", input);
109 }
110
FetchOvercommitRatio(int *output)111 static void FetchOvercommitRatio(int *output)
112 {
113 FetchProc("/proc/sys/vm/overcommit_ratio", output);
114 }
115
TstSmaps(void *va)116 static int TstSmaps(void *va)
117 {
118 FILE *fp;
119 char cmd[NUM];
120 char line[NUM];
121 char *p;
122 int rss = 0;
123 unsigned long ptr = reinterpret_cast<unsigned long>(va);
124 int ret = snprintf_s(cmd, sizeof(cmd), sizeof(cmd) - 1,
125 "cat /proc/%d/smaps |grep -A 10 '%lx-' |grep 'Rss:'", getpid(), ptr);
126 if (ret < 0) {
127 std::cout << "Error snprintf_s open .\n" << std::endl;
128 return ret;
129 }
130 fp = popen(cmd, "r");
131 if (fp == nullptr) {
132 std::cout << "Error popen open .\n" << std::endl;
133 return -1;
134 }
135 while (fgets(line, NUM, fp) != nullptr) {
136 p = strstr(line, "Rss:");
137 if (p != nullptr) {
138 int err = sscanf_s(p, "Rss: %d KB", &rss);
139 if (err < 0) {
140 std::cout << "Error sscanf_s open .\n" << std::endl;
141 return err;
142 }
143 break;
144 }
145 }
146 pclose(fp);
147 return rss;
148 }
149
ForceReclaim(void)150 static void ForceReclaim(void)
151 {
152 FILE *fp = fopen("/proc/self/reclaim", "w");
153 if (fp != nullptr) {
154 fputs("5", fp);
155 fclose(fp);
156 } else {
157 std::cout << "Error ForceReclaim open .\n" << std::endl;
158 ASSERT_TRUE(fp != nullptr);
159 }
160 }
161
162 /*
163 * @tc.number SUB_KERNEL_MEM_MAP_0600
164 * @tc.name MMAPNoreServe001
165 * @tc.desc mmap use MAP_NORESERVE without OVERCOMMIT_NEVER
166 */
HWTEST_F(MmapVApiTest, MMAPNoreServe001, Function | MediumTest | Level1)167 HWTEST_F(MmapVApiTest, MMAPNoreServe001, Function | MediumTest | Level1)
168 {
169 FetchOvercommitMemory(&g_overcommitPolicy);
170 ASSERT_TRUE(g_overcommitPolicy >= 0);
171 FetchOvercommitRatio(&g_overcommitRatio);
172 ASSERT_TRUE(g_overcommitRatio >= 0);
173 SetOvercommitRatio(OVERCOMMIT_RATIO);
174 SetOvercommitMemory(OVERCOMMIT_GUESS);
175 void *va = mmap(nullptr, (NR_OVERCOMMIT_HUGEPAGES << HUGETLB_SHIFT), PROT_READ | PROT_WRITE,
176 MAP_ANON | MAP_PRIVATE | MAP_HUGETLB | MAP_NORESERVE, -1, 0);
177 ASSERT_TRUE(va != MAP_FAILED);
178 int ret = munmap(va, (NR_OVERCOMMIT_HUGEPAGES << HUGETLB_SHIFT));
179 ASSERT_TRUE(ret == 0);
180 va = nullptr;
181 }
182
183 /*
184 * @tc.number SUB_KERNEL_MEM_MAP_0700
185 * @tc.name MMAPNoreServe002
186 * @tc.desc mmap use MAP_NORESERVE with OVERCOMMIT_NEVER
187 */
HWTEST_F(MmapVApiTest, MMAPNoreServe002, Function | MediumTest | Level1)188 HWTEST_F(MmapVApiTest, MMAPNoreServe002, Function | MediumTest | Level1)
189 {
190 FetchOvercommitMemory(&g_overcommitPolicy);
191 ASSERT_TRUE(g_overcommitPolicy >= 0);
192 FetchOvercommitRatio(&g_overcommitRatio);
193 ASSERT_TRUE(g_overcommitRatio >= 0);
194 SetOvercommitRatio(OVERCOMMIT_RATIO);
195 SetOvercommitMemory(OVERCOMMIT_NEVER);
196
197 void *va = mmap(nullptr, (NR_OVERCOMMIT_HUGEPAGES << HUGETLB_SHIFT), PROT_READ | PROT_WRITE,
198 MAP_ANON | MAP_PRIVATE | MAP_HUGETLB | MAP_NORESERVE, -1, 0);
199 ASSERT_TRUE(va != MAP_FAILED);
200 int ret = munmap(va, (NR_OVERCOMMIT_HUGEPAGES << HUGETLB_SHIFT));
201 ASSERT_TRUE(ret == 0);
202 va = nullptr;
203 }
204
205
206 /*
207 * @tc.number SUB_KERNEL_MEM_MAP_1100
208 * @tc.name MMAPNonBlock002
209 * @tc.desc mmap use MAP_NONBLOCK with MAP_POPULATE
210 */
HWTEST_F(MmapVApiTest, MMAPNonBlock002, Function | MediumTest | Level1)211 HWTEST_F(MmapVApiTest, MMAPNonBlock002, Function | MediumTest | Level1)
212 {
213 void *va = mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE,
214 MAP_ANON | MAP_PRIVATE | MAP_NONBLOCK | MAP_POPULATE, -1, 0);
215 ASSERT_TRUE(va != MAP_FAILED);
216 int ret = munmap(va, PAGE_SIZE);
217 ASSERT_TRUE(ret == 0);
218 va = nullptr;
219 }
220
221 /*
222 * @tc.number SUB_KERNEL_MEM_MAP_1200
223 * @tc.name mlockall001
224 * @tc.desc mlockall current pthread
225 */
HWTEST_F(MmapVApiTest, mlockall001, Function | MediumTest | Level1)226 HWTEST_F(MmapVApiTest, mlockall001, Function | MediumTest | Level1)
227 {
228 void *va = mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
229 ASSERT_TRUE(va != MAP_FAILED);
230 int rc = mlockall(MCL_CURRENT | MCL_ONFAULT);
231 ASSERT_TRUE(rc == 0);
232 int64_t rss = TstSmaps(va);
233 ASSERT_TRUE(rss == 0LL);
234 *(char *)va = 1;
235 ForceReclaim();
236 rss = TstSmaps(va);
237 ASSERT_TRUE(rss == (PAGE_SIZE >> KB_SHIFT));
238 rc = munlockall();
239 ASSERT_TRUE(rc == 0);
240 int ret = munmap(va, PAGE_SIZE);
241 ASSERT_TRUE(ret == 0);
242 va = nullptr;
243 }
244
245 /*
246 * @tc.number SUB_KERNEL_MEM_MAP_1300
247 * @tc.name mlockall002
248 * @tc.desc create vregion after mlockall
249 */
HWTEST_F(MmapVApiTest, mlockall002, Function | MediumTest | Level1)250 HWTEST_F(MmapVApiTest, mlockall002, Function | MediumTest | Level1)
251 {
252 int rc = mlockall(MCL_CURRENT | MCL_ONFAULT);
253 ASSERT_TRUE(rc == 0);
254 void *va = mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
255 ASSERT_TRUE(va != MAP_FAILED);
256 int64_t rss = TstSmaps(va);
257 ASSERT_TRUE(rss == 0LL);
258 *(char *)va = 1;
259 ForceReclaim();
260 rss = TstSmaps(va);
261 ASSERT_TRUE(rss == (PAGE_SIZE >> KB_SHIFT));
262 rc = munlockall();
263 ASSERT_TRUE(rc == 0);
264 int ret = munmap(va, PAGE_SIZE);
265 ASSERT_TRUE(ret == 0);
266 va = nullptr;
267 }