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