1 /* synchro-test.c: run some threads to test the synchronisation primitives
3 * Copyright (C) 2005, 2006 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
11 * The module should be run as something like:
13 * insmod synchro-test.ko rd=2 wr=2
14 * insmod synchro-test.ko mx=1
15 * insmod synchro-test.ko sm=2 ism=1
16 * insmod synchro-test.ko sm=2 ism=2
18 * See Documentation/synchro-test.txt for more information.
21 #include <linux/module.h>
22 #include <linux/poll.h>
23 #include <linux/moduleparam.h>
24 #include <linux/sched.h>
25 #include <linux/stat.h>
26 #include <linux/init.h>
27 #include <asm/atomic.h>
28 #include <linux/personality.h>
29 #include <linux/smp_lock.h>
30 #include <linux/delay.h>
31 #include <linux/timer.h>
32 #include <linux/completion.h>
33 #include <linux/mutex.h>
35 #define MAX_THREADS 64
38 * Turn on self-validation if we do a one-shot boot-time test:
41 # define VALIDATE_OPERATORS
45 static int numsm
, seminit
= 4;
46 static int numrd
, numwr
, numdg
;
47 static int elapse
= 5, load
= 2, do_sched
, interval
= 2;
48 static int verbose
= 0;
50 MODULE_AUTHOR("David Howells");
51 MODULE_DESCRIPTION("Synchronisation primitive test demo");
52 MODULE_LICENSE("GPL");
54 module_param_named(v
, verbose
, int, 0);
55 MODULE_PARM_DESC(verbose
, "Verbosity");
57 module_param_named(mx
, nummx
, int, 0);
58 MODULE_PARM_DESC(nummx
, "Number of mutex threads");
60 module_param_named(sm
, numsm
, int, 0);
61 MODULE_PARM_DESC(numsm
, "Number of semaphore threads");
63 module_param_named(ism
, seminit
, int, 0);
64 MODULE_PARM_DESC(seminit
, "Initial semaphore value");
66 module_param_named(rd
, numrd
, int, 0);
67 MODULE_PARM_DESC(numrd
, "Number of reader threads");
69 module_param_named(wr
, numwr
, int, 0);
70 MODULE_PARM_DESC(numwr
, "Number of writer threads");
72 module_param_named(dg
, numdg
, int, 0);
73 MODULE_PARM_DESC(numdg
, "Number of downgrader threads");
75 module_param(elapse
, int, 0);
76 MODULE_PARM_DESC(elapse
, "Number of seconds to run for");
78 module_param(load
, int, 0);
79 MODULE_PARM_DESC(load
, "Length of load in uS");
81 module_param(interval
, int, 0);
82 MODULE_PARM_DESC(interval
, "Length of interval in uS before re-getting lock");
84 module_param(do_sched
, int, 0);
85 MODULE_PARM_DESC(do_sched
, "True if each thread should schedule regularly");
87 /* the semaphores under test */
88 static struct mutex ____cacheline_aligned mutex
;
89 static struct semaphore ____cacheline_aligned sem
;
90 static struct rw_semaphore ____cacheline_aligned rwsem
;
92 static atomic_t ____cacheline_aligned do_stuff
= ATOMIC_INIT(0);
94 #ifdef VALIDATE_OPERATORS
95 static atomic_t ____cacheline_aligned mutexes
= ATOMIC_INIT(0);
96 static atomic_t ____cacheline_aligned semaphores
= ATOMIC_INIT(0);
97 static atomic_t ____cacheline_aligned readers
= ATOMIC_INIT(0);
98 static atomic_t ____cacheline_aligned writers
= ATOMIC_INIT(0);
101 static unsigned int ____cacheline_aligned mutexes_taken
[MAX_THREADS
];
102 static unsigned int ____cacheline_aligned semaphores_taken
[MAX_THREADS
];
103 static unsigned int ____cacheline_aligned reads_taken
[MAX_THREADS
];
104 static unsigned int ____cacheline_aligned writes_taken
[MAX_THREADS
];
105 static unsigned int ____cacheline_aligned downgrades_taken
[MAX_THREADS
];
107 static struct completion ____cacheline_aligned mx_comp
[MAX_THREADS
];
108 static struct completion ____cacheline_aligned sm_comp
[MAX_THREADS
];
109 static struct completion ____cacheline_aligned rd_comp
[MAX_THREADS
];
110 static struct completion ____cacheline_aligned wr_comp
[MAX_THREADS
];
111 static struct completion ____cacheline_aligned dg_comp
[MAX_THREADS
];
113 static struct timer_list ____cacheline_aligned timer
;
115 #define ACCOUNT(var, N) var##_taken[N]++;
117 #ifdef VALIDATE_OPERATORS
118 #define TRACK(var, dir) atomic_##dir(&(var))
120 #define CHECK(var, cond, val) \
122 int x = atomic_read(&(var)); \
123 if (unlikely(!(x cond (val)))) \
124 printk("check [%s %s %d, == %d] failed in %s\n", \
125 #var, #cond, (val), x, __func__); \
129 #define TRACK(var, dir) do {} while(0)
130 #define CHECK(var, cond, val) do {} while(0)
133 static inline void do_mutex_lock(unsigned int N
)
139 CHECK(mutexes
, ==, 1);
142 static inline void do_mutex_unlock(unsigned int N
)
144 CHECK(mutexes
, ==, 1);
147 mutex_unlock(&mutex
);
150 static inline void do_down(unsigned int N
)
152 CHECK(mutexes
, <, seminit
);
156 ACCOUNT(semaphores
, N
);
157 TRACK(semaphores
, inc
);
160 static inline void do_up(unsigned int N
)
162 CHECK(semaphores
, >, 0);
163 TRACK(semaphores
, dec
);
168 static inline void do_down_read(unsigned int N
)
174 CHECK(readers
, >, 0);
175 CHECK(writers
, ==, 0);
178 static inline void do_up_read(unsigned int N
)
180 CHECK(readers
, >, 0);
181 CHECK(writers
, ==, 0);
187 static inline void do_down_write(unsigned int N
)
193 CHECK(writers
, ==, 1);
194 CHECK(readers
, ==, 0);
197 static inline void do_up_write(unsigned int N
)
199 CHECK(writers
, ==, 1);
200 CHECK(readers
, ==, 0);
206 static inline void do_downgrade_write(unsigned int N
)
208 CHECK(writers
, ==, 1);
209 CHECK(readers
, ==, 0);
213 downgrade_write(&rwsem
);
215 ACCOUNT(downgrades
, N
);
218 static inline void sched(void)
224 static int mutexer(void *arg
)
226 unsigned int N
= (unsigned long) arg
;
228 daemonize("Mutex%u", N
);
229 set_user_nice(current
, 19);
231 while (atomic_read(&do_stuff
)) {
242 printk("%s: done\n", current
->comm
);
243 complete_and_exit(&mx_comp
[N
], 0);
246 static int semaphorer(void *arg
)
248 unsigned int N
= (unsigned long) arg
;
250 daemonize("Sem%u", N
);
251 set_user_nice(current
, 19);
253 while (atomic_read(&do_stuff
)) {
264 printk("%s: done\n", current
->comm
);
265 complete_and_exit(&sm_comp
[N
], 0);
268 static int reader(void *arg
)
270 unsigned int N
= (unsigned long) arg
;
272 daemonize("Read%u", N
);
273 set_user_nice(current
, 19);
275 while (atomic_read(&do_stuff
)) {
288 printk("%s: done\n", current
->comm
);
289 complete_and_exit(&rd_comp
[N
], 0);
292 static int writer(void *arg
)
294 unsigned int N
= (unsigned long) arg
;
296 daemonize("Write%u", N
);
297 set_user_nice(current
, 19);
299 while (atomic_read(&do_stuff
)) {
312 printk("%s: done\n", current
->comm
);
313 complete_and_exit(&wr_comp
[N
], 0);
316 static int downgrader(void *arg
)
318 unsigned int N
= (unsigned long) arg
;
320 daemonize("Down%u", N
);
321 set_user_nice(current
, 19);
323 while (atomic_read(&do_stuff
)) {
329 do_downgrade_write(N
);
341 printk("%s: done\n", current
->comm
);
342 complete_and_exit(&dg_comp
[N
], 0);
345 static void stop_test(unsigned long dummy
)
347 atomic_set(&do_stuff
, 0);
350 static unsigned int total(const char *what
, unsigned int counts
[], int num
)
352 unsigned int tot
= 0, max
= 0, min
= UINT_MAX
, zeros
= 0, cnt
;
355 for (loop
= 0; loop
< num
; loop
++) {
371 if (verbose
&& tot
> 0) {
374 for (loop
= 0; loop
< num
; loop
++) {
380 printk(" %d%%", cnt
* 100 / tot
);
389 /*****************************************************************************/
393 static int __init
do_tests(void)
396 unsigned int mutex_total
, sem_total
, rd_total
, wr_total
, dg_total
;
398 if (nummx
< 0 || nummx
> MAX_THREADS
||
399 numsm
< 0 || numsm
> MAX_THREADS
||
400 numrd
< 0 || numrd
> MAX_THREADS
||
401 numwr
< 0 || numwr
> MAX_THREADS
||
402 numdg
< 0 || numdg
> MAX_THREADS
||
405 load
< 0 || load
> 999 ||
406 interval
< 0 || interval
> 999
408 printk("Parameter out of range\n");
412 if ((nummx
| numsm
| numrd
| numwr
| numdg
) == 0) {
413 int num
= num_online_cpus();
415 if (num
> MAX_THREADS
)
417 nummx
= numsm
= numrd
= numwr
= numdg
= num
;
422 printk("No parameters - using defaults.\n");
426 printk("\nStarting synchronisation primitive tests...\n");
429 sema_init(&sem
, seminit
);
431 atomic_set(&do_stuff
, 1);
433 /* kick off all the children */
434 for (loop
= 0; loop
< MAX_THREADS
; loop
++) {
436 init_completion(&mx_comp
[loop
]);
437 kernel_thread(mutexer
, (void *) loop
, 0);
441 init_completion(&sm_comp
[loop
]);
442 kernel_thread(semaphorer
, (void *) loop
, 0);
446 init_completion(&rd_comp
[loop
]);
447 kernel_thread(reader
, (void *) loop
, 0);
451 init_completion(&wr_comp
[loop
]);
452 kernel_thread(writer
, (void *) loop
, 0);
456 init_completion(&dg_comp
[loop
]);
457 kernel_thread(downgrader
, (void *) loop
, 0);
461 /* set a stop timer */
463 timer
.function
= stop_test
;
464 timer
.expires
= jiffies
+ elapse
* HZ
;
467 /* now wait until it's all done */
468 for (loop
= 0; loop
< nummx
; loop
++)
469 wait_for_completion(&mx_comp
[loop
]);
471 for (loop
= 0; loop
< numsm
; loop
++)
472 wait_for_completion(&sm_comp
[loop
]);
474 for (loop
= 0; loop
< numrd
; loop
++)
475 wait_for_completion(&rd_comp
[loop
]);
477 for (loop
= 0; loop
< numwr
; loop
++)
478 wait_for_completion(&wr_comp
[loop
]);
480 for (loop
= 0; loop
< numdg
; loop
++)
481 wait_for_completion(&dg_comp
[loop
]);
483 atomic_set(&do_stuff
, 0);
486 if (mutex_is_locked(&mutex
))
487 printk(KERN_ERR
"Mutex is still locked!\n");
490 mutex_total
= total("MTX", mutexes_taken
, nummx
);
491 sem_total
= total("SEM", semaphores_taken
, numsm
);
492 rd_total
= total("RD ", reads_taken
, numrd
);
493 wr_total
= total("WR ", writes_taken
, numwr
);
494 dg_total
= total("DG ", downgrades_taken
, numdg
);
496 /* print the results */
498 printk("mutexes taken: %u\n", mutex_total
);
499 printk("semaphores taken: %u\n", sem_total
);
500 printk("reads taken: %u\n", rd_total
);
501 printk("writes taken: %u\n", wr_total
);
502 printk("downgrades taken: %u\n", dg_total
);
507 sprintf(buf
, "%d/%d", interval
, load
);
509 printk("%3d %3d %3d %3d %3d %c %5s %9u %9u %9u %9u %9u\n",
510 nummx
, numsm
, numrd
, numwr
, numdg
,
511 do_sched
? 's' : '-',
520 /* tell insmod to discard the module */
522 printk("Tests complete\n");
525 } /* end do_tests() */
527 module_init(do_tests
);