forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ww_mutex.h
569 lines (476 loc) · 13.7 KB
/
ww_mutex.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
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
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef WW_RT
#define MUTEX mutex
#define MUTEX_WAITER mutex_waiter
static inline struct mutex_waiter *
__ww_waiter_first(struct mutex *lock)
{
struct mutex_waiter *w;
w = list_first_entry(&lock->wait_list, struct mutex_waiter, list);
if (list_entry_is_head(w, &lock->wait_list, list))
return NULL;
return w;
}
static inline struct mutex_waiter *
__ww_waiter_next(struct mutex *lock, struct mutex_waiter *w)
{
w = list_next_entry(w, list);
if (list_entry_is_head(w, &lock->wait_list, list))
return NULL;
return w;
}
static inline struct mutex_waiter *
__ww_waiter_prev(struct mutex *lock, struct mutex_waiter *w)
{
w = list_prev_entry(w, list);
if (list_entry_is_head(w, &lock->wait_list, list))
return NULL;
return w;
}
static inline struct mutex_waiter *
__ww_waiter_last(struct mutex *lock)
{
struct mutex_waiter *w;
w = list_last_entry(&lock->wait_list, struct mutex_waiter, list);
if (list_entry_is_head(w, &lock->wait_list, list))
return NULL;
return w;
}
static inline void
__ww_waiter_add(struct mutex *lock, struct mutex_waiter *waiter, struct mutex_waiter *pos)
{
struct list_head *p = &lock->wait_list;
if (pos)
p = &pos->list;
__mutex_add_waiter(lock, waiter, p);
}
static inline struct task_struct *
__ww_mutex_owner(struct mutex *lock)
{
return __mutex_owner(lock);
}
static inline bool
__ww_mutex_has_waiters(struct mutex *lock)
{
return atomic_long_read(&lock->owner) & MUTEX_FLAG_WAITERS;
}
static inline void lock_wait_lock(struct mutex *lock)
{
raw_spin_lock(&lock->wait_lock);
}
static inline void unlock_wait_lock(struct mutex *lock)
{
raw_spin_unlock(&lock->wait_lock);
}
static inline void lockdep_assert_wait_lock_held(struct mutex *lock)
{
lockdep_assert_held(&lock->wait_lock);
}
#else /* WW_RT */
#define MUTEX rt_mutex
#define MUTEX_WAITER rt_mutex_waiter
static inline struct rt_mutex_waiter *
__ww_waiter_first(struct rt_mutex *lock)
{
struct rb_node *n = rb_first(&lock->rtmutex.waiters.rb_root);
if (!n)
return NULL;
return rb_entry(n, struct rt_mutex_waiter, tree_entry);
}
static inline struct rt_mutex_waiter *
__ww_waiter_next(struct rt_mutex *lock, struct rt_mutex_waiter *w)
{
struct rb_node *n = rb_next(&w->tree_entry);
if (!n)
return NULL;
return rb_entry(n, struct rt_mutex_waiter, tree_entry);
}
static inline struct rt_mutex_waiter *
__ww_waiter_prev(struct rt_mutex *lock, struct rt_mutex_waiter *w)
{
struct rb_node *n = rb_prev(&w->tree_entry);
if (!n)
return NULL;
return rb_entry(n, struct rt_mutex_waiter, tree_entry);
}
static inline struct rt_mutex_waiter *
__ww_waiter_last(struct rt_mutex *lock)
{
struct rb_node *n = rb_last(&lock->rtmutex.waiters.rb_root);
if (!n)
return NULL;
return rb_entry(n, struct rt_mutex_waiter, tree_entry);
}
static inline void
__ww_waiter_add(struct rt_mutex *lock, struct rt_mutex_waiter *waiter, struct rt_mutex_waiter *pos)
{
/* RT unconditionally adds the waiter first and then removes it on error */
}
static inline struct task_struct *
__ww_mutex_owner(struct rt_mutex *lock)
{
return rt_mutex_owner(&lock->rtmutex);
}
static inline bool
__ww_mutex_has_waiters(struct rt_mutex *lock)
{
return rt_mutex_has_waiters(&lock->rtmutex);
}
static inline void lock_wait_lock(struct rt_mutex *lock)
{
raw_spin_lock(&lock->rtmutex.wait_lock);
}
static inline void unlock_wait_lock(struct rt_mutex *lock)
{
raw_spin_unlock(&lock->rtmutex.wait_lock);
}
static inline void lockdep_assert_wait_lock_held(struct rt_mutex *lock)
{
lockdep_assert_held(&lock->rtmutex.wait_lock);
}
#endif /* WW_RT */
/*
* Wait-Die:
* The newer transactions are killed when:
* It (the new transaction) makes a request for a lock being held
* by an older transaction.
*
* Wound-Wait:
* The newer transactions are wounded when:
* An older transaction makes a request for a lock being held by
* the newer transaction.
*/
/*
* Associate the ww_mutex @ww with the context @ww_ctx under which we acquired
* it.
*/
static __always_inline void
ww_mutex_lock_acquired(struct ww_mutex *ww, struct ww_acquire_ctx *ww_ctx)
{
#ifdef DEBUG_WW_MUTEXES
/*
* If this WARN_ON triggers, you used ww_mutex_lock to acquire,
* but released with a normal mutex_unlock in this call.
*
* This should never happen, always use ww_mutex_unlock.
*/
DEBUG_LOCKS_WARN_ON(ww->ctx);
/*
* Not quite done after calling ww_acquire_done() ?
*/
DEBUG_LOCKS_WARN_ON(ww_ctx->done_acquire);
if (ww_ctx->contending_lock) {
/*
* After -EDEADLK you tried to
* acquire a different ww_mutex? Bad!
*/
DEBUG_LOCKS_WARN_ON(ww_ctx->contending_lock != ww);
/*
* You called ww_mutex_lock after receiving -EDEADLK,
* but 'forgot' to unlock everything else first?
*/
DEBUG_LOCKS_WARN_ON(ww_ctx->acquired > 0);
ww_ctx->contending_lock = NULL;
}
/*
* Naughty, using a different class will lead to undefined behavior!
*/
DEBUG_LOCKS_WARN_ON(ww_ctx->ww_class != ww->ww_class);
#endif
ww_ctx->acquired++;
ww->ctx = ww_ctx;
}
/*
* Determine if @a is 'less' than @b. IOW, either @a is a lower priority task
* or, when of equal priority, a younger transaction than @b.
*
* Depending on the algorithm, @a will either need to wait for @b, or die.
*/
static inline bool
__ww_ctx_less(struct ww_acquire_ctx *a, struct ww_acquire_ctx *b)
{
/*
* Can only do the RT prio for WW_RT, because task->prio isn't stable due to PI,
* so the wait_list ordering will go wobbly. rt_mutex re-queues the waiter and
* isn't affected by this.
*/
#ifdef WW_RT
/* kernel prio; less is more */
int a_prio = a->task->prio;
int b_prio = b->task->prio;
if (rt_prio(a_prio) || rt_prio(b_prio)) {
if (a_prio > b_prio)
return true;
if (a_prio < b_prio)
return false;
/* equal static prio */
if (dl_prio(a_prio)) {
if (dl_time_before(b->task->dl.deadline,
a->task->dl.deadline))
return true;
if (dl_time_before(a->task->dl.deadline,
b->task->dl.deadline))
return false;
}
/* equal prio */
}
#endif
/* FIFO order tie break -- bigger is younger */
return (signed long)(a->stamp - b->stamp) > 0;
}
/*
* Wait-Die; wake a lesser waiter context (when locks held) such that it can
* die.
*
* Among waiters with context, only the first one can have other locks acquired
* already (ctx->acquired > 0), because __ww_mutex_add_waiter() and
* __ww_mutex_check_kill() wake any but the earliest context.
*/
static bool
__ww_mutex_die(struct MUTEX *lock, struct MUTEX_WAITER *waiter,
struct ww_acquire_ctx *ww_ctx)
{
if (!ww_ctx->is_wait_die)
return false;
if (waiter->ww_ctx->acquired > 0 && __ww_ctx_less(waiter->ww_ctx, ww_ctx)) {
#ifndef WW_RT
debug_mutex_wake_waiter(lock, waiter);
#endif
wake_up_process(waiter->task);
}
return true;
}
/*
* Wound-Wait; wound a lesser @hold_ctx if it holds the lock.
*
* Wound the lock holder if there are waiters with more important transactions
* than the lock holders. Even if multiple waiters may wound the lock holder,
* it's sufficient that only one does.
*/
static bool __ww_mutex_wound(struct MUTEX *lock,
struct ww_acquire_ctx *ww_ctx,
struct ww_acquire_ctx *hold_ctx)
{
struct task_struct *owner = __ww_mutex_owner(lock);
lockdep_assert_wait_lock_held(lock);
/*
* Possible through __ww_mutex_add_waiter() when we race with
* ww_mutex_set_context_fastpath(). In that case we'll get here again
* through __ww_mutex_check_waiters().
*/
if (!hold_ctx)
return false;
/*
* Can have !owner because of __mutex_unlock_slowpath(), but if owner,
* it cannot go away because we'll have FLAG_WAITERS set and hold
* wait_lock.
*/
if (!owner)
return false;
if (ww_ctx->acquired > 0 && __ww_ctx_less(hold_ctx, ww_ctx)) {
hold_ctx->wounded = 1;
/*
* wake_up_process() paired with set_current_state()
* inserts sufficient barriers to make sure @owner either sees
* it's wounded in __ww_mutex_check_kill() or has a
* wakeup pending to re-read the wounded state.
*/
if (owner != current)
wake_up_process(owner);
return true;
}
return false;
}
/*
* We just acquired @lock under @ww_ctx, if there are more important contexts
* waiting behind us on the wait-list, check if they need to die, or wound us.
*
* See __ww_mutex_add_waiter() for the list-order construction; basically the
* list is ordered by stamp, smallest (oldest) first.
*
* This relies on never mixing wait-die/wound-wait on the same wait-list;
* which is currently ensured by that being a ww_class property.
*
* The current task must not be on the wait list.
*/
static void
__ww_mutex_check_waiters(struct MUTEX *lock, struct ww_acquire_ctx *ww_ctx)
{
struct MUTEX_WAITER *cur;
lockdep_assert_wait_lock_held(lock);
for (cur = __ww_waiter_first(lock); cur;
cur = __ww_waiter_next(lock, cur)) {
if (!cur->ww_ctx)
continue;
if (__ww_mutex_die(lock, cur, ww_ctx) ||
__ww_mutex_wound(lock, cur->ww_ctx, ww_ctx))
break;
}
}
/*
* After acquiring lock with fastpath, where we do not hold wait_lock, set ctx
* and wake up any waiters so they can recheck.
*/
static __always_inline void
ww_mutex_set_context_fastpath(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
{
ww_mutex_lock_acquired(lock, ctx);
/*
* The lock->ctx update should be visible on all cores before
* the WAITERS check is done, otherwise contended waiters might be
* missed. The contended waiters will either see ww_ctx == NULL
* and keep spinning, or it will acquire wait_lock, add itself
* to waiter list and sleep.
*/
smp_mb(); /* See comments above and below. */
/*
* [W] ww->ctx = ctx [W] MUTEX_FLAG_WAITERS
* MB MB
* [R] MUTEX_FLAG_WAITERS [R] ww->ctx
*
* The memory barrier above pairs with the memory barrier in
* __ww_mutex_add_waiter() and makes sure we either observe ww->ctx
* and/or !empty list.
*/
if (likely(!__ww_mutex_has_waiters(&lock->base)))
return;
/*
* Uh oh, we raced in fastpath, check if any of the waiters need to
* die or wound us.
*/
lock_wait_lock(&lock->base);
__ww_mutex_check_waiters(&lock->base, ctx);
unlock_wait_lock(&lock->base);
}
static __always_inline int
__ww_mutex_kill(struct MUTEX *lock, struct ww_acquire_ctx *ww_ctx)
{
if (ww_ctx->acquired > 0) {
#ifdef DEBUG_WW_MUTEXES
struct ww_mutex *ww;
ww = container_of(lock, struct ww_mutex, base);
DEBUG_LOCKS_WARN_ON(ww_ctx->contending_lock);
ww_ctx->contending_lock = ww;
#endif
return -EDEADLK;
}
return 0;
}
/*
* Check the wound condition for the current lock acquire.
*
* Wound-Wait: If we're wounded, kill ourself.
*
* Wait-Die: If we're trying to acquire a lock already held by an older
* context, kill ourselves.
*
* Since __ww_mutex_add_waiter() orders the wait-list on stamp, we only have to
* look at waiters before us in the wait-list.
*/
static inline int
__ww_mutex_check_kill(struct MUTEX *lock, struct MUTEX_WAITER *waiter,
struct ww_acquire_ctx *ctx)
{
struct ww_mutex *ww = container_of(lock, struct ww_mutex, base);
struct ww_acquire_ctx *hold_ctx = READ_ONCE(ww->ctx);
struct MUTEX_WAITER *cur;
if (ctx->acquired == 0)
return 0;
if (!ctx->is_wait_die) {
if (ctx->wounded)
return __ww_mutex_kill(lock, ctx);
return 0;
}
if (hold_ctx && __ww_ctx_less(ctx, hold_ctx))
return __ww_mutex_kill(lock, ctx);
/*
* If there is a waiter in front of us that has a context, then its
* stamp is earlier than ours and we must kill ourself.
*/
for (cur = __ww_waiter_prev(lock, waiter); cur;
cur = __ww_waiter_prev(lock, cur)) {
if (!cur->ww_ctx)
continue;
return __ww_mutex_kill(lock, ctx);
}
return 0;
}
/*
* Add @waiter to the wait-list, keep the wait-list ordered by stamp, smallest
* first. Such that older contexts are preferred to acquire the lock over
* younger contexts.
*
* Waiters without context are interspersed in FIFO order.
*
* Furthermore, for Wait-Die kill ourself immediately when possible (there are
* older contexts already waiting) to avoid unnecessary waiting and for
* Wound-Wait ensure we wound the owning context when it is younger.
*/
static inline int
__ww_mutex_add_waiter(struct MUTEX_WAITER *waiter,
struct MUTEX *lock,
struct ww_acquire_ctx *ww_ctx)
{
struct MUTEX_WAITER *cur, *pos = NULL;
bool is_wait_die;
if (!ww_ctx) {
__ww_waiter_add(lock, waiter, NULL);
return 0;
}
is_wait_die = ww_ctx->is_wait_die;
/*
* Add the waiter before the first waiter with a higher stamp.
* Waiters without a context are skipped to avoid starving
* them. Wait-Die waiters may die here. Wound-Wait waiters
* never die here, but they are sorted in stamp order and
* may wound the lock holder.
*/
for (cur = __ww_waiter_last(lock); cur;
cur = __ww_waiter_prev(lock, cur)) {
if (!cur->ww_ctx)
continue;
if (__ww_ctx_less(ww_ctx, cur->ww_ctx)) {
/*
* Wait-Die: if we find an older context waiting, there
* is no point in queueing behind it, as we'd have to
* die the moment it would acquire the lock.
*/
if (is_wait_die) {
int ret = __ww_mutex_kill(lock, ww_ctx);
if (ret)
return ret;
}
break;
}
pos = cur;
/* Wait-Die: ensure younger waiters die. */
__ww_mutex_die(lock, cur, ww_ctx);
}
__ww_waiter_add(lock, waiter, pos);
/*
* Wound-Wait: if we're blocking on a mutex owned by a younger context,
* wound that such that we might proceed.
*/
if (!is_wait_die) {
struct ww_mutex *ww = container_of(lock, struct ww_mutex, base);
/*
* See ww_mutex_set_context_fastpath(). Orders setting
* MUTEX_FLAG_WAITERS vs the ww->ctx load,
* such that either we or the fastpath will wound @ww->ctx.
*/
smp_mb();
__ww_mutex_wound(lock, ww_ctx, ww->ctx);
}
return 0;
}
static inline void __ww_mutex_unlock(struct ww_mutex *lock)
{
if (lock->ctx) {
#ifdef DEBUG_WW_MUTEXES
DEBUG_LOCKS_WARN_ON(!lock->ctx->acquired);
#endif
if (lock->ctx->acquired > 0)
lock->ctx->acquired--;
lock->ctx = NULL;
}
}