libroot_debug: Merge guarded heap into libroot_debug.
[haiku.git] / src / system / kernel / low_resource_manager.cpp
blob55962db93d92e8646cfd4a5e5bc12b86f1769f2a
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 */
8 #include <low_resource_manager.h>
10 #include <new>
11 #include <stdio.h>
12 #include <stdlib.h>
14 #include <KernelExport.h>
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>
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
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;
42 typedef DoublyLinkedList<low_resource_handler> HandlerList;
45 static const bigtime_t kLowResourceInterval = 3000000; // 3 secs
46 static const bigtime_t kWarnResourceInterval = 500000; // 0.5 secs
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;
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;
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;
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;
74 static recursive_lock sLowResourceLock
75 = RECURSIVE_LOCK_INITIALIZER("low resource");
76 static sem_id sLowResourceWaitSem;
77 static HandlerList sLowResourceHandlers;
79 static ConditionVariable sLowResourceWaiterCondition;
82 static const char*
83 state_to_string(uint32 state)
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";
93 default:
94 return "normal";
99 static int32
100 low_resource_state_no_update(uint32 resources)
102 int32 state = B_NO_LOW_RESOURCE;
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);
113 return state;
117 /*! Calls low resource handlers for the given resources.
118 sLowResourceLock must be held.
120 static void
121 call_handlers(uint32 lowResources)
123 if (sLowResourceHandlers.IsEmpty())
124 return;
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);
131 while (low_resource_handler* handler
132 = sLowResourceHandlers.GetNext(&marker)) {
133 // swap with handler
134 sLowResourceHandlers.Swap(&marker, handler);
135 marker.priority = handler->priority;
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);
146 // remove marker
147 sLowResourceHandlers.Remove(&marker);
151 static void
152 compute_state(void)
154 sLastMeasurement = system_time();
156 sLowResources = B_ALL_KERNEL_RESOURCES;
158 // free pages state
159 uint32 freePages = vm_page_num_free_pages();
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;
173 if (sLowPagesState != oldState) {
174 dprintf("low resource pages: %s -> %s\n", state_to_string(oldState),
175 state_to_string(sLowPagesState));
178 // free memory state
179 off_t freeMemory = vm_available_not_needed_memory();
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;
193 if (sLowMemoryState != oldState) {
194 dprintf("low resource memory: %s -> %s\n", state_to_string(oldState),
195 state_to_string(sLowMemoryState));
198 // free semaphores state
199 uint32 maxSems = sem_max_sems();
200 uint32 freeSems = maxSems - sem_used_sems();
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;
214 if (sLowSemaphoresState != oldState) {
215 dprintf("low resource semaphores: %s -> %s\n",
216 state_to_string(oldState), state_to_string(sLowSemaphoresState));
219 // free kernel address space state
220 // TODO: this should take fragmentation into account
221 size_t freeSpace = vm_kernel_address_space_left();
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;
235 if (sLowSpaceState != oldState) {
236 dprintf("low resource address space: %s -> %s\n",
237 state_to_string(oldState), state_to_string(sLowSpaceState));
242 static status_t
243 low_resource_manager(void*)
245 bigtime_t timeout = kLowResourceInterval;
246 while (true) {
247 int32 state = low_resource_state_no_update(B_ALL_KERNEL_RESOURCES);
248 if (state != B_LOW_RESOURCE_CRITICAL) {
249 acquire_sem_etc(sLowResourceWaitSem, 1, B_RELATIVE_TIMEOUT,
250 timeout);
253 RecursiveLocker _(&sLowResourceLock);
255 compute_state();
256 state = low_resource_state_no_update(B_ALL_KERNEL_RESOURCES);
258 TRACE(("low_resource_manager: state = %ld, %ld free pages, %lld free "
259 "memory, %lu free semaphores\n", state, vm_page_num_free_pages(),
260 vm_available_not_needed_memory(),
261 sem_max_sems() - sem_used_sems()));
263 if (state < B_LOW_RESOURCE_NOTE)
264 continue;
266 call_handlers(sLowResources);
268 if (state == B_LOW_RESOURCE_WARNING)
269 timeout = kWarnResourceInterval;
270 else
271 timeout = kLowResourceInterval;
273 sLowResourceWaiterCondition.NotifyAll();
275 return 0;
279 static int
280 dump_handlers(int argc, char** argv)
282 kprintf("current state: %c%c%c%c\n",
283 (sLowResources & B_KERNEL_RESOURCE_PAGES) != 0 ? 'p' : '-',
284 (sLowResources & B_KERNEL_RESOURCE_MEMORY) != 0 ? 'm' : '-',
285 (sLowResources & B_KERNEL_RESOURCE_SEMAPHORES) != 0 ? 's' : '-',
286 (sLowResources & B_KERNEL_RESOURCE_ADDRESS_SPACE) != 0 ? 'a' : '-');
287 kprintf(" pages: %s (%" B_PRIu64 ")\n", state_to_string(sLowPagesState),
288 (uint64)vm_page_num_free_pages());
289 kprintf(" memory: %s (%" B_PRIdOFF ")\n", state_to_string(sLowMemoryState),
290 vm_available_not_needed_memory_debug());
291 kprintf(" sems: %s (%" B_PRIu32 ")\n",
292 state_to_string(sLowSemaphoresState), sem_max_sems() - sem_used_sems());
293 kprintf(" aspace: %s (%" B_PRIuSIZE ")\n\n",
294 state_to_string(sLowSpaceState), vm_kernel_address_space_left());
296 HandlerList::Iterator iterator = sLowResourceHandlers.GetIterator();
297 kprintf("function data resources prio function-name\n");
299 while (iterator.HasNext()) {
300 low_resource_handler *handler = iterator.Next();
302 const char* symbol = NULL;
303 elf_debug_lookup_symbol_address((addr_t)handler->function, NULL,
304 &symbol, NULL, NULL);
306 char resources[16];
307 snprintf(resources, sizeof(resources), "%c %c %c %c",
308 handler->resources & B_KERNEL_RESOURCE_PAGES ? 'p' : ' ',
309 handler->resources & B_KERNEL_RESOURCE_MEMORY ? 'm' : ' ',
310 handler->resources & B_KERNEL_RESOURCE_SEMAPHORES ? 's' : ' ',
311 handler->resources & B_KERNEL_RESOURCE_ADDRESS_SPACE ? 'a' : ' ');
313 kprintf("%p %p %s %4" B_PRId32 " %s\n", handler->function,
314 handler->data, resources, handler->priority, symbol);
317 return 0;
321 // #pragma mark - private kernel API
324 /*! Notifies the low resource manager that a resource is lacking. If \a flags
325 and \a timeout specify a timeout, the function will wait until the low
326 resource manager has finished its next iteration of calling low resource
327 handlers, or until the timeout occurs (whichever happens first).
329 void
330 low_resource(uint32 resource, uint64 requirements, uint32 flags, uint32 timeout)
332 // TODO: take requirements into account
334 switch (resource) {
335 case B_KERNEL_RESOURCE_PAGES:
336 case B_KERNEL_RESOURCE_MEMORY:
337 case B_KERNEL_RESOURCE_SEMAPHORES:
338 case B_KERNEL_RESOURCE_ADDRESS_SPACE:
339 break;
342 release_sem(sLowResourceWaitSem);
344 if ((flags & B_RELATIVE_TIMEOUT) == 0 || timeout > 0)
345 sLowResourceWaiterCondition.Wait(flags, timeout);
349 int32
350 low_resource_state(uint32 resources)
352 recursive_lock_lock(&sLowResourceLock);
354 if (system_time() - sLastMeasurement > 500000)
355 compute_state();
357 int32 state = low_resource_state_no_update(resources);
359 recursive_lock_unlock(&sLowResourceLock);
361 return state;
365 status_t
366 low_resource_manager_init(void)
368 new(&sLowResourceHandlers) HandlerList;
369 // static initializers do not work in the kernel,
370 // so we have to do it here, manually
372 sLowResourceWaiterCondition.Init(NULL, "low resource waiters");
374 // compute the free memory limits
375 off_t totalMemory = (off_t)vm_page_num_pages() * B_PAGE_SIZE;
376 sNoteMemoryLimit = totalMemory / 16;
377 if (sNoteMemoryLimit < kMinNoteMemoryLimit) {
378 sNoteMemoryLimit = kMinNoteMemoryLimit;
379 sWarnMemoryLimit = kMinWarnMemoryLimit;
380 sCriticalMemoryLimit = kMinCriticalMemoryLimit;
381 } else {
382 sWarnMemoryLimit = totalMemory / 64;
383 sCriticalMemoryLimit = totalMemory / 256;
386 return B_OK;
390 status_t
391 low_resource_manager_init_post_thread(void)
393 sLowResourceWaitSem = create_sem(0, "low resource wait");
394 if (sLowResourceWaitSem < B_OK)
395 return sLowResourceWaitSem;
397 thread_id thread = spawn_kernel_thread(&low_resource_manager,
398 "low resource manager", B_LOW_PRIORITY, NULL);
399 resume_thread(thread);
401 add_debugger_command("low_resource", &dump_handlers,
402 "Dump list of low resource handlers");
403 return B_OK;
407 status_t
408 unregister_low_resource_handler(low_resource_func function, void* data)
410 TRACE(("unregister_low_resource_handler(function = %p, data = %p)\n",
411 function, data));
413 RecursiveLocker locker(&sLowResourceLock);
414 HandlerList::Iterator iterator = sLowResourceHandlers.GetIterator();
416 while (iterator.HasNext()) {
417 low_resource_handler* handler = iterator.Next();
419 if (handler->function == function && handler->data == data) {
420 sLowResourceHandlers.Remove(handler);
421 free(handler);
422 return B_OK;
426 return B_ENTRY_NOT_FOUND;
430 /*! Registers a low resource handler. The higher the \a priority, the earlier
431 the handler will be called in low resource situations.
433 status_t
434 register_low_resource_handler(low_resource_func function, void* data,
435 uint32 resources, int32 priority)
437 TRACE(("register_low_resource_handler(function = %p, data = %p)\n",
438 function, data));
440 low_resource_handler *newHandler = (low_resource_handler*)malloc(
441 sizeof(low_resource_handler));
442 if (newHandler == NULL)
443 return B_NO_MEMORY;
445 newHandler->function = function;
446 newHandler->data = data;
447 newHandler->resources = resources;
448 newHandler->priority = priority;
450 RecursiveLocker locker(&sLowResourceLock);
452 // sort it in after priority (higher priority comes first)
454 HandlerList::ReverseIterator iterator
455 = sLowResourceHandlers.GetReverseIterator();
456 low_resource_handler* last = NULL;
457 while (iterator.HasNext()) {
458 low_resource_handler *handler = iterator.Next();
460 if (handler->priority >= priority) {
461 sLowResourceHandlers.Insert(last, newHandler);
462 return B_OK;
464 last = handler;
467 sLowResourceHandlers.Add(newHandler, false);
468 return B_OK;