diff options
Diffstat (limited to 'kernel/locking/rtmutex.c')
-rw-r--r-- | kernel/locking/rtmutex.c | 68 |
1 files changed, 66 insertions, 2 deletions
diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c index 1ec0f48962b3..2c49d76f96c3 100644 --- a/kernel/locking/rtmutex.c +++ b/kernel/locking/rtmutex.c | |||
@@ -65,8 +65,72 @@ static inline void clear_rt_mutex_waiters(struct rt_mutex *lock) | |||
65 | 65 | ||
66 | static void fixup_rt_mutex_waiters(struct rt_mutex *lock) | 66 | static void fixup_rt_mutex_waiters(struct rt_mutex *lock) |
67 | { | 67 | { |
68 | if (!rt_mutex_has_waiters(lock)) | 68 | unsigned long owner, *p = (unsigned long *) &lock->owner; |
69 | clear_rt_mutex_waiters(lock); | 69 | |
70 | if (rt_mutex_has_waiters(lock)) | ||
71 | return; | ||
72 | |||
73 | /* | ||
74 | * The rbtree has no waiters enqueued, now make sure that the | ||
75 | * lock->owner still has the waiters bit set, otherwise the | ||
76 | * following can happen: | ||
77 | * | ||
78 | * CPU 0 CPU 1 CPU2 | ||
79 | * l->owner=T1 | ||
80 | * rt_mutex_lock(l) | ||
81 | * lock(l->lock) | ||
82 | * l->owner = T1 | HAS_WAITERS; | ||
83 | * enqueue(T2) | ||
84 | * boost() | ||
85 | * unlock(l->lock) | ||
86 | * block() | ||
87 | * | ||
88 | * rt_mutex_lock(l) | ||
89 | * lock(l->lock) | ||
90 | * l->owner = T1 | HAS_WAITERS; | ||
91 | * enqueue(T3) | ||
92 | * boost() | ||
93 | * unlock(l->lock) | ||
94 | * block() | ||
95 | * signal(->T2) signal(->T3) | ||
96 | * lock(l->lock) | ||
97 | * dequeue(T2) | ||
98 | * deboost() | ||
99 | * unlock(l->lock) | ||
100 | * lock(l->lock) | ||
101 | * dequeue(T3) | ||
102 | * ==> wait list is empty | ||
103 | * deboost() | ||
104 | * unlock(l->lock) | ||
105 | * lock(l->lock) | ||
106 | * fixup_rt_mutex_waiters() | ||
107 | * if (wait_list_empty(l) { | ||
108 | * l->owner = owner | ||
109 | * owner = l->owner & ~HAS_WAITERS; | ||
110 | * ==> l->owner = T1 | ||
111 | * } | ||
112 | * lock(l->lock) | ||
113 | * rt_mutex_unlock(l) fixup_rt_mutex_waiters() | ||
114 | * if (wait_list_empty(l) { | ||
115 | * owner = l->owner & ~HAS_WAITERS; | ||
116 | * cmpxchg(l->owner, T1, NULL) | ||
117 | * ===> Success (l->owner = NULL) | ||
118 | * | ||
119 | * l->owner = owner | ||
120 | * ==> l->owner = T1 | ||
121 | * } | ||
122 | * | ||
123 | * With the check for the waiter bit in place T3 on CPU2 will not | ||
124 | * overwrite. All tasks fiddling with the waiters bit are | ||
125 | * serialized by l->lock, so nothing else can modify the waiters | ||
126 | * bit. If the bit is set then nothing can change l->owner either | ||
127 | * so the simple RMW is safe. The cmpxchg() will simply fail if it | ||
128 | * happens in the middle of the RMW because the waiters bit is | ||
129 | * still set. | ||
130 | */ | ||
131 | owner = READ_ONCE(*p); | ||
132 | if (owner & RT_MUTEX_HAS_WAITERS) | ||
133 | WRITE_ONCE(*p, owner & ~RT_MUTEX_HAS_WAITERS); | ||
70 | } | 134 | } |
71 | 135 | ||
72 | /* | 136 | /* |