posix-mutex.c 24.1 KB
Newer Older
Philippe Gerum's avatar
Philippe Gerum committed
1
/*
2
 * Functional testing of the mutex implementation for Cobalt.
Philippe Gerum's avatar
Philippe Gerum committed
3
4
5
6
 *
 * Copyright (C) Gilles Chanteperdrix  <gilles.chanteperdrix@xenomai.org>,
 *               Marion Deveaud <marion.deveaud@siemens.com>,
 *               Jan Kiszka <jan.kiszka@siemens.com>
7
 *               Philippe Gerum <rpm@xenomai.org>
Philippe Gerum's avatar
Philippe Gerum committed
8
9
10
11
 *
 * Released under the terms of GPLv2.
 */
#include <stdio.h>
12
#include <limits.h>
Philippe Gerum's avatar
Philippe Gerum committed
13
14
15
16
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <stdarg.h>
17
18
#include <unistd.h>
#include <signal.h>
Philippe Gerum's avatar
Philippe Gerum committed
19
#include <pthread.h>
20
#include <cobalt/sys/cobalt.h>
21
22
#include <smokey/smokey.h>

23
smokey_test_plugin(posix_mutex,
24
		   SMOKEY_NOARGS,
25
		   "Check POSIX mutex services"
26
);
Philippe Gerum's avatar
Philippe Gerum committed
27

28
static const char *reason_str[] = {
29
	[SIGDEBUG_UNDEFINED] = "received SIGDEBUG for unknown reason",
30
31
32
33
	[SIGDEBUG_MIGRATE_SIGNAL] = "received signal",
	[SIGDEBUG_MIGRATE_SYSCALL] = "invoked syscall",
	[SIGDEBUG_MIGRATE_FAULT] = "triggered fault",
	[SIGDEBUG_MIGRATE_PRIOINV] = "affected by priority inversion",
34
35
36
	[SIGDEBUG_NOMLOCK] = "process memory not locked",
	[SIGDEBUG_WATCHDOG] = "watchdog triggered (period too short?)",
	[SIGDEBUG_LOCK_BREAK] = "scheduler lock break",
37
38
};

39
static void sigdebug(int sig, siginfo_t *si, void *context)
40
{
41
42
	const char fmt[] = "%s, this is unexpected.\n"
		"(enabling CONFIG_XENO_OPT_DEBUG_TRACE_RELAX may help)\n";
43
	unsigned int reason = sigdebug_reason(si);
44
45
	int n __attribute__ ((unused));
	static char buffer[256];
46

47
48
	if (reason > SIGDEBUG_WATCHDOG)
		reason = SIGDEBUG_UNDEFINED;
49

50
51
	n = snprintf(buffer, sizeof(buffer), fmt, reason_str[reason]);
	n = write(STDERR_FILENO, buffer, n);
52
53
}

54
55
56
57
58
#define THREAD_PRIO_WEAK	0
#define THREAD_PRIO_LOW		1
#define THREAD_PRIO_MEDIUM	2
#define THREAD_PRIO_HIGH	3
#define THREAD_PRIO_VERY_HIGH	4
59

60
61
62
63
64
65
66
#define MAX_100_MS  100000000ULL

struct locker_context {
	pthread_mutex_t *mutex;
	struct smokey_barrier *barrier;
	int lock_acquired;
};
Jan Kiszka's avatar
Jan Kiszka committed
67

68
static void sleep_ms(unsigned int ms)	/* < 1000 */
Philippe Gerum's avatar
Philippe Gerum committed
69
70
{
	struct timespec ts;
71
	
Philippe Gerum's avatar
Philippe Gerum committed
72
	ts.tv_sec = 0;
73
74
	ts.tv_nsec = ms * 1000000;
	clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
Philippe Gerum's avatar
Philippe Gerum committed
75
76
}

77
static int get_effective_prio(void) 
Philippe Gerum's avatar
Philippe Gerum committed
78
{
79
80
	struct cobalt_threadstat stat;
	int ret;
Philippe Gerum's avatar
Philippe Gerum committed
81

82
	ret = cobalt_thread_stat(0, &stat);
83
84
	if (ret)
		return ret;
Philippe Gerum's avatar
Philippe Gerum committed
85

86
	return stat.cprio;
Philippe Gerum's avatar
Philippe Gerum committed
87
88
}

89
90
static int create_thread(pthread_t *tid, int policy, int prio,
			 void *(*thread)(void *), void *arg)
Philippe Gerum's avatar
Philippe Gerum committed
91
{
92
93
94
	struct sched_param param;
	pthread_attr_t thattr;
	int ret;
Philippe Gerum's avatar
Philippe Gerum committed
95

96
97
98
99
100
	pthread_attr_init(&thattr);
	param.sched_priority = prio;
	pthread_attr_setschedpolicy(&thattr, policy);
	pthread_attr_setschedparam(&thattr, &param);
	pthread_attr_setinheritsched(&thattr, PTHREAD_EXPLICIT_SCHED);
Gilles Chanteperdrix's avatar
Gilles Chanteperdrix committed
101

102
103
	if (!__T(ret, pthread_create(tid, &thattr, thread, arg)))
		return ret;
Philippe Gerum's avatar
Philippe Gerum committed
104

105
106
	return 0;
}
107

