1/*
2 * Copyright 2003-2010, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <kernel_daemon.h>
8
9#include <new>
10#include <stdlib.h>
11
12#include <KernelExport.h>
13
14#include <elf.h>
15#include <lock.h>
16#include <util/AutoLock.h>
17#include <util/DoublyLinkedList.h>
18
19
20struct daemon : DoublyLinkedListLinkImpl<struct daemon> {
21	daemon_hook	function;
22	void*		arg;
23	int32		frequency;
24	bigtime_t	last;
25	bool		executing;
26};
27
28
29typedef DoublyLinkedList<struct daemon> DaemonList;
30
31
32class KernelDaemon {
33public:
34			status_t			Init(const char* name);
35
36			status_t			Register(daemon_hook function, void* arg,
37									int frequency);
38			status_t			Unregister(daemon_hook function, void* arg);
39
40			void				Dump();
41
42private:
43	static	status_t			_DaemonThreadEntry(void* data);
44			struct daemon*		_NextDaemon(struct daemon& marker);
45			status_t			_DaemonThread();
46			bool				_IsDaemon() const;
47
48private:
49			recursive_lock		fLock;
50			DaemonList			fDaemons;
51			sem_id				fDaemonAddedSem;
52			thread_id			fThread;
53			ConditionVariable	fUnregisterCondition;
54			int32				fUnregisterWaiters;
55};
56
57
58static KernelDaemon sKernelDaemon;
59static KernelDaemon sResourceResizer;
60
61
62status_t
63KernelDaemon::Init(const char* name)
64{
65	recursive_lock_init(&fLock, name);
66
67	fDaemonAddedSem = create_sem(0, "kernel daemon added");
68	if (fDaemonAddedSem < 0)
69		return fDaemonAddedSem;
70
71	fThread = spawn_kernel_thread(&_DaemonThreadEntry, name, B_LOW_PRIORITY,
72		this);
73	if (fThread < 0)
74		return fThread;
75
76	resume_thread(fThread);
77	fUnregisterCondition.Init(this, name);
78
79	return B_OK;
80}
81
82
83status_t
84KernelDaemon::Register(daemon_hook function, void* arg, int frequency)
85{
86	if (function == NULL || frequency < 1)
87		return B_BAD_VALUE;
88
89	struct ::daemon* daemon = new(std::nothrow) (struct ::daemon);
90	if (daemon == NULL)
91		return B_NO_MEMORY;
92
93	daemon->function = function;
94	daemon->arg = arg;
95	daemon->frequency = frequency;
96	daemon->last = 0;
97	daemon->executing = false;
98
99	RecursiveLocker locker(fLock);
100	fDaemons.Add(daemon);
101	locker.Unlock();
102
103	release_sem(fDaemonAddedSem);
104	return B_OK;
105}
106
107
108status_t
109KernelDaemon::Unregister(daemon_hook function, void* arg)
110{
111	RecursiveLocker locker(fLock);
112
113	DaemonList::Iterator iterator = fDaemons.GetIterator();
114
115	// search for the daemon and remove it from the list
116	while (iterator.HasNext()) {
117		struct daemon* daemon = iterator.Next();
118
119		if (daemon->function == function && daemon->arg == arg) {
120			// found it!
121			if (!_IsDaemon()) {
122				// wait if it's busy
123				while (daemon->executing) {
124					fUnregisterWaiters++;
125					fUnregisterCondition.Wait(locker.Get());
126				}
127			}
128
129			iterator.Remove();
130			delete daemon;
131			return B_OK;
132		}
133	}
134
135	return B_ENTRY_NOT_FOUND;
136}
137
138
139void
140KernelDaemon::Dump()
141{
142	DaemonList::Iterator iterator = fDaemons.GetIterator();
143
144	while (iterator.HasNext()) {
145		struct daemon* daemon = iterator.Next();
146		const char* imageName;
147		const char* symbol;
148		bool exactMatch;
149
150		status_t status = elf_debug_lookup_symbol_address(
151			(addr_t)daemon->function, NULL, &symbol, &imageName, &exactMatch);
152		if (status == B_OK && exactMatch) {
153			if (strchr(imageName, '/') != NULL)
154				imageName = strrchr(imageName, '/') + 1;
155
156			kprintf("\t%s:%s (%p)", imageName, symbol, daemon->function);
157		} else
158			kprintf("\t%p", daemon->function);
159
160		kprintf(", arg %p%s\n", daemon->arg,
161			daemon->executing ? " (running) " : "");
162	}
163}
164
165
166/*static*/ status_t
167KernelDaemon::_DaemonThreadEntry(void* data)
168{
169	return ((KernelDaemon*)data)->_DaemonThread();
170}
171
172
173struct daemon*
174KernelDaemon::_NextDaemon(struct daemon& marker)
175{
176	struct daemon* daemon;
177
178	if (marker.arg == NULL) {
179		// The marker is not part of the list yet, just return the first entry
180		daemon = fDaemons.Head();
181	} else {
182		daemon = fDaemons.GetNext(&marker);
183		fDaemons.Remove(&marker);
184	}
185
186	marker.arg = daemon;
187
188	if (daemon != NULL)
189		fDaemons.InsertAfter(daemon, &marker);
190
191	return daemon;
192}
193
194
195status_t
196KernelDaemon::_DaemonThread()
197{
198	struct daemon marker;
199	const bigtime_t start = system_time(), iterationToUsecs = 100 * 1000;
200
201	marker.arg = NULL;
202
203	while (true) {
204		RecursiveLocker locker(fLock);
205
206		bigtime_t timeout = INT64_MAX;
207
208		// iterate through the list and execute each daemon if needed
209		while (struct daemon* daemon = _NextDaemon(marker)) {
210			daemon->executing = true;
211			locker.Unlock();
212
213			const bigtime_t time = system_time();
214			bigtime_t next = (daemon->last +
215				(daemon->frequency * iterationToUsecs)) - time;
216			if (next <= 0) {
217				daemon->last = time;
218				next = daemon->frequency * iterationToUsecs;
219				daemon->function(daemon->arg, (time - start) / iterationToUsecs);
220			}
221			timeout = min_c(timeout, next);
222
223			locker.Lock();
224			daemon->executing = false;
225		}
226
227		if (fUnregisterWaiters != 0) {
228			fUnregisterCondition.NotifyAll();
229			fUnregisterWaiters = 0;
230		}
231
232		locker.Unlock();
233
234		acquire_sem_etc(fDaemonAddedSem, 1, B_RELATIVE_TIMEOUT, timeout);
235	}
236
237	return B_OK;
238}
239
240
241bool
242KernelDaemon::_IsDaemon() const
243{
244	return find_thread(NULL) == fThread;
245}
246
247
248// #pragma mark -
249
250
251static int
252dump_daemons(int argc, char** argv)
253{
254	kprintf("kernel daemons:\n");
255	sKernelDaemon.Dump();
256
257	kprintf("\nresource resizers:\n");
258	sResourceResizer.Dump();
259
260	return 0;
261}
262
263
264//	#pragma mark -
265
266
267extern "C" status_t
268register_kernel_daemon(daemon_hook function, void* arg, int frequency)
269{
270	return sKernelDaemon.Register(function, arg, frequency);
271}
272
273
274extern "C" status_t
275unregister_kernel_daemon(daemon_hook function, void* arg)
276{
277	return sKernelDaemon.Unregister(function, arg);
278}
279
280
281extern "C" status_t
282register_resource_resizer(daemon_hook function, void* arg, int frequency)
283{
284	return sResourceResizer.Register(function, arg, frequency);
285}
286
287
288extern "C" status_t
289unregister_resource_resizer(daemon_hook function, void* arg)
290{
291	return sResourceResizer.Unregister(function, arg);
292}
293
294
295//	#pragma mark -
296
297
298extern "C" status_t
299kernel_daemon_init(void)
300{
301	new(&sKernelDaemon) KernelDaemon;
302	if (sKernelDaemon.Init("kernel daemon") != B_OK)
303		panic("kernel_daemon_init(): failed to init kernel daemon");
304
305	new(&sResourceResizer) KernelDaemon;
306	if (sResourceResizer.Init("resource resizer") != B_OK)
307		panic("kernel_daemon_init(): failed to init resource resizer");
308
309	add_debugger_command("daemons", dump_daemons,
310		"Shows registered kernel daemons.");
311	return B_OK;
312}
313