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 typedef struct { 494 bigtime_t active_time; 495 bool enabled; 496 } beta2_cpu_info; 497 498 499 extern "C" status_t 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 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 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 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 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 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 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 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 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