Linux: Mark our super block as not updating access time
[openafs.git] / src / afs / LINUX24 / 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         AFS_GLOCK();
198         if (signal_pending(current)) {
199             retval = EINTR;
200             break;
201         }
202     }
203     remove_wait_queue(&evp->cond, &wait);
204     set_current_state(TASK_RUNNING);
205
206     relevent(evp);
207     return retval;
208 }
209
210 /* afs_osi_Sleep -- waits for an event to be notified, ignoring signals.
211  * - NOTE: that on Linux, there are circumstances in which TASK_INTERRUPTIBLE
212  *   can wake up, even if all signals are blocked
213  * - TODO: handle signals correctly by passing an indication back to the
214  *   caller that the wait has been interrupted and the stack should be cleaned
215  *   up preparatory to signal delivery
216  */
217 void
218 afs_osi_Sleep(void *event)
219 {
220     sigset_t saved_set;
221
222     SIG_LOCK(current);
223     saved_set = current->blocked;
224     sigfillset(&current->blocked);
225     RECALC_SIGPENDING(current);
226     SIG_UNLOCK(current);
227
228     afs_osi_SleepSig(event);
229
230     SIG_LOCK(current);
231     current->blocked = saved_set;
232     RECALC_SIGPENDING(current);
233     SIG_UNLOCK(current);
234 }
235
236 /* osi_TimedSleep
237  * 
238  * Arguments:
239  * event - event to sleep on
240  * ams --- max sleep time in milliseconds
241  * aintok - 1 if should sleep interruptibly
242  *
243  * Returns 0 if timeout, EINTR if signalled, and EGAIN if it might
244  * have raced.
245  */
246 static int
247 osi_TimedSleep(char *event, afs_int32 ams, int aintok)
248 {
249     int code = 0;
250     long ticks = (ams * HZ / 1000) + 1;
251     struct afs_event *evp;
252 #ifdef DECLARE_WAITQUEUE
253     DECLARE_WAITQUEUE(wait, current);
254 #else
255     struct wait_queue wait = { current, NULL };
256 #endif
257
258     evp = afs_getevent(event);
259     if (!evp) {
260         afs_addevent(event);
261         evp = afs_getevent(event);
262     }
263
264     add_wait_queue(&evp->cond, &wait);
265     set_current_state(TASK_INTERRUPTIBLE);
266     /* always sleep TASK_INTERRUPTIBLE to keep load average
267      * from artifically increasing. */
268     AFS_GUNLOCK();
269
270     if (aintok) {
271         if (schedule_timeout(ticks))
272             code = EINTR;
273     } else
274         schedule_timeout(ticks);
275
276     AFS_GLOCK();
277     remove_wait_queue(&evp->cond, &wait);
278     set_current_state(TASK_RUNNING);
279
280     relevent(evp);
281
282     return code;
283 }
284
285
286 int
287 afs_osi_Wakeup(void *event)
288 {
289     int ret = 2;
290     struct afs_event *evp;
291
292     evp = afs_getevent(event);
293     if (!evp)                   /* No sleepers */
294         return 1;
295
296     if (evp->refcount > 1) {
297         evp->seq++;
298         wake_up(&evp->cond);
299         ret = 0;
300     }
301     relevent(evp);
302     return ret;
303 }