linux-note-we-use-interruptible-sleeps-in-non-compliant-manner-20020731
[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("$Header$");
14
15 #include "../afs/sysincludes.h" /* Standard vendor system headers */
16 #include "../afs/afsincludes.h" /* Afs-based standard headers */
17 #include "../afs/afs_stats.h"   /* afs statistics */
18
19
20 static int osi_TimedSleep(char *event, afs_int32 ams, int aintok);
21 void afs_osi_Wakeup(char *event);
22 void afs_osi_Sleep(char *event);
23
24 static char waitV, dummyV;
25
26
27 void 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 afs_osi_CancelWait(struct afs_osi_WaitHandle *achandle)
35 {
36     caddr_t proc;
37
38     AFS_STATCNT(osi_CancelWait);
39     proc = achandle->proc;
40     if (proc == 0) return;
41     achandle->proc = (caddr_t) 0;   /* so dude can figure out he was signalled */
42     afs_osi_Wakeup(&waitV);
43 }
44
45 /* afs_osi_Wait
46  * Waits for data on ahandle, or ams ms later.  ahandle may be null.
47  * Returns 0 if timeout and EINTR if signalled.
48  */
49 int afs_osi_Wait(afs_int32 ams, struct afs_osi_WaitHandle *ahandle, int aintok)
50 {
51     int code;
52     afs_int32 endTime, tid;
53     struct timer_list *timer = NULL;
54
55     AFS_STATCNT(osi_Wait);
56     endTime = osi_Time() + (ams/1000);
57     if (ahandle)
58         ahandle->proc = (caddr_t) current;
59
60     do {
61         AFS_ASSERT_GLOCK();
62         code = 0;
63         code = osi_TimedSleep(&waitV, ams, aintok);
64
65         if (code) break;
66         if (ahandle && (ahandle->proc == (caddr_t) 0)) {
67             /* we've been signalled */
68             break;
69         }
70     } while (osi_Time() < endTime);
71     return code;
72 }
73
74
75
76
77 typedef struct afs_event {
78     struct afs_event *next;     /* next in hash chain */
79     char *event;                /* lwp event: an address */
80     int refcount;               /* Is it in use? */
81     int seq;                    /* Sequence number: this is incremented
82                                    by wakeup calls; wait will not return until
83                                    it changes */
84 #if defined(AFS_LINUX24_ENV)
85     wait_queue_head_t cond;
86 #else
87     struct wait_queue *cond;
88 #endif
89 } afs_event_t;
90
91 #define HASHSIZE 128
92 afs_event_t *afs_evhasht[HASHSIZE];/* Hash table for events */
93 #define afs_evhash(event)       (afs_uint32) ((((long)event)>>2) & (HASHSIZE-1));
94 int afs_evhashcnt = 0;
95
96 /* Get and initialize event structure corresponding to lwp event (i.e. address)
97  * */
98 static afs_event_t *afs_getevent(char *event)
99 {
100     afs_event_t *evp, *newp = 0;
101     int hashcode;
102
103     AFS_ASSERT_GLOCK();
104     hashcode = afs_evhash(event);
105     evp = afs_evhasht[hashcode];
106     while (evp) {
107         if (evp->event == event) {
108             evp->refcount++;
109             return evp;
110         }
111         if (evp->refcount == 0)
112             newp = evp;
113         evp = evp->next;
114     }
115     if (!newp)
116         return NULL;
117
118     newp->event = event;
119     newp->refcount = 1;
120     return newp;
121 }
122
123 /* afs_addevent -- allocates a new event for the address.  It isn't returned;
124  *     instead, afs_getevent should be called again.  Thus, the real effect of
125  *     this routine is to add another event to the hash bucket for this
126  *     address.
127  *
128  * Locks:
129  *     Called with GLOCK held. However the function might drop
130  *     GLOCK when it calls osi_AllocSmallSpace for allocating
131  *     a new event (In Linux, the allocator drops GLOCK to avoid
132  *     a deadlock).
133  */
134
135 static void afs_addevent(char *event)
136 {
137     int hashcode;
138     afs_event_t *newp;
139     
140     AFS_ASSERT_GLOCK();
141     hashcode = afs_evhash(event);
142     newp = osi_AllocSmallSpace(sizeof(afs_event_t));
143     afs_evhashcnt++;
144     newp->next = afs_evhasht[hashcode];
145     afs_evhasht[hashcode] = newp;
146 #if defined(AFS_LINUX24_ENV)
147     init_waitqueue_head(&newp->cond);
148 #else
149     init_waitqueue(&newp->cond);
150 #endif
151     newp->seq = 0;
152     newp->event = &dummyV;  /* Dummy address for new events */
153     newp->refcount = 0;
154 }
155
156 #ifndef set_current_state
157 #define set_current_state(x)            current->state = (x);
158 #endif
159
160 /* Release the specified event */
161 #define relevent(evp) ((evp)->refcount--)
162
163 /* afs_osi_SleepSig
164  *
165  * Waits for an event to be notified, returning early if a signal
166  * is received.  Returns EINTR if signaled, and 0 otherwise.
167  */
168 int afs_osi_SleepSig(char *event)
169 {
170     struct afs_event *evp;
171     int seq, retval;
172 #ifdef DECLARE_WAITQUEUE
173     DECLARE_WAITQUEUE(wait, current);
174 #else
175     struct wait_queue wait = { current, NULL };
176 #endif
177
178     evp = afs_getevent(event);
179     if (!evp) {
180         afs_addevent(event);
181         evp = afs_getevent(event);
182     }
183
184     seq = evp->seq;
185     retval = 0;
186
187     add_wait_queue(&evp->cond, &wait);
188     while (seq == evp->seq) {
189         set_current_state(TASK_INTERRUPTIBLE);
190         AFS_ASSERT_GLOCK();
191         AFS_GUNLOCK();
192         schedule();
193         AFS_GLOCK();
194         if (signal_pending(current)) {
195             retval = EINTR;
196             break;
197         }
198     }
199     remove_wait_queue(&evp->cond, &wait);
200     set_current_state(TASK_RUNNING);
201
202     relevent(evp);
203     return retval;
204 }
205
206 /* afs_osi_Sleep -- waits for an event to be notified, ignoring signals.
207  * - NOTE: that on Linux, there are circumstances in which TASK_INTERRUPTIBLE
208  *   can wake up, even if all signals are blocked
209  * - TODO: handle signals correctly by passing an indication back to the
210  *   caller that the wait has been interrupted and the stack should be cleaned
211  *   up preparatory to signal delivery
212  */
213 void afs_osi_Sleep(char *event)
214 {
215     sigset_t saved_set;
216
217     spin_lock_irq(&current->sigmask_lock);
218     saved_set = current->blocked;
219     sigfillset(&current->blocked);
220     recalc_sigpending(current);
221     spin_unlock_irq(&current->sigmask_lock);
222
223     afs_osi_SleepSig(event);
224
225     spin_lock_irq(&current->sigmask_lock);
226     current->blocked = saved_set;
227     recalc_sigpending(current);
228     spin_unlock_irq(&current->sigmask_lock);
229 }
230
231 /* osi_TimedSleep
232  * 
233  * Arguments:
234  * event - event to sleep on
235  * ams --- max sleep time in milliseconds
236  * aintok - 1 if should sleep interruptibly
237  *
238  * Returns 0 if timeout, EINTR if signalled, and EGAIN if it might
239  * have raced.
240  */
241 static int osi_TimedSleep(char *event, afs_int32 ams, int aintok)
242 {
243     int code = 0;
244     long ticks = (ams * HZ / 1000) + 1;
245     struct afs_event *evp;
246 #ifdef DECLARE_WAITQUEUE
247     DECLARE_WAITQUEUE(wait, current);
248 #else
249     struct wait_queue wait = { current, NULL };
250 #endif
251
252     evp = afs_getevent(event);
253     if (!evp) {
254         afs_addevent(event);
255         evp = afs_getevent(event);
256     }
257
258     add_wait_queue(&evp->cond, &wait);
259     set_current_state(TASK_INTERRUPTIBLE);
260                 /* always sleep TASK_INTERRUPTIBLE to keep load average
261                    from artifically increasing. */
262     AFS_GUNLOCK();
263
264     if (aintok) {
265         if (schedule_timeout(ticks))
266             code = EINTR;
267     } else
268         schedule_timeout(ticks);
269
270     AFS_GLOCK();
271     remove_wait_queue(&evp->cond, &wait);
272     set_current_state(TASK_RUNNING);
273
274     relevent(evp);
275
276     return code;
277 }
278
279
280 void afs_osi_Wakeup(char *event)
281 {
282     struct afs_event *evp;
283
284     evp = afs_getevent(event);
285     if (!evp)                          /* No sleepers */
286         return;
287
288     if (evp->refcount > 1) {
289         evp->seq++;    
290         wake_up(&evp->cond);
291     }
292     relevent(evp);
293 }