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