2 * Copyright 2000, International Business Machines Corporation and others.
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
15 #include "../afs/param.h" /* Should be always first */
16 #include <afsconfig.h>
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"
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 */
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
36 Simple_lock afs_callout_lock;
38 #define AFS_DISABLE_LOCK(_pri, _lock) disable_lock((_pri), (_lock))
39 #define AFS_UNLOCK_ENABLE(_pri, _lock) unlock_enable((_pri), (_lock))
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 */
51 int ncallo; /* number of callout table elements */
52 struct tos *head; /* callout table head element */
55 struct callo afs_callo = {0, NULL};/* callout table anchor */
57 static void timeout_end(struct trb *); /* timeout()'s timeout function */
58 extern void tstart(struct trb *);
59 extern void i_enable(int);
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 */
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 */
73 tv.it_value.tv_sec = ticks / HZ;
74 tv.it_value.tv_nsec = (ticks % HZ) * (NS_PER_SEC / HZ);
76 osi_Assert(afs_callo.ncallo != 0);
80 ipri = AFS_DISABLE_LOCK(INTMAX, &afs_callout_lock);
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.
86 for(tos = afs_callo.head; tos != NULL; tos = tos->tonext) {
87 if((tos->trb->tof == func) && (tos->trb->func_data == (ulong) arg)) {
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
98 for(tos = afs_callo.head; tos != NULL; tos = tos->tonext) {
99 if(tos->trb->tof == NULL) {
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.
111 osi_Assert(tos != NULL);
114 * A pending timeout for the specified function WAS found.
115 * If the request is still active, stop it.
117 while (tstop(tos->trb)) {
118 AFS_UNLOCK_ENABLE(ipri, &afs_callout_lock);
122 tos->type = type; /* Temp */
123 tos->p1 = (long)p1; /* Temp */
124 tos->trb->knext = NULL;
125 tos->trb->kprev = NULL;
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;
135 AFS_UNLOCK_ENABLE(ipri, &afs_callout_lock);
140 register void (*func)(),
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 */
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) {
159 * Found it on the timeout list - stop the pending timeout
162 while(tstop(tos->trb)) {
163 AFS_UNLOCK_ENABLE(ipri, &afs_callout_lock);
164 goto untimeout_retry;
167 /* Mark this callout table entry as free. */
168 tos->trb->knext = NULL;
169 tos->trb->kprev = NULL;
170 tos->trb->tof = NULL;
172 AFS_UNLOCK_ENABLE(ipri, &afs_callout_lock);
176 static void timeout_end(
177 struct trb *trb) /* trb of the current timeout */
179 register void (*func)(); /* function to call at timeout */
181 ipri = AFS_DISABLE_LOCK(INTMAX, &afs_callout_lock);
185 trb->tof = NULL; /* Zero out pointer to user function */
187 AFS_UNLOCK_ENABLE(ipri, &afs_callout_lock);
189 (* func)(trb->func_data);
190 /* for compatibility with untimeout() */
195 register int cocnt) /* # entries to change callout table by */
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 */
206 * Callout table is being enlarged - keep working until the
207 * right number of elements have been added.
210 /* Allocate a timer request block. */
211 trb = (struct trb *) talloc();
214 * If the low-level timer service could not provide
215 * a trb, the callout table can't be expanded any
223 /* Allocate memory for the callout table structure. */
225 xmalloc((uint)sizeof(struct tos), (uint)0, pinned_heap);
228 * If memory couldn't be allocated for the tos, the
229 * callout table can't be expanded any more so get out.
236 bzero(tos, sizeof(struct tos));
239 /* The trb and the tos were both allocated. */
243 * Debug code to ensure that the low-level timer
244 * service talloc() clears out the pointers.
246 osi_Assert(trb->knext == NULL);
247 osi_Assert(trb->kprev == NULL);
250 ipri = AFS_DISABLE_LOCK(INTMAX, &afs_callout_lock);
251 if(afs_callo.head == NULL) {
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.
257 afs_callo.head = tos;
261 * The callout table is not empty. Chain this
262 * trb to the head of the callout chain.
264 tos->tonext = afs_callo.head;
265 afs_callo.head->toprev = tos;
266 afs_callo.head = tos;
269 /* Just finished adding a trb to the callout table. */
272 AFS_UNLOCK_ENABLE(ipri, &afs_callout_lock);
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.
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.
289 osi_Assert(afs_callo.ncallo >= -cocnt);
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).
298 ipri = AFS_DISABLE_LOCK(INTMAX,
300 tos = afs_callo.head;
301 osi_Assert(tos != NULL);
304 * Keep walking down the callout chain until
305 * a tos is found which is not currently
308 while((tos != NULL) &&
309 (tos->trb->tof != NULL)) {
314 * If trb is not NULL, then there was not a
315 * callout table entry that wasn't set to
318 osi_Assert(tos != NULL);
321 * Found a free callout table element, free
322 * it and remove it from the callout table.
325 if(afs_callo.head == tos) {
326 afs_callo.head = tos->tonext;
327 if(afs_callo.head != NULL) {
328 afs_callo.head->toprev = NULL;
332 osi_Assert(tos->toprev != NULL);
333 tos->toprev->tonext = tos->tonext;
334 if(tos->tonext != NULL) {
335 tos->tonext->toprev =
340 * Just finished removing a trb from the
345 AFS_UNLOCK_ENABLE(ipri, &afs_callout_lock);
346 xmfree((void *)tos, pinned_heap);