diff options
| author | Matthew Wilcox <matthew@wil.cx> | 2008-03-14 14:35:22 -0400 |
|---|---|---|
| committer | Matthew Wilcox <willy@linux.intel.com> | 2008-04-17 10:42:54 -0400 |
| commit | b17170b2fac96705db3188f093f89e8e838418e4 (patch) | |
| tree | 3264d8a297cff20338b606559274c36fbf663f04 /kernel | |
| parent | f1241c87a16c4fe9f4f51d6ed3589f031c505e8d (diff) | |
Simplify semaphore implementation
By removing the negative values of 'count' and relying on the wait_list to
indicate whether we have any waiters, we can simplify the implementation
by removing the protection against an unlikely race condition. Thanks to
David Howells for his suggestions.
Signed-off-by: Matthew Wilcox <willy@linux.intel.com>
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/semaphore.c | 78 |
1 files changed, 24 insertions, 54 deletions
diff --git a/kernel/semaphore.c b/kernel/semaphore.c index 5a12a8558982..bef977b16966 100644 --- a/kernel/semaphore.c +++ b/kernel/semaphore.c | |||
| @@ -18,18 +18,8 @@ | |||
| 18 | * down_trylock() and up() can be called from interrupt context. | 18 | * down_trylock() and up() can be called from interrupt context. |
| 19 | * So we have to disable interrupts when taking the lock. | 19 | * So we have to disable interrupts when taking the lock. |
| 20 | * | 20 | * |
| 21 | * The ->count variable, if positive, defines how many more tasks can | 21 | * The ->count variable defines how many more tasks can acquire the |
| 22 | * acquire the semaphore. If negative, it represents how many tasks are | 22 | * semaphore. If it's zero, there may be tasks waiting on the list. |
| 23 | * waiting on the semaphore (*). If zero, no tasks are waiting, and no more | ||
| 24 | * tasks can acquire the semaphore. | ||
| 25 | * | ||
| 26 | * (*) Except for the window between one task calling up() and the task | ||
| 27 | * sleeping in a __down_common() waking up. In order to avoid a third task | ||
| 28 | * coming in and stealing the second task's wakeup, we leave the ->count | ||
| 29 | * negative. If we have a more complex situation, the ->count may become | ||
| 30 | * zero or negative (eg a semaphore with count = 2, three tasks attempt to | ||
| 31 | * acquire it, one sleeps, two finish and call up(), the second task to call | ||
| 32 | * up() notices that the list is empty and just increments count). | ||
| 33 | */ | 23 | */ |
| 34 | 24 | ||
| 35 | static noinline void __down(struct semaphore *sem); | 25 | static noinline void __down(struct semaphore *sem); |
| @@ -43,7 +33,9 @@ void down(struct semaphore *sem) | |||
| 43 | unsigned long flags; | 33 | unsigned long flags; |
| 44 | 34 | ||
| 45 | spin_lock_irqsave(&sem->lock, flags); | 35 | spin_lock_irqsave(&sem->lock, flags); |
| 46 | if (unlikely(sem->count-- <= 0)) | 36 | if (likely(sem->count > 0)) |
| 37 | sem->count--; | ||
| 38 | else | ||
| 47 | __down(sem); | 39 | __down(sem); |
| 48 | spin_unlock_irqrestore(&sem->lock, flags); | 40 | spin_unlock_irqrestore(&sem->lock, flags); |
| 49 | } | 41 | } |
| @@ -55,7 +47,9 @@ int down_interruptible(struct semaphore *sem) | |||
| 55 | int result = 0; | 47 | int result = 0; |
| 56 | 48 | ||
| 57 | spin_lock_irqsave(&sem->lock, flags); | 49 | spin_lock_irqsave(&sem->lock, flags); |
| 58 | if (unlikely(sem->count-- <= 0)) | 50 | if (likely(sem->count > 0)) |
| 51 | sem->count--; | ||
| 52 | else | ||
| 59 | result = __down_interruptible(sem); | 53 | result = __down_interruptible(sem); |
| 60 | spin_unlock_irqrestore(&sem->lock, flags); | 54 | spin_unlock_irqrestore(&sem->lock, flags); |
| 61 | 55 | ||
| @@ -69,7 +63,9 @@ int down_killable(struct semaphore *sem) | |||
| 69 | int result = 0; | 63 | int result = 0; |
| 70 | 64 | ||
| 71 | spin_lock_irqsave(&sem->lock, flags); | 65 | spin_lock_irqsave(&sem->lock, flags); |
| 72 | if (unlikely(sem->count-- <= 0)) | 66 | if (likely(sem->count > 0)) |
| 67 | sem->count--; | ||
| 68 | else | ||
| 73 | result = __down_killable(sem); | 69 | result = __down_killable(sem); |
| 74 | spin_unlock_irqrestore(&sem->lock, flags); | 70 | spin_unlock_irqrestore(&sem->lock, flags); |
| 75 | 71 | ||
| @@ -111,7 +107,9 @@ int down_timeout(struct semaphore *sem, long jiffies) | |||
| 111 | int result = 0; | 107 | int result = 0; |
| 112 | 108 | ||
| 113 | spin_lock_irqsave(&sem->lock, flags); | 109 | spin_lock_irqsave(&sem->lock, flags); |
| 114 | if (unlikely(sem->count-- <= 0)) | 110 | if (likely(sem->count > 0)) |
| 111 | sem->count--; | ||
| 112 | else | ||
| 115 | result = __down_timeout(sem, jiffies); | 113 | result = __down_timeout(sem, jiffies); |
| 116 | spin_unlock_irqrestore(&sem->lock, flags); | 114 | spin_unlock_irqrestore(&sem->lock, flags); |
| 117 | 115 | ||
| @@ -124,7 +122,7 @@ void up(struct semaphore *sem) | |||
| 124 | unsigned long flags; | 122 | unsigned long flags; |
| 125 | 123 | ||
| 126 | spin_lock_irqsave(&sem->lock, flags); | 124 | spin_lock_irqsave(&sem->lock, flags); |
| 127 | if (likely(sem->count >= 0)) | 125 | if (likely(list_empty(&sem->wait_list))) |
| 128 | sem->count++; | 126 | sem->count++; |
| 129 | else | 127 | else |
| 130 | __up(sem); | 128 | __up(sem); |
| @@ -141,22 +139,6 @@ struct semaphore_waiter { | |||
| 141 | }; | 139 | }; |
| 142 | 140 | ||
| 143 | /* | 141 | /* |
| 144 | * Wake up a process waiting on a semaphore. We need to call this from both | ||
| 145 | * __up and __down_common as it's possible to race a task into the semaphore | ||
| 146 | * if it comes in at just the right time between two tasks calling up() and | ||
| 147 | * a third task waking up. This function assumes the wait_list is already | ||
| 148 | * checked for being non-empty. | ||
| 149 | */ | ||
| 150 | static noinline void __sched __up_down_common(struct semaphore *sem) | ||
| 151 | { | ||
| 152 | struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list, | ||
| 153 | struct semaphore_waiter, list); | ||
| 154 | list_del(&waiter->list); | ||
| 155 | waiter->up = 1; | ||
| 156 | wake_up_process(waiter->task); | ||
| 157 | } | ||
| 158 | |||
| 159 | /* | ||
| 160 | * Because this function is inlined, the 'state' parameter will be | 142 | * Because this function is inlined, the 'state' parameter will be |
| 161 | * constant, and thus optimised away by the compiler. Likewise the | 143 | * constant, and thus optimised away by the compiler. Likewise the |
| 162 | * 'timeout' parameter for the cases without timeouts. | 144 | * 'timeout' parameter for the cases without timeouts. |
| @@ -164,7 +146,6 @@ static noinline void __sched __up_down_common(struct semaphore *sem) | |||
| 164 | static inline int __sched __down_common(struct semaphore *sem, long state, | 146 | static inline int __sched __down_common(struct semaphore *sem, long state, |
| 165 | long timeout) | 147 | long timeout) |
| 166 | { | 148 | { |
| 167 | int result = 0; | ||
| 168 | struct task_struct *task = current; | 149 | struct task_struct *task = current; |
| 169 | struct semaphore_waiter waiter; | 150 | struct semaphore_waiter waiter; |
| 170 | 151 | ||
| @@ -184,28 +165,16 @@ static inline int __sched __down_common(struct semaphore *sem, long state, | |||
| 184 | timeout = schedule_timeout(timeout); | 165 | timeout = schedule_timeout(timeout); |
| 185 | spin_lock_irq(&sem->lock); | 166 | spin_lock_irq(&sem->lock); |
| 186 | if (waiter.up) | 167 | if (waiter.up) |
| 187 | goto woken; | 168 | return 0; |
| 188 | } | 169 | } |
| 189 | 170 | ||
| 190 | timed_out: | 171 | timed_out: |
| 191 | list_del(&waiter.list); | 172 | list_del(&waiter.list); |
| 192 | result = -ETIME; | 173 | return -ETIME; |
| 193 | goto woken; | 174 | |
| 194 | interrupted: | 175 | interrupted: |
| 195 | list_del(&waiter.list); | 176 | list_del(&waiter.list); |
| 196 | result = -EINTR; | 177 | return -EINTR; |
| 197 | woken: | ||
| 198 | /* | ||
| 199 | * Account for the process which woke us up. For the case where | ||
| 200 | * we're interrupted, we need to increment the count on our own | ||
| 201 | * behalf. I don't believe we can hit the case where the | ||
| 202 | * sem->count hits zero, *and* there's a second task sleeping, | ||
| 203 | * but it doesn't hurt, that's not a commonly exercised path and | ||
| 204 | * it's not a performance path either. | ||
| 205 | */ | ||
| 206 | if (unlikely((++sem->count >= 0) && !list_empty(&sem->wait_list))) | ||
| 207 | __up_down_common(sem); | ||
| 208 | return result; | ||
| 209 | } | 178 | } |
| 210 | 179 | ||
| 211 | static noinline void __sched __down(struct semaphore *sem) | 180 | static noinline void __sched __down(struct semaphore *sem) |
| @@ -230,8 +199,9 @@ static noinline int __sched __down_timeout(struct semaphore *sem, long jiffies) | |||
| 230 | 199 | ||
| 231 | static noinline void __sched __up(struct semaphore *sem) | 200 | static noinline void __sched __up(struct semaphore *sem) |
| 232 | { | 201 | { |
| 233 | if (unlikely(list_empty(&sem->wait_list))) | 202 | struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list, |
| 234 | sem->count++; | 203 | struct semaphore_waiter, list); |
| 235 | else | 204 | list_del(&waiter->list); |
| 236 | __up_down_common(sem); | 205 | waiter->up = 1; |
| 206 | wake_up_process(waiter->task); | ||
| 237 | } | 207 | } |
