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
dump_info(int argc,char ** argv)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:
SystemNotificationService()85 SystemNotificationService()
86 {
87 mutex_init(&fLock, "system notification service");
88 }
89
Init()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
StartListening(int32 object,uint32 flags,port_id port,int32 token)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
StopListening(int32 object,uint32 flags,port_id port,int32 token)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
HashKeySystemNotificationService::ListenerHashDefinition213 size_t HashKey(int32 key) const
214 {
215 return key;
216 }
217
HashSystemNotificationService::ListenerHashDefinition218 size_t Hash(const ListenerList* value) const
219 {
220 return HashKey(value->object);
221 }
222
CompareSystemNotificationService::ListenerHashDefinition223 bool Compare(int32 key, const ListenerList* value) const
224 {
225 return value->object == key;
226 }
227
GetLinkSystemNotificationService::ListenerHashDefinition228 ListenerList*& GetLink(ListenerList* value) const
229 {
230 return value->hashNext;
231 }
232 };
233
234 typedef BOpenHashTable<ListenerHashDefinition> ListenerHash;
235
236 private:
EventOccurred(NotificationService & service,const KMessage * event)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
_AddTargets(ListenerList * listenerList,uint32 flags,messaging_target * targets,int32 & targetCount,int32 object,uint32 opcode)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
_SendMessage(messaging_target * targets,int32 targetCount,int32 object,uint32 opcode)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
_FindListener(int32 object,port_id port,int32 token,ListenerList * & _listenerList)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
_RemoveObsoleteListener(Listener * listener)371 void _RemoveObsoleteListener(Listener* listener)
372 {
373 MutexLocker locker(fLock);
374 _RemoveListener(listener);
375 }
376
_RemoveListener(Listener * listener)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
OwnerDeleted(AssociatedDataOwner * owner)403 SystemNotificationService::Listener::OwnerDeleted(AssociatedDataOwner* owner)
404 {
405 sSystemNotificationService._RemoveObsoleteListener(this);
406 }
407
408
409 // #pragma mark - private functions
410
411
412 static void
count_topology_nodes(const cpu_topology_node * node,uint32 & count)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
get_logical_processor(const cpu_topology_node * node)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*
generate_topology_array(cpu_topology_node_info * topology,const cpu_topology_node * node,uint32 & count)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
get_system_info(system_info * info)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 typedef struct {
494 bigtime_t active_time;
495 bool enabled;
496 } beta2_cpu_info;
497
498
499 extern "C" status_t
__get_cpu_info(uint32 firstCPU,uint32 cpuCount,beta2_cpu_info * beta2_info)500 __get_cpu_info(uint32 firstCPU, uint32 cpuCount, beta2_cpu_info* beta2_info)
501 {
502 cpu_info info[cpuCount];
503 status_t err = _get_cpu_info_etc(firstCPU, cpuCount, info, sizeof(cpu_info));
504 if (err == B_OK) {
505 for (uint32 i = 0; i < cpuCount; i++) {
506 beta2_info[i].active_time = info[i].active_time;
507 beta2_info[i].enabled = info[i].enabled;
508 }
509 }
510 return err;
511 }
512
513
514 status_t
_get_cpu_info_etc(uint32 firstCPU,uint32 cpuCount,cpu_info * info,size_t size)515 _get_cpu_info_etc(uint32 firstCPU, uint32 cpuCount, cpu_info* info, size_t size)
516 {
517 if (cpuCount == 0)
518 return B_OK;
519 if (size != sizeof(cpu_info))
520 return B_BAD_VALUE;
521 if (firstCPU >= (uint32)smp_get_num_cpus())
522 return B_BAD_VALUE;
523
524 const uint32 endCPU = firstCPU + std::min(cpuCount, smp_get_num_cpus() - firstCPU);
525
526 // This function is called very often from userland by applications
527 // that display CPU usage information, so we want to keep this as
528 // optimized and touch as little as possible. Hence, we avoid use
529 // of an allocated temporary buffer.
530
531 cpu_info localInfo[8];
532 for (uint32 cpuIdx = firstCPU; cpuIdx < endCPU; ) {
533 uint32 localIdx;
534 for (localIdx = 0; cpuIdx < endCPU && localIdx < B_COUNT_OF(localInfo);
535 cpuIdx++, localIdx++) {
536 localInfo[localIdx].active_time = cpu_get_active_time(cpuIdx);
537 localInfo[localIdx].enabled = !gCPU[cpuIdx].disabled;
538 localInfo[localIdx].current_frequency = cpu_frequency(cpuIdx);
539 }
540
541 if (user_memcpy(info, localInfo, sizeof(cpu_info) * localIdx) != B_OK)
542 return B_BAD_ADDRESS;
543 info += localIdx;
544 }
545
546 return B_OK;
547 }
548
549
550 status_t
system_info_init(struct kernel_args * args)551 system_info_init(struct kernel_args *args)
552 {
553 add_debugger_command("info", &dump_info, "System info");
554
555 return arch_system_info_init(args);
556 }
557
558
559 status_t
system_notifications_init()560 system_notifications_init()
561 {
562 new (&sSystemNotificationService) SystemNotificationService;
563
564 status_t error = sSystemNotificationService.Init();
565 if (error != B_OK) {
566 panic("system_info_init(): Failed to init system notification service");
567 return error;
568 }
569
570 return B_OK;
571 }
572
573
574 // #pragma mark -
575
576
577 status_t
_user_get_system_info(system_info * userInfo)578 _user_get_system_info(system_info* userInfo)
579 {
580 if (userInfo == NULL || !IS_USER_ADDRESS(userInfo))
581 return B_BAD_ADDRESS;
582
583 system_info info;
584 status_t status = get_system_info(&info);
585 if (status == B_OK) {
586 if (user_memcpy(userInfo, &info, sizeof(system_info)) < B_OK)
587 return B_BAD_ADDRESS;
588
589 return B_OK;
590 }
591
592 return status;
593 }
594
595
596 status_t
_user_get_cpu_info(uint32 firstCPU,uint32 cpuCount,cpu_info * userInfo)597 _user_get_cpu_info(uint32 firstCPU, uint32 cpuCount, cpu_info* userInfo)
598 {
599 if (userInfo == NULL || !IS_USER_ADDRESS(userInfo))
600 return B_BAD_ADDRESS;
601
602 return _get_cpu_info_etc(firstCPU, cpuCount, userInfo, sizeof(cpu_info));
603 }
604
605
606 status_t
_user_get_cpu_topology_info(cpu_topology_node_info * topologyInfos,uint32 * topologyInfoCount)607 _user_get_cpu_topology_info(cpu_topology_node_info* topologyInfos,
608 uint32* topologyInfoCount)
609 {
610 if (topologyInfoCount == NULL || !IS_USER_ADDRESS(topologyInfoCount))
611 return B_BAD_ADDRESS;
612
613 const cpu_topology_node* node = get_cpu_topology();
614
615 uint32 count = 0;
616 count_topology_nodes(node, count);
617
618 if (topologyInfos == NULL)
619 return user_memcpy(topologyInfoCount, &count, sizeof(uint32));
620 else if (!IS_USER_ADDRESS(topologyInfos))
621 return B_BAD_ADDRESS;
622
623 uint32 userCount;
624 status_t error = user_memcpy(&userCount, topologyInfoCount, sizeof(uint32));
625 if (error != B_OK)
626 return error;
627 if (userCount == 0)
628 return B_OK;
629 count = std::min(count, userCount);
630
631 cpu_topology_node_info* topology
632 = new(std::nothrow) cpu_topology_node_info[count];
633 if (topology == NULL)
634 return B_NO_MEMORY;
635 ArrayDeleter<cpu_topology_node_info> _(topology);
636 memset(topology, 0, sizeof(cpu_topology_node_info) * count);
637
638 uint32 nodesLeft = count;
639 generate_topology_array(topology, node, nodesLeft);
640 ASSERT(nodesLeft == 0);
641
642 error = user_memcpy(topologyInfos, topology,
643 sizeof(cpu_topology_node_info) * count);
644 if (error != B_OK)
645 return error;
646 return user_memcpy(topologyInfoCount, &count, sizeof(uint32));
647 }
648
649
650 status_t
_user_start_watching_system(int32 object,uint32 flags,port_id port,int32 token)651 _user_start_watching_system(int32 object, uint32 flags, port_id port,
652 int32 token)
653 {
654 return sSystemNotificationService.StartListening(object, flags, port,
655 token);
656 }
657
658
659 status_t
_user_stop_watching_system(int32 object,uint32 flags,port_id port,int32 token)660 _user_stop_watching_system(int32 object, uint32 flags, port_id port,
661 int32 token)
662 {
663 return sSystemNotificationService.StopListening(object, flags, port, token);
664 }
665