xref: /haiku/src/system/kernel/system_info.cpp (revision 1a3518cf757c2da8006753f83962da5935bbc82b)
1 /*
2  * Copyright (c) 2004-2020, Haiku, Inc.
3  * Distributed under the terms of the MIT license.
4  *
5  * Authors:
6  *		Stefano Ceccherini
7  *		Axel Dörfler, axeld@pinc-software.de
8  *		Paweł Dziepak, pdziepak@quarnos.org
9  *		Ingo Weinhold, ingo_weinhold@gmx.de
10  */
11 
12 
13 #include <ksystem_info.h>
14 #include <system_info.h>
15 #include <system_revision.h>
16 #include <arch/system_info.h>
17 
18 #include <string.h>
19 
20 #include <algorithm>
21 
22 #include <OS.h>
23 #include <KernelExport.h>
24 
25 #include <AutoDeleter.h>
26 
27 #include <block_cache.h>
28 #include <cpu.h>
29 #include <debug.h>
30 #include <kernel.h>
31 #include <lock.h>
32 #include <Notifications.h>
33 #include <messaging.h>
34 #include <port.h>
35 #include <real_time_clock.h>
36 #include <sem.h>
37 #include <smp.h>
38 #include <team.h>
39 #include <thread.h>
40 #include <util/AutoLock.h>
41 #include <vm/vm.h>
42 #include <vm/vm_page.h>
43 
44 
45 const static int64 kKernelVersion = 0x1;
46 const static char *kKernelName = "kernel_" HAIKU_ARCH;
47 
48 
49 static int
50 dump_info(int argc, char **argv)
51 {
52 	kprintf("kernel build: %s %s (gcc%d %s), debug level %d\n", __DATE__,
53 		__TIME__, __GNUC__, __VERSION__, KDEBUG_LEVEL);
54 	kprintf("revision: %s\n\n", get_haiku_revision());
55 
56 	kprintf("cpu count: %" B_PRId32 "\n", smp_get_num_cpus());
57 
58 	for (int32 i = 0; i < smp_get_num_cpus(); i++)
59 		kprintf("  [%" B_PRId32 "] active time: %10" B_PRId64 ", interrupt"
60 			" time: %10" B_PRId64 ", irq time: %10" B_PRId64 "\n", i + 1,
61 			gCPU[i].active_time, gCPU[i].interrupt_time, gCPU[i].irq_time);
62 
63 	// ToDo: Add page_faults
64 	kprintf("pages:\t\t%" B_PRIuPHYSADDR " (%" B_PRIuPHYSADDR " max)\n",
65 		vm_page_num_pages() - vm_page_num_free_pages(), vm_page_num_pages());
66 
67 	kprintf("sems:\t\t%" B_PRId32 " (%" B_PRId32 " max)\n", sem_used_sems(),
68 		sem_max_sems());
69 	kprintf("ports:\t\t%" B_PRId32 " (%" B_PRId32 " max)\n", port_used_ports(),
70 			port_max_ports());
71 	kprintf("threads:\t%" B_PRId32 " (%" B_PRId32 " max)\n",
72 		thread_used_threads(), thread_max_threads());
73 	kprintf("teams:\t\t%" B_PRId32 " (%" B_PRId32 " max)\n", team_used_teams(),
74 		team_max_teams());
75 
76 	return 0;
77 }
78 
79 
80 //	#pragma mark - user notifications
81 
82 
83 class SystemNotificationService : private NotificationListener {
84 public:
85 	SystemNotificationService()
86 	{
87 		mutex_init(&fLock, "system notification service");
88 	}
89 
90 	status_t Init()
91 	{
92 		status_t error = fTeamListeners.Init();
93 		if (error != B_OK)
94 			return error;
95 
96 		error = NotificationManager::Manager().AddListener("teams",
97 			TEAM_ADDED | TEAM_REMOVED | TEAM_EXEC, *this);
98 		if (error != B_OK)
99 			return error;
100 
101 		error = NotificationManager::Manager().AddListener("threads",
102 			THREAD_ADDED | THREAD_REMOVED | TEAM_EXEC, *this);
103 		if (error != B_OK)
104 			return error;
105 
106 		return B_OK;
107 	}
108 
109 	status_t StartListening(int32 object, uint32 flags, port_id port,
110 		int32 token)
111 	{
112 		// check the parameters
113 		if ((object < 0 && object != -1) || port < 0)
114 			return B_BAD_VALUE;
115 
116 		if ((flags & B_WATCH_SYSTEM_ALL) == 0
117 			|| (flags & ~(uint32)B_WATCH_SYSTEM_ALL) != 0) {
118 			return B_BAD_VALUE;
119 		}
120 
121 		MutexLocker locker(fLock);
122 
123 		// maybe the listener already exists
124 		ListenerList* listenerList;
125 		Listener* listener = _FindListener(object, port, token, listenerList);
126 		if (listener != NULL) {
127 			// just add the new flags
128 			listener->flags |= flags;
129 			return B_OK;
130 		}
131 
132 		// create a new listener
133 		listener = new(std::nothrow) Listener;
134 		if (listener == NULL)
135 			return B_NO_MEMORY;
136 		ObjectDeleter<Listener> listenerDeleter(listener);
137 
138 		listener->port = port;
139 		listener->token = token;
140 		listener->flags = flags;
141 
142 		// if there's no list yet, create a new list
143 		if (listenerList == NULL) {
144 			listenerList = new(std::nothrow) ListenerList;
145 			if (listenerList == NULL)
146 				return B_NO_MEMORY;
147 
148 			listenerList->object = object;
149 
150 			fTeamListeners.Insert(listenerList);
151 		}
152 
153 		listener->list = listenerList;
154 		listenerList->listeners.Add(listener);
155 		listenerDeleter.Detach();
156 
157 		team_associate_data(listener);
158 
159 		return B_OK;
160 	}
161 
162 	status_t StopListening(int32 object, uint32 flags, port_id port,
163 		int32 token)
164 	{
165 		MutexLocker locker(fLock);
166 
167 		// find the listener
168 		ListenerList* listenerList;
169 		Listener* listener = _FindListener(object, port, token, listenerList);
170 		if (listener == NULL)
171 			return B_ENTRY_NOT_FOUND;
172 
173 		// clear the given flags
174 		listener->flags &= ~flags;
175 
176 		if (listener->flags != 0)
177 			return B_OK;
178 
179 		team_dissociate_data(listener);
180 		_RemoveListener(listener);
181 
182 		return B_OK;
183 	}
184 
185 private:
186 	struct ListenerList;
187 
188 	struct Listener : AssociatedData {
189 		DoublyLinkedListLink<Listener>	listLink;
190 		ListenerList*					list;
191 		port_id							port;
192 		int32							token;
193 		uint32							flags;
194 
195 		virtual void OwnerDeleted(AssociatedDataOwner* owner);
196 	};
197 
198 	friend struct Listener;
199 
200 	struct ListenerList {
201 		typedef DoublyLinkedList<Listener,
202 			DoublyLinkedListMemberGetLink<Listener, &Listener::listLink> > List;
203 
204 		ListenerList*	hashNext;
205 		List			listeners;
206 		int32			object;
207 	};
208 
209 	struct ListenerHashDefinition {
210 		typedef int32			KeyType;
211 		typedef	ListenerList	ValueType;
212 
213 		size_t HashKey(int32 key) const
214 		{
215 			return key;
216 		}
217 
218 		size_t Hash(const ListenerList* value) const
219 		{
220 			return HashKey(value->object);
221 		}
222 
223 		bool Compare(int32 key, const ListenerList* value) const
224 		{
225 			return value->object == key;
226 		}
227 
228 		ListenerList*& GetLink(ListenerList* value) const
229 		{
230 			return value->hashNext;
231 		}
232 	};
233 
234 	typedef BOpenHashTable<ListenerHashDefinition> ListenerHash;
235 
236 private:
237 	virtual void EventOccurred(NotificationService& service,
238 		const KMessage* event)
239 	{
240 		MutexLocker locker(fLock);
241 
242 		int32 eventCode;
243 		int32 teamID = 0;
244 		if (event->FindInt32("event", &eventCode) != B_OK
245 			|| event->FindInt32("team", &teamID) != B_OK) {
246 			return;
247 		}
248 
249 		int32 object;
250 		uint32 opcode;
251 		uint32 flags;
252 
253 		// translate the event
254 		if (event->What() == TEAM_MONITOR) {
255 			switch (eventCode) {
256 				case TEAM_ADDED:
257 					opcode = B_TEAM_CREATED;
258 					flags = B_WATCH_SYSTEM_TEAM_CREATION;
259 					break;
260 				case TEAM_REMOVED:
261 					opcode = B_TEAM_DELETED;
262 					flags = B_WATCH_SYSTEM_TEAM_DELETION;
263 					break;
264 				case TEAM_EXEC:
265 					opcode = B_TEAM_EXEC;
266 					flags = B_WATCH_SYSTEM_TEAM_CREATION
267 						| B_WATCH_SYSTEM_TEAM_DELETION;
268 					break;
269 				default:
270 					return;
271 			}
272 
273 			object = teamID;
274 		} else if (event->What() == THREAD_MONITOR) {
275 			if (event->FindInt32("thread", &object) != B_OK)
276 				return;
277 
278 			switch (eventCode) {
279 				case THREAD_ADDED:
280 					opcode = B_THREAD_CREATED;
281 					flags = B_WATCH_SYSTEM_THREAD_CREATION;
282 					break;
283 				case THREAD_REMOVED:
284 					opcode = B_THREAD_DELETED;
285 					flags = B_WATCH_SYSTEM_THREAD_DELETION;
286 					break;
287 				case THREAD_NAME_CHANGED:
288 					opcode = B_THREAD_NAME_CHANGED;
289 					flags = B_WATCH_SYSTEM_THREAD_PROPERTIES;
290 					break;
291 				default:
292 					return;
293 			}
294 		} else
295 			return;
296 
297 		// find matching listeners
298 		messaging_target targets[kMaxMessagingTargetCount];
299 		int32 targetCount = 0;
300 
301 		_AddTargets(fTeamListeners.Lookup(teamID), flags, targets,
302 			targetCount, object, opcode);
303 		_AddTargets(fTeamListeners.Lookup(-1), flags, targets, targetCount,
304 			object, opcode);
305 
306 		// send the message
307 		if (targetCount > 0)
308 			_SendMessage(targets, targetCount, object, opcode);
309 	}
310 
311 	void _AddTargets(ListenerList* listenerList, uint32 flags,
312 		messaging_target* targets, int32& targetCount, int32 object,
313 		uint32 opcode)
314 	{
315 		if (listenerList == NULL)
316 			return;
317 
318 		for (ListenerList::List::Iterator it
319 				= listenerList->listeners.GetIterator();
320 			Listener* listener = it.Next();) {
321 			if ((listener->flags & flags) == 0)
322 				continue;
323 
324 			// array is full -- need to flush it first
325 			if (targetCount == kMaxMessagingTargetCount) {
326 				_SendMessage(targets, targetCount, object, opcode);
327 				targetCount = 0;
328 			}
329 
330 			// add the listener
331 			targets[targetCount].port = listener->port;
332 			targets[targetCount++].token = listener->token;
333 		}
334 	}
335 
336 	void _SendMessage(messaging_target* targets, int32 targetCount,
337 		int32 object, uint32 opcode)
338 	{
339 		// prepare the message
340 		char buffer[128];
341 		KMessage message;
342 		message.SetTo(buffer, sizeof(buffer), B_SYSTEM_OBJECT_UPDATE);
343 		message.AddInt32("opcode", opcode);
344 		if (opcode < B_THREAD_CREATED)
345 			message.AddInt32("team", object);
346 		else
347 			message.AddInt32("thread", object);
348 
349 		// send it
350 		send_message(message.Buffer(), message.ContentSize(), targets,
351 			targetCount);
352 	}
353 
354 	Listener* _FindListener(int32 object, port_id port, int32 token,
355 		ListenerList*& _listenerList)
356 	{
357 		_listenerList = fTeamListeners.Lookup(object);
358 		if (_listenerList == NULL)
359 			return NULL;
360 
361 		for (ListenerList::List::Iterator it
362 				= _listenerList->listeners.GetIterator();
363 			Listener* listener = it.Next();) {
364 			if (listener->port == port && listener->token == token)
365 				return listener;
366 		}
367 
368 		return NULL;
369 	}
370 
371 	void _RemoveObsoleteListener(Listener* listener)
372 	{
373 		MutexLocker locker(fLock);
374 		_RemoveListener(listener);
375 	}
376 
377 	void _RemoveListener(Listener* listener)
378 	{
379 		// no flags anymore -- remove the listener
380 		ListenerList* listenerList = listener->list;
381 		listenerList->listeners.Remove(listener);
382 		listener->ReleaseReference();
383 
384 		if (listenerList->listeners.IsEmpty()) {
385 			// no listeners in the list anymore -- remove the list from the hash
386 			// table
387 			fTeamListeners.Remove(listenerList);
388 			delete listenerList;
389 		}
390 	}
391 
392 private:
393 	static const int32 kMaxMessagingTargetCount = 8;
394 
395 	mutex			fLock;
396 	ListenerHash	fTeamListeners;
397 };
398 
399 static SystemNotificationService sSystemNotificationService;
400 
401 
402 void
403 SystemNotificationService::Listener::OwnerDeleted(AssociatedDataOwner* owner)
404 {
405 	sSystemNotificationService._RemoveObsoleteListener(this);
406 }
407 
408 
409 //	#pragma mark - private functions
410 
411 
412 static void
413 count_topology_nodes(const cpu_topology_node* node, uint32& count)
414 {
415 	count++;
416 	for (int32 i = 0; i < node->children_count; i++)
417 		count_topology_nodes(node->children[i], count);
418 }
419 
420 
421 static int32
422 get_logical_processor(const cpu_topology_node* node)
423 {
424 	while (node->level != CPU_TOPOLOGY_SMT) {
425 		ASSERT(node->children_count > 0);
426 		node = node->children[0];
427 	}
428 
429 	return node->id;
430 }
431 
432 
433 static cpu_topology_node_info*
434 generate_topology_array(cpu_topology_node_info* topology,
435 	const cpu_topology_node* node, uint32& count)
436 {
437 	if (count == 0)
438 		return topology;
439 
440 	static const topology_level_type mapTopologyLevels[] = { B_TOPOLOGY_SMT,
441 		B_TOPOLOGY_CORE, B_TOPOLOGY_PACKAGE, B_TOPOLOGY_ROOT };
442 
443 	STATIC_ASSERT(sizeof(mapTopologyLevels) / sizeof(topology_level_type)
444 		== CPU_TOPOLOGY_LEVELS + 1);
445 
446 	topology->id = node->id;
447 	topology->level = node->level;
448 	topology->type = mapTopologyLevels[node->level];
449 
450 	arch_fill_topology_node(topology, get_logical_processor(node));
451 
452 	count--;
453 	topology++;
454 	for (int32 i = 0; i < node->children_count && count > 0; i++)
455 		topology = generate_topology_array(topology, node->children[i], count);
456 	return topology;
457 }
458 
459 
460 //	#pragma mark -
461 
462 
463 status_t
464 get_system_info(system_info* info)
465 {
466 	memset(info, 0, sizeof(system_info));
467 
468 	info->boot_time = rtc_boot_time();
469 	info->cpu_count = smp_get_num_cpus();
470 
471 	vm_page_get_stats(info);
472 	vm_get_info(info);
473 
474 	info->used_threads = thread_used_threads();
475 	info->max_threads = thread_max_threads();
476 	info->used_teams = team_used_teams();
477 	info->max_teams = team_max_teams();
478 	info->used_ports = port_used_ports();
479 	info->max_ports = port_max_ports();
480 	info->used_sems = sem_used_sems();
481 	info->max_sems = sem_max_sems();
482 
483 	info->kernel_version = kKernelVersion;
484 	strlcpy(info->kernel_name, kKernelName, B_FILE_NAME_LENGTH);
485 	strlcpy(info->kernel_build_date, __DATE__, B_OS_NAME_LENGTH);
486 	strlcpy(info->kernel_build_time, __TIME__, B_OS_NAME_LENGTH);
487 	info->abi = B_HAIKU_ABI;
488 
489 	return B_OK;
490 }
491 
492 
493 status_t
494 get_cpu_info(uint32 firstCPU, uint32 cpuCount, cpu_info* info)
495 {
496 	if (cpuCount == 0)
497 		return B_OK;
498 	if (firstCPU >= (uint32)smp_get_num_cpus())
499 		return B_BAD_VALUE;
500 
501 	uint32 count = std::min(cpuCount, smp_get_num_cpus() - firstCPU);
502 
503 	// This function is called very often from userland by applications
504 	// that display CPU usage information, so we want to keep this as
505 	// optimized and touch as little as possible. Hence, no use of
506 	// a temporary buffer.
507 
508 	if (IS_USER_ADDRESS(info)) {
509 		if (user_memset(info, 0, sizeof(cpu_info) * count) != B_OK)
510 			return B_BAD_ADDRESS;
511 		set_ac();
512 	} else {
513 		memset(info, 0, sizeof(cpu_info) * count);
514 	}
515 
516 	for (uint32 i = 0; i < count; i++) {
517 		info[i].active_time = cpu_get_active_time(firstCPU + i);
518 		info[i].enabled = !gCPU[firstCPU + i].disabled;
519 	}
520 
521 	if (IS_USER_ADDRESS(info)) {
522 		clear_ac();
523 	}
524 
525 	return B_OK;
526 }
527 
528 
529 status_t
530 system_info_init(struct kernel_args *args)
531 {
532 	add_debugger_command("info", &dump_info, "System info");
533 
534 	return arch_system_info_init(args);
535 }
536 
537 
538 status_t
539 system_notifications_init()
540 {
541 	new (&sSystemNotificationService) SystemNotificationService;
542 
543 	status_t error = sSystemNotificationService.Init();
544 	if (error != B_OK) {
545 		panic("system_info_init(): Failed to init system notification service");
546 		return error;
547 	}
548 
549 	return B_OK;
550 }
551 
552 
553 //	#pragma mark -
554 
555 
556 status_t
557 _user_get_system_info(system_info* userInfo)
558 {
559 	if (userInfo == NULL || !IS_USER_ADDRESS(userInfo))
560 		return B_BAD_ADDRESS;
561 
562 	system_info info;
563 	status_t status = get_system_info(&info);
564 	if (status == B_OK) {
565 		if (user_memcpy(userInfo, &info, sizeof(system_info)) < B_OK)
566 			return B_BAD_ADDRESS;
567 
568 		return B_OK;
569 	}
570 
571 	return status;
572 }
573 
574 
575 status_t
576 _user_get_cpu_info(uint32 firstCPU, uint32 cpuCount, cpu_info* userInfo)
577 {
578 	if (userInfo == NULL || !IS_USER_ADDRESS(userInfo))
579 		return B_BAD_ADDRESS;
580 
581 	return get_cpu_info(firstCPU, cpuCount, userInfo);
582 }
583 
584 
585 status_t
586 _user_get_cpu_topology_info(cpu_topology_node_info* topologyInfos,
587 	uint32* topologyInfoCount)
588 {
589 	if (topologyInfoCount == NULL || !IS_USER_ADDRESS(topologyInfoCount))
590 		return B_BAD_ADDRESS;
591 
592 	const cpu_topology_node* node = get_cpu_topology();
593 
594 	uint32 count = 0;
595 	count_topology_nodes(node, count);
596 
597 	if (topologyInfos == NULL)
598 		return user_memcpy(topologyInfoCount, &count, sizeof(uint32));
599 	else if (!IS_USER_ADDRESS(topologyInfos))
600 		return B_BAD_ADDRESS;
601 
602 	uint32 userCount;
603 	status_t error = user_memcpy(&userCount, topologyInfoCount, sizeof(uint32));
604 	if (error != B_OK)
605 		return error;
606 	if (userCount == 0)
607 		return B_OK;
608 	count = std::min(count, userCount);
609 
610 	cpu_topology_node_info* topology
611 		= new(std::nothrow) cpu_topology_node_info[count];
612 	if (topology == NULL)
613 		return B_NO_MEMORY;
614 	ArrayDeleter<cpu_topology_node_info> _(topology);
615 	memset(topology, 0, sizeof(cpu_topology_node_info) * count);
616 
617 	uint32 nodesLeft = count;
618 	generate_topology_array(topology, node, nodesLeft);
619 	ASSERT(nodesLeft == 0);
620 
621 	error = user_memcpy(topologyInfos, topology,
622 		sizeof(cpu_topology_node_info) * count);
623 	if (error != B_OK)
624 		return error;
625 	return user_memcpy(topologyInfoCount, &count, sizeof(uint32));
626 }
627 
628 
629 status_t
630 _user_start_watching_system(int32 object, uint32 flags, port_id port,
631 	int32 token)
632 {
633 	return sSystemNotificationService.StartListening(object, flags, port,
634 		token);
635 }
636 
637 
638 status_t
639 _user_stop_watching_system(int32 object, uint32 flags, port_id port,
640 	int32 token)
641 {
642 	return sSystemNotificationService.StopListening(object, flags, port, token);
643 }
644