aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/locking/rwsem-xadd.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/locking/rwsem-xadd.c')
-rw-r--r--kernel/locking/rwsem-xadd.c92
1 files changed, 41 insertions, 51 deletions
diff --git a/kernel/locking/rwsem-xadd.c b/kernel/locking/rwsem-xadd.c
index 447e08de1fab..2337b4bb2366 100644
--- a/kernel/locking/rwsem-xadd.c
+++ b/kernel/locking/rwsem-xadd.c
@@ -121,16 +121,19 @@ enum rwsem_wake_type {
121 * - woken process blocks are discarded from the list after having task zeroed 121 * - woken process blocks are discarded from the list after having task zeroed
122 * - writers are only marked woken if downgrading is false 122 * - writers are only marked woken if downgrading is false
123 */ 123 */
124static struct rw_semaphore * 124static void __rwsem_mark_wake(struct rw_semaphore *sem,
125__rwsem_mark_wake(struct rw_semaphore *sem, 125 enum rwsem_wake_type wake_type,
126 enum rwsem_wake_type wake_type, struct wake_q_head *wake_q) 126 struct wake_q_head *wake_q)
127{ 127{
128 struct rwsem_waiter *waiter; 128 struct rwsem_waiter *waiter, *tmp;
129 struct task_struct *tsk; 129 long oldcount, woken = 0, adjustment = 0;
130 struct list_head *next; 130
131 long oldcount, woken, loop, adjustment; 131 /*
132 * Take a peek at the queue head waiter such that we can determine
133 * the wakeup(s) to perform.
134 */
135 waiter = list_first_entry(&sem->wait_list, struct rwsem_waiter, list);
132 136
133 waiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list);
134 if (waiter->type == RWSEM_WAITING_FOR_WRITE) { 137 if (waiter->type == RWSEM_WAITING_FOR_WRITE) {
135 if (wake_type == RWSEM_WAKE_ANY) { 138 if (wake_type == RWSEM_WAKE_ANY) {
136 /* 139 /*
@@ -142,19 +145,19 @@ __rwsem_mark_wake(struct rw_semaphore *sem,
142 */ 145 */
143 wake_q_add(wake_q, waiter->task); 146 wake_q_add(wake_q, waiter->task);
144 } 147 }
145 goto out; 148
149 return;
146 } 150 }
147 151
148 /* Writers might steal the lock before we grant it to the next reader. 152 /*
153 * Writers might steal the lock before we grant it to the next reader.
149 * We prefer to do the first reader grant before counting readers 154 * We prefer to do the first reader grant before counting readers
150 * so we can bail out early if a writer stole the lock. 155 * so we can bail out early if a writer stole the lock.
151 */ 156 */
152 adjustment = 0;
153 if (wake_type != RWSEM_WAKE_READ_OWNED) { 157 if (wake_type != RWSEM_WAKE_READ_OWNED) {
154 adjustment = RWSEM_ACTIVE_READ_BIAS; 158 adjustment = RWSEM_ACTIVE_READ_BIAS;
155 try_reader_grant: 159 try_reader_grant:
156 oldcount = atomic_long_fetch_add(adjustment, &sem->count); 160 oldcount = atomic_long_fetch_add(adjustment, &sem->count);
157
158 if (unlikely(oldcount < RWSEM_WAITING_BIAS)) { 161 if (unlikely(oldcount < RWSEM_WAITING_BIAS)) {
159 /* 162 /*
160 * If the count is still less than RWSEM_WAITING_BIAS 163 * If the count is still less than RWSEM_WAITING_BIAS
@@ -164,7 +167,8 @@ __rwsem_mark_wake(struct rw_semaphore *sem,
164 */ 167 */
165 if (atomic_long_add_return(-adjustment, &sem->count) < 168 if (atomic_long_add_return(-adjustment, &sem->count) <
166 RWSEM_WAITING_BIAS) 169 RWSEM_WAITING_BIAS)
167 goto out; 170 return;
171
168 /* Last active locker left. Retry waking readers. */ 172 /* Last active locker left. Retry waking readers. */
169 goto try_reader_grant; 173 goto try_reader_grant;
170 } 174 }
@@ -176,38 +180,23 @@ __rwsem_mark_wake(struct rw_semaphore *sem,
176 rwsem_set_reader_owned(sem); 180 rwsem_set_reader_owned(sem);
177 } 181 }
178 182
179 /* Grant an infinite number of read locks to the readers at the front 183 /*
180 * of the queue. Note we increment the 'active part' of the count by 184 * Grant an infinite number of read locks to the readers at the front
181 * the number of readers before waking any processes up. 185 * of the queue. We know that woken will be at least 1 as we accounted
186 * for above. Note we increment the 'active part' of the count by the
187 * number of readers before waking any processes up.
182 */ 188 */
183 woken = 0; 189 list_for_each_entry_safe(waiter, tmp, &sem->wait_list, list) {
184 do { 190 struct task_struct *tsk;
185 woken++;
186 191
187 if (waiter->list.next == &sem->wait_list) 192 if (waiter->type == RWSEM_WAITING_FOR_WRITE)
188 break; 193 break;
189 194
190 waiter = list_entry(waiter->list.next, 195 woken++;
191 struct rwsem_waiter, list);
192
193 } while (waiter->type != RWSEM_WAITING_FOR_WRITE);
194
195 adjustment = woken * RWSEM_ACTIVE_READ_BIAS - adjustment;
196 if (waiter->type != RWSEM_WAITING_FOR_WRITE)
197 /* hit end of list above */
198 adjustment -= RWSEM_WAITING_BIAS;
199
200 if (adjustment)
201 atomic_long_add(adjustment, &sem->count);
202
203 next = sem->wait_list.next;
204 loop = woken;
205 do {
206 waiter = list_entry(next, struct rwsem_waiter, list);
207 next = waiter->list.next;
208 tsk = waiter->task; 196 tsk = waiter->task;
209 197
210 wake_q_add(wake_q, tsk); 198 wake_q_add(wake_q, tsk);
199 list_del(&waiter->list);
211 /* 200 /*
212 * Ensure that the last operation is setting the reader 201 * Ensure that the last operation is setting the reader
213 * waiter to nil such that rwsem_down_read_failed() cannot 202 * waiter to nil such that rwsem_down_read_failed() cannot
@@ -215,13 +204,16 @@ __rwsem_mark_wake(struct rw_semaphore *sem,
215 * to the task to wakeup. 204 * to the task to wakeup.
216 */ 205 */
217 smp_store_release(&waiter->task, NULL); 206 smp_store_release(&waiter->task, NULL);
218 } while (--loop); 207 }
219 208
220 sem->wait_list.next = next; 209 adjustment = woken * RWSEM_ACTIVE_READ_BIAS - adjustment;
221 next->prev = &sem->wait_list; 210 if (list_empty(&sem->wait_list)) {
211 /* hit end of list above */
212 adjustment -= RWSEM_WAITING_BIAS;
213 }
222 214
223 out: 215 if (adjustment)
224 return sem; 216 atomic_long_add(adjustment, &sem->count);
225} 217}
226 218
227/* 219/*
@@ -235,7 +227,6 @@ struct rw_semaphore __sched *rwsem_down_read_failed(struct rw_semaphore *sem)
235 struct task_struct *tsk = current; 227 struct task_struct *tsk = current;
236 WAKE_Q(wake_q); 228 WAKE_Q(wake_q);
237 229
238 /* set up my own style of waitqueue */
239 waiter.task = tsk; 230 waiter.task = tsk;
240 waiter.type = RWSEM_WAITING_FOR_READ; 231 waiter.type = RWSEM_WAITING_FOR_READ;
241 232
@@ -247,7 +238,8 @@ struct rw_semaphore __sched *rwsem_down_read_failed(struct rw_semaphore *sem)
247 /* we're now waiting on the lock, but no longer actively locking */ 238 /* we're now waiting on the lock, but no longer actively locking */
248 count = atomic_long_add_return(adjustment, &sem->count); 239 count = atomic_long_add_return(adjustment, &sem->count);
249 240
250 /* If there are no active locks, wake the front queued process(es). 241 /*
242 * If there are no active locks, wake the front queued process(es).
251 * 243 *
252 * If there are no writers and we are first in the queue, 244 * If there are no writers and we are first in the queue,
253 * wake our own waiter to join the existing active readers ! 245 * wake our own waiter to join the existing active readers !
@@ -255,7 +247,7 @@ struct rw_semaphore __sched *rwsem_down_read_failed(struct rw_semaphore *sem)
255 if (count == RWSEM_WAITING_BIAS || 247 if (count == RWSEM_WAITING_BIAS ||
256 (count > RWSEM_WAITING_BIAS && 248 (count > RWSEM_WAITING_BIAS &&
257 adjustment != -RWSEM_ACTIVE_READ_BIAS)) 249 adjustment != -RWSEM_ACTIVE_READ_BIAS))
258 sem = __rwsem_mark_wake(sem, RWSEM_WAKE_ANY, &wake_q); 250 __rwsem_mark_wake(sem, RWSEM_WAKE_ANY, &wake_q);
259 251
260 raw_spin_unlock_irq(&sem->wait_lock); 252 raw_spin_unlock_irq(&sem->wait_lock);
261 wake_up_q(&wake_q); 253 wake_up_q(&wake_q);
@@ -505,7 +497,7 @@ __rwsem_down_write_failed_common(struct rw_semaphore *sem, int state)
505 if (count > RWSEM_WAITING_BIAS) { 497 if (count > RWSEM_WAITING_BIAS) {
506 WAKE_Q(wake_q); 498 WAKE_Q(wake_q);
507 499
508 sem = __rwsem_mark_wake(sem, RWSEM_WAKE_READERS, &wake_q); 500 __rwsem_mark_wake(sem, RWSEM_WAKE_READERS, &wake_q);
509 /* 501 /*
510 * The wakeup is normally called _after_ the wait_lock 502 * The wakeup is normally called _after_ the wait_lock
511 * is released, but given that we are proactively waking 503 * is released, but given that we are proactively waking
@@ -614,9 +606,8 @@ struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem)
614 raw_spin_lock_irqsave(&sem->wait_lock, flags); 606 raw_spin_lock_irqsave(&sem->wait_lock, flags);
615locked: 607locked:
616 608
617 /* do nothing if list empty */
618 if (!list_empty(&sem->wait_list)) 609 if (!list_empty(&sem->wait_list))
619 sem = __rwsem_mark_wake(sem, RWSEM_WAKE_ANY, &wake_q); 610 __rwsem_mark_wake(sem, RWSEM_WAKE_ANY, &wake_q);
620 611
621 raw_spin_unlock_irqrestore(&sem->wait_lock, flags); 612 raw_spin_unlock_irqrestore(&sem->wait_lock, flags);
622 wake_up_q(&wake_q); 613 wake_up_q(&wake_q);
@@ -638,9 +629,8 @@ struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem)
638 629
639 raw_spin_lock_irqsave(&sem->wait_lock, flags); 630 raw_spin_lock_irqsave(&sem->wait_lock, flags);
640 631
641 /* do nothing if list empty */
642 if (!list_empty(&sem->wait_list)) 632 if (!list_empty(&sem->wait_list))
643 sem = __rwsem_mark_wake(sem, RWSEM_WAKE_READ_OWNED, &wake_q); 633 __rwsem_mark_wake(sem, RWSEM_WAKE_READ_OWNED, &wake_q);
644 634
645 raw_spin_unlock_irqrestore(&sem->wait_lock, flags); 635 raw_spin_unlock_irqrestore(&sem->wait_lock, flags);
646 wake_up_q(&wake_q); 636 wake_up_q(&wake_q);