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