xref: /haiku/src/system/kernel/low_resource_manager.cpp (revision 125183f9e5c136781f71c879faaeab43fdc3ea7b)
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