]>
Commit | Line | Data |
---|---|---|
92f90d3b | 1 | // SPDX-License-Identifier: GPL-2.0 |
2 | ||
3 | /* | |
4 | * Test module for lockless object pool | |
5 | * | |
6 | * Copyright: wuqiang.matt@bytedance.com | |
7 | */ | |
8 | ||
92f90d3b | 9 | #include <linux/errno.h> |
10 | #include <linux/module.h> | |
11 | #include <linux/moduleparam.h> | |
92f90d3b | 12 | #include <linux/completion.h> |
13 | #include <linux/kthread.h> | |
92f90d3b | 14 | #include <linux/slab.h> |
15 | #include <linux/vmalloc.h> | |
16 | #include <linux/delay.h> | |
17 | #include <linux/hrtimer.h> | |
92f90d3b | 18 | #include <linux/objpool.h> |
19 | ||
20 | #define OT_NR_MAX_BULK (16) | |
21 | ||
22 | /* memory usage */ | |
23 | struct ot_mem_stat { | |
24 | atomic_long_t alloc; | |
25 | atomic_long_t free; | |
26 | }; | |
27 | ||
28 | /* object allocation results */ | |
29 | struct ot_obj_stat { | |
30 | unsigned long nhits; | |
31 | unsigned long nmiss; | |
32 | }; | |
33 | ||
34 | /* control & results per testcase */ | |
35 | struct ot_data { | |
36 | struct rw_semaphore start; | |
37 | struct completion wait; | |
38 | struct completion rcu; | |
39 | atomic_t nthreads ____cacheline_aligned_in_smp; | |
40 | atomic_t stop ____cacheline_aligned_in_smp; | |
41 | struct ot_mem_stat kmalloc; | |
42 | struct ot_mem_stat vmalloc; | |
43 | struct ot_obj_stat objects; | |
44 | u64 duration; | |
45 | }; | |
46 | ||
47 | /* testcase */ | |
48 | struct ot_test { | |
49 | int async; /* synchronous or asynchronous */ | |
50 | int mode; /* only mode 0 supported */ | |
51 | int objsz; /* object size */ | |
52 | int duration; /* ms */ | |
53 | int delay; /* ms */ | |
54 | int bulk_normal; | |
55 | int bulk_irq; | |
56 | unsigned long hrtimer; /* ms */ | |
57 | const char *name; | |
58 | struct ot_data data; | |
59 | }; | |
60 | ||
61 | /* per-cpu worker */ | |
62 | struct ot_item { | |
63 | struct objpool_head *pool; /* pool head */ | |
64 | struct ot_test *test; /* test parameters */ | |
65 | ||
66 | void (*worker)(struct ot_item *item, int irq); | |
67 | ||
68 | /* hrtimer control */ | |
69 | ktime_t hrtcycle; | |
70 | struct hrtimer hrtimer; | |
71 | ||
72 | int bulk[2]; /* for thread and irq */ | |
73 | int delay; | |
74 | u32 niters; | |
75 | ||
76 | /* summary per thread */ | |
77 | struct ot_obj_stat stat[2]; /* thread and irq */ | |
78 | u64 duration; | |
79 | }; | |
80 | ||
81 | /* | |
82 | * memory leakage checking | |
83 | */ | |
84 | ||
85 | static void *ot_kzalloc(struct ot_test *test, long size) | |
86 | { | |
87 | void *ptr = kzalloc(size, GFP_KERNEL); | |
88 | ||
89 | if (ptr) | |
90 | atomic_long_add(size, &test->data.kmalloc.alloc); | |
91 | return ptr; | |
92 | } | |
93 | ||
94 | static void ot_kfree(struct ot_test *test, void *ptr, long size) | |
95 | { | |
96 | if (!ptr) | |
97 | return; | |
98 | atomic_long_add(size, &test->data.kmalloc.free); | |
99 | kfree(ptr); | |
100 | } | |
101 | ||
102 | static void ot_mem_report(struct ot_test *test) | |
103 | { | |
104 | long alloc, free; | |
105 | ||
106 | pr_info("memory allocation summary for %s\n", test->name); | |
107 | ||
108 | alloc = atomic_long_read(&test->data.kmalloc.alloc); | |
109 | free = atomic_long_read(&test->data.kmalloc.free); | |
110 | pr_info(" kmalloc: %lu - %lu = %lu\n", alloc, free, alloc - free); | |
111 | ||
112 | alloc = atomic_long_read(&test->data.vmalloc.alloc); | |
113 | free = atomic_long_read(&test->data.vmalloc.free); | |
114 | pr_info(" vmalloc: %lu - %lu = %lu\n", alloc, free, alloc - free); | |
115 | } | |
116 | ||
117 | /* user object instance */ | |
118 | struct ot_node { | |
119 | void *owner; | |
120 | unsigned long data; | |
121 | unsigned long refs; | |
122 | unsigned long payload[32]; | |
123 | }; | |
124 | ||
125 | /* user objpool manager */ | |
126 | struct ot_context { | |
127 | struct objpool_head pool; /* objpool head */ | |
128 | struct ot_test *test; /* test parameters */ | |
129 | void *ptr; /* user pool buffer */ | |
130 | unsigned long size; /* buffer size */ | |
131 | struct rcu_head rcu; | |
132 | }; | |
133 | ||
134 | static DEFINE_PER_CPU(struct ot_item, ot_pcup_items); | |
135 | ||
136 | static int ot_init_data(struct ot_data *data) | |
137 | { | |
138 | memset(data, 0, sizeof(*data)); | |
139 | init_rwsem(&data->start); | |
140 | init_completion(&data->wait); | |
141 | init_completion(&data->rcu); | |
142 | atomic_set(&data->nthreads, 1); | |
143 | ||
144 | return 0; | |
145 | } | |
146 | ||
147 | static int ot_init_node(void *nod, void *context) | |
148 | { | |
149 | struct ot_context *sop = context; | |
150 | struct ot_node *on = nod; | |
151 | ||
152 | on->owner = &sop->pool; | |
153 | return 0; | |
154 | } | |
155 | ||
156 | static enum hrtimer_restart ot_hrtimer_handler(struct hrtimer *hrt) | |
157 | { | |
158 | struct ot_item *item = container_of(hrt, struct ot_item, hrtimer); | |
159 | struct ot_test *test = item->test; | |
160 | ||
161 | if (atomic_read_acquire(&test->data.stop)) | |
162 | return HRTIMER_NORESTART; | |
163 | ||
164 | /* do bulk-testings for objects pop/push */ | |
165 | item->worker(item, 1); | |
166 | ||
167 | hrtimer_forward(hrt, hrt->base->get_time(), item->hrtcycle); | |
168 | return HRTIMER_RESTART; | |
169 | } | |
170 | ||
171 | static void ot_start_hrtimer(struct ot_item *item) | |
172 | { | |
173 | if (!item->test->hrtimer) | |
174 | return; | |
175 | hrtimer_start(&item->hrtimer, item->hrtcycle, HRTIMER_MODE_REL); | |
176 | } | |
177 | ||
178 | static void ot_stop_hrtimer(struct ot_item *item) | |
179 | { | |
180 | if (!item->test->hrtimer) | |
181 | return; | |
182 | hrtimer_cancel(&item->hrtimer); | |
183 | } | |
184 | ||
185 | static int ot_init_hrtimer(struct ot_item *item, unsigned long hrtimer) | |
186 | { | |
187 | struct hrtimer *hrt = &item->hrtimer; | |
188 | ||
189 | if (!hrtimer) | |
190 | return -ENOENT; | |
191 | ||
192 | item->hrtcycle = ktime_set(0, hrtimer * 1000000UL); | |
193 | hrtimer_init(hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | |
194 | hrt->function = ot_hrtimer_handler; | |
195 | return 0; | |
196 | } | |
197 | ||
198 | static int ot_init_cpu_item(struct ot_item *item, | |
199 | struct ot_test *test, | |
200 | struct objpool_head *pool, | |
201 | void (*worker)(struct ot_item *, int)) | |
202 | { | |
203 | memset(item, 0, sizeof(*item)); | |
204 | item->pool = pool; | |
205 | item->test = test; | |
206 | item->worker = worker; | |
207 | ||
208 | item->bulk[0] = test->bulk_normal; | |
209 | item->bulk[1] = test->bulk_irq; | |
210 | item->delay = test->delay; | |
211 | ||
212 | /* initialize hrtimer */ | |
213 | ot_init_hrtimer(item, item->test->hrtimer); | |
214 | return 0; | |
215 | } | |
216 | ||
217 | static int ot_thread_worker(void *arg) | |
218 | { | |
219 | struct ot_item *item = arg; | |
220 | struct ot_test *test = item->test; | |
221 | ktime_t start; | |
222 | ||
223 | atomic_inc(&test->data.nthreads); | |
224 | down_read(&test->data.start); | |
225 | up_read(&test->data.start); | |
226 | start = ktime_get(); | |
227 | ot_start_hrtimer(item); | |
228 | do { | |
229 | if (atomic_read_acquire(&test->data.stop)) | |
230 | break; | |
231 | /* do bulk-testings for objects pop/push */ | |
232 | item->worker(item, 0); | |
233 | } while (!kthread_should_stop()); | |
234 | ot_stop_hrtimer(item); | |
235 | item->duration = (u64) ktime_us_delta(ktime_get(), start); | |
236 | if (atomic_dec_and_test(&test->data.nthreads)) | |
237 | complete(&test->data.wait); | |
238 | ||
239 | return 0; | |
240 | } | |
241 | ||
242 | static void ot_perf_report(struct ot_test *test, u64 duration) | |
243 | { | |
244 | struct ot_obj_stat total, normal = {0}, irq = {0}; | |
245 | int cpu, nthreads = 0; | |
246 | ||
247 | pr_info("\n"); | |
248 | pr_info("Testing summary for %s\n", test->name); | |
249 | ||
250 | for_each_possible_cpu(cpu) { | |
251 | struct ot_item *item = per_cpu_ptr(&ot_pcup_items, cpu); | |
252 | if (!item->duration) | |
253 | continue; | |
254 | normal.nhits += item->stat[0].nhits; | |
255 | normal.nmiss += item->stat[0].nmiss; | |
256 | irq.nhits += item->stat[1].nhits; | |
257 | irq.nmiss += item->stat[1].nmiss; | |
258 | pr_info("CPU: %d duration: %lluus\n", cpu, item->duration); | |
259 | pr_info("\tthread:\t%16lu hits \t%16lu miss\n", | |
260 | item->stat[0].nhits, item->stat[0].nmiss); | |
261 | pr_info("\tirq: \t%16lu hits \t%16lu miss\n", | |
262 | item->stat[1].nhits, item->stat[1].nmiss); | |
263 | pr_info("\ttotal: \t%16lu hits \t%16lu miss\n", | |
264 | item->stat[0].nhits + item->stat[1].nhits, | |
265 | item->stat[0].nmiss + item->stat[1].nmiss); | |
266 | nthreads++; | |
267 | } | |
268 | ||
269 | total.nhits = normal.nhits + irq.nhits; | |
270 | total.nmiss = normal.nmiss + irq.nmiss; | |
271 | ||
272 | pr_info("ALL: \tnthreads: %d duration: %lluus\n", nthreads, duration); | |
273 | pr_info("SUM: \t%16lu hits \t%16lu miss\n", | |
274 | total.nhits, total.nmiss); | |
275 | ||
276 | test->data.objects = total; | |
277 | test->data.duration = duration; | |
278 | } | |
279 | ||
280 | /* | |
281 | * synchronous test cases for objpool manipulation | |
282 | */ | |
283 | ||
284 | /* objpool manipulation for synchronous mode (percpu objpool) */ | |
285 | static struct ot_context *ot_init_sync_m0(struct ot_test *test) | |
286 | { | |
287 | struct ot_context *sop = NULL; | |
288 | int max = num_possible_cpus() << 3; | |
289 | gfp_t gfp = GFP_KERNEL; | |
290 | ||
291 | sop = (struct ot_context *)ot_kzalloc(test, sizeof(*sop)); | |
292 | if (!sop) | |
293 | return NULL; | |
294 | sop->test = test; | |
295 | if (test->objsz < 512) | |
296 | gfp = GFP_ATOMIC; | |
297 | ||
298 | if (objpool_init(&sop->pool, max, test->objsz, | |
299 | gfp, sop, ot_init_node, NULL)) { | |
300 | ot_kfree(test, sop, sizeof(*sop)); | |
301 | return NULL; | |
302 | } | |
303 | WARN_ON(max != sop->pool.nr_objs); | |
304 | ||
305 | return sop; | |
306 | } | |
307 | ||
308 | static void ot_fini_sync(struct ot_context *sop) | |
309 | { | |
310 | objpool_fini(&sop->pool); | |
311 | ot_kfree(sop->test, sop, sizeof(*sop)); | |
312 | } | |
313 | ||
3afe7337 | 314 | static struct { |
92f90d3b | 315 | struct ot_context * (*init)(struct ot_test *oc); |
316 | void (*fini)(struct ot_context *sop); | |
317 | } g_ot_sync_ops[] = { | |
318 | {.init = ot_init_sync_m0, .fini = ot_fini_sync}, | |
319 | }; | |
320 | ||
321 | /* | |
322 | * synchronous test cases: performance mode | |
323 | */ | |
324 | ||
325 | static void ot_bulk_sync(struct ot_item *item, int irq) | |
326 | { | |
327 | struct ot_node *nods[OT_NR_MAX_BULK]; | |
328 | int i; | |
329 | ||
330 | for (i = 0; i < item->bulk[irq]; i++) | |
331 | nods[i] = objpool_pop(item->pool); | |
332 | ||
333 | if (!irq && (item->delay || !(++(item->niters) & 0x7FFF))) | |
334 | msleep(item->delay); | |
335 | ||
336 | while (i-- > 0) { | |
337 | struct ot_node *on = nods[i]; | |
338 | if (on) { | |
339 | on->refs++; | |
340 | objpool_push(on, item->pool); | |
341 | item->stat[irq].nhits++; | |
342 | } else { | |
343 | item->stat[irq].nmiss++; | |
344 | } | |
345 | } | |
346 | } | |
347 | ||
348 | static int ot_start_sync(struct ot_test *test) | |
349 | { | |
350 | struct ot_context *sop; | |
351 | ktime_t start; | |
352 | u64 duration; | |
353 | unsigned long timeout; | |
354 | int cpu; | |
355 | ||
356 | /* initialize objpool for syncrhonous testcase */ | |
357 | sop = g_ot_sync_ops[test->mode].init(test); | |
358 | if (!sop) | |
359 | return -ENOMEM; | |
360 | ||
361 | /* grab rwsem to block testing threads */ | |
362 | down_write(&test->data.start); | |
363 | ||
364 | for_each_possible_cpu(cpu) { | |
365 | struct ot_item *item = per_cpu_ptr(&ot_pcup_items, cpu); | |
366 | struct task_struct *work; | |
367 | ||
368 | ot_init_cpu_item(item, test, &sop->pool, ot_bulk_sync); | |
369 | ||
370 | /* skip offline cpus */ | |
371 | if (!cpu_online(cpu)) | |
372 | continue; | |
373 | ||
374 | work = kthread_create_on_node(ot_thread_worker, item, | |
375 | cpu_to_node(cpu), "ot_worker_%d", cpu); | |
376 | if (IS_ERR(work)) { | |
377 | pr_err("failed to create thread for cpu %d\n", cpu); | |
378 | } else { | |
379 | kthread_bind(work, cpu); | |
380 | wake_up_process(work); | |
381 | } | |
382 | } | |
383 | ||
384 | /* wait a while to make sure all threads waiting at start line */ | |
385 | msleep(20); | |
386 | ||
387 | /* in case no threads were created: memory insufficient ? */ | |
388 | if (atomic_dec_and_test(&test->data.nthreads)) | |
389 | complete(&test->data.wait); | |
390 | ||
391 | // sched_set_fifo_low(current); | |
392 | ||
393 | /* start objpool testing threads */ | |
394 | start = ktime_get(); | |
395 | up_write(&test->data.start); | |
396 | ||
397 | /* yeild cpu to worker threads for duration ms */ | |
398 | timeout = msecs_to_jiffies(test->duration); | |
399 | schedule_timeout_interruptible(timeout); | |
400 | ||
401 | /* tell workers threads to quit */ | |
402 | atomic_set_release(&test->data.stop, 1); | |
403 | ||
404 | /* wait all workers threads finish and quit */ | |
405 | wait_for_completion(&test->data.wait); | |
406 | duration = (u64) ktime_us_delta(ktime_get(), start); | |
407 | ||
408 | /* cleanup objpool */ | |
409 | g_ot_sync_ops[test->mode].fini(sop); | |
410 | ||
411 | /* report testing summary and performance results */ | |
412 | ot_perf_report(test, duration); | |
413 | ||
414 | /* report memory allocation summary */ | |
415 | ot_mem_report(test); | |
416 | ||
417 | return 0; | |
418 | } | |
419 | ||
420 | /* | |
421 | * asynchronous test cases: pool lifecycle controlled by refcount | |
422 | */ | |
423 | ||
424 | static void ot_fini_async_rcu(struct rcu_head *rcu) | |
425 | { | |
426 | struct ot_context *sop = container_of(rcu, struct ot_context, rcu); | |
427 | struct ot_test *test = sop->test; | |
428 | ||
429 | /* here all cpus are aware of the stop event: test->data.stop = 1 */ | |
430 | WARN_ON(!atomic_read_acquire(&test->data.stop)); | |
431 | ||
432 | objpool_fini(&sop->pool); | |
433 | complete(&test->data.rcu); | |
434 | } | |
435 | ||
436 | static void ot_fini_async(struct ot_context *sop) | |
437 | { | |
438 | /* make sure the stop event is acknowledged by all cores */ | |
439 | call_rcu(&sop->rcu, ot_fini_async_rcu); | |
440 | } | |
441 | ||
442 | static int ot_objpool_release(struct objpool_head *head, void *context) | |
443 | { | |
444 | struct ot_context *sop = context; | |
445 | ||
446 | WARN_ON(!head || !sop || head != &sop->pool); | |
447 | ||
448 | /* do context cleaning if needed */ | |
449 | if (sop) | |
450 | ot_kfree(sop->test, sop, sizeof(*sop)); | |
451 | ||
452 | return 0; | |
453 | } | |
454 | ||
455 | static struct ot_context *ot_init_async_m0(struct ot_test *test) | |
456 | { | |
457 | struct ot_context *sop = NULL; | |
458 | int max = num_possible_cpus() << 3; | |
459 | gfp_t gfp = GFP_KERNEL; | |
460 | ||
461 | sop = (struct ot_context *)ot_kzalloc(test, sizeof(*sop)); | |
462 | if (!sop) | |
463 | return NULL; | |
464 | sop->test = test; | |
465 | if (test->objsz < 512) | |
466 | gfp = GFP_ATOMIC; | |
467 | ||
468 | if (objpool_init(&sop->pool, max, test->objsz, gfp, sop, | |
469 | ot_init_node, ot_objpool_release)) { | |
470 | ot_kfree(test, sop, sizeof(*sop)); | |
471 | return NULL; | |
472 | } | |
473 | WARN_ON(max != sop->pool.nr_objs); | |
474 | ||
475 | return sop; | |
476 | } | |
477 | ||
3afe7337 | 478 | static struct { |
92f90d3b | 479 | struct ot_context * (*init)(struct ot_test *oc); |
480 | void (*fini)(struct ot_context *sop); | |
481 | } g_ot_async_ops[] = { | |
482 | {.init = ot_init_async_m0, .fini = ot_fini_async}, | |
483 | }; | |
484 | ||
485 | static void ot_nod_recycle(struct ot_node *on, struct objpool_head *pool, | |
486 | int release) | |
487 | { | |
488 | struct ot_context *sop; | |
489 | ||
490 | on->refs++; | |
491 | ||
492 | if (!release) { | |
493 | /* push object back to opjpool for reuse */ | |
494 | objpool_push(on, pool); | |
495 | return; | |
496 | } | |
497 | ||
498 | sop = container_of(pool, struct ot_context, pool); | |
499 | WARN_ON(sop != pool->context); | |
500 | ||
501 | /* unref objpool with nod removed forever */ | |
502 | objpool_drop(on, pool); | |
503 | } | |
504 | ||
505 | static void ot_bulk_async(struct ot_item *item, int irq) | |
506 | { | |
507 | struct ot_test *test = item->test; | |
508 | struct ot_node *nods[OT_NR_MAX_BULK]; | |
509 | int i, stop; | |
510 | ||
511 | for (i = 0; i < item->bulk[irq]; i++) | |
512 | nods[i] = objpool_pop(item->pool); | |
513 | ||
514 | if (!irq) { | |
515 | if (item->delay || !(++(item->niters) & 0x7FFF)) | |
516 | msleep(item->delay); | |
517 | get_cpu(); | |
518 | } | |
519 | ||
520 | stop = atomic_read_acquire(&test->data.stop); | |
521 | ||
522 | /* drop all objects and deref objpool */ | |
523 | while (i-- > 0) { | |
524 | struct ot_node *on = nods[i]; | |
525 | ||
526 | if (on) { | |
527 | on->refs++; | |
528 | ot_nod_recycle(on, item->pool, stop); | |
529 | item->stat[irq].nhits++; | |
530 | } else { | |
531 | item->stat[irq].nmiss++; | |
532 | } | |
533 | } | |
534 | ||
535 | if (!irq) | |
536 | put_cpu(); | |
537 | } | |
538 | ||
539 | static int ot_start_async(struct ot_test *test) | |
540 | { | |
541 | struct ot_context *sop; | |
542 | ktime_t start; | |
543 | u64 duration; | |
544 | unsigned long timeout; | |
545 | int cpu; | |
546 | ||
547 | /* initialize objpool for syncrhonous testcase */ | |
548 | sop = g_ot_async_ops[test->mode].init(test); | |
549 | if (!sop) | |
550 | return -ENOMEM; | |
551 | ||
552 | /* grab rwsem to block testing threads */ | |
553 | down_write(&test->data.start); | |
554 | ||
555 | for_each_possible_cpu(cpu) { | |
556 | struct ot_item *item = per_cpu_ptr(&ot_pcup_items, cpu); | |
557 | struct task_struct *work; | |
558 | ||
559 | ot_init_cpu_item(item, test, &sop->pool, ot_bulk_async); | |
560 | ||
561 | /* skip offline cpus */ | |
562 | if (!cpu_online(cpu)) | |
563 | continue; | |
564 | ||
565 | work = kthread_create_on_node(ot_thread_worker, item, | |
566 | cpu_to_node(cpu), "ot_worker_%d", cpu); | |
567 | if (IS_ERR(work)) { | |
568 | pr_err("failed to create thread for cpu %d\n", cpu); | |
569 | } else { | |
570 | kthread_bind(work, cpu); | |
571 | wake_up_process(work); | |
572 | } | |
573 | } | |
574 | ||
575 | /* wait a while to make sure all threads waiting at start line */ | |
576 | msleep(20); | |
577 | ||
578 | /* in case no threads were created: memory insufficient ? */ | |
579 | if (atomic_dec_and_test(&test->data.nthreads)) | |
580 | complete(&test->data.wait); | |
581 | ||
582 | /* start objpool testing threads */ | |
583 | start = ktime_get(); | |
584 | up_write(&test->data.start); | |
585 | ||
586 | /* yeild cpu to worker threads for duration ms */ | |
587 | timeout = msecs_to_jiffies(test->duration); | |
588 | schedule_timeout_interruptible(timeout); | |
589 | ||
590 | /* tell workers threads to quit */ | |
591 | atomic_set_release(&test->data.stop, 1); | |
592 | ||
593 | /* do async-finalization */ | |
594 | g_ot_async_ops[test->mode].fini(sop); | |
595 | ||
596 | /* wait all workers threads finish and quit */ | |
597 | wait_for_completion(&test->data.wait); | |
598 | duration = (u64) ktime_us_delta(ktime_get(), start); | |
599 | ||
600 | /* assure rcu callback is triggered */ | |
601 | wait_for_completion(&test->data.rcu); | |
602 | ||
603 | /* | |
604 | * now we are sure that objpool is finalized either | |
605 | * by rcu callback or by worker threads | |
606 | */ | |
607 | ||
608 | /* report testing summary and performance results */ | |
609 | ot_perf_report(test, duration); | |
610 | ||
611 | /* report memory allocation summary */ | |
612 | ot_mem_report(test); | |
613 | ||
614 | return 0; | |
615 | } | |
616 | ||
617 | /* | |
618 | * predefined testing cases: | |
619 | * synchronous case / overrun case / async case | |
620 | * | |
621 | * async: synchronous or asynchronous testing | |
622 | * mode: only mode 0 supported | |
623 | * objsz: object size | |
624 | * duration: int, total test time in ms | |
625 | * delay: int, delay (in ms) between each iteration | |
626 | * bulk_normal: int, repeat times for thread worker | |
627 | * bulk_irq: int, repeat times for irq consumer | |
628 | * hrtimer: unsigned long, hrtimer intervnal in ms | |
629 | * name: char *, tag for current test ot_item | |
630 | */ | |
631 | ||
632 | #define NODE_COMPACT sizeof(struct ot_node) | |
633 | #define NODE_VMALLOC (512) | |
634 | ||
3afe7337 | 635 | static struct ot_test g_testcases[] = { |
92f90d3b | 636 | |
637 | /* sync & normal */ | |
638 | {0, 0, NODE_COMPACT, 1000, 0, 1, 0, 0, "sync: percpu objpool"}, | |
639 | {0, 0, NODE_VMALLOC, 1000, 0, 1, 0, 0, "sync: percpu objpool from vmalloc"}, | |
640 | ||
641 | /* sync & hrtimer */ | |
642 | {0, 0, NODE_COMPACT, 1000, 0, 1, 1, 4, "sync & hrtimer: percpu objpool"}, | |
643 | {0, 0, NODE_VMALLOC, 1000, 0, 1, 1, 4, "sync & hrtimer: percpu objpool from vmalloc"}, | |
644 | ||
645 | /* sync & overrun */ | |
646 | {0, 0, NODE_COMPACT, 1000, 0, 16, 0, 0, "sync overrun: percpu objpool"}, | |
647 | {0, 0, NODE_VMALLOC, 1000, 0, 16, 0, 0, "sync overrun: percpu objpool from vmalloc"}, | |
648 | ||
649 | /* async mode */ | |
650 | {1, 0, NODE_COMPACT, 1000, 100, 1, 0, 0, "async: percpu objpool"}, | |
651 | {1, 0, NODE_VMALLOC, 1000, 100, 1, 0, 0, "async: percpu objpool from vmalloc"}, | |
652 | ||
653 | /* async + hrtimer mode */ | |
654 | {1, 0, NODE_COMPACT, 1000, 0, 4, 4, 4, "async & hrtimer: percpu objpool"}, | |
655 | {1, 0, NODE_VMALLOC, 1000, 0, 4, 4, 4, "async & hrtimer: percpu objpool from vmalloc"}, | |
656 | }; | |
657 | ||
658 | static int __init ot_mod_init(void) | |
659 | { | |
660 | int i; | |
661 | ||
662 | /* perform testings */ | |
663 | for (i = 0; i < ARRAY_SIZE(g_testcases); i++) { | |
664 | ot_init_data(&g_testcases[i].data); | |
665 | if (g_testcases[i].async) | |
666 | ot_start_async(&g_testcases[i]); | |
667 | else | |
668 | ot_start_sync(&g_testcases[i]); | |
669 | } | |
670 | ||
671 | /* show tests summary */ | |
672 | pr_info("\n"); | |
673 | pr_info("Summary of testcases:\n"); | |
674 | for (i = 0; i < ARRAY_SIZE(g_testcases); i++) { | |
675 | pr_info(" duration: %lluus \thits: %10lu \tmiss: %10lu \t%s\n", | |
676 | g_testcases[i].data.duration, g_testcases[i].data.objects.nhits, | |
677 | g_testcases[i].data.objects.nmiss, g_testcases[i].name); | |
678 | } | |
679 | ||
680 | return -EAGAIN; | |
681 | } | |
682 | ||
683 | static void __exit ot_mod_exit(void) | |
684 | { | |
685 | } | |
686 | ||
687 | module_init(ot_mod_init); | |
688 | module_exit(ot_mod_exit); | |
689 | ||
690 | MODULE_LICENSE("GPL"); |