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 <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* imageName; 148 const char* symbol; 149 bool exactMatch; 150 151 status_t status = elf_debug_lookup_symbol_address( 152 (addr_t)daemon->function, NULL, &symbol, &imageName, &exactMatch); 153 if (status == B_OK && exactMatch) { 154 if (strchr(imageName, '/') != NULL) 155 imageName = strrchr(imageName, '/') + 1; 156 157 kprintf("\t%s:%s (%p), arg %p\n", imageName, symbol, 158 daemon->function, daemon->arg); 159 } else { 160 kprintf("\t%p, arg %p\n", daemon->function, daemon->arg); 161 } 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 int32 iteration = 0; 200 201 marker.arg = NULL; 202 203 while (true) { 204 RecursiveLocker locker(fLock); 205 206 // iterate through the list and execute each daemon if needed 207 while (struct daemon* daemon = _NextDaemon(marker)) { 208 if (((iteration + daemon->offset) % daemon->frequency) == 0) 209 daemon->function(daemon->arg, iteration); 210 } 211 212 locker.Unlock(); 213 214 iteration++; 215 snooze(100000); // 0.1 seconds 216 } 217 218 return B_OK; 219 } 220 221 222 // #pragma mark - 223 224 225 static int 226 dump_daemons(int argc, char** argv) 227 { 228 kprintf("kernel daemons:\n"); 229 sKernelDaemon.Dump(); 230 231 kprintf("\nresource resizers:\n"); 232 sResourceResizer.Dump(); 233 234 return 0; 235 } 236 237 238 // #pragma mark - 239 240 241 extern "C" status_t 242 register_kernel_daemon(daemon_hook function, void* arg, int frequency) 243 { 244 return sKernelDaemon.Register(function, arg, frequency); 245 } 246 247 248 extern "C" status_t 249 unregister_kernel_daemon(daemon_hook function, void* arg) 250 { 251 return sKernelDaemon.Unregister(function, arg); 252 } 253 254 255 extern "C" status_t 256 register_resource_resizer(daemon_hook function, void* arg, int frequency) 257 { 258 return sResourceResizer.Register(function, arg, frequency); 259 } 260 261 262 extern "C" status_t 263 unregister_resource_resizer(daemon_hook function, void* arg) 264 { 265 return sResourceResizer.Unregister(function, arg); 266 } 267 268 269 // #pragma mark - 270 271 272 extern "C" status_t 273 kernel_daemon_init(void) 274 { 275 new(&sKernelDaemon) KernelDaemon; 276 if (sKernelDaemon.Init("kernel daemon") != B_OK) 277 panic("kernel_daemon_init(): failed to init kernel daemon"); 278 279 new(&sResourceResizer) KernelDaemon; 280 if (sResourceResizer.Init("resource resizer") != B_OK) 281 panic("kernel_daemon_init(): failed to init resource resizer"); 282 283 add_debugger_command("daemons", dump_daemons, 284 "Shows registered kernel daemons."); 285 return B_OK; 286 } 287