Import Upstream version 1.8.5
[hcoop/debian/openafs.git] / src / afs / AIX / osi_timeout.c
CommitLineData
805e021f
CE
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 */
35Simple_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
41struct 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
49struct callo {
50 int ncallo; /* number of callout table elements */
51 struct tos *head; /* callout table head element */
52};
53
54struct callo afs_callo = { 0, NULL }; /* callout table anchor */
55
56static void timeout_end(struct trb *); /* timeout()'s timeout function */
57extern void tstart(struct trb *);
58extern void i_enable(int);
59
60void
61timeout(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
137void
138untimeout(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
173static void
174timeout_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
191int
192timeoutcf(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}