/*** Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuCpuId.Linux.cpp Date: 2022-1-25 Author: Reece ***/ #include #include "AuHWInfo.hpp" #include "AuCpuInfo.hpp" #include "AuCpuInfo.Linux.hpp" #include #include #include namespace Aurora::HWInfo { AuUInt32 GetCPUIDAPICDCores(); AuUInt32 GetCPUIDAPICDThreads(); static AuUInt32 ReadUInt(const AuString &path) { AuString contents; if (!AuIOFS::ReadString(path, contents)) { return 0; } char *endPtr; auto word = strtoll(contents.c_str(), &endPtr, 10); if (errno == ERANGE) { return 0; } return word; } static AuString ReadString(const AuString &path) { AuString contents; AuIOFS::ReadString(path, contents); return contents; } static void SetCaches() { gCpuInfo.dwCacheLine = ReadUInt("/sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size"); gCpuInfo.dwCacheL1 = ReadUInt("/sys/devices/system/cpu/cpu0/cache/index1/size"); gCpuInfo.dwCacheL2 = ReadUInt("/sys/devices/system/cpu/cpu0/cache/index2/size"); gCpuInfo.dwCacheL3 = ReadUInt("/sys/devices/system/cpu/cpu0/cache/index3/size"); } static void SetCpuA() { static const AuString kBasePath = "/sys/devices/system/cpu/"; AuList files; if (!AuIOFS::DirsInDirectory(kBasePath, files)) { return; } AuList> cpuThreads; cpuThreads.reserve(files.size()); std::sort(files.begin(), files.end()); for (auto & file : files) { if (AuStartsWith(file, "cpu")) { char *endPtr; auto word = strtoll(file.c_str() + 3, &endPtr, 10); if (errno == ERANGE) continue; if (*endPtr != '\x00') continue; cpuThreads.push_back(AuMakeTuple(word, file)); } } bool bIsHyperThreaded {}; for (auto &[threadId, coreStr] : cpuThreads) { auto cpuId = CpuBitId(threadId); auto coreID = ReadUInt(kBasePath + coreStr + "/topology/core_id"); auto cpuList = ReadString(kBasePath + coreStr + "/topology/core_cpus_list"); auto isHVCore = AuStringContains(cpuList, ","); if (!bIsHyperThreaded && isHVCore) { bIsHyperThreaded = true; } if (bIsHyperThreaded && !isHVCore) { gCpuInfo.maskECores.Add(cpuId); } else { gCpuInfo.maskPCores.Add(cpuId); gCpuInfo.pCoreTopology.push_back(cpuId); } if (coreID == threadId) { auto children = CpuBitId(); auto cores = AuSplitString(cpuList, ","); for (const auto core : cores) { char *endPtr; auto word = strtoll(core.data(), &endPtr, 10); if (errno == ERANGE) continue; children.Add(CpuBitId(word)); } gCpuInfo.uCores++; gCpuInfo.coreTopology.push_back(children); gCpuInfo.threadTopology.push_back(children.lower); } gCpuInfo.uThreads++; gCpuInfo.maskAllCores.Add(cpuId); } } static void SetBasicAPICInformation() { if (gCpuInfo.uCores == 1 || gCpuInfo.uCores == 0) { gCpuInfo.uCores = GetCPUIDAPICDCores(); } if (!gCpuInfo.uThreads) { gCpuInfo.uThreads = GetCPUIDAPICDThreads(); } else { return; } if (gCpuInfo.uThreads & 1) { for (AU_ITERATE_N(i, gCpuInfo.uThreads)) { auto mask = 1 << i; gCpuInfo.maskPCores.SetBit(i); gCpuInfo.threadTopology.push_back(mask); CpuBitId coreId; coreId.lower = mask; gCpuInfo.coreTopology.push_back(coreId); gCpuInfo.pCoreTopology.push_back(coreId); gCpuInfo.maskAllCores.Add(coreId); } if (gCpuInfo.uCores == 1 || gCpuInfo.uCores == 0) { gCpuInfo.uCores = gCpuInfo.uThreads; } } else { auto uHalf = gCpuInfo.uThreads / 2; for (AU_ITERATE_N(i, uHalf)) { gCpuInfo.maskPCores.SetBit(i); gCpuInfo.maskPCores.SetBit(i + uHalf); auto maskA = 1u << i; auto maskB = 1u << (i + uHalf); auto maskC = maskA | maskB; gCpuInfo.threadTopology.push_back(maskA); gCpuInfo.threadTopology.push_back(maskB); CpuBitId coreId; coreId.lower = maskC; gCpuInfo.coreTopology.push_back(coreId); gCpuInfo.pCoreTopology.push_back(coreId); gCpuInfo.maskAllCores.Add(coreId); } gCpuInfo.uCores = gCpuInfo.uThreads / 2; gCpuInfo.bMaskMTHalf = true; } } void SetCpuTopologyLinux() { SetCaches(); SetCpuA(); gCpuInfo.uSocket = AuMax(gCpuInfo.uSocket, AuUInt8(1)); if (gCpuInfo.uCores == 1 || gCpuInfo.uCores == 0) { SetBasicAPICInformation(); } } }