~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/mm/virtual_address_range.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 /*
  3  * Copyright 2017, Anshuman Khandual, IBM Corp.
  4  *
  5  * Works on architectures which support 128TB virtual
  6  * address range and beyond.
  7  */
  8 #include <stdio.h>
  9 #include <stdlib.h>
 10 #include <string.h>
 11 #include <unistd.h>
 12 #include <errno.h>
 13 #include <sys/mman.h>
 14 #include <sys/time.h>
 15 #include <fcntl.h>
 16 
 17 #include "../kselftest.h"
 18 
 19 /*
 20  * Maximum address range mapped with a single mmap()
 21  * call is little bit more than 1GB. Hence 1GB is
 22  * chosen as the single chunk size for address space
 23  * mapping.
 24  */
 25 
 26 #define SZ_1GB  (1024 * 1024 * 1024UL)
 27 #define SZ_1TB  (1024 * 1024 * 1024 * 1024UL)
 28 
 29 #define MAP_CHUNK_SIZE  SZ_1GB
 30 
 31 /*
 32  * Address space till 128TB is mapped without any hint
 33  * and is enabled by default. Address space beyond 128TB
 34  * till 512TB is obtained by passing hint address as the
 35  * first argument into mmap() system call.
 36  *
 37  * The process heap address space is divided into two
 38  * different areas one below 128TB and one above 128TB
 39  * till it reaches 512TB. One with size 128TB and the
 40  * other being 384TB.
 41  *
 42  * On Arm64 the address space is 256TB and support for
 43  * high mappings up to 4PB virtual address space has
 44  * been added.
 45  */
 46 
 47 #define NR_CHUNKS_128TB   ((128 * SZ_1TB) / MAP_CHUNK_SIZE) /* Number of chunks for 128TB */
 48 #define NR_CHUNKS_256TB   (NR_CHUNKS_128TB * 2UL)
 49 #define NR_CHUNKS_384TB   (NR_CHUNKS_128TB * 3UL)
 50 #define NR_CHUNKS_3840TB  (NR_CHUNKS_128TB * 30UL)
 51 
 52 #define ADDR_MARK_128TB  (1UL << 47) /* First address beyond 128TB */
 53 #define ADDR_MARK_256TB  (1UL << 48) /* First address beyond 256TB */
 54 
 55 #ifdef __aarch64__
 56 #define HIGH_ADDR_MARK  ADDR_MARK_256TB
 57 #define HIGH_ADDR_SHIFT 49
 58 #define NR_CHUNKS_LOW   NR_CHUNKS_256TB
 59 #define NR_CHUNKS_HIGH  NR_CHUNKS_3840TB
 60 #else
 61 #define HIGH_ADDR_MARK  ADDR_MARK_128TB
 62 #define HIGH_ADDR_SHIFT 48
 63 #define NR_CHUNKS_LOW   NR_CHUNKS_128TB
 64 #define NR_CHUNKS_HIGH  NR_CHUNKS_384TB
 65 #endif
 66 
 67 static char *hind_addr(void)
 68 {
 69         int bits = HIGH_ADDR_SHIFT + rand() % (63 - HIGH_ADDR_SHIFT);
 70 
 71         return (char *) (1UL << bits);
 72 }
 73 
 74 static void validate_addr(char *ptr, int high_addr)
 75 {
 76         unsigned long addr = (unsigned long) ptr;
 77 
 78         if (high_addr && addr < HIGH_ADDR_MARK)
 79                 ksft_exit_fail_msg("Bad address %lx\n", addr);
 80 
 81         if (addr > HIGH_ADDR_MARK)
 82                 ksft_exit_fail_msg("Bad address %lx\n", addr);
 83 }
 84 
 85 static int validate_lower_address_hint(void)
 86 {
 87         char *ptr;
 88 
 89         ptr = mmap((void *) (1UL << 45), MAP_CHUNK_SIZE, PROT_READ |
 90                    PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
 91 
 92         if (ptr == MAP_FAILED)
 93                 return 0;
 94 
 95         return 1;
 96 }
 97 
 98 static int validate_complete_va_space(void)
 99 {
100         unsigned long start_addr, end_addr, prev_end_addr;
101         char line[400];
102         char prot[6];
103         FILE *file;
104         int fd;
105 
106         fd = open("va_dump", O_CREAT | O_WRONLY, 0600);
107         unlink("va_dump");
108         if (fd < 0) {
109                 ksft_test_result_skip("cannot create or open dump file\n");
110                 ksft_finished();
111         }
112 
113         file = fopen("/proc/self/maps", "r");
114         if (file == NULL)
115                 ksft_exit_fail_msg("cannot open /proc/self/maps\n");
116 
117         prev_end_addr = 0;
118         while (fgets(line, sizeof(line), file)) {
119                 unsigned long hop;
120 
121                 if (sscanf(line, "%lx-%lx %s[rwxp-]",
122                            &start_addr, &end_addr, prot) != 3)
123                         ksft_exit_fail_msg("cannot parse /proc/self/maps\n");
124 
125                 /* end of userspace mappings; ignore vsyscall mapping */
126                 if (start_addr & (1UL << 63))
127                         return 0;
128 
129                 /* /proc/self/maps must have gaps less than MAP_CHUNK_SIZE */
130                 if (start_addr - prev_end_addr >= MAP_CHUNK_SIZE)
131                         return 1;
132 
133                 prev_end_addr = end_addr;
134 
135                 if (prot[0] != 'r')
136                         continue;
137 
138                 /*
139                  * Confirm whether MAP_CHUNK_SIZE chunk can be found or not.
140                  * If write succeeds, no need to check MAP_CHUNK_SIZE - 1
141                  * addresses after that. If the address was not held by this
142                  * process, write would fail with errno set to EFAULT.
143                  * Anyways, if write returns anything apart from 1, exit the
144                  * program since that would mean a bug in /proc/self/maps.
145                  */
146                 hop = 0;
147                 while (start_addr + hop < end_addr) {
148                         if (write(fd, (void *)(start_addr + hop), 1) != 1)
149                                 return 1;
150                         lseek(fd, 0, SEEK_SET);
151 
152                         hop += MAP_CHUNK_SIZE;
153                 }
154         }
155         return 0;
156 }
157 
158 int main(int argc, char *argv[])
159 {
160         char *ptr[NR_CHUNKS_LOW];
161         char **hptr;
162         char *hint;
163         unsigned long i, lchunks, hchunks;
164 
165         ksft_print_header();
166         ksft_set_plan(1);
167 
168         for (i = 0; i < NR_CHUNKS_LOW; i++) {
169                 ptr[i] = mmap(NULL, MAP_CHUNK_SIZE, PROT_READ | PROT_WRITE,
170                               MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
171 
172                 if (ptr[i] == MAP_FAILED) {
173                         if (validate_lower_address_hint())
174                                 ksft_exit_fail_msg("mmap unexpectedly succeeded with hint\n");
175                         break;
176                 }
177 
178                 validate_addr(ptr[i], 0);
179         }
180         lchunks = i;
181         hptr = (char **) calloc(NR_CHUNKS_HIGH, sizeof(char *));
182         if (hptr == NULL) {
183                 ksft_test_result_skip("Memory constraint not fulfilled\n");
184                 ksft_finished();
185         }
186 
187         for (i = 0; i < NR_CHUNKS_HIGH; i++) {
188                 hint = hind_addr();
189                 hptr[i] = mmap(hint, MAP_CHUNK_SIZE, PROT_READ | PROT_WRITE,
190                                MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
191 
192                 if (hptr[i] == MAP_FAILED)
193                         break;
194 
195                 validate_addr(hptr[i], 1);
196         }
197         hchunks = i;
198         if (validate_complete_va_space()) {
199                 ksft_test_result_fail("BUG in mmap() or /proc/self/maps\n");
200                 ksft_finished();
201         }
202 
203         for (i = 0; i < lchunks; i++)
204                 munmap(ptr[i], MAP_CHUNK_SIZE);
205 
206         for (i = 0; i < hchunks; i++)
207                 munmap(hptr[i], MAP_CHUNK_SIZE);
208 
209         free(hptr);
210 
211         ksft_test_result_pass("Test\n");
212         ksft_finished();
213 }
214 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php