|
19 | 19 | * Date: Thu May 22 11:54:17 2014 -0700
|
20 | 20 | *
|
21 | 21 | * mm: madvise: fix MADV_WILLNEED on shmem swapouts
|
| 22 | + * |
| 23 | + * Two checks are performed, the first looks at how SwapCache |
| 24 | + * changes during madvise. When the pages are dirtied, about half |
| 25 | + * will be accounted for under Cached and the other half will be |
| 26 | + * moved into Swap. When madvise is run it will cause the pages |
| 27 | + * under Cached to also be moved to Swap while rotating the pages |
| 28 | + * already in Swap into SwapCached. So we expect that SwapCached has |
| 29 | + * roughly MEM_LIMIT bytes added to it, but for reliability the |
| 30 | + * PASS_THRESHOLD is much lower than that. |
| 31 | + * |
| 32 | + * Secondly we run madvise again, but only on the first |
| 33 | + * PASS_THRESHOLD bytes to ensure these are entirely in RAM. Then we |
| 34 | + * dirty these pages and check there were (almost) no page |
| 35 | + * faults. Two faults are allowed incase some tasklet or something |
| 36 | + * else unexpected, but irrelevant procedure, registers a fault to |
| 37 | + * our process. |
| 38 | + * |
22 | 39 | */
|
23 | 40 |
|
24 | 41 | #include <errno.h>
|
|
28 | 45 | #include "tst_test.h"
|
29 | 46 |
|
30 | 47 | #define CHUNK_SZ (400*1024*1024L)
|
31 |
| -#define CHUNK_PAGES (CHUNK_SZ / pg_sz) |
| 48 | +#define MEM_LIMIT (CHUNK_SZ / 2) |
| 49 | +#define MEMSW_LIMIT (2 * CHUNK_SZ) |
32 | 50 | #define PASS_THRESHOLD (CHUNK_SZ / 4)
|
| 51 | +#define PASS_THRESHOLD_KB (PASS_THRESHOLD / 1024) |
33 | 52 |
|
34 | 53 | #define MNT_NAME "memory"
|
35 | 54 | #define GROUP_NAME "madvise06"
|
36 | 55 |
|
37 | 56 | static const char drop_caches_fname[] = "/proc/sys/vm/drop_caches";
|
38 | 57 | static int pg_sz;
|
39 | 58 |
|
| 59 | +static long init_swap, init_swap_cached, init_cached; |
| 60 | + |
40 | 61 | static void check_path(const char *path)
|
41 | 62 | {
|
42 | 63 | if (access(path, R_OK | W_OK))
|
43 | 64 | tst_brk(TCONF, "file needed: %s\n", path);
|
44 | 65 | }
|
45 | 66 |
|
| 67 | +#define READ_CGMEM(item) \ |
| 68 | + ({long tst_rval; \ |
| 69 | + SAFE_FILE_LINES_SCANF(MNT_NAME"/"GROUP_NAME"/memory."item, \ |
| 70 | + "%ld", \ |
| 71 | + &tst_rval); \ |
| 72 | + tst_rval;}) |
| 73 | + |
| 74 | +static void meminfo_diag(const char *point) |
| 75 | +{ |
| 76 | + FILE_PRINTF("/proc/sys/vm/stat_refresh", "1"); |
| 77 | + tst_res(TINFO, "%s", point); |
| 78 | + tst_res(TINFO, "\tSwap: %ld Kb", |
| 79 | + SAFE_READ_MEMINFO("SwapTotal:") - SAFE_READ_MEMINFO("SwapFree:") - init_swap); |
| 80 | + tst_res(TINFO, "\tSwapCached: %ld Kb", |
| 81 | + SAFE_READ_MEMINFO("SwapCached:") - init_swap_cached); |
| 82 | + tst_res(TINFO, "\tCached: %ld Kb", |
| 83 | + SAFE_READ_MEMINFO("Cached:") - init_cached); |
| 84 | + tst_res(TINFO, "\tcgmem.usage_in_bytes: %ld Kb", |
| 85 | + READ_CGMEM("usage_in_bytes") / 1024); |
| 86 | + tst_res(TINFO, "\tcgmem.memsw.usage_in_bytes: %ld Kb", |
| 87 | + READ_CGMEM("memsw.usage_in_bytes") / 1024); |
| 88 | + tst_res(TINFO, "\tcgmem.kmem.usage_in_bytes: %ld Kb", |
| 89 | + READ_CGMEM("kmem.usage_in_bytes") / 1024); |
| 90 | +} |
| 91 | + |
46 | 92 | static void setup(void)
|
47 | 93 | {
|
48 | 94 | struct sysinfo sys_buf_start;
|
@@ -77,9 +123,19 @@ static void setup(void)
|
77 | 123 |
|
78 | 124 | SAFE_FILE_PRINTF("/proc/self/oom_score_adj", "%d", -1000);
|
79 | 125 | SAFE_FILE_PRINTF(MNT_NAME"/"GROUP_NAME"/memory.limit_in_bytes", "%ld\n",
|
80 |
| - PASS_THRESHOLD); |
| 126 | + MEM_LIMIT); |
| 127 | + SAFE_FILE_PRINTF(MNT_NAME"/"GROUP_NAME"/memory.memsw.limit_in_bytes", "%ld\n", |
| 128 | + MEMSW_LIMIT); |
81 | 129 | SAFE_FILE_PRINTF(MNT_NAME"/"GROUP_NAME"/memory.swappiness", "60");
|
82 | 130 | SAFE_FILE_PRINTF(MNT_NAME"/"GROUP_NAME"/tasks", "%d\n", getpid());
|
| 131 | + |
| 132 | + meminfo_diag("Initial meminfo, later values are relative to this (except memcg)"); |
| 133 | + init_swap = SAFE_READ_MEMINFO("SwapTotal:") - SAFE_READ_MEMINFO("SwapFree:"); |
| 134 | + init_swap_cached = SAFE_READ_MEMINFO("SwapCached:"); |
| 135 | + init_cached = SAFE_READ_MEMINFO("Cached:"); |
| 136 | + |
| 137 | + tst_res(TINFO, "mapping %ld Kb (%ld pages), limit %ld Kb, pass threshold %ld Kb", |
| 138 | + CHUNK_SZ / 1024, CHUNK_SZ / pg_sz, MEM_LIMIT / 1024, PASS_THRESHOLD_KB); |
83 | 139 | }
|
84 | 140 |
|
85 | 141 | static void cleanup(void)
|
@@ -112,55 +168,60 @@ static int get_page_fault_num(void)
|
112 | 168 |
|
113 | 169 | static void test_advice_willneed(void)
|
114 | 170 | {
|
115 |
| - int loops = 50; |
| 171 | + int loops = 50, res; |
116 | 172 | char *target;
|
117 | 173 | long swapcached_start, swapcached;
|
118 | 174 | int page_fault_num_1, page_fault_num_2;
|
119 | 175 |
|
| 176 | + meminfo_diag("Before mmap"); |
| 177 | + tst_res(TINFO, "PageFault(before mmap): %d", get_page_fault_num()); |
120 | 178 | target = SAFE_MMAP(NULL, CHUNK_SZ, PROT_READ | PROT_WRITE,
|
121 | 179 | MAP_SHARED | MAP_ANONYMOUS,
|
122 | 180 | -1, 0);
|
| 181 | + meminfo_diag("Before dirty"); |
| 182 | + tst_res(TINFO, "PageFault(before dirty): %d", get_page_fault_num()); |
123 | 183 | dirty_pages(target, CHUNK_SZ);
|
| 184 | + tst_res(TINFO, "PageFault(after dirty): %d", get_page_fault_num()); |
124 | 185 |
|
| 186 | + meminfo_diag("Before madvise"); |
125 | 187 | SAFE_FILE_LINES_SCANF("/proc/meminfo", "SwapCached: %ld",
|
126 | 188 | &swapcached_start);
|
127 |
| - tst_res(TINFO, "SwapCached (before madvise): %ld", swapcached_start); |
128 | 189 |
|
129 |
| - TEST(madvise(target, CHUNK_SZ, MADV_WILLNEED)); |
| 190 | + TEST(madvise(target, MEM_LIMIT, MADV_WILLNEED)); |
130 | 191 | if (TST_RET == -1)
|
131 | 192 | tst_brk(TBROK | TERRNO, "madvise failed");
|
132 | 193 |
|
133 | 194 | do {
|
134 | 195 | loops--;
|
135 | 196 | usleep(100000);
|
| 197 | + FILE_PRINTF("/proc/sys/vm/stat_refresh", "1"); |
136 | 198 | SAFE_FILE_LINES_SCANF("/proc/meminfo", "SwapCached: %ld",
|
137 | 199 | &swapcached);
|
138 |
| - } while (swapcached < swapcached_start + PASS_THRESHOLD / 1024 |
139 |
| - && loops > 0); |
140 |
| - |
141 |
| - tst_res(TINFO, "SwapCached (after madvise): %ld", swapcached); |
142 |
| - if (swapcached > swapcached_start + PASS_THRESHOLD / 1024) { |
143 |
| - tst_res(TPASS, "Regression test pass"); |
144 |
| - SAFE_MUNMAP(target, CHUNK_SZ); |
145 |
| - return; |
146 |
| - } |
| 200 | + } while (swapcached < swapcached_start + PASS_THRESHOLD_KB && loops > 0); |
| 201 | + |
| 202 | + meminfo_diag("After madvise"); |
| 203 | + res = swapcached > swapcached_start + PASS_THRESHOLD_KB; |
| 204 | + tst_res(res ? TPASS : TFAIL, |
| 205 | + "%s than %ld Kb were moved to the swap cache", |
| 206 | + res ? "more" : "less", PASS_THRESHOLD_KB); |
| 207 | + |
| 208 | + |
| 209 | + TEST(madvise(target, PASS_THRESHOLD, MADV_WILLNEED)); |
| 210 | + if (TST_RET == -1) |
| 211 | + tst_brk(TBROK | TERRNO, "madvise failed"); |
147 | 212 |
|
148 |
| - /* |
149 |
| - * We may have hit a bug or we just have slow I/O, |
150 |
| - * try accessing first page. |
151 |
| - */ |
152 | 213 | page_fault_num_1 = get_page_fault_num();
|
153 | 214 | tst_res(TINFO, "PageFault(madvice / no mem access): %d",
|
154 | 215 | page_fault_num_1);
|
155 |
| - target[0] = 'a'; |
| 216 | + dirty_pages(target, PASS_THRESHOLD); |
156 | 217 | page_fault_num_2 = get_page_fault_num();
|
157 | 218 | tst_res(TINFO, "PageFault(madvice / mem access): %d",
|
158 | 219 | page_fault_num_2);
|
| 220 | + meminfo_diag("After page access"); |
159 | 221 |
|
160 |
| - if (page_fault_num_1 != page_fault_num_2) |
161 |
| - tst_res(TFAIL, "Bug has been reproduced"); |
162 |
| - else |
163 |
| - tst_res(TPASS, "Regression test pass"); |
| 222 | + res = page_fault_num_2 - page_fault_num_1; |
| 223 | + tst_res(res < 3 ? TPASS : TFAIL, |
| 224 | + "%d pages were faulted out of 2 max", res); |
164 | 225 |
|
165 | 226 | SAFE_MUNMAP(target, CHUNK_SZ);
|
166 | 227 | }
|
|
0 commit comments