Branch data 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 : 66022 : void down(struct semaphore *sem)
54 : : {
55 : : unsigned long flags;
56 : :
57 : 66022 : raw_spin_lock_irqsave(&sem->lock, flags);
58 [ + + ]: 66024 : if (likely(sem->count > 0))
59 : 65548 : sem->count--;
60 : : else
61 : 476 : __down(sem);
62 : 66024 : raw_spin_unlock_irqrestore(&sem->lock, flags);
63 : 66024 : }
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 : 1035 : int down_interruptible(struct semaphore *sem)
76 : : {
77 : : unsigned long flags;
78 : : int result = 0;
79 : :
80 : 1035 : raw_spin_lock_irqsave(&sem->lock, flags);
81 [ + + ]: 1035 : if (likely(sem->count > 0))
82 : 828 : sem->count--;
83 : : else
84 : 207 : result = __down_interruptible(sem);
85 : 828 : raw_spin_unlock_irqrestore(&sem->lock, flags);
86 : :
87 : 828 : 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 : : unsigned long flags;
104 : : 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 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 : 68041 : int down_trylock(struct semaphore *sem)
131 : : {
132 : : unsigned long flags;
133 : : int count;
134 : :
135 : 68041 : raw_spin_lock_irqsave(&sem->lock, flags);
136 : 68041 : count = sem->count - 1;
137 [ + + ]: 68041 : if (likely(count >= 0))
138 : 67477 : sem->count = count;
139 : 68041 : raw_spin_unlock_irqrestore(&sem->lock, flags);
140 : :
141 : 68041 : 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 : : unsigned long flags;
158 : : 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 : 134329 : void up(struct semaphore *sem)
179 : : {
180 : : unsigned long flags;
181 : :
182 : 134329 : raw_spin_lock_irqsave(&sem->lock, flags);
183 [ + + ]: 268658 : if (likely(list_empty(&sem->wait_list)))
184 : 133853 : sem->count++;
185 : : else
186 : 476 : __up(sem);
187 : 134329 : raw_spin_unlock_irqrestore(&sem->lock, flags);
188 : 134329 : }
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 : 683 : static inline int __sched __down_common(struct semaphore *sem, long state,
205 : : long timeout)
206 : : {
207 : : struct semaphore_waiter waiter;
208 : :
209 : 683 : list_add_tail(&waiter.list, &sem->wait_list);
210 : 683 : waiter.task = current;
211 : 683 : waiter.up = false;
212 : :
213 : : for (;;) {
214 [ + - ]: 1366 : if (signal_pending_state(state, current))
215 : : goto interrupted;
216 [ + - ]: 683 : if (unlikely(timeout <= 0))
217 : : goto timed_out;
218 : 683 : __set_current_state(state);
219 : 683 : raw_spin_unlock_irq(&sem->lock);
220 : 683 : timeout = schedule_timeout(timeout);
221 : 476 : raw_spin_lock_irq(&sem->lock);
222 [ - + ]: 476 : if (waiter.up)
223 : : return 0;
224 : : }
225 : :
226 : : timed_out:
227 : : list_del(&waiter.list);
228 : 0 : return -ETIME;
229 : :
230 : : interrupted:
231 : : list_del(&waiter.list);
232 : 0 : return -EINTR;
233 : : }
234 : :
235 : 476 : static noinline void __sched __down(struct semaphore *sem)
236 : : {
237 : 476 : __down_common(sem, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
238 : 476 : }
239 : :
240 : 207 : static noinline int __sched __down_interruptible(struct semaphore *sem)
241 : : {
242 : 207 : 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 : 476 : static noinline void __sched __up(struct semaphore *sem)
256 : : {
257 : 476 : struct semaphore_waiter *waiter = list_first_entry(&sem->wait_list,
258 : : struct semaphore_waiter, list);
259 : : list_del(&waiter->list);
260 : 476 : waiter->up = true;
261 : 476 : wake_up_process(waiter->task);
262 : 476 : }
|