433c82ba52a8ab270641c46d0f436b04ec056c7d
[openafs.git] / src / afs / LINUX / osi_sleep.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include "afs/param.h"
12
13
14 #include "afs/sysincludes.h"    /* Standard vendor system headers */
15 #include "afsincludes.h"        /* Afs-based standard headers */
16 #include "afs/afs_stats.h"      /* afs statistics */
17
18 #if defined(FREEZER_H_EXISTS)
19 #include <linux/freezer.h>
20 #endif
21
22 static int osi_TimedSleep(char *event, afs_int32 ams, int aintok);
23
24 static char waitV, dummyV;
25
26 void
27 afs_osi_InitWaitHandle(struct afs_osi_WaitHandle *achandle)
28 {
29     AFS_STATCNT(osi_InitWaitHandle);
30     achandle->proc = (caddr_t) 0;
31 }
32
33 /* cancel osi_Wait */
34 void
35 afs_osi_CancelWait(struct afs_osi_WaitHandle *achandle)
36 {
37     caddr_t proc;
38
39     AFS_STATCNT(osi_CancelWait);
40     proc = achandle->proc;
41     if (proc == 0)
42         return;
43     achandle->proc = (caddr_t) 0;       /* so dude can figure out he was signalled */
44     afs_osi_Wakeup(&waitV);
45 }
46
47 /* afs_osi_Wait
48  * Waits for data on ahandle, or ams ms later.  ahandle may be null.
49  * Returns 0 if timeout and EINTR if signalled.
50  */
51 int
52 afs_osi_Wait(afs_int32 ams, struct afs_osi_WaitHandle *ahandle, int aintok)
53 {
54     afs_int32 endTime;
55     int code;
56
57     AFS_STATCNT(osi_Wait);
58     endTime = osi_Time() + (ams / 1000);
59     if (ahandle)
60         ahandle->proc = (caddr_t) current;
61
62     do {
63         AFS_ASSERT_GLOCK();
64         code = osi_TimedSleep(&waitV, ams, 1);
65         if (code)
66             break;
67         if (ahandle && (ahandle->proc == (caddr_t) 0)) {
68             /* we've been signalled */
69             break;
70         }
71     } while (osi_Time() < endTime);
72     return code;
73 }
74
75
76
77
78 typedef struct afs_event {
79     struct afs_event *next;     /* next in hash chain */
80     char *event;                /* lwp event: an address */
81     int refcount;               /* Is it in use? */
82     int seq;                    /* Sequence number: this is incremented
83                                  * by wakeup calls; wait will not return until
84                                  * it changes */
85 #if defined(AFS_LINUX24_ENV)
86     wait_queue_head_t cond;
87 #else
88     struct wait_queue *cond;
89 #endif
90 } afs_event_t;
91
92 #define HASHSIZE 128
93 afs_event_t *afs_evhasht[HASHSIZE];     /* Hash table for events */
94 #define afs_evhash(event)       (afs_uint32) ((((long)event)>>2) & (HASHSIZE-1));
95 int afs_evhashcnt = 0;
96
97 /* Get and initialize event structure corresponding to lwp event (i.e. address)
98  * */
99 static afs_event_t *
100 afs_getevent(char *event)
101 {
102     afs_event_t *evp, *newp = 0;
103     int hashcode;
104
105     AFS_ASSERT_GLOCK();
106     hashcode = afs_evhash(event);
107     evp = afs_evhasht[hashcode];
108     while (evp) {
109         if (evp->event == event) {
110             evp->refcount++;
111             return evp;
112         }
113         if (evp->refcount == 0)
114             newp = evp;
115         evp = evp->next;
116     }
117     if (!newp)
118         return NULL;
119
120     newp->event = event;
121     newp->refcount = 1;
122     return newp;
123 }
124
125 /* afs_addevent -- allocates a new event for the address.  It isn't returned;
126  *     instead, afs_getevent should be called again.  Thus, the real effect of
127  *     this routine is to add another event to the hash bucket for this
128  *     address.
129  *
130  * Locks:
131  *     Called with GLOCK held. However the function might drop
132  *     GLOCK when it calls osi_AllocSmallSpace for allocating
133  *     a new event (In Linux, the allocator drops GLOCK to avoid
134  *     a deadlock).
135  */
136
137 static void
138 afs_addevent(char *event)
139 {
140     int hashcode;
141     afs_event_t *newp;
142
143     AFS_ASSERT_GLOCK();
144     hashcode = afs_evhash(event);
145     newp = osi_linux_alloc(sizeof(afs_event_t), 0);
146     afs_evhashcnt++;
147     newp->next = afs_evhasht[hashcode];
148     afs_evhasht[hashcode] = newp;
149 #if defined(AFS_LINUX24_ENV)
150     init_waitqueue_head(&newp->cond);
151 #else
152     init_waitqueue(&newp->cond);
153 #endif
154     newp->seq = 0;
155     newp->event = &dummyV;      /* Dummy address for new events */
156     newp->refcount = 0;
157 }
158
159 #ifndef set_current_state
160 #define set_current_state(x)            current->state = (x);
161 #endif
162
163 /* Release the specified event */
164 #define relevent(evp) ((evp)->refcount--)
165
166 /* afs_osi_SleepSig
167  *
168  * Waits for an event to be notified, returning early if a signal
169  * is received.  Returns EINTR if signaled, and 0 otherwise.
170  */
171 int
172 afs_osi_SleepSig(void *event)
173 {
174     struct afs_event *evp;
175     int seq, retval;
176 #ifdef DECLARE_WAITQUEUE
177     DECLARE_WAITQUEUE(wait, current);
178 #else
179     struct wait_queue wait = { current, NULL };
180 #endif
181
182     evp = afs_getevent(event);
183     if (!evp) {
184         afs_addevent(event);
185         evp = afs_getevent(event);
186     }
187
188     seq = evp->seq;
189     retval = 0;
190
191     add_wait_queue(&evp->cond, &wait);
192     while (seq == evp->seq) {
193         set_current_state(TASK_INTERRUPTIBLE);
194         AFS_ASSERT_GLOCK();
195         AFS_GUNLOCK();
196         schedule();
197 #ifdef AFS_LINUX26_ENV
198 #ifdef CONFIG_PM
199         if (
200 #ifdef PF_FREEZE
201             current->flags & PF_FREEZE
202 #else
203 #if defined(STRUCT_TASK_STRUCT_HAS_TODO)
204             !current->todo
205 #else
206 #if defined(STRUCT_TASK_STRUCT_HAS_THREAD_INFO)
207             test_ti_thread_flag(current->thread_info, TIF_FREEZE)
208 #else
209             test_ti_thread_flag(task_thread_info(current), TIF_FREEZE)
210 #endif
211 #endif
212 #endif
213             )
214 #ifdef LINUX_REFRIGERATOR_TAKES_PF_FREEZE
215             refrigerator(PF_FREEZE);
216 #else
217             refrigerator();
218 #endif
219 #endif
220 #endif
221         AFS_GLOCK();
222         if (signal_pending(current)) {
223             retval = EINTR;
224             break;
225         }
226     }
227     remove_wait_queue(&evp->cond, &wait);
228     set_current_state(TASK_RUNNING);
229
230     relevent(evp);
231     return retval;
232 }
233
234 /* afs_osi_Sleep -- waits for an event to be notified, ignoring signals.
235  * - NOTE: that on Linux, there are circumstances in which TASK_INTERRUPTIBLE
236  *   can wake up, even if all signals are blocked
237  * - TODO: handle signals correctly by passing an indication back to the
238  *   caller that the wait has been interrupted and the stack should be cleaned
239  *   up preparatory to signal delivery
240  */
241 void
242 afs_osi_Sleep(void *event)
243 {
244     sigset_t saved_set;
245
246     SIG_LOCK(current);
247     saved_set = current->blocked;
248     sigfillset(&current->blocked);
249     RECALC_SIGPENDING(current);
250     SIG_UNLOCK(current);
251
252     afs_osi_SleepSig(event);
253
254     SIG_LOCK(current);
255     current->blocked = saved_set;
256     RECALC_SIGPENDING(current);
257     SIG_UNLOCK(current);
258 }
259
260 /* osi_TimedSleep
261  * 
262  * Arguments:
263  * event - event to sleep on
264  * ams --- max sleep time in milliseconds
265  * aintok - 1 if should sleep interruptibly
266  *
267  * Returns 0 if timeout, EINTR if signalled, and EGAIN if it might
268  * have raced.
269  */
270 static int
271 osi_TimedSleep(char *event, afs_int32 ams, int aintok)
272 {
273     int code = 0;
274     long ticks = (ams * HZ / 1000) + 1;
275     struct afs_event *evp;
276 #ifdef DECLARE_WAITQUEUE
277     DECLARE_WAITQUEUE(wait, current);
278 #else
279     struct wait_queue wait = { current, NULL };
280 #endif
281
282     evp = afs_getevent(event);
283     if (!evp) {
284         afs_addevent(event);
285         evp = afs_getevent(event);
286     }
287
288     add_wait_queue(&evp->cond, &wait);
289     set_current_state(TASK_INTERRUPTIBLE);
290     /* always sleep TASK_INTERRUPTIBLE to keep load average
291      * from artifically increasing. */
292     AFS_GUNLOCK();
293
294     if (aintok) {
295         if (schedule_timeout(ticks))
296             code = EINTR;
297     } else
298         schedule_timeout(ticks);
299 #ifdef AFS_LINUX26_ENV
300 #ifdef CONFIG_PM
301     if (
302 #ifdef PF_FREEZE
303             current->flags & PF_FREEZE
304 #else
305 #if defined(STRUCT_TASK_STRUCT_HAS_TODO)
306             !current->todo
307 #else
308 #if defined(STRUCT_TASK_STRUCT_HAS_THREAD_INFO)
309             test_ti_thread_flag(current->thread_info, TIF_FREEZE)
310 #else
311             test_ti_thread_flag(task_thread_info(current), TIF_FREEZE)
312 #endif
313 #endif
314 #endif
315             )
316 #ifdef LINUX_REFRIGERATOR_TAKES_PF_FREEZE
317         refrigerator(PF_FREEZE);
318 #else
319         refrigerator();
320 #endif
321 #endif
322 #endif
323
324     AFS_GLOCK();
325     remove_wait_queue(&evp->cond, &wait);
326     set_current_state(TASK_RUNNING);
327
328     relevent(evp);
329
330     return code;
331 }
332
333
334 int
335 afs_osi_Wakeup(void *event)
336 {
337     int ret = 2;
338     struct afs_event *evp;
339
340     evp = afs_getevent(event);
341     if (!evp)                   /* No sleepers */
342         return 1;
343
344     if (evp->refcount > 1) {
345         evp->seq++;
346         wake_up(&evp->cond);
347         ret = 0;
348     }
349     relevent(evp);
350     return ret;
351 }