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