]> git.proxmox.com Git - mirror_spl-debian.git/blob - include/sys/rwlock.h
Fix race in rwlock implementation which can occur when
[mirror_spl-debian.git] / include / sys / rwlock.h
1 #ifndef _SPL_RWLOCK_H
2 #define _SPL_RWLOCK_H
3
4 #include <linux/module.h>
5 #include <linux/slab.h>
6 #include <linux/rwsem.h>
7 #include <asm/current.h>
8 #include <sys/types.h>
9
10 #ifdef __cplusplus
11 extern "C" {
12 #endif
13
14 typedef enum {
15 RW_DRIVER = 2, /* driver (DDI) rwlock */
16 RW_DEFAULT = 4 /* kernel default rwlock */
17 } krw_type_t;
18
19 typedef enum {
20 RW_WRITER,
21 RW_READER
22 } krw_t;
23
24 #define RW_READ_HELD(x) (__rw_read_held((x)))
25 #define RW_WRITE_HELD(x) (__rw_write_held((x)))
26 #define RW_LOCK_HELD(x) (__rw_lock_held((x)))
27 #define RW_ISWRITER(x) (__rw_iswriter(x))
28
29 #define RW_MAGIC 0x3423645a
30 #define RW_POISON 0xa6
31
32 typedef struct {
33 int rw_magic;
34 char *rw_name;
35 struct rw_semaphore rw_sem;
36 struct task_struct *rw_owner; /* holder of the write lock */
37 } krwlock_t;
38
39 #ifdef CONFIG_RWSEM_GENERIC_SPINLOCK
40 struct rwsem_waiter {
41 struct list_head list;
42 struct task_struct *task;
43 unsigned int flags;
44 #define RWSEM_WAITING_FOR_READ 0x00000001
45 #define RWSEM_WAITING_FOR_WRITE 0x00000002
46 };
47
48 /*
49 * wake a single writer
50 */
51 static inline struct rw_semaphore *
52 __rwsem_wake_one_writer_locked(struct rw_semaphore *sem)
53 {
54 struct rwsem_waiter *waiter;
55 struct task_struct *tsk;
56
57 sem->activity = -1;
58
59 waiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list);
60 list_del(&waiter->list);
61
62 tsk = waiter->task;
63 smp_mb();
64 waiter->task = NULL;
65 wake_up_process(tsk);
66 put_task_struct(tsk);
67 return sem;
68 }
69
70 /*
71 * release a read lock on the semaphore
72 */
73 static void fastcall
74 __up_read_locked(struct rw_semaphore *sem)
75 {
76 if (--sem->activity == 0 && !list_empty(&sem->wait_list))
77 sem = __rwsem_wake_one_writer_locked(sem);
78 }
79
80 /*
81 * trylock for writing -- returns 1 if successful, 0 if contention
82 */
83 static int fastcall
84 __down_write_trylock_locked(struct rw_semaphore *sem)
85 {
86 int ret = 0;
87
88 if (sem->activity == 0 && list_empty(&sem->wait_list)) {
89 /* granted */
90 sem->activity = -1;
91 ret = 1;
92 }
93
94 return ret;
95 }
96 #endif
97
98 extern int __rw_read_held(krwlock_t *rwlp);
99 extern int __rw_write_held(krwlock_t *rwlp);
100 extern int __rw_lock_held(krwlock_t *rwlp);
101
102 static __inline__ void
103 rw_init(krwlock_t *rwlp, char *name, krw_type_t type, void *arg)
104 {
105 BUG_ON(type != RW_DEFAULT); /* XXX no irq handler use */
106 BUG_ON(arg != NULL); /* XXX no irq handler use */
107 rwlp->rw_magic = RW_MAGIC;
108 rwlp->rw_owner = NULL; /* no one holds the write lock yet */
109 init_rwsem(&rwlp->rw_sem);
110 rwlp->rw_name = NULL;
111
112 if (name) {
113 rwlp->rw_name = kmalloc(strlen(name) + 1, GFP_KERNEL);
114 if (rwlp->rw_name)
115 strcpy(rwlp->rw_name, name);
116 }
117 }
118
119 static __inline__ void
120 rw_destroy(krwlock_t *rwlp)
121 {
122 BUG_ON(rwlp == NULL);
123 BUG_ON(rwlp->rw_magic != RW_MAGIC);
124 BUG_ON(rwlp->rw_owner != NULL);
125 spin_lock(&rwlp->rw_sem.wait_lock);
126 BUG_ON(!list_empty(&rwlp->rw_sem.wait_list));
127 spin_unlock(&rwlp->rw_sem.wait_lock);
128
129 if (rwlp->rw_name)
130 kfree(rwlp->rw_name);
131
132 memset(rwlp, RW_POISON, sizeof(krwlock_t));
133 }
134
135 /* Return 0 if the lock could not be obtained without blocking.
136 */
137 static __inline__ int
138 rw_tryenter(krwlock_t *rwlp, krw_t rw)
139 {
140 int result;
141
142 BUG_ON(rwlp->rw_magic != RW_MAGIC);
143 switch (rw) {
144 /* these functions return 1 if success, 0 if contention */
145 case RW_READER:
146 /* Here the Solaris code would return 0
147 * if there were any write waiters. Specifically
148 * thinking about the case where readers may have
149 * the lock and we would also allow this thread
150 * to grab the read lock with a writer waiting in the
151 * queue. This doesn't seem like a correctness
152 * issue, so just call down_read_trylock()
153 * for the test. We may have to revisit this if
154 * it becomes an issue */
155 result = down_read_trylock(&rwlp->rw_sem);
156 break;
157 case RW_WRITER:
158 result = down_write_trylock(&rwlp->rw_sem);
159 if (result) {
160 /* there better not be anyone else
161 * holding the write lock here */
162 BUG_ON(rwlp->rw_owner != NULL);
163 rwlp->rw_owner = current;
164 }
165 break;
166 default:
167 BUG_ON(1);
168 }
169
170 return result;
171 }
172
173 static __inline__ void
174 rw_enter(krwlock_t *rwlp, krw_t rw)
175 {
176 BUG_ON(rwlp->rw_magic != RW_MAGIC);
177 switch (rw) {
178 case RW_READER:
179 /* Here the Solaris code would block
180 * if there were any write waiters. Specifically
181 * thinking about the case where readers may have
182 * the lock and we would also allow this thread
183 * to grab the read lock with a writer waiting in the
184 * queue. This doesn't seem like a correctness
185 * issue, so just call down_read()
186 * for the test. We may have to revisit this if
187 * it becomes an issue */
188 down_read(&rwlp->rw_sem);
189 break;
190 case RW_WRITER:
191 down_write(&rwlp->rw_sem);
192
193 /* there better not be anyone else
194 * holding the write lock here */
195 BUG_ON(rwlp->rw_owner != NULL);
196 rwlp->rw_owner = current;
197 break;
198 default:
199 BUG_ON(1);
200 }
201 }
202
203 static __inline__ void
204 rw_exit(krwlock_t *rwlp)
205 {
206 BUG_ON(rwlp->rw_magic != RW_MAGIC);
207
208 /* rw_owner is held by current
209 * thread iff it is a writer */
210 if (rwlp->rw_owner == current) {
211 rwlp->rw_owner = NULL;
212 up_write(&rwlp->rw_sem);
213 } else {
214 up_read(&rwlp->rw_sem);
215 }
216 }
217
218 static __inline__ void
219 rw_downgrade(krwlock_t *rwlp)
220 {
221 BUG_ON(rwlp->rw_magic != RW_MAGIC);
222 BUG_ON(rwlp->rw_owner != current);
223 rwlp->rw_owner = NULL;
224 downgrade_write(&rwlp->rw_sem);
225 }
226
227 /* Return 0 if unable to perform the upgrade.
228 * Might be wise to fix the caller
229 * to acquire the write lock first?
230 */
231 static __inline__ int
232 rw_tryupgrade(krwlock_t *rwlp)
233 {
234 int result = 0;
235 BUG_ON(rwlp->rw_magic != RW_MAGIC);
236
237 spin_lock(&rwlp->rw_sem.wait_lock);
238
239 /* Check if there is anyone waiting for the
240 * lock. If there is, then we know we should
241 * not try to upgrade the lock */
242 if (!list_empty(&rwlp->rw_sem.wait_list)) {
243 printk("spl: Warning There are threads waiting\n");
244 spin_unlock(&rwlp->rw_sem.wait_lock);
245 return 0;
246 }
247 #ifdef CONFIG_RWSEM_GENERIC_SPINLOCK
248 /* Note that activity is protected by
249 * the wait_lock. Don't try to upgrade
250 * if there are multiple readers currently
251 * holding the lock */
252 if (rwlp->rw_sem.activity > 1) {
253 #else
254 /* Don't try to upgrade
255 * if there are multiple readers currently
256 * holding the lock */
257 if ((rwlp->rw_sem.count & RWSEM_ACTIVE_MASK) > 1) {
258 #endif
259 spin_unlock(&rwlp->rw_sem.wait_lock);
260 return 0;
261 }
262
263 #ifdef CONFIG_RWSEM_GENERIC_SPINLOCK
264 /* Here it should be safe to drop the
265 * read lock and reacquire it for writing since
266 * we know there are no waiters */
267 __up_read_locked(&rwlp->rw_sem);
268
269 /* returns 1 if success, 0 if contention */
270 result = __down_write_trylock_locked(&rwlp->rw_sem);
271 #else
272 /* Here it should be safe to drop the
273 * read lock and reacquire it for writing since
274 * we know there are no waiters */
275 up_read(&rwlp->rw_sem);
276
277 /* returns 1 if success, 0 if contention */
278 result = down_write_trylock(&rwlp->rw_sem);
279 #endif
280
281 /* Check if upgrade failed. Should not ever happen
282 * if we got to this point */
283 BUG_ON(!result);
284 BUG_ON(rwlp->rw_owner != NULL);
285 rwlp->rw_owner = current;
286 spin_unlock(&rwlp->rw_sem.wait_lock);
287 return 1;
288 }
289
290 static __inline__ kthread_t *
291 rw_owner(krwlock_t *rwlp)
292 {
293 BUG_ON(rwlp->rw_magic != RW_MAGIC);
294 return rwlp->rw_owner;
295 }
296
297 #ifdef __cplusplus
298 }
299 #endif
300
301 #endif /* _SPL_RWLOCK_H */