Blame view
lib/rwsem-spinlock.c
6.94 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/* rwsem-spinlock.c: R/W semaphores: contention handling functions for * generic spinlock implementation * * Copyright (c) 2001 David Howells (dhowells@redhat.com). * - Derived partially from idea by Andrea Arcangeli <andrea@suse.de> * - Derived also from comments by Linus */ #include <linux/rwsem.h> #include <linux/sched.h> #include <linux/module.h> struct rwsem_waiter { struct list_head list; struct task_struct *task; unsigned int flags; #define RWSEM_WAITING_FOR_READ 0x00000001 #define RWSEM_WAITING_FOR_WRITE 0x00000002 }; |
29671f22a rwsem: fix rwsem_... |
19 20 21 22 23 24 25 26 27 28 29 30 |
int rwsem_is_locked(struct rw_semaphore *sem) { int ret = 1; unsigned long flags; if (spin_trylock_irqsave(&sem->wait_lock, flags)) { ret = (sem->activity != 0); spin_unlock_irqrestore(&sem->wait_lock, flags); } return ret; } EXPORT_SYMBOL(rwsem_is_locked); |
1da177e4c Linux-2.6.12-rc2 |
31 32 33 |
/* * initialise the semaphore */ |
4ea2176df [PATCH] lockdep: ... |
34 35 |
void __init_rwsem(struct rw_semaphore *sem, const char *name, struct lock_class_key *key) |
1da177e4c Linux-2.6.12-rc2 |
36 |
{ |
4ea2176df [PATCH] lockdep: ... |
37 38 39 40 41 |
#ifdef CONFIG_DEBUG_LOCK_ALLOC /* * Make sure we are not reinitializing a held semaphore: */ debug_check_no_locks_freed((void *)sem, sizeof(*sem)); |
4dfbb9d8c Lockdep: add lock... |
42 |
lockdep_init_map(&sem->dep_map, name, key, 0); |
4ea2176df [PATCH] lockdep: ... |
43 |
#endif |
1da177e4c Linux-2.6.12-rc2 |
44 45 46 |
sem->activity = 0; spin_lock_init(&sem->wait_lock); INIT_LIST_HEAD(&sem->wait_list); |
1da177e4c Linux-2.6.12-rc2 |
47 |
} |
118d52da1 rwsem-spinlock: r... |
48 |
EXPORT_SYMBOL(__init_rwsem); |
1da177e4c Linux-2.6.12-rc2 |
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
/* * handle the lock release when processes blocked on it that can now run * - if we come here, then: * - the 'active count' _reached_ zero * - the 'waiting count' is non-zero * - the spinlock must be held by the caller * - woken process blocks are discarded from the list after having task zeroed * - writers are only woken if wakewrite is non-zero */ static inline struct rw_semaphore * __rwsem_do_wake(struct rw_semaphore *sem, int wakewrite) { struct rwsem_waiter *waiter; struct task_struct *tsk; int woken; |
1da177e4c Linux-2.6.12-rc2 |
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
waiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list); if (!wakewrite) { if (waiter->flags & RWSEM_WAITING_FOR_WRITE) goto out; goto dont_wake_writers; } /* if we are allowed to wake writers try to grant a single write lock * if there's a writer at the front of the queue * - we leave the 'waiting count' incremented to signify potential * contention */ if (waiter->flags & RWSEM_WAITING_FOR_WRITE) { sem->activity = -1; list_del(&waiter->list); tsk = waiter->task; /* Don't touch waiter after ->task has been NULLed */ |
d59dd4620 [PATCH] use smp_m... |
83 |
smp_mb(); |
1da177e4c Linux-2.6.12-rc2 |
84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
waiter->task = NULL; wake_up_process(tsk); put_task_struct(tsk); goto out; } /* grant an infinite number of read locks to the front of the queue */ dont_wake_writers: woken = 0; while (waiter->flags & RWSEM_WAITING_FOR_READ) { struct list_head *next = waiter->list.next; list_del(&waiter->list); tsk = waiter->task; |
d59dd4620 [PATCH] use smp_m... |
98 |
smp_mb(); |
1da177e4c Linux-2.6.12-rc2 |
99 100 101 102 103 104 105 106 107 108 109 110 |
waiter->task = NULL; wake_up_process(tsk); put_task_struct(tsk); woken++; if (list_empty(&sem->wait_list)) break; waiter = list_entry(next, struct rwsem_waiter, list); } sem->activity += woken; out: |
1da177e4c Linux-2.6.12-rc2 |
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
return sem; } /* * wake a single writer */ static inline struct rw_semaphore * __rwsem_wake_one_writer(struct rw_semaphore *sem) { struct rwsem_waiter *waiter; struct task_struct *tsk; sem->activity = -1; waiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list); list_del(&waiter->list); tsk = waiter->task; |
d59dd4620 [PATCH] use smp_m... |
129 |
smp_mb(); |
1da177e4c Linux-2.6.12-rc2 |
130 131 132 133 134 135 136 137 138 |
waiter->task = NULL; wake_up_process(tsk); put_task_struct(tsk); return sem; } /* * get a read lock on the semaphore */ |
9f741cb8f lib: remove fastc... |
139 |
void __sched __down_read(struct rw_semaphore *sem) |
1da177e4c Linux-2.6.12-rc2 |
140 141 142 |
{ struct rwsem_waiter waiter; struct task_struct *tsk; |
3eac4abaa rwsem generic spi... |
143 |
unsigned long flags; |
1da177e4c Linux-2.6.12-rc2 |
144 |
|
3eac4abaa rwsem generic spi... |
145 |
spin_lock_irqsave(&sem->wait_lock, flags); |
1da177e4c Linux-2.6.12-rc2 |
146 147 148 149 |
if (sem->activity >= 0 && list_empty(&sem->wait_list)) { /* granted */ sem->activity++; |
3eac4abaa rwsem generic spi... |
150 |
spin_unlock_irqrestore(&sem->wait_lock, flags); |
1da177e4c Linux-2.6.12-rc2 |
151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
goto out; } tsk = current; set_task_state(tsk, TASK_UNINTERRUPTIBLE); /* set up my own style of waitqueue */ waiter.task = tsk; waiter.flags = RWSEM_WAITING_FOR_READ; get_task_struct(tsk); list_add_tail(&waiter.list, &sem->wait_list); /* we don't need to touch the semaphore struct anymore */ |
3eac4abaa rwsem generic spi... |
165 |
spin_unlock_irqrestore(&sem->wait_lock, flags); |
1da177e4c Linux-2.6.12-rc2 |
166 167 168 169 170 171 172 173 174 175 |
/* wait to be given the lock */ for (;;) { if (!waiter.task) break; schedule(); set_task_state(tsk, TASK_UNINTERRUPTIBLE); } tsk->state = TASK_RUNNING; |
1da177e4c Linux-2.6.12-rc2 |
176 |
out: |
c4e05116a [PATCH] lockdep: ... |
177 |
; |
1da177e4c Linux-2.6.12-rc2 |
178 179 180 181 182 |
} /* * trylock for reading -- returns 1 if successful, 0 if contention */ |
9f741cb8f lib: remove fastc... |
183 |
int __down_read_trylock(struct rw_semaphore *sem) |
1da177e4c Linux-2.6.12-rc2 |
184 185 186 |
{ unsigned long flags; int ret = 0; |
1da177e4c Linux-2.6.12-rc2 |
187 188 189 190 191 192 193 194 195 196 |
spin_lock_irqsave(&sem->wait_lock, flags); if (sem->activity >= 0 && list_empty(&sem->wait_list)) { /* granted */ sem->activity++; ret = 1; } spin_unlock_irqrestore(&sem->wait_lock, flags); |
1da177e4c Linux-2.6.12-rc2 |
197 198 199 200 201 202 203 |
return ret; } /* * get a write lock on the semaphore * - we increment the waiting count anyway to indicate an exclusive lock */ |
9f741cb8f lib: remove fastc... |
204 |
void __sched __down_write_nested(struct rw_semaphore *sem, int subclass) |
1da177e4c Linux-2.6.12-rc2 |
205 206 207 |
{ struct rwsem_waiter waiter; struct task_struct *tsk; |
3eac4abaa rwsem generic spi... |
208 |
unsigned long flags; |
1da177e4c Linux-2.6.12-rc2 |
209 |
|
3eac4abaa rwsem generic spi... |
210 |
spin_lock_irqsave(&sem->wait_lock, flags); |
1da177e4c Linux-2.6.12-rc2 |
211 212 213 214 |
if (sem->activity == 0 && list_empty(&sem->wait_list)) { /* granted */ sem->activity = -1; |
3eac4abaa rwsem generic spi... |
215 |
spin_unlock_irqrestore(&sem->wait_lock, flags); |
1da177e4c Linux-2.6.12-rc2 |
216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
goto out; } tsk = current; set_task_state(tsk, TASK_UNINTERRUPTIBLE); /* set up my own style of waitqueue */ waiter.task = tsk; waiter.flags = RWSEM_WAITING_FOR_WRITE; get_task_struct(tsk); list_add_tail(&waiter.list, &sem->wait_list); /* we don't need to touch the semaphore struct anymore */ |
3eac4abaa rwsem generic spi... |
230 |
spin_unlock_irqrestore(&sem->wait_lock, flags); |
1da177e4c Linux-2.6.12-rc2 |
231 232 233 234 235 236 237 238 239 240 |
/* wait to be given the lock */ for (;;) { if (!waiter.task) break; schedule(); set_task_state(tsk, TASK_UNINTERRUPTIBLE); } tsk->state = TASK_RUNNING; |
1da177e4c Linux-2.6.12-rc2 |
241 |
out: |
c4e05116a [PATCH] lockdep: ... |
242 |
; |
1da177e4c Linux-2.6.12-rc2 |
243 |
} |
9f741cb8f lib: remove fastc... |
244 |
void __sched __down_write(struct rw_semaphore *sem) |
4ea2176df [PATCH] lockdep: ... |
245 246 247 |
{ __down_write_nested(sem, 0); } |
1da177e4c Linux-2.6.12-rc2 |
248 249 250 |
/* * trylock for writing -- returns 1 if successful, 0 if contention */ |
9f741cb8f lib: remove fastc... |
251 |
int __down_write_trylock(struct rw_semaphore *sem) |
1da177e4c Linux-2.6.12-rc2 |
252 253 254 |
{ unsigned long flags; int ret = 0; |
1da177e4c Linux-2.6.12-rc2 |
255 256 257 258 259 260 261 262 263 |
spin_lock_irqsave(&sem->wait_lock, flags); if (sem->activity == 0 && list_empty(&sem->wait_list)) { /* granted */ sem->activity = -1; ret = 1; } spin_unlock_irqrestore(&sem->wait_lock, flags); |
1da177e4c Linux-2.6.12-rc2 |
264 265 266 267 268 269 |
return ret; } /* * release a read lock on the semaphore */ |
9f741cb8f lib: remove fastc... |
270 |
void __up_read(struct rw_semaphore *sem) |
1da177e4c Linux-2.6.12-rc2 |
271 272 |
{ unsigned long flags; |
1da177e4c Linux-2.6.12-rc2 |
273 274 275 276 277 278 |
spin_lock_irqsave(&sem->wait_lock, flags); if (--sem->activity == 0 && !list_empty(&sem->wait_list)) sem = __rwsem_wake_one_writer(sem); spin_unlock_irqrestore(&sem->wait_lock, flags); |
1da177e4c Linux-2.6.12-rc2 |
279 280 281 282 283 |
} /* * release a write lock on the semaphore */ |
9f741cb8f lib: remove fastc... |
284 |
void __up_write(struct rw_semaphore *sem) |
1da177e4c Linux-2.6.12-rc2 |
285 286 |
{ unsigned long flags; |
1da177e4c Linux-2.6.12-rc2 |
287 288 289 290 291 292 293 |
spin_lock_irqsave(&sem->wait_lock, flags); sem->activity = 0; if (!list_empty(&sem->wait_list)) sem = __rwsem_do_wake(sem, 1); spin_unlock_irqrestore(&sem->wait_lock, flags); |
1da177e4c Linux-2.6.12-rc2 |
294 295 296 297 298 299 |
} /* * downgrade a write lock into a read lock * - just wake up any readers at the front of the queue */ |
9f741cb8f lib: remove fastc... |
300 |
void __downgrade_write(struct rw_semaphore *sem) |
1da177e4c Linux-2.6.12-rc2 |
301 302 |
{ unsigned long flags; |
1da177e4c Linux-2.6.12-rc2 |
303 304 305 306 307 308 309 |
spin_lock_irqsave(&sem->wait_lock, flags); sem->activity = 1; if (!list_empty(&sem->wait_list)) sem = __rwsem_do_wake(sem, 0); spin_unlock_irqrestore(&sem->wait_lock, flags); |
1da177e4c Linux-2.6.12-rc2 |
310 |
} |