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
20// The use of snooze() in the kernel_daemon() function is very inaccurate, of
21// course - the time the daemons need to execute add up in each iteration.
22// But since the kernel daemon is documented to be very inaccurate, this
23// actually might be okay (and that's why it's implemented this way now :-).
24// BeOS R5 seems to do it in the same way, anyway.
25
26struct daemon : DoublyLinkedListLinkImpl<struct daemon> {
27	daemon_hook	function;
28	void*		arg;
29	int32		frequency;
30	int32		offset;
31	bool		executing;
32};
33
34
35typedef DoublyLinkedList<struct daemon> DaemonList;
36
37
38class KernelDaemon {
39public:
40			status_t			Init(const char* name);
41
42			status_t			Register(daemon_hook function, void* arg,
43									int frequency);
44			status_t			Unregister(daemon_hook function, void* arg);
45
46			void				Dump();
47
48private:
49	static	status_t			_DaemonThreadEntry(void* data);
50			struct daemon*		_NextDaemon(struct daemon& marker);
51			status_t			_DaemonThread();
52			bool				_IsDaemon() const;
53
54private:
55			recursive_lock		fLock;
56			DaemonList			fDaemons;
57			thread_id			fThread;
58			ConditionVariable	fUnregisterCondition;
59			int32				fUnregisterWaiters;
60};
61
62
63static KernelDaemon sKernelDaemon;
64static KernelDaemon sResourceResizer;
65
66
67status_t
68KernelDaemon::Init(const char* name)
69{
70	recursive_lock_init(&fLock, name);
71
72	fThread = spawn_kernel_thread(&_DaemonThreadEntry, name, B_LOW_PRIORITY,
73		this);
74	if (fThread < 0)
75		return fThread;
76
77	resume_thread(fThread);
78	fUnregisterCondition.Init(this, name);
79
80	return B_OK;
81}
82
83
84status_t
85KernelDaemon::Register(daemon_hook function, void* arg, int frequency)
86{
87	if (function == NULL || frequency < 1)
88		return B_BAD_VALUE;
89
90	struct ::daemon* daemon = new(std::nothrow) (struct ::daemon);
91	if (daemon == NULL)
92		return B_NO_MEMORY;
93
94	daemon->function = function;
95	daemon->arg = arg;
96	daemon->frequency = frequency;
97	daemon->executing = false;
98
99	RecursiveLocker _(fLock);
100
101	if (frequency > 1) {
102		// we try to balance the work-load for each daemon run
103		// (beware, it's a very simple algorithm, yet effective)
104
105		DaemonList::Iterator iterator = fDaemons.GetIterator();
106		int32 num = 0;
107
108		while (iterator.HasNext()) {
109			if (iterator.Next()->frequency == frequency)
110				num++;
111		}
112
113		daemon->offset = num % frequency;
114	} else
115		daemon->offset = 0;
116
117	fDaemons.Add(daemon);
118	return B_OK;
119}
120
121
122status_t
123KernelDaemon::Unregister(daemon_hook function, void* arg)
124{
125	RecursiveLocker locker(fLock);
126
127	DaemonList::Iterator iterator = fDaemons.GetIterator();
128
129	// search for the daemon and remove it from the list
130	while (iterator.HasNext()) {
131		struct daemon* daemon = iterator.Next();
132
133		if (daemon->function == function && daemon->arg == arg) {
134			// found it!
135			if (!_IsDaemon()) {
136				// wait if it's busy
137				while (daemon->executing) {
138					fUnregisterWaiters++;
139
140					ConditionVariableEntry entry;
141					fUnregisterCondition.Add(&entry);
142
143					locker.Unlock();
144
145					entry.Wait();
146
147					locker.Lock();
148				}
149			}
150
151			iterator.Remove();
152			delete daemon;
153			return B_OK;
154		}
155	}
156
157	return B_ENTRY_NOT_FOUND;
158}
159
160
161void
162KernelDaemon::Dump()
163{
164	DaemonList::Iterator iterator = fDaemons.GetIterator();
165
166	while (iterator.HasNext()) {
167		struct daemon* daemon = iterator.Next();
168		const char* imageName;
169		const char* symbol;
170		bool exactMatch;
171
172		status_t status = elf_debug_lookup_symbol_address(
173			(addr_t)daemon->function, NULL, &symbol, &imageName, &exactMatch);
174		if (status == B_OK && exactMatch) {
175			if (strchr(imageName, '/') != NULL)
176				imageName = strrchr(imageName, '/') + 1;
177
178			kprintf("\t%s:%s (%p)", imageName, symbol, daemon->function);
179		} else
180			kprintf("\t%p", daemon->function);
181
182		kprintf(", arg %p%s\n", daemon->arg,
183			daemon->executing ? " (running) " : "");
184	}
185}
186
187
188/*static*/ status_t
189KernelDaemon::_DaemonThreadEntry(void* data)
190{
191	return ((KernelDaemon*)data)->_DaemonThread();
192}
193
194
195struct daemon*
196KernelDaemon::_NextDaemon(struct daemon& marker)
197{
198	struct daemon* daemon;
199
200	if (marker.arg == NULL) {
201		// The marker is not part of the list yet, just return the first entry
202		daemon = fDaemons.Head();
203	} else {
204		daemon = fDaemons.GetNext(&marker);
205		fDaemons.Remove(&marker);
206	}
207
208	marker.arg = daemon;
209
210	if (daemon != NULL)
211		fDaemons.InsertAfter(daemon, &marker);
212
213	return daemon;
214}
215
216
217status_t
218KernelDaemon::_DaemonThread()
219{
220	struct daemon marker;
221	int32 iteration = 0;
222
223	marker.arg = NULL;
224
225	while (true) {
226		RecursiveLocker locker(fLock);
227
228		// iterate through the list and execute each daemon if needed
229		while (struct daemon* daemon = _NextDaemon(marker)) {
230			daemon->executing = true;
231			locker.Unlock();
232
233			if (((iteration + daemon->offset) % daemon->frequency) == 0)
234				daemon->function(daemon->arg, iteration);
235
236			locker.Lock();
237			daemon->executing = false;
238		}
239
240		if (fUnregisterWaiters != 0) {
241			fUnregisterCondition.NotifyAll();
242			fUnregisterWaiters = 0;
243		}
244
245		locker.Unlock();
246
247		iteration++;
248		snooze(100000);	// 0.1 seconds
249	}
250
251	return B_OK;
252}
253
254
255bool
256KernelDaemon::_IsDaemon() const
257{
258	return find_thread(NULL) == fThread;
259}
260
261
262// #pragma mark -
263
264
265static int
266dump_daemons(int argc, char** argv)
267{
268	kprintf("kernel daemons:\n");
269	sKernelDaemon.Dump();
270
271	kprintf("\nresource resizers:\n");
272	sResourceResizer.Dump();
273
274	return 0;
275}
276
277
278//	#pragma mark -
279
280
281extern "C" status_t
282register_kernel_daemon(daemon_hook function, void* arg, int frequency)
283{
284	return sKernelDaemon.Register(function, arg, frequency);
285}
286
287
288extern "C" status_t
289unregister_kernel_daemon(daemon_hook function, void* arg)
290{
291	return sKernelDaemon.Unregister(function, arg);
292}
293
294
295extern "C" status_t
296register_resource_resizer(daemon_hook function, void* arg, int frequency)
297{
298	return sResourceResizer.Register(function, arg, frequency);
299}
300
301
302extern "C" status_t
303unregister_resource_resizer(daemon_hook function, void* arg)
304{
305	return sResourceResizer.Unregister(function, arg);
306}
307
308
309//	#pragma mark -
310
311
312extern "C" status_t
313kernel_daemon_init(void)
314{
315	new(&sKernelDaemon) KernelDaemon;
316	if (sKernelDaemon.Init("kernel daemon") != B_OK)
317		panic("kernel_daemon_init(): failed to init kernel daemon");
318
319	new(&sResourceResizer) KernelDaemon;
320	if (sResourceResizer.Init("resource resizer") != B_OK)
321		panic("kernel_daemon_init(): failed to init resource resizer");
322
323	add_debugger_command("daemons", dump_daemons,
324		"Shows registered kernel daemons.");
325	return B_OK;
326}
327