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.
8 #include <low_resource_manager.h>
14 #include <KernelExport.h>
16 #include <condition_variable.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
34 struct low_resource_handler
35 : public DoublyLinkedListLinkImpl
<low_resource_handler
> {
36 low_resource_func function
;
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
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
;
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
;
83 state_to_string(uint32 state
)
86 case B_LOW_RESOURCE_CRITICAL
:
88 case B_LOW_RESOURCE_WARNING
:
90 case B_LOW_RESOURCE_NOTE
:
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
);
117 /*! Calls low resource handlers for the given resources.
118 sLowResourceLock must be held.
121 call_handlers(uint32 lowResources
)
123 if (sLowResourceHandlers
.IsEmpty())
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
)) {
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
);
147 sLowResourceHandlers
.Remove(&marker
);
154 sLastMeasurement
= system_time();
156 sLowResources
= B_ALL_KERNEL_RESOURCES
;
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
;
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
));
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
;
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
;
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
;
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
));
243 low_resource_manager(void*)
245 bigtime_t timeout
= kLowResourceInterval
;
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
,
253 RecursiveLocker
_(&sLowResourceLock
);
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
)
266 call_handlers(sLowResources
);
268 if (state
== B_LOW_RESOURCE_WARNING
)
269 timeout
= kWarnResourceInterval
;
271 timeout
= kLowResourceInterval
;
273 sLowResourceWaiterCondition
.NotifyAll();
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
);
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
);
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).
330 low_resource(uint32 resource
, uint64 requirements
, uint32 flags
, uint32 timeout
)
332 // TODO: take requirements into account
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
:
342 release_sem(sLowResourceWaitSem
);
344 if ((flags
& B_RELATIVE_TIMEOUT
) == 0 || timeout
> 0)
345 sLowResourceWaiterCondition
.Wait(flags
, timeout
);
350 low_resource_state(uint32 resources
)
352 recursive_lock_lock(&sLowResourceLock
);
354 if (system_time() - sLastMeasurement
> 500000)
357 int32 state
= low_resource_state_no_update(resources
);
359 recursive_lock_unlock(&sLowResourceLock
);
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
;
382 sWarnMemoryLimit
= totalMemory
/ 64;
383 sCriticalMemoryLimit
= totalMemory
/ 256;
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");
408 unregister_low_resource_handler(low_resource_func function
, void* data
)
410 TRACE(("unregister_low_resource_handler(function = %p, data = %p)\n",
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
);
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.
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",
440 low_resource_handler
*newHandler
= (low_resource_handler
*)malloc(
441 sizeof(low_resource_handler
));
442 if (newHandler
== NULL
)
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
);
467 sLowResourceHandlers
.Add(newHandler
, false);