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@jp.fujitsu.com> 31 * Ported-to-LTP-by: Caspar Zhang <caspar@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 <limits.h> 49#include "test.h" 50#include "safe_macros.h" 51#include "numa_helper.h" 52 53char *TCID = "vma04"; 54int TST_TOTAL = 5; 55 56#ifdef HAVE_NUMA_V2 57 58static unsigned long pagesize; 59static int opt_node; 60static char *opt_nodestr; 61static char retbuf[BUFSIZ]; 62static void *mmap_addr; 63static struct bitmask *nmask; 64 65static option_t options[] = { 66 {"n:", &opt_node, &opt_nodestr}, 67 {NULL, NULL, NULL} 68}; 69 70static void init(void); 71static void fin(void); 72static void mem_bind(int index, int len); 73static void mem_interleave(int index, int len); 74static void mem_unbind(int index, int len); 75static void assertion(char *expected, char *value, char *name); 76static void get_vmas(char *retbuf, void *addr_s, void *addr_e); 77static void case4(void); 78static void case5(void); 79static void case6(void); 80static void case7(void); 81static void case8(void); 82static void setup(void); 83static void cleanup(void); 84static void usage(void); 85 86int 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 */ 124static 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 138static 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 148static 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 161static 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 174static 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 187static 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 196static 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 */ 231static 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 */ 248static 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 */ 264static 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 */ 281static 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 */ 298static 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 309static void setup(void) 310{ 311 tst_sig(FORK, DEF_HANDLER, cleanup); 312 313 TEST_PAUSE; 314 315 pagesize = getpagesize(); 316} 317 318static void cleanup(void) 319{ 320} 321 322static void usage(void) 323{ 324 printf(" -n Number of NUMA nodes\n"); 325} 326 327#else 328int main(void) 329{ 330 tst_brkm(TCONF, NULL, "test requires libnuma >= 2 and it's development packages"); 331} 332#endif 333