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 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 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 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 126 ConditionVariableEntry entry; 127 fUnregisterCondition.Add(&entry); 128 129 locker.Unlock(); 130 131 entry.Wait(); 132 133 locker.Lock(); 134 } 135 } 136 137 iterator.Remove(); 138 delete daemon; 139 return B_OK; 140 } 141 } 142 143 return B_ENTRY_NOT_FOUND; 144 } 145 146 147 void 148 KernelDaemon::Dump() 149 { 150 DaemonList::Iterator iterator = fDaemons.GetIterator(); 151 152 while (iterator.HasNext()) { 153 struct daemon* daemon = iterator.Next(); 154 const char* imageName; 155 const char* symbol; 156 bool exactMatch; 157 158 status_t status = elf_debug_lookup_symbol_address( 159 (addr_t)daemon->function, NULL, &symbol, &imageName, &exactMatch); 160 if (status == B_OK && exactMatch) { 161 if (strchr(imageName, '/') != NULL) 162 imageName = strrchr(imageName, '/') + 1; 163 164 kprintf("\t%s:%s (%p)", imageName, symbol, daemon->function); 165 } else 166 kprintf("\t%p", daemon->function); 167 168 kprintf(", arg %p%s\n", daemon->arg, 169 daemon->executing ? " (running) " : ""); 170 } 171 } 172 173 174 /*static*/ status_t 175 KernelDaemon::_DaemonThreadEntry(void* data) 176 { 177 return ((KernelDaemon*)data)->_DaemonThread(); 178 } 179 180 181 struct daemon* 182 KernelDaemon::_NextDaemon(struct daemon& marker) 183 { 184 struct daemon* daemon; 185 186 if (marker.arg == NULL) { 187 // The marker is not part of the list yet, just return the first entry 188 daemon = fDaemons.Head(); 189 } else { 190 daemon = fDaemons.GetNext(&marker); 191 fDaemons.Remove(&marker); 192 } 193 194 marker.arg = daemon; 195 196 if (daemon != NULL) 197 fDaemons.InsertAfter(daemon, &marker); 198 199 return daemon; 200 } 201 202 203 status_t 204 KernelDaemon::_DaemonThread() 205 { 206 struct daemon marker; 207 const bigtime_t start = system_time(), iterationToUsecs = 100 * 1000; 208 209 marker.arg = NULL; 210 211 while (true) { 212 RecursiveLocker locker(fLock); 213 214 bigtime_t timeout = INT64_MAX; 215 216 // iterate through the list and execute each daemon if needed 217 while (struct daemon* daemon = _NextDaemon(marker)) { 218 daemon->executing = true; 219 locker.Unlock(); 220 221 const bigtime_t time = system_time(); 222 bigtime_t next = (daemon->last + 223 (daemon->frequency * iterationToUsecs)) - time; 224 if (next <= 0) { 225 daemon->last = time; 226 next = daemon->frequency * iterationToUsecs; 227 daemon->function(daemon->arg, (time - start) / iterationToUsecs); 228 } 229 timeout = min_c(timeout, next); 230 231 locker.Lock(); 232 daemon->executing = false; 233 } 234 235 if (fUnregisterWaiters != 0) { 236 fUnregisterCondition.NotifyAll(); 237 fUnregisterWaiters = 0; 238 } 239 240 locker.Unlock(); 241 242 acquire_sem_etc(fDaemonAddedSem, 1, B_RELATIVE_TIMEOUT, timeout); 243 } 244 245 return B_OK; 246 } 247 248 249 bool 250 KernelDaemon::_IsDaemon() const 251 { 252 return find_thread(NULL) == fThread; 253 } 254 255 256 // #pragma mark - 257 258 259 static int 260 dump_daemons(int argc, char** argv) 261 { 262 kprintf("kernel daemons:\n"); 263 sKernelDaemon.Dump(); 264 265 kprintf("\nresource resizers:\n"); 266 sResourceResizer.Dump(); 267 268 return 0; 269 } 270 271 272 // #pragma mark - 273 274 275 extern "C" status_t 276 register_kernel_daemon(daemon_hook function, void* arg, int frequency) 277 { 278 return sKernelDaemon.Register(function, arg, frequency); 279 } 280 281 282 extern "C" status_t 283 unregister_kernel_daemon(daemon_hook function, void* arg) 284 { 285 return sKernelDaemon.Unregister(function, arg); 286 } 287 288 289 extern "C" status_t 290 register_resource_resizer(daemon_hook function, void* arg, int frequency) 291 { 292 return sResourceResizer.Register(function, arg, frequency); 293 } 294 295 296 extern "C" status_t 297 unregister_resource_resizer(daemon_hook function, void* arg) 298 { 299 return sResourceResizer.Unregister(function, arg); 300 } 301 302 303 // #pragma mark - 304 305 306 extern "C" status_t 307 kernel_daemon_init(void) 308 { 309 new(&sKernelDaemon) KernelDaemon; 310 if (sKernelDaemon.Init("kernel daemon") != B_OK) 311 panic("kernel_daemon_init(): failed to init kernel daemon"); 312 313 new(&sResourceResizer) KernelDaemon; 314 if (sResourceResizer.Init("resource resizer") != B_OK) 315 panic("kernel_daemon_init(): failed to init resource resizer"); 316 317 add_debugger_command("daemons", dump_daemons, 318 "Shows registered kernel daemons."); 319 return B_OK; 320 } 321