/* IBM Corporation */ /* 01/02/2003 Port to LTP avenkat@us.ibm.com */ /* 06/30/2001 Port to Linux nsharoff@us.ibm.com */ /* * Copyright (c) International Business Machines Corp., 2003 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * This test mmaps over the tail of the brk segment, growing and * shrinking brk over holes, while changing from small to large and * large to small virtual memory representations. After mmaping over the * end of the brk segment, it increases the brk which should split * it into two segments (i.e. |---brk---|-mmap-|--more brk--|). Next it * decreases the brk segment to the end of the map, and finally decreases * it some more. Then more vmsegments are created by punching holes in * the brk segments with munmap. This should cause the vm system to use a * large virtual address space object to keep track of this process. The * above test is then repeated using the large process object. After * this, the brk is shrunk to less than 1 page before exiting in order to * test the code which compacts large address space objects. It also asks * for a huge mmap which is refused. */ #define _KMEMUSER #include <sys/types.h> #include <stdio.h> #include <sys/mman.h> #include <errno.h> #include <unistd.h> #include <limits.h> #include <stdlib.h> #include <stdint.h> #include "test.h" #include "tst_kernel.h" char *TCID = "mmapstress03"; FILE *temp; int TST_TOTAL = 1; int anyfail(); void ok_exit(); #define AS_SVSM_VSEG_MAX 48UL #define AS_SVSM_MMAP_MAX 16UL #define EXTRA_VSEGS 2L #define NUM_SEGS (AS_SVSM_VSEG_MAX + EXTRA_VSEGS) #define ERROR(M) (void)fprintf(stderr, "%s: errno = %d: " M "\n", TCID, \ errno) #define NEG1 (char *)-1 static void do_test(void* brk_max, long pagesize); int main(void) { char *brk_max_addr, *hole_addr, *brk_start, *hole_start; size_t pagesize = (size_t) sysconf(_SC_PAGE_SIZE); int kernel_bits = tst_kernel_bits(); if ((brk_start = sbrk(0)) == NEG1) { ERROR("initial sbrk failed"); anyfail(); } if ((u_long) brk_start % (u_long) pagesize) { if (sbrk(pagesize - ((u_long) brk_start % (u_long) pagesize)) == NEG1) { ERROR("couldn't round up brk to a page boundary"); anyfail(); } } /* The brk is now at the beginning of a page. */ if ((hole_addr = hole_start = sbrk(NUM_SEGS * 2 * pagesize)) == NEG1) { ERROR("couldn't brk large space for segments"); anyfail(); } if ((brk_max_addr = sbrk(0)) == NEG1) { ERROR("couldn't find top of brk"); anyfail(); } do_test((void*) brk_max_addr, pagesize); /* now make holes and repeat test */ while (hole_addr + pagesize < brk_max_addr) { if (munmap(hole_addr, pagesize) == -1) { ERROR("failed to munmap odd hole in brk segment"); anyfail(); } hole_addr += 2 * pagesize; } if (brk_max_addr != sbrk(0)) { ERROR("do_test should leave the top of brk where it began"); anyfail(); } do_test((void*) brk_max_addr, pagesize); /* Shrink brk */ if (sbrk(-NUM_SEGS * pagesize) == NEG1) { ERROR("couldn't brk back over holes"); anyfail(); } if ((brk_max_addr = sbrk(0)) == NEG1) { ERROR("couldn't find top of break again"); anyfail(); } /* sbrked over about half the holes */ hole_addr = hole_start + pagesize; /* munmap the other pages */ while (hole_addr + pagesize < brk_max_addr) { if (munmap(hole_addr, pagesize) == -1) { ERROR("failed to munmap even hole in brk segment"); anyfail(); } hole_addr += 2 * pagesize; } /* munmaped the rest of the brk except a little at the beginning */ if (brk(brk_start) == -1) { ERROR("failed to completely remove brk"); anyfail(); } if (sbrk(pagesize) == NEG1 || sbrk(-pagesize) == NEG1) { ERROR("failed to fiddle with brk at the end"); anyfail(); } /* Ask for a ridiculously large mmap region at a high address */ if (mmap((void*) (((uintptr_t)1) << ((sizeof(void*)<<3) - 1)) - pagesize, (size_t) ((1ULL << (kernel_bits - 1)) - pagesize), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_FIXED | MAP_SHARED, 0, 0) != (void*) - 1) { ERROR("really large mmap didn't fail"); anyfail(); } if (errno != ENOMEM && errno != EINVAL) { ERROR("really large mmap didn't set errno = ENOMEM nor EINVAL"); anyfail(); } ok_exit(); tst_exit(); } /* * do_test assumes that brk_max is a multiple of pagesize */ static void do_test(void* brk_max, long pagesize) { if (mmap((void*) ((long)brk_max - 3 * pagesize), (2 * pagesize), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, 0, 0) == (void*) - 1) { ERROR("mmap failed"); anyfail(); } /* extend mmap */ if (mmap((void*) ((long)brk_max - 2 * pagesize), (2 * pagesize), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, 0, 0) == (void*) - 1) { ERROR("mmap failed"); anyfail(); } if (sbrk(pagesize) == NEG1) { ERROR("sbrk failed to grow over mmaped region"); anyfail(); } if (sbrk(-pagesize) == NEG1) { ERROR("sbrk failed to shrink back to mmaped region"); anyfail(); } if (sbrk(-pagesize) == NEG1) { ERROR("sbrk failed to shrink over mmaped region more"); anyfail(); } if (sbrk(-pagesize) == NEG1) { ERROR("sbrk failed to shrink some more"); anyfail(); } if (sbrk(2 * pagesize) == NEG1) { ERROR("sbrk failed to change brk segment to original size"); anyfail(); } } void ok_exit(void) { tst_resm(TPASS, "Test passed"); tst_exit(); } int anyfail(void) { tst_brkm(TFAIL, NULL, "Test failed"); }