1 /* 2 * Copyright (c) International Business Machines Corp., 2004 3 * Copyright (c) Linux Test Project, 2013-2016 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 13 * the GNU General Public License for more details. 14 */ 15 16 /* 17 * This is a test for the madvise(2) system call. It is intended 18 * to provide a complete exposure of the system call. It tests 19 * madvise(2) for all error conditions to occur correctly. 20 * 21 * (A) Test Case for EINVAL 22 * 1. start is not page-aligned 23 * 2. advice is not a valid value 24 * 3. application is attempting to release 25 * locked or shared pages (with MADV_DONTNEED) 26 * 4. MADV_MERGEABLE or MADV_UNMERGEABLE was specified in advice, 27 * but the kernel was not configured with CONFIG_KSM. 28 * 29 * (B) Test Case for ENOMEM 30 * 5|6. addresses in the specified range are not currently mapped 31 * or are outside the address space of the process 32 * b. Not enough memory - paging in failed 33 * 34 * (C) Test Case for EBADF 35 * 7. the map exists, 36 * but the area maps something that isn't a file. 37 */ 38 39 #include <sys/types.h> 40 #include <sys/mman.h> 41 #include <sys/resource.h> 42 #include <sys/stat.h> 43 #include <sys/time.h> 44 #include <errno.h> 45 #include <fcntl.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <unistd.h> 50 51 #include "tst_test.h" 52 #include "lapi/mmap.h" 53 54 #define TEST_FILE "testfile" 55 #define STR "abcdefghijklmnopqrstuvwxyz12345\n" 56 #define KSM_SYS_DIR "/sys/kernel/mm/ksm" 57 58 static struct stat st; 59 static long pagesize; 60 static char *file1; 61 static char *file2; 62 static char *ptr_addr; 63 static char *tmp_addr; 64 static char *nonalign; 65 66 static struct tcase { 67 int advice; 68 char *name; 69 char **addr; 70 int exp_errno; 71 int skip; 72 } tcases[] = { 73 {MADV_NORMAL, "MADV_NORMAL", &nonalign, EINVAL, 0}, 74 {1212, "MADV_NORMAL", &file1, EINVAL, 0}, 75 {MADV_DONTNEED, "MADV_DONTNEED", &file1, EINVAL, 1}, 76 {MADV_MERGEABLE, "MADV_MERGEABLE", &file1, EINVAL, 0}, 77 {MADV_UNMERGEABLE, "MADV_UNMERGEABLE", &file1, EINVAL, 0}, 78 {MADV_NORMAL, "MADV_NORMAL", &file2, ENOMEM, 0}, 79 {MADV_WILLNEED, "MADV_WILLNEED", &file2, ENOMEM, 0}, 80 {MADV_WILLNEED, "MADV_WILLNEED", &tmp_addr, EBADF, 0}, 81 }; 82 83 static void tcases_filter(void) 84 { 85 unsigned int i; 86 87 for (i = 0; i < ARRAY_SIZE(tcases); i++) { 88 struct tcase *tc = &tcases[i]; 89 90 switch (tc->advice) { 91 case MADV_DONTNEED: 92 #if !defined(UCLINUX) 93 if (mlock(file1, st.st_size) < 0) 94 tst_brk(TBROK | TERRNO, "mlock failed"); 95 tc->skip = 0; 96 #endif /* if !defined(UCLINUX) */ 97 break; 98 99 case MADV_MERGEABLE: 100 case MADV_UNMERGEABLE: 101 if ((tst_kvercmp(2, 6, 32)) < 0) 102 tc->skip = 1; 103 104 /* kernel configured with CONFIG_KSM, 105 * skip EINVAL test for MADV_MERGEABLE. */ 106 if (access(KSM_SYS_DIR, F_OK) == 0) 107 tc->skip = 1; 108 break; 109 case MADV_WILLNEED: 110 /* In kernel commit 1998cc0, madvise(MADV_WILLNEED) to 111 * anon mem doesn't return -EBADF now, as now we support 112 * swap prefretch. */ 113 if ((tst_kvercmp(3, 9, 0)) > 0 && 114 tc->exp_errno == EBADF) 115 tc->skip = 1; 116 break; 117 default: 118 break; 119 } 120 } 121 } 122 123 static void setup(void) 124 { 125 int i, fd; 126 127 fd = SAFE_OPEN(TEST_FILE, O_RDWR | O_CREAT, 0664); 128 129 pagesize = getpagesize(); 130 131 /* Writing 16 pages of random data into this file */ 132 for (i = 0; i < (pagesize / 2); i++) 133 SAFE_WRITE(1, fd, STR, sizeof(STR) - 1); 134 135 SAFE_FSTAT(fd, &st); 136 137 file1 = SAFE_MMAP(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0); 138 file2 = SAFE_MMAP(0, st.st_size, PROT_READ, MAP_SHARED, fd, 0); 139 SAFE_MUNMAP(file2 + st.st_size - pagesize, pagesize); 140 141 nonalign = file1 + 100; 142 143 ptr_addr = SAFE_MALLOC(st.st_size); 144 tmp_addr = (void*)LTP_ALIGN((long)ptr_addr, pagesize); 145 146 SAFE_CLOSE(fd); 147 148 tcases_filter(); 149 } 150 151 152 static void advice_test(unsigned int i) 153 { 154 struct tcase *tc = &tcases[i]; 155 156 if (tc->skip == 1) { 157 tst_res(TCONF, "%s is not supported", tc->name); 158 return; 159 } 160 161 TEST(madvise(*(tc->addr), st.st_size, tc->advice)); 162 if (TEST_RETURN == -1) { 163 if (TEST_ERRNO == tc->exp_errno) { 164 tst_res(TPASS | TTERRNO, "failed as expected"); 165 } else { 166 tst_res(TFAIL | TTERRNO, 167 "failed unexpectedly; expected - %d : %s", 168 tc->exp_errno, tst_strerrno(TFAIL | TTERRNO)); 169 } 170 } else { 171 tst_res(TFAIL, "madvise succeeded unexpectedly"); 172 } 173 } 174 175 static void cleanup(void) 176 { 177 free(ptr_addr); 178 munmap(file1, st.st_size); 179 munmap(file2, st.st_size - pagesize); 180 } 181 182 static struct tst_test test = { 183 .tid = "madvise02", 184 .tcnt = ARRAY_SIZE(tcases), 185 .test = advice_test, 186 .needs_tmpdir = 1, 187 .needs_root = 1, 188 .setup = setup, 189 .cleanup = cleanup, 190 }; 191