Home | History | Annotate | Download | only in vm
      1 /*
      2  *
      3  * Authors: Kirill A. Shutemov <kirill.shutemov (at) linux.intel.com>
      4  * Authors: Aneesh Kumar K.V <aneesh.kumar (at) linux.vnet.ibm.com>
      5  *
      6  * This program is free software; you can redistribute it and/or modify
      7  * it under the terms of the GNU General Public License, version 2, as
      8  * published by the Free Software Foundation.
      9 
     10  * This program is distributed in the hope that it would be useful, but
     11  * WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     13  *
     14  */
     15 
     16 #include <stdio.h>
     17 #include <sys/mman.h>
     18 #include <string.h>
     19 
     20 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
     21 
     22 #ifdef __powerpc64__
     23 #define PAGE_SIZE	(64 << 10)
     24 /*
     25  * This will work with 16M and 2M hugepage size
     26  */
     27 #define HUGETLB_SIZE	(16 << 20)
     28 #else
     29 #define PAGE_SIZE	(4 << 10)
     30 #define HUGETLB_SIZE	(2 << 20)
     31 #endif
     32 
     33 /*
     34  * >= 128TB is the hint addr value we used to select
     35  * large address space.
     36  */
     37 #define ADDR_SWITCH_HINT (1UL << 47)
     38 #define LOW_ADDR	((void *) (1UL << 30))
     39 #define HIGH_ADDR	((void *) (1UL << 48))
     40 
     41 struct testcase {
     42 	void *addr;
     43 	unsigned long size;
     44 	unsigned long flags;
     45 	const char *msg;
     46 	unsigned int low_addr_required:1;
     47 	unsigned int keep_mapped:1;
     48 };
     49 
     50 static struct testcase testcases[] = {
     51 	{
     52 		/*
     53 		 * If stack is moved, we could possibly allocate
     54 		 * this at the requested address.
     55 		 */
     56 		.addr = ((void *)(ADDR_SWITCH_HINT - PAGE_SIZE)),
     57 		.size = PAGE_SIZE,
     58 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
     59 		.msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, PAGE_SIZE)",
     60 		.low_addr_required = 1,
     61 	},
     62 	{
     63 		/*
     64 		 * We should never allocate at the requested address or above it
     65 		 * The len cross the 128TB boundary. Without MAP_FIXED
     66 		 * we will always search in the lower address space.
     67 		 */
     68 		.addr = ((void *)(ADDR_SWITCH_HINT - PAGE_SIZE)),
     69 		.size = 2 * PAGE_SIZE,
     70 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
     71 		.msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, (2 * PAGE_SIZE))",
     72 		.low_addr_required = 1,
     73 	},
     74 	{
     75 		/*
     76 		 * Exact mapping at 128TB, the area is free we should get that
     77 		 * even without MAP_FIXED.
     78 		 */
     79 		.addr = ((void *)(ADDR_SWITCH_HINT)),
     80 		.size = PAGE_SIZE,
     81 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
     82 		.msg = "mmap(ADDR_SWITCH_HINT, PAGE_SIZE)",
     83 		.keep_mapped = 1,
     84 	},
     85 	{
     86 		.addr = (void *)(ADDR_SWITCH_HINT),
     87 		.size = 2 * PAGE_SIZE,
     88 		.flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
     89 		.msg = "mmap(ADDR_SWITCH_HINT, 2 * PAGE_SIZE, MAP_FIXED)",
     90 	},
     91 	{
     92 		.addr = NULL,
     93 		.size = 2 * PAGE_SIZE,
     94 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
     95 		.msg = "mmap(NULL)",
     96 		.low_addr_required = 1,
     97 	},
     98 	{
     99 		.addr = LOW_ADDR,
    100 		.size = 2 * PAGE_SIZE,
    101 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
    102 		.msg = "mmap(LOW_ADDR)",
    103 		.low_addr_required = 1,
    104 	},
    105 	{
    106 		.addr = HIGH_ADDR,
    107 		.size = 2 * PAGE_SIZE,
    108 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
    109 		.msg = "mmap(HIGH_ADDR)",
    110 		.keep_mapped = 1,
    111 	},
    112 	{
    113 		.addr = HIGH_ADDR,
    114 		.size = 2 * PAGE_SIZE,
    115 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
    116 		.msg = "mmap(HIGH_ADDR) again",
    117 		.keep_mapped = 1,
    118 	},
    119 	{
    120 		.addr = HIGH_ADDR,
    121 		.size = 2 * PAGE_SIZE,
    122 		.flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
    123 		.msg = "mmap(HIGH_ADDR, MAP_FIXED)",
    124 	},
    125 	{
    126 		.addr = (void *) -1,
    127 		.size = 2 * PAGE_SIZE,
    128 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
    129 		.msg = "mmap(-1)",
    130 		.keep_mapped = 1,
    131 	},
    132 	{
    133 		.addr = (void *) -1,
    134 		.size = 2 * PAGE_SIZE,
    135 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
    136 		.msg = "mmap(-1) again",
    137 	},
    138 	{
    139 		.addr = ((void *)(ADDR_SWITCH_HINT - PAGE_SIZE)),
    140 		.size = PAGE_SIZE,
    141 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
    142 		.msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, PAGE_SIZE)",
    143 		.low_addr_required = 1,
    144 	},
    145 	{
    146 		.addr = (void *)(ADDR_SWITCH_HINT - PAGE_SIZE),
    147 		.size = 2 * PAGE_SIZE,
    148 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
    149 		.msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, 2 * PAGE_SIZE)",
    150 		.low_addr_required = 1,
    151 		.keep_mapped = 1,
    152 	},
    153 	{
    154 		.addr = (void *)(ADDR_SWITCH_HINT - PAGE_SIZE / 2),
    155 		.size = 2 * PAGE_SIZE,
    156 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
    157 		.msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE/2 , 2 * PAGE_SIZE)",
    158 		.low_addr_required = 1,
    159 		.keep_mapped = 1,
    160 	},
    161 	{
    162 		.addr = ((void *)(ADDR_SWITCH_HINT)),
    163 		.size = PAGE_SIZE,
    164 		.flags = MAP_PRIVATE | MAP_ANONYMOUS,
    165 		.msg = "mmap(ADDR_SWITCH_HINT, PAGE_SIZE)",
    166 	},
    167 	{
    168 		.addr = (void *)(ADDR_SWITCH_HINT),
    169 		.size = 2 * PAGE_SIZE,
    170 		.flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
    171 		.msg = "mmap(ADDR_SWITCH_HINT, 2 * PAGE_SIZE, MAP_FIXED)",
    172 	},
    173 };
    174 
    175 static struct testcase hugetlb_testcases[] = {
    176 	{
    177 		.addr = NULL,
    178 		.size = HUGETLB_SIZE,
    179 		.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
    180 		.msg = "mmap(NULL, MAP_HUGETLB)",
    181 		.low_addr_required = 1,
    182 	},
    183 	{
    184 		.addr = LOW_ADDR,
    185 		.size = HUGETLB_SIZE,
    186 		.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
    187 		.msg = "mmap(LOW_ADDR, MAP_HUGETLB)",
    188 		.low_addr_required = 1,
    189 	},
    190 	{
    191 		.addr = HIGH_ADDR,
    192 		.size = HUGETLB_SIZE,
    193 		.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
    194 		.msg = "mmap(HIGH_ADDR, MAP_HUGETLB)",
    195 		.keep_mapped = 1,
    196 	},
    197 	{
    198 		.addr = HIGH_ADDR,
    199 		.size = HUGETLB_SIZE,
    200 		.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
    201 		.msg = "mmap(HIGH_ADDR, MAP_HUGETLB) again",
    202 		.keep_mapped = 1,
    203 	},
    204 	{
    205 		.addr = HIGH_ADDR,
    206 		.size = HUGETLB_SIZE,
    207 		.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
    208 		.msg = "mmap(HIGH_ADDR, MAP_FIXED | MAP_HUGETLB)",
    209 	},
    210 	{
    211 		.addr = (void *) -1,
    212 		.size = HUGETLB_SIZE,
    213 		.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
    214 		.msg = "mmap(-1, MAP_HUGETLB)",
    215 		.keep_mapped = 1,
    216 	},
    217 	{
    218 		.addr = (void *) -1,
    219 		.size = HUGETLB_SIZE,
    220 		.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
    221 		.msg = "mmap(-1, MAP_HUGETLB) again",
    222 	},
    223 	{
    224 		.addr = (void *)(ADDR_SWITCH_HINT - PAGE_SIZE),
    225 		.size = 2 * HUGETLB_SIZE,
    226 		.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS,
    227 		.msg = "mmap(ADDR_SWITCH_HINT - PAGE_SIZE, 2*HUGETLB_SIZE, MAP_HUGETLB)",
    228 		.low_addr_required = 1,
    229 		.keep_mapped = 1,
    230 	},
    231 	{
    232 		.addr = (void *)(ADDR_SWITCH_HINT),
    233 		.size = 2 * HUGETLB_SIZE,
    234 		.flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
    235 		.msg = "mmap(ADDR_SWITCH_HINT , 2*HUGETLB_SIZE, MAP_FIXED | MAP_HUGETLB)",
    236 	},
    237 };
    238 
    239 static int run_test(struct testcase *test, int count)
    240 {
    241 	void *p;
    242 	int i, ret = 0;
    243 
    244 	for (i = 0; i < count; i++) {
    245 		struct testcase *t = test + i;
    246 
    247 		p = mmap(t->addr, t->size, PROT_READ | PROT_WRITE, t->flags, -1, 0);
    248 
    249 		printf("%s: %p - ", t->msg, p);
    250 
    251 		if (p == MAP_FAILED) {
    252 			printf("FAILED\n");
    253 			ret = 1;
    254 			continue;
    255 		}
    256 
    257 		if (t->low_addr_required && p >= (void *)(ADDR_SWITCH_HINT)) {
    258 			printf("FAILED\n");
    259 			ret = 1;
    260 		} else {
    261 			/*
    262 			 * Do a dereference of the address returned so that we catch
    263 			 * bugs in page fault handling
    264 			 */
    265 			memset(p, 0, t->size);
    266 			printf("OK\n");
    267 		}
    268 		if (!t->keep_mapped)
    269 			munmap(p, t->size);
    270 	}
    271 
    272 	return ret;
    273 }
    274 
    275 static int supported_arch(void)
    276 {
    277 #if defined(__powerpc64__)
    278 	return 1;
    279 #elif defined(__x86_64__)
    280 	return 1;
    281 #else
    282 	return 0;
    283 #endif
    284 }
    285 
    286 int main(int argc, char **argv)
    287 {
    288 	int ret;
    289 
    290 	if (!supported_arch())
    291 		return 0;
    292 
    293 	ret = run_test(testcases, ARRAY_SIZE(testcases));
    294 	if (argc == 2 && !strcmp(argv[1], "--run-hugetlb"))
    295 		ret = run_test(hugetlb_testcases, ARRAY_SIZE(hugetlb_testcases));
    296 	return ret;
    297 }
    298