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 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 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 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 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 242 KernelDaemon::_IsDaemon() const 243 { 244 return find_thread(NULL) == fThread; 245 } 246 247 248 // #pragma mark - 249 250 251 static int 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 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 275 unregister_kernel_daemon(daemon_hook function, void* arg) 276 { 277 return sKernelDaemon.Unregister(function, arg); 278 } 279 280 281 extern "C" status_t 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 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 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