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