linux-sleep-wakeup-cleanup-20020624
[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 void afs_osi_Sleep(char *event)
208 {
209     sigset_t saved_set;
210
211     spin_lock_irq(&current->sigmask_lock);
212     saved_set = current->blocked;
213     sigfillset(&current->blocked);
214     recalc_sigpending(current);
215     spin_unlock_irq(&current->sigmask_lock);
216
217     afs_osi_SleepSig(event);
218
219     spin_lock_irq(&current->sigmask_lock);
220     current->blocked = saved_set;
221     recalc_sigpending(current);
222     spin_unlock_irq(&current->sigmask_lock);
223 }
224
225 /* osi_TimedSleep
226  * 
227  * Arguments:
228  * event - event to sleep on
229  * ams --- max sleep time in milliseconds
230  * aintok - 1 if should sleep interruptibly
231  *
232  * Returns 0 if timeout, EINTR if signalled, and EGAIN if it might
233  * have raced.
234  */
235 static int osi_TimedSleep(char *event, afs_int32 ams, int aintok)
236 {
237     int code = 0;
238     long ticks = (ams * HZ / 1000) + 1;
239     struct afs_event *evp;
240 #ifdef DECLARE_WAITQUEUE
241     DECLARE_WAITQUEUE(wait, current);
242 #else
243     struct wait_queue wait = { current, NULL };
244 #endif
245
246     evp = afs_getevent(event);
247     if (!evp) {
248         afs_addevent(event);
249         evp = afs_getevent(event);
250     }
251
252     add_wait_queue(&evp->cond, &wait);
253     set_current_state(TASK_INTERRUPTIBLE);
254                 /* always sleep TASK_INTERRUPTIBLE to keep load average
255                    from artifically increasing. */
256     AFS_GUNLOCK();
257
258     if (aintok) {
259         if (schedule_timeout(ticks))
260             code = EINTR;
261     } else
262         schedule_timeout(ticks);
263
264     AFS_GLOCK();
265     remove_wait_queue(&evp->cond, &wait);
266     set_current_state(TASK_RUNNING);
267
268     relevent(evp);
269
270     return code;
271 }
272
273
274 void afs_osi_Wakeup(char *event)
275 {
276     struct afs_event *evp;
277
278     evp = afs_getevent(event);
279     if (!evp)                          /* No sleepers */
280         return;
281
282     if (evp->refcount > 1) {
283         evp->seq++;    
284         wake_up(&evp->cond);
285     }
286     relevent(evp);
287 }