xref: /haiku/src/system/kernel/kernel_daemon.cpp (revision b289aaf66bbf6e173aa90fa194fc256965f1b34d)
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 <signal.h>
11 #include <stdlib.h>
12 
13 #include <KernelExport.h>
14 
15 #include <elf.h>
16 #include <lock.h>
17 #include <util/AutoLock.h>
18 #include <util/DoublyLinkedList.h>
19 
20 
21 // The use of snooze() in the kernel_daemon() function is very inaccurate, of
22 // course - the time the daemons need to execute add up in each iteration.
23 // But since the kernel daemon is documented to be very inaccurate, this
24 // actually might be okay (and that's why it's implemented this way now :-).
25 // BeOS R5 seems to do it in the same way, anyway.
26 
27 struct daemon : DoublyLinkedListLinkImpl<struct daemon> {
28 	daemon_hook	function;
29 	void*		arg;
30 	int32		frequency;
31 	int32		offset;
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 
53 private:
54 			recursive_lock		fLock;
55 			DaemonList			fDaemons;
56 			thread_id			fThread;
57 };
58 
59 
60 static KernelDaemon sKernelDaemon;
61 static KernelDaemon sResourceResizer;
62 
63 
64 status_t
65 KernelDaemon::Init(const char* name)
66 {
67 	recursive_lock_init(&fLock, name);
68 
69 	fThread = spawn_kernel_thread(&_DaemonThreadEntry, name, B_LOW_PRIORITY,
70 		this);
71 	if (fThread < 0)
72 		return fThread;
73 
74 	send_signal_etc(fThread, SIGCONT, B_DO_NOT_RESCHEDULE);
75 
76 	return B_OK;
77 }
78 
79 
80 status_t
81 KernelDaemon::Register(daemon_hook function, void* arg, int frequency)
82 {
83 	if (function == NULL || frequency < 1)
84 		return B_BAD_VALUE;
85 
86 	struct ::daemon* daemon = new(std::nothrow) (struct ::daemon);
87 	if (daemon == NULL)
88 		return B_NO_MEMORY;
89 
90 	daemon->function = function;
91 	daemon->arg = arg;
92 	daemon->frequency = frequency;
93 
94 	RecursiveLocker _(fLock);
95 
96 	if (frequency > 1) {
97 		// we try to balance the work-load for each daemon run
98 		// (beware, it's a very simple algorithm, yet effective)
99 
100 		DaemonList::Iterator iterator = fDaemons.GetIterator();
101 		int32 num = 0;
102 
103 		while (iterator.HasNext()) {
104 			if (iterator.Next()->frequency == frequency)
105 				num++;
106 		}
107 
108 		daemon->offset = num % frequency;
109 	} else
110 		daemon->offset = 0;
111 
112 	fDaemons.Add(daemon);
113 	return B_OK;
114 }
115 
116 
117 status_t
118 KernelDaemon::Unregister(daemon_hook function, void* arg)
119 {
120 	RecursiveLocker _(fLock);
121 
122 	DaemonList::Iterator iterator = fDaemons.GetIterator();
123 
124 	// search for the daemon and remove it from the list
125 	while (iterator.HasNext()) {
126 		struct daemon* daemon = iterator.Next();
127 
128 		if (daemon->function == function && daemon->arg == arg) {
129 			// found it!
130 			iterator.Remove();
131 			delete daemon;
132 			return B_OK;
133 		}
134 	}
135 
136 	return B_ENTRY_NOT_FOUND;
137 }
138 
139 
140 void
141 KernelDaemon::Dump()
142 {
143 	DaemonList::Iterator iterator = fDaemons.GetIterator();
144 
145 	while (iterator.HasNext()) {
146 		struct daemon* daemon = iterator.Next();
147 		const char* imageName;
148 		const char* symbol;
149 		bool exactMatch;
150 
151 		status_t status = elf_debug_lookup_symbol_address(
152 			(addr_t)daemon->function, NULL, &symbol, &imageName, &exactMatch);
153 		if (status == B_OK && exactMatch) {
154 			if (strchr(imageName, '/') != NULL)
155 				imageName = strrchr(imageName, '/') + 1;
156 
157 			kprintf("\t%s:%s (%p), arg %p\n", imageName, symbol,
158 				daemon->function, daemon->arg);
159 		} else {
160 			kprintf("\t%p, arg %p\n", daemon->function, daemon->arg);
161 		}
162 	}
163 }
164 
165 
166 /*static*/ status_t
167 KernelDaemon::_DaemonThreadEntry(void* data)
168 {
169 	return ((KernelDaemon*)data)->_DaemonThread();
170 }
171 
172 
173 struct daemon*
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
196 KernelDaemon::_DaemonThread()
197 {
198 	struct daemon marker;
199 	int32 iteration = 0;
200 
201 	marker.arg = NULL;
202 
203 	while (true) {
204 		RecursiveLocker locker(fLock);
205 
206 		// iterate through the list and execute each daemon if needed
207 		while (struct daemon* daemon = _NextDaemon(marker)) {
208 			if (((iteration + daemon->offset) % daemon->frequency) == 0)
209 				daemon->function(daemon->arg, iteration);
210 		}
211 
212 		locker.Unlock();
213 
214 		iteration++;
215 		snooze(100000);	// 0.1 seconds
216 	}
217 
218 	return B_OK;
219 }
220 
221 
222 //	#pragma mark -
223 
224 
225 static int
226 dump_daemons(int argc, char** argv)
227 {
228 	kprintf("kernel daemons:\n");
229 	sKernelDaemon.Dump();
230 
231 	kprintf("\nresource resizers:\n");
232 	sResourceResizer.Dump();
233 
234 	return 0;
235 }
236 
237 
238 //	#pragma mark -
239 
240 
241 extern "C" status_t
242 register_kernel_daemon(daemon_hook function, void* arg, int frequency)
243 {
244 	return sKernelDaemon.Register(function, arg, frequency);
245 }
246 
247 
248 extern "C" status_t
249 unregister_kernel_daemon(daemon_hook function, void* arg)
250 {
251 	return sKernelDaemon.Unregister(function, arg);
252 }
253 
254 
255 extern "C" status_t
256 register_resource_resizer(daemon_hook function, void* arg, int frequency)
257 {
258 	return sResourceResizer.Register(function, arg, frequency);
259 }
260 
261 
262 extern "C" status_t
263 unregister_resource_resizer(daemon_hook function, void* arg)
264 {
265 	return sResourceResizer.Unregister(function, arg);
266 }
267 
268 
269 //	#pragma mark -
270 
271 
272 extern "C" status_t
273 kernel_daemon_init(void)
274 {
275 	new(&sKernelDaemon) KernelDaemon;
276 	if (sKernelDaemon.Init("kernel daemon") != B_OK)
277 		panic("kernel_daemon_init(): failed to init kernel daemon");
278 
279 	new(&sResourceResizer) KernelDaemon;
280 	if (sResourceResizer.Init("resource resizer") != B_OK)
281 		panic("kernel_daemon_init(): failed to init resource resizer");
282 
283 	add_debugger_command("daemons", dump_daemons,
284 		"Shows registered kernel daemons.");
285 	return B_OK;
286 }
287