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