ttm_lock.c revision 1.1.1.1 1 /* $NetBSD: ttm_lock.c,v 1.1.1.1 2021/12/18 20:15:54 riastradh Exp $ */
2
3 /* SPDX-License-Identifier: GPL-2.0 OR MIT */
4 /**************************************************************************
5 *
6 * Copyright (c) 2007-2009 VMware, Inc., Palo Alto, CA., USA
7 * All Rights Reserved.
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the
11 * "Software"), to deal in the Software without restriction, including
12 * without limitation the rights to use, copy, modify, merge, publish,
13 * distribute, sub license, and/or sell copies of the Software, and to
14 * permit persons to whom the Software is furnished to do so, subject to
15 * the following conditions:
16 *
17 * The above copyright notice and this permission notice (including the
18 * next paragraph) shall be included in all copies or substantial portions
19 * of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
24 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
25 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
26 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
27 * USE OR OTHER DEALINGS IN THE SOFTWARE.
28 *
29 **************************************************************************/
30 /*
31 * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
32 */
33
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: ttm_lock.c,v 1.1.1.1 2021/12/18 20:15:54 riastradh Exp $");
36
37 #include <linux/atomic.h>
38 #include <linux/errno.h>
39 #include <linux/wait.h>
40 #include <linux/sched/signal.h>
41 #include "ttm_lock.h"
42 #include "ttm_object.h"
43
44 #define TTM_WRITE_LOCK_PENDING (1 << 0)
45 #define TTM_VT_LOCK_PENDING (1 << 1)
46 #define TTM_SUSPEND_LOCK_PENDING (1 << 2)
47 #define TTM_VT_LOCK (1 << 3)
48 #define TTM_SUSPEND_LOCK (1 << 4)
49
50 void ttm_lock_init(struct ttm_lock *lock)
51 {
52 spin_lock_init(&lock->lock);
53 init_waitqueue_head(&lock->queue);
54 lock->rw = 0;
55 lock->flags = 0;
56 }
57
58 void ttm_read_unlock(struct ttm_lock *lock)
59 {
60 spin_lock(&lock->lock);
61 if (--lock->rw == 0)
62 wake_up_all(&lock->queue);
63 spin_unlock(&lock->lock);
64 }
65
66 static bool __ttm_read_lock(struct ttm_lock *lock)
67 {
68 bool locked = false;
69
70 spin_lock(&lock->lock);
71 if (lock->rw >= 0 && lock->flags == 0) {
72 ++lock->rw;
73 locked = true;
74 }
75 spin_unlock(&lock->lock);
76 return locked;
77 }
78
79 int ttm_read_lock(struct ttm_lock *lock, bool interruptible)
80 {
81 int ret = 0;
82
83 if (interruptible)
84 ret = wait_event_interruptible(lock->queue,
85 __ttm_read_lock(lock));
86 else
87 wait_event(lock->queue, __ttm_read_lock(lock));
88 return ret;
89 }
90
91 static bool __ttm_read_trylock(struct ttm_lock *lock, bool *locked)
92 {
93 bool block = true;
94
95 *locked = false;
96
97 spin_lock(&lock->lock);
98 if (lock->rw >= 0 && lock->flags == 0) {
99 ++lock->rw;
100 block = false;
101 *locked = true;
102 } else if (lock->flags == 0) {
103 block = false;
104 }
105 spin_unlock(&lock->lock);
106
107 return !block;
108 }
109
110 int ttm_read_trylock(struct ttm_lock *lock, bool interruptible)
111 {
112 int ret = 0;
113 bool locked;
114
115 if (interruptible)
116 ret = wait_event_interruptible
117 (lock->queue, __ttm_read_trylock(lock, &locked));
118 else
119 wait_event(lock->queue, __ttm_read_trylock(lock, &locked));
120
121 if (unlikely(ret != 0)) {
122 BUG_ON(locked);
123 return ret;
124 }
125
126 return (locked) ? 0 : -EBUSY;
127 }
128
129 void ttm_write_unlock(struct ttm_lock *lock)
130 {
131 spin_lock(&lock->lock);
132 lock->rw = 0;
133 wake_up_all(&lock->queue);
134 spin_unlock(&lock->lock);
135 }
136
137 static bool __ttm_write_lock(struct ttm_lock *lock)
138 {
139 bool locked = false;
140
141 spin_lock(&lock->lock);
142 if (lock->rw == 0 && ((lock->flags & ~TTM_WRITE_LOCK_PENDING) == 0)) {
143 lock->rw = -1;
144 lock->flags &= ~TTM_WRITE_LOCK_PENDING;
145 locked = true;
146 } else {
147 lock->flags |= TTM_WRITE_LOCK_PENDING;
148 }
149 spin_unlock(&lock->lock);
150 return locked;
151 }
152
153 int ttm_write_lock(struct ttm_lock *lock, bool interruptible)
154 {
155 int ret = 0;
156
157 if (interruptible) {
158 ret = wait_event_interruptible(lock->queue,
159 __ttm_write_lock(lock));
160 if (unlikely(ret != 0)) {
161 spin_lock(&lock->lock);
162 lock->flags &= ~TTM_WRITE_LOCK_PENDING;
163 wake_up_all(&lock->queue);
164 spin_unlock(&lock->lock);
165 }
166 } else
167 wait_event(lock->queue, __ttm_write_lock(lock));
168
169 return ret;
170 }
171
172 void ttm_suspend_unlock(struct ttm_lock *lock)
173 {
174 spin_lock(&lock->lock);
175 lock->flags &= ~TTM_SUSPEND_LOCK;
176 wake_up_all(&lock->queue);
177 spin_unlock(&lock->lock);
178 }
179
180 static bool __ttm_suspend_lock(struct ttm_lock *lock)
181 {
182 bool locked = false;
183
184 spin_lock(&lock->lock);
185 if (lock->rw == 0) {
186 lock->flags &= ~TTM_SUSPEND_LOCK_PENDING;
187 lock->flags |= TTM_SUSPEND_LOCK;
188 locked = true;
189 } else {
190 lock->flags |= TTM_SUSPEND_LOCK_PENDING;
191 }
192 spin_unlock(&lock->lock);
193 return locked;
194 }
195
196 void ttm_suspend_lock(struct ttm_lock *lock)
197 {
198 wait_event(lock->queue, __ttm_suspend_lock(lock));
199 }
200