1 /* 2 * Copyright (c) 2015-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 */ 14 15 /* 16 * DESCRIPTION 17 * 18 * There is a race condition if we map a same file on different processes. 19 * Region tracking is protected by mmap_sem and hugetlb_instantiation_mutex. 20 * When we do mmap, we don't grab a hugetlb_instantiation_mutex, but only 21 * mmap_sem (exclusively). This doesn't prevent other tasks from modifying 22 * the region structure, so it can be modified by two processes concurrently. 23 * 24 * This bug was fixed on stable kernel by commits: 25 * f522c3ac00(mm, hugetlb: change variable name reservations to resv) 26 * 9119a41e90(mm, hugetlb: unify region structure handling) 27 * 7b24d8616b(mm, hugetlb: fix race in region tracking) 28 * 1406ec9ba6(mm, hugetlb: improve, cleanup resv_map parameters) 29 * 30 * AUTHOR: 31 * Herton R. Krzesinski <herton (at) redhat.com> 32 * Li Wang <liwang (at) redhat.com> 33 */ 34 35 #define _GNU_SOURCE 36 #include <pthread.h> 37 #include <stdio.h> 38 #include "hugetlb.h" 39 #include "lapi/mmap.h" 40 41 static long hpage_size; 42 static long hugepages; 43 44 struct mp { 45 char *addr; 46 int sz; 47 }; 48 49 #define ARSZ 50 50 #define LOOP 5 51 52 static void setup(void) 53 { 54 save_nr_hugepages(); 55 56 hpage_size = SAFE_READ_MEMINFO("Hugepagesize:") * 1024; 57 58 hugepages = (ARSZ + 1) * LOOP; 59 60 if (hugepages * SAFE_READ_MEMINFO("Hugepagesize:") > SAFE_READ_MEMINFO("MemTotal:")) 61 tst_brk(TCONF, "System RAM is not enough to test."); 62 63 set_sys_tune("nr_hugepages", hugepages, 1); 64 } 65 66 static void cleanup(void) 67 { 68 restore_nr_hugepages(); 69 } 70 71 static void *thr(void *arg) 72 { 73 struct mp *mmap_sz = arg; 74 int i, lim, a, b, c; 75 76 srand(time(NULL)); 77 lim = rand() % 10; 78 for (i = 0; i < lim; i++) { 79 a = rand() % mmap_sz->sz; 80 for (c = 0; c <= a; c++) { 81 b = rand() % mmap_sz->sz; 82 *(mmap_sz->addr + b * hpage_size) = rand(); 83 } 84 } 85 return NULL; 86 } 87 88 static void do_mmap(unsigned int j LTP_ATTRIBUTE_UNUSED) 89 { 90 int i, sz = ARSZ + 1; 91 void *addr, *new_addr; 92 struct mp mmap_sz[ARSZ]; 93 pthread_t tid[ARSZ]; 94 95 addr = mmap(NULL, sz * hpage_size, 96 PROT_READ | PROT_WRITE, 97 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, 98 -1, 0); 99 100 if (addr == MAP_FAILED) { 101 if (errno == ENOMEM) { 102 tst_brk(TCONF, 103 "Cannot allocate hugepage, memory too fragmented?"); 104 } 105 106 tst_brk(TBROK | TERRNO, "Cannot allocate hugepage"); 107 } 108 109 for (i = 0; i < ARSZ; ++i, --sz) { 110 mmap_sz[i].sz = sz; 111 mmap_sz[i].addr = addr; 112 113 TEST(pthread_create(&tid[i], NULL, thr, &mmap_sz[i])); 114 if (TST_RET) 115 tst_brk(TBROK | TRERRNO, 116 "pthread_create failed"); 117 118 new_addr = mmap(addr, (sz - 1) * hpage_size, 119 PROT_READ | PROT_WRITE, 120 MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_FIXED, 121 -1, 0); 122 123 if (new_addr == MAP_FAILED) 124 tst_brk(TFAIL | TERRNO, "mmap failed"); 125 126 addr = new_addr; 127 } 128 129 for (i = 0; i < ARSZ; ++i) { 130 TEST(pthread_join(tid[i], NULL)); 131 if (TST_RET) 132 tst_brk(TBROK | TRERRNO, 133 "pthread_join failed"); 134 } 135 136 if (munmap(addr, sz * hpage_size) == -1) 137 tst_brk(TFAIL | TERRNO, "huge munmap failed"); 138 139 tst_res(TPASS, "No regression found."); 140 } 141 142 static struct tst_test test = { 143 .min_kver = "2.6.32", 144 .needs_root = 1, 145 .tcnt = LOOP, 146 .needs_tmpdir = 1, 147 .test = do_mmap, 148 .setup = setup, 149 .cleanup = cleanup, 150 }; 151