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
);