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