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 (at) redhat.com> 35 * Li Wang <liwang (at) 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 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 89 void cleanup(void) 90 { 91 set_sys_tune("nr_hugepages", orig_hugepages, 0); 92 } 93 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 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 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