1 /* 2 * Copyright (C) 2012 Linux Test Project 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of version 2 of the GNU General Public 6 * License as published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it would be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 11 * 12 * Further, this software is distributed without any warranty that it 13 * is free of the rightful claim of any third person regarding 14 * infringement or the like. Any license provided herein, whether 15 * implied or otherwise, applies only to this software file. Patent 16 * licenses, if any, provided herein do not apply to combinations of 17 * this program with other software, or any other product whatsoever. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write the Free Software 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 22 * 02110-1301, USA. 23 */ 24 /* 25 * There are several corner cases (documented in mm/mmap.c) for mbind 26 * vma merge issue, which makes commit 8aacc9f550 slightly incorrect. 27 * KOSAKI Motohiro made a patch for it (commit e26a511) and composed 28 * a reproducer containing these corner cases. Now I port it to LTP. 29 * 30 * Author: KOSAKI Motohiro <kosaki.motohiro (at) jp.fujitsu.com> 31 * Ported-to-LTP-by: Caspar Zhang <caspar (at) casparzhang.com> 32 */ 33 34 #include "config.h" 35 #include <sys/types.h> 36 #include <sys/mman.h> 37 #include <errno.h> 38 #if HAVE_NUMA_H 39 #include <numa.h> 40 #endif 41 #if HAVE_NUMAIF_H 42 #include <numaif.h> 43 #endif 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <unistd.h> 48 #include "test.h" 49 #include "safe_macros.h" 50 #include "numa_helper.h" 51 52 char *TCID = "vma04"; 53 int TST_TOTAL = 5; 54 55 #if HAVE_NUMA_H && HAVE_LINUX_MEMPOLICY_H && HAVE_NUMAIF_H \ 56 && HAVE_MPOL_CONSTANTS 57 #if defined(LIBNUMA_API_VERSION) && LIBNUMA_API_VERSION == 2 58 static unsigned long pagesize; 59 static int opt_node; 60 static char *opt_nodestr; 61 static char retbuf[BUFSIZ]; 62 static void *mmap_addr; 63 static struct bitmask *nmask; 64 65 static option_t options[] = { 66 {"n:", &opt_node, &opt_nodestr}, 67 {NULL, NULL, NULL} 68 }; 69 70 static void init(void); 71 static void fin(void); 72 static void mem_bind(int index, int len); 73 static void mem_interleave(int index, int len); 74 static void mem_unbind(int index, int len); 75 static void assertion(char *expected, char *value, char *name); 76 static void get_vmas(char *retbuf, void *addr_s, void *addr_e); 77 static void case4(void); 78 static void case5(void); 79 static void case6(void); 80 static void case7(void); 81 static void case8(void); 82 static void setup(void); 83 static void cleanup(void); 84 static void usage(void); 85 86 int main(int argc, char **argv) 87 { 88 int lc, node, err; 89 90 tst_parse_opts(argc, argv, options, usage); 91 92 nmask = numa_allocate_nodemask(); 93 if (opt_node) { 94 node = SAFE_STRTOL(NULL, opt_nodestr, 1, LONG_MAX); 95 } else { 96 err = get_allowed_nodes(NH_MEMS | NH_MEMS, 1, &node); 97 if (err == -3) 98 tst_brkm(TCONF, NULL, "requires at least one node."); 99 else if (err < 0) 100 tst_brkm(TBROK | TERRNO, NULL, "get_allowed_nodes"); 101 } 102 numa_bitmask_setbit(nmask, node); 103 104 setup(); 105 106 for (lc = 0; TEST_LOOPING(lc); lc++) { 107 tst_count = 0; 108 109 case4(); 110 case5(); 111 case6(); 112 case7(); 113 case8(); 114 } 115 116 cleanup(); 117 tst_exit(); 118 } 119 120 /* 121 * BBBBBB 122 * AAAAAAAA 123 */ 124 static void init(void) 125 { 126 void *addr; 127 128 addr = SAFE_MMAP(cleanup, NULL, pagesize * 8, PROT_NONE, 129 MAP_ANON | MAP_PRIVATE, 0, 0); 130 SAFE_MMAP(cleanup, addr + pagesize, pagesize * 6, 131 PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE | MAP_FIXED, 0, 132 0); 133 134 mmap_addr = addr + pagesize; 135 memset(mmap_addr, 0, pagesize * 6); 136 } 137 138 static void fin(void) 139 { 140 void *addr; 141 142 addr = mmap_addr - pagesize; 143 SAFE_MUNMAP(cleanup, addr, pagesize * 8); 144 145 memset(retbuf, 0, sizeof(retbuf)); 146 } 147 148 static void mem_bind(int index, int len) 149 { 150 if (mbind(mmap_addr + pagesize * index, pagesize * len, 151 MPOL_BIND, nmask->maskp, nmask->size, 0) != 0) { 152 if (errno != ENOSYS) 153 tst_brkm(TBROK | TERRNO, cleanup, "mbind: bind"); 154 else 155 tst_brkm(TCONF, cleanup, 156 "mbind syscall not implemented " 157 "on this system."); 158 } 159 } 160 161 static void mem_interleave(int index, int len) 162 { 163 if (mbind(mmap_addr + pagesize * index, pagesize * len, 164 MPOL_INTERLEAVE, nmask->maskp, nmask->size, 0) != 0) { 165 if (errno != ENOSYS) 166 tst_brkm(TBROK | TERRNO, cleanup, "mbind: interleave"); 167 else 168 tst_brkm(TCONF, cleanup, 169 "mbind syscall not implemented " 170 "on this system."); 171 } 172 } 173 174 static void mem_unbind(int index, int len) 175 { 176 if (mbind(mmap_addr + pagesize * index, pagesize * len, 177 MPOL_DEFAULT, NULL, 0, 0) != 0) { 178 if (errno != ENOSYS) 179 tst_brkm(TBROK | TERRNO, cleanup, "mbind: unbind"); 180 else 181 tst_brkm(TCONF, cleanup, 182 "mbind syscall not implemented " 183 "on this system."); 184 } 185 } 186 187 static void assertion(char *expected, char *value, char *name) 188 { 189 if (strcmp(expected, value) == 0) 190 tst_resm(TPASS, "%s: passed.", name); 191 else 192 tst_resm(TFAIL, "%s: failed. expect '%s', actual '%s'", 193 name, expected, value); 194 } 195 196 static void get_vmas(char *retbuf, void *addr_s, void *addr_e) 197 { 198 FILE *fp; 199 void *s, *t; 200 char buf[BUFSIZ], tmpstr[BUFSIZ]; 201 int flag; 202 203 retbuf[0] = '\0'; 204 flag = 0; 205 fp = fopen("/proc/self/maps", "r"); 206 if (fp == NULL) 207 tst_brkm(TBROK | TERRNO, cleanup, "fopen"); 208 while (fgets(buf, BUFSIZ, fp) != NULL) { 209 if (sscanf(buf, "%p-%p ", &s, &t) != 2) 210 continue; 211 if (addr_s <= s && s < addr_e) { 212 if (!flag) { 213 sprintf(tmpstr, "%ld", (t - s) / pagesize); 214 flag = 1; 215 } else { 216 sprintf(tmpstr, ",%ld", (t - s) / pagesize); 217 } 218 strncat(retbuf, tmpstr, 32); 219 } 220 } 221 fclose(fp); 222 } 223 224 /* 225 * AAAA 226 * PPPPPPNNNNNN 227 * might become 228 * PPNNNNNNNNNN 229 * case 4 below 230 */ 231 static void case4(void) 232 { 233 init(); 234 mem_bind(0, 4); 235 mem_unbind(2, 2); 236 get_vmas(retbuf, mmap_addr, mmap_addr + pagesize * 6); 237 assertion("2,4", retbuf, "case4"); 238 fin(); 239 } 240 241 /* 242 * AAAA 243 * PPPPPPNNNNNN 244 * might become 245 * PPPPPPPPPPNN 246 * case 5 below 247 */ 248 static void case5(void) 249 { 250 init(); 251 mem_bind(0, 2); 252 mem_bind(2, 2); 253 get_vmas(retbuf, mmap_addr, mmap_addr + pagesize * 6); 254 assertion("4,2", retbuf, "case5"); 255 fin(); 256 } 257 258 /* 259 * AAAA 260 * PPPPNNNNXXXX 261 * might become 262 * PPPPPPPPPPPP 6 263 */ 264 static void case6(void) 265 { 266 init(); 267 mem_bind(0, 2); 268 mem_bind(4, 2); 269 mem_bind(2, 2); 270 get_vmas(retbuf, mmap_addr, mmap_addr + pagesize * 6); 271 assertion("6", retbuf, "case6"); 272 fin(); 273 } 274 275 /* 276 * AAAA 277 * PPPPNNNNXXXX 278 * might become 279 * PPPPPPPPXXXX 7 280 */ 281 static void case7(void) 282 { 283 init(); 284 mem_bind(0, 2); 285 mem_interleave(4, 2); 286 mem_bind(2, 2); 287 get_vmas(retbuf, mmap_addr, mmap_addr + pagesize * 6); 288 assertion("4,2", retbuf, "case7"); 289 fin(); 290 } 291 292 /* 293 * AAAA 294 * PPPPNNNNXXXX 295 * might become 296 * PPPPNNNNNNNN 8 297 */ 298 static void case8(void) 299 { 300 init(); 301 mem_bind(0, 2); 302 mem_interleave(4, 2); 303 mem_interleave(2, 2); 304 get_vmas(retbuf, mmap_addr, mmap_addr + pagesize * 6); 305 assertion("2,4", retbuf, "case8"); 306 fin(); 307 } 308 309 static void setup(void) 310 { 311 tst_sig(FORK, DEF_HANDLER, cleanup); 312 313 TEST_PAUSE; 314 315 pagesize = getpagesize(); 316 } 317 318 static void cleanup(void) 319 { 320 } 321 322 static void usage(void) 323 { 324 printf(" -n Number of NUMA nodes\n"); 325 } 326 327 #else /* libnuma v1 */ 328 int main(void) 329 { 330 tst_brkm(TCONF, NULL, "XXX: test is only supported on libnuma v2."); 331 } 332 #endif 333 #else /* no NUMA */ 334 int main(void) 335 { 336 tst_brkm(TCONF, NULL, "no NUMA development packages installed."); 337 } 338 #endif 339