abaa35a366eeabcc72e47d95920e74db7a219dc1
[bertos.git] / bertos / kern / sem_test.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2008 Develer S.r.l. (http://www.develer.com/)
30  * -->
31  *
32  * \brief Semaphore test.
33  *
34  * For testing priority inversion (avoidance) a set of processes
35  * interacting among each others by means of two semaphores are
36  * disturbed by an unrelated process, i.e., a process not using
37  * any semaphore at all.
38  *
39  * In case of priority inversion, high priority processes
40  * are affected (delayed!) by such process, even it has lower
41  * priority, because of semaphores. On the other hand, when priority
42  * inheritance is enabled, non interacting low priority processes
43  * can't affect the execution of high priority ones.
44  *
45  * It all can be seen looking at the finishing time of the various
46  * processes involved in sem_inv_test (logged).
47  *
48  * Notice that priority inheritance makes sense iff priorities
49  * exist, so the whole test depends on CONFIG_KERN_PRI.
50  *
51  * \author Daniele Basile <asterix@develer.com>
52  * \author Stefano Fedrigo <aleph@develer.com>
53  * \author Dario Faggioli <raistlin@linux.it>
54  *
55  * $test$: cp bertos/cfg/cfg_proc.h $cfgdir/
56  * $test$: echo  "#undef CONFIG_KERN" >> $cfgdir/cfg_proc.h
57  * $test$: echo "#define CONFIG_KERN 1" >> $cfgdir/cfg_proc.h
58  * $test$: echo  "#undef CONFIG_KERN_PRI" >> $cfgdir/cfg_proc.h
59  * $test$: echo "#define CONFIG_KERN_PRI 1" >> $cfgdir/cfg_proc.h
60  * $test$: echo  "#undef CONFIG_KERN_PRI_INHERIT" >> $cfgdir/cfg_proc.h
61  * $test$: echo "#define CONFIG_KERN_PRI_INHERIT 1" >> $cfgdir/cfg_proc.h
62  * $test$: cp bertos/cfg/cfg_sem.h $cfgdir/
63  * $test$: echo  "#undef CONFIG_KERN_SEMAPHORES" >> $cfgdir/cfg_sem.h
64  * $test$: echo "#define CONFIG_KERN_SEMAPHORES 1" >> $cfgdir/cfg_sem.h
65  */
66
67 #include <cfg/debug.h>
68 #include <cfg/test.h>
69
70 #include <kern/sem.h>
71 #include <kern/proc.h>
72 #include <kern/irq.h>
73
74 #include <drv/timer.h>
75
76 // Global settings for the serialization test.
77 #define MAX_GLOBAL_COUNT             1024
78 #define TEST_TIME_OUT_MS             6000
79 #define DELAY                           5
80
81 // Settings for the test processes (serialization test).
82 //Process 1
83 #define INC_PROC_T1                     1
84 #define DELAY_PROC_T1   INC_PROC_T1*DELAY
85 //Process 2
86 #define INC_PROC_T2                     3
87 #define DELAY_PROC_T2   INC_PROC_T2*DELAY
88 //Process 3
89 #define INC_PROC_T3                     5
90 #define DELAY_PROC_T3   INC_PROC_T3*DELAY
91 //Process 4
92 #define INC_PROC_T4                     7
93 #define DELAY_PROC_T4   INC_PROC_T4*DELAY
94 //Process 5
95 #define INC_PROC_T5                    11
96 #define DELAY_PROC_T5   INC_PROC_T5*DELAY
97 //Process 6
98 #define INC_PROC_T6                    13
99 #define DELAY_PROC_T6   INC_PROC_T6*DELAY
100 //Process 7
101 #define INC_PROC_T7                    17
102 #define DELAY_PROC_T7   INC_PROC_T7*DELAY
103 //Process 8
104 #define INC_PROC_T8                    19
105 #define DELAY_PROC_T8   INC_PROC_T8*DELAY
106
107 Semaphore sem;
108 unsigned int global_count = 0;
109
110 /*
111  * These macros generate the code needed to create the test process functions.
112  */
113 #define PROC_TEST(num) static void proc_semTest##num(void) \
114 { \
115         unsigned int local_count = 0; \
116         \
117         for (int i = 0; i < INC_PROC_T##num; ++i) \
118         { \
119                 sem_obtain(&sem); \
120                 kprintf("> test%d: Obtain semaphore.\n", num); \
121                 local_count = global_count; \
122                 kprintf("> test%d: Read global count [%d]\n", num, local_count); \
123                 timer_delay(DELAY_PROC_T##num); \
124                 local_count += INC_PROC_T##num; \
125                 global_count = local_count; \
126                 kprintf("> test%d: Update count g[%d] l[%d]\n", num, global_count, local_count); \
127                 sem_release(&sem); \
128                 kprintf("> test%d: Relase semaphore.\n", num); \
129         } \
130 } \
131
132 #define PROC_TEST_STACK(num)  PROC_DEFINE_STACK(proc_sem_test##num##_stack, KERN_MINSTACKSIZE * 2)
133 #define PROC_TEST_INIT(num)   proc_new(proc_semTest##num, NULL, sizeof(proc_sem_test##num##_stack), proc_sem_test##num##_stack);
134
135 // Define processes for the serialization test.
136 PROC_TEST(1)
137 PROC_TEST(2)
138 PROC_TEST(3)
139 PROC_TEST(4)
140 PROC_TEST(5)
141 PROC_TEST(6)
142 PROC_TEST(7)
143 PROC_TEST(8)
144
145 #if CONFIG_KERN_PRI
146
147 // Global settings for the priority inversion test.
148 // 0.5 secs, enough for seeing the effects
149 #define BASETIME 500
150
151 Semaphore s1, s2;
152 unsigned int loops = 0; // For counting iterations
153 int finishing_time[8];
154
155 typedef enum ProcType {NONE, S1, S2, S1S2} ProcType;
156 /*
157  * Macros for the processes of the priority inversion test.
158  */
159 #define PROC_INV_TEST(num) static void proc_semInvTest##num(void) \
160 { \
161         ProcType p_type = (ProcType)((int) proc_currentUserData()); \
162         int mult = p_type == NONE ? 5 : 1; \
163         unsigned int i, local_count = 0; \
164         ticks_t start; \
165         \
166         kprintf("> test%d(%d): Start.\n", num, proc_current()->link.pri); \
167         finishing_time[num-1] = timer_clock(); \
168         \
169         if (p_type == S1 || p_type == S1S2) { \
170                 kprintf("> test%d(prio=%d): Obtain %p..\n", num, \
171                                 proc_current()->link.pri, &s1); \
172                 sem_obtain(&s1); \
173                 kprintf("> test%d(prio=%d): Obtained %p.\n", num, \
174                                 proc_current()->link.pri, &s1); \
175         } \
176         if (p_type == S2 || p_type == S1S2) { \
177                 kprintf("> test%d(prio=%d): Obtain %p..\n", num, \
178                                 proc_current()->link.pri, &s2); \
179                 sem_obtain(&s2); \
180                 kprintf("> test%d(prio=%d): Obtained %p.\n", num, \
181                                 proc_current()->link.pri, &s2); \
182         } \
183         \
184         start = timer_clock(); \
185         for (i = 0; i < loops * mult && (((unsigned)timer_clock()-start) <= loops*mult); i++) { \
186                 local_count++; \
187         } \
188         \
189         sem_obtain(&sem); \
190         global_count += local_count; \
191         kprintf("> test%d(prio=%d): global_count=%u..\n", num, \
192                         proc_current()->link.pri, global_count); \
193         sem_release(&sem); \
194         \
195         if (p_type == S2 || p_type == S1S2) { \
196                 kprintf("> test%d(prio=%d): Release %p..\n", num, \
197                                 proc_current()->link.pri, &s2); \
198                 sem_release(&s2); \
199                 kprintf("> test%d(prio=%d): %p Released.\n", num, \
200                                 proc_current()->link.pri, &s2); \
201         } \
202         if (p_type == S1 || p_type == S1S2) { \
203                 kprintf("> test%d(prio=%d): Release %p..\n", num, \
204                                 proc_current()->link.pri, &s1); \
205                 sem_release(&s1); \
206                 kprintf("> test%d(prio=%d): %p Released.\n", num, \
207                                 proc_current()->link.pri, &s1); \
208         } \
209         \
210         finishing_time[num-1] = timer_clock() - finishing_time[num-1]; \
211         kprintf("> test%d(prio=%d): Exit.\n", num, proc_current()->link.pri); \
212 } \
213
214 #define PROC_INV_TEST_INIT(num, pri, type) \
215 do { \
216         struct Process *p; \
217         \
218         timer_delay(10); \
219         p = proc_new(proc_semInvTest##num, \
220                         ((void*)type), sizeof(proc_sem_test##num##_stack), \
221                         proc_sem_test##num##_stack); \
222         proc_setPri(p, pri); \
223 } while (0) \
224
225 // Define processes for the priority inversion test.
226 PROC_INV_TEST(1)
227 PROC_INV_TEST(2)
228 PROC_INV_TEST(3)
229 PROC_INV_TEST(4)
230 PROC_INV_TEST(5)
231 PROC_INV_TEST(6)
232 PROC_INV_TEST(7)
233 PROC_INV_TEST(8)
234
235 #endif /* CONFIG_KERN_PRI */
236
237 // Define process stacks for both of the tests.
238 PROC_TEST_STACK(1)
239 PROC_TEST_STACK(2)
240 PROC_TEST_STACK(3)
241 PROC_TEST_STACK(4)
242 PROC_TEST_STACK(5)
243 PROC_TEST_STACK(6)
244 PROC_TEST_STACK(7)
245 PROC_TEST_STACK(8)
246
247 static int sem_ser_test(void)
248 {
249         ticks_t start_time = timer_clock();
250
251         sem_init(&sem);
252         global_count = 0;
253
254         kprintf("Run semaphore serialization test..\n");
255
256         // Initialize the processes.
257         PROC_TEST_INIT(1)
258         PROC_TEST_INIT(2)
259         PROC_TEST_INIT(3)
260         PROC_TEST_INIT(4)
261         PROC_TEST_INIT(5)
262         PROC_TEST_INIT(6)
263         PROC_TEST_INIT(7)
264         PROC_TEST_INIT(8)
265         kputs("> Main: Processes created\n");
266
267         /*
268          * Wait until all processes exit, if something goes wrong we return an
269          * error after timeout_ms.
270          */
271         while((timer_clock() - start_time) < ms_to_ticks(TEST_TIME_OUT_MS))
272         {
273                 if (sem_attempt(&sem))
274                 {
275                         kputs("> Main: Check if test has finished..\n");
276                         if(global_count == MAX_GLOBAL_COUNT)
277                         {
278                                 kputs("> Main: Test Finished..Ok!\n");
279                                 return 0;
280                         }
281                         sem_release(&sem);
282                         kputs("> Main: Test is still running..\n");
283                 }
284                 proc_yield();
285         }
286
287         kputs("Semaphore serialization test failed..\n");
288         return -1;
289 }
290
291 #if CONFIG_KERN_PRI
292
293 static int sem_inv_test(void)
294 {
295         int i, orig_pri = proc_current()->link.pri;
296         ticks_t fake, start_time;
297
298         sem_init(&sem);
299         global_count = 0;
300         loops = 0;
301
302         sem_init(&s1);
303         sem_init(&s2);
304
305         kputs("> Main: calibration for the busy wait cycle..\n");
306         proc_setPri(proc_current(), 10);
307
308         fake = start_time = timer_clock();
309         while ((fake - start_time) < ms_to_ticks(BASETIME)) {
310                 fake = timer_clock();
311                 loops++;
312         }
313         kprintf("> Main: calibration done, %dms equals to %u cycles!\n", BASETIME, loops);
314
315         kputs("> Main: Run Priority Inversion test...\n");
316
317         // Will take s2
318         PROC_INV_TEST_INIT(1, 2, S2);
319
320         // 2 will block on s2; 3 will take s2 and still block on s2
321         PROC_INV_TEST_INIT(2, 3, S2);
322         PROC_INV_TEST_INIT(3, 4, S1S2);
323
324         // Will block on s1, nothing happens..
325         PROC_INV_TEST_INIT(4, 5, S1);
326
327         // No semaphore, without PI this will delay everyone!
328         PROC_INV_TEST_INIT(5, 6, NONE);
329
330         // Will block on s1 and boost
331         PROC_INV_TEST_INIT(6, 7, S1);
332         PROC_INV_TEST_INIT(7, 8, S1);
333         PROC_INV_TEST_INIT(8, 9, S1);
334
335         // All processes created, let them run.
336         proc_setPri(proc_current(), orig_pri);
337         while ((timer_clock() - start_time) < ms_to_ticks(TEST_TIME_OUT_MS*2)) {
338                 if (sem_attempt(&sem)) {
339                         if (global_count >= loops*7 + loops*5) {
340                                 for (i = 0; i < 8; i++)
341                                         kprintf("> Main: I-O latency of %d = %ldms\n", i+1, ms_to_ticks(finishing_time[i]));
342                                 kputs("> Main: Test Finished..Ok!\n");
343                                 return 0;
344                         }
345                         sem_release(&sem);
346                 }
347                 proc_yield();
348         }
349
350         kputs("> Main: Priority Inversion Test failed..\n");
351         return -1;
352 }
353
354 #else
355
356 void sem_inv_test(void)
357 {
358 }
359
360 #endif /* CONFIG_KERN_PRI */
361
362 /**
363  * Run semaphore test
364  */
365 int sem_testRun(void)
366 {
367         /* Start tests */
368         sem_ser_test();         // Serialization
369         sem_inv_test();         // Priority Inversion
370
371         return 0;
372 }
373
374 int sem_testSetup(void)
375 {
376         kdbg_init();
377
378         kprintf("Init Timer..");
379         timer_init();
380         kprintf("Done.\n");
381
382         kprintf("Init Process..");
383         proc_init();
384         kprintf("Done.\n");
385
386         return 0;
387 }
388
389 int sem_testTearDown(void)
390 {
391         kputs("TearDown Semaphore test.\n");
392         return 0;
393 }
394
395 TEST_MAIN(sem);