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