Merge with /pub/scm/linux/kernel/git/torvalds/linux-2.6.git
[linux-2.6] / arch / ia64 / kernel / semaphore.c
1 /*
2  * IA-64 semaphore implementation (derived from x86 version).
3  *
4  * Copyright (C) 1999-2000, 2002 Hewlett-Packard Co
5  *      David Mosberger-Tang <davidm@hpl.hp.com>
6  */
7
8 /*
9  * Semaphores are implemented using a two-way counter: The "count"
10  * variable is decremented for each process that tries to acquire the
11  * semaphore, while the "sleepers" variable is a count of such
12  * acquires.
13  *
14  * Notably, the inline "up()" and "down()" functions can efficiently
15  * test if they need to do any extra work (up needs to do something
16  * only if count was negative before the increment operation.
17  *
18  * "sleeping" and the contention routine ordering is protected
19  * by the spinlock in the semaphore's waitqueue head.
20  *
21  * Note that these functions are only called when there is contention
22  * on the lock, and as such all this is the "non-critical" part of the
23  * whole semaphore business. The critical part is the inline stuff in
24  * <asm/semaphore.h> where we want to avoid any extra jumps and calls.
25  */
26 #include <linux/sched.h>
27 #include <linux/init.h>
28
29 #include <asm/errno.h>
30 #include <asm/semaphore.h>
31
32 /*
33  * Logic:
34  *  - Only on a boundary condition do we need to care. When we go
35  *    from a negative count to a non-negative, we wake people up.
36  *  - When we go from a non-negative count to a negative do we
37  *    (a) synchronize with the "sleepers" count and (b) make sure
38  *    that we're on the wakeup list before we synchronize so that
39  *    we cannot lose wakeup events.
40  */
41
42 void
43 __up (struct semaphore *sem)
44 {
45         wake_up(&sem->wait);
46 }
47
48 void __sched __down (struct semaphore *sem)
49 {
50         struct task_struct *tsk = current;
51         DECLARE_WAITQUEUE(wait, tsk);
52         unsigned long flags;
53
54         tsk->state = TASK_UNINTERRUPTIBLE;
55         spin_lock_irqsave(&sem->wait.lock, flags);
56         add_wait_queue_exclusive_locked(&sem->wait, &wait);
57
58         sem->sleepers++;
59         for (;;) {
60                 int sleepers = sem->sleepers;
61
62                 /*
63                  * Add "everybody else" into it. They aren't
64                  * playing, because we own the spinlock in
65                  * the wait_queue_head.
66                  */
67                 if (!atomic_add_negative(sleepers - 1, &sem->count)) {
68                         sem->sleepers = 0;
69                         break;
70                 }
71                 sem->sleepers = 1;      /* us - see -1 above */
72                 spin_unlock_irqrestore(&sem->wait.lock, flags);
73
74                 schedule();
75
76                 spin_lock_irqsave(&sem->wait.lock, flags);
77                 tsk->state = TASK_UNINTERRUPTIBLE;
78         }
79         remove_wait_queue_locked(&sem->wait, &wait);
80         wake_up_locked(&sem->wait);
81         spin_unlock_irqrestore(&sem->wait.lock, flags);
82         tsk->state = TASK_RUNNING;
83 }
84
85 int __sched __down_interruptible (struct semaphore * sem)
86 {
87         int retval = 0;
88         struct task_struct *tsk = current;
89         DECLARE_WAITQUEUE(wait, tsk);
90         unsigned long flags;
91
92         tsk->state = TASK_INTERRUPTIBLE;
93         spin_lock_irqsave(&sem->wait.lock, flags);
94         add_wait_queue_exclusive_locked(&sem->wait, &wait);
95
96         sem->sleepers ++;
97         for (;;) {
98                 int sleepers = sem->sleepers;
99
100                 /*
101                  * With signals pending, this turns into
102                  * the trylock failure case - we won't be
103                  * sleeping, and we* can't get the lock as
104                  * it has contention. Just correct the count
105                  * and exit.
106                  */
107                 if (signal_pending(current)) {
108                         retval = -EINTR;
109                         sem->sleepers = 0;
110                         atomic_add(sleepers, &sem->count);
111                         break;
112                 }
113
114                 /*
115                  * Add "everybody else" into it. They aren't
116                  * playing, because we own the spinlock in
117                  * wait_queue_head. The "-1" is because we're
118                  * still hoping to get the semaphore.
119                  */
120                 if (!atomic_add_negative(sleepers - 1, &sem->count)) {
121                         sem->sleepers = 0;
122                         break;
123                 }
124                 sem->sleepers = 1;      /* us - see -1 above */
125                 spin_unlock_irqrestore(&sem->wait.lock, flags);
126
127                 schedule();
128
129                 spin_lock_irqsave(&sem->wait.lock, flags);
130                 tsk->state = TASK_INTERRUPTIBLE;
131         }
132         remove_wait_queue_locked(&sem->wait, &wait);
133         wake_up_locked(&sem->wait);
134         spin_unlock_irqrestore(&sem->wait.lock, flags);
135
136         tsk->state = TASK_RUNNING;
137         return retval;
138 }
139
140 /*
141  * Trylock failed - make sure we correct for having decremented the
142  * count.
143  */
144 int
145 __down_trylock (struct semaphore *sem)
146 {
147         unsigned long flags;
148         int sleepers;
149
150         spin_lock_irqsave(&sem->wait.lock, flags);
151         sleepers = sem->sleepers + 1;
152         sem->sleepers = 0;
153
154         /*
155          * Add "everybody else" and us into it. They aren't
156          * playing, because we own the spinlock in the
157          * wait_queue_head.
158          */
159         if (!atomic_add_negative(sleepers, &sem->count)) {
160                 wake_up_locked(&sem->wait);
161         }
162
163         spin_unlock_irqrestore(&sem->wait.lock, flags);
164         return 1;
165 }