]> git.karo-electronics.de Git - karo-tx-linux.git/blob - kernel/rtmutex-tester.c
Merge branch 'yem-kconfig-rc-fixes' of git://gitorious.org/linux-kconfig/linux-kconfi...
[karo-tx-linux.git] / kernel / rtmutex-tester.c
1 /*
2  * RT-Mutex-tester: scriptable tester for rt mutexes
3  *
4  * started by Thomas Gleixner:
5  *
6  *  Copyright (C) 2006, Timesys Corp., Thomas Gleixner <tglx@timesys.com>
7  *
8  */
9 #include <linux/device.h>
10 #include <linux/kthread.h>
11 #include <linux/export.h>
12 #include <linux/sched.h>
13 #include <linux/sched/rt.h>
14 #include <linux/spinlock.h>
15 #include <linux/timer.h>
16 #include <linux/freezer.h>
17
18 #include "rtmutex.h"
19
20 #define MAX_RT_TEST_THREADS     8
21 #define MAX_RT_TEST_MUTEXES     8
22
23 static spinlock_t rttest_lock;
24 static atomic_t rttest_event;
25
26 struct test_thread_data {
27         int                     opcode;
28         int                     opdata;
29         int                     mutexes[MAX_RT_TEST_MUTEXES];
30         int                     event;
31         struct device           dev;
32 };
33
34 static struct test_thread_data thread_data[MAX_RT_TEST_THREADS];
35 static struct task_struct *threads[MAX_RT_TEST_THREADS];
36 static struct rt_mutex mutexes[MAX_RT_TEST_MUTEXES];
37
38 enum test_opcodes {
39         RTTEST_NOP = 0,
40         RTTEST_SCHEDOT,         /* 1 Sched other, data = nice */
41         RTTEST_SCHEDRT,         /* 2 Sched fifo, data = prio */
42         RTTEST_LOCK,            /* 3 Lock uninterruptible, data = lockindex */
43         RTTEST_LOCKNOWAIT,      /* 4 Lock uninterruptible no wait in wakeup, data = lockindex */
44         RTTEST_LOCKINT,         /* 5 Lock interruptible, data = lockindex */
45         RTTEST_LOCKINTNOWAIT,   /* 6 Lock interruptible no wait in wakeup, data = lockindex */
46         RTTEST_LOCKCONT,        /* 7 Continue locking after the wakeup delay */
47         RTTEST_UNLOCK,          /* 8 Unlock, data = lockindex */
48         /* 9, 10 - reserved for BKL commemoration */
49         RTTEST_SIGNAL = 11,     /* 11 Signal other test thread, data = thread id */
50         RTTEST_RESETEVENT = 98, /* 98 Reset event counter */
51         RTTEST_RESET = 99,      /* 99 Reset all pending operations */
52 };
53
54 static int handle_op(struct test_thread_data *td, int lockwakeup)
55 {
56         int i, id, ret = -EINVAL;
57
58         switch(td->opcode) {
59
60         case RTTEST_NOP:
61                 return 0;
62
63         case RTTEST_LOCKCONT:
64                 td->mutexes[td->opdata] = 1;
65                 td->event = atomic_add_return(1, &rttest_event);
66                 return 0;
67
68         case RTTEST_RESET:
69                 for (i = 0; i < MAX_RT_TEST_MUTEXES; i++) {
70                         if (td->mutexes[i] == 4) {
71                                 rt_mutex_unlock(&mutexes[i]);
72                                 td->mutexes[i] = 0;
73                         }
74                 }
75                 return 0;
76
77         case RTTEST_RESETEVENT:
78                 atomic_set(&rttest_event, 0);
79                 return 0;
80
81         default:
82                 if (lockwakeup)
83                         return ret;
84         }
85
86         switch(td->opcode) {
87
88         case RTTEST_LOCK:
89         case RTTEST_LOCKNOWAIT:
90                 id = td->opdata;
91                 if (id < 0 || id >= MAX_RT_TEST_MUTEXES)
92                         return ret;
93
94                 td->mutexes[id] = 1;
95                 td->event = atomic_add_return(1, &rttest_event);
96                 rt_mutex_lock(&mutexes[id]);
97                 td->event = atomic_add_return(1, &rttest_event);
98                 td->mutexes[id] = 4;
99                 return 0;
100
101         case RTTEST_LOCKINT:
102         case RTTEST_LOCKINTNOWAIT:
103                 id = td->opdata;
104                 if (id < 0 || id >= MAX_RT_TEST_MUTEXES)
105                         return ret;
106
107                 td->mutexes[id] = 1;
108                 td->event = atomic_add_return(1, &rttest_event);
109                 ret = rt_mutex_lock_interruptible(&mutexes[id], 0);
110                 td->event = atomic_add_return(1, &rttest_event);
111                 td->mutexes[id] = ret ? 0 : 4;
112                 return ret ? -EINTR : 0;
113
114         case RTTEST_UNLOCK:
115                 id = td->opdata;
116                 if (id < 0 || id >= MAX_RT_TEST_MUTEXES || td->mutexes[id] != 4)
117                         return ret;
118
119                 td->event = atomic_add_return(1, &rttest_event);
120                 rt_mutex_unlock(&mutexes[id]);
121                 td->event = atomic_add_return(1, &rttest_event);
122                 td->mutexes[id] = 0;
123                 return 0;
124
125         default:
126                 break;
127         }
128         return ret;
129 }
130
131 /*
132  * Schedule replacement for rtsem_down(). Only called for threads with
133  * PF_MUTEX_TESTER set.
134  *
135  * This allows us to have finegrained control over the event flow.
136  *
137  */
138 void schedule_rt_mutex_test(struct rt_mutex *mutex)
139 {
140         int tid, op, dat;
141         struct test_thread_data *td;
142
143         /* We have to lookup the task */
144         for (tid = 0; tid < MAX_RT_TEST_THREADS; tid++) {
145                 if (threads[tid] == current)
146                         break;
147         }
148
149         BUG_ON(tid == MAX_RT_TEST_THREADS);
150
151         td = &thread_data[tid];
152
153         op = td->opcode;
154         dat = td->opdata;
155
156         switch (op) {
157         case RTTEST_LOCK:
158         case RTTEST_LOCKINT:
159         case RTTEST_LOCKNOWAIT:
160         case RTTEST_LOCKINTNOWAIT:
161                 if (mutex != &mutexes[dat])
162                         break;
163
164                 if (td->mutexes[dat] != 1)
165                         break;
166
167                 td->mutexes[dat] = 2;
168                 td->event = atomic_add_return(1, &rttest_event);
169                 break;
170
171         default:
172                 break;
173         }
174
175         schedule();
176
177
178         switch (op) {
179         case RTTEST_LOCK:
180         case RTTEST_LOCKINT:
181                 if (mutex != &mutexes[dat])
182                         return;
183
184                 if (td->mutexes[dat] != 2)
185                         return;
186
187                 td->mutexes[dat] = 3;
188                 td->event = atomic_add_return(1, &rttest_event);
189                 break;
190
191         case RTTEST_LOCKNOWAIT:
192         case RTTEST_LOCKINTNOWAIT:
193                 if (mutex != &mutexes[dat])
194                         return;
195
196                 if (td->mutexes[dat] != 2)
197                         return;
198
199                 td->mutexes[dat] = 1;
200                 td->event = atomic_add_return(1, &rttest_event);
201                 return;
202
203         default:
204                 return;
205         }
206
207         td->opcode = 0;
208
209         for (;;) {
210                 set_current_state(TASK_INTERRUPTIBLE);
211
212                 if (td->opcode > 0) {
213                         int ret;
214
215                         set_current_state(TASK_RUNNING);
216                         ret = handle_op(td, 1);
217                         set_current_state(TASK_INTERRUPTIBLE);
218                         if (td->opcode == RTTEST_LOCKCONT)
219                                 break;
220                         td->opcode = ret;
221                 }
222
223                 /* Wait for the next command to be executed */
224                 schedule();
225         }
226
227         /* Restore previous command and data */
228         td->opcode = op;
229         td->opdata = dat;
230 }
231
232 static int test_func(void *data)
233 {
234         struct test_thread_data *td = data;
235         int ret;
236
237         current->flags |= PF_MUTEX_TESTER;
238         set_freezable();
239         allow_signal(SIGHUP);
240
241         for(;;) {
242
243                 set_current_state(TASK_INTERRUPTIBLE);
244
245                 if (td->opcode > 0) {
246                         set_current_state(TASK_RUNNING);
247                         ret = handle_op(td, 0);
248                         set_current_state(TASK_INTERRUPTIBLE);
249                         td->opcode = ret;
250                 }
251
252                 /* Wait for the next command to be executed */
253                 schedule();
254                 try_to_freeze();
255
256                 if (signal_pending(current))
257                         flush_signals(current);
258
259                 if(kthread_should_stop())
260                         break;
261         }
262         return 0;
263 }
264
265 /**
266  * sysfs_test_command - interface for test commands
267  * @dev:        thread reference
268  * @buf:        command for actual step
269  * @count:      length of buffer
270  *
271  * command syntax:
272  *
273  * opcode:data
274  */
275 static ssize_t sysfs_test_command(struct device *dev, struct device_attribute *attr,
276                                   const char *buf, size_t count)
277 {
278         struct sched_param schedpar;
279         struct test_thread_data *td;
280         char cmdbuf[32];
281         int op, dat, tid, ret;
282
283         td = container_of(dev, struct test_thread_data, dev);
284         tid = td->dev.id;
285
286         /* strings from sysfs write are not 0 terminated! */
287         if (count >= sizeof(cmdbuf))
288                 return -EINVAL;
289
290         /* strip of \n: */
291         if (buf[count-1] == '\n')
292                 count--;
293         if (count < 1)
294                 return -EINVAL;
295
296         memcpy(cmdbuf, buf, count);
297         cmdbuf[count] = 0;
298
299         if (sscanf(cmdbuf, "%d:%d", &op, &dat) != 2)
300                 return -EINVAL;
301
302         switch (op) {
303         case RTTEST_SCHEDOT:
304                 schedpar.sched_priority = 0;
305                 ret = sched_setscheduler(threads[tid], SCHED_NORMAL, &schedpar);
306                 if (ret)
307                         return ret;
308                 set_user_nice(current, 0);
309                 break;
310
311         case RTTEST_SCHEDRT:
312                 schedpar.sched_priority = dat;
313                 ret = sched_setscheduler(threads[tid], SCHED_FIFO, &schedpar);
314                 if (ret)
315                         return ret;
316                 break;
317
318         case RTTEST_SIGNAL:
319                 send_sig(SIGHUP, threads[tid], 0);
320                 break;
321
322         default:
323                 if (td->opcode > 0)
324                         return -EBUSY;
325                 td->opdata = dat;
326                 td->opcode = op;
327                 wake_up_process(threads[tid]);
328         }
329
330         return count;
331 }
332
333 /**
334  * sysfs_test_status - sysfs interface for rt tester
335  * @dev:        thread to query
336  * @buf:        char buffer to be filled with thread status info
337  */
338 static ssize_t sysfs_test_status(struct device *dev, struct device_attribute *attr,
339                                  char *buf)
340 {
341         struct test_thread_data *td;
342         struct task_struct *tsk;
343         char *curr = buf;
344         int i;
345
346         td = container_of(dev, struct test_thread_data, dev);
347         tsk = threads[td->dev.id];
348
349         spin_lock(&rttest_lock);
350
351         curr += sprintf(curr,
352                 "O: %4d, E:%8d, S: 0x%08lx, P: %4d, N: %4d, B: %p, M:",
353                 td->opcode, td->event, tsk->state,
354                         (MAX_RT_PRIO - 1) - tsk->prio,
355                         (MAX_RT_PRIO - 1) - tsk->normal_prio,
356                 tsk->pi_blocked_on);
357
358         for (i = MAX_RT_TEST_MUTEXES - 1; i >=0 ; i--)
359                 curr += sprintf(curr, "%d", td->mutexes[i]);
360
361         spin_unlock(&rttest_lock);
362
363         curr += sprintf(curr, ", T: %p, R: %p\n", tsk,
364                         mutexes[td->dev.id].owner);
365
366         return curr - buf;
367 }
368
369 static DEVICE_ATTR(status, 0600, sysfs_test_status, NULL);
370 static DEVICE_ATTR(command, 0600, NULL, sysfs_test_command);
371
372 static struct bus_type rttest_subsys = {
373         .name = "rttest",
374         .dev_name = "rttest",
375 };
376
377 static int init_test_thread(int id)
378 {
379         thread_data[id].dev.bus = &rttest_subsys;
380         thread_data[id].dev.id = id;
381
382         threads[id] = kthread_run(test_func, &thread_data[id], "rt-test-%d", id);
383         if (IS_ERR(threads[id]))
384                 return PTR_ERR(threads[id]);
385
386         return device_register(&thread_data[id].dev);
387 }
388
389 static int init_rttest(void)
390 {
391         int ret, i;
392
393         spin_lock_init(&rttest_lock);
394
395         for (i = 0; i < MAX_RT_TEST_MUTEXES; i++)
396                 rt_mutex_init(&mutexes[i]);
397
398         ret = subsys_system_register(&rttest_subsys, NULL);
399         if (ret)
400                 return ret;
401
402         for (i = 0; i < MAX_RT_TEST_THREADS; i++) {
403                 ret = init_test_thread(i);
404                 if (ret)
405                         break;
406                 ret = device_create_file(&thread_data[i].dev, &dev_attr_status);
407                 if (ret)
408                         break;
409                 ret = device_create_file(&thread_data[i].dev, &dev_attr_command);
410                 if (ret)
411                         break;
412         }
413
414         printk("Initializing RT-Tester: %s\n", ret ? "Failed" : "OK" );
415
416         return ret;
417 }
418
419 device_initcall(init_rttest);