1e873783ee6bb5ae16fa96132af92ec8aa28ec54
[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 RCSID
14     ("$Header$");
15
16 #include "afs/sysincludes.h"    /* Standard vendor system headers */
17 #include "afsincludes.h"        /* Afs-based standard headers */
18 #include "afs/afs_stats.h"      /* afs statistics */
19
20 #include <linux/freezer.h>
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             test_ti_thread_flag(current->thread_info, TIF_FREEZE)
207 #endif
208 #endif
209             )
210 #ifdef LINUX_REFRIGERATOR_TAKES_PF_FREEZE
211             refrigerator(PF_FREEZE);
212 #else
213             refrigerator();
214 #endif
215 #endif
216 #endif
217         AFS_GLOCK();
218         if (signal_pending(current)) {
219             retval = EINTR;
220             break;
221         }
222     }
223     remove_wait_queue(&evp->cond, &wait);
224     set_current_state(TASK_RUNNING);
225
226     relevent(evp);
227     return retval;
228 }
229
230 /* afs_osi_Sleep -- waits for an event to be notified, ignoring signals.
231  * - NOTE: that on Linux, there are circumstances in which TASK_INTERRUPTIBLE
232  *   can wake up, even if all signals are blocked
233  * - TODO: handle signals correctly by passing an indication back to the
234  *   caller that the wait has been interrupted and the stack should be cleaned
235  *   up preparatory to signal delivery
236  */
237 void
238 afs_osi_Sleep(void *event)
239 {
240     sigset_t saved_set;
241
242     SIG_LOCK(current);
243     saved_set = current->blocked;
244     sigfillset(&current->blocked);
245     RECALC_SIGPENDING(current);
246     SIG_UNLOCK(current);
247
248     afs_osi_SleepSig(event);
249
250     SIG_LOCK(current);
251     current->blocked = saved_set;
252     RECALC_SIGPENDING(current);
253     SIG_UNLOCK(current);
254 }
255
256 /* osi_TimedSleep
257  * 
258  * Arguments:
259  * event - event to sleep on
260  * ams --- max sleep time in milliseconds
261  * aintok - 1 if should sleep interruptibly
262  *
263  * Returns 0 if timeout, EINTR if signalled, and EGAIN if it might
264  * have raced.
265  */
266 static int
267 osi_TimedSleep(char *event, afs_int32 ams, int aintok)
268 {
269     int code = 0;
270     long ticks = (ams * HZ / 1000) + 1;
271     struct afs_event *evp;
272 #ifdef DECLARE_WAITQUEUE
273     DECLARE_WAITQUEUE(wait, current);
274 #else
275     struct wait_queue wait = { current, NULL };
276 #endif
277
278     evp = afs_getevent(event);
279     if (!evp) {
280         afs_addevent(event);
281         evp = afs_getevent(event);
282     }
283
284     add_wait_queue(&evp->cond, &wait);
285     set_current_state(TASK_INTERRUPTIBLE);
286     /* always sleep TASK_INTERRUPTIBLE to keep load average
287      * from artifically increasing. */
288     AFS_GUNLOCK();
289
290     if (aintok) {
291         if (schedule_timeout(ticks))
292             code = EINTR;
293     } else
294         schedule_timeout(ticks);
295 #ifdef AFS_LINUX26_ENV
296 #ifdef CONFIG_PM
297     if (
298 #ifdef PF_FREEZE
299             current->flags & PF_FREEZE
300 #else
301 #if defined(STRUCT_TASK_STRUCT_HAS_TODO)
302             !current->todo
303 #else
304             test_ti_thread_flag(current->thread_info, TIF_FREEZE)
305 #endif
306 #endif
307             )
308 #ifdef LINUX_REFRIGERATOR_TAKES_PF_FREEZE
309         refrigerator(PF_FREEZE);
310 #else
311         refrigerator();
312 #endif
313 #endif
314 #endif
315
316     AFS_GLOCK();
317     remove_wait_queue(&evp->cond, &wait);
318     set_current_state(TASK_RUNNING);
319
320     relevent(evp);
321
322     return code;
323 }
324
325
326 int
327 afs_osi_Wakeup(void *event)
328 {
329     int ret = 2;
330     struct afs_event *evp;
331
332     evp = afs_getevent(event);
333     if (!evp)                   /* No sleepers */
334         return 1;
335
336     if (evp->refcount > 1) {
337         evp->seq++;
338         wake_up(&evp->cond);
339         ret = 0;
340     }
341     relevent(evp);
342     return ret;
343 }