]> Git Repo - J-linux.git/blob - tools/testing/selftests/mm/mlock-random-test.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / tools / testing / selftests / mm / mlock-random-test.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * It tests the mlock/mlock2() when they are invoked
4  * on randomly memory region.
5  */
6 #include <unistd.h>
7 #include <sys/resource.h>
8 #include <sys/capability.h>
9 #include <sys/mman.h>
10 #include <linux/mman.h>
11 #include <fcntl.h>
12 #include <string.h>
13 #include <sys/ipc.h>
14 #include <sys/shm.h>
15 #include <time.h>
16 #include "../kselftest.h"
17 #include "mlock2.h"
18
19 #define CHUNK_UNIT (128 * 1024)
20 #define MLOCK_RLIMIT_SIZE (CHUNK_UNIT * 2)
21 #define MLOCK_WITHIN_LIMIT_SIZE CHUNK_UNIT
22 #define MLOCK_OUTOF_LIMIT_SIZE (CHUNK_UNIT * 3)
23
24 #define TEST_LOOP 100
25 #define PAGE_ALIGN(size, ps) (((size) + ((ps) - 1)) & ~((ps) - 1))
26
27 int set_cap_limits(rlim_t max)
28 {
29         struct rlimit new;
30         cap_t cap = cap_init();
31
32         new.rlim_cur = max;
33         new.rlim_max = max;
34         if (setrlimit(RLIMIT_MEMLOCK, &new)) {
35                 ksft_perror("setrlimit() returns error\n");
36                 return -1;
37         }
38
39         /* drop capabilities including CAP_IPC_LOCK */
40         if (cap_set_proc(cap)) {
41                 ksft_perror("cap_set_proc() returns error\n");
42                 return -1;
43         }
44
45         return 0;
46 }
47
48 int get_proc_locked_vm_size(void)
49 {
50         FILE *f;
51         int ret = -1;
52         char line[1024] = {0};
53         unsigned long lock_size = 0;
54
55         f = fopen("/proc/self/status", "r");
56         if (!f)
57                 ksft_exit_fail_msg("fopen: %s\n", strerror(errno));
58
59         while (fgets(line, 1024, f)) {
60                 if (strstr(line, "VmLck")) {
61                         ret = sscanf(line, "VmLck:\t%8lu kB", &lock_size);
62                         if (ret <= 0) {
63                                 fclose(f);
64                                 ksft_exit_fail_msg("sscanf() on VmLck error: %s: %d\n",
65                                                    line, ret);
66                         }
67                         fclose(f);
68                         return (int)(lock_size << 10);
69                 }
70         }
71
72         fclose(f);
73         ksft_exit_fail_msg("cannot parse VmLck in /proc/self/status: %s\n", strerror(errno));
74         return -1;
75 }
76
77 /*
78  * Get the MMUPageSize of the memory region including input
79  * address from proc file.
80  *
81  * return value: on error case, 0 will be returned.
82  * Otherwise the page size(in bytes) is returned.
83  */
84 int get_proc_page_size(unsigned long addr)
85 {
86         FILE *smaps;
87         char *line;
88         unsigned long mmupage_size = 0;
89         size_t size;
90
91         smaps = seek_to_smaps_entry(addr);
92         if (!smaps)
93                 ksft_exit_fail_msg("Unable to parse /proc/self/smaps\n");
94
95         while (getline(&line, &size, smaps) > 0) {
96                 if (!strstr(line, "MMUPageSize")) {
97                         free(line);
98                         line = NULL;
99                         size = 0;
100                         continue;
101                 }
102
103                 /* found the MMUPageSize of this section */
104                 if (sscanf(line, "MMUPageSize:    %8lu kB", &mmupage_size) < 1)
105                         ksft_exit_fail_msg("Unable to parse smaps entry for Size:%s\n",
106                                            line);
107
108         }
109         free(line);
110         if (smaps)
111                 fclose(smaps);
112         return mmupage_size << 10;
113 }
114
115 /*
116  * Test mlock/mlock2() on provided memory chunk.
117  * It expects the mlock/mlock2() to be successful (within rlimit)
118  *
119  * With allocated memory chunk [p, p + alloc_size), this
120  * test will choose start/len randomly to perform mlock/mlock2
121  * [start, start +  len] memory range. The range is within range
122  * of the allocated chunk.
123  *
124  * The memory region size alloc_size is within the rlimit.
125  * So we always expect a success of mlock/mlock2.
126  *
127  * VmLck is assumed to be 0 before this test.
128  *
129  *    return value: 0 - success
130  *    else: failure
131  */
132 static void test_mlock_within_limit(char *p, int alloc_size)
133 {
134         int i;
135         int ret = 0;
136         int locked_vm_size = 0;
137         struct rlimit cur;
138         int page_size = 0;
139
140         getrlimit(RLIMIT_MEMLOCK, &cur);
141         if (cur.rlim_cur < alloc_size)
142                 ksft_exit_fail_msg("alloc_size[%d] < %u rlimit,lead to mlock failure\n",
143                                    alloc_size, (unsigned int)cur.rlim_cur);
144
145         srand(time(NULL));
146         for (i = 0; i < TEST_LOOP; i++) {
147                 /*
148                  * - choose mlock/mlock2 randomly
149                  * - choose lock_size randomly but lock_size < alloc_size
150                  * - choose start_offset randomly but p+start_offset+lock_size
151                  *   < p+alloc_size
152                  */
153                 int is_mlock = !!(rand() % 2);
154                 int lock_size = rand() % alloc_size;
155                 int start_offset = rand() % (alloc_size - lock_size);
156
157                 if (is_mlock)
158                         ret = mlock(p + start_offset, lock_size);
159                 else
160                         ret = mlock2_(p + start_offset, lock_size,
161                                        MLOCK_ONFAULT);
162
163                 if (ret)
164                         ksft_exit_fail_msg("%s() failure at |%p(%d)| mlock:|%p(%d)|\n",
165                                            is_mlock ? "mlock" : "mlock2",
166                                            p, alloc_size,
167                                            p + start_offset, lock_size);
168         }
169
170         /*
171          * Check VmLck left by the tests.
172          */
173         locked_vm_size = get_proc_locked_vm_size();
174         page_size = get_proc_page_size((unsigned long)p);
175
176         if (locked_vm_size > PAGE_ALIGN(alloc_size, page_size) + page_size)
177                 ksft_exit_fail_msg("%s left VmLck:%d on %d chunk\n",
178                                    __func__, locked_vm_size, alloc_size);
179
180         ksft_test_result_pass("%s\n", __func__);
181 }
182
183
184 /*
185  * We expect the mlock/mlock2() to be fail (outof limitation)
186  *
187  * With allocated memory chunk [p, p + alloc_size), this
188  * test will randomly choose start/len and perform mlock/mlock2
189  * on [start, start+len] range.
190  *
191  * The memory region size alloc_size is above the rlimit.
192  * And the len to be locked is higher than rlimit.
193  * So we always expect a failure of mlock/mlock2.
194  * No locked page number should be increased as a side effect.
195  *
196  *    return value: 0 - success
197  *    else: failure
198  */
199 static void test_mlock_outof_limit(char *p, int alloc_size)
200 {
201         int i;
202         int ret = 0;
203         int locked_vm_size = 0, old_locked_vm_size = 0;
204         struct rlimit cur;
205
206         getrlimit(RLIMIT_MEMLOCK, &cur);
207         if (cur.rlim_cur >= alloc_size)
208                 ksft_exit_fail_msg("alloc_size[%d] >%u rlimit, violates test condition\n",
209                                    alloc_size, (unsigned int)cur.rlim_cur);
210
211         old_locked_vm_size = get_proc_locked_vm_size();
212         srand(time(NULL));
213         for (i = 0; i < TEST_LOOP; i++) {
214                 int is_mlock = !!(rand() % 2);
215                 int lock_size = (rand() % (alloc_size - cur.rlim_cur))
216                         + cur.rlim_cur;
217                 int start_offset = rand() % (alloc_size - lock_size);
218
219                 if (is_mlock)
220                         ret = mlock(p + start_offset, lock_size);
221                 else
222                         ret = mlock2_(p + start_offset, lock_size,
223                                         MLOCK_ONFAULT);
224                 if (ret == 0)
225                         ksft_exit_fail_msg("%s() succeeds? on %p(%d) mlock%p(%d)\n",
226                                            is_mlock ? "mlock" : "mlock2",
227                                            p, alloc_size, p + start_offset, lock_size);
228         }
229
230         locked_vm_size = get_proc_locked_vm_size();
231         if (locked_vm_size != old_locked_vm_size)
232                 ksft_exit_fail_msg("tests leads to new mlocked page: old[%d], new[%d]\n",
233                                    old_locked_vm_size,
234                                    locked_vm_size);
235
236         ksft_test_result_pass("%s\n", __func__);
237 }
238
239 int main(int argc, char **argv)
240 {
241         char *p = NULL;
242
243         ksft_print_header();
244
245         if (set_cap_limits(MLOCK_RLIMIT_SIZE))
246                 ksft_finished();
247
248         ksft_set_plan(2);
249
250         p = malloc(MLOCK_WITHIN_LIMIT_SIZE);
251         if (p == NULL)
252                 ksft_exit_fail_msg("malloc() failure: %s\n", strerror(errno));
253
254         test_mlock_within_limit(p, MLOCK_WITHIN_LIMIT_SIZE);
255         munlock(p, MLOCK_WITHIN_LIMIT_SIZE);
256         free(p);
257
258         p = malloc(MLOCK_OUTOF_LIMIT_SIZE);
259         if (p == NULL)
260                 ksft_exit_fail_msg("malloc() failure: %s\n", strerror(errno));
261
262         test_mlock_outof_limit(p, MLOCK_OUTOF_LIMIT_SIZE);
263         munlock(p, MLOCK_OUTOF_LIMIT_SIZE);
264         free(p);
265
266         ksft_finished();
267 }
This page took 0.042779 seconds and 4 git commands to generate.