Blame view
kernel/semaphore.c
7.23 KB
64ac24e73 Generic semaphore... |
1 2 3 4 5 |
/* * Copyright (c) 2008 Intel Corporation * Author: Matthew Wilcox <willy@linux.intel.com> * * Distributed under the terms of the GNU GPL, version 2 |
714493cd5 Improve semaphore... |
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
* * This file implements counting semaphores. * A counting semaphore may be acquired 'n' times before sleeping. * See mutex.c for single-acquisition sleeping locks which enforce * rules which allow code to be debugged more easily. */ /* * Some notes on the implementation: * * The spinlock controls access to the other members of the semaphore. * down_trylock() and up() can be called from interrupt context, so we * have to disable interrupts when taking the lock. It turns out various * parts of the kernel expect to be able to use down() on a semaphore in * interrupt context when they know it will succeed, so we have to use * irqsave variants for down(), down_interruptible() and down_killable() * too. * * The ->count variable represents how many more tasks can acquire this * semaphore. If it's zero, there may be tasks waiting on the wait_list. |
64ac24e73 Generic semaphore... |
26 27 28 29 |
*/ #include <linux/compiler.h> #include <linux/kernel.h> |
9984de1a5 kernel: Map most ... |
30 |
#include <linux/export.h> |
64ac24e73 Generic semaphore... |
31 32 33 |
#include <linux/sched.h> #include <linux/semaphore.h> #include <linux/spinlock.h> |
74f4e369f ftrace: stacktrac... |
34 |
#include <linux/ftrace.h> |
64ac24e73 Generic semaphore... |
35 |
|
64ac24e73 Generic semaphore... |
36 37 |
static noinline void __down(struct semaphore *sem); static noinline int __down_interruptible(struct semaphore *sem); |
f06d96865 Introduce down_ki... |
38 |
static noinline int __down_killable(struct semaphore *sem); |
f1241c87a Add down_timeout ... |
39 |
static noinline int __down_timeout(struct semaphore *sem, long jiffies); |
64ac24e73 Generic semaphore... |
40 |
static noinline void __up(struct semaphore *sem); |
714493cd5 Improve semaphore... |
41 42 43 44 45 46 47 48 49 50 51 |
/** * down - acquire the semaphore * @sem: the semaphore to be acquired * * Acquires the semaphore. If no more tasks are allowed to acquire the * semaphore, calling this function will put the task to sleep until the * semaphore is released. * * Use of this function is deprecated, please use down_interruptible() or * down_killable() instead. */ |
64ac24e73 Generic semaphore... |
52 53 54 |
void down(struct semaphore *sem) { unsigned long flags; |
8292c9e15 locking, semaphor... |
55 |
raw_spin_lock_irqsave(&sem->lock, flags); |
00b41ec26 Revert "semaphore... |
56 57 58 |
if (likely(sem->count > 0)) sem->count--; else |
64ac24e73 Generic semaphore... |
59 |
__down(sem); |
8292c9e15 locking, semaphor... |
60 |
raw_spin_unlock_irqrestore(&sem->lock, flags); |
64ac24e73 Generic semaphore... |
61 62 |
} EXPORT_SYMBOL(down); |
714493cd5 Improve semaphore... |
63 64 65 66 67 68 69 70 71 |
/** * down_interruptible - acquire the semaphore unless interrupted * @sem: the semaphore to be acquired * * Attempts to acquire the semaphore. If no more tasks are allowed to * acquire the semaphore, calling this function will put the task to sleep. * If the sleep is interrupted by a signal, this function will return -EINTR. * If the semaphore is successfully acquired, this function returns 0. */ |
64ac24e73 Generic semaphore... |
72 73 74 75 |
int down_interruptible(struct semaphore *sem) { unsigned long flags; int result = 0; |
8292c9e15 locking, semaphor... |
76 |
raw_spin_lock_irqsave(&sem->lock, flags); |
00b41ec26 Revert "semaphore... |
77 |
if (likely(sem->count > 0)) |
bf726eab3 semaphore: fix |
78 |
sem->count--; |
00b41ec26 Revert "semaphore... |
79 80 |
else result = __down_interruptible(sem); |
8292c9e15 locking, semaphor... |
81 |
raw_spin_unlock_irqrestore(&sem->lock, flags); |
64ac24e73 Generic semaphore... |
82 83 84 85 |
return result; } EXPORT_SYMBOL(down_interruptible); |
714493cd5 Improve semaphore... |
86 87 88 89 90 91 92 93 94 95 |
/** * down_killable - acquire the semaphore unless killed * @sem: the semaphore to be acquired * * Attempts to acquire the semaphore. If no more tasks are allowed to * acquire the semaphore, calling this function will put the task to sleep. * If the sleep is interrupted by a fatal signal, this function will return * -EINTR. If the semaphore is successfully acquired, this function returns * 0. */ |
f06d96865 Introduce down_ki... |
96 97 98 99 |
int down_killable(struct semaphore *sem) { unsigned long flags; int result = 0; |
8292c9e15 locking, semaphor... |
100 |
raw_spin_lock_irqsave(&sem->lock, flags); |
00b41ec26 Revert "semaphore... |
101 |
if (likely(sem->count > 0)) |
bf726eab3 semaphore: fix |
102 |
sem->count--; |
00b41ec26 Revert "semaphore... |
103 104 |
else result = __down_killable(sem); |
8292c9e15 locking, semaphor... |
105 |
raw_spin_unlock_irqrestore(&sem->lock, flags); |
f06d96865 Introduce down_ki... |
106 107 108 109 |
return result; } EXPORT_SYMBOL(down_killable); |
64ac24e73 Generic semaphore... |
110 111 112 113 114 |
/** * down_trylock - try to acquire the semaphore, without waiting * @sem: the semaphore to be acquired * * Try to acquire the semaphore atomically. Returns 0 if the mutex has |
714493cd5 Improve semaphore... |
115 |
* been acquired successfully or 1 if it it cannot be acquired. |
64ac24e73 Generic semaphore... |
116 117 118 119 120 121 122 123 124 125 126 |
* * NOTE: This return value is inverted from both spin_trylock and * mutex_trylock! Be careful about this when converting code. * * Unlike mutex_trylock, this function can be used from interrupt context, * and the semaphore can be released by any task or interrupt. */ int down_trylock(struct semaphore *sem) { unsigned long flags; int count; |
8292c9e15 locking, semaphor... |
127 |
raw_spin_lock_irqsave(&sem->lock, flags); |
64ac24e73 Generic semaphore... |
128 129 130 |
count = sem->count - 1; if (likely(count >= 0)) sem->count = count; |
8292c9e15 locking, semaphor... |
131 |
raw_spin_unlock_irqrestore(&sem->lock, flags); |
64ac24e73 Generic semaphore... |
132 133 134 135 |
return (count < 0); } EXPORT_SYMBOL(down_trylock); |
714493cd5 Improve semaphore... |
136 137 138 139 140 141 142 143 144 145 |
/** * down_timeout - acquire the semaphore within a specified time * @sem: the semaphore to be acquired * @jiffies: how long to wait before failing * * Attempts to acquire the semaphore. If no more tasks are allowed to * acquire the semaphore, calling this function will put the task to sleep. * If the semaphore is not released within the specified number of jiffies, * this function returns -ETIME. It returns 0 if the semaphore was acquired. */ |
f1241c87a Add down_timeout ... |
146 147 148 149 |
int down_timeout(struct semaphore *sem, long jiffies) { unsigned long flags; int result = 0; |
8292c9e15 locking, semaphor... |
150 |
raw_spin_lock_irqsave(&sem->lock, flags); |
00b41ec26 Revert "semaphore... |
151 |
if (likely(sem->count > 0)) |
bf726eab3 semaphore: fix |
152 |
sem->count--; |
00b41ec26 Revert "semaphore... |
153 154 |
else result = __down_timeout(sem, jiffies); |
8292c9e15 locking, semaphor... |
155 |
raw_spin_unlock_irqrestore(&sem->lock, flags); |
f1241c87a Add down_timeout ... |
156 157 158 159 |
return result; } EXPORT_SYMBOL(down_timeout); |
714493cd5 Improve semaphore... |
160 161 162 163 164 165 166 |
/** * up - release the semaphore * @sem: the semaphore to release * * Release the semaphore. Unlike mutexes, up() may be called from any * context and even by tasks which have never called down(). */ |
64ac24e73 Generic semaphore... |
167 168 169 |
void up(struct semaphore *sem) { unsigned long flags; |
8292c9e15 locking, semaphor... |
170 |
raw_spin_lock_irqsave(&sem->lock, flags); |
00b41ec26 Revert "semaphore... |
171 172 173 |
if (likely(list_empty(&sem->wait_list))) sem->count++; else |
64ac24e73 Generic semaphore... |
174 |
__up(sem); |
8292c9e15 locking, semaphor... |
175 |
raw_spin_unlock_irqrestore(&sem->lock, flags); |
64ac24e73 Generic semaphore... |
176 177 178 179 180 181 182 183 |
} EXPORT_SYMBOL(up); /* Functions for the contended case */ struct semaphore_waiter { struct list_head list; struct task_struct *task; |
00b41ec26 Revert "semaphore... |
184 |
int up; |
64ac24e73 Generic semaphore... |
185 186 187 |
}; /* |
f1241c87a Add down_timeout ... |
188 189 190 |
* Because this function is inlined, the 'state' parameter will be * constant, and thus optimised away by the compiler. Likewise the * 'timeout' parameter for the cases without timeouts. |
64ac24e73 Generic semaphore... |
191 |
*/ |
f1241c87a Add down_timeout ... |
192 193 |
static inline int __sched __down_common(struct semaphore *sem, long state, long timeout) |
64ac24e73 Generic semaphore... |
194 |
{ |
64ac24e73 Generic semaphore... |
195 196 |
struct task_struct *task = current; struct semaphore_waiter waiter; |
bf726eab3 semaphore: fix |
197 |
list_add_tail(&waiter.list, &sem->wait_list); |
00b41ec26 Revert "semaphore... |
198 199 |
waiter.task = task; waiter.up = 0; |
64ac24e73 Generic semaphore... |
200 201 |
for (;;) { |
5b2becc8c semaphore: __down... |
202 |
if (signal_pending_state(state, task)) |
00b41ec26 Revert "semaphore... |
203 204 205 |
goto interrupted; if (timeout <= 0) goto timed_out; |
64ac24e73 Generic semaphore... |
206 |
__set_task_state(task, state); |
8292c9e15 locking, semaphor... |
207 |
raw_spin_unlock_irq(&sem->lock); |
f1241c87a Add down_timeout ... |
208 |
timeout = schedule_timeout(timeout); |
8292c9e15 locking, semaphor... |
209 |
raw_spin_lock_irq(&sem->lock); |
00b41ec26 Revert "semaphore... |
210 211 |
if (waiter.up) return 0; |
64ac24e73 Generic semaphore... |
212 |
} |
00b41ec26 Revert "semaphore... |
213 214 215 216 217 |
timed_out: list_del(&waiter.list); return -ETIME; interrupted: |
64ac24e73 Generic semaphore... |
218 |
list_del(&waiter.list); |
00b41ec26 Revert "semaphore... |
219 |
return -EINTR; |
64ac24e73 Generic semaphore... |
220 221 222 223 |
} static noinline void __sched __down(struct semaphore *sem) { |
f1241c87a Add down_timeout ... |
224 |
__down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); |
64ac24e73 Generic semaphore... |
225 226 227 228 |
} static noinline int __sched __down_interruptible(struct semaphore *sem) { |
f1241c87a Add down_timeout ... |
229 |
return __down_common(sem, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); |
64ac24e73 Generic semaphore... |
230 |
} |
f06d96865 Introduce down_ki... |
231 232 |
static noinline int __sched __down_killable(struct semaphore *sem) { |
f1241c87a Add down_timeout ... |
233 234 235 236 237 238 |
return __down_common(sem, TASK_KILLABLE, MAX_SCHEDULE_TIMEOUT); } static noinline int __sched __down_timeout(struct semaphore *sem, long jiffies) { return __down_common(sem, TASK_UNINTERRUPTIBLE, jiffies); |
f06d96865 Introduce down_ki... |
239 |
} |
64ac24e73 Generic semaphore... |
240 241 |
static noinline void __sched __up(struct semaphore *sem) { |
b17170b2f Simplify semaphor... |
242 243 |
struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list, struct semaphore_waiter, list); |
00b41ec26 Revert "semaphore... |
244 245 |
list_del(&waiter->list); waiter->up = 1; |
b17170b2f Simplify semaphor... |
246 |
wake_up_process(waiter->task); |
64ac24e73 Generic semaphore... |
247 |
} |