xref: /haiku/src/system/kernel/kernel_daemon.cpp (revision 46b7da1f4f40f7157d74fc7fb26ff9ec7f2416f2)
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 struct daemon : DoublyLinkedListLinkImpl<struct daemon> {
21 	daemon_hook	function;
22 	void*		arg;
23 	int32		frequency;
24 	bigtime_t	last;
25 	bool		executing;
26 };
27 
28 
29 typedef DoublyLinkedList<struct daemon> DaemonList;
30 
31 
32 class KernelDaemon {
33 public:
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 
42 private:
43 	static	status_t			_DaemonThreadEntry(void* data);
44 			struct daemon*		_NextDaemon(struct daemon& marker);
45 			status_t			_DaemonThread();
46 			bool				_IsDaemon() const;
47 
48 private:
49 			recursive_lock		fLock;
50 			DaemonList			fDaemons;
51 			sem_id				fDaemonAddedSem;
52 			thread_id			fThread;
53 			ConditionVariable	fUnregisterCondition;
54 			int32				fUnregisterWaiters;
55 };
56 
57 
58 static KernelDaemon sKernelDaemon;
59 static KernelDaemon sResourceResizer;
60 
61 
62 status_t
Init(const char * name)63 KernelDaemon::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 
83 status_t
Register(daemon_hook function,void * arg,int frequency)84 KernelDaemon::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 
108 status_t
Unregister(daemon_hook function,void * arg)109 KernelDaemon::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 
139 void
Dump()140 KernelDaemon::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
_DaemonThreadEntry(void * data)167 KernelDaemon::_DaemonThreadEntry(void* data)
168 {
169 	return ((KernelDaemon*)data)->_DaemonThread();
170 }
171 
172 
173 struct daemon*
_NextDaemon(struct daemon & marker)174 KernelDaemon::_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 
195 status_t
_DaemonThread()196 KernelDaemon::_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 
241 bool
_IsDaemon() const242 KernelDaemon::_IsDaemon() const
243 {
244 	return find_thread(NULL) == fThread;
245 }
246 
247 
248 // #pragma mark -
249 
250 
251 static int
dump_daemons(int argc,char ** argv)252 dump_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 
267 extern "C" status_t
register_kernel_daemon(daemon_hook function,void * arg,int frequency)268 register_kernel_daemon(daemon_hook function, void* arg, int frequency)
269 {
270 	return sKernelDaemon.Register(function, arg, frequency);
271 }
272 
273 
274 extern "C" status_t
unregister_kernel_daemon(daemon_hook function,void * arg)275 unregister_kernel_daemon(daemon_hook function, void* arg)
276 {
277 	return sKernelDaemon.Unregister(function, arg);
278 }
279 
280 
281 extern "C" status_t
register_resource_resizer(daemon_hook function,void * arg,int frequency)282 register_resource_resizer(daemon_hook function, void* arg, int frequency)
283 {
284 	return sResourceResizer.Register(function, arg, frequency);
285 }
286 
287 
288 extern "C" status_t
unregister_resource_resizer(daemon_hook function,void * arg)289 unregister_resource_resizer(daemon_hook function, void* arg)
290 {
291 	return sResourceResizer.Unregister(function, arg);
292 }
293 
294 
295 //	#pragma mark -
296 
297 
298 extern "C" status_t
kernel_daemon_init(void)299 kernel_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