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 :
36 : static noinline void __down(struct semaphore *sem);
37 : static noinline int __down_interruptible(struct semaphore *sem);
38 : static noinline int __down_killable(struct semaphore *sem);
39 : static noinline int __down_timeout(struct semaphore *sem, long timeout);
40 : static noinline void __up(struct semaphore *sem);
41 :
42 : /**
43 : * down - acquire the semaphore
44 : * @sem: the semaphore to be acquired
45 : *
46 : * Acquires the semaphore. If no more tasks are allowed to acquire the
47 : * semaphore, calling this function will put the task to sleep until the
48 : * semaphore is released.
49 : *
50 : * Use of this function is deprecated, please use down_interruptible() or
51 : * down_killable() instead.
52 : */
53 215 : void down(struct semaphore *sem)
54 : {
55 215 : unsigned long flags;
56 :
57 215 : raw_spin_lock_irqsave(&sem->lock, flags);
58 215 : if (likely(sem->count > 0))
59 215 : sem->count--;
60 : else
61 0 : __down(sem);
62 215 : raw_spin_unlock_irqrestore(&sem->lock, flags);
63 215 : }
64 : EXPORT_SYMBOL(down);
65 :
66 : /**
67 : * down_interruptible - acquire the semaphore unless interrupted
68 : * @sem: the semaphore to be acquired
69 : *
70 : * Attempts to acquire the semaphore. If no more tasks are allowed to
71 : * acquire the semaphore, calling this function will put the task to sleep.
72 : * If the sleep is interrupted by a signal, this function will return -EINTR.
73 : * If the semaphore is successfully acquired, this function returns 0.
74 : */
75 0 : int down_interruptible(struct semaphore *sem)
76 : {
77 0 : unsigned long flags;
78 0 : int result = 0;
79 :
80 0 : raw_spin_lock_irqsave(&sem->lock, flags);
81 0 : if (likely(sem->count > 0))
82 0 : sem->count--;
83 : else
84 0 : result = __down_interruptible(sem);
85 0 : raw_spin_unlock_irqrestore(&sem->lock, flags);
86 :
87 0 : return result;
88 : }
89 : EXPORT_SYMBOL(down_interruptible);
90 :
91 : /**
92 : * down_killable - acquire the semaphore unless killed
93 : * @sem: the semaphore to be acquired
94 : *
95 : * Attempts to acquire the semaphore. If no more tasks are allowed to
96 : * acquire the semaphore, calling this function will put the task to sleep.
97 : * If the sleep is interrupted by a fatal signal, this function will return
98 : * -EINTR. If the semaphore is successfully acquired, this function returns
99 : * 0.
100 : */
101 0 : int down_killable(struct semaphore *sem)
102 : {
103 0 : unsigned long flags;
104 0 : int result = 0;
105 :
106 0 : raw_spin_lock_irqsave(&sem->lock, flags);
107 0 : if (likely(sem->count > 0))
108 0 : sem->count--;
109 : else
110 0 : result = __down_killable(sem);
111 0 : raw_spin_unlock_irqrestore(&sem->lock, flags);
112 :
113 0 : return result;
114 : }
115 : EXPORT_SYMBOL(down_killable);
116 :
117 : /**
118 : * down_trylock - try to acquire the semaphore, without waiting
119 : * @sem: the semaphore to be acquired
120 : *
121 : * Try to acquire the semaphore atomically. Returns 0 if the semaphore has
122 : * been acquired successfully or 1 if it cannot be acquired.
123 : *
124 : * NOTE: This return value is inverted from both spin_trylock and
125 : * mutex_trylock! Be careful about this when converting code.
126 : *
127 : * Unlike mutex_trylock, this function can be used from interrupt context,
128 : * and the semaphore can be released by any task or interrupt.
129 : */
130 307 : int down_trylock(struct semaphore *sem)
131 : {
132 307 : unsigned long flags;
133 307 : int count;
134 :
135 307 : raw_spin_lock_irqsave(&sem->lock, flags);
136 307 : count = sem->count - 1;
137 307 : if (likely(count >= 0))
138 306 : sem->count = count;
139 307 : raw_spin_unlock_irqrestore(&sem->lock, flags);
140 :
141 307 : return (count < 0);
142 : }
143 : EXPORT_SYMBOL(down_trylock);
144 :
145 : /**
146 : * down_timeout - acquire the semaphore within a specified time
147 : * @sem: the semaphore to be acquired
148 : * @timeout: how long to wait before failing
149 : *
150 : * Attempts to acquire the semaphore. If no more tasks are allowed to
151 : * acquire the semaphore, calling this function will put the task to sleep.
152 : * If the semaphore is not released within the specified number of jiffies,
153 : * this function returns -ETIME. It returns 0 if the semaphore was acquired.
154 : */
155 0 : int down_timeout(struct semaphore *sem, long timeout)
156 : {
157 0 : unsigned long flags;
158 0 : int result = 0;
159 :
160 0 : raw_spin_lock_irqsave(&sem->lock, flags);
161 0 : if (likely(sem->count > 0))
162 0 : sem->count--;
163 : else
164 0 : result = __down_timeout(sem, timeout);
165 0 : raw_spin_unlock_irqrestore(&sem->lock, flags);
166 :
167 0 : return result;
168 : }
169 : EXPORT_SYMBOL(down_timeout);
170 :
171 : /**
172 : * up - release the semaphore
173 : * @sem: the semaphore to release
174 : *
175 : * Release the semaphore. Unlike mutexes, up() may be called from any
176 : * context and even by tasks which have never called down().
177 : */
178 521 : void up(struct semaphore *sem)
179 : {
180 521 : unsigned long flags;
181 :
182 521 : raw_spin_lock_irqsave(&sem->lock, flags);
183 521 : if (likely(list_empty(&sem->wait_list)))
184 521 : sem->count++;
185 : else
186 0 : __up(sem);
187 521 : raw_spin_unlock_irqrestore(&sem->lock, flags);
188 521 : }
189 : EXPORT_SYMBOL(up);
190 :
191 : /* Functions for the contended case */
192 :
193 : struct semaphore_waiter {
194 : struct list_head list;
195 : struct task_struct *task;
196 : bool up;
197 : };
198 :
199 : /*
200 : * Because this function is inlined, the 'state' parameter will be
201 : * constant, and thus optimised away by the compiler. Likewise the
202 : * 'timeout' parameter for the cases without timeouts.
203 : */
204 0 : static inline int __sched __down_common(struct semaphore *sem, long state,
205 : long timeout)
206 : {
207 0 : struct semaphore_waiter waiter;
208 :
209 0 : list_add_tail(&waiter.list, &sem->wait_list);
210 0 : waiter.task = current;
211 0 : waiter.up = false;
212 :
213 0 : for (;;) {
214 0 : if (signal_pending_state(state, current))
215 0 : goto interrupted;
216 0 : if (unlikely(timeout <= 0))
217 0 : goto timed_out;
218 0 : __set_current_state(state);
219 0 : raw_spin_unlock_irq(&sem->lock);
220 0 : timeout = schedule_timeout(timeout);
221 0 : raw_spin_lock_irq(&sem->lock);
222 0 : if (waiter.up)
223 : return 0;
224 : }
225 :
226 0 : timed_out:
227 0 : list_del(&waiter.list);
228 0 : return -ETIME;
229 :
230 0 : interrupted:
231 0 : list_del(&waiter.list);
232 0 : return -EINTR;
233 : }
234 :
235 0 : static noinline void __sched __down(struct semaphore *sem)
236 : {
237 0 : __down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
238 0 : }
239 :
240 0 : static noinline int __sched __down_interruptible(struct semaphore *sem)
241 : {
242 0 : return __down_common(sem, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
243 : }
244 :
245 0 : static noinline int __sched __down_killable(struct semaphore *sem)
246 : {
247 0 : return __down_common(sem, TASK_KILLABLE, MAX_SCHEDULE_TIMEOUT);
248 : }
249 :
250 0 : static noinline int __sched __down_timeout(struct semaphore *sem, long timeout)
251 : {
252 0 : return __down_common(sem, TASK_UNINTERRUPTIBLE, timeout);
253 : }
254 :
255 0 : static noinline void __sched __up(struct semaphore *sem)
256 : {
257 0 : struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list,
258 : struct semaphore_waiter, list);
259 0 : list_del(&waiter->list);
260 0 : waiter->up = true;
261 0 : wake_up_process(waiter->task);
262 0 : }
|