108
static int do_init_mutexattr(pthread_mutexattr_t *mattr, int type, int protocol)
Philippe Gerum's avatar
Philippe Gerum committed
109
{
110
	int ret;
Philippe Gerum's avatar
Philippe Gerum committed
111

112
113
114
115
116
117
118
119
120
121
122
	if (!__T(ret, pthread_mutexattr_init(mattr)))
		return ret;
	
	if (!__T(ret, pthread_mutexattr_settype(mattr, type)))
		return ret;
	
	if (!__T(ret, pthread_mutexattr_setprotocol(mattr, protocol)))
		return ret;
	
	if (!__T(ret, pthread_mutexattr_setpshared(mattr, PTHREAD_PROCESS_PRIVATE)))
		return ret;
Philippe Gerum's avatar
Philippe Gerum committed
123

124
	return 0;
Philippe Gerum's avatar
Philippe Gerum committed
125
126
}

127
static int do_init_mutex(pthread_mutex_t *mutex, int type, int protocol)
Philippe Gerum's avatar
Philippe Gerum committed
128
{
129
130
	pthread_mutexattr_t mattr;
	int ret;
Philippe Gerum's avatar
Philippe Gerum committed
131

132
133
134
135
136
137
	ret = do_init_mutexattr(&mattr, type, protocol);
	if (ret)
		return ret;
	
	if (!__T(ret, pthread_mutex_init(mutex, &mattr)))
		return ret;
Philippe Gerum's avatar
Philippe Gerum committed
138

139
140
141
142
	if (!__T(ret, pthread_mutexattr_destroy(&mattr)))
		return ret;
	
	return 0;
Philippe Gerum's avatar
Philippe Gerum committed
143
144
}

145
static int do_init_mutex_ceiling(pthread_mutex_t *mutex, int type, int prio)
146
{
147
148
149
150
151
152
153
154
155
	pthread_mutexattr_t mattr;
	int ret;

	ret = do_init_mutexattr(&mattr, type, PTHREAD_PRIO_PROTECT);
	if (ret)
		return ret;
	
	if (!__T(ret, pthread_mutexattr_setprioceiling(&mattr, prio)))
		return ret;
156

157
158
159
160
161
162
163
	if (!__T(ret, pthread_mutex_init(mutex, &mattr)))
		return ret;

	if (!__T(ret, pthread_mutexattr_destroy(&mattr)))
		return ret;
	
	return 0;
164
165
}

166
static void *mutex_timed_locker(void *arg)
Philippe Gerum's avatar
Philippe Gerum committed
167
{
168
169
170
171
172
	struct locker_context *p = arg;
	struct timespec now, ts;
	int ret;

	clock_gettime(CLOCK_REALTIME, &now);
173
174
	/* 5ms (or 50ms in VM) from now */
	timespec_adds(&ts, &now, smokey_on_vm ? 50000000 : 5000000);
175
176
177
178
179
180
181

	if (p->barrier)
		smokey_barrier_release(p->barrier);
	
	if (__F(ret, pthread_mutex_timedlock(p->mutex, &ts)) &&
	    __Tassert(ret == -ETIMEDOUT))
		return (void *)1;
Philippe Gerum's avatar
Philippe Gerum committed
182

183
	return NULL;
Philippe Gerum's avatar
Philippe Gerum committed
184
185
}

186
static int do_timed_contend(pthread_mutex_t *mutex, int prio)
187
{
188
189
190
191
	struct locker_context args = { .barrier = NULL };
	pthread_t tid;
	void *status;
	int ret;
192

193
194
	if (!__T(ret, pthread_mutex_lock(mutex)))
		return ret;
195

196
197
198
199
200
201
202
203
	args.mutex = mutex;
	ret = create_thread(&tid, SCHED_FIFO, prio,
			    mutex_timed_locker, &args);
	if (ret)
		return ret;
	
	if (!__T(ret, pthread_join(tid, &status)))
		return ret;
204

205
206
	if (!__T(ret, pthread_mutex_unlock(mutex)))
		return ret;
207

208
209
	if (!__Fassert(status == NULL))
		return -EINVAL;
210

211
212
	if (!__T(ret, pthread_mutex_destroy(mutex)))
		return ret;
213

214
	return 0;
215
216
}

217
static void *mutex_locker(void *arg)
Philippe Gerum's avatar
Philippe Gerum committed
218
{
219
220
	struct locker_context *p = arg;
	int ret;
Philippe Gerum's avatar
Philippe Gerum committed
221

222
223
	if (!__T(ret, pthread_mutex_lock(p->mutex)))
		return (void *)(long)ret;
Philippe Gerum's avatar
Philippe Gerum committed
224

225
	p->lock_acquired = 1;
Philippe Gerum's avatar
Philippe Gerum committed
226

227
228
	if (!__T(ret, pthread_mutex_unlock(p->mutex)))
		return (void *)(long)ret;
Philippe Gerum's avatar
Philippe Gerum committed
229

230
	smokey_barrier_release(p->barrier);
Philippe Gerum's avatar
Philippe Gerum committed
231

232
	return NULL;
Philippe Gerum's avatar
Philippe Gerum committed
233
234
}

