xref: /openbsd-src/usr.sbin/amd/amd/clock.c (revision 819011edeb9887dad37bf71f8d8961a6057a887b)
1 /*
2  * Copyright (c) 1989 Jan-Simon Pendry
3  * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
4  * Copyright (c) 1989, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Jan-Simon Pendry at Imperial College, London.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  *	from: @(#)clock.c	8.1 (Berkeley) 6/6/93
35  *	$Id: clock.c,v 1.7 2014/10/26 03:08:21 guenther Exp $
36  */
37 
38 /*
39  * Callouts.
40  *
41  * Modelled on kernel object of the same name.
42  * See usual references.
43  *
44  * Use of a heap-based mechanism was rejected:
45  * 1.  more complex implementation needed.
46  * 2.  not obvious that a list is too slow for Amd.
47  */
48 
49 #include "am.h"
50 
51 typedef struct callout callout;
52 struct callout {
53 	callout	*c_next;		/* List of callouts */
54 	void	(*c_fn)(void *);	/* Function to call */
55 	void	*c_closure;		/* Closure to pass to call */
56 	time_t	c_time;			/* Time of call */
57 	int	c_id;			/* Unique identifier */
58 };
59 
60 static callout callouts;		/* List of pending callouts */
61 static callout *free_callouts;		/* Cache of free callouts */
62 static int nfree_callouts;		/* Number on free list */
63 static int callout_id;			/* Next free callout identifier */
64 time_t next_softclock;			/* Time of next call to softclock() */
65 
66 /*
67  * Number of callout slots we keep on the free list
68  */
69 #define	CALLOUT_FREE_SLOP	10
70 
71 /*
72  * Global assumption: valid id's are non-zero.
73  */
74 #define	CID_ALLOC()	(++callout_id)
75 #define	CID_UNDEF	(0)
76 
77 static callout *
alloc_callout(void)78 alloc_callout(void)
79 {
80 	callout *cp = free_callouts;
81 	if (cp) {
82 		--nfree_callouts;
83 		free_callouts = free_callouts->c_next;
84 		return cp;
85 	}
86 	return ALLOC(callout);
87 }
88 
89 static void
free_callout(callout * cp)90 free_callout(callout *cp)
91 {
92 	if (nfree_callouts > CALLOUT_FREE_SLOP) {
93 		free(cp);
94 	} else {
95 		cp->c_next = free_callouts;
96 		free_callouts = cp;
97 		nfree_callouts++;
98 	}
99 }
100 
101 /*
102  * Schedule a callout.
103  *
104  * (*fn)(closure) will be called at clocktime() + secs
105  */
106 int
timeout(unsigned int secs,void (* fn)(void *),void * closure)107 timeout(unsigned int secs, void (*fn)(void *), void *closure)
108 {
109 	callout *cp, *cp2;
110 	time_t t = clocktime() + secs;
111 
112 	/*
113 	 * Allocate and fill in a new callout structure
114 	 */
115 	callout *cpnew = alloc_callout();
116 	cpnew->c_closure = closure;
117 	cpnew->c_fn = fn;
118 	cpnew->c_time = t;
119 	cpnew->c_id = CID_ALLOC();
120 
121 	if (t < next_softclock)
122 		next_softclock = t;
123 
124 	/*
125 	 * Find the correct place in the list
126 	 */
127 	for (cp = &callouts; (cp2 = cp->c_next); cp = cp2)
128 		if (cp2->c_time >= t)
129 			break;
130 
131 	/*
132 	 * And link it in
133 	 */
134 	cp->c_next = cpnew;
135 	cpnew->c_next = cp2;
136 
137 	/*
138 	 * Return callout identifier
139 	 */
140 	return cpnew->c_id;
141 }
142 
143 /*
144  * De-schedule a callout
145  */
146 void
untimeout(int id)147 untimeout(int id)
148 {
149 	callout *cp, *cp2;
150 	for (cp = &callouts; (cp2 = cp->c_next); cp = cp2) {
151 		if (cp2->c_id == id) {
152 			cp->c_next = cp2->c_next;
153 			free_callout(cp2);
154 			break;
155 		}
156 	}
157 }
158 
159 /*
160  * Reschedule after clock changed
161  */
162 void
reschedule_timeouts(time_t now,time_t then)163 reschedule_timeouts(time_t now, time_t then)
164 {
165 	callout *cp;
166 
167 	for (cp = callouts.c_next; cp; cp = cp->c_next) {
168 		if (cp->c_time >= now && cp->c_time <= then) {
169 			plog(XLOG_WARNING, "job %d rescheduled to run immediately", cp->c_id);
170 #ifdef DEBUG
171 			dlog("rescheduling job %d back %d seconds",
172 				cp->c_id, cp->c_time - now);
173 #endif
174 			next_softclock = cp->c_time = now;
175 		}
176 	}
177 }
178 
179 /*
180  * Clock handler
181  */
182 int
softclock(void)183 softclock(void)
184 {
185 	time_t now;
186 	callout *cp;
187 
188 	do {
189 		if (task_notify_todo)
190 			do_task_notify();
191 
192 		now = clocktime();
193 
194 		/*
195 		 * While there are more callouts waiting...
196 		 */
197 		while ((cp = callouts.c_next) && cp->c_time <= now) {
198 			/*
199 			 * Extract first from list, save fn & closure and
200 			 * unlink callout from list and free.
201 			 * Finally call function.
202 			 *
203 			 * The free is done first because
204 			 * it is quite common that the
205 			 * function will call timeout()
206 			 * and try to allocate a callout
207 			 */
208 			void (*fn)(void *) = cp->c_fn;
209 			void *closure = cp->c_closure;
210 
211 			callouts.c_next = cp->c_next;
212 			free_callout(cp);
213 #ifdef DEBUG
214 			/*dlog("Calling %#x(%#x)", fn, closure);*/
215 #endif /* DEBUG */
216 			(*fn)(closure);
217 		}
218 
219 	} while (task_notify_todo);
220 
221 	/*
222 	 * Return number of seconds to next event,
223 	 * or 0 if there is no event.
224 	 */
225 	if ((cp = callouts.c_next))
226 		return cp->c_time - now;
227 	return 0;
228 }
229