Home | History | Annotate | Download | only in hugemmap
      1 /*
      2  * Copyright (C) 2010-2017  Red Hat, Inc.
      3  *
      4  * This program is free software;  you can redistribute it and/or modify
      5  * it under the terms of the GNU General Public License as published by
      6  * the Free Software Foundation; either version 2 of the License, or
      7  * (at your option) any later version.
      8  *
      9  * This program is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY;  without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
     12  * the GNU General Public License for more details.
     13  * overcommit hugetlbfs and check the statistics.
     14  *
     15  * Description:
     16  *
     17  * hugetlbfs allows to overcommit hugepages and there are tunables in
     18  * sysfs and procfs. The test here want to ensure it is possible to
     19  * overcommit by either mmap or shared memory. Also ensure those
     20  * reservation can be read/write, and several statistics work correctly.
     21  *
     22  * First, it resets nr_hugepages and nr_overcommit_hugepages. Then, set
     23  * both to a specify value - N, and allocate N + %50 x N hugepages.
     24  * Finally, it reads and writes every page. There are command options to
     25  * choose either to manage hugepages from sysfs or procfs, and reserve
     26  * them by mmap or shmget.
     27  */
     28 
     29 #include <string.h>
     30 #include <unistd.h>
     31 #include <stdio.h>
     32 #include "hugetlb.h"
     33 #include "tst_safe_sysv_ipc.h"
     34 #include "tst_test.h"
     35 
     36 #define PROTECTION		(PROT_READ | PROT_WRITE)
     37 #define PATH_MEMINFO		"/proc/meminfo"
     38 
     39 static char path_sys_sz[BUFSIZ];
     40 static char path_sys_sz_over[BUFSIZ];
     41 static char path_sys_sz_free[BUFSIZ];
     42 static char path_sys_sz_resv[BUFSIZ];
     43 static char path_sys_sz_surp[BUFSIZ];
     44 static char path_sys_sz_huge[BUFSIZ];
     45 
     46 #define PATH_PROC_VM		"/proc/sys/vm/"
     47 #define PATH_PROC_OVER		PATH_PROC_VM "nr_overcommit_hugepages"
     48 #define PATH_PROC_HUGE		PATH_PROC_VM "nr_hugepages"
     49 #define PATH_SHMMAX		"/proc/sys/kernel/shmmax"
     50 
     51 /* Only ia64 requires this */
     52 #ifdef __ia64__
     53 #define ADDR (void *)(0x8000000000000000UL)
     54 #define FLAGS (MAP_SHARED | MAP_FIXED)
     55 #define SHMAT_FLAGS (SHM_RND)
     56 #else
     57 #define ADDR (void *)(0x0UL)
     58 #define FLAGS (MAP_SHARED)
     59 #define SHMAT_FLAGS (0)
     60 #endif
     61 
     62 #ifndef SHM_HUGETLB
     63 #define SHM_HUGETLB 04000
     64 #endif
     65 
     66 #define MOUNT_DIR "hugemmap05"
     67 #define TEST_FILE MOUNT_DIR "/file"
     68 
     69 static unsigned long long shmmax;
     70 static char *path, *pathover;
     71 static int key = -1, shmid = -1, fd = -1;
     72 static int mounted, restore_shmmax, restore_nr_hgpgs, restore_overcomm_hgpgs;
     73 static long hugepagesize, nr_hugepages, nr_overcommit_hugepages;
     74 static long size = 128, length = 384;
     75 
     76 char *opt_sysfs;
     77 char *opt_alloc;
     78 char *opt_shmid;
     79 static struct tst_option options[] = {
     80 	{"s",  &opt_sysfs, "-s        Setup hugepages from sysfs"},
     81 	{"m",  &opt_shmid, "-m        Reserve hugepages by shmget"},
     82 	{"a:", &opt_alloc, "-a        Number of overcommint hugepages"},
     83 	{NULL, NULL, NULL}
     84 };
     85 
     86 static void check_wr_bytes(void *addr);
     87 static int checkproc(long act_val, char *string, long exp_val);
     88 static int checksys(char *path, char *pattern, long exp_val);
     89 static void init_sys_sz_paths(void);
     90 
     91 static void test_overcommit(void)
     92 {
     93 	void *addr = NULL, *shmaddr = NULL;
     94 
     95 	if (opt_shmid) {
     96 		shmid = SAFE_SHMGET(key, (length / 2 * hugepagesize),
     97 				 SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W);
     98 	} else {
     99 		fd = SAFE_OPEN(TEST_FILE, O_CREAT | O_RDWR, 0755);
    100 		addr = SAFE_MMAP(ADDR, (length / 2 * hugepagesize),
    101 				 PROTECTION, FLAGS, fd, 0);
    102 	}
    103 
    104 	if (opt_sysfs) {
    105 		tst_res(TINFO, "check sysfs before allocation.");
    106 		if (checksys(path_sys_sz_huge, "HugePages_Total", length / 2))
    107 			return;
    108 		if (checksys(path_sys_sz_free, "HugePages_Free", length / 2))
    109 			return;
    110 		if (checksys(path_sys_sz_surp, "HugePages_Surp",
    111 			     length / 2 - size))
    112 			return;
    113 		if (checksys(path_sys_sz_resv, "HugePages_Rsvd", length / 2))
    114 			return;
    115 	} else {
    116 		tst_res(TINFO, "check /proc/meminfo before allocation.");
    117 		if (checkproc(SAFE_READ_MEMINFO("HugePages_Total:"),
    118 			      "HugePages_Total", length / 2))
    119 			return;
    120 		if (checkproc(SAFE_READ_MEMINFO("HugePages_Free:"),
    121 			      "HugePages_Free", length / 2))
    122 			return;
    123 		if (checkproc(SAFE_READ_MEMINFO("HugePages_Surp:"),
    124 			      "HugePages_Surp", length / 2 - size))
    125 			return;
    126 		if (checkproc(SAFE_READ_MEMINFO("HugePages_Rsvd:"),
    127 			      "HugePages_Rsvd", length / 2))
    128 			return;
    129 	}
    130 
    131 	if (opt_shmid) {
    132 		tst_res(TINFO, "shmid: 0x%x", shmid);
    133 		shmaddr = SAFE_SHMAT(shmid, ADDR, SHMAT_FLAGS);
    134 		check_wr_bytes(shmaddr);
    135 	} else {
    136 		check_wr_bytes(addr);
    137 	}
    138 
    139 	if (opt_sysfs) {
    140 		tst_res(TINFO, "check sysfs.");
    141 		if (checksys(path_sys_sz_huge, "HugePages_Total", length / 2))
    142 			return;
    143 		if (checksys(path_sys_sz_free, "HugePages_Free", 0))
    144 			return;
    145 		if (checksys(path_sys_sz_surp, "HugePages_Surp",
    146 			     length / 2 - size))
    147 			return;
    148 		if (checksys(path_sys_sz_resv, "HugePages_Rsvd", 0))
    149 			return;
    150 	} else {
    151 		tst_res(TINFO, "check /proc/meminfo.");
    152 		if (checkproc(SAFE_READ_MEMINFO("HugePages_Total:"),
    153 			      "HugePages_Total", length / 2))
    154 			return;
    155 		if (checkproc(SAFE_READ_MEMINFO("HugePages_Free:"),
    156 			      "HugePages_Free", 0))
    157 			return;
    158 		if (checkproc(SAFE_READ_MEMINFO("HugePages_Surp:"),
    159 			      "HugePages_Surp", length / 2 - size))
    160 			return;
    161 		if (checkproc(SAFE_READ_MEMINFO("HugePages_Rsvd:"),
    162 			      "HugePages_Rsvd", 0))
    163 			return;
    164 	}
    165 
    166 	if (opt_shmid) {
    167 		SAFE_SHMDT(shmaddr);
    168 		SAFE_SHMCTL(shmid, IPC_RMID, NULL);
    169 	} else {
    170 		SAFE_MUNMAP(addr, (length / 2 * hugepagesize));
    171 		SAFE_CLOSE(fd);
    172 		SAFE_UNLINK(TEST_FILE);
    173 	}
    174 
    175 	tst_res(TPASS, "hugepages overcommit test pass");
    176 }
    177 
    178 static void cleanup(void)
    179 {
    180 	if (opt_shmid && shmid != -1)
    181 		SAFE_SHMCTL(shmid, IPC_RMID, NULL);
    182 
    183 	if (!opt_shmid && fd != -1) {
    184 		SAFE_CLOSE(fd);
    185 		SAFE_UNLINK(TEST_FILE);
    186 	}
    187 
    188 	if (mounted)
    189 		tst_umount(MOUNT_DIR);
    190 
    191 	if (restore_nr_hgpgs) {
    192 		tst_res(TINFO, "restore nr_hugepages to %ld.", nr_hugepages);
    193 		SAFE_FILE_PRINTF(path, "%ld", nr_hugepages);
    194 	}
    195 
    196 	if (restore_shmmax)
    197 		SAFE_FILE_PRINTF(PATH_SHMMAX, "%llu", shmmax);
    198 
    199 	if (restore_overcomm_hgpgs) {
    200 		tst_res(TINFO, "restore nr_overcommit_hugepages to %ld.",
    201 			nr_overcommit_hugepages);
    202 		SAFE_FILE_PRINTF(pathover, "%ld", nr_overcommit_hugepages);
    203 	}
    204 }
    205 
    206 static void setup(void)
    207 {
    208 	check_hugepage();
    209 	hugepagesize = SAFE_READ_MEMINFO("Hugepagesize:") * 1024;
    210 	init_sys_sz_paths();
    211 
    212 	if (opt_sysfs) {
    213 		path = path_sys_sz_huge;
    214 		pathover = path_sys_sz_over;
    215 	} else {
    216 		path = PATH_PROC_HUGE;
    217 		pathover = PATH_PROC_OVER;
    218 	}
    219 
    220 	if (opt_alloc) {
    221 		size = atoi(opt_alloc);
    222 		length = (size + size * 0.5) * 2;
    223 	}
    224 
    225 	if (opt_shmid) {
    226 		SAFE_FILE_SCANF(PATH_SHMMAX, "%llu", &shmmax);
    227 		if (shmmax < (unsigned long long)(length / 2 * hugepagesize)) {
    228 			restore_shmmax = 1;
    229 			SAFE_FILE_PRINTF(PATH_SHMMAX, "%ld",
    230 					(length / 2 * hugepagesize));
    231 		}
    232 	}
    233 
    234 	SAFE_FILE_SCANF(path, "%ld", &nr_hugepages);
    235 	tst_res(TINFO, "original nr_hugepages is %ld", nr_hugepages);
    236 
    237 	/* Reset. */
    238 	SAFE_FILE_PRINTF(path, "%ld", size);
    239 	restore_nr_hgpgs = 1;
    240 
    241 	if (access(pathover, F_OK)) {
    242 		tst_brk(TCONF, "file %s does not exist in the system",
    243 			pathover);
    244 	}
    245 
    246 	SAFE_FILE_SCANF(pathover, "%ld", &nr_overcommit_hugepages);
    247 	tst_res(TINFO, "original nr_overcommit_hugepages is %ld",
    248 		nr_overcommit_hugepages);
    249 
    250 	/* Reset. */
    251 	SAFE_FILE_PRINTF(pathover, "%ld", size);
    252 	restore_overcomm_hgpgs = 1;
    253 
    254 	SAFE_MKDIR(MOUNT_DIR, 0700);
    255 	SAFE_MOUNT(NULL, MOUNT_DIR, "hugetlbfs", 0, NULL);
    256 	mounted = 1;
    257 
    258 	if (opt_shmid) {
    259 		/* Use /proc/meminfo to generate an IPC key. */
    260 		key = ftok(PATH_MEMINFO, strlen(PATH_MEMINFO));
    261 		if (key == -1)
    262 			tst_brk(TBROK | TERRNO, "ftok");
    263 	}
    264 }
    265 
    266 static void check_wr_bytes(void *addr)
    267 {
    268 	long i;
    269 
    270 	memset((char *)addr, '\a', (length / 2 * hugepagesize));
    271 
    272 	tst_res(TINFO, "First hex is %x", *((unsigned int *)addr));
    273 	for (i = 0; i < (length / 2 * hugepagesize); i++) {
    274 		if (((char *)addr)[i] != '\a') {
    275 			tst_res(TFAIL, "mismatch at %ld", i);
    276 			break;
    277 		}
    278 	}
    279 }
    280 
    281 static int checksys(char *path, char *string, long exp_val)
    282 {
    283 	long act_val;
    284 
    285 	SAFE_FILE_SCANF(path, "%ld", &act_val);
    286 	tst_res(TINFO, "%s is %ld.", string, act_val);
    287 	if (act_val != exp_val) {
    288 		tst_res(TFAIL, "%s is not %ld but %ld.", string, exp_val,
    289 			act_val);
    290 		return 1;
    291 	}
    292 	return 0;
    293 }
    294 
    295 static int checkproc(long act_val, char *pattern, long exp_val)
    296 {
    297 	tst_res(TINFO, "%s is %ld.", pattern, act_val);
    298 	if (act_val != exp_val) {
    299 		tst_res(TFAIL, "%s is not %ld but %ld.",
    300 			pattern, exp_val, act_val);
    301 		return 1;
    302 	}
    303 	return 0;
    304 }
    305 
    306 static void init_sys_sz_paths(void)
    307 {
    308 	sprintf(path_sys_sz, "/sys/kernel/mm/hugepages/hugepages-%ldkB",
    309 		hugepagesize / 1024);
    310 	sprintf(path_sys_sz_over, "%s/nr_overcommit_hugepages", path_sys_sz);
    311 	sprintf(path_sys_sz_free, "%s/free_hugepages", path_sys_sz);
    312 	sprintf(path_sys_sz_resv, "%s/resv_hugepages", path_sys_sz);
    313 	sprintf(path_sys_sz_surp, "%s/surplus_hugepages", path_sys_sz);
    314 	sprintf(path_sys_sz_huge, "%s/nr_hugepages", path_sys_sz);
    315 }
    316 
    317 static struct tst_test test = {
    318 	.needs_root = 1,
    319 	.needs_tmpdir = 1,
    320 	.options = options,
    321 	.setup = setup,
    322 	.cleanup = cleanup,
    323 	.test_all = test_overcommit,
    324 };
    325