blob: d393e4a12f39ce8cb65bed6832c52c2b2bfaf439 [file] [log] [blame]
Jonathan Doman94c94bf2020-10-05 23:25:45 -07001// Copyright (c) 2020 Intel Corporation
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "speed_select.hpp"
16
17#include "cpuinfo.hpp"
Jonathan Doman703a1852020-11-11 13:04:02 -080018#include "cpuinfo_utils.hpp"
Jonathan Doman94c94bf2020-10-05 23:25:45 -070019
20#include <peci.h>
21
Jonathan Doman49ea8302022-05-26 14:29:46 -070022#include <boost/asio/error.hpp>
Jonathan Doman94c94bf2020-10-05 23:25:45 -070023#include <boost/asio/steady_timer.hpp>
Jonathan Doman703a1852020-11-11 13:04:02 -080024#include <xyz/openbmc_project/Common/Device/error.hpp>
Jonathan Doman94c94bf2020-10-05 23:25:45 -070025#include <xyz/openbmc_project/Common/error.hpp>
26#include <xyz/openbmc_project/Control/Processor/CurrentOperatingConfig/server.hpp>
27#include <xyz/openbmc_project/Inventory/Item/Cpu/OperatingConfig/server.hpp>
28
Jonathan Doman16a2ced2021-11-01 11:13:22 -070029#include <algorithm>
Jonathan Doman94c94bf2020-10-05 23:25:45 -070030#include <iostream>
31#include <memory>
Jonathan Doman16a2ced2021-11-01 11:13:22 -070032#include <stdexcept>
Jonathan Doman94c94bf2020-10-05 23:25:45 -070033#include <string>
34
35namespace cpu_info
36{
37namespace sst
38{
39
Jonathan Doman16a2ced2021-11-01 11:13:22 -070040// Specialize char to print the integer value instead of ascii. We basically
41// never want to print a single ascii char.
42std::ostream& operator<<(std::ostream& os, uint8_t value)
Jonathan Doman94c94bf2020-10-05 23:25:45 -070043{
Jonathan Doman16a2ced2021-11-01 11:13:22 -070044 return os << static_cast<int>(value);
Jonathan Doman94c94bf2020-10-05 23:25:45 -070045}
46
Jonathan Doman16a2ced2021-11-01 11:13:22 -070047bool checkPECIStatus(EPECIStatus libStatus, uint8_t completionCode)
Jonathan Doman94c94bf2020-10-05 23:25:45 -070048{
49 if (libStatus != PECI_CC_SUCCESS || completionCode != PECI_DEV_CC_SUCCESS)
50 {
51 std::cerr << "PECI command failed."
52 << " Driver Status = " << libStatus << ","
Jonathan Doman16a2ced2021-11-01 11:13:22 -070053 << " Completion Code = " << completionCode << '\n';
Jonathan Doman94c94bf2020-10-05 23:25:45 -070054 return false;
55 }
56 return true;
57}
58
Jonathan Doman49ea8302022-05-26 14:29:46 -070059std::vector<uint32_t> convertMaskToList(std::bitset<64> mask)
60{
61 std::vector<uint32_t> bitList;
62 for (size_t i = 0; i < mask.size(); ++i)
63 {
64 if (mask.test(i))
65 {
66 bitList.push_back(i);
67 }
68 }
69 return bitList;
70}
71
Jonathan Doman16a2ced2021-11-01 11:13:22 -070072static std::vector<BackendProvider>& getProviders()
Jonathan Doman94c94bf2020-10-05 23:25:45 -070073{
Jonathan Doman16a2ced2021-11-01 11:13:22 -070074 static auto* providers = new std::vector<BackendProvider>;
75 return *providers;
76}
Jonathan Doman94c94bf2020-10-05 23:25:45 -070077
Jonathan Doman16a2ced2021-11-01 11:13:22 -070078void registerBackend(BackendProvider providerFn)
79{
80 getProviders().push_back(providerFn);
81}
Jonathan Doman94c94bf2020-10-05 23:25:45 -070082
Jonathan Domanb5d72222024-01-11 14:20:13 -080083std::unique_ptr<SSTInterface> getInstance(uint8_t address, CPUModel model,
84 WakePolicy wakePolicy)
Jonathan Doman16a2ced2021-11-01 11:13:22 -070085{
86 DEBUG_PRINT << "Searching for provider for " << address << ", model "
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -080087 << std::hex << model << std::dec << '\n';
Jonathan Doman16a2ced2021-11-01 11:13:22 -070088 for (const auto& provider : getProviders())
Jonathan Doman94c94bf2020-10-05 23:25:45 -070089 {
Jonathan Doman94c94bf2020-10-05 23:25:45 -070090 try
91 {
Jonathan Domanb5d72222024-01-11 14:20:13 -080092 auto interface = provider(address, model, wakePolicy);
Jonathan Doman16a2ced2021-11-01 11:13:22 -070093 DEBUG_PRINT << "returned " << interface << '\n';
94 if (interface)
Jonathan Doman94c94bf2020-10-05 23:25:45 -070095 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -070096 return interface;
Jonathan Doman94c94bf2020-10-05 23:25:45 -070097 }
98 }
Jonathan Doman16a2ced2021-11-01 11:13:22 -070099 catch (...)
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700100 {}
101 }
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700102 DEBUG_PRINT << "No supported backends found\n";
103 return nullptr;
104}
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700105
106using BaseCurrentOperatingConfig =
Jason M. Bills33ae81f2023-04-26 09:06:08 -0700107 sdbusplus::server::object_t<sdbusplus::server::xyz::openbmc_project::
108 control::processor::CurrentOperatingConfig>;
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700109
110using BaseOperatingConfig =
Jason M. Bills33ae81f2023-04-26 09:06:08 -0700111 sdbusplus::server::object_t<sdbusplus::server::xyz::openbmc_project::
112 inventory::item::cpu::OperatingConfig>;
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700113
114class OperatingConfig : public BaseOperatingConfig
115{
116 public:
117 std::string path;
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700118 unsigned int level;
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700119
120 public:
121 using BaseOperatingConfig::BaseOperatingConfig;
Patrick Williams77b9c472022-07-22 19:26:57 -0500122 OperatingConfig(sdbusplus::bus_t& bus, unsigned int level_,
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700123 std::string path_) :
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700124 BaseOperatingConfig(bus, path_.c_str(), action::defer_emit),
125 path(std::move(path_)), level(level_)
126 {}
127};
128
129class CPUConfig : public BaseCurrentOperatingConfig
130{
131 private:
132 /** Objects describing all available SST configs - not modifiable. */
133 std::vector<std::unique_ptr<OperatingConfig>> availConfigs;
Patrick Williams77b9c472022-07-22 19:26:57 -0500134 sdbusplus::bus_t& bus;
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700135 const uint8_t peciAddress;
Jonathan Doman703a1852020-11-11 13:04:02 -0800136 const std::string path; ///< D-Bus path of CPU object
137 const CPUModel cpuModel;
Jonathan Doman703a1852020-11-11 13:04:02 -0800138
139 // Keep mutable copies of the properties so we can cache values that we
140 // retrieve in the getters. We don't want to throw an error on a D-Bus
141 // get-property call (extra error handling in clients), so by caching we can
142 // hide any temporary hiccup in PECI communication.
143 // These values can be changed by in-band software so we have to do a full
144 // PECI read on every get-property, and can't assume that values will change
145 // only when set-property is done.
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700146 mutable unsigned int currentLevel;
Jonathan Doman703a1852020-11-11 13:04:02 -0800147 mutable bool bfEnabled;
Jonathan Doman703a1852020-11-11 13:04:02 -0800148
149 /**
150 * Enforce common pre-conditions for D-Bus set property handlers.
151 */
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700152 void setPropertyCheckOrThrow(SSTInterface& sst)
Jonathan Doman703a1852020-11-11 13:04:02 -0800153 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700154 if (!sst.supportsControl())
Jonathan Doman703a1852020-11-11 13:04:02 -0800155 {
156 throw sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed();
157 }
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700158 if (hostState != HostState::postComplete || !sst.ready())
Jonathan Doman703a1852020-11-11 13:04:02 -0800159 {
160 throw sdbusplus::xyz::openbmc_project::Common::Error::Unavailable();
161 }
162 }
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700163
164 public:
Jonathan Domanb5d72222024-01-11 14:20:13 -0800165 CPUConfig(sdbusplus::bus_t& bus_, uint8_t index, CPUModel model,
166 unsigned int currentLevel_, bool bfEnabled_) :
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700167 BaseCurrentOperatingConfig(bus_, generatePath(index).c_str(),
168 action::defer_emit),
Jonathan Doman703a1852020-11-11 13:04:02 -0800169 bus(bus_), peciAddress(index + MIN_CLIENT_ADDR),
Jonathan Domanb5d72222024-01-11 14:20:13 -0800170 path(generatePath(index)), cpuModel(model), currentLevel(currentLevel_),
171 bfEnabled(bfEnabled_)
Jonathan Doman703a1852020-11-11 13:04:02 -0800172 {}
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700173
174 //
175 // D-Bus Property Overrides
176 //
177
178 sdbusplus::message::object_path appliedConfig() const override
179 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700180 DEBUG_PRINT << "Reading AppliedConfig\n";
Jonathan Doman703a1852020-11-11 13:04:02 -0800181 if (hostState != HostState::off)
182 {
183 // Otherwise, try to read current state
Jonathan Domanb5d72222024-01-11 14:20:13 -0800184 auto sst = getInstance(peciAddress, cpuModel, dontWake);
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800185 if (!sst || !sst->ready())
Jonathan Doman703a1852020-11-11 13:04:02 -0800186 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700187 std::cerr << __func__
188 << ": Failed to get SST provider instance\n";
Jonathan Doman703a1852020-11-11 13:04:02 -0800189 }
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700190 else
Jonathan Doman703a1852020-11-11 13:04:02 -0800191 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700192 try
193 {
194 currentLevel = sst->currentLevel();
195 }
196 catch (const PECIError& error)
197 {
198 std::cerr << "Failed to get SST-PP level: " << error.what()
199 << "\n";
200 }
Jonathan Doman703a1852020-11-11 13:04:02 -0800201 }
Jonathan Doman703a1852020-11-11 13:04:02 -0800202 }
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800203 return generateConfigPath(currentLevel);
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700204 }
205
206 bool baseSpeedPriorityEnabled() const override
207 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700208 DEBUG_PRINT << "Reading BaseSpeedPriorityEnabled\n";
Jonathan Doman703a1852020-11-11 13:04:02 -0800209 if (hostState != HostState::off)
210 {
Jonathan Domanb5d72222024-01-11 14:20:13 -0800211 auto sst = getInstance(peciAddress, cpuModel, dontWake);
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800212 if (!sst || !sst->ready())
Jonathan Doman703a1852020-11-11 13:04:02 -0800213 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700214 std::cerr << __func__
215 << ": Failed to get SST provider instance\n";
Jonathan Doman703a1852020-11-11 13:04:02 -0800216 }
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700217 else
Jonathan Doman703a1852020-11-11 13:04:02 -0800218 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700219 try
220 {
221 bfEnabled = sst->bfEnabled(currentLevel);
222 }
223 catch (const PECIError& error)
224 {
225 std::cerr << "Failed to get SST-BF status: " << error.what()
226 << "\n";
227 }
Jonathan Doman703a1852020-11-11 13:04:02 -0800228 }
Jonathan Doman703a1852020-11-11 13:04:02 -0800229 }
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800230 return bfEnabled;
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700231 }
232
233 sdbusplus::message::object_path
Jonathan Doman703a1852020-11-11 13:04:02 -0800234 appliedConfig(sdbusplus::message::object_path value) override
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700235 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700236 DEBUG_PRINT << "Writing AppliedConfig\n";
Jonathan Doman703a1852020-11-11 13:04:02 -0800237 const OperatingConfig* newConfig = nullptr;
238 for (const auto& config : availConfigs)
239 {
240 if (config->path == value.str)
241 {
242 newConfig = config.get();
243 }
244 }
245
246 if (newConfig == nullptr)
247 {
248 throw sdbusplus::xyz::openbmc_project::Common::Error::
249 InvalidArgument();
250 }
251
Jonathan Domanb5d72222024-01-11 14:20:13 -0800252 auto sst = getInstance(peciAddress, cpuModel, wakeAllowed);
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700253 if (!sst)
254 {
255 std::cerr << __func__ << ": Failed to get SST provider instance\n";
256 return sdbusplus::message::object_path();
257 }
Jonathan Doman703a1852020-11-11 13:04:02 -0800258 try
259 {
Jonathan Doman117671e2023-12-07 17:03:15 -0800260 setPropertyCheckOrThrow(*sst);
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700261 sst->setCurrentLevel(newConfig->level);
Jonathan Doman703a1852020-11-11 13:04:02 -0800262 currentLevel = newConfig->level;
263 }
264 catch (const PECIError& error)
265 {
266 std::cerr << "Failed to set new SST-PP level: " << error.what()
267 << "\n";
268 throw sdbusplus::xyz::openbmc_project::Common::Device::Error::
269 WriteFailure();
270 }
271
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700272 // return value not used
273 return sdbusplus::message::object_path();
274 }
275
Jonathan Doman06639632022-06-13 22:00:45 -0700276 bool baseSpeedPriorityEnabled(bool /* value */) override
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700277 {
Jonathan Doman06639632022-06-13 22:00:45 -0700278 DEBUG_PRINT << "Writing BaseSpeedPriorityEnabled not allowed\n";
279 throw sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed();
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700280 // return value not used
281 return false;
282 }
283
284 //
285 // Additions
286 //
287
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700288 OperatingConfig& newConfig(unsigned int level)
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700289 {
290 availConfigs.emplace_back(std::make_unique<OperatingConfig>(
291 bus, level, generateConfigPath(level)));
292 return *availConfigs.back();
293 }
294
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700295 std::string generateConfigPath(unsigned int level) const
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700296 {
297 return path + "/config" + std::to_string(level);
298 }
299
300 /**
301 * Emit the interface added signals which were deferred. This is required
Jonathan Doman703a1852020-11-11 13:04:02 -0800302 * for ObjectMapper to pick up the objects, if we initially defered the
303 * signal emitting.
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700304 */
305 void finalize()
306 {
307 emit_added();
308 for (auto& config : availConfigs)
309 {
310 config->emit_added();
311 }
312 }
313
314 static std::string generatePath(int index)
315 {
Jonathan Doman0a385372021-03-08 17:04:13 -0800316 return cpuPath + std::to_string(index);
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700317 }
318};
319
320/**
321 * Retrieve the SST parameters for a single config and fill the values into the
322 * properties on the D-Bus interface.
323 *
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700324 * @param[in,out] sst Interface to SST backend.
325 * @param[in] level Config TDP level to retrieve.
326 * @param[out] config D-Bus interface to update.
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700327 */
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700328static void getSingleConfig(SSTInterface& sst, unsigned int level,
329 OperatingConfig& config)
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700330{
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700331 config.powerLimit(sst.tdp(level));
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800332 DEBUG_PRINT << " TDP = " << config.powerLimit() << '\n';
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700333
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700334 config.availableCoreCount(sst.coreCount(level));
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800335 DEBUG_PRINT << " coreCount = " << config.availableCoreCount() << '\n';
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700336
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700337 config.baseSpeed(sst.p1Freq(level));
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800338 DEBUG_PRINT << " baseSpeed = " << config.baseSpeed() << '\n';
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700339
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700340 config.maxSpeed(sst.p0Freq(level));
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800341 DEBUG_PRINT << " maxSpeed = " << config.maxSpeed() << '\n';
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700342
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700343 config.maxJunctionTemperature(sst.prochotTemp(level));
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800344 DEBUG_PRINT << " procHot = " << config.maxJunctionTemperature() << '\n';
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700345
346 // Construct BaseSpeedPrioritySettings
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700347 std::vector<std::tuple<uint32_t, std::vector<uint32_t>>> baseSpeeds;
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700348 if (sst.bfSupported(level))
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700349 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700350 std::vector<uint32_t> totalCoreList, loFreqCoreList, hiFreqCoreList;
351 totalCoreList = sst.enabledCoreList(level);
352 hiFreqCoreList = sst.bfHighPriorityCoreList(level);
353 std::set_difference(
354 totalCoreList.begin(), totalCoreList.end(), hiFreqCoreList.begin(),
355 hiFreqCoreList.end(),
356 std::inserter(loFreqCoreList, loFreqCoreList.begin()));
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700357
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700358 baseSpeeds = {{sst.bfHighPriorityFreq(level), hiFreqCoreList},
359 {sst.bfLowPriorityFreq(level), loFreqCoreList}};
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700360 }
361 config.baseSpeedPrioritySettings(baseSpeeds);
362
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700363 config.turboProfile(sst.sseTurboProfile(level));
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700364}
365
366/**
367 * Retrieve all SST configuration info for all discoverable CPUs, and publish
368 * the info on new D-Bus objects on the given bus connection.
369 *
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700370 * @param[in,out] ioc ASIO context.
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700371 * @param[in,out] conn D-Bus ASIO connection.
372 *
Jonathan Doman703a1852020-11-11 13:04:02 -0800373 * @return Whether discovery was successfully finished.
374 *
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700375 * @throw PECIError A PECI command failed on a CPU which had previously
376 * responded to a command.
377 */
Jonathan Doman49ea8302022-05-26 14:29:46 -0700378static bool discoverCPUsAndConfigs(boost::asio::io_context& ioc,
379 sdbusplus::asio::connection& conn)
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700380{
Jonathan Doman49ea8302022-05-26 14:29:46 -0700381 // Persistent list - only populated after complete/successful discovery
382 static std::vector<std::unique_ptr<CPUConfig>> cpus;
383 cpus.clear();
384
385 // Temporary staging list. In case there is any failure, these temporary
386 // objects will get dropped to avoid presenting incomplete info until the
387 // next discovery attempt.
388 std::vector<std::unique_ptr<CPUConfig>> cpuList;
389
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700390 for (uint8_t i = MIN_CLIENT_ADDR; i <= MAX_CLIENT_ADDR; ++i)
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700391 {
Jonathan Doman703a1852020-11-11 13:04:02 -0800392 // Let the event handler run any waiting tasks. If there is a lot of
393 // PECI contention, SST discovery could take a long time. This lets us
394 // get updates to hostState and handle any D-Bus requests.
395 ioc.poll();
396
397 if (hostState == HostState::off)
398 {
399 return false;
400 }
401
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700402 unsigned int cpuIndex = i - MIN_CLIENT_ADDR;
403 DEBUG_PRINT << "Discovering CPU " << cpuIndex << '\n';
404
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700405 // We could possibly check D-Bus for CPU presence and model, but PECI is
406 // 10x faster and so much simpler.
407 uint8_t cc, stepping;
408 CPUModel cpuModel;
409 EPECIStatus status = peci_GetCPUID(i, &cpuModel, &stepping, &cc);
Jonathan Doman703a1852020-11-11 13:04:02 -0800410 if (status == PECI_CC_TIMEOUT)
411 {
412 // Timing out indicates the CPU is present but PCS services not
413 // working yet. Try again later.
414 throw PECIError("Get CPUID timed out");
415 }
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700416 if (status == PECI_CC_CPU_NOT_PRESENT)
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700417 {
418 continue;
419 }
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700420 if (status != PECI_CC_SUCCESS || cc != PECI_DEV_CC_SUCCESS)
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700421 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700422 std::cerr << "GetCPUID returned status " << status
423 << ", cc = " << cc << '\n';
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700424 continue;
425 }
426
Jonathan Domanb5d72222024-01-11 14:20:13 -0800427 std::unique_ptr<SSTInterface> sst = getInstance(i, cpuModel,
428 wakeAllowed);
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700429
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700430 if (!sst)
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700431 {
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700432 // No supported backend for this CPU.
433 continue;
434 }
435
436 if (!sst->ready())
437 {
438 // Supported CPU but it can't be queried yet. Try again later.
439 std::cerr << "sst not ready yet\n";
440 return false;
441 }
442
443 if (!sst->ppEnabled())
444 {
445 // Supported CPU but the specific SKU doesn't support SST-PP.
446 std::cerr << "CPU doesn't support SST-PP\n";
447 continue;
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700448 }
449
450 // Create the per-CPU configuration object
Jonathan Domanb5d72222024-01-11 14:20:13 -0800451 unsigned int currentLevel = sst->currentLevel();
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700452 cpuList.emplace_back(
Jonathan Domanb5d72222024-01-11 14:20:13 -0800453 std::make_unique<CPUConfig>(conn, cpuIndex, cpuModel, currentLevel,
454 sst->bfEnabled(currentLevel)));
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700455 CPUConfig& cpu = *cpuList.back();
456
457 bool foundCurrentLevel = false;
458
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800459 for (unsigned int level = 0; level <= sst->maxLevel(); ++level)
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700460 {
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800461 DEBUG_PRINT << "checking level " << level << ": ";
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700462 // levels 1 and 2 were legacy/deprecated, originally used for AVX
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700463 // license pre-granting. They may be reused for more levels in
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700464 // future generations. So we need to check for discontinuities.
465 if (!sst->levelSupported(level))
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700466 {
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800467 DEBUG_PRINT << "not supported\n";
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700468 continue;
469 }
470
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800471 DEBUG_PRINT << "supported\n";
472
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700473 getSingleConfig(*sst, level, cpu.newConfig(level));
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700474
Jonathan Domanb5d72222024-01-11 14:20:13 -0800475 if (level == currentLevel)
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700476 {
477 foundCurrentLevel = true;
478 }
479 }
480
Jonathan Domanb5d72222024-01-11 14:20:13 -0800481 DEBUG_PRINT << "current level is " << currentLevel << '\n';
Jonathan Domanb4c3bcd2023-03-09 11:41:41 -0800482
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700483 if (!foundCurrentLevel)
484 {
485 // In case we didn't encounter a PECI error, but also didn't find
486 // the config which is supposedly applied, we won't be able to
487 // populate the CurrentOperatingConfig so we have to remove this CPU
488 // from consideration.
489 std::cerr << "CPU " << cpuIndex
490 << " claimed SST support but invalid configs\n";
491 cpuList.pop_back();
492 continue;
493 }
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700494 }
Jonathan Doman703a1852020-11-11 13:04:02 -0800495
Jonathan Doman49ea8302022-05-26 14:29:46 -0700496 cpuList.swap(cpus);
497 std::for_each(cpus.begin(), cpus.end(), [](auto& cpu) { cpu->finalize(); });
Jonathan Doman703a1852020-11-11 13:04:02 -0800498 return true;
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700499}
500
Jonathan Doman49ea8302022-05-26 14:29:46 -0700501/**
502 * Attempt discovery process, and if it fails, wait for 10 seconds to try again.
503 */
504static void discoverOrWait()
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700505{
Jonathan Doman49ea8302022-05-26 14:29:46 -0700506 static boost::asio::steady_timer peciRetryTimer(dbus::getIOContext());
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700507 static int peciErrorCount = 0;
Jonathan Doman703a1852020-11-11 13:04:02 -0800508 bool finished = false;
Jonathan Doman49ea8302022-05-26 14:29:46 -0700509
510 // This function may be called from hostStateHandler or by retrying itself.
511 // In case those overlap, cancel any outstanding retry timer.
512 peciRetryTimer.cancel();
513
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700514 try
515 {
Jonathan Doman703a1852020-11-11 13:04:02 -0800516 DEBUG_PRINT << "Starting discovery\n";
Jonathan Doman49ea8302022-05-26 14:29:46 -0700517 finished = discoverCPUsAndConfigs(dbus::getIOContext(),
518 *dbus::getConnection());
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700519 }
520 catch (const PECIError& err)
521 {
522 std::cerr << "PECI Error: " << err.what() << '\n';
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700523
524 // In case of repeated failure to finish discovery, turn off this
525 // feature altogether. Possible cause is that the CPU model does not
Jonathan Doman16a2ced2021-11-01 11:13:22 -0700526 // actually support the necessary commands.
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700527 if (++peciErrorCount >= 50)
528 {
529 std::cerr << "Aborting SST discovery\n";
530 return;
531 }
532
533 std::cerr << "Retrying SST discovery later\n";
534 }
535
Jonathan Doman703a1852020-11-11 13:04:02 -0800536 DEBUG_PRINT << "Finished discovery attempt: " << finished << '\n';
537
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700538 // Retry later if no CPUs were available, or there was a PECI error.
Jonathan Doman703a1852020-11-11 13:04:02 -0800539 if (!finished)
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700540 {
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700541 peciRetryTimer.expires_after(std::chrono::seconds(10));
Jonathan Doman49ea8302022-05-26 14:29:46 -0700542 peciRetryTimer.async_wait([](boost::system::error_code ec) {
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700543 if (ec)
544 {
Jonathan Doman49ea8302022-05-26 14:29:46 -0700545 if (ec != boost::asio::error::operation_aborted)
546 {
547 std::cerr << "SST PECI Retry Timer failed: " << ec << '\n';
548 }
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700549 return;
550 }
Jonathan Doman49ea8302022-05-26 14:29:46 -0700551 discoverOrWait();
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700552 });
553 }
554}
555
Jonathan Doman49ea8302022-05-26 14:29:46 -0700556static void hostStateHandler(HostState prevState, HostState)
557{
558 if (prevState == HostState::off)
559 {
560 // Start or re-start discovery any time the host moves out of the
561 // powered off state.
562 discoverOrWait();
563 }
564}
565
566void init()
567{
568 addHostStateCallback(hostStateHandler);
569}
570
Jonathan Doman94c94bf2020-10-05 23:25:45 -0700571} // namespace sst
572} // namespace cpu_info