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