headers/bsd: Add sys/queue.h.
[haiku.git] / src / system / kernel / system_info.cpp
blobf09a7a09567198ae8ef0dd8f35cc6af666ac0db2
1 /*
2 * Copyright (c) 2004-2014, Haiku, Inc.
3 * Distributed under the terms of the MIT license.
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
13 #include <ksystem_info.h>
14 #include <system_info.h>
15 #include <system_revision.h>
16 #include <arch/system_info.h>
18 #include <string.h>
20 #include <algorithm>
22 #include <OS.h>
23 #include <KernelExport.h>
25 #include <AutoDeleter.h>
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>
45 const static int64 kKernelVersion = 0x1;
46 const static char *kKernelName = "kernel_" HAIKU_ARCH;
49 static int
50 dump_info(int argc, char **argv)
52 kprintf("kernel build: %s %s (gcc%d %s)\n", __DATE__, __TIME__, __GNUC__,
53 __VERSION__);
54 kprintf("revision: %s\n\n", get_haiku_revision());
56 kprintf("cpu count: %" B_PRId32 "\n", smp_get_num_cpus());
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);
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());
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());
76 return 0;
80 // #pragma mark - user notifications
83 class SystemNotificationService : private NotificationListener {
84 public:
85 SystemNotificationService()
87 mutex_init(&fLock, "system notification service");
90 status_t Init()
92 status_t error = fTeamListeners.Init();
93 if (error != B_OK)
94 return error;
96 error = NotificationManager::Manager().AddListener("teams",
97 TEAM_ADDED | TEAM_REMOVED | TEAM_EXEC, *this);
98 if (error != B_OK)
99 return error;
101 error = NotificationManager::Manager().AddListener("threads",
102 THREAD_ADDED | THREAD_REMOVED | TEAM_EXEC, *this);
103 if (error != B_OK)
104 return error;
106 return B_OK;
109 status_t StartListening(int32 object, uint32 flags, port_id port,
110 int32 token)
112 // check the parameters
113 if ((object < 0 && object != -1) || port < 0)
114 return B_BAD_VALUE;
116 if ((flags & B_WATCH_SYSTEM_ALL) == 0
117 || (flags & ~(uint32)B_WATCH_SYSTEM_ALL) != 0) {
118 return B_BAD_VALUE;
121 MutexLocker locker(fLock);
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;
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);
138 listener->port = port;
139 listener->token = token;
140 listener->flags = flags;
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;
148 listenerList->object = object;
150 fTeamListeners.Insert(listenerList);
153 listener->list = listenerList;
154 listenerList->listeners.Add(listener);
155 listenerDeleter.Detach();
157 team_associate_data(listener);
159 return B_OK;
162 status_t StopListening(int32 object, uint32 flags, port_id port,
163 int32 token)
165 MutexLocker locker(fLock);
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;
173 // clear the given flags
174 listener->flags &= ~flags;
176 if (listener->flags != 0)
177 return B_OK;
179 team_dissociate_data(listener);
180 _RemoveListener(listener);
182 return B_OK;
185 private:
186 struct ListenerList;
188 struct Listener : AssociatedData {
189 DoublyLinkedListLink<Listener> listLink;
190 ListenerList* list;
191 port_id port;
192 int32 token;
193 uint32 flags;
195 virtual void OwnerDeleted(AssociatedDataOwner* owner);
198 friend struct Listener;
200 struct ListenerList {
201 typedef DoublyLinkedList<Listener,
202 DoublyLinkedListMemberGetLink<Listener, &Listener::listLink> > List;
204 ListenerList* hashNext;
205 List listeners;
206 int32 object;
209 struct ListenerHashDefinition {
210 typedef int32 KeyType;
211 typedef ListenerList ValueType;
213 size_t HashKey(int32 key) const
215 return key;
218 size_t Hash(const ListenerList* value) const
220 return HashKey(value->object);
223 bool Compare(int32 key, const ListenerList* value) const
225 return value->object == key;
228 ListenerList*& GetLink(ListenerList* value) const
230 return value->hashNext;
234 typedef BOpenHashTable<ListenerHashDefinition> ListenerHash;
236 private:
237 virtual void EventOccurred(NotificationService& service,
238 const KMessage* event)
240 MutexLocker locker(fLock);
242 int32 eventCode;
243 int32 teamID = 0;
244 if (event->FindInt32("event", &eventCode) != B_OK
245 || event->FindInt32("team", &teamID) != B_OK) {
246 return;
249 int32 object;
250 uint32 opcode;
251 uint32 flags;
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;
273 object = teamID;
274 } else if (event->What() == THREAD_MONITOR) {
275 if (event->FindInt32("thread", &object) != B_OK)
276 return;
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;
294 } else
295 return;
297 // find matching listeners
298 messaging_target targets[kMaxMessagingTargetCount];
299 int32 targetCount = 0;
301 _AddTargets(fTeamListeners.Lookup(teamID), flags, targets,
302 targetCount, object, opcode);
303 _AddTargets(fTeamListeners.Lookup(-1), flags, targets, targetCount,
304 object, opcode);
306 // send the message
307 if (targetCount > 0)
308 _SendMessage(targets, targetCount, object, opcode);
311 void _AddTargets(ListenerList* listenerList, uint32 flags,
312 messaging_target* targets, int32& targetCount, int32 object,
313 uint32 opcode)
315 if (listenerList == NULL)
316 return;
318 for (ListenerList::List::Iterator it
319 = listenerList->listeners.GetIterator();
320 Listener* listener = it.Next();) {
321 if ((listener->flags & flags) == 0)
322 continue;
324 // array is full -- need to flush it first
325 if (targetCount == kMaxMessagingTargetCount) {
326 _SendMessage(targets, targetCount, object, opcode);
327 targetCount = 0;
330 // add the listener
331 targets[targetCount].port = listener->port;
332 targets[targetCount++].token = listener->token;
336 void _SendMessage(messaging_target* targets, int32 targetCount,
337 int32 object, uint32 opcode)
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);
349 // send it
350 send_message(message.Buffer(), message.ContentSize(), targets,
351 targetCount);
354 Listener* _FindListener(int32 object, port_id port, int32 token,
355 ListenerList*& _listenerList)
357 _listenerList = fTeamListeners.Lookup(object);
358 if (_listenerList == NULL)
359 return NULL;
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;
368 return NULL;
371 void _RemoveObsoleteListener(Listener* listener)
373 MutexLocker locker(fLock);
374 _RemoveListener(listener);
377 void _RemoveListener(Listener* listener)
379 // no flags anymore -- remove the listener
380 ListenerList* listenerList = listener->list;
381 listenerList->listeners.Remove(listener);
382 listener->ReleaseReference();
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;
392 private:
393 static const int32 kMaxMessagingTargetCount = 8;
395 mutex fLock;
396 ListenerHash fTeamListeners;
399 static SystemNotificationService sSystemNotificationService;
402 void
403 SystemNotificationService::Listener::OwnerDeleted(AssociatedDataOwner* owner)
405 sSystemNotificationService._RemoveObsoleteListener(this);
409 // #pragma mark - private functions
412 static void
413 count_topology_nodes(const cpu_topology_node* node, uint32& count)
415 count++;
416 for (int32 i = 0; i < node->children_count; i++)
417 count_topology_nodes(node->children[i], count);
421 static int32
422 get_logical_processor(const cpu_topology_node* node)
424 while (node->level != CPU_TOPOLOGY_SMT) {
425 ASSERT(node->children_count > 0);
426 node = node->children[0];
429 return node->id;
433 static cpu_topology_node_info*
434 generate_topology_array(cpu_topology_node_info* topology,
435 const cpu_topology_node* node, uint32& count)
437 if (count == 0)
438 return topology;
440 static const topology_level_type mapTopologyLevels[] = { B_TOPOLOGY_SMT,
441 B_TOPOLOGY_CORE, B_TOPOLOGY_PACKAGE, B_TOPOLOGY_ROOT };
443 STATIC_ASSERT(sizeof(mapTopologyLevels) / sizeof(topology_level_type)
444 == CPU_TOPOLOGY_LEVELS + 1);
446 topology->id = node->id;
447 topology->level = node->level;
448 topology->type = mapTopologyLevels[node->level];
450 arch_fill_topology_node(topology, get_logical_processor(node));
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;
460 // #pragma mark -
463 status_t
464 get_system_info(system_info* info)
466 memset(info, 0, sizeof(system_info));
468 info->boot_time = rtc_boot_time();
469 info->cpu_count = smp_get_num_cpus();
471 vm_page_get_stats(info);
472 vm_get_info(info);
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();
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;
489 return B_OK;
493 status_t
494 get_cpu_info(uint32 firstCPU, uint32 cpuCount, cpu_info* info)
496 if (firstCPU >= (uint32)smp_get_num_cpus())
497 return B_BAD_VALUE;
498 if (cpuCount == 0)
499 return B_OK;
501 uint32 count = std::min(cpuCount, smp_get_num_cpus() - firstCPU);
503 memset(info, 0, sizeof(cpu_info) * count);
504 for (uint32 i = 0; i < count; i++) {
505 info[i].active_time = cpu_get_active_time(firstCPU + i);
506 info[i].enabled = !gCPU[firstCPU + i].disabled;
509 return B_OK;
513 status_t
514 system_info_init(struct kernel_args *args)
516 add_debugger_command("info", &dump_info, "System info");
518 return arch_system_info_init(args);
522 status_t
523 system_notifications_init()
525 new (&sSystemNotificationService) SystemNotificationService;
527 status_t error = sSystemNotificationService.Init();
528 if (error != B_OK) {
529 panic("system_info_init(): Failed to init system notification service");
530 return error;
533 return B_OK;
537 // #pragma mark -
540 status_t
541 _user_get_system_info(system_info* userInfo)
543 if (userInfo == NULL || !IS_USER_ADDRESS(userInfo))
544 return B_BAD_ADDRESS;
546 system_info info;
547 status_t status = get_system_info(&info);
548 if (status == B_OK) {
549 if (user_memcpy(userInfo, &info, sizeof(system_info)) < B_OK)
550 return B_BAD_ADDRESS;
552 return B_OK;
555 return status;
559 status_t
560 _user_get_cpu_info(uint32 firstCPU, uint32 cpuCount, cpu_info* userInfo)
562 if (userInfo == NULL || !IS_USER_ADDRESS(userInfo))
563 return B_BAD_ADDRESS;
564 if (firstCPU >= (uint32)smp_get_num_cpus())
565 return B_BAD_VALUE;
566 if (cpuCount == 0)
567 return B_OK;
569 uint32 count = std::min(cpuCount, smp_get_num_cpus() - firstCPU);
571 cpu_info* cpuInfos = new(std::nothrow) cpu_info[count];
572 if (cpuInfos == NULL)
573 return B_NO_MEMORY;
574 ArrayDeleter<cpu_info> _(cpuInfos);
576 status_t error = get_cpu_info(firstCPU, count, cpuInfos);
577 if (error != B_OK)
578 return error;
580 return user_memcpy(userInfo, cpuInfos, sizeof(cpu_info) * count);
584 status_t
585 _user_get_cpu_topology_info(cpu_topology_node_info* topologyInfos,
586 uint32* topologyInfoCount)
588 if (topologyInfoCount == NULL || !IS_USER_ADDRESS(topologyInfoCount))
589 return B_BAD_ADDRESS;
591 const cpu_topology_node* node = get_cpu_topology();
593 uint32 count = 0;
594 count_topology_nodes(node, count);
596 if (topologyInfos == NULL)
597 return user_memcpy(topologyInfoCount, &count, sizeof(uint32));
598 else if (!IS_USER_ADDRESS(topologyInfoCount))
599 return B_BAD_ADDRESS;
601 uint32 userCount;
602 status_t error = user_memcpy(&userCount, topologyInfoCount, sizeof(uint32));
603 if (error != B_OK)
604 return error;
605 if (userCount == 0)
606 return B_OK;
607 count = std::min(count, userCount);
609 cpu_topology_node_info* topology
610 = new(std::nothrow) cpu_topology_node_info[count];
611 if (topology == NULL)
612 return B_NO_MEMORY;
613 ArrayDeleter<cpu_topology_node_info> _(topology);
614 memset(topology, 0, sizeof(cpu_topology_node_info) * count);
616 uint32 nodesLeft = count;
617 generate_topology_array(topology, node, nodesLeft);
618 ASSERT(nodesLeft == 0);
620 error = user_memcpy(topologyInfos, topology,
621 sizeof(cpu_topology_node_info) * count);
622 if (error != B_OK)
623 return error;
624 return user_memcpy(topologyInfoCount, &count, sizeof(uint32));
628 status_t
629 _user_start_watching_system(int32 object, uint32 flags, port_id port,
630 int32 token)
632 return sSystemNotificationService.StartListening(object, flags, port,
633 token);
637 status_t
638 _user_stop_watching_system(int32 object, uint32 flags, port_id port,
639 int32 token)
641 return sSystemNotificationService.StopListening(object, flags, port, token);