/* * Copyright (c) 2004-2020, Haiku, Inc. * Distributed under the terms of the MIT license. * * Authors: * Stefano Ceccherini * Axel Dörfler, axeld@pinc-software.de * Paweł Dziepak, pdziepak@quarnos.org * Ingo Weinhold, ingo_weinhold@gmx.de */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const static int64 kKernelVersion = 0x1; const static char *kKernelName = "kernel_" HAIKU_ARCH; static int dump_info(int argc, char **argv) { kprintf("kernel build: %s %s (gcc%d %s), debug level %d\n", __DATE__, __TIME__, __GNUC__, __VERSION__, KDEBUG_LEVEL); kprintf("revision: %s\n\n", get_haiku_revision()); kprintf("cpu count: %" B_PRId32 "\n", smp_get_num_cpus()); for (int32 i = 0; i < smp_get_num_cpus(); i++) kprintf(" [%" B_PRId32 "] active time: %10" B_PRId64 ", interrupt" " time: %10" B_PRId64 ", irq time: %10" B_PRId64 "\n", i + 1, gCPU[i].active_time, gCPU[i].interrupt_time, gCPU[i].irq_time); // ToDo: Add page_faults kprintf("pages:\t\t%" B_PRIuPHYSADDR " (%" B_PRIuPHYSADDR " max)\n", vm_page_num_pages() - vm_page_num_free_pages(), vm_page_num_pages()); kprintf("sems:\t\t%" B_PRId32 " (%" B_PRId32 " max)\n", sem_used_sems(), sem_max_sems()); kprintf("ports:\t\t%" B_PRId32 " (%" B_PRId32 " max)\n", port_used_ports(), port_max_ports()); kprintf("threads:\t%" B_PRId32 " (%" B_PRId32 " max)\n", thread_used_threads(), thread_max_threads()); kprintf("teams:\t\t%" B_PRId32 " (%" B_PRId32 " max)\n", team_used_teams(), team_max_teams()); return 0; } // #pragma mark - user notifications class SystemNotificationService : private NotificationListener { public: SystemNotificationService() { mutex_init(&fLock, "system notification service"); } status_t Init() { status_t error = fTeamListeners.Init(); if (error != B_OK) return error; error = NotificationManager::Manager().AddListener("teams", TEAM_ADDED | TEAM_REMOVED | TEAM_EXEC, *this); if (error != B_OK) return error; error = NotificationManager::Manager().AddListener("threads", THREAD_ADDED | THREAD_REMOVED | TEAM_EXEC, *this); if (error != B_OK) return error; return B_OK; } status_t StartListening(int32 object, uint32 flags, port_id port, int32 token) { // check the parameters if ((object < 0 && object != -1) || port < 0) return B_BAD_VALUE; if ((flags & B_WATCH_SYSTEM_ALL) == 0 || (flags & ~(uint32)B_WATCH_SYSTEM_ALL) != 0) { return B_BAD_VALUE; } MutexLocker locker(fLock); // maybe the listener already exists ListenerList* listenerList; Listener* listener = _FindListener(object, port, token, listenerList); if (listener != NULL) { // just add the new flags listener->flags |= flags; return B_OK; } // create a new listener listener = new(std::nothrow) Listener; if (listener == NULL) return B_NO_MEMORY; ObjectDeleter listenerDeleter(listener); listener->port = port; listener->token = token; listener->flags = flags; // if there's no list yet, create a new list if (listenerList == NULL) { listenerList = new(std::nothrow) ListenerList; if (listenerList == NULL) return B_NO_MEMORY; listenerList->object = object; fTeamListeners.Insert(listenerList); } listener->list = listenerList; listenerList->listeners.Add(listener); listenerDeleter.Detach(); team_associate_data(listener); return B_OK; } status_t StopListening(int32 object, uint32 flags, port_id port, int32 token) { MutexLocker locker(fLock); // find the listener ListenerList* listenerList; Listener* listener = _FindListener(object, port, token, listenerList); if (listener == NULL) return B_ENTRY_NOT_FOUND; // clear the given flags listener->flags &= ~flags; if (listener->flags != 0) return B_OK; team_dissociate_data(listener); _RemoveListener(listener); return B_OK; } private: struct ListenerList; struct Listener : AssociatedData { DoublyLinkedListLink listLink; ListenerList* list; port_id port; int32 token; uint32 flags; virtual void OwnerDeleted(AssociatedDataOwner* owner); }; friend struct Listener; struct ListenerList { typedef DoublyLinkedList > List; ListenerList* hashNext; List listeners; int32 object; }; struct ListenerHashDefinition { typedef int32 KeyType; typedef ListenerList ValueType; size_t HashKey(int32 key) const { return key; } size_t Hash(const ListenerList* value) const { return HashKey(value->object); } bool Compare(int32 key, const ListenerList* value) const { return value->object == key; } ListenerList*& GetLink(ListenerList* value) const { return value->hashNext; } }; typedef BOpenHashTable ListenerHash; private: virtual void EventOccurred(NotificationService& service, const KMessage* event) { MutexLocker locker(fLock); int32 eventCode; int32 teamID = 0; if (event->FindInt32("event", &eventCode) != B_OK || event->FindInt32("team", &teamID) != B_OK) { return; } int32 object; uint32 opcode; uint32 flags; // translate the event if (event->What() == TEAM_MONITOR) { switch (eventCode) { case TEAM_ADDED: opcode = B_TEAM_CREATED; flags = B_WATCH_SYSTEM_TEAM_CREATION; break; case TEAM_REMOVED: opcode = B_TEAM_DELETED; flags = B_WATCH_SYSTEM_TEAM_DELETION; break; case TEAM_EXEC: opcode = B_TEAM_EXEC; flags = B_WATCH_SYSTEM_TEAM_CREATION | B_WATCH_SYSTEM_TEAM_DELETION; break; default: return; } object = teamID; } else if (event->What() == THREAD_MONITOR) { if (event->FindInt32("thread", &object) != B_OK) return; switch (eventCode) { case THREAD_ADDED: opcode = B_THREAD_CREATED; flags = B_WATCH_SYSTEM_THREAD_CREATION; break; case THREAD_REMOVED: opcode = B_THREAD_DELETED; flags = B_WATCH_SYSTEM_THREAD_DELETION; break; case THREAD_NAME_CHANGED: opcode = B_THREAD_NAME_CHANGED; flags = B_WATCH_SYSTEM_THREAD_PROPERTIES; break; default: return; } } else return; // find matching listeners messaging_target targets[kMaxMessagingTargetCount]; int32 targetCount = 0; _AddTargets(fTeamListeners.Lookup(teamID), flags, targets, targetCount, object, opcode); _AddTargets(fTeamListeners.Lookup(-1), flags, targets, targetCount, object, opcode); // send the message if (targetCount > 0) _SendMessage(targets, targetCount, object, opcode); } void _AddTargets(ListenerList* listenerList, uint32 flags, messaging_target* targets, int32& targetCount, int32 object, uint32 opcode) { if (listenerList == NULL) return; for (ListenerList::List::Iterator it = listenerList->listeners.GetIterator(); Listener* listener = it.Next();) { if ((listener->flags & flags) == 0) continue; // array is full -- need to flush it first if (targetCount == kMaxMessagingTargetCount) { _SendMessage(targets, targetCount, object, opcode); targetCount = 0; } // add the listener targets[targetCount].port = listener->port; targets[targetCount++].token = listener->token; } } void _SendMessage(messaging_target* targets, int32 targetCount, int32 object, uint32 opcode) { // prepare the message char buffer[128]; KMessage message; message.SetTo(buffer, sizeof(buffer), B_SYSTEM_OBJECT_UPDATE); message.AddInt32("opcode", opcode); if (opcode < B_THREAD_CREATED) message.AddInt32("team", object); else message.AddInt32("thread", object); // send it send_message(message.Buffer(), message.ContentSize(), targets, targetCount); } Listener* _FindListener(int32 object, port_id port, int32 token, ListenerList*& _listenerList) { _listenerList = fTeamListeners.Lookup(object); if (_listenerList == NULL) return NULL; for (ListenerList::List::Iterator it = _listenerList->listeners.GetIterator(); Listener* listener = it.Next();) { if (listener->port == port && listener->token == token) return listener; } return NULL; } void _RemoveObsoleteListener(Listener* listener) { MutexLocker locker(fLock); _RemoveListener(listener); } void _RemoveListener(Listener* listener) { // no flags anymore -- remove the listener ListenerList* listenerList = listener->list; listenerList->listeners.Remove(listener); listener->ReleaseReference(); if (listenerList->listeners.IsEmpty()) { // no listeners in the list anymore -- remove the list from the hash // table fTeamListeners.Remove(listenerList); delete listenerList; } } private: static const int32 kMaxMessagingTargetCount = 8; mutex fLock; ListenerHash fTeamListeners; }; static SystemNotificationService sSystemNotificationService; void SystemNotificationService::Listener::OwnerDeleted(AssociatedDataOwner* owner) { sSystemNotificationService._RemoveObsoleteListener(this); } // #pragma mark - private functions static void count_topology_nodes(const cpu_topology_node* node, uint32& count) { count++; for (int32 i = 0; i < node->children_count; i++) count_topology_nodes(node->children[i], count); } static int32 get_logical_processor(const cpu_topology_node* node) { while (node->level != CPU_TOPOLOGY_SMT) { ASSERT(node->children_count > 0); node = node->children[0]; } return node->id; } static cpu_topology_node_info* generate_topology_array(cpu_topology_node_info* topology, const cpu_topology_node* node, uint32& count) { if (count == 0) return topology; static const topology_level_type mapTopologyLevels[] = { B_TOPOLOGY_SMT, B_TOPOLOGY_CORE, B_TOPOLOGY_PACKAGE, B_TOPOLOGY_ROOT }; STATIC_ASSERT(sizeof(mapTopologyLevels) / sizeof(topology_level_type) == CPU_TOPOLOGY_LEVELS + 1); topology->id = node->id; topology->level = node->level; topology->type = mapTopologyLevels[node->level]; arch_fill_topology_node(topology, get_logical_processor(node)); count--; topology++; for (int32 i = 0; i < node->children_count && count > 0; i++) topology = generate_topology_array(topology, node->children[i], count); return topology; } // #pragma mark - status_t get_system_info(system_info* info) { memset(info, 0, sizeof(system_info)); info->boot_time = rtc_boot_time(); info->cpu_count = smp_get_num_cpus(); vm_page_get_stats(info); vm_get_info(info); info->used_threads = thread_used_threads(); info->max_threads = thread_max_threads(); info->used_teams = team_used_teams(); info->max_teams = team_max_teams(); info->used_ports = port_used_ports(); info->max_ports = port_max_ports(); info->used_sems = sem_used_sems(); info->max_sems = sem_max_sems(); info->kernel_version = kKernelVersion; strlcpy(info->kernel_name, kKernelName, B_FILE_NAME_LENGTH); strlcpy(info->kernel_build_date, __DATE__, B_OS_NAME_LENGTH); strlcpy(info->kernel_build_time, __TIME__, B_OS_NAME_LENGTH); info->abi = B_HAIKU_ABI; return B_OK; } typedef struct { bigtime_t active_time; bool enabled; } beta2_cpu_info; extern "C" status_t __get_cpu_info(uint32 firstCPU, uint32 cpuCount, beta2_cpu_info* beta2_info) { cpu_info info[cpuCount]; status_t err = _get_cpu_info_etc(firstCPU, cpuCount, info, sizeof(cpu_info)); if (err == B_OK) { for (uint32 i = 0; i < cpuCount; i++) { beta2_info[i].active_time = info[i].active_time; beta2_info[i].enabled = info[i].enabled; } } return err; } status_t _get_cpu_info_etc(uint32 firstCPU, uint32 cpuCount, cpu_info* info, size_t size) { if (cpuCount == 0) return B_OK; if (size != sizeof(cpu_info)) return B_BAD_VALUE; if (firstCPU >= (uint32)smp_get_num_cpus()) return B_BAD_VALUE; const uint32 endCPU = firstCPU + std::min(cpuCount, smp_get_num_cpus() - firstCPU); // This function is called very often from userland by applications // that display CPU usage information, so we want to keep this as // optimized and touch as little as possible. Hence, we avoid use // of an allocated temporary buffer. cpu_info localInfo[8]; for (uint32 cpuIdx = firstCPU; cpuIdx < endCPU; ) { uint32 localIdx; for (localIdx = 0; cpuIdx < endCPU && localIdx < B_COUNT_OF(localInfo); cpuIdx++, localIdx++) { localInfo[localIdx].active_time = cpu_get_active_time(cpuIdx); localInfo[localIdx].enabled = !gCPU[cpuIdx].disabled; localInfo[localIdx].current_frequency = cpu_frequency(cpuIdx); } if (user_memcpy(info, localInfo, sizeof(cpu_info) * localIdx) != B_OK) return B_BAD_ADDRESS; info += localIdx; } return B_OK; } status_t system_info_init(struct kernel_args *args) { add_debugger_command("info", &dump_info, "System info"); return arch_system_info_init(args); } status_t system_notifications_init() { new (&sSystemNotificationService) SystemNotificationService; status_t error = sSystemNotificationService.Init(); if (error != B_OK) { panic("system_info_init(): Failed to init system notification service"); return error; } return B_OK; } // #pragma mark - status_t _user_get_system_info(system_info* userInfo) { if (userInfo == NULL || !IS_USER_ADDRESS(userInfo)) return B_BAD_ADDRESS; system_info info; status_t status = get_system_info(&info); if (status == B_OK) { if (user_memcpy(userInfo, &info, sizeof(system_info)) < B_OK) return B_BAD_ADDRESS; return B_OK; } return status; } status_t _user_get_cpu_info(uint32 firstCPU, uint32 cpuCount, cpu_info* userInfo) { if (userInfo == NULL || !IS_USER_ADDRESS(userInfo)) return B_BAD_ADDRESS; return _get_cpu_info_etc(firstCPU, cpuCount, userInfo, sizeof(cpu_info)); } status_t _user_get_cpu_topology_info(cpu_topology_node_info* topologyInfos, uint32* topologyInfoCount) { if (topologyInfoCount == NULL || !IS_USER_ADDRESS(topologyInfoCount)) return B_BAD_ADDRESS; const cpu_topology_node* node = get_cpu_topology(); uint32 count = 0; count_topology_nodes(node, count); if (topologyInfos == NULL) return user_memcpy(topologyInfoCount, &count, sizeof(uint32)); else if (!IS_USER_ADDRESS(topologyInfos)) return B_BAD_ADDRESS; uint32 userCount; status_t error = user_memcpy(&userCount, topologyInfoCount, sizeof(uint32)); if (error != B_OK) return error; if (userCount == 0) return B_OK; count = std::min(count, userCount); cpu_topology_node_info* topology = new(std::nothrow) cpu_topology_node_info[count]; if (topology == NULL) return B_NO_MEMORY; ArrayDeleter _(topology); memset(topology, 0, sizeof(cpu_topology_node_info) * count); uint32 nodesLeft = count; generate_topology_array(topology, node, nodesLeft); ASSERT(nodesLeft == 0); error = user_memcpy(topologyInfos, topology, sizeof(cpu_topology_node_info) * count); if (error != B_OK) return error; return user_memcpy(topologyInfoCount, &count, sizeof(uint32)); } status_t _user_start_watching_system(int32 object, uint32 flags, port_id port, int32 token) { return sSystemNotificationService.StartListening(object, flags, port, token); } status_t _user_stop_watching_system(int32 object, uint32 flags, port_id port, int32 token) { return sSystemNotificationService.StopListening(object, flags, port, token); }