235
static int do_contend(pthread_mutex_t *mutex, int type)
236
{
237
238
239
240
241
	struct smokey_barrier barrier;
	struct locker_context args;
	pthread_t tid;
	void *status;
	int ret;
242

243
244
	if (!__T(ret, pthread_mutex_lock(mutex)))
		return ret;
245

246
247
248
249
250
251
252
	if (type == PTHREAD_MUTEX_RECURSIVE) {
		if (!__T(ret, pthread_mutex_lock(mutex)))
			return ret;
	} else if (type == PTHREAD_MUTEX_ERRORCHECK) {
		if (!__F(ret, pthread_mutex_lock(mutex)) ||
		    !__Tassert(ret == -EDEADLK))
			return -EINVAL;
253
254
	}

255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
	args.mutex = mutex;
	smokey_barrier_init(&barrier);
	args.barrier = &barrier;
	args.lock_acquired = 0;
	ret = create_thread(&tid, SCHED_FIFO, THREAD_PRIO_MEDIUM,
			    mutex_locker, &args);
	if (ret)
		return ret;

	if (!__T(ret, pthread_mutex_unlock(mutex)))
		return ret;

	if (type == PTHREAD_MUTEX_RECURSIVE) {
		if (!__T(ret, pthread_mutex_unlock(mutex)))
			return ret;
	} else if (type == PTHREAD_MUTEX_ERRORCHECK) {
		if (!__F(ret, pthread_mutex_unlock(mutex)) ||
		    !__Tassert(ret == -EPERM))
			return -EINVAL;
274
275
	}

276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
	/* Wait until locker runs through. */
	if (!__T(ret, smokey_barrier_wait(&barrier)))
		return ret;

	if (!__T(ret, pthread_mutex_lock(mutex)))
		return ret;

	if (!__T(ret, pthread_mutex_unlock(mutex)))
		return ret;

	if (!__T(ret, pthread_mutex_destroy(mutex)))
		return ret;

	if (!__T(ret, pthread_join(tid, &status)))
		return ret;

	if (!__Tassert(status == NULL))
		return -EINVAL;

295
296
	smokey_barrier_destroy(&barrier);

297
	return 0;
298
299
}

300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
static int do_destroy(pthread_mutex_t *mutex, pthread_mutex_t *invalmutex,
	int type)
{
	int ret;

	if (!__T(ret, pthread_mutex_destroy(mutex)))
		return ret;
	if (!__F(ret, pthread_mutex_destroy(invalmutex)) &&
		__Tassert(ret == -EINVAL))
		return -1;
	return 0;
}

static int static_init_normal_destroy(void)
{
	pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
	pthread_mutex_t invalmutex = PTHREAD_MUTEX_INITIALIZER;

	unsigned int invalmagic = ~0x86860303; // ~COBALT_MUTEX_MAGIC

	memcpy((char *)&invalmutex + sizeof(invalmutex) - sizeof(invalmagic),
		&invalmagic, sizeof(invalmagic));
	return do_destroy(&mutex, &invalmutex, PTHREAD_MUTEX_NORMAL);
}

325
static int static_init_normal_contend(void)
Philippe Gerum's avatar
Philippe Gerum committed
326
{
327
	pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
Philippe Gerum's avatar
Philippe Gerum committed
328

329
330
	return do_contend(&mutex, PTHREAD_MUTEX_NORMAL);
}
Philippe Gerum's avatar
Philippe Gerum committed
331

332
333
334
335
static int __dynamic_init_contend(int type)
{
	pthread_mutex_t mutex;
	int ret;
Philippe Gerum's avatar
Philippe Gerum committed
336

337
338
339
340
341
342
	ret = do_init_mutex(&mutex, type, PTHREAD_PRIO_NONE);
	if (ret)
		return ret;
	
	return do_contend(&mutex, type);
}
Philippe Gerum's avatar
Philippe Gerum committed
343

344
345
346
347
static int dynamic_init_normal_contend(void)
{
	return __dynamic_init_contend(PTHREAD_MUTEX_NORMAL);
}
Philippe Gerum's avatar
Philippe Gerum committed
348

349
350
351
352
353
354
355
356
357
358
359
360
static int static_init_recursive_destroy(void)
{
	pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
	pthread_mutex_t invalmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;

	unsigned int invalmagic = ~0x86860303; // ~COBALT_MUTEX_MAGIC

	memcpy((char *)&invalmutex + sizeof(invalmutex) - sizeof(invalmagic),
		&invalmagic, sizeof(invalmagic));
	return do_destroy(&mutex, &invalmutex, PTHREAD_MUTEX_RECURSIVE);
}

