• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2015 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 3 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 the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /*
19  * DESCRIPTION
20  *
21  *   There is a race condition if we map a same file on different processes.
22  *   Region tracking is protected by mmap_sem and hugetlb_instantiation_mutex.
23  *   When we do mmap, we don't grab a hugetlb_instantiation_mutex, but only
24  *   mmap_sem (exclusively).  This doesn't prevent other tasks from modifying
25  *   the region structure, so it can be modified by two processes concurrently.
26  *
27  *   This bug was fixed on stable kernel by commits:
28  *       f522c3ac00(mm, hugetlb: change variable name reservations to resv)
29  *       9119a41e90(mm, hugetlb: unify region structure handling)
30  *       7b24d8616b(mm, hugetlb: fix race in region tracking)
31  *       1406ec9ba6(mm, hugetlb: improve, cleanup resv_map parameters)
32  *
33  * AUTHOR:
34  *    Herton R. Krzesinski <herton@redhat.com>
35  *    Li Wang <liwang@redhat.com>
36  */
37 
38 #define _GNU_SOURCE
39 #include <errno.h>
40 #include <pthread.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <sys/mman.h>
44 #include <sys/types.h>
45 #include <unistd.h>
46 
47 #include "test.h"
48 #include "mem.h"
49 #include "hugetlb.h"
50 #include "lapi/mmap.h"
51 
52 char *TCID = "hugemmap06";
53 int TST_TOTAL = 5;
54 
55 static long hpage_size;
56 static long hugepages;
57 
58 struct mp {
59 	char *addr;
60 	int sz;
61 };
62 
63 #define ARSZ 50
64 
setup(void)65 void setup(void)
66 {
67 	tst_require_root();
68 	check_hugepage();
69 
70 	/* MAP_HUGETLB check */
71 	if ((tst_kvercmp(2, 6, 32)) < 0) {
72 		tst_brkm(TCONF, NULL, "This test can only run on kernels "
73 			"that are 2.6.32 or higher");
74 	}
75 
76 	hpage_size = read_meminfo("Hugepagesize:") * 1024;
77 	orig_hugepages = get_sys_tune("nr_hugepages");
78 
79 	hugepages = (ARSZ + 1) * TST_TOTAL;
80 
81 	if (hugepages * read_meminfo("Hugepagesize:") > read_meminfo("MemTotal:"))
82 		tst_brkm(TCONF, NULL, "System RAM is not enough to test.");
83 
84 	set_sys_tune("nr_hugepages", hugepages, 1);
85 
86 	TEST_PAUSE;
87 }
88 
cleanup(void)89 void cleanup(void)
90 {
91 	set_sys_tune("nr_hugepages", orig_hugepages, 0);
92 }
93 
thr(void * arg)94 void *thr(void *arg)
95 {
96 	struct mp *mmap_sz = arg;
97 	int i, lim, a, b, c;
98 
99 	srand(time(NULL));
100 	lim = rand() % 10;
101 	for (i = 0; i < lim; i++) {
102 		a = rand() % mmap_sz->sz;
103 		for (c = 0; c <= a; c++) {
104 			b = rand() % mmap_sz->sz;
105 			*(mmap_sz->addr + b * hpage_size) = rand();
106 		}
107 	}
108 	return NULL;
109 }
110 
do_mmap(void)111 void do_mmap(void)
112 {
113 	int i, sz = ARSZ + 1;
114 	void *addr, *new_addr;
115 	struct mp mmap_sz[ARSZ];
116 	pthread_t tid[ARSZ];
117 
118 	addr = mmap(NULL, sz * hpage_size,
119 			PROT_READ | PROT_WRITE,
120 			MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
121 			-1, 0);
122 
123 	if (addr == MAP_FAILED) {
124 		if (errno == ENOMEM) {
125 			tst_brkm(TCONF, cleanup,
126 				"Cannot allocate hugepage, memory too fragmented?");
127 		}
128 
129 		tst_brkm(TBROK | TERRNO, cleanup, "Cannot allocate hugepage");
130 	}
131 
132 	for (i = 0; i < ARSZ; ++i, --sz) {
133 		mmap_sz[i].sz = sz;
134 		mmap_sz[i].addr = addr;
135 
136 		TEST(pthread_create(&tid[i], NULL, thr, &mmap_sz[i]));
137 		if (TEST_RETURN)
138 			tst_brkm(TBROK | TRERRNO, cleanup,
139 					"pthread_create failed");
140 
141 		new_addr = mmap(addr, (sz - 1) * hpage_size,
142 				PROT_READ | PROT_WRITE,
143 				MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_FIXED,
144 				-1, 0);
145 
146 		if (new_addr == MAP_FAILED)
147 			tst_brkm(TFAIL | TERRNO, cleanup, "mmap failed");
148 
149 		addr = new_addr;
150 	}
151 
152 	for (i = 0; i < ARSZ; ++i) {
153 		TEST(pthread_join(tid[i], NULL));
154 		if (TEST_RETURN)
155 			tst_brkm(TBROK | TRERRNO, cleanup,
156 					"pthread_join failed");
157 	}
158 
159 	if (munmap(addr, sz * hpage_size) == -1)
160 		tst_brkm(TFAIL | TERRNO, cleanup, "huge munmap failed");
161 }
162 
main(int ac,char ** av)163 int main(int ac, char **av)
164 {
165 	int lc, i;
166 
167 	tst_parse_opts(ac, av, NULL, NULL);
168 
169 	setup();
170 
171 	for (lc = 0; TEST_LOOPING(lc); lc++) {
172 		tst_count = 0;
173 
174 		for (i = 0; i < TST_TOTAL; i++)
175 			do_mmap();
176 
177 		tst_resm(TPASS, "No regression found.");
178 	}
179 
180 	cleanup();
181 	tst_exit();
182 }
183