1 /* 2 * Copyright (c) 2016 Red Hat, Inc. 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 /* 19 * DESCRIPTION 20 * 21 * Page fault occurs in spite that madvise(WILLNEED) system call is called 22 * to prefetch the page. This issue is reproduced by running a program 23 * which sequentially accesses to a shared memory and calls madvise(WILLNEED) 24 * to the next page on a page fault. 25 * 26 * This bug is present in all RHEL7 versions. It looks like this was fixed in 27 * mainline kernel > v3.15 by the following patch: 28 * 29 * commit 55231e5c898c5c03c14194001e349f40f59bd300 30 * Author: Johannes Weiner <hannes (at) cmpxchg.org> 31 * Date: Thu May 22 11:54:17 2014 -0700 32 * 33 * mm: madvise: fix MADV_WILLNEED on shmem swapouts 34 */ 35 36 #include <errno.h> 37 #include <sys/sysinfo.h> 38 #include "tst_test.h" 39 40 #define GB_SZ (1024*1024*1024) 41 42 static long dst_max; 43 static int pg_sz; 44 45 static void setup(void) 46 { 47 struct sysinfo sys_buf; 48 49 sysinfo(&sys_buf); 50 51 if (sys_buf.totalram < 2L * GB_SZ) 52 tst_brk(TCONF, "Test requires more than 2GB of RAM"); 53 if (sys_buf.totalram > 100L * GB_SZ) 54 tst_brk(TCONF, "System RAM is too large, skip test"); 55 56 dst_max = sys_buf.totalram / GB_SZ; 57 tst_res(TINFO, "dst_max = %ld", dst_max); 58 59 pg_sz = getpagesize(); 60 } 61 62 static int get_page_fault_num(void) 63 { 64 int pg; 65 66 SAFE_FILE_SCANF("/proc/self/stat", 67 "%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %d", 68 &pg); 69 70 return pg; 71 } 72 73 static void test_advice_willneed(void) 74 { 75 int i; 76 char *src; 77 char *dst[100]; 78 int page_fault_num_1; 79 int page_fault_num_2; 80 81 /* allocate source memory (1GB only) */ 82 src = SAFE_MMAP(NULL, 1 * GB_SZ, PROT_READ | PROT_WRITE, 83 MAP_SHARED | MAP_ANONYMOUS, 84 -1, 0); 85 86 /* allocate destination memory (array) */ 87 for (i = 0; i < dst_max; ++i) 88 dst[i] = SAFE_MMAP(NULL, 1 * GB_SZ, 89 PROT_READ | PROT_WRITE, 90 MAP_SHARED | MAP_ANONYMOUS, 91 -1, 0); 92 93 /* memmove source to each destination memories (for SWAP-OUT) */ 94 for (i = 0; i < dst_max; ++i) 95 memmove(dst[i], src, 1 * GB_SZ); 96 97 tst_res(TINFO, "PageFault(no madvice): %d", get_page_fault_num()); 98 99 /* Do madvice() to dst[0] */ 100 TEST(madvise(dst[0], pg_sz, MADV_WILLNEED)); 101 if (TEST_RETURN == -1) 102 tst_brk(TBROK | TERRNO, "madvise failed"); 103 104 page_fault_num_1 = get_page_fault_num(); 105 tst_res(TINFO, "PageFault(madvice / no mem access): %d", 106 page_fault_num_1); 107 108 *dst[0] = 'a'; 109 page_fault_num_2 = get_page_fault_num(); 110 tst_res(TINFO, "PageFault(madvice / mem access): %d", 111 page_fault_num_2); 112 113 if (page_fault_num_1 != page_fault_num_2) 114 tst_res(TFAIL, "Bug has been reproduced"); 115 else 116 tst_res(TPASS, "Regression test pass"); 117 118 SAFE_MUNMAP(src, 1 * GB_SZ); 119 for (i = 0; i < dst_max; ++i) 120 SAFE_MUNMAP(dst[i], 1 * GB_SZ); 121 } 122 123 static struct tst_test test = { 124 .tid = "madvice06", 125 .test_all = test_advice_willneed, 126 .setup = setup, 127 }; 128