diff options
Diffstat (limited to 'kernel/locking/rwsem-spinlock.c')
-rw-r--r-- | kernel/locking/rwsem-spinlock.c | 339 |
1 files changed, 0 insertions, 339 deletions
diff --git a/kernel/locking/rwsem-spinlock.c b/kernel/locking/rwsem-spinlock.c deleted file mode 100644 index a7ffb2a96ede..000000000000 --- a/kernel/locking/rwsem-spinlock.c +++ /dev/null | |||
@@ -1,339 +0,0 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* rwsem-spinlock.c: R/W semaphores: contention handling functions for | ||
3 | * generic spinlock implementation | ||
4 | * | ||
5 | * Copyright (c) 2001 David Howells (dhowells@redhat.com). | ||
6 | * - Derived partially from idea by Andrea Arcangeli <andrea@suse.de> | ||
7 | * - Derived also from comments by Linus | ||
8 | */ | ||
9 | #include <linux/rwsem.h> | ||
10 | #include <linux/sched/signal.h> | ||
11 | #include <linux/sched/debug.h> | ||
12 | #include <linux/export.h> | ||
13 | |||
14 | enum rwsem_waiter_type { | ||
15 | RWSEM_WAITING_FOR_WRITE, | ||
16 | RWSEM_WAITING_FOR_READ | ||
17 | }; | ||
18 | |||
19 | struct rwsem_waiter { | ||
20 | struct list_head list; | ||
21 | struct task_struct *task; | ||
22 | enum rwsem_waiter_type type; | ||
23 | }; | ||
24 | |||
25 | int rwsem_is_locked(struct rw_semaphore *sem) | ||
26 | { | ||
27 | int ret = 1; | ||
28 | unsigned long flags; | ||
29 | |||
30 | if (raw_spin_trylock_irqsave(&sem->wait_lock, flags)) { | ||
31 | ret = (sem->count != 0); | ||
32 | raw_spin_unlock_irqrestore(&sem->wait_lock, flags); | ||
33 | } | ||
34 | return ret; | ||
35 | } | ||
36 | EXPORT_SYMBOL(rwsem_is_locked); | ||
37 | |||
38 | /* | ||
39 | * initialise the semaphore | ||
40 | */ | ||
41 | void __init_rwsem(struct rw_semaphore *sem, const char *name, | ||
42 | struct lock_class_key *key) | ||
43 | { | ||
44 | #ifdef CONFIG_DEBUG_LOCK_ALLOC | ||
45 | /* | ||
46 | * Make sure we are not reinitializing a held semaphore: | ||
47 | */ | ||
48 | debug_check_no_locks_freed((void *)sem, sizeof(*sem)); | ||
49 | lockdep_init_map(&sem->dep_map, name, key, 0); | ||
50 | #endif | ||
51 | sem->count = 0; | ||
52 | raw_spin_lock_init(&sem->wait_lock); | ||
53 | INIT_LIST_HEAD(&sem->wait_list); | ||
54 | } | ||
55 | EXPORT_SYMBOL(__init_rwsem); | ||
56 | |||
57 | /* | ||
58 | * handle the lock release when processes blocked on it that can now run | ||
59 | * - if we come here, then: | ||
60 | * - the 'active count' _reached_ zero | ||
61 | * - the 'waiting count' is non-zero | ||
62 | * - the spinlock must be held by the caller | ||
63 | * - woken process blocks are discarded from the list after having task zeroed | ||
64 | * - writers are only woken if wakewrite is non-zero | ||
65 | */ | ||
66 | static inline struct rw_semaphore * | ||
67 | __rwsem_do_wake(struct rw_semaphore *sem, int wakewrite) | ||
68 | { | ||
69 | struct rwsem_waiter *waiter; | ||
70 | struct task_struct *tsk; | ||
71 | int woken; | ||
72 | |||
73 | waiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list); | ||
74 | |||
75 | if (waiter->type == RWSEM_WAITING_FOR_WRITE) { | ||
76 | if (wakewrite) | ||
77 | /* Wake up a writer. Note that we do not grant it the | ||
78 | * lock - it will have to acquire it when it runs. */ | ||
79 | wake_up_process(waiter->task); | ||
80 | goto out; | ||
81 | } | ||
82 | |||
83 | /* grant an infinite number of read locks to the front of the queue */ | ||
84 | woken = 0; | ||
85 | do { | ||
86 | struct list_head *next = waiter->list.next; | ||
87 | |||
88 | list_del(&waiter->list); | ||
89 | tsk = waiter->task; | ||
90 | /* | ||
91 | * Make sure we do not wakeup the next reader before | ||
92 | * setting the nil condition to grant the next reader; | ||
93 | * otherwise we could miss the wakeup on the other | ||
94 | * side and end up sleeping again. See the pairing | ||
95 | * in rwsem_down_read_failed(). | ||
96 | */ | ||
97 | smp_mb(); | ||
98 | waiter->task = NULL; | ||
99 | wake_up_process(tsk); | ||
100 | put_task_struct(tsk); | ||
101 | woken++; | ||
102 | if (next == &sem->wait_list) | ||
103 | break; | ||
104 | waiter = list_entry(next, struct rwsem_waiter, list); | ||
105 | } while (waiter->type != RWSEM_WAITING_FOR_WRITE); | ||
106 | |||
107 | sem->count += woken; | ||
108 | |||
109 | out: | ||
110 | return sem; | ||
111 | } | ||
112 | |||
113 | /* | ||
114 | * wake a single writer | ||
115 | */ | ||
116 | static inline struct rw_semaphore * | ||
117 | __rwsem_wake_one_writer(struct rw_semaphore *sem) | ||
118 | { | ||
119 | struct rwsem_waiter *waiter; | ||
120 | |||
121 | waiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list); | ||
122 | wake_up_process(waiter->task); | ||
123 | |||
124 | return sem; | ||
125 | } | ||
126 | |||
127 | /* | ||
128 | * get a read lock on the semaphore | ||
129 | */ | ||
130 | int __sched __down_read_common(struct rw_semaphore *sem, int state) | ||
131 | { | ||
132 | struct rwsem_waiter waiter; | ||
133 | unsigned long flags; | ||
134 | |||
135 | raw_spin_lock_irqsave(&sem->wait_lock, flags); | ||
136 | |||
137 | if (sem->count >= 0 && list_empty(&sem->wait_list)) { | ||
138 | /* granted */ | ||
139 | sem->count++; | ||
140 | raw_spin_unlock_irqrestore(&sem->wait_lock, flags); | ||
141 | goto out; | ||
142 | } | ||
143 | |||
144 | /* set up my own style of waitqueue */ | ||
145 | waiter.task = current; | ||
146 | waiter.type = RWSEM_WAITING_FOR_READ; | ||
147 | get_task_struct(current); | ||
148 | |||
149 | list_add_tail(&waiter.list, &sem->wait_list); | ||
150 | |||
151 | /* wait to be given the lock */ | ||
152 | for (;;) { | ||
153 | if (!waiter.task) | ||
154 | break; | ||
155 | if (signal_pending_state(state, current)) | ||
156 | goto out_nolock; | ||
157 | set_current_state(state); | ||
158 | raw_spin_unlock_irqrestore(&sem->wait_lock, flags); | ||
159 | schedule(); | ||
160 | raw_spin_lock_irqsave(&sem->wait_lock, flags); | ||
161 | } | ||
162 | |||
163 | raw_spin_unlock_irqrestore(&sem->wait_lock, flags); | ||
164 | out: | ||
165 | return 0; | ||
166 | |||
167 | out_nolock: | ||
168 | /* | ||
169 | * We didn't take the lock, so that there is a writer, which | ||
170 | * is owner or the first waiter of the sem. If it's a waiter, | ||
171 | * it will be woken by current owner. Not need to wake anybody. | ||
172 | */ | ||
173 | list_del(&waiter.list); | ||
174 | raw_spin_unlock_irqrestore(&sem->wait_lock, flags); | ||
175 | return -EINTR; | ||
176 | } | ||
177 | |||
178 | void __sched __down_read(struct rw_semaphore *sem) | ||
179 | { | ||
180 | __down_read_common(sem, TASK_UNINTERRUPTIBLE); | ||
181 | } | ||
182 | |||
183 | int __sched __down_read_killable(struct rw_semaphore *sem) | ||
184 | { | ||
185 | return __down_read_common(sem, TASK_KILLABLE); | ||
186 | } | ||
187 | |||
188 | /* | ||
189 | * trylock for reading -- returns 1 if successful, 0 if contention | ||
190 | */ | ||
191 | int __down_read_trylock(struct rw_semaphore *sem) | ||
192 | { | ||
193 | unsigned long flags; | ||
194 | int ret = 0; | ||
195 | |||
196 | |||
197 | raw_spin_lock_irqsave(&sem->wait_lock, flags); | ||
198 | |||
199 | if (sem->count >= 0 && list_empty(&sem->wait_list)) { | ||
200 | /* granted */ | ||
201 | sem->count++; | ||
202 | ret = 1; | ||
203 | } | ||
204 | |||
205 | raw_spin_unlock_irqrestore(&sem->wait_lock, flags); | ||
206 | |||
207 | return ret; | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | * get a write lock on the semaphore | ||
212 | */ | ||
213 | int __sched __down_write_common(struct rw_semaphore *sem, int state) | ||
214 | { | ||
215 | struct rwsem_waiter waiter; | ||
216 | unsigned long flags; | ||
217 | int ret = 0; | ||
218 | |||
219 | raw_spin_lock_irqsave(&sem->wait_lock, flags); | ||
220 | |||
221 | /* set up my own style of waitqueue */ | ||
222 | waiter.task = current; | ||
223 | waiter.type = RWSEM_WAITING_FOR_WRITE; | ||
224 | list_add_tail(&waiter.list, &sem->wait_list); | ||
225 | |||
226 | /* wait for someone to release the lock */ | ||
227 | for (;;) { | ||
228 | /* | ||
229 | * That is the key to support write lock stealing: allows the | ||
230 | * task already on CPU to get the lock soon rather than put | ||
231 | * itself into sleep and waiting for system woke it or someone | ||
232 | * else in the head of the wait list up. | ||
233 | */ | ||
234 | if (sem->count == 0) | ||
235 | break; | ||
236 | if (signal_pending_state(state, current)) | ||
237 | goto out_nolock; | ||
238 | |||
239 | set_current_state(state); | ||
240 | raw_spin_unlock_irqrestore(&sem->wait_lock, flags); | ||
241 | schedule(); | ||
242 | raw_spin_lock_irqsave(&sem->wait_lock, flags); | ||
243 | } | ||
244 | /* got the lock */ | ||
245 | sem->count = -1; | ||
246 | list_del(&waiter.list); | ||
247 | |||
248 | raw_spin_unlock_irqrestore(&sem->wait_lock, flags); | ||
249 | |||
250 | return ret; | ||
251 | |||
252 | out_nolock: | ||
253 | list_del(&waiter.list); | ||
254 | if (!list_empty(&sem->wait_list) && sem->count >= 0) | ||
255 | __rwsem_do_wake(sem, 0); | ||
256 | raw_spin_unlock_irqrestore(&sem->wait_lock, flags); | ||
257 | |||
258 | return -EINTR; | ||
259 | } | ||
260 | |||
261 | void __sched __down_write(struct rw_semaphore *sem) | ||
262 | { | ||
263 | __down_write_common(sem, TASK_UNINTERRUPTIBLE); | ||
264 | } | ||
265 | |||
266 | int __sched __down_write_killable(struct rw_semaphore *sem) | ||
267 | { | ||
268 | return __down_write_common(sem, TASK_KILLABLE); | ||
269 | } | ||
270 | |||
271 | /* | ||
272 | * trylock for writing -- returns 1 if successful, 0 if contention | ||
273 | */ | ||
274 | int __down_write_trylock(struct rw_semaphore *sem) | ||
275 | { | ||
276 | unsigned long flags; | ||
277 | int ret = 0; | ||
278 | |||
279 | raw_spin_lock_irqsave(&sem->wait_lock, flags); | ||
280 | |||
281 | if (sem->count == 0) { | ||
282 | /* got the lock */ | ||
283 | sem->count = -1; | ||
284 | ret = 1; | ||
285 | } | ||
286 | |||
287 | raw_spin_unlock_irqrestore(&sem->wait_lock, flags); | ||
288 | |||
289 | return ret; | ||
290 | } | ||
291 | |||
292 | /* | ||
293 | * release a read lock on the semaphore | ||
294 | */ | ||
295 | void __up_read(struct rw_semaphore *sem) | ||
296 | { | ||
297 | unsigned long flags; | ||
298 | |||
299 | raw_spin_lock_irqsave(&sem->wait_lock, flags); | ||
300 | |||
301 | if (--sem->count == 0 && !list_empty(&sem->wait_list)) | ||
302 | sem = __rwsem_wake_one_writer(sem); | ||
303 | |||
304 | raw_spin_unlock_irqrestore(&sem->wait_lock, flags); | ||
305 | } | ||
306 | |||
307 | /* | ||
308 | * release a write lock on the semaphore | ||
309 | */ | ||
310 | void __up_write(struct rw_semaphore *sem) | ||
311 | { | ||
312 | unsigned long flags; | ||
313 | |||
314 | raw_spin_lock_irqsave(&sem->wait_lock, flags); | ||
315 | |||
316 | sem->count = 0; | ||
317 | if (!list_empty(&sem->wait_list)) | ||
318 | sem = __rwsem_do_wake(sem, 1); | ||
319 | |||
320 | raw_spin_unlock_irqrestore(&sem->wait_lock, flags); | ||
321 | } | ||
322 | |||
323 | /* | ||
324 | * downgrade a write lock into a read lock | ||
325 | * - just wake up any readers at the front of the queue | ||
326 | */ | ||
327 | void __downgrade_write(struct rw_semaphore *sem) | ||
328 | { | ||
329 | unsigned long flags; | ||
330 | |||
331 | raw_spin_lock_irqsave(&sem->wait_lock, flags); | ||
332 | |||
333 | sem->count = 1; | ||
334 | if (!list_empty(&sem->wait_list)) | ||
335 | sem = __rwsem_do_wake(sem, 0); | ||
336 | |||
337 | raw_spin_unlock_irqrestore(&sem->wait_lock, flags); | ||
338 | } | ||
339 | |||