/* 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");
}