diff options
Diffstat (limited to 'kernel/locking/rwsem-xadd.c')
-rw-r--r-- | kernel/locking/rwsem-xadd.c | 92 |
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 | */ |
124 | static struct rw_semaphore * | 124 | static 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); |
615 | locked: | 607 | locked: |
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); |