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