1
2
3
4
5
6
7
8
9
10#include <linux/rwsem.h>
11#include <linux/sched.h>
12#include <linux/mm.h>
13#include <linux/module.h>
14
15struct rwsem_waiter {
16 struct list_head list;
17 struct task_struct *task;
18 unsigned int flags;
19#define RWSEM_WAITING_FOR_READ 0x00000001
20#define RWSEM_WAITING_FOR_WRITE 0x00000002
21};
22
23#if RWSEM_DEBUG
24void rwsemtrace(struct rw_semaphore *sem, const char *str)
25{
26 if (sem->debug)
27 printk("[%d] %s({%d,%d})\n",
28 current->pid,str,sem->activity,list_empty(&sem->wait_list)?0:1);
29}
30#endif
31
32
33
34
35void fastcall init_rwsem(struct rw_semaphore *sem)
36{
37 sem->activity = 0;
38 spin_lock_init(&sem->wait_lock);
39 INIT_LIST_HEAD(&sem->wait_list);
40#if RWSEM_DEBUG
41 sem->debug = 0;
42#endif
43}
44
45
46
47
48
49
50
51
52
53static inline struct rw_semaphore *__rwsem_do_wake(struct rw_semaphore *sem)
54{
55 struct rwsem_waiter *waiter;
56 struct task_struct *tsk;
57 int woken;
58
59 rwsemtrace(sem,"Entering __rwsem_do_wake");
60
61 waiter = list_entry(sem->wait_list.next,struct rwsem_waiter,list);
62
63
64
65
66 if (waiter->flags & RWSEM_WAITING_FOR_WRITE) {
67 sem->activity = -1;
68 list_del(&waiter->list);
69 tsk = waiter->task;
70 mb();
71 waiter->task = NULL;
72 wake_up_process(tsk);
73 free_task_struct(tsk);
74 goto out;
75 }
76
77
78 woken = 0;
79 do {
80 list_del(&waiter->list);
81 tsk = waiter->task;
82 mb();
83 waiter->task = NULL;
84 wake_up_process(tsk);
85 free_task_struct(tsk);
86 woken++;
87 if (list_empty(&sem->wait_list))
88 break;
89 waiter = list_entry(sem->wait_list.next,struct rwsem_waiter,list);
90 } while (waiter->flags&RWSEM_WAITING_FOR_READ);
91
92 sem->activity += woken;
93
94 out:
95 rwsemtrace(sem,"Leaving __rwsem_do_wake");
96 return sem;
97}
98
99
100
101
102static inline struct rw_semaphore *__rwsem_wake_one_writer(struct rw_semaphore *sem)
103{
104 struct rwsem_waiter *waiter;
105 struct task_struct *tsk;
106
107 sem->activity = -1;
108
109 waiter = list_entry(sem->wait_list.next,struct rwsem_waiter,list);
110 list_del(&waiter->list);
111
112 tsk = waiter->task;
113 mb();
114 waiter->task = NULL;
115 wake_up_process(tsk);
116 free_task_struct(tsk);
117 return sem;
118}
119
120
121
122
123void fastcall __down_read(struct rw_semaphore *sem)
124{
125 struct rwsem_waiter waiter;
126 struct task_struct *tsk;
127
128 rwsemtrace(sem,"Entering __down_read");
129
130 spin_lock(&sem->wait_lock);
131
132 if (sem->activity>=0 && list_empty(&sem->wait_list)) {
133
134 sem->activity++;
135 spin_unlock(&sem->wait_lock);
136 goto out;
137 }
138
139 tsk = current;
140 set_task_state(tsk,TASK_UNINTERRUPTIBLE);
141
142
143 waiter.task = tsk;
144 waiter.flags = RWSEM_WAITING_FOR_READ;
145 get_task_struct(tsk);
146
147 list_add_tail(&waiter.list,&sem->wait_list);
148
149
150 spin_unlock(&sem->wait_lock);
151
152
153 for (;;) {
154 if (!waiter.task)
155 break;
156 schedule();
157 set_task_state(tsk, TASK_UNINTERRUPTIBLE);
158 }
159
160 tsk->state = TASK_RUNNING;
161
162 out:
163 rwsemtrace(sem,"Leaving __down_read");
164}
165
166
167
168
169int fastcall __down_read_trylock(struct rw_semaphore *sem)
170{
171 int ret = 0;
172 rwsemtrace(sem,"Entering __down_read_trylock");
173
174 spin_lock(&sem->wait_lock);
175
176 if (sem->activity>=0 && list_empty(&sem->wait_list)) {
177
178 sem->activity++;
179 ret = 1;
180 }
181
182 spin_unlock(&sem->wait_lock);
183
184 rwsemtrace(sem,"Leaving __down_read_trylock");
185 return ret;
186}
187
188
189
190
191
192void fastcall __down_write(struct rw_semaphore *sem)
193{
194 struct rwsem_waiter waiter;
195 struct task_struct *tsk;
196
197 rwsemtrace(sem,"Entering __down_write");
198
199 spin_lock(&sem->wait_lock);
200
201 if (sem->activity==0 && list_empty(&sem->wait_list)) {
202
203 sem->activity = -1;
204 spin_unlock(&sem->wait_lock);
205 goto out;
206 }
207
208 tsk = current;
209 set_task_state(tsk,TASK_UNINTERRUPTIBLE);
210
211
212 waiter.task = tsk;
213 waiter.flags = RWSEM_WAITING_FOR_WRITE;
214 get_task_struct(tsk);
215
216 list_add_tail(&waiter.list,&sem->wait_list);
217
218
219 spin_unlock(&sem->wait_lock);
220
221
222 for (;;) {
223 if (!waiter.task)
224 break;
225 schedule();
226 set_task_state(tsk, TASK_UNINTERRUPTIBLE);
227 }
228
229 tsk->state = TASK_RUNNING;
230
231 out:
232 rwsemtrace(sem,"Leaving __down_write");
233}
234
235
236
237
238int fastcall __down_write_trylock(struct rw_semaphore *sem)
239{
240 int ret = 0;
241 rwsemtrace(sem,"Entering __down_write_trylock");
242
243 spin_lock(&sem->wait_lock);
244
245 if (sem->activity==0 && list_empty(&sem->wait_list)) {
246
247 sem->activity = -1;
248 ret = 1;
249 }
250
251 spin_unlock(&sem->wait_lock);
252
253 rwsemtrace(sem,"Leaving __down_write_trylock");
254 return ret;
255}
256
257
258
259
260void fastcall __up_read(struct rw_semaphore *sem)
261{
262 rwsemtrace(sem,"Entering __up_read");
263
264 spin_lock(&sem->wait_lock);
265
266 if (--sem->activity==0 && !list_empty(&sem->wait_list))
267 sem = __rwsem_wake_one_writer(sem);
268
269 spin_unlock(&sem->wait_lock);
270
271 rwsemtrace(sem,"Leaving __up_read");
272}
273
274
275
276
277void fastcall __up_write(struct rw_semaphore *sem)
278{
279 rwsemtrace(sem,"Entering __up_write");
280
281 spin_lock(&sem->wait_lock);
282
283 sem->activity = 0;
284 if (!list_empty(&sem->wait_list))
285 sem = __rwsem_do_wake(sem);
286
287 spin_unlock(&sem->wait_lock);
288
289 rwsemtrace(sem,"Leaving __up_write");
290}
291
292EXPORT_SYMBOL(init_rwsem);
293EXPORT_SYMBOL(__down_read);
294EXPORT_SYMBOL(__down_write);
295EXPORT_SYMBOL(__up_read);
296EXPORT_SYMBOL(__up_write);
297#if RWSEM_DEBUG
298EXPORT_SYMBOL(rwsemtrace);
299#endif
300