361
362
363
364
365
static int static_init_recursive_contend(void)
{
	pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;

	return do_contend(&mutex, PTHREAD_MUTEX_RECURSIVE);
Philippe Gerum's avatar
Philippe Gerum committed
366
367
}

368
static int dynamic_init_recursive_contend(void)
Jan Kiszka's avatar
Jan Kiszka committed
369
{
370
371
	return __dynamic_init_contend(PTHREAD_MUTEX_RECURSIVE);
}
Jan Kiszka's avatar
Jan Kiszka committed
372

373
374
375
376
377
378
379
380
381
382
383
384
static int static_init_errorcheck_destroy(void)
{
	pthread_mutex_t mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
	pthread_mutex_t invalmutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;

	unsigned int invalmagic = ~0x86860303; // ~COBALT_MUTEX_MAGIC

	memcpy((char *)&invalmutex + sizeof(invalmutex) - sizeof(invalmagic),
		&invalmagic, sizeof(invalmagic));
	return do_destroy(&mutex, &invalmutex, PTHREAD_MUTEX_ERRORCHECK);
}

385
386
387
static int static_init_errorcheck_contend(void)
{
	pthread_mutex_t mutex = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
Jan Kiszka's avatar
Jan Kiszka committed
388

389
390
	return do_contend(&mutex, PTHREAD_MUTEX_ERRORCHECK);
}
Jan Kiszka's avatar
Jan Kiszka committed
391

392
393
394
static int dynamic_init_errorcheck_contend(void)
{
	return __dynamic_init_contend(PTHREAD_MUTEX_ERRORCHECK);
Jan Kiszka's avatar
Jan Kiszka committed
395
396
}

397
static int timed_contend(void)
Jan Kiszka's avatar
Jan Kiszka committed
398
{
399
	pthread_mutex_t mutex;
400
	int ret;
Jan Kiszka's avatar
Jan Kiszka committed
401

402
403
404
405
	ret = do_init_mutex(&mutex, PTHREAD_MUTEX_NORMAL,
			    PTHREAD_PRIO_INHERIT);
	if (ret)
		return ret;
Jan Kiszka's avatar
Jan Kiszka committed
406

407
	return do_timed_contend(&mutex, THREAD_PRIO_MEDIUM);
Jan Kiszka's avatar
Jan Kiszka committed
408
409
}

410
static int weak_mode_switch(void)
Philippe Gerum's avatar
Philippe Gerum committed
411
{
412
413
	struct sched_param old_param, param = { .sched_priority = 0 };
	int old_policy, ret, mode;
414
	pthread_mutex_t mutex;
Philippe Gerum's avatar
Philippe Gerum committed
415

416
417
418
419
	ret = do_init_mutex(&mutex, PTHREAD_MUTEX_NORMAL,
			    PTHREAD_PRIO_INHERIT);
	if (ret)
		return ret;
420

421
	/* Save old schedparams, then switch to weak mode. */
Philippe Gerum's avatar
Philippe Gerum committed
422

423
424
425
	if (!__T(ret, pthread_getschedparam(pthread_self(),
					    &old_policy, &old_param)))
		return ret;
Philippe Gerum's avatar
Philippe Gerum committed
426

427
	/* Assume we are running SCHED_FIFO. */
Philippe Gerum's avatar
Philippe Gerum committed
428

429
430
431
	mode = cobalt_thread_mode();
	if (!__Fassert(mode & XNWEAK))
		return -EINVAL;
Philippe Gerum's avatar
Philippe Gerum committed
432

433
434
435
436
437
	/* Enter SCHED_WEAK scheduling. */
	
	if (!__T(ret, pthread_setschedparam(pthread_self(),
					    SCHED_OTHER, &param)))
		return ret;
Philippe Gerum's avatar
Philippe Gerum committed
438

439
440
441
	mode = cobalt_thread_mode();
	if (!__Tassert((mode & (XNWEAK|XNRELAX)) == (XNWEAK|XNRELAX)))
		return -EINVAL;
Philippe Gerum's avatar
Philippe Gerum committed
442

443
444
	if (!__T(ret, pthread_mutex_lock(&mutex)))
		return ret;
Philippe Gerum's avatar
Philippe Gerum committed
445

446
447
448
449
450
451
452
	/*
	 * Holding a mutex should have switched us out of relaxed
	 * mode despite being assigned to the SCHED_WEAK class.
	 */
	mode = cobalt_thread_mode();
	if (!__Tassert((mode & (XNWEAK|XNRELAX)) == XNWEAK))
		return -EINVAL;
Philippe Gerum's avatar
Philippe Gerum committed
453

454
455
	if (!__T(ret, pthread_mutex_unlock(&mutex)))
		return ret;
Philippe Gerum's avatar
Philippe Gerum committed
456

457
458
459
460
461
	/* Dropped it, we should have relaxed in the same move. */
	
	mode = cobalt_thread_mode();
	if (!__Tassert((mode & (XNWEAK|XNRELAX)) == (XNWEAK|XNRELAX)))
		return -EINVAL;
Philippe Gerum's avatar
Philippe Gerum committed
462

463
464
	if (!__T(ret, pthread_mutex_destroy(&mutex)))
		return ret;
Philippe Gerum's avatar
Philippe Gerum committed
465

466
	/* Leaving the SCHED_WEAK class. */
Philippe Gerum's avatar
Philippe Gerum committed
467

468
469
470
	if (!__T(ret, pthread_setschedparam(pthread_self(),
					    old_policy, &old_param)))
		return ret;
Philippe Gerum's avatar
Philippe Gerum committed
471

472
473
474
	mode = cobalt_thread_mode();
	if (!__Fassert(mode & XNWEAK))
		return -EINVAL;
Philippe Gerum's avatar
Philippe Gerum committed
475

476
	return 0;
Philippe Gerum's avatar
Philippe Gerum committed
477
478
}

