death to register
[openafs.git] / src / afs / AIX / osi_timeout.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 /*
11  * afs_timout.c
12  *
13  * Implements:
14  */
15 #include <afsconfig.h>
16 #include "afs/param.h"
17
18
19 #include "afs/sysincludes.h"    /* Standard vendor system headers */
20 #include "afsincludes.h"        /* Afs-based standard headers */
21 #include "sys/limits.h"
22 #include "sys/types.h"
23 #include "sys/user.h"
24 #include "sys/pri.h"
25 #include "sys/priv.h"           /* XXX */
26 #include "sys/lockl.h"
27 #include "sys/malloc.h"
28 #include <sys/timer.h>          /* For the timer related defines                */
29 #include <sys/intr.h>           /* for the serialization defines                */
30
31 /* NOTE: This lock makes the callout table MP-safe. timeout itself could
32  * be subject to deadlocks if used for anything more complex than we are
33  * doing here.
34  */
35 Simple_lock afs_callout_lock;
36
37 #define AFS_DISABLE_LOCK(_pri, _lock) disable_lock((_pri), (_lock))
38 #define AFS_UNLOCK_ENABLE(_pri, _lock) unlock_enable((_pri), (_lock))
39
40
41 struct tos {
42     struct tos *toprev;         /* previous tos in callout table */
43     struct tos *tonext;         /* next tos in callout table    */
44     struct trb *trb;            /* this timer request block     */
45     afs_int32 type;
46     long p1;
47 };
48
49 struct callo {
50     int ncallo;                 /* number of callout table elements     */
51     struct tos *head;           /* callout table head element           */
52 };
53
54 struct callo afs_callo = { 0, NULL };   /* callout table anchor                     */
55
56 static void timeout_end(struct trb *);  /* timeout()'s timeout function */
57 extern void tstart(struct trb *);
58 extern void i_enable(int);
59
60 void
61 timeout(void (*func) (),        /* function to call at timeout */
62         caddr_t arg,    /*   It's argument. */
63         int ticks,      /* when to set timeout for */
64         int type, char *p1)
65 {
66     int ipri;           /* caller's interrupt priority  */
67     struct tos *tos;    /* tos to use for timeout       */
68     struct trb *trb;    /* trb in the tos being used    */
69     struct itimerstruc_t tv;    /* timeout interval             */
70
71     tv.it_value.tv_sec = ticks / HZ;
72     tv.it_value.tv_nsec = (ticks % HZ) * (NS_PER_SEC / HZ);
73
74     osi_Assert(afs_callo.ncallo != 0);
75
76   timeout_retry:
77
78     ipri = AFS_DISABLE_LOCK(INTMAX, &afs_callout_lock);
79     /*
80      *  Run the callout table chain to see if there is already a pending
81      *  timeout for the specified function.  If so, that timeout will
82      *  be cancelled and the tos re-used.
83      */
84     for (tos = afs_callo.head; tos != NULL; tos = tos->tonext) {
85         if ((tos->trb->tof == func) && (tos->trb->func_data == (ulong) arg)) {
86             break;
87         }
88     }
89
90     /*
91      *  If a pending timeout for the specified function was NOT found,
92      *  then the callout table chain will have to be run to find an
93      *  unused tos.
94      */
95     if (tos == NULL) {
96         for (tos = afs_callo.head; tos != NULL; tos = tos->tonext) {
97             if (tos->trb->tof == NULL) {
98                 break;
99             }
100         }
101
102         /*
103          *  If there isn't an available tos, then there is no error
104          *  recovery.  This means that either the caller has not
105          *  correctly registered the number of callout table entries
106          *  that would be needed or is incorrectly using the ones that
107          *  were registered.  Either way, panic is the only recourse.
108          */
109         osi_Assert(tos != NULL);
110     }
111     /* 
112      *  A pending timeout for the specified function WAS found.
113      *  If the request is still active, stop it.
114      */
115     while (tstop(tos->trb)) {
116         AFS_UNLOCK_ENABLE(ipri, &afs_callout_lock);
117         goto timeout_retry;
118     }
119
120     tos->type = type;           /* Temp */
121     tos->p1 = (long)p1;         /* Temp */
122     tos->trb->knext = NULL;
123     tos->trb->kprev = NULL;
124     tos->trb->flags = 0;
125     tos->trb->timeout = tv;
126     tos->trb->tof = func;
127     tos->trb->func = timeout_end;
128     tos->trb->func_data = (ulong) arg;
129     tos->trb->ipri = INTTIMER;
130     tos->trb->id = -1;
131     tstart(tos->trb);
132
133     AFS_UNLOCK_ENABLE(ipri, &afs_callout_lock);
134 }
135
136
137 void
138 untimeout(void (*func) (), ulong arg)
139 {
140     int ipri;           /* caller's interrupt priority  */
141     struct tos *tos;    /* tos to walk callout table    */
142     struct trb *trb;    /* trb for this tos             */
143
144   untimeout_retry:
145
146     ipri = AFS_DISABLE_LOCK(INTMAX, &afs_callout_lock);
147     /*  Run the callout table chain looking for the timeout.  */
148     for (tos = afs_callo.head; tos != NULL; tos = tos->tonext) {
149         if (tos->trb->tof == func && tos->trb->func_data == arg) {
150             break;
151         }
152     }
153
154     if (tos) {
155         /*
156          *  Found it on the timeout list - stop the pending timeout 
157          *  if it is active.
158          */
159         while (tstop(tos->trb)) {
160             AFS_UNLOCK_ENABLE(ipri, &afs_callout_lock);
161             goto untimeout_retry;
162         }
163
164         /*  Mark this callout table entry as free.  */
165         tos->trb->knext = NULL;
166         tos->trb->kprev = NULL;
167         tos->trb->tof = NULL;
168     }
169     AFS_UNLOCK_ENABLE(ipri, &afs_callout_lock);
170 }
171
172
173 static void
174 timeout_end(struct trb *trb)
175 {                               /* trb of the current timeout     */
176     void (*func) ();    /* function to call at timeout  */
177     int ipri;
178     ipri = AFS_DISABLE_LOCK(INTMAX, &afs_callout_lock);
179
180     func = trb->tof;
181     trb->func = NULL;
182     trb->tof = NULL;            /* Zero out pointer to user function  */
183
184     AFS_UNLOCK_ENABLE(ipri, &afs_callout_lock);
185
186     (*func) (trb->func_data);
187     /* for compatibility with untimeout() */
188 }
189
190
191 int
192 timeoutcf(int cocnt)
193 {                               /* # entries to change callout table by   */
194     int ipri;           /* caller's interrupt priority  */
195     int rv;             /* return value to the caller           */
196     struct tos *tos;    /* tos to add to/remove from table    */
197     struct trb *trb;    /* trb in the tos to be added/removed */
198
199     rv = 0;
200
201     if (cocnt > 0) {
202         /*
203          *  Callout table is being enlarged - keep working until the
204          *  right number of elements have been added.
205          */
206         while (cocnt > 0) {
207             /*  Allocate a timer request block.  */
208             trb = (struct trb *)talloc();
209
210             /*
211              *  If the low-level timer service could not provide
212              *  a trb, the callout table can't be expanded any
213              *  more so get out.
214              */
215             if (trb == NULL) {
216                 rv = -1;
217                 break;
218             }
219
220             /*  Allocate memory for the callout table structure.  */
221             tos = (struct tos *)
222                 xmalloc((uint) sizeof(struct tos), (uint) 0, pinned_heap);
223
224             /*
225              *  If memory couldn't be allocated for the tos, the
226              *  callout table can't be expanded any more so get out.
227              */
228             if (tos == NULL) {
229                 rv = -1;
230                 break;
231             } else {
232                 memset(tos, 0, sizeof(struct tos));
233             }
234
235             /*  The trb and the tos were both allocated.  */
236             tos->trb = trb;
237 #ifdef DEBUG
238             /* 
239              *  Debug code to ensure that the low-level timer 
240              *  service talloc() clears out the pointers.
241              */
242             osi_Assert(trb->knext == NULL);
243             osi_Assert(trb->kprev == NULL);
244 #endif /* DEBUG */
245
246             ipri = AFS_DISABLE_LOCK(INTMAX, &afs_callout_lock);
247             if (afs_callo.head == NULL) {
248                 /*
249                  *  The callout table is currently empty.  This
250                  *  is the easy case, just set the head of the
251                  *  callout chain to this tos.
252                  */
253                 afs_callo.head = tos;
254             } else {
255                 /*
256                  *  The callout table is not empty.  Chain this
257                  *  trb to the head of the callout chain.
258                  */
259                 tos->tonext = afs_callo.head;
260                 afs_callo.head->toprev = tos;
261                 afs_callo.head = tos;
262             }
263
264             /*  Just finished adding a trb to the callout table.  */
265             afs_callo.ncallo++;
266             cocnt--;
267             AFS_UNLOCK_ENABLE(ipri, &afs_callout_lock);
268         }
269     } else {
270         /*
271          *  Callout table is being shrunk - keep working until the
272          *  right number of elements have been removed being careful
273          *  only to remove elements which do not belong to timeout
274          *  requests that are currently active.
275          */
276         if (cocnt < 0) {
277
278             /*
279              *  There had better be at least as many tos's in
280              *  the callout table as the size by which the caller 
281              *  wants to decrease the size of the table.
282              */
283             osi_Assert(afs_callo.ncallo >= -cocnt);
284
285             while (cocnt < 0) {
286
287                 /*
288                  *  Start from the head of the callout chain,
289                  *  making sure that there is a tos at the 
290                  *  head (i.e. that there is a callout chain).
291                  */
292                 ipri = AFS_DISABLE_LOCK(INTMAX, &afs_callout_lock);
293                 tos = afs_callo.head;
294                 osi_Assert(tos != NULL);
295
296                 /*
297                  *  Keep walking down the callout chain until
298                  *  a tos is found which is not currently 
299                  *  active.
300                  */
301                 while ((tos != NULL) && (tos->trb->tof != NULL)) {
302                     tos = tos->tonext;
303                 }
304
305                 /*
306                  *  If trb is not NULL, then there was not a
307                  *  callout table entry that wasn't set to
308                  *  timeout.  Panic.
309                  */
310                 osi_Assert(tos != NULL);
311
312                 /*
313                  *  Found a free callout table element, free
314                  *  it and remove it from the callout table.
315                  */
316                 tfree(tos->trb);
317                 if (afs_callo.head == tos) {
318                     afs_callo.head = tos->tonext;
319                     if (afs_callo.head != NULL) {
320                         afs_callo.head->toprev = NULL;
321                     }
322                 } else {
323                     osi_Assert(tos->toprev != NULL);
324                     tos->toprev->tonext = tos->tonext;
325                     if (tos->tonext != NULL) {
326                         tos->tonext->toprev = tos->toprev;
327                     }
328                 }
329                 /*
330                  *  Just finished removing a trb from the
331                  *  callout table.
332                  */
333                 afs_callo.ncallo--;
334                 cocnt++;
335                 AFS_UNLOCK_ENABLE(ipri, &afs_callout_lock);
336                 xmfree((void *)tos, pinned_heap);
337
338             }
339         }
340     }
341
342     return (rv);
343 }