aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorUladzislau Rezki (Sony) <urezki@gmail.com>2019-03-05 18:43:34 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2019-03-06 00:07:15 -0500
commit3f21a6b7ef207892841feecc3b9216e1a29c745f (patch)
tree647f3e3c8eea81e484f145a0583479fbe7f1e235 /lib
parent153178edc7819b5c550e5d498d50697ff9d5f223 (diff)
vmalloc: add test driver to analyse vmalloc allocator
This adds a new kernel module for analysis of vmalloc allocator. It is only enabled as a module. There are two main reasons this module should be used for: performance evaluation and stressing of vmalloc subsystem. It consists of several test cases. As of now there are 8. The module has five parameters we can specify to change its the behaviour. 1) run_test_mask - set of tests to be run id: 1, name: fix_size_alloc_test id: 2, name: full_fit_alloc_test id: 4, name: long_busy_list_alloc_test id: 8, name: random_size_alloc_test id: 16, name: fix_align_alloc_test id: 32, name: random_size_align_alloc_test id: 64, name: align_shift_alloc_test id: 128, name: pcpu_alloc_test By default all tests are in run test mask. If you want to select some specific tests it is possible to pass the mask. For example for first, second and fourth tests we go 11 value. 2) test_repeat_count - how many times each test should be repeated By default it is one time per test. It is possible to pass any number. As high the value is the test duration gets increased. 3) test_loop_count - internal test loop counter. By default it is set to 1000000. 4) single_cpu_test - use one CPU to run the tests By default this parameter is set to false. It means that all online CPUs execute tests. By setting it to 1, the tests are executed by first online CPU only. 5) sequential_test_order - run tests in sequential order By default this parameter is set to false. It means that before running tests the order is shuffled. It is possible to make it sequential, just set it to 1. Performance analysis: In order to evaluate performance of vmalloc allocations, usually it makes sense to use only one CPU that runs tests, use sequential order, number of repeat tests can be different as well as set of test mask. For example if we want to run all tests, to use one CPU and repeat each test 3 times. Insert the module passing following parameters: single_cpu_test=1 sequential_test_order=1 test_repeat_count=3 with following output: <snip> Summary: fix_size_alloc_test passed: 3 failed: 0 repeat: 3 loops: 1000000 avg: 901177 usec Summary: full_fit_alloc_test passed: 3 failed: 0 repeat: 3 loops: 1000000 avg: 1039341 usec Summary: long_busy_list_alloc_test passed: 3 failed: 0 repeat: 3 loops: 1000000 avg: 11775763 usec Summary: random_size_alloc_test passed 3: failed: 0 repeat: 3 loops: 1000000 avg: 6081992 usec Summary: fix_align_alloc_test passed: 3 failed: 0 repeat: 3, loops: 1000000 avg: 2003712 usec Summary: random_size_align_alloc_test passed: 3 failed: 0 repeat: 3 loops: 1000000 avg: 2895689 usec Summary: align_shift_alloc_test passed: 0 failed: 3 repeat: 3 loops: 1000000 avg: 573 usec Summary: pcpu_alloc_test passed: 3 failed: 0 repeat: 3 loops: 1000000 avg: 95802 usec All test took CPU0=192945605995 cycles <snip> The align_shift_alloc_test is expected to be failed. Stressing: In order to stress the vmalloc subsystem we run all available test cases on all available CPUs simultaneously. In order to prevent constant behaviour pattern, the test cases array is shuffled by default to randomize the order of test execution. For example if we want to run all tests(default), use all online CPUs(default) with shuffled order(default) and to repeat each test 30 times. The command would be like: modprobe vmalloc_test test_repeat_count=30 Expected results are the system is alive, there are no any BUG_ONs or Kernel Panics the tests are completed, no memory leaks. [urezki@gmail.com: fix 32-bit builds] Link: http://lkml.kernel.org/r/20190106214839.ffvjvmrn52uqog7k@pc636 [urezki@gmail.com: make CONFIG_TEST_VMALLOC depend on CONFIG_MMU] Link: http://lkml.kernel.org/r/20190219085441.s6bg2gpy4esny5vw@pc636 Link: http://lkml.kernel.org/r/20190103142108.20744-3-urezki@gmail.com Signed-off-by: Uladzislau Rezki (Sony) <urezki@gmail.com> Cc: Kees Cook <keescook@chromium.org> Cc: Matthew Wilcox <willy@infradead.org> Cc: Michal Hocko <mhocko@suse.com> Cc: Oleksiy Avramchenko <oleksiy.avramchenko@sonymobile.com> Cc: Shuah Khan <shuah@kernel.org> Cc: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'lib')
-rw-r--r--lib/Kconfig.debug13
-rw-r--r--lib/Makefile1
-rw-r--r--lib/test_vmalloc.c551
3 files changed, 565 insertions, 0 deletions
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index a219f3488ad7..48f584393e28 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1875,6 +1875,19 @@ config TEST_LKM
1875 1875
1876 If unsure, say N. 1876 If unsure, say N.
1877 1877
1878config TEST_VMALLOC
1879 tristate "Test module for stress/performance analysis of vmalloc allocator"
1880 default n
1881 depends on MMU
1882 depends on m
1883 help
1884 This builds the "test_vmalloc" module that should be used for
1885 stress and performance analysis. So, any new change for vmalloc
1886 subsystem can be evaluated from performance and stability point
1887 of view.
1888
1889 If unsure, say N.
1890
1878config TEST_USER_COPY 1891config TEST_USER_COPY
1879 tristate "Test user/kernel boundary protections" 1892 tristate "Test user/kernel boundary protections"
1880 depends on m 1893 depends on m
diff --git a/lib/Makefile b/lib/Makefile
index e1b59da71418..cbfacd55aeca 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -60,6 +60,7 @@ UBSAN_SANITIZE_test_ubsan.o := y
60obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o 60obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o
61obj-$(CONFIG_TEST_LIST_SORT) += test_list_sort.o 61obj-$(CONFIG_TEST_LIST_SORT) += test_list_sort.o
62obj-$(CONFIG_TEST_LKM) += test_module.o 62obj-$(CONFIG_TEST_LKM) += test_module.o
63obj-$(CONFIG_TEST_VMALLOC) += test_vmalloc.o
63obj-$(CONFIG_TEST_OVERFLOW) += test_overflow.o 64obj-$(CONFIG_TEST_OVERFLOW) += test_overflow.o
64obj-$(CONFIG_TEST_RHASHTABLE) += test_rhashtable.o 65obj-$(CONFIG_TEST_RHASHTABLE) += test_rhashtable.o
65obj-$(CONFIG_TEST_SORT) += test_sort.o 66obj-$(CONFIG_TEST_SORT) += test_sort.o
diff --git a/lib/test_vmalloc.c b/lib/test_vmalloc.c
new file mode 100644
index 000000000000..83cdcaa82bf6
--- /dev/null
+++ b/lib/test_vmalloc.c
@@ -0,0 +1,551 @@
1// SPDX-License-Identifier: GPL-2.0
2
3/*
4 * Test module for stress and analyze performance of vmalloc allocator.
5 * (C) 2018 Uladzislau Rezki (Sony) <urezki@gmail.com>
6 */
7#include <linux/init.h>
8#include <linux/kernel.h>
9#include <linux/module.h>
10#include <linux/vmalloc.h>
11#include <linux/random.h>
12#include <linux/kthread.h>
13#include <linux/moduleparam.h>
14#include <linux/completion.h>
15#include <linux/delay.h>
16#include <linux/rwsem.h>
17#include <linux/mm.h>
18
19#define __param(type, name, init, msg) \
20 static type name = init; \
21 module_param(name, type, 0444); \
22 MODULE_PARM_DESC(name, msg) \
23
24__param(bool, single_cpu_test, false,
25 "Use single first online CPU to run tests");
26
27__param(bool, sequential_test_order, false,
28 "Use sequential stress tests order");
29
30__param(int, test_repeat_count, 1,
31 "Set test repeat counter");
32
33__param(int, test_loop_count, 1000000,
34 "Set test loop counter");
35
36__param(int, run_test_mask, INT_MAX,
37 "Set tests specified in the mask.\n\n"
38 "\t\tid: 1, name: fix_size_alloc_test\n"
39 "\t\tid: 2, name: full_fit_alloc_test\n"
40 "\t\tid: 4, name: long_busy_list_alloc_test\n"
41 "\t\tid: 8, name: random_size_alloc_test\n"
42 "\t\tid: 16, name: fix_align_alloc_test\n"
43 "\t\tid: 32, name: random_size_align_alloc_test\n"
44 "\t\tid: 64, name: align_shift_alloc_test\n"
45 "\t\tid: 128, name: pcpu_alloc_test\n"
46 /* Add a new test case description here. */
47);
48
49/*
50 * Depends on single_cpu_test parameter. If it is true, then
51 * use first online CPU to trigger a test on, otherwise go with
52 * all online CPUs.
53 */
54static cpumask_t cpus_run_test_mask = CPU_MASK_NONE;
55
56/*
57 * Read write semaphore for synchronization of setup
58 * phase that is done in main thread and workers.
59 */
60static DECLARE_RWSEM(prepare_for_test_rwsem);
61
62/*
63 * Completion tracking for worker threads.
64 */
65static DECLARE_COMPLETION(test_all_done_comp);
66static atomic_t test_n_undone = ATOMIC_INIT(0);
67
68static inline void
69test_report_one_done(void)
70{
71 if (atomic_dec_and_test(&test_n_undone))
72 complete(&test_all_done_comp);
73}
74
75static int random_size_align_alloc_test(void)
76{
77 unsigned long size, align, rnd;
78 void *ptr;
79 int i;
80
81 for (i = 0; i < test_loop_count; i++) {
82 get_random_bytes(&rnd, sizeof(rnd));
83
84 /*
85 * Maximum 1024 pages, if PAGE_SIZE is 4096.
86 */
87 align = 1 << (rnd % 23);
88
89 /*
90 * Maximum 10 pages.
91 */
92 size = ((rnd % 10) + 1) * PAGE_SIZE;
93
94 ptr = __vmalloc_node_range(size, align,
95 VMALLOC_START, VMALLOC_END,
96 GFP_KERNEL | __GFP_ZERO,
97 PAGE_KERNEL,
98 0, 0, __builtin_return_address(0));
99
100 if (!ptr)
101 return -1;
102
103 vfree(ptr);
104 }
105
106 return 0;
107}
108
109/*
110 * This test case is supposed to be failed.
111 */
112static int align_shift_alloc_test(void)
113{
114 unsigned long align;
115 void *ptr;
116 int i;
117
118 for (i = 0; i < BITS_PER_LONG; i++) {
119 align = ((unsigned long) 1) << i;
120
121 ptr = __vmalloc_node_range(PAGE_SIZE, align,
122 VMALLOC_START, VMALLOC_END,
123 GFP_KERNEL | __GFP_ZERO,
124 PAGE_KERNEL,
125 0, 0, __builtin_return_address(0));
126
127 if (!ptr)
128 return -1;
129
130 vfree(ptr);
131 }
132
133 return 0;
134}
135
136static int fix_align_alloc_test(void)
137{
138 void *ptr;
139 int i;
140
141 for (i = 0; i < test_loop_count; i++) {
142 ptr = __vmalloc_node_range(5 * PAGE_SIZE,
143 THREAD_ALIGN << 1,
144 VMALLOC_START, VMALLOC_END,
145 GFP_KERNEL | __GFP_ZERO,
146 PAGE_KERNEL,
147 0, 0, __builtin_return_address(0));
148
149 if (!ptr)
150 return -1;
151
152 vfree(ptr);
153 }
154
155 return 0;
156}
157
158static int random_size_alloc_test(void)
159{
160 unsigned int n;
161 void *p;
162 int i;
163
164 for (i = 0; i < test_loop_count; i++) {
165 get_random_bytes(&n, sizeof(i));
166 n = (n % 100) + 1;
167
168 p = vmalloc(n * PAGE_SIZE);
169
170 if (!p)
171 return -1;
172
173 *((__u8 *)p) = 1;
174 vfree(p);
175 }
176
177 return 0;
178}
179
180static int long_busy_list_alloc_test(void)
181{
182 void *ptr_1, *ptr_2;
183 void **ptr;
184 int rv = -1;
185 int i;
186
187 ptr = vmalloc(sizeof(void *) * 15000);
188 if (!ptr)
189 return rv;
190
191 for (i = 0; i < 15000; i++)
192 ptr[i] = vmalloc(1 * PAGE_SIZE);
193
194 for (i = 0; i < test_loop_count; i++) {
195 ptr_1 = vmalloc(100 * PAGE_SIZE);
196 if (!ptr_1)
197 goto leave;
198
199 ptr_2 = vmalloc(1 * PAGE_SIZE);
200 if (!ptr_2) {
201 vfree(ptr_1);
202 goto leave;
203 }
204
205 *((__u8 *)ptr_1) = 0;
206 *((__u8 *)ptr_2) = 1;
207
208 vfree(ptr_1);
209 vfree(ptr_2);
210 }
211
212 /* Success */
213 rv = 0;
214
215leave:
216 for (i = 0; i < 15000; i++)
217 vfree(ptr[i]);
218
219 vfree(ptr);
220 return rv;
221}
222
223static int full_fit_alloc_test(void)
224{
225 void **ptr, **junk_ptr, *tmp;
226 int junk_length;
227 int rv = -1;
228 int i;
229
230 junk_length = fls(num_online_cpus());
231 junk_length *= (32 * 1024 * 1024 / PAGE_SIZE);
232
233 ptr = vmalloc(sizeof(void *) * junk_length);
234 if (!ptr)
235 return rv;
236
237 junk_ptr = vmalloc(sizeof(void *) * junk_length);
238 if (!junk_ptr) {
239 vfree(ptr);
240 return rv;
241 }
242
243 for (i = 0; i < junk_length; i++) {
244 ptr[i] = vmalloc(1 * PAGE_SIZE);
245 junk_ptr[i] = vmalloc(1 * PAGE_SIZE);
246 }
247
248 for (i = 0; i < junk_length; i++)
249 vfree(junk_ptr[i]);
250
251 for (i = 0; i < test_loop_count; i++) {
252 tmp = vmalloc(1 * PAGE_SIZE);
253
254 if (!tmp)
255 goto error;
256
257 *((__u8 *)tmp) = 1;
258 vfree(tmp);
259 }
260
261 /* Success */
262 rv = 0;
263
264error:
265 for (i = 0; i < junk_length; i++)
266 vfree(ptr[i]);
267
268 vfree(ptr);
269 vfree(junk_ptr);
270
271 return rv;
272}
273
274static int fix_size_alloc_test(void)
275{
276 void *ptr;
277 int i;
278
279 for (i = 0; i < test_loop_count; i++) {
280 ptr = vmalloc(3 * PAGE_SIZE);
281
282 if (!ptr)
283 return -1;
284
285 *((__u8 *)ptr) = 0;
286
287 vfree(ptr);
288 }
289
290 return 0;
291}
292
293static int
294pcpu_alloc_test(void)
295{
296 int rv = 0;
297#ifndef CONFIG_NEED_PER_CPU_KM
298 void __percpu **pcpu;
299 size_t size, align;
300 int i;
301
302 pcpu = vmalloc(sizeof(void __percpu *) * 35000);
303 if (!pcpu)
304 return -1;
305
306 for (i = 0; i < 35000; i++) {
307 unsigned int r;
308
309 get_random_bytes(&r, sizeof(i));
310 size = (r % (PAGE_SIZE / 4)) + 1;
311
312 /*
313 * Maximum PAGE_SIZE
314 */
315 get_random_bytes(&r, sizeof(i));
316 align = 1 << ((i % 11) + 1);
317
318 pcpu[i] = __alloc_percpu(size, align);
319 if (!pcpu[i])
320 rv = -1;
321 }
322
323 for (i = 0; i < 35000; i++)
324 free_percpu(pcpu[i]);
325
326 vfree(pcpu);
327#endif
328 return rv;
329}
330
331struct test_case_desc {
332 const char *test_name;
333 int (*test_func)(void);
334};
335
336static struct test_case_desc test_case_array[] = {
337 { "fix_size_alloc_test", fix_size_alloc_test },
338 { "full_fit_alloc_test", full_fit_alloc_test },
339 { "long_busy_list_alloc_test", long_busy_list_alloc_test },
340 { "random_size_alloc_test", random_size_alloc_test },
341 { "fix_align_alloc_test", fix_align_alloc_test },
342 { "random_size_align_alloc_test", random_size_align_alloc_test },
343 { "align_shift_alloc_test", align_shift_alloc_test },
344 { "pcpu_alloc_test", pcpu_alloc_test },
345 /* Add a new test case here. */
346};
347
348struct test_case_data {
349 int test_failed;
350 int test_passed;
351 u64 time;
352};
353
354/* Split it to get rid of: WARNING: line over 80 characters */
355static struct test_case_data
356 per_cpu_test_data[NR_CPUS][ARRAY_SIZE(test_case_array)];
357
358static struct test_driver {
359 struct task_struct *task;
360 unsigned long start;
361 unsigned long stop;
362 int cpu;
363} per_cpu_test_driver[NR_CPUS];
364
365static void shuffle_array(int *arr, int n)
366{
367 unsigned int rnd;
368 int i, j, x;
369
370 for (i = n - 1; i > 0; i--) {
371 get_random_bytes(&rnd, sizeof(rnd));
372
373 /* Cut the range. */
374 j = rnd % i;
375
376 /* Swap indexes. */
377 x = arr[i];
378 arr[i] = arr[j];
379 arr[j] = x;
380 }
381}
382
383static int test_func(void *private)
384{
385 struct test_driver *t = private;
386 cpumask_t newmask = CPU_MASK_NONE;
387 int random_array[ARRAY_SIZE(test_case_array)];
388 int index, i, j, ret;
389 ktime_t kt;
390 u64 delta;
391
392 cpumask_set_cpu(t->cpu, &newmask);
393 set_cpus_allowed_ptr(current, &newmask);
394
395 for (i = 0; i < ARRAY_SIZE(test_case_array); i++)
396 random_array[i] = i;
397
398 if (!sequential_test_order)
399 shuffle_array(random_array, ARRAY_SIZE(test_case_array));
400
401 /*
402 * Block until initialization is done.
403 */
404 down_read(&prepare_for_test_rwsem);
405
406 t->start = get_cycles();
407 for (i = 0; i < ARRAY_SIZE(test_case_array); i++) {
408 index = random_array[i];
409
410 /*
411 * Skip tests if run_test_mask has been specified.
412 */
413 if (!((run_test_mask & (1 << index)) >> index))
414 continue;
415
416 kt = ktime_get();
417 for (j = 0; j < test_repeat_count; j++) {
418 ret = test_case_array[index].test_func();
419 if (!ret)
420 per_cpu_test_data[t->cpu][index].test_passed++;
421 else
422 per_cpu_test_data[t->cpu][index].test_failed++;
423 }
424
425 /*
426 * Take an average time that test took.
427 */
428 delta = (u64) ktime_us_delta(ktime_get(), kt);
429 do_div(delta, (u32) test_repeat_count);
430
431 per_cpu_test_data[t->cpu][index].time = delta;
432 }
433 t->stop = get_cycles();
434
435 up_read(&prepare_for_test_rwsem);
436 test_report_one_done();
437
438 /*
439 * Wait for the kthread_stop() call.
440 */
441 while (!kthread_should_stop())
442 msleep(10);
443
444 return 0;
445}
446
447static void
448init_test_configurtion(void)
449{
450 /*
451 * Reset all data of all CPUs.
452 */
453 memset(per_cpu_test_data, 0, sizeof(per_cpu_test_data));
454
455 if (single_cpu_test)
456 cpumask_set_cpu(cpumask_first(cpu_online_mask),
457 &cpus_run_test_mask);
458 else
459 cpumask_and(&cpus_run_test_mask, cpu_online_mask,
460 cpu_online_mask);
461
462 if (test_repeat_count <= 0)
463 test_repeat_count = 1;
464
465 if (test_loop_count <= 0)
466 test_loop_count = 1;
467}
468
469static void do_concurrent_test(void)
470{
471 int cpu, ret;
472
473 /*
474 * Set some basic configurations plus sanity check.
475 */
476 init_test_configurtion();
477
478 /*
479 * Put on hold all workers.
480 */
481 down_write(&prepare_for_test_rwsem);
482
483 for_each_cpu(cpu, &cpus_run_test_mask) {
484 struct test_driver *t = &per_cpu_test_driver[cpu];
485
486 t->cpu = cpu;
487 t->task = kthread_run(test_func, t, "vmalloc_test/%d", cpu);
488
489 if (!IS_ERR(t->task))
490 /* Success. */
491 atomic_inc(&test_n_undone);
492 else
493 pr_err("Failed to start kthread for %d CPU\n", cpu);
494 }
495
496 /*
497 * Now let the workers do their job.
498 */
499 up_write(&prepare_for_test_rwsem);
500
501 /*
502 * Sleep quiet until all workers are done with 1 second
503 * interval. Since the test can take a lot of time we
504 * can run into a stack trace of the hung task. That is
505 * why we go with completion_timeout and HZ value.
506 */
507 do {
508 ret = wait_for_completion_timeout(&test_all_done_comp, HZ);
509 } while (!ret);
510
511 for_each_cpu(cpu, &cpus_run_test_mask) {
512 struct test_driver *t = &per_cpu_test_driver[cpu];
513 int i;
514
515 if (!IS_ERR(t->task))
516 kthread_stop(t->task);
517
518 for (i = 0; i < ARRAY_SIZE(test_case_array); i++) {
519 if (!((run_test_mask & (1 << i)) >> i))
520 continue;
521
522 pr_info(
523 "Summary: %s passed: %d failed: %d repeat: %d loops: %d avg: %llu usec\n",
524 test_case_array[i].test_name,
525 per_cpu_test_data[cpu][i].test_passed,
526 per_cpu_test_data[cpu][i].test_failed,
527 test_repeat_count, test_loop_count,
528 per_cpu_test_data[cpu][i].time);
529 }
530
531 pr_info("All test took CPU%d=%lu cycles\n",
532 cpu, t->stop - t->start);
533 }
534}
535
536static int vmalloc_test_init(void)
537{
538 do_concurrent_test();
539 return -EAGAIN; /* Fail will directly unload the module */
540}
541
542static void vmalloc_test_exit(void)
543{
544}
545
546module_init(vmalloc_test_init)
547module_exit(vmalloc_test_exit)
548
549MODULE_LICENSE("GPL");
550MODULE_AUTHOR("Uladzislau Rezki");
551MODULE_DESCRIPTION("vmalloc test module");