9acd27a8c4d12f35f65123e7bc131354b73f8f5e
[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 "../afs/param.h"       /* Should be always first */
11 #include "../afs/sysincludes.h" /* Standard vendor system headers */
12 #include "../afs/afsincludes.h" /* Afs-based standard headers */
13 #include "../afs/afs_stats.h"   /* afs statistics */
14
15
16
17 #if defined(AFS_GLOBAL_SUNLOCK)
18 static int osi_TimedSleep(char *event, afs_int32 ams, int aintok);
19 #endif
20
21 void afs_osi_Wakeup(char *event);
22 void afs_osi_Sleep(char *event);
23
24 static char waitV;
25
26 #if ! defined(AFS_GLOBAL_SUNLOCK)
27
28 /* call procedure aproc with arock as an argument, in ams milliseconds */
29 static struct timer_list *afs_osi_CallProc(void *aproc, void *arock, int ams)
30 {
31     struct timer_list *timer = NULL;
32     
33     timer = (struct timer_list*)osi_Alloc(sizeof(struct timer_list));
34     if (timer) {
35         init_timer(timer);
36         timer->expires = (ams*afs_hz)/1000 + 1;
37         timer->data = (unsigned long)arock;
38         timer->function = aproc;
39         add_timer(timer);
40     }
41     return timer;
42 }
43
44 /* cancel a timeout, whether or not it has already occurred */
45 static int afs_osi_CancelProc(struct timer_list *timer)
46 {
47     if (timer) {
48         del_timer(timer);
49         osi_Free(timer, sizeof(struct timer_list));
50     }
51     return 0;
52 }
53
54 static AfsWaitHack()
55 {
56     AFS_STATCNT(WaitHack);
57     wakeup(&waitV);
58 }
59
60 #endif
61
62 void afs_osi_InitWaitHandle(struct afs_osi_WaitHandle *achandle)
63 {
64     AFS_STATCNT(osi_InitWaitHandle);
65     achandle->proc = (caddr_t) 0;
66 }
67
68 /* cancel osi_Wait */
69 void afs_osi_CancelWait(struct afs_osi_WaitHandle *achandle)
70 {
71     caddr_t proc;
72
73     AFS_STATCNT(osi_CancelWait);
74     proc = achandle->proc;
75     if (proc == 0) return;
76     achandle->proc = (caddr_t) 0;   /* so dude can figure out he was signalled */
77     afs_osi_Wakeup(&waitV);
78 }
79
80 /* afs_osi_Wait
81  * Waits for data on ahandle, or ams ms later.  ahandle may be null.
82  * Returns 0 if timeout and EINTR if signalled.
83  */
84 int afs_osi_Wait(afs_int32 ams, struct afs_osi_WaitHandle *ahandle, int aintok)
85 {
86     int code;
87     afs_int32 endTime, tid;
88     struct timer_list *timer = NULL;
89
90     AFS_STATCNT(osi_Wait);
91     endTime = osi_Time() + (ams/1000);
92     if (ahandle)
93         ahandle->proc = (caddr_t) current;
94
95     do {
96         AFS_ASSERT_GLOCK();
97         code = 0;
98 #if     defined(AFS_GLOBAL_SUNLOCK)
99         code = osi_TimedSleep(&waitV, ams, 1);
100         if (code) {
101                 if (aintok) break;
102                 flush_signals(current);
103                 code = 0;
104         }
105 #else
106         timer = afs_osi_CallProc(AfsWaitHack, (char *) current, ams);
107         afs_osi_Sleep(&waitV);
108         afs_osi_CancelProc(timer);
109 #endif /* AFS_GLOBAL_SUNLOCK */
110         if (ahandle && (ahandle->proc == (caddr_t) 0)) {
111             /* we've been signalled */
112             break;
113         }
114     } while (osi_Time() < endTime);
115     return code;
116 }
117
118
119
120
121 typedef struct afs_event {
122     struct afs_event *next;     /* next in hash chain */
123     char *event;                /* lwp event: an address */
124     int refcount;               /* Is it in use? */
125     int seq;                    /* Sequence number: this is incremented
126                                    by wakeup calls; wait will not return until
127                                    it changes */
128 #if defined(AFS_LINUX24_ENV)
129     wait_queue_head_t cond;
130 #else
131     struct wait_queue *cond;
132 #endif
133 } afs_event_t;
134
135 #define HASHSIZE 128
136 afs_event_t *afs_evhasht[HASHSIZE];/* Hash table for events */
137 #define afs_evhash(event)       (afs_uint32) ((((long)event)>>2) & (HASHSIZE-1));
138 int afs_evhashcnt = 0;
139
140 /* Get and initialize event structure corresponding to lwp event (i.e. address)
141  * */
142 static afs_event_t *afs_getevent(char *event)
143 {
144     afs_event_t *evp, *newp = 0;
145     int hashcode;
146
147     AFS_ASSERT_GLOCK();
148     hashcode = afs_evhash(event);
149     evp = afs_evhasht[hashcode];
150     while (evp) {
151         if (evp->event == event) {
152             evp->refcount++;
153             return evp;
154         }
155         if (evp->refcount == 0)
156             newp = evp;
157         evp = evp->next;
158     }
159     if (!newp) {
160         newp = (afs_event_t *) osi_AllocSmallSpace(sizeof (afs_event_t));
161         afs_evhashcnt++;
162         newp->next = afs_evhasht[hashcode];
163         afs_evhasht[hashcode] = newp;
164 #if defined(AFS_LINUX24_ENV)
165         init_waitqueue_head(&newp->cond);
166 #else
167         init_waitqueue(&newp->cond);
168 #endif
169         newp->seq = 0;
170     }
171     newp->event = event;
172     newp->refcount = 1;
173     return newp;
174 }
175
176 /* Release the specified event */
177 #define relevent(evp) ((evp)->refcount--)
178
179
180 void afs_osi_Sleep(char *event)
181 {
182     struct afs_event *evp;
183     int seq;
184
185     evp = afs_getevent(event);
186     seq = evp->seq;
187     while (seq == evp->seq) {
188         AFS_ASSERT_GLOCK();
189         AFS_GUNLOCK();
190         interruptible_sleep_on(&evp->cond);
191         AFS_GLOCK();
192     }
193     relevent(evp);
194 }
195
196 /* osi_TimedSleep
197  * 
198  * Arguments:
199  * event - event to sleep on
200  * ams --- max sleep time in milliseconds
201  * aintok - 1 if should sleep interruptibly
202  *
203  * Returns 0 if timeout and EINTR if signalled.
204  *
205  * While the Linux kernel still has a global lock, we can use the standard
206  * sleep calls and drop our locks early. The kernel lock will protect us
207  * until we get to sleep.
208  */
209 static int osi_TimedSleep(char *event, afs_int32 ams, int aintok)
210 {
211     long t = ams * HZ / 1000;
212     struct afs_event *evp;
213
214     evp = afs_getevent(event);
215
216     AFS_GUNLOCK();
217     if (aintok)
218         t = interruptible_sleep_on_timeout(&evp->cond, t);
219     else
220         t = sleep_on_timeout(&evp->cond, t);
221     AFS_GLOCK();
222
223     return t ? EINTR : 0;
224 }
225
226
227 void afs_osi_Wakeup(char *event)
228 {
229     struct afs_event *evp;
230
231     evp = afs_getevent(event);
232     if (evp->refcount > 1) {
233         evp->seq++;    
234         wake_up(&evp->cond);
235     }
236     relevent(evp);
237 }