479
static int do_pi_contend(int prio)
Philippe Gerum's avatar
Philippe Gerum committed
480
{
481
482
	struct smokey_barrier barrier;
	struct locker_context args;
483
	pthread_mutex_t mutex;
484
485
486
	pthread_t tid;
	void *status;
	int ret;
Philippe Gerum's avatar
Philippe Gerum committed
487

488
489
490
491
	ret = do_init_mutex(&mutex, PTHREAD_MUTEX_NORMAL,
			    PTHREAD_PRIO_INHERIT);
	if (ret)
		return ret;
Philippe Gerum's avatar
Philippe Gerum committed
492

493
494
	if (!__T(ret, pthread_mutex_lock(&mutex)))
		return ret;
Philippe Gerum's avatar
Philippe Gerum committed
495

496
497
498
499
500
501
502
	args.mutex = &mutex;
	smokey_barrier_init(&barrier);
	args.barrier = &barrier;
	ret = create_thread(&tid, SCHED_FIFO, prio,
			    mutex_timed_locker, &args);
	if (ret)
		return ret;
Philippe Gerum's avatar
Philippe Gerum committed
503

504
505
	if (!__T(ret, smokey_barrier_wait(&barrier)))
		return ret;
Philippe Gerum's avatar
Philippe Gerum committed
506

507
508
509
510
511
512
513
514
515
	/*
	 * Back while mutex_timed_locker is waiting. We should have
	 * been boosted by now.
	 */
	if (!__Tassert(get_effective_prio() == prio))
		return -EINVAL;
	
	if (!__T(ret, pthread_join(tid, &status)))
		return ret;
Philippe Gerum's avatar
Philippe Gerum committed
516

517
518
	if (!__T(ret, pthread_mutex_unlock(&mutex)))
		return ret;
Philippe Gerum's avatar
Philippe Gerum committed
519

520
521
	if (!__Fassert(status == NULL))
		return -EINVAL;
Philippe Gerum's avatar
Philippe Gerum committed
522

523
524
525
	if (!__T(ret, pthread_mutex_destroy(&mutex)))
		return ret;

526
527
	smokey_barrier_destroy(&barrier);

528
529
	return 0;
}
Philippe Gerum's avatar
Philippe Gerum committed
530

531
532
533
534
static int pi_contend(void)
{
	return do_pi_contend(THREAD_PRIO_HIGH);
}
Philippe Gerum's avatar
Philippe Gerum committed
535

536
537
538
539
static void *mutex_locker_steal(void *arg)
{
	struct locker_context *p = arg;
	int ret;
Philippe Gerum's avatar
Philippe Gerum committed
540

541
542
543
544
	smokey_barrier_release(p->barrier);
	
	if (!__T(ret, pthread_mutex_lock(p->mutex)))
		return (void *)(long)ret;
Philippe Gerum's avatar
Philippe Gerum committed
545

546
	p->lock_acquired = 1;
Philippe Gerum's avatar
Philippe Gerum committed
547

548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
	if (!__T(ret, pthread_mutex_unlock(p->mutex)))
		return (void *)(long)ret;

	return NULL;
}

