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 <afsconfig.h>
16 #include "afs/param.h"
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"
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 */
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
35 Simple_lock afs_callout_lock;
37 #define AFS_DISABLE_LOCK(_pri, _lock) disable_lock((_pri), (_lock))
38 #define AFS_UNLOCK_ENABLE(_pri, _lock) unlock_enable((_pri), (_lock))
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 */
50 int ncallo; /* number of callout table elements */
51 struct tos *head; /* callout table head element */
54 struct callo afs_callo = { 0, NULL }; /* callout table anchor */
56 static void timeout_end(struct trb *); /* timeout()'s timeout function */
57 extern void tstart(struct trb *);
58 extern void i_enable(int);
61 timeout(void (*func) (), /* function to call at timeout */
62 caddr_t arg, /* It's argument. */
63 int ticks, /* when to set timeout for */
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 */
71 tv.it_value.tv_sec = ticks / HZ;
72 tv.it_value.tv_nsec = (ticks % HZ) * (NS_PER_SEC / HZ);
74 osi_Assert(afs_callo.ncallo != 0);
78 ipri = AFS_DISABLE_LOCK(INTMAX, &afs_callout_lock);
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.
84 for (tos = afs_callo.head; tos != NULL; tos = tos->tonext) {
85 if ((tos->trb->tof == func) && (tos->trb->func_data == (ulong) arg)) {
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
96 for (tos = afs_callo.head; tos != NULL; tos = tos->tonext) {
97 if (tos->trb->tof == NULL) {
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.
109 osi_Assert(tos != NULL);
112 * A pending timeout for the specified function WAS found.
113 * If the request is still active, stop it.
115 while (tstop(tos->trb)) {
116 AFS_UNLOCK_ENABLE(ipri, &afs_callout_lock);
120 tos->type = type; /* Temp */
121 tos->p1 = (long)p1; /* Temp */
122 tos->trb->knext = NULL;
123 tos->trb->kprev = NULL;
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;
133 AFS_UNLOCK_ENABLE(ipri, &afs_callout_lock);
138 untimeout(void (*func) (), ulong arg)
140 int ipri; /* caller's interrupt priority */
141 struct tos *tos; /* tos to walk callout table */
142 struct trb *trb; /* trb for this tos */
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) {
156 * Found it on the timeout list - stop the pending timeout
159 while (tstop(tos->trb)) {
160 AFS_UNLOCK_ENABLE(ipri, &afs_callout_lock);
161 goto untimeout_retry;
164 /* Mark this callout table entry as free. */
165 tos->trb->knext = NULL;
166 tos->trb->kprev = NULL;
167 tos->trb->tof = NULL;
169 AFS_UNLOCK_ENABLE(ipri, &afs_callout_lock);
174 timeout_end(struct trb *trb)
175 { /* trb of the current timeout */
176 void (*func) (); /* function to call at timeout */
178 ipri = AFS_DISABLE_LOCK(INTMAX, &afs_callout_lock);
182 trb->tof = NULL; /* Zero out pointer to user function */
184 AFS_UNLOCK_ENABLE(ipri, &afs_callout_lock);
186 (*func) (trb->func_data);
187 /* for compatibility with untimeout() */
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 */
203 * Callout table is being enlarged - keep working until the
204 * right number of elements have been added.
207 /* Allocate a timer request block. */
208 trb = (struct trb *)talloc();
211 * If the low-level timer service could not provide
212 * a trb, the callout table can't be expanded any
220 /* Allocate memory for the callout table structure. */
222 xmalloc((uint) sizeof(struct tos), (uint) 0, pinned_heap);
225 * If memory couldn't be allocated for the tos, the
226 * callout table can't be expanded any more so get out.
232 memset(tos, 0, sizeof(struct tos));
235 /* The trb and the tos were both allocated. */
239 * Debug code to ensure that the low-level timer
240 * service talloc() clears out the pointers.
242 osi_Assert(trb->knext == NULL);
243 osi_Assert(trb->kprev == NULL);
246 ipri = AFS_DISABLE_LOCK(INTMAX, &afs_callout_lock);
247 if (afs_callo.head == NULL) {
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.
253 afs_callo.head = tos;
256 * The callout table is not empty. Chain this
257 * trb to the head of the callout chain.
259 tos->tonext = afs_callo.head;
260 afs_callo.head->toprev = tos;
261 afs_callo.head = tos;
264 /* Just finished adding a trb to the callout table. */
267 AFS_UNLOCK_ENABLE(ipri, &afs_callout_lock);
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.
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.
283 osi_Assert(afs_callo.ncallo >= -cocnt);
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).
292 ipri = AFS_DISABLE_LOCK(INTMAX, &afs_callout_lock);
293 tos = afs_callo.head;
294 osi_Assert(tos != NULL);
297 * Keep walking down the callout chain until
298 * a tos is found which is not currently
301 while ((tos != NULL) && (tos->trb->tof != NULL)) {
306 * If trb is not NULL, then there was not a
307 * callout table entry that wasn't set to
310 osi_Assert(tos != NULL);
313 * Found a free callout table element, free
314 * it and remove it from the callout table.
317 if (afs_callo.head == tos) {
318 afs_callo.head = tos->tonext;
319 if (afs_callo.head != NULL) {
320 afs_callo.head->toprev = NULL;
323 osi_Assert(tos->toprev != NULL);
324 tos->toprev->tonext = tos->tonext;
325 if (tos->tonext != NULL) {
326 tos->tonext->toprev = tos->toprev;
330 * Just finished removing a trb from the
335 AFS_UNLOCK_ENABLE(ipri, &afs_callout_lock);
336 xmfree((void *)tos, pinned_heap);