Line data Source code
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 0 : void __sched down(struct semaphore *sem)
55 : {
56 : unsigned long flags;
57 :
58 : might_sleep();
59 0 : raw_spin_lock_irqsave(&sem->lock, flags);
60 0 : if (likely(sem->count > 0))
61 0 : sem->count--;
62 : else
63 0 : __down(sem);
64 0 : raw_spin_unlock_irqrestore(&sem->lock, flags);
65 0 : }
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 0 : int __sched down_interruptible(struct semaphore *sem)
78 : {
79 : unsigned long flags;
80 0 : int result = 0;
81 :
82 : might_sleep();
83 0 : raw_spin_lock_irqsave(&sem->lock, flags);
84 0 : if (likely(sem->count > 0))
85 0 : sem->count--;
86 : else
87 0 : result = __down_interruptible(sem);
88 0 : raw_spin_unlock_irqrestore(&sem->lock, flags);
89 :
90 0 : 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 0 : int __sched down_killable(struct semaphore *sem)
105 : {
106 : unsigned long flags;
107 0 : int result = 0;
108 :
109 : might_sleep();
110 0 : raw_spin_lock_irqsave(&sem->lock, flags);
111 0 : if (likely(sem->count > 0))
112 0 : sem->count--;
113 : else
114 0 : result = __down_killable(sem);
115 0 : raw_spin_unlock_irqrestore(&sem->lock, flags);
116 :
117 0 : 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 284 : int __sched down_trylock(struct semaphore *sem)
135 : {
136 : unsigned long flags;
137 : int count;
138 :
139 284 : raw_spin_lock_irqsave(&sem->lock, flags);
140 284 : count = sem->count - 1;
141 284 : if (likely(count >= 0))
142 284 : sem->count = count;
143 568 : raw_spin_unlock_irqrestore(&sem->lock, flags);
144 :
145 284 : 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 0 : int __sched down_timeout(struct semaphore *sem, long timeout)
160 : {
161 : unsigned long flags;
162 0 : int result = 0;
163 :
164 : might_sleep();
165 0 : raw_spin_lock_irqsave(&sem->lock, flags);
166 0 : if (likely(sem->count > 0))
167 0 : sem->count--;
168 : else
169 0 : result = __down_timeout(sem, timeout);
170 0 : raw_spin_unlock_irqrestore(&sem->lock, flags);
171 :
172 0 : 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 284 : void __sched up(struct semaphore *sem)
184 : {
185 : unsigned long flags;
186 :
187 284 : raw_spin_lock_irqsave(&sem->lock, flags);
188 568 : if (likely(list_empty(&sem->wait_list)))
189 284 : sem->count++;
190 : else
191 0 : __up(sem);
192 568 : raw_spin_unlock_irqrestore(&sem->lock, flags);
193 284 : }
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 0 : static inline int __sched ___down_common(struct semaphore *sem, long state,
210 : long timeout)
211 : {
212 : struct semaphore_waiter waiter;
213 :
214 0 : list_add_tail(&waiter.list, &sem->wait_list);
215 0 : waiter.task = current;
216 0 : waiter.up = false;
217 :
218 : for (;;) {
219 0 : if (signal_pending_state(state, current))
220 : goto interrupted;
221 0 : if (unlikely(timeout <= 0))
222 : goto timed_out;
223 0 : __set_current_state(state);
224 0 : raw_spin_unlock_irq(&sem->lock);
225 0 : timeout = schedule_timeout(timeout);
226 0 : raw_spin_lock_irq(&sem->lock);
227 0 : if (waiter.up)
228 : return 0;
229 : }
230 :
231 : timed_out:
232 0 : list_del(&waiter.list);
233 0 : return -ETIME;
234 :
235 : interrupted:
236 0 : list_del(&waiter.list);
237 0 : 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 0 : trace_contention_begin(sem, 0);
246 0 : ret = ___down_common(sem, state, timeout);
247 0 : trace_contention_end(sem, ret);
248 :
249 : return ret;
250 : }
251 :
252 0 : static noinline void __sched __down(struct semaphore *sem)
253 : {
254 0 : __down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
255 0 : }
256 :
257 0 : static noinline int __sched __down_interruptible(struct semaphore *sem)
258 : {
259 0 : return __down_common(sem, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
260 : }
261 :
262 0 : static noinline int __sched __down_killable(struct semaphore *sem)
263 : {
264 0 : return __down_common(sem, TASK_KILLABLE, MAX_SCHEDULE_TIMEOUT);
265 : }
266 :
267 0 : static noinline int __sched __down_timeout(struct semaphore *sem, long timeout)
268 : {
269 0 : return __down_common(sem, TASK_UNINTERRUPTIBLE, timeout);
270 : }
271 :
272 0 : static noinline void __sched __up(struct semaphore *sem)
273 : {
274 0 : struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list,
275 : struct semaphore_waiter, list);
276 0 : list_del(&waiter->list);
277 0 : waiter->up = true;
278 0 : wake_up_process(waiter->task);
279 0 : }
|