static int do_steal(int may_steal)
{
	struct smokey_barrier barrier;
	struct locker_context args;
	pthread_mutex_t mutex;
	pthread_t tid;
	void *status;
	int ret;

	ret = do_init_mutex(&mutex, PTHREAD_MUTEX_NORMAL,
			    PTHREAD_PRIO_NONE);
	if (ret)
		return ret;

	if (!__T(ret, pthread_mutex_lock(&mutex)))
		return ret;

	args.mutex = &mutex;
	smokey_barrier_init(&barrier);
	args.barrier = &barrier;
	args.lock_acquired = 0;
	ret = create_thread(&tid, SCHED_FIFO, THREAD_PRIO_LOW,
			    mutex_locker_steal, &args);
	if (ret)
		return ret;

	/* Make sure the locker thread emerges... */
	if (!__T(ret, smokey_barrier_wait(&barrier)))
		return ret;

	/* ...and blocks waiting on the mutex. */
	sleep_ms(1);

	/*
	 * Back while mutex_locker should be blocking.
	 *
	 * If stealing is exercised, unlock then relock immediately:
	 * we should have kept the ownership of the mutex and the
	 * locker thread should not have grabbed it so far, because of
	 * our higher priority.
	 *
	 * If stealing should not happen, unlock, wait a moment then
	 * observe whether the locker thread was able to grab it as
	 * expected.
	 *
	 * CAUTION: don't use pthread_mutex_trylock() to re-grab the
	 * mutex, this is not going to do what you want, since there
	 * is no stealing from userland, so using a fast op which
	 * never enters the kernel won't help.
	 */
	if (!__T(ret, pthread_mutex_unlock(&mutex)))
		return ret;

	if (may_steal) {
		if (!__T(ret, pthread_mutex_lock(&mutex)))
			return ret;

		if (!__Fassert(args.lock_acquired))
			return -EINVAL;
	} else {
		sleep_ms(1);

		if (!__T(ret, pthread_mutex_lock(&mutex)))
			return ret;

		if (!__Tassert(args.lock_acquired))
			return -EINVAL;
Philippe Gerum's avatar
Philippe Gerum committed
621
622
	}

623
624
	if (!__T(ret, pthread_mutex_unlock(&mutex)))
		return ret;
Philippe Gerum's avatar
Philippe Gerum committed
625

626
627
	if (!__T(ret, pthread_join(tid, &status)))
		return ret;
Philippe Gerum's avatar
Philippe Gerum committed
628

629
630
	if (!__Tassert(status == NULL))
		return -EINVAL;
Philippe Gerum's avatar
Philippe Gerum committed
631

632
633
	if (!__T(ret, pthread_mutex_destroy(&mutex)))
		return ret;
Philippe Gerum's avatar
Philippe Gerum committed
634

635
636
	smokey_barrier_destroy(&barrier);

637
638
	return 0;
}
Philippe Gerum's avatar
Philippe Gerum committed
639

640
641
642
static int steal(void)
{
	return do_steal(1);
Philippe Gerum's avatar
Philippe Gerum committed
643
644
}

645
static int no_steal(void)
646
{
647
648
	return do_steal(0);
}
649

650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
/*
 * NOTE: Cobalt implements a lazy enforcement scheme for priority
 * protection of threads running in primary mode, which only registers
 * a pending boost at locking time, committing it eventually when/if
 * the owner thread schedules away while holding it. Entering a short
 * sleep (in primary mode) right after a mutex is grabbed makes sure
 * the boost is actually applied.
 */
static int protect_raise(void)
{
	pthread_mutex_t mutex;
	int ret;

	ret = do_init_mutex_ceiling(&mutex, PTHREAD_MUTEX_NORMAL,
				    THREAD_PRIO_HIGH);
	if (ret)
		return ret;
667

668
669
	if (!__T(ret, pthread_mutex_lock(&mutex)))
		return ret;
670

671
	sleep_ms(1);	/* Commit the pending PP request. */
672

673
674
675
676
677
678
679
680
681
	/* We should have been given a MEDIUM -> HIGH boost. */
	if (!__Tassert(get_effective_prio() == THREAD_PRIO_HIGH))
		return -EINVAL;
	
	if (!__T(ret, pthread_mutex_unlock(&mutex)))
		return ret;

	if (!__Tassert(get_effective_prio() == THREAD_PRIO_MEDIUM))
		return -EINVAL;
682
683
684
685

	if (!__T(ret, pthread_mutex_destroy(&mutex)))
		return ret;

686
	return 0;
687
688
}

689
static int protect_lower(void)
690
{
691
	pthread_mutex_t mutex;
692
	int ret;
693

694
695
696
697
	ret = do_init_mutex_ceiling(&mutex, PTHREAD_MUTEX_NORMAL,
				    THREAD_PRIO_LOW);
	if (ret)
		return ret;
698

699
700
	if (!__T(ret, pthread_mutex_lock(&mutex)))
		return ret;
701

702
	sleep_ms(1);	/* Commit the pending PP request. */
703

704
705
706
707
708
709
	/* No boost should be applied. */
	if (!__Tassert(get_effective_prio() == THREAD_PRIO_MEDIUM))
		return -EINVAL;
	
	if (!__T(ret, pthread_mutex_unlock(&mutex)))
		return ret;
710

711
712
	if (!__Tassert(get_effective_prio() == THREAD_PRIO_MEDIUM))
		return -EINVAL;
713
714
715
716

	if (!__T(ret, pthread_mutex_destroy(&mutex)))
		return ret;

717
718
	return 0;
}
719

