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 */ 27void __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 62void __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 78int __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} 103