xref: /haiku/src/system/kernel/kernel_daemon.cpp (revision b671e9bbdbd10268a042b4f4cc4317ccd03d105e)
1 /*
2  * Copyright 2003-2008, 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 *symbol, *imageName;
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), arg %p\n", imageName, symbol,
157 				daemon->function, daemon->arg);
158 		} else {
159 			kprintf("\t%p, arg %p\n", daemon->function, daemon->arg);
160 		}
161 	}
162 }
163 
164 
165 /*static*/ status_t
166 KernelDaemon::_DaemonThreadEntry(void* data)
167 {
168 	return ((KernelDaemon*)data)->_DaemonThread();
169 }
170 
171 
172 struct daemon*
173 KernelDaemon::_NextDaemon(struct daemon& marker)
174 {
175 	struct daemon* daemon;
176 
177 	if (marker.GetDoublyLinkedListLink()->next == NULL
178 		&& marker.GetDoublyLinkedListLink()->previous == NULL
179 		&& fDaemons.Head() != &marker) {
180 		// Marker is not part of the list yet, just return the first entry
181 		daemon = fDaemons.Head();
182 	} else {
183 		daemon = marker.GetDoublyLinkedListLink()->next;
184 		fDaemons.Remove(&marker);
185 	}
186 
187 	if (daemon != NULL)
188 		fDaemons.Insert(daemon->GetDoublyLinkedListLink()->next, &marker);
189 
190 	return daemon;
191 }
192 
193 
194 status_t
195 KernelDaemon::_DaemonThread()
196 {
197 	struct daemon marker;
198 	int32 iteration = 0;
199 
200 	while (true) {
201 		RecursiveLocker locker(fLock);
202 
203 		// iterate through the list and execute each daemon if needed
204 		while (struct daemon* daemon = _NextDaemon(marker)) {
205 			if (((iteration + daemon->offset) % daemon->frequency) == 0)
206 				daemon->function(daemon->arg, iteration);
207 		}
208 
209 		locker.Unlock();
210 
211 		iteration++;
212 		snooze(100000);	// 0.1 seconds
213 	}
214 
215 	return B_OK;
216 }
217 
218 
219 //	#pragma mark -
220 
221 
222 static int
223 dump_daemons(int argc, char** argv)
224 {
225 	kprintf("kernel daemons:\n");
226 	sKernelDaemon.Dump();
227 
228 	kprintf("\nresource resizers:\n");
229 	sResourceResizer.Dump();
230 
231 	return 0;
232 }
233 
234 
235 //	#pragma mark -
236 
237 
238 extern "C" status_t
239 register_kernel_daemon(daemon_hook function, void* arg, int frequency)
240 {
241 	return sKernelDaemon.Register(function, arg, frequency);
242 }
243 
244 
245 extern "C" status_t
246 unregister_kernel_daemon(daemon_hook function, void* arg)
247 {
248 	return sKernelDaemon.Unregister(function, arg);
249 }
250 
251 
252 extern "C" status_t
253 register_resource_resizer(daemon_hook function, void* arg, int frequency)
254 {
255 	return sResourceResizer.Register(function, arg, frequency);
256 }
257 
258 
259 extern "C" status_t
260 unregister_resource_resizer(daemon_hook function, void* arg)
261 {
262 	return sResourceResizer.Unregister(function, arg);
263 }
264 
265 
266 //	#pragma mark -
267 
268 
269 extern "C" status_t
270 kernel_daemon_init(void)
271 {
272 	new(&sKernelDaemon) KernelDaemon;
273 	if (sKernelDaemon.Init("kernel daemon") != B_OK)
274 		panic("kernel_daemon_init(): failed to init kernel daemon");
275 
276 	new(&sResourceResizer) KernelDaemon;
277 	if (sResourceResizer.Init("resource resizer") != B_OK)
278 		panic("kernel_daemon_init(): failed to init resource resizer");
279 
280 	add_debugger_command("daemons", dump_daemons, "Shows registered kernel daemons.");
281 	return B_OK;
282 }
283