720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
static int protect_weak(void)
{
	struct sched_param old_param, weak_param;
	pthread_mutex_t mutex;
	int ret, old_policy;

	if (!__T(ret, pthread_getschedparam(pthread_self(),
					    &old_policy, &old_param)))
		return ret;

	/*
	 * Switch to the SCHED_WEAK class if present. THREAD_PRIO_WEAK
	 * (0) is used to make this work even without SCHED_WEAK
	 * support.
	 */
	weak_param.sched_priority = THREAD_PRIO_WEAK;
	if (!__T(ret, pthread_setschedparam(pthread_self(),
					    SCHED_WEAK, &weak_param)))
		return ret;

	ret = do_init_mutex_ceiling(&mutex, PTHREAD_MUTEX_NORMAL,
				    THREAD_PRIO_HIGH);
	if (ret)
		return ret;

	if (!__T(ret, pthread_mutex_lock(&mutex)))
		return ret;

	sleep_ms(1);	/* Commit the pending PP request. */

	/* We should have been sent to SCHED_FIFO, THREAD_PRIO_HIGH. */
	if (!__Tassert(get_effective_prio() == THREAD_PRIO_HIGH))
		return -EINVAL;
	
	if (!__T(ret, pthread_mutex_unlock(&mutex)))
		return ret;

	/* Back to SCHED_WEAK, THREAD_PRIO_WEAK. */
	if (!__Tassert(get_effective_prio() == THREAD_PRIO_WEAK))
		return -EINVAL;

	if (!__T(ret, pthread_setschedparam(pthread_self(),
					    old_policy, &old_param)))
		return ret;
764
765
766
767

	if (!__T(ret, pthread_mutex_destroy(&mutex)))
		return ret;

768
769
	return 0;
}
770

771
772
773
774
static int protect_nesting_protect(void)
{
	pthread_mutex_t mutex_high, mutex_very_high;
	int ret;
775

776
777
778
779
	ret = do_init_mutex_ceiling(&mutex_high, PTHREAD_MUTEX_NORMAL,
				    THREAD_PRIO_HIGH);
	if (ret)
		return ret;
780

781
782
783
784
	ret = do_init_mutex_ceiling(&mutex_very_high, PTHREAD_MUTEX_NORMAL,
				    THREAD_PRIO_VERY_HIGH);
	if (ret)
		return ret;
785

786
787
	if (!__T(ret, pthread_mutex_lock(&mutex_high)))
		return ret;
788

789
	sleep_ms(1);	/* Commit the pending PP request. */
790

791
792
	if (!__Tassert(get_effective_prio() == THREAD_PRIO_HIGH))
		return -EINVAL;
Philippe Gerum's avatar
Philippe Gerum committed
793

794
795
	if (!__T(ret, pthread_mutex_lock(&mutex_very_high)))
		return ret;
Philippe Gerum's avatar
Philippe Gerum committed
796

797
	sleep_ms(1);	/* Commit the pending PP request. */
Philippe Gerum's avatar
Philippe Gerum committed
798

799
800
801
802
803
	if (!__Tassert(get_effective_prio() == THREAD_PRIO_VERY_HIGH))
		return -EINVAL;

	if (!__T(ret, pthread_mutex_unlock(&mutex_very_high)))
		return ret;
Philippe Gerum's avatar
Philippe Gerum committed
804

805
806
807
808
809
810
811
812
	if (!__Tassert(get_effective_prio() == THREAD_PRIO_HIGH))
		return -EINVAL;
	
	if (!__T(ret, pthread_mutex_unlock(&mutex_high)))
		return ret;

	if (!__Tassert(get_effective_prio() == THREAD_PRIO_MEDIUM))
		return -EINVAL;
813
814
815
816
817

	if (!__T(ret, pthread_mutex_destroy(&mutex_high)) ||
	    !__T(ret, pthread_mutex_destroy(&mutex_very_high)))
		return ret;

818
	return 0;
Philippe Gerum's avatar
Philippe Gerum committed
819
820
}

821
static int protect_nesting_pi(void)
Philippe Gerum's avatar
Philippe Gerum committed
822
{
823
824
	pthread_mutex_t mutex_pp;
	int ret;
825

826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
	ret = do_init_mutex_ceiling(&mutex_pp, PTHREAD_MUTEX_NORMAL,
				    THREAD_PRIO_HIGH);
	if (ret)
		return ret;

	if (!__T(ret, pthread_mutex_lock(&mutex_pp)))
		return ret;

	sleep_ms(1);	/* Commit the pending PP request. */

	/* PP ceiling: MEDIUM -> HIGH */
	if (!__Tassert(get_effective_prio() == THREAD_PRIO_HIGH))
		return -EINVAL;
	
	/* PI boost expected: HIGH -> VERY_HIGH, then back to HIGH */
	ret = do_pi_contend(THREAD_PRIO_VERY_HIGH);
	if (ret)
		return ret;

	if (!__Tassert(get_effective_prio() == THREAD_PRIO_HIGH))
		return -EINVAL;
	
	if (!__T(ret, pthread_mutex_unlock(&mutex_pp)))
		return ret;

	/* PP boost just dropped: HIGH -> MEDIUM. */
	if (!__Tassert(get_effective_prio() == THREAD_PRIO_MEDIUM))
		return -EINVAL;
854
855
856
857

	if (!__T(ret, pthread_mutex_destroy(&mutex_pp)))
		return ret;

858
	return 0;
Philippe Gerum's avatar
Philippe Gerum committed
859
860
}

861
static int protect_dynamic(void)
Philippe Gerum's avatar
Philippe Gerum committed
862
{
863
	pthread_mutex_t mutex;
864
	int ret, old_ceiling;
Philippe Gerum's avatar
Philippe Gerum committed
865

866
867
868
869
	ret = do_init_mutex_ceiling(&mutex, PTHREAD_MUTEX_NORMAL,
				    THREAD_PRIO_HIGH);
	if (ret)
		return ret;
870

871
872
873
	if (!__T(ret, pthread_mutex_setprioceiling(&mutex,
						   THREAD_PRIO_VERY_HIGH, &old_ceiling)))
		return ret;
Philippe Gerum's avatar
Philippe Gerum committed
874

875
876
877
878
879
880
881
	if (!__Tassert(old_ceiling == THREAD_PRIO_HIGH))
		return -EINVAL;

	if (!__T(ret, pthread_mutex_lock(&mutex)))
		return ret;

	sleep_ms(1);	/* Commit the pending PP request. */
882

883
884
885
886
887
888
	/* We should have been given a HIGH -> VERY_HIGH boost. */
	if (!__Tassert(get_effective_prio() == THREAD_PRIO_VERY_HIGH))
		return -EINVAL;
	
	if (!__T(ret, pthread_mutex_unlock(&mutex)))
		return ret;
889

890
891
892
893
894
895
896
897
898
899
900
901
902
903
	/* Drop the boost: VERY_HIGH -> MEDIUM. */
	if (!__Tassert(get_effective_prio() == THREAD_PRIO_MEDIUM))
		return -EINVAL;

	if (!__T(ret, pthread_mutex_getprioceiling(&mutex, &old_ceiling)))
		return ret;

	if (!__Tassert(old_ceiling == THREAD_PRIO_VERY_HIGH))
		return -EINVAL;

	if (!__T(ret, pthread_mutex_destroy(&mutex)))
		return ret;

	return 0;
904
905
}

906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
static int protect_trylock(void)
{
	pthread_mutex_t mutex;
	int ret;

	ret = do_init_mutex_ceiling(&mutex, PTHREAD_MUTEX_NORMAL,
				    THREAD_PRIO_HIGH);
	if (ret)
		return ret;

	/* make sure we are primary to take the fast-path */
	sleep_ms(1);

	if (!__T(ret, pthread_mutex_trylock(&mutex)))
		return ret;

	if (!__Tassert(pthread_mutex_trylock(&mutex) == EBUSY))
		return -EINVAL;

	sleep_ms(1);	/* Commit the pending PP request. */

	if (!__Tassert(get_effective_prio() == THREAD_PRIO_HIGH))
		return -EINVAL;

	if (!__T(ret, pthread_mutex_unlock(&mutex)))
		return ret;

	/* force to secondary to take the slow-path */
	__real_usleep(1);

	if (!__T(ret, pthread_mutex_trylock(&mutex)))
		return ret;

	/* force to secondary to take the slow-path */
	if (!__Tassert(pthread_mutex_trylock(&mutex) == EBUSY))
		return -EINVAL;

	sleep_ms(1);	/* Commit the pending PP request. */

	if (!__Tassert(get_effective_prio() == THREAD_PRIO_HIGH))
		return -EINVAL;

	if (!__T(ret, pthread_mutex_unlock(&mutex)))
		return ret;

	if (!__T(ret, pthread_mutex_destroy(&mutex)))
		return ret;

	return 0;
}

957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
static int protect_handover(void)
{
	struct smokey_barrier barrier;
	struct locker_context args;
	pthread_mutex_t mutex;
	pthread_t tid;
	int ret;

	ret = do_init_mutex_ceiling(&mutex, PTHREAD_MUTEX_NORMAL,
				    THREAD_PRIO_HIGH);
	if (ret)
		return ret;

	if (!__T(ret, pthread_mutex_lock(&mutex)))
		return ret;

	args.mutex = &mutex;
	smokey_barrier_init(&barrier);
	args.barrier = &barrier;
	args.lock_acquired = 0;
	ret = create_thread(&tid, SCHED_FIFO, THREAD_PRIO_LOW,
			    mutex_locker, &args);
	if (ret)
		return ret;

	sleep_ms(1);	/* Wait for the locker to contend on the mutex. */

	if (!__T(ret, pthread_mutex_unlock(&mutex)))
		return ret;

	/* Make sure the locker thread released the lock again. */
	if (!__T(ret, smokey_barrier_wait(&barrier)))
		return ret;

	if (!__T(ret, pthread_mutex_lock(&mutex)))
		return ret;

	if (!__T(ret, pthread_mutex_unlock(&mutex)))
		return ret;

	if (!__T(ret, pthread_mutex_destroy(&mutex)))
		return ret;

	return 0;