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