1 /* 2 * Copyright 2003-2008, 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 *symbol, *imageName; 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), arg %p\n", imageName, symbol, 157 daemon->function, daemon->arg); 158 } else { 159 kprintf("\t%p, arg %p\n", daemon->function, daemon->arg); 160 } 161 } 162 } 163 164 165 /*static*/ status_t 166 KernelDaemon::_DaemonThreadEntry(void* data) 167 { 168 return ((KernelDaemon*)data)->_DaemonThread(); 169 } 170 171 172 struct daemon* 173 KernelDaemon::_NextDaemon(struct daemon& marker) 174 { 175 struct daemon* daemon; 176 177 if (marker.GetDoublyLinkedListLink()->next == NULL 178 && marker.GetDoublyLinkedListLink()->previous == NULL 179 && fDaemons.Head() != &marker) { 180 // Marker is not part of the list yet, just return the first entry 181 daemon = fDaemons.Head(); 182 } else { 183 daemon = marker.GetDoublyLinkedListLink()->next; 184 fDaemons.Remove(&marker); 185 } 186 187 if (daemon != NULL) 188 fDaemons.Insert(daemon->GetDoublyLinkedListLink()->next, &marker); 189 190 return daemon; 191 } 192 193 194 status_t 195 KernelDaemon::_DaemonThread() 196 { 197 struct daemon marker; 198 int32 iteration = 0; 199 200 while (true) { 201 RecursiveLocker locker(fLock); 202 203 // iterate through the list and execute each daemon if needed 204 while (struct daemon* daemon = _NextDaemon(marker)) { 205 if (((iteration + daemon->offset) % daemon->frequency) == 0) 206 daemon->function(daemon->arg, iteration); 207 } 208 209 locker.Unlock(); 210 211 iteration++; 212 snooze(100000); // 0.1 seconds 213 } 214 215 return B_OK; 216 } 217 218 219 // #pragma mark - 220 221 222 static int 223 dump_daemons(int argc, char** argv) 224 { 225 kprintf("kernel daemons:\n"); 226 sKernelDaemon.Dump(); 227 228 kprintf("\nresource resizers:\n"); 229 sResourceResizer.Dump(); 230 231 return 0; 232 } 233 234 235 // #pragma mark - 236 237 238 extern "C" status_t 239 register_kernel_daemon(daemon_hook function, void* arg, int frequency) 240 { 241 return sKernelDaemon.Register(function, arg, frequency); 242 } 243 244 245 extern "C" status_t 246 unregister_kernel_daemon(daemon_hook function, void* arg) 247 { 248 return sKernelDaemon.Unregister(function, arg); 249 } 250 251 252 extern "C" status_t 253 register_resource_resizer(daemon_hook function, void* arg, int frequency) 254 { 255 return sResourceResizer.Register(function, arg, frequency); 256 } 257 258 259 extern "C" status_t 260 unregister_resource_resizer(daemon_hook function, void* arg) 261 { 262 return sResourceResizer.Unregister(function, arg); 263 } 264 265 266 // #pragma mark - 267 268 269 extern "C" status_t 270 kernel_daemon_init(void) 271 { 272 new(&sKernelDaemon) KernelDaemon; 273 if (sKernelDaemon.Init("kernel daemon") != B_OK) 274 panic("kernel_daemon_init(): failed to init kernel daemon"); 275 276 new(&sResourceResizer) KernelDaemon; 277 if (sResourceResizer.Init("resource resizer") != B_OK) 278 panic("kernel_daemon_init(): failed to init resource resizer"); 279 280 add_debugger_command("daemons", dump_daemons, "Shows registered kernel daemons."); 281 return B_OK; 282 } 283