Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Semaphore implementation Copyright (c) 2001 Matthew Wilcox, Hewlett-Packard |
| 3 | */ |
| 4 | |
| 5 | #include <linux/sched.h> |
| 6 | #include <linux/spinlock.h> |
| 7 | #include <linux/errno.h> |
| 8 | #include <linux/init.h> |
| 9 | |
| 10 | /* |
| 11 | * Semaphores are complex as we wish to avoid using two variables. |
| 12 | * `count' has multiple roles, depending on its value. If it is positive |
| 13 | * or zero, there are no waiters. The functions here will never be |
| 14 | * called; see <asm/semaphore.h> |
| 15 | * |
| 16 | * When count is -1 it indicates there is at least one task waiting |
| 17 | * for the semaphore. |
| 18 | * |
| 19 | * When count is less than that, there are '- count - 1' wakeups |
| 20 | * pending. ie if it has value -3, there are 2 wakeups pending. |
| 21 | * |
| 22 | * Note that these functions are only called when there is contention |
| 23 | * on the lock, and as such all this is the "non-critical" part of the |
| 24 | * whole semaphore business. The critical part is the inline stuff in |
| 25 | * <asm/semaphore.h> where we want to avoid any extra jumps and calls. |
| 26 | */ |
| 27 | void __up(struct semaphore *sem) |
| 28 | { |
| 29 | sem->count--; |
| 30 | wake_up(&sem->wait); |
| 31 | } |
| 32 | |
| 33 | #define wakers(count) (-1 - count) |
| 34 | |
| 35 | #define DOWN_HEAD \ |
| 36 | int ret = 0; \ |
| 37 | DECLARE_WAITQUEUE(wait, current); \ |
| 38 | \ |
| 39 | /* Note that someone is waiting */ \ |
| 40 | if (sem->count == 0) \ |
| 41 | sem->count = -1; \ |
| 42 | \ |
| 43 | /* protected by the sentry still -- use unlocked version */ \ |
| 44 | wait.flags = WQ_FLAG_EXCLUSIVE; \ |
| 45 | __add_wait_queue_tail(&sem->wait, &wait); \ |
| 46 | lost_race: \ |
| 47 | spin_unlock_irq(&sem->sentry); \ |
| 48 | |
| 49 | #define DOWN_TAIL \ |
| 50 | spin_lock_irq(&sem->sentry); \ |
| 51 | if (wakers(sem->count) == 0 && ret == 0) \ |
| 52 | goto lost_race; /* Someone stole our wakeup */ \ |
| 53 | __remove_wait_queue(&sem->wait, &wait); \ |
| 54 | current->state = TASK_RUNNING; \ |
| 55 | if (!waitqueue_active(&sem->wait) && (sem->count < 0)) \ |
| 56 | sem->count = wakers(sem->count); |
| 57 | |
| 58 | #define UPDATE_COUNT \ |
| 59 | sem->count += (sem->count < 0) ? 1 : - 1; |
| 60 | |
| 61 | |
| 62 | void __sched __down(struct semaphore * sem) |
| 63 | { |
| 64 | DOWN_HEAD |
| 65 | |
| 66 | for(;;) { |
| 67 | set_task_state(current, TASK_UNINTERRUPTIBLE); |
| 68 | /* we can _read_ this without the sentry */ |
| 69 | if (sem->count != -1) |
| 70 | break; |
| 71 | schedule(); |
| 72 | } |
| 73 | |
| 74 | DOWN_TAIL |
| 75 | UPDATE_COUNT |
| 76 | } |
| 77 | |
| 78 | int __sched __down_interruptible(struct semaphore * sem) |
| 79 | { |
| 80 | DOWN_HEAD |
| 81 | |
| 82 | for(;;) { |
| 83 | set_task_state(current, TASK_INTERRUPTIBLE); |
| 84 | /* we can _read_ this without the sentry */ |
| 85 | if (sem->count != -1) |
| 86 | break; |
| 87 | |
| 88 | if (signal_pending(current)) { |
| 89 | ret = -EINTR; |
| 90 | break; |
| 91 | } |
| 92 | schedule(); |
| 93 | } |
| 94 | |
| 95 | DOWN_TAIL |
| 96 | |
| 97 | if (!ret) { |
| 98 | UPDATE_COUNT |
| 99 | } |
| 100 | |
| 101 | return ret; |
| 102 | } |