1 /* 2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2005-2009, Axel Dörfler, axeld@pinc-software.de. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 #include <low_resource_manager.h> 9 10 #include <new> 11 #include <signal.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 15 #include <KernelExport.h> 16 17 #include <condition_variable.h> 18 #include <elf.h> 19 #include <lock.h> 20 #include <sem.h> 21 #include <util/AutoLock.h> 22 #include <util/DoublyLinkedList.h> 23 #include <vm/vm_page.h> 24 #include <vm/vm_priv.h> 25 26 27 //#define TRACE_LOW_RESOURCE_MANAGER 28 #ifdef TRACE_LOW_RESOURCE_MANAGER 29 # define TRACE(x) dprintf x 30 #else 31 # define TRACE(x) ; 32 #endif 33 34 35 struct low_resource_handler 36 : public DoublyLinkedListLinkImpl<low_resource_handler> { 37 low_resource_func function; 38 void* data; 39 uint32 resources; 40 int32 priority; 41 }; 42 43 typedef DoublyLinkedList<low_resource_handler> HandlerList; 44 45 46 static const bigtime_t kLowResourceInterval = 3000000; // 3 secs 47 static const bigtime_t kWarnResourceInterval = 500000; // 0.5 secs 48 49 // page limits 50 static const size_t kNotePagesLimit = VM_PAGE_RESERVE_USER * 4; 51 static const size_t kWarnPagesLimit = VM_PAGE_RESERVE_USER; 52 static const size_t kCriticalPagesLimit = VM_PAGE_RESERVE_SYSTEM; 53 54 // memory limits 55 static const off_t kMinNoteMemoryLimit = VM_MEMORY_RESERVE_USER * 4; 56 static const off_t kMinWarnMemoryLimit = VM_MEMORY_RESERVE_USER; 57 static const off_t kMinCriticalMemoryLimit = VM_MEMORY_RESERVE_SYSTEM; 58 static off_t sNoteMemoryLimit; 59 static off_t sWarnMemoryLimit; 60 static off_t sCriticalMemoryLimit; 61 62 63 static int32 sLowPagesState = B_NO_LOW_RESOURCE; 64 static int32 sLowMemoryState = B_NO_LOW_RESOURCE; 65 static int32 sLowSemaphoresState = B_NO_LOW_RESOURCE; 66 static int32 sLowSpaceState = B_NO_LOW_RESOURCE; 67 static uint32 sLowResources = 0; // resources that are not B_NO_LOW_RESOURCE 68 static bigtime_t sLastMeasurement; 69 70 static recursive_lock sLowResourceLock 71 = RECURSIVE_LOCK_INITIALIZER("low resource"); 72 static sem_id sLowResourceWaitSem; 73 static HandlerList sLowResourceHandlers; 74 75 static ConditionVariable sLowResourceWaiterCondition; 76 77 78 static int32 79 low_resource_state_no_update(uint32 resources) 80 { 81 int32 state = B_NO_LOW_RESOURCE; 82 83 if ((resources & B_KERNEL_RESOURCE_PAGES) != 0) 84 state = max_c(state, sLowPagesState); 85 if ((resources & B_KERNEL_RESOURCE_MEMORY) != 0) 86 state = max_c(state, sLowMemoryState); 87 if ((resources & B_KERNEL_RESOURCE_SEMAPHORES) != 0) 88 state = max_c(state, sLowSemaphoresState); 89 if ((resources & B_KERNEL_RESOURCE_ADDRESS_SPACE) != 0) 90 state = max_c(state, sLowSpaceState); 91 92 return state; 93 } 94 95 96 /*! Calls low resource handlers for the given resources. 97 sLowResourceLock must be held. 98 */ 99 static void 100 call_handlers(uint32 lowResources) 101 { 102 if (sLowResourceHandlers.IsEmpty()) 103 return; 104 105 // Add a marker, so we can drop the lock while calling the handlers and 106 // still iterate safely. 107 low_resource_handler marker; 108 sLowResourceHandlers.Insert(&marker, false); 109 110 while (low_resource_handler* handler 111 = sLowResourceHandlers.GetNext(&marker)) { 112 // swap with handler 113 sLowResourceHandlers.Swap(&marker, handler); 114 marker.priority = handler->priority; 115 116 int32 resources = handler->resources & lowResources; 117 if (resources != 0) { 118 recursive_lock_unlock(&sLowResourceLock); 119 handler->function(handler->data, resources, 120 low_resource_state_no_update(resources)); 121 recursive_lock_lock(&sLowResourceLock); 122 } 123 } 124 125 // remove marker 126 sLowResourceHandlers.Remove(&marker); 127 } 128 129 130 static void 131 compute_state(void) 132 { 133 sLastMeasurement = system_time(); 134 135 sLowResources = B_ALL_KERNEL_RESOURCES; 136 137 // free pages state 138 uint32 freePages = vm_page_num_free_pages(); 139 140 if (freePages < kCriticalPagesLimit) { 141 sLowPagesState = B_LOW_RESOURCE_CRITICAL; 142 } else if (freePages < kWarnPagesLimit) { 143 sLowPagesState = B_LOW_RESOURCE_WARNING; 144 } else if (freePages < kNotePagesLimit) { 145 sLowPagesState = B_LOW_RESOURCE_NOTE; 146 } else { 147 sLowPagesState = B_NO_LOW_RESOURCE; 148 sLowResources &= ~B_KERNEL_RESOURCE_PAGES; 149 } 150 151 // free memory state 152 off_t freeMemory = vm_available_not_needed_memory(); 153 154 if (freeMemory < sCriticalMemoryLimit) { 155 sLowMemoryState = B_LOW_RESOURCE_CRITICAL; 156 } else if (freeMemory < sWarnMemoryLimit) { 157 sLowMemoryState = B_LOW_RESOURCE_WARNING; 158 } else if (freeMemory < sNoteMemoryLimit) { 159 sLowMemoryState = B_LOW_RESOURCE_NOTE; 160 } else { 161 sLowMemoryState = B_NO_LOW_RESOURCE; 162 sLowResources &= ~B_KERNEL_RESOURCE_MEMORY; 163 } 164 165 // free semaphores state 166 uint32 maxSems = sem_max_sems(); 167 uint32 freeSems = maxSems - sem_used_sems(); 168 169 if (freeSems < maxSems >> 16) { 170 sLowSemaphoresState = B_LOW_RESOURCE_CRITICAL; 171 } else if (freeSems < maxSems >> 8) { 172 sLowSemaphoresState = B_LOW_RESOURCE_WARNING; 173 } else if (freeSems < maxSems >> 4) { 174 sLowSemaphoresState = B_LOW_RESOURCE_NOTE; 175 } else { 176 sLowSemaphoresState = B_NO_LOW_RESOURCE; 177 sLowResources &= ~B_KERNEL_RESOURCE_SEMAPHORES; 178 } 179 180 // free kernel address space state 181 // TODO: this should take fragmentation into account 182 size_t maxSpace = KERNEL_SIZE; 183 size_t freeSpace = vm_kernel_address_space_left(); 184 185 if (freeSpace < maxSpace >> 16) 186 sLowSpaceState = B_LOW_RESOURCE_CRITICAL; 187 if (freeSpace < maxSpace >> 8) 188 sLowSpaceState = B_LOW_RESOURCE_WARNING; 189 if (freeSpace < maxSpace >> 4) 190 sLowSpaceState = B_LOW_RESOURCE_NOTE; 191 else { 192 sLowSpaceState = B_NO_LOW_RESOURCE; 193 sLowResources &= ~B_KERNEL_RESOURCE_ADDRESS_SPACE; 194 } 195 } 196 197 198 static status_t 199 low_resource_manager(void*) 200 { 201 bigtime_t timeout = kLowResourceInterval; 202 while (true) { 203 int32 state = low_resource_state_no_update(B_ALL_KERNEL_RESOURCES); 204 if (state != B_LOW_RESOURCE_CRITICAL) { 205 acquire_sem_etc(sLowResourceWaitSem, 1, B_RELATIVE_TIMEOUT, 206 timeout); 207 } 208 209 RecursiveLocker _(&sLowResourceLock); 210 211 compute_state(); 212 state = low_resource_state_no_update(B_ALL_KERNEL_RESOURCES); 213 214 TRACE(("low_resource_manager: state = %ld, %ld free pages, %lld free " 215 "memory, %lu free semaphores\n", state, vm_page_num_free_pages(), 216 vm_available_not_needed_memory(), 217 sem_max_sems() - sem_used_sems())); 218 219 if (state < B_LOW_RESOURCE_NOTE) 220 continue; 221 222 call_handlers(sLowResources); 223 224 if (state == B_LOW_RESOURCE_WARNING) 225 timeout = kWarnResourceInterval; 226 else 227 timeout = kLowResourceInterval; 228 229 sLowResourceWaiterCondition.NotifyAll(); 230 } 231 return 0; 232 } 233 234 235 static const char* 236 state_to_string(uint32 state) 237 { 238 switch (state) { 239 case B_LOW_RESOURCE_CRITICAL: 240 return "critical"; 241 case B_LOW_RESOURCE_WARNING: 242 return "warning"; 243 case B_LOW_RESOURCE_NOTE: 244 return "note"; 245 246 default: 247 return "normal"; 248 } 249 } 250 251 252 static int 253 dump_handlers(int argc, char** argv) 254 { 255 kprintf("current state: %c%c%c%c\n", 256 (sLowResources & B_KERNEL_RESOURCE_PAGES) != 0 ? 'p' : '-', 257 (sLowResources & B_KERNEL_RESOURCE_MEMORY) != 0 ? 'm' : '-', 258 (sLowResources & B_KERNEL_RESOURCE_SEMAPHORES) != 0 ? 's' : '-', 259 (sLowResources & B_KERNEL_RESOURCE_ADDRESS_SPACE) != 0 ? 'a' : '-'); 260 kprintf(" pages: %s\n", state_to_string(sLowPagesState)); 261 kprintf(" memory: %s\n", state_to_string(sLowMemoryState)); 262 kprintf(" sems: %s\n", state_to_string(sLowSemaphoresState)); 263 kprintf(" aspace: %s\n\n", state_to_string(sLowSpaceState)); 264 265 HandlerList::Iterator iterator = sLowResourceHandlers.GetIterator(); 266 kprintf("function data resources prio function-name\n"); 267 268 while (iterator.HasNext()) { 269 low_resource_handler *handler = iterator.Next(); 270 271 const char* symbol = NULL; 272 elf_debug_lookup_symbol_address((addr_t)handler->function, NULL, 273 &symbol, NULL, NULL); 274 275 char resources[16]; 276 snprintf(resources, sizeof(resources), "%c %c %c", 277 handler->resources & B_KERNEL_RESOURCE_PAGES ? 'p' : ' ', 278 handler->resources & B_KERNEL_RESOURCE_MEMORY ? 'm' : ' ', 279 handler->resources & B_KERNEL_RESOURCE_SEMAPHORES ? 's' : ' '); 280 281 kprintf("%p %p %s %4ld %s\n", handler->function, handler->data, 282 resources, handler->priority, symbol); 283 } 284 285 return 0; 286 } 287 288 289 // #pragma mark - private kernel API 290 291 292 /*! Notifies the low resource manager that a resource is lacking. If \a flags 293 and \a timeout specify a timeout, the function will wait until the low 294 resource manager has finished its next iteration of calling low resource 295 handlers, or until the timeout occurs (whichever happens first). 296 */ 297 void 298 low_resource(uint32 resource, uint64 requirements, uint32 flags, uint32 timeout) 299 { 300 // TODO: take requirements into account 301 302 switch (resource) { 303 case B_KERNEL_RESOURCE_PAGES: 304 case B_KERNEL_RESOURCE_MEMORY: 305 case B_KERNEL_RESOURCE_SEMAPHORES: 306 case B_KERNEL_RESOURCE_ADDRESS_SPACE: 307 break; 308 } 309 310 release_sem(sLowResourceWaitSem); 311 312 if ((flags & B_RELATIVE_TIMEOUT) == 0 || timeout > 0) 313 sLowResourceWaiterCondition.Wait(flags, timeout); 314 } 315 316 317 int32 318 low_resource_state(uint32 resources) 319 { 320 recursive_lock_lock(&sLowResourceLock); 321 322 if (system_time() - sLastMeasurement > 500000) 323 compute_state(); 324 325 int32 state = low_resource_state_no_update(resources); 326 327 recursive_lock_unlock(&sLowResourceLock); 328 329 return state; 330 } 331 332 333 status_t 334 low_resource_manager_init(void) 335 { 336 new(&sLowResourceHandlers) HandlerList; 337 // static initializers do not work in the kernel, 338 // so we have to do it here, manually 339 340 sLowResourceWaiterCondition.Init(NULL, "low resource waiters"); 341 342 // compute the free memory limits 343 off_t totalMemory = (off_t)vm_page_num_pages() * B_PAGE_SIZE; 344 sNoteMemoryLimit = totalMemory / 16; 345 if (sNoteMemoryLimit < kMinNoteMemoryLimit) { 346 sNoteMemoryLimit = kMinNoteMemoryLimit; 347 sWarnMemoryLimit = kMinWarnMemoryLimit; 348 sCriticalMemoryLimit = kMinCriticalMemoryLimit; 349 } else { 350 sWarnMemoryLimit = totalMemory / 64; 351 sCriticalMemoryLimit = totalMemory / 256; 352 } 353 354 return B_OK; 355 } 356 357 358 status_t 359 low_resource_manager_init_post_thread(void) 360 { 361 sLowResourceWaitSem = create_sem(0, "low resource wait"); 362 if (sLowResourceWaitSem < B_OK) 363 return sLowResourceWaitSem; 364 365 thread_id thread = spawn_kernel_thread(&low_resource_manager, 366 "low resource manager", B_LOW_PRIORITY, NULL); 367 send_signal_etc(thread, SIGCONT, B_DO_NOT_RESCHEDULE); 368 369 add_debugger_command("low_resource", &dump_handlers, 370 "Dump list of low resource handlers"); 371 return B_OK; 372 } 373 374 375 status_t 376 unregister_low_resource_handler(low_resource_func function, void* data) 377 { 378 TRACE(("unregister_low_resource_handler(function = %p, data = %p)\n", 379 function, data)); 380 381 RecursiveLocker locker(&sLowResourceLock); 382 HandlerList::Iterator iterator = sLowResourceHandlers.GetIterator(); 383 384 while (iterator.HasNext()) { 385 low_resource_handler* handler = iterator.Next(); 386 387 if (handler->function == function && handler->data == data) { 388 sLowResourceHandlers.Remove(handler); 389 free(handler); 390 return B_OK; 391 } 392 } 393 394 return B_ENTRY_NOT_FOUND; 395 } 396 397 398 /*! Registers a low resource handler. The higher the \a priority, the earlier 399 the handler will be called in low resource situations. 400 */ 401 status_t 402 register_low_resource_handler(low_resource_func function, void* data, 403 uint32 resources, int32 priority) 404 { 405 TRACE(("register_low_resource_handler(function = %p, data = %p)\n", 406 function, data)); 407 408 low_resource_handler *newHandler = (low_resource_handler*)malloc( 409 sizeof(low_resource_handler)); 410 if (newHandler == NULL) 411 return B_NO_MEMORY; 412 413 newHandler->function = function; 414 newHandler->data = data; 415 newHandler->resources = resources; 416 newHandler->priority = priority; 417 418 RecursiveLocker locker(&sLowResourceLock); 419 420 // sort it in after priority (higher priority comes first) 421 422 HandlerList::ReverseIterator iterator 423 = sLowResourceHandlers.GetReverseIterator(); 424 low_resource_handler* last = NULL; 425 while (iterator.HasNext()) { 426 low_resource_handler *handler = iterator.Next(); 427 428 if (handler->priority >= priority) { 429 sLowResourceHandlers.Insert(last, newHandler); 430 return B_OK; 431 } 432 last = handler; 433 } 434 435 sLowResourceHandlers.Add(newHandler, false); 436 return B_OK; 437 } 438