xref: /haiku/src/system/kernel/kernel_daemon.cpp (revision 50b3e74489a1a46fec88df793e4f6780e4de933c)
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 
26 struct daemon : DoublyLinkedListLinkImpl<struct daemon> {
27 	daemon_hook	function;
28 	void*		arg;
29 	int32		frequency;
30 	int32		offset;
31 	bool		executing;
32 };
33 
34 
35 typedef DoublyLinkedList<struct daemon> DaemonList;
36 
37 
38 class KernelDaemon {
39 public:
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 
48 private:
49 	static	status_t			_DaemonThreadEntry(void* data);
50 			struct daemon*		_NextDaemon(struct daemon& marker);
51 			status_t			_DaemonThread();
52 			bool				_IsDaemon() const;
53 
54 private:
55 			recursive_lock		fLock;
56 			DaemonList			fDaemons;
57 			thread_id			fThread;
58 			ConditionVariable	fUnregisterCondition;
59 			int32				fUnregisterWaiters;
60 };
61 
62 
63 static KernelDaemon sKernelDaemon;
64 static KernelDaemon sResourceResizer;
65 
66 
67 status_t
68 KernelDaemon::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 
84 status_t
85 KernelDaemon::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 
122 status_t
123 KernelDaemon::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 
161 void
162 KernelDaemon::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
189 KernelDaemon::_DaemonThreadEntry(void* data)
190 {
191 	return ((KernelDaemon*)data)->_DaemonThread();
192 }
193 
194 
195 struct daemon*
196 KernelDaemon::_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 
217 status_t
218 KernelDaemon::_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 
255 bool
256 KernelDaemon::_IsDaemon() const
257 {
258 	return find_thread(NULL) == fThread;
259 }
260 
261 
262 // #pragma mark -
263 
264 
265 static int
266 dump_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 
281 extern "C" status_t
282 register_kernel_daemon(daemon_hook function, void* arg, int frequency)
283 {
284 	return sKernelDaemon.Register(function, arg, frequency);
285 }
286 
287 
288 extern "C" status_t
289 unregister_kernel_daemon(daemon_hook function, void* arg)
290 {
291 	return sKernelDaemon.Unregister(function, arg);
292 }
293 
294 
295 extern "C" status_t
296 register_resource_resizer(daemon_hook function, void* arg, int frequency)
297 {
298 	return sResourceResizer.Register(function, arg, frequency);
299 }
300 
301 
302 extern "C" status_t
303 unregister_resource_resizer(daemon_hook function, void* arg)
304 {
305 	return sResourceResizer.Unregister(function, arg);
306 }
307 
308 
309 //	#pragma mark -
310 
311 
312 extern "C" status_t
313